diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6d950b8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build/ +lib/ +emanuelesorce.qvbam* diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..6b3c5af --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,538 @@ +# The project's name is VBA-M it uses C and C++ code +PROJECT(VBA-M C CXX) +#set( CMAKE_VERBOSE_MAKEFILE on ) + +cmake_minimum_required( VERSION 2.6.0 ) +if( COMMAND cmake_policy ) + cmake_policy( SET CMP0003 NEW ) + cmake_policy( SET CMP0005 OLD ) +endif( COMMAND cmake_policy ) +SET( CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMakeScripts ) + +option( ENABLE_SDL "Build the SDL port" ON ) +option( ENABLE_GTK "Build the GTK+ GUI" ON ) +option( ENABLE_WX "Build the wxWidgets port" OFF ) +option( ENABLE_DEBUGGER "Enable the debugger" ON ) +option( ENABLE_NLS "Enable translations" ON ) +option( ENABLE_ASM_CORE "Enable x86 ASM CPU cores" OFF ) +option( ENABLE_ASM_SCALERS "Enable x86 ASM graphic filters" OFF ) +option( ENABLE_LINK "Enable GBA linking functionality" OFF ) +option( ENABLE_LIRC "Enable LIRC support" OFF ) +option( ENABLE_FFMPEG "Enable ffmpeg A/V recording" OFF ) +option( ENABLE_QT "Enable the Qt GUI" ON) +option( SDL_PATH "path for static SDL lib") +if(ENABLE_ASM_SCALERS) + option( ENABLE_MMX "Enable MMX" OFF ) +endif(ENABLE_ASM_SCALERS) +option( ENABLE_GBA_LOGGING "Enable extended GBA logging" ON ) +if( ENABLE_GBA_LOGGING ) + ADD_DEFINITIONS (-DGBA_LOGGING ) +endif( ENABLE_GBA_LOGGING ) +if(ENABLE_MMX) + ADD_DEFINITIONS (-DMMX) +endif(ENABLE_MMX) + +if(ENABLE_QT) + set(ENABLE_GTK OFF) + set(ENABLE_SDL OFF) + if(NOT SDL_PATH) + message( SEND_ERROR "SDL static build path be given to build staticly" ) + endif(NOT SDL_PATH) +endif(ENABLE_QT) + +# The SDL port can't be built without debugging support +if( NOT ENABLE_DEBUGGER AND ENABLE_SDL ) + message( SEND_ERROR "The SDL port can't be built without debugging support" ) +endif( NOT ENABLE_DEBUGGER AND ENABLE_SDL ) + +# Set the version number with -DVERSION=X.X.X-uber +IF( NOT VERSION ) + SET( VERSION "1.8.0-SVN" ) + + IF(EXISTS "${CMAKE_SOURCE_DIR}/.svn") + FIND_PACKAGE(Subversion) + IF(SUBVERSION_FOUND) + Subversion_WC_INFO(${CMAKE_SOURCE_DIR} SVN_INFO) + SET( VERSION "1.8.0-SVN${SVN_INFO_WC_REVISION}" ) + ENDIF(SUBVERSION_FOUND) + ENDIF(EXISTS "${CMAKE_SOURCE_DIR}/.svn") +ENDIF( NOT VERSION ) + +# Fill in SDLMAIN_LIBRARY on OS X manually to avoid using SDLMain.m +# OS X users will have to compile and install SDL from source. +if( APPLE ) + SET(SDLMAIN_LIBRARY "-lSDLmain") +endif( APPLE ) + +# Check for nasm +if( ENABLE_ASM_SCALERS ) + ENABLE_LANGUAGE( ASM_NASM ) +endif( ENABLE_ASM_SCALERS ) + +# Look for some dependencies using CMake scripts +FIND_PACKAGE ( ZLIB REQUIRED ) +FIND_PACKAGE ( PNG REQUIRED ) +FIND_PACKAGE ( OpenGL REQUIRED ) +#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 +IF( ENABLE_GTK ) + FIND_PACKAGE ( PkgConfig REQUIRED ) + FIND_PACKAGE ( Gettext REQUIRED ) + + # These dependencies require pkg-config to be found + PKG_CHECK_MODULES ( GTKMM REQUIRED gtkmm-2.4 ) + PKG_CHECK_MODULES ( GDKMM REQUIRED gdkmm-2.4 ) + PKG_CHECK_MODULES ( GLIBMM REQUIRED glibmm-2.4 ) + PKG_CHECK_MODULES ( GIOMM REQUIRED giomm-2.4 ) + PKG_CHECK_MODULES ( GTKGLMM REQUIRED gtkglextmm-x11-1.2 ) +ENDIF( ENABLE_GTK ) + +if(ENABLE_FFMPEG) + FIND_PACKAGE ( PkgConfig REQUIRED ) + + PKG_CHECK_MODULES(FFMPEG REQUIRED libavcodec libavformat libswscale libavutil) +endif(ENABLE_FFMPEG) + +if(NOT ENABLE_FFMPEG) + ADD_DEFINITIONS(-DNO_FFMPEG) +endif(NOT ENABLE_FFMPEG) + +IF( ENABLE_LIRC ) + SET( WITHLIRC 1 ) +ELSE( ENABLE_LIRC ) + SET( WITHLIRC 0 ) +ENDIF( ENABLE_LIRC ) + +# Set the default install dir +IF( NOT DATA_INSTALL_DIR ) + SET( DATA_INSTALL_DIR "share/vbam" ) +ENDIF( NOT DATA_INSTALL_DIR ) + +SET( PKGDATADIR ${CMAKE_INSTALL_PREFIX}/${DATA_INSTALL_DIR} ) + +# Set the configuration file location +IF( NOT SYSCONFDIR ) + SET( SYSCONFDIR "/etc" ) +ENDIF( NOT SYSCONFDIR ) + +# C defines +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( ENABLE_LINK ) + # IPC linking code needs sem_timedwait which can be either in librt or pthreads + FIND_LIBRARY(RT_LIB rt) + IF(RT_LIB) + SET(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${RT_LIB}) + SET(VBAMCORE_LIBS ${VBAMCORE_LIBS} ${RT_LIB}) + ENDIF(RT_LIB) + + FIND_LIBRARY(PTHREAD_LIB pthread) + IF(PTHREAD_LIB) + SET(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${PTHREAD_LIB}) + SET(VBAMCORE_LIBS ${VBAMCORE_LIBS} ${PTHREAD_LIB}) + ENDIF(PTHREAD_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( ENABLE_LINK ) + +# The debugger is enabled by default +if( NOT ENABLE_DEBUGGER ) + ADD_DEFINITIONS (-DNO_DEBUGGER) +else( NOT ENABLE_DEBUGGER ) + ADD_DEFINITIONS (-DBKPT_SUPPORT) +endif( NOT ENABLE_DEBUGGER ) + +# The ASM core is disabled by default because we don't know on which platform we are +IF( NOT ENABLE_ASM_CORE ) + ADD_DEFINITIONS (-DC_CORE) +ENDIF( NOT ENABLE_ASM_CORE ) + +# Enable internationalization +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 +IF ( WIN32 ) + SET( CMAKE_ASM_NASM_FLAGS "-I$(CMAKE_SOURCE_DIR)/src/filters/hq/asm/ -O1 -w-orphan-labels") +ELSE ( WIN32 ) + SET( CMAKE_ASM_NASM_FLAGS "-I$(CMAKE_SOURCE_DIR)/src/filters/hq/asm/ -O1 -DELF -w-orphan-labels") +ENDIF ( WIN32 ) + +SET( CMAKE_BUILD_TYPE RELEASE) +SET( CMAKE_C_FLAGS_RELEASE "-O3") +SET( CMAKE_CXX_FLAGS_RELEASE "-O3") +SET( CMAKE_C_FLAGS_DEBUG "-Wall") +SET( CMAKE_CXX_FLAGS_DEBUG "-Wall") + +# Source files definition +SET(SRC_FEX + fex/7z_C/7zAlloc.c + fex/7z_C/7zBuf.c + fex/7z_C/7zCrc.c + fex/7z_C/7zCrcOpt.c + fex/7z_C/7zDec.c + fex/7z_C/7zIn.c + fex/7z_C/7zStream.c + fex/7z_C/Bcj2.c + fex/7z_C/Bra86.c + fex/7z_C/Bra.c + fex/7z_C/CpuArch.c + fex/7z_C/Lzma2Dec.c + fex/7z_C/LzmaDec.c + fex/7z_C/Ppmd7.c + fex/7z_C/Ppmd7Dec.c + fex/fex/Binary_Extractor.cpp + fex/fex/blargg_common.cpp + fex/fex/blargg_errors.cpp + fex/fex/Data_Reader.cpp + fex/fex/fex.cpp + fex/fex/File_Extractor.cpp + fex/fex/Gzip_Extractor.cpp + fex/fex/Gzip_Reader.cpp + fex/fex/Rar_Extractor.cpp + fex/fex/Zip7_Extractor.cpp + fex/fex/Zip_Extractor.cpp + fex/fex/Zlib_Inflater.cpp +) + +SET(SRC_MAIN + src/Util.cpp + src/common/Patch.cpp + src/common/memgzio.c + src/common/SoundSDL.cpp +) + +if(ENABLE_FFMPEG) + SET(SRC_MAIN ${SRC_MAIN} src/common/ffmpeg.cpp) +endif(ENABLE_FFMPEG) + +SET(SRC_GBA + src/gba/agbprint.cpp + src/gba/bios.cpp + src/gba/Cheats.cpp + src/gba/CheatSearch.cpp + src/gba/EEprom.cpp + src/gba/Flash.cpp + src/gba/GBA.cpp + src/gba/GBAGfx.cpp + src/gba/GBALink.cpp + src/gba/GBASockClient.cpp + src/gba/GBA-thumb.cpp + src/gba/GBA-arm.cpp + src/gba/gbafilter.cpp + src/gba/Globals.cpp + src/gba/Mode0.cpp + src/gba/Mode1.cpp + src/gba/Mode2.cpp + src/gba/Mode3.cpp + src/gba/Mode4.cpp + src/gba/Mode5.cpp + src/gba/RTC.cpp + src/gba/Sound.cpp + src/gba/Sram.cpp +) + +SET(SRC_GB + src/gb/GB.cpp + src/gb/gbCheats.cpp + src/gb/gbDis.cpp + src/gb/gbGfx.cpp + src/gb/gbGlobals.cpp + src/gb/gbMemory.cpp + src/gb/gbPrinter.cpp + src/gb/gbSGB.cpp + src/gb/gbSound.cpp +) + +SET(SRC_APU + src/apu/Blip_Buffer.cpp + src/apu/Effects_Buffer.cpp + src/apu/Gb_Apu.cpp + src/apu/Gb_Apu_State.cpp + src/apu/Gb_Oscs.cpp + src/apu/Multi_Buffer.cpp +) + +SET(SRC_SDL + src/sdl/debugger.cpp + src/sdl/SDL.cpp + src/sdl/filters.cpp + src/sdl/text.cpp + src/sdl/inputSDL.cpp + src/sdl/expr.cpp + src/sdl/exprNode.cpp + src/sdl/expr-lex.cpp +) + +SET(SRC_FILTERS + src/filters/2xSaI.cpp + src/filters/admame.cpp + src/filters/bilinear.cpp + src/filters/hq2x.cpp + src/filters/interframe.cpp + src/filters/pixel.cpp + src/filters/scanline.cpp + src/filters/simpleFilter.cpp +) + +SET(SRC_HQ_C + src/filters/hq/c/hq_implementation.cpp +) + +SET(SRC_HQ_ASM + src/filters/hq/asm/hq3x_16.asm + src/filters/hq/asm/hq3x_32.asm + src/filters/hq/asm/hq4x_16.asm + src/filters/hq/asm/hq4x_32.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 + src/gtk/system.cpp + src/gtk/windowcallbacks.cpp + src/gtk/filters.cpp + src/gtk/generalconfig.cpp + src/gtk/gameboyconfig.cpp + src/gtk/gameboyadvanceconfig.cpp + src/gtk/cheatlist.cpp + src/gtk/cheatedit.cpp + src/gtk/gameboyadvancecheatlist.cpp + src/gtk/gameboycheatlist.cpp + src/gtk/joypadconfig.cpp + src/gtk/directoriesconfig.cpp + src/gtk/displayconfig.cpp + src/gtk/soundconfig.cpp + src/gtk/screenarea.cpp + src/gtk/screenarea-cairo.cpp + src/gtk/screenarea-opengl.cpp + src/gtk/tools.cpp + src/gtk/window.cpp + src/sdl/inputSDL.cpp +) + +SET(SRC_QT + src/qt/main.cpp + src/qt/window.cpp + src/qt/system.cpp + src/sdl/inputSDL.cpp + src/qt/ScreenArea.cpp + src/qt/CustomQQuickView.cpp + src/qt/ScreenAreaOpenGL.cpp + src/qt/FilesModel.cpp + src/qt/Config.cpp + src/qt/SoundQt.cpp + src/qt/QGameSlot.cpp +) + + +file(GLOB UBUNTU_QMLS ${CMAKE_SOURCE_DIR}/src/qt/qml/*.qml) + +if( ENABLE_DEBUGGER ) + SET(SRC_DEBUGGER + src/gba/armdis.cpp + src/gba/elf.cpp + src/gba/remote.cpp + ) +endif( ENABLE_DEBUGGER ) + +INCLUDE_DIRECTORIES( + ${ZLIB_INCLUDE_DIR} + fex + ${PNG_INCLUDE_DIR} + #${SDL_INCLUDE_DIR} + ${SDL_PATH}/include +) + +IF( ENABLE_GTK ) + INCLUDE_DIRECTORIES( + ${GLIBMM_INCLUDE_DIRS} + ${GIOMM_INCLUDE_DIRS} + ${GTKMM_INCLUDE_DIRS} + ${GDKMM_INCLUDE_DIRS} + ${GTKGLMM_INCLUDE_DIRS} + ) + + LINK_DIRECTORIES( + ${GLIBMM_LIBRARY_DIRS} + ${GIOMM_LIBRARY_DIRS} + ${GTKMM_LIBRARY_DIRS} + ${GTKGLMM_LIBRARY_DIRS} + ) +ENDIF( ENABLE_GTK ) + +IF( ENABLE_FFMPEG ) + INCLUDE_DIRECTORIES( + ${FFMPEG_INCLUDE_DIRS} + ) +ENDIF( ENABLE_FFMPEG ) + +ADD_LIBRARY( + fex + ${SRC_FEX} +) + +ADD_LIBRARY ( + vbamcore + ${PROJECT_SRCS} + ${SRC_MAIN} + ${SRC_GBA} + ${SRC_GB} + ${SRC_APU} + ${SRC_FILTERS} + ${SRC_DEBUGGER} +) + +IF( ENABLE_SDL ) + ADD_EXECUTABLE ( + vbam + WIN32 + ${SRC_SDL} + ) + + IF( WIN32 ) + SET( WIN32_LIBRARIES wsock32 ) + ENDIF( WIN32 ) + + IF( ENABLE_LIRC ) + SET( LIRC_CLIENT_LIBRARY lirc_client ) + ENDIF( ENABLE_LIRC ) + + TARGET_LINK_LIBRARIES ( + vbam + ${VBAMCORE_LIBS} + ${WIN32_LIBRARIES} + ${LIRC_CLIENT_LIBRARY} + ) + + INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/vbam DESTINATION bin) + INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/sdl/vbam.cfg-example + DESTINATION ${SYSCONFDIR} + RENAME vbam.cfg) + INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/vba-over.ini DESTINATION ${DATA_INSTALL_DIR}) +ENDIF( ENABLE_SDL ) + +IF( ENABLE_GTK ) + ADD_EXECUTABLE ( + gvbam + WIN32 + ${SRC_GTK} + ) + + TARGET_LINK_LIBRARIES ( + gvbam + ${VBAMCORE_LIBS} + ${GTKMM_LIBRARIES} + ${GTKGLMM_LIBRARIES} + ) + + INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/gvbam DESTINATION bin) + INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/gtk/gvbam.desktop DESTINATION share/applications) + INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src/gtk/icons/ DESTINATION share/icons/hicolor PATTERN ".svn" EXCLUDE) + 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_QT) + set(CMAKE_INCLUDE_CURRENT_DIR ON) + set(CMAKE_AUTOMOC ON) + find_package(Qt5Core) + find_package(Qt5Quick) + find_package(Qt5Gui) + find_package(Qt5Multimedia) + add_executable( + qvbam + ${SRC_QT} + ) + TARGET_LINK_LIBRARIES ( + qvbam + vbamcore + fex + "${SDL_PATH}/build/.libs/libSDL.a" + "${SDL_PATH}/build/.libs/libSDLmain.a" + "-ldl" + #"-lts" + "-pthread" + ${SFML_LIBRARY} + ${OPENGL_LIBRARIES} + ${ZLIB_LIBRARY} + ${PNG_LIBRARY} + #${VBAMCORE_LIBS} + ) + file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/src/qt/qml DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) + qt5_use_modules(qvbam Core Quick Gui Multimedia) + + add_custom_target( + ubuntu_Files + ALL SOURCES + ${UBUNTU_QMLS} + ) + +endif(ENABLE_QT) + +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 OR ENABLE_WX ) diff --git a/README.md b/README.md new file mode 100644 index 0000000..a9b6f7b --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# QVBAM + +Based on the original app created by bobo1993324 +UNOFFICIAL vbam fork. +This app is an adaptation designed for ubuntu touch of the gba emulator VBAM. +vbam website: http://vba-m.com/ + +## Build + +Run build.sh +You need to have cmake, autoconf, libasound-dev and libpulse-dev for it to work +to build the ubuntu touch click package run package.sh after building + +## License + +See source code for details + +## Contact + +Emanuele Sorce diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..3b5f0af --- /dev/null +++ b/build.sh @@ -0,0 +1,14 @@ +#!/bin/bash +if [[ ! -f lib/SDL-1.2.15/build/.libs/libSDL.a ]]; then + cd lib + tar xf SDL-1.2.15.tar.gz + cd SDL-1.2.15/ + patch -p 1 -f < ../SDL1.patch + ./autogen.sh && ./configure --enable-input-tslib=off && make -j4 + cd ../.. +fi +mkdir build +cd build +cmake .. -DSDL_PATH=`pwd`/../lib/SDL-1.2.15/ +make + diff --git a/fex/7z_C/7z.h b/fex/7z_C/7z.h new file mode 100644 index 0000000..01c4cac --- /dev/null +++ b/fex/7z_C/7z.h @@ -0,0 +1,203 @@ +/* 7z.h -- 7z interface +2010-03-11 : Igor Pavlov : Public domain */ + +#ifndef __7Z_H +#define __7Z_H + +#include "7zBuf.h" + +EXTERN_C_BEGIN + +#define k7zStartHeaderSize 0x20 +#define k7zSignatureSize 6 +extern Byte k7zSignature[k7zSignatureSize]; +#define k7zMajorVersion 0 + +enum EIdEnum +{ + k7zIdEnd, + k7zIdHeader, + k7zIdArchiveProperties, + k7zIdAdditionalStreamsInfo, + k7zIdMainStreamsInfo, + k7zIdFilesInfo, + k7zIdPackInfo, + k7zIdUnpackInfo, + k7zIdSubStreamsInfo, + k7zIdSize, + k7zIdCRC, + k7zIdFolder, + k7zIdCodersUnpackSize, + k7zIdNumUnpackStream, + k7zIdEmptyStream, + k7zIdEmptyFile, + k7zIdAnti, + k7zIdName, + k7zIdCTime, + k7zIdATime, + k7zIdMTime, + k7zIdWinAttributes, + k7zIdComment, + k7zIdEncodedHeader, + k7zIdStartPos, + k7zIdDummy +}; + +typedef struct +{ + UInt32 NumInStreams; + UInt32 NumOutStreams; + UInt64 MethodID; + CBuf Props; +} CSzCoderInfo; + +void SzCoderInfo_Init(CSzCoderInfo *p); +void SzCoderInfo_Free(CSzCoderInfo *p, ISzAlloc *alloc); + +typedef struct +{ + UInt32 InIndex; + UInt32 OutIndex; +} CSzBindPair; + +typedef struct +{ + CSzCoderInfo *Coders; + CSzBindPair *BindPairs; + UInt32 *PackStreams; + UInt64 *UnpackSizes; + UInt32 NumCoders; + UInt32 NumBindPairs; + UInt32 NumPackStreams; + int UnpackCRCDefined; + UInt32 UnpackCRC; + + UInt32 NumUnpackStreams; +} CSzFolder; + +void SzFolder_Init(CSzFolder *p); +UInt64 SzFolder_GetUnpackSize(CSzFolder *p); +int SzFolder_FindBindPairForInStream(CSzFolder *p, UInt32 inStreamIndex); +UInt32 SzFolder_GetNumOutStreams(CSzFolder *p); +UInt64 SzFolder_GetUnpackSize(CSzFolder *p); + +SRes SzFolder_Decode(const CSzFolder *folder, const UInt64 *packSizes, + ILookInStream *stream, UInt64 startPos, + Byte *outBuffer, size_t outSize, ISzAlloc *allocMain); + +typedef struct +{ + UInt32 Low; + UInt32 High; +} CNtfsFileTime; + +typedef struct +{ + CNtfsFileTime MTime; + UInt64 Size; + UInt32 Crc; + UInt32 Attrib; + Byte HasStream; + Byte IsDir; + Byte IsAnti; + Byte CrcDefined; + Byte MTimeDefined; + Byte AttribDefined; +} CSzFileItem; + +void SzFile_Init(CSzFileItem *p); + +typedef struct +{ + UInt64 *PackSizes; + Byte *PackCRCsDefined; + UInt32 *PackCRCs; + CSzFolder *Folders; + CSzFileItem *Files; + UInt32 NumPackStreams; + UInt32 NumFolders; + UInt32 NumFiles; +} CSzAr; + +void SzAr_Init(CSzAr *p); +void SzAr_Free(CSzAr *p, ISzAlloc *alloc); + + +/* + SzExtract extracts file from archive + + *outBuffer must be 0 before first call for each new archive. + + Extracting cache: + If you need to decompress more than one file, you can send + these values from previous call: + *blockIndex, + *outBuffer, + *outBufferSize + You can consider "*outBuffer" as cache of solid block. If your archive is solid, + it will increase decompression speed. + + If you use external function, you can declare these 3 cache variables + (blockIndex, outBuffer, outBufferSize) as static in that external function. + + Free *outBuffer and set *outBuffer to 0, if you want to flush cache. +*/ + +typedef struct +{ + CSzAr db; + + UInt64 startPosAfterHeader; + UInt64 dataPos; + + UInt32 *FolderStartPackStreamIndex; + UInt64 *PackStreamStartPositions; + UInt32 *FolderStartFileIndex; + UInt32 *FileIndexToFolderIndexMap; + + size_t *FileNameOffsets; /* in 2-byte steps */ + CBuf FileNames; /* UTF-16-LE */ +} CSzArEx; + +void SzArEx_Init(CSzArEx *p); +void SzArEx_Free(CSzArEx *p, ISzAlloc *alloc); +UInt64 SzArEx_GetFolderStreamPos(const CSzArEx *p, UInt32 folderIndex, UInt32 indexInFolder); +int SzArEx_GetFolderFullPackSize(const CSzArEx *p, UInt32 folderIndex, UInt64 *resSize); + +/* +if dest == NULL, the return value specifies the required size of the buffer, + in 16-bit characters, including the null-terminating character. +if dest != NULL, the return value specifies the number of 16-bit characters that + are written to the dest, including the null-terminating character. */ + +size_t SzArEx_GetFileNameUtf16(const CSzArEx *p, size_t fileIndex, UInt16 *dest); + +SRes SzArEx_Extract( + const CSzArEx *db, + ILookInStream *inStream, + UInt32 fileIndex, /* index of file */ + UInt32 *blockIndex, /* index of solid block */ + Byte **outBuffer, /* pointer to pointer to output buffer (allocated with allocMain) */ + size_t *outBufferSize, /* buffer size for output buffer */ + size_t *offset, /* offset of stream for required file in *outBuffer */ + size_t *outSizeProcessed, /* size of file in *outBuffer */ + ISzAlloc *allocMain, + ISzAlloc *allocTemp); + + +/* +SzArEx_Open Errors: +SZ_ERROR_NO_ARCHIVE +SZ_ERROR_ARCHIVE +SZ_ERROR_UNSUPPORTED +SZ_ERROR_MEM +SZ_ERROR_CRC +SZ_ERROR_INPUT_EOF +SZ_ERROR_FAIL +*/ + +SRes SzArEx_Open(CSzArEx *p, ILookInStream *inStream, ISzAlloc *allocMain, ISzAlloc *allocTemp); + +EXTERN_C_END + +#endif diff --git a/fex/7z_C/7zAlloc.c b/fex/7z_C/7zAlloc.c new file mode 100644 index 0000000..964b28d --- /dev/null +++ b/fex/7z_C/7zAlloc.c @@ -0,0 +1,76 @@ +/* 7zAlloc.c -- Allocation functions +2010-10-29 : Igor Pavlov : Public domain */ + +#include "7zAlloc.h" + +/* #define _SZ_ALLOC_DEBUG */ +/* use _SZ_ALLOC_DEBUG to debug alloc/free operations */ + +#ifdef _SZ_ALLOC_DEBUG + +#ifdef _WIN32 +#include +#endif + +#include +int g_allocCount = 0; +int g_allocCountTemp = 0; + +#endif + +void *SzAlloc(void *p, size_t size) +{ + p = p; + if (size == 0) + return 0; + #ifdef _SZ_ALLOC_DEBUG + fprintf(stderr, "\nAlloc %10d bytes; count = %10d", size, g_allocCount); + g_allocCount++; + #endif + return malloc(size); +} + +void SzFree(void *p, void *address) +{ + p = p; + #ifdef _SZ_ALLOC_DEBUG + if (address != 0) + { + g_allocCount--; + fprintf(stderr, "\nFree; count = %10d", g_allocCount); + } + #endif + free(address); +} + +void *SzAllocTemp(void *p, size_t size) +{ + p = p; + if (size == 0) + return 0; + #ifdef _SZ_ALLOC_DEBUG + fprintf(stderr, "\nAlloc_temp %10d bytes; count = %10d", size, g_allocCountTemp); + g_allocCountTemp++; + #ifdef _WIN32 + return HeapAlloc(GetProcessHeap(), 0, size); + #endif + #endif + return malloc(size); +} + +void SzFreeTemp(void *p, void *address) +{ + p = p; + #ifdef _SZ_ALLOC_DEBUG + if (address != 0) + { + g_allocCountTemp--; + fprintf(stderr, "\nFree_temp; count = %10d", g_allocCountTemp); + } + #ifdef _WIN32 + HeapFree(GetProcessHeap(), 0, address); + return; + #endif + #endif + free(address); +} diff --git a/fex/7z_C/7zAlloc.h b/fex/7z_C/7zAlloc.h new file mode 100644 index 0000000..3344e93 --- /dev/null +++ b/fex/7z_C/7zAlloc.h @@ -0,0 +1,15 @@ +/* 7zAlloc.h -- Allocation functions +2010-10-29 : Igor Pavlov : Public domain */ + +#ifndef __7Z_ALLOC_H +#define __7Z_ALLOC_H + +#include + +void *SzAlloc(void *p, size_t size); +void SzFree(void *p, void *address); + +void *SzAllocTemp(void *p, size_t size); +void SzFreeTemp(void *p, void *address); + +#endif diff --git a/fex/7z_C/7zBuf.c b/fex/7z_C/7zBuf.c new file mode 100644 index 0000000..14e7f4e --- /dev/null +++ b/fex/7z_C/7zBuf.c @@ -0,0 +1,36 @@ +/* 7zBuf.c -- Byte Buffer +2008-03-28 +Igor Pavlov +Public domain */ + +#include "7zBuf.h" + +void Buf_Init(CBuf *p) +{ + p->data = 0; + p->size = 0; +} + +int Buf_Create(CBuf *p, size_t size, ISzAlloc *alloc) +{ + p->size = 0; + if (size == 0) + { + p->data = 0; + return 1; + } + p->data = (Byte *)alloc->Alloc(alloc, size); + if (p->data != 0) + { + p->size = size; + return 1; + } + return 0; +} + +void Buf_Free(CBuf *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->data); + p->data = 0; + p->size = 0; +} diff --git a/fex/7z_C/7zBuf.h b/fex/7z_C/7zBuf.h new file mode 100644 index 0000000..e9f2f31 --- /dev/null +++ b/fex/7z_C/7zBuf.h @@ -0,0 +1,39 @@ +/* 7zBuf.h -- Byte Buffer +2009-02-07 : Igor Pavlov : Public domain */ + +#ifndef __7Z_BUF_H +#define __7Z_BUF_H + +#include "Types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + Byte *data; + size_t size; +} CBuf; + +void Buf_Init(CBuf *p); +int Buf_Create(CBuf *p, size_t size, ISzAlloc *alloc); +void Buf_Free(CBuf *p, ISzAlloc *alloc); + +typedef struct +{ + Byte *data; + size_t size; + size_t pos; +} CDynBuf; + +void DynBuf_Construct(CDynBuf *p); +void DynBuf_SeekToBeg(CDynBuf *p); +int DynBuf_Write(CDynBuf *p, const Byte *buf, size_t size, ISzAlloc *alloc); +void DynBuf_Free(CDynBuf *p, ISzAlloc *alloc); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/fex/7z_C/7zC.txt b/fex/7z_C/7zC.txt new file mode 100644 index 0000000..5d5d06d --- /dev/null +++ b/fex/7z_C/7zC.txt @@ -0,0 +1,194 @@ +7z ANSI-C Decoder 4.62 +---------------------- + +7z ANSI-C provides 7z/LZMA decoding. +7z ANSI-C version is simplified version ported from C++ code. + +LZMA is default and general compression method of 7z format +in 7-Zip compression program (www.7-zip.org). LZMA provides high +compression ratio and very fast decompression. + + +LICENSE +------- + +7z ANSI-C Decoder is part of the LZMA SDK. +LZMA SDK is written and placed in the public domain by Igor Pavlov. + +Files +--------------------- + +7zDecode.* - Low level 7z decoding +7zExtract.* - High level 7z decoding +7zHeader.* - .7z format constants +7zIn.* - .7z archive opening +7zItem.* - .7z structures +7zMain.c - Test application + + +How To Use +---------- + +You must download 7-Zip program from www.7-zip.org. + +You can create .7z archive with 7z.exe or 7za.exe: + + 7za.exe a archive.7z *.htm -r -mx -m0fb=255 + +If you have big number of files in archive, and you need fast extracting, +you can use partly-solid archives: + + 7za.exe a archive.7z *.htm -ms=512K -r -mx -m0fb=255 -m0d=512K + +In that example 7-Zip will use 512KB solid blocks. So it needs to decompress only +512KB for extracting one file from such archive. + + +Limitations of current version of 7z ANSI-C Decoder +--------------------------------------------------- + + - It reads only "FileName", "Size", "LastWriteTime" and "CRC" information for each file in archive. + - It supports only LZMA and Copy (no compression) methods with BCJ or BCJ2 filters. + - It converts original UTF-16 Unicode file names to UTF-8 Unicode file names. + +These limitations will be fixed in future versions. + + +Using 7z ANSI-C Decoder Test application: +----------------------------------------- + +Usage: 7zDec + +: + e: Extract files from archive + l: List contents of archive + t: Test integrity of archive + +Example: + + 7zDec l archive.7z + +lists contents of archive.7z + + 7zDec e archive.7z + +extracts files from archive.7z to current folder. + + +How to use .7z Decoder +---------------------- + +Memory allocation +~~~~~~~~~~~~~~~~~ + +7z Decoder uses two memory pools: +1) Temporary pool +2) Main pool +Such scheme can allow you to avoid fragmentation of allocated blocks. + + +Steps for using 7z decoder +-------------------------- + +Use code at 7zMain.c as example. + +1) Declare variables: + inStream /* implements ILookInStream interface */ + CSzArEx db; /* 7z archive database structure */ + ISzAlloc allocImp; /* memory functions for main pool */ + ISzAlloc allocTempImp; /* memory functions for temporary pool */ + +2) call CrcGenerateTable(); function to initialize CRC structures. + +3) call SzArEx_Init(&db); function to initialize db structures. + +4) call SzArEx_Open(&db, inStream, &allocMain, &allocTemp) to open archive + +This function opens archive "inStream" and reads headers to "db". +All items in "db" will be allocated with "allocMain" functions. +SzArEx_Open function allocates and frees temporary structures by "allocTemp" functions. + +5) List items or Extract items + + Listing code: + ~~~~~~~~~~~~~ + { + UInt32 i; + for (i = 0; i < db.db.NumFiles; i++) + { + CFileItem *f = db.db.Files + i; + printf("%10d %s\n", (int)f->Size, f->Name); + } + } + + Extracting code: + ~~~~~~~~~~~~~~~~ + + SZ_RESULT SzAr_Extract( + CArchiveDatabaseEx *db, + ILookInStream *inStream, + UInt32 fileIndex, /* index of file */ + UInt32 *blockIndex, /* index of solid block */ + Byte **outBuffer, /* pointer to pointer to output buffer (allocated with allocMain) */ + size_t *outBufferSize, /* buffer size for output buffer */ + size_t *offset, /* offset of stream for required file in *outBuffer */ + size_t *outSizeProcessed, /* size of file in *outBuffer */ + ISzAlloc *allocMain, + ISzAlloc *allocTemp); + + If you need to decompress more than one file, you can send these values from previous call: + blockIndex, + outBuffer, + outBufferSize, + You can consider "outBuffer" as cache of solid block. If your archive is solid, + it will increase decompression speed. + + After decompressing you must free "outBuffer": + allocImp.Free(outBuffer); + +6) call SzArEx_Free(&db, allocImp.Free) to free allocated items in "db". + + + + +Memory requirements for .7z decoding +------------------------------------ + +Memory usage for Archive opening: + - Temporary pool: + - Memory for uncompressed .7z headers + - some other temporary blocks + - Main pool: + - Memory for database: + Estimated size of one file structures in solid archive: + - Size (4 or 8 Bytes) + - CRC32 (4 bytes) + - LastWriteTime (8 bytes) + - Some file information (4 bytes) + - File Name (variable length) + pointer + allocation structures + +Memory usage for archive Decompressing: + - Temporary pool: + - Memory for LZMA decompressing structures + - Main pool: + - Memory for decompressed solid block + - Memory for temprorary buffers, if BCJ2 fileter is used. Usually these + temprorary buffers can be about 15% of solid block size. + + +7z Decoder doesn't allocate memory for compressed blocks. +Instead of this, you must allocate buffer with desired +size before calling 7z Decoder. Use 7zMain.c as example. + + +Defines +------- + +_SZ_ALLOC_DEBUG - define it if you want to debug alloc/free operations to stderr. + + +--- + +http://www.7-zip.org +http://www.7-zip.org/sdk.html +http://www.7-zip.org/support.html diff --git a/fex/7z_C/7zCrc.c b/fex/7z_C/7zCrc.c new file mode 100644 index 0000000..a920849 --- /dev/null +++ b/fex/7z_C/7zCrc.c @@ -0,0 +1,74 @@ +/* 7zCrc.c -- CRC32 calculation +2009-11-23 : Igor Pavlov : Public domain */ + +#include "7zCrc.h" +#include "CpuArch.h" + +#define kCrcPoly 0xEDB88320 + +#ifdef MY_CPU_LE +#define CRC_NUM_TABLES 8 +#else +#define CRC_NUM_TABLES 1 +#endif + +typedef UInt32 (MY_FAST_CALL *CRC_FUNC)(UInt32 v, const void *data, size_t size, const UInt32 *table); + +static CRC_FUNC g_CrcUpdate; +UInt32 g_CrcTable[256 * CRC_NUM_TABLES]; + +#if CRC_NUM_TABLES == 1 + +#define CRC_UPDATE_BYTE_2(crc, b) (table[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8)) + +static UInt32 MY_FAST_CALL CrcUpdateT1(UInt32 v, const void *data, size_t size, const UInt32 *table) +{ + const Byte *p = (const Byte *)data; + for (; size > 0; size--, p++) + v = CRC_UPDATE_BYTE_2(v, *p); + return v; +} + +#else + +UInt32 MY_FAST_CALL CrcUpdateT4(UInt32 v, const void *data, size_t size, const UInt32 *table); +UInt32 MY_FAST_CALL CrcUpdateT8(UInt32 v, const void *data, size_t size, const UInt32 *table); + +#endif + +UInt32 MY_FAST_CALL CrcUpdate(UInt32 v, const void *data, size_t size) +{ + return g_CrcUpdate(v, data, size, g_CrcTable); +} + +UInt32 MY_FAST_CALL CrcCalc(const void *data, size_t size) +{ + return g_CrcUpdate(CRC_INIT_VAL, data, size, g_CrcTable) ^ CRC_INIT_VAL; +} + +void MY_FAST_CALL CrcGenerateTable() +{ + UInt32 i; + for (i = 0; i < 256; i++) + { + UInt32 r = i; + unsigned j; + for (j = 0; j < 8; j++) + r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); + g_CrcTable[i] = r; + } + #if CRC_NUM_TABLES == 1 + g_CrcUpdate = CrcUpdateT1; + #else + for (; i < 256 * CRC_NUM_TABLES; i++) + { + UInt32 r = g_CrcTable[i - 256]; + g_CrcTable[i] = g_CrcTable[r & 0xFF] ^ (r >> 8); + } + g_CrcUpdate = CrcUpdateT4; + #ifdef MY_CPU_X86_OR_AMD64 + if (!CPU_Is_InOrder()) + g_CrcUpdate = CrcUpdateT8; + #endif + #endif +} diff --git a/fex/7z_C/7zCrc.h b/fex/7z_C/7zCrc.h new file mode 100644 index 0000000..38e3e5f --- /dev/null +++ b/fex/7z_C/7zCrc.h @@ -0,0 +1,25 @@ +/* 7zCrc.h -- CRC32 calculation +2009-11-21 : Igor Pavlov : Public domain */ + +#ifndef __7Z_CRC_H +#define __7Z_CRC_H + +#include "Types.h" + +EXTERN_C_BEGIN + +extern UInt32 g_CrcTable[]; + +/* Call CrcGenerateTable one time before other CRC functions */ +void MY_FAST_CALL CrcGenerateTable(void); + +#define CRC_INIT_VAL 0xFFFFFFFF +#define CRC_GET_DIGEST(crc) ((crc) ^ CRC_INIT_VAL) +#define CRC_UPDATE_BYTE(crc, b) (g_CrcTable[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8)) + +UInt32 MY_FAST_CALL CrcUpdate(UInt32 crc, const void *data, size_t size); +UInt32 MY_FAST_CALL CrcCalc(const void *data, size_t size); + +EXTERN_C_END + +#endif diff --git a/fex/7z_C/7zCrcOpt.c b/fex/7z_C/7zCrcOpt.c new file mode 100644 index 0000000..6c766a2 --- /dev/null +++ b/fex/7z_C/7zCrcOpt.c @@ -0,0 +1,34 @@ +/* 7zCrcOpt.c -- CRC32 calculation : optimized version +2009-11-23 : Igor Pavlov : Public domain */ + +#include "CpuArch.h" + +#ifdef MY_CPU_LE + +#define CRC_UPDATE_BYTE_2(crc, b) (table[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8)) + +UInt32 MY_FAST_CALL CrcUpdateT4(UInt32 v, const void *data, size_t size, const UInt32 *table) +{ + const Byte *p = (const Byte *)data; + for (; size > 0 && ((unsigned)(ptrdiff_t)p & 3) != 0; size--, p++) + v = CRC_UPDATE_BYTE_2(v, *p); + for (; size >= 4; size -= 4, p += 4) + { + v ^= *(const UInt32 *)p; + v = + table[0x300 + (v & 0xFF)] ^ + table[0x200 + ((v >> 8) & 0xFF)] ^ + table[0x100 + ((v >> 16) & 0xFF)] ^ + table[0x000 + ((v >> 24))]; + } + for (; size > 0; size--, p++) + v = CRC_UPDATE_BYTE_2(v, *p); + return v; +} + +UInt32 MY_FAST_CALL CrcUpdateT8(UInt32 v, const void *data, size_t size, const UInt32 *table) +{ + return CrcUpdateT4(v, data, size, table); +} + +#endif diff --git a/fex/7z_C/7zDec.c b/fex/7z_C/7zDec.c new file mode 100644 index 0000000..a79ed5b --- /dev/null +++ b/fex/7z_C/7zDec.c @@ -0,0 +1,470 @@ +/* 7zDec.c -- Decoding from 7z folder +2010-11-02 : Igor Pavlov : Public domain */ + +#include + +#define _7ZIP_PPMD_SUPPPORT + +#include "7z.h" + +#include "Bcj2.h" +#include "Bra.h" +#include "CpuArch.h" +#include "LzmaDec.h" +#include "Lzma2Dec.h" +#ifdef _7ZIP_PPMD_SUPPPORT +#include "Ppmd7.h" +#endif + +#define k_Copy 0 +#define k_LZMA2 0x21 +#define k_LZMA 0x30101 +#define k_BCJ 0x03030103 +#define k_PPC 0x03030205 +#define k_ARM 0x03030501 +#define k_ARMT 0x03030701 +#define k_SPARC 0x03030805 +#define k_BCJ2 0x0303011B + +#ifdef _7ZIP_PPMD_SUPPPORT + +#define k_PPMD 0x30401 + +typedef struct +{ + IByteIn p; + const Byte *cur; + const Byte *end; + const Byte *begin; + UInt64 processed; + Bool extra; + SRes res; + ILookInStream *inStream; +} CByteInToLook; + +static Byte ReadByte(void *pp) +{ + CByteInToLook *p = (CByteInToLook *)pp; + if (p->cur != p->end) + return *p->cur++; + if (p->res == SZ_OK) + { + size_t size = p->cur - p->begin; + p->processed += size; + p->res = p->inStream->Skip(p->inStream, size); + size = (1 << 25); + p->res = p->inStream->Look(p->inStream, (const void **)&p->begin, &size); + p->cur = p->begin; + p->end = p->begin + size; + if (size != 0) + return *p->cur++;; + } + p->extra = True; + return 0; +} + +static SRes SzDecodePpmd(CSzCoderInfo *coder, UInt64 inSize, ILookInStream *inStream, + Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain) +{ + CPpmd7 ppmd; + CByteInToLook s; + SRes res = SZ_OK; + + s.p.Read = ReadByte; + s.inStream = inStream; + s.begin = s.end = s.cur = NULL; + s.extra = False; + s.res = SZ_OK; + s.processed = 0; + + if (coder->Props.size != 5) + return SZ_ERROR_UNSUPPORTED; + + { + unsigned order = coder->Props.data[0]; + UInt32 memSize = GetUi32(coder->Props.data + 1); + if (order < PPMD7_MIN_ORDER || + order > PPMD7_MAX_ORDER || + memSize < PPMD7_MIN_MEM_SIZE || + memSize > PPMD7_MAX_MEM_SIZE) + return SZ_ERROR_UNSUPPORTED; + Ppmd7_Construct(&ppmd); + if (!Ppmd7_Alloc(&ppmd, memSize, allocMain)) + return SZ_ERROR_MEM; + Ppmd7_Init(&ppmd, order); + } + { + CPpmd7z_RangeDec rc; + Ppmd7z_RangeDec_CreateVTable(&rc); + rc.Stream = &s.p; + if (!Ppmd7z_RangeDec_Init(&rc)) + res = SZ_ERROR_DATA; + else if (s.extra) + res = (s.res != SZ_OK ? s.res : SZ_ERROR_DATA); + else + { + SizeT i; + for (i = 0; i < outSize; i++) + { + int sym = Ppmd7_DecodeSymbol(&ppmd, &rc.p); + if (s.extra || sym < 0) + break; + outBuffer[i] = (Byte)sym; + } + if (i != outSize) + res = (s.res != SZ_OK ? s.res : SZ_ERROR_DATA); + else if (s.processed + (s.cur - s.begin) != inSize || !Ppmd7z_RangeDec_IsFinishedOK(&rc)) + res = SZ_ERROR_DATA; + } + } + Ppmd7_Free(&ppmd, allocMain); + return res; +} + +#endif + + +static SRes SzDecodeLzma(CSzCoderInfo *coder, UInt64 inSize, ILookInStream *inStream, + Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain) +{ + CLzmaDec state; + SRes res = SZ_OK; + + LzmaDec_Construct(&state); + RINOK(LzmaDec_AllocateProbs(&state, coder->Props.data, (unsigned)coder->Props.size, allocMain)); + state.dic = outBuffer; + state.dicBufSize = outSize; + LzmaDec_Init(&state); + + for (;;) + { + Byte *inBuf = NULL; + size_t lookahead = (1 << 18); + if (lookahead > inSize) + lookahead = (size_t)inSize; + res = inStream->Look((void *)inStream, (const void **)&inBuf, &lookahead); + if (res != SZ_OK) + break; + + { + SizeT inProcessed = (SizeT)lookahead, dicPos = state.dicPos; + ELzmaStatus status; + res = LzmaDec_DecodeToDic(&state, outSize, inBuf, &inProcessed, LZMA_FINISH_END, &status); + lookahead -= inProcessed; + inSize -= inProcessed; + if (res != SZ_OK) + break; + if (state.dicPos == state.dicBufSize || (inProcessed == 0 && dicPos == state.dicPos)) + { + if (state.dicBufSize != outSize || lookahead != 0 || + (status != LZMA_STATUS_FINISHED_WITH_MARK && + status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK)) + res = SZ_ERROR_DATA; + break; + } + res = inStream->Skip((void *)inStream, inProcessed); + if (res != SZ_OK) + break; + } + } + + LzmaDec_FreeProbs(&state, allocMain); + return res; +} + +static SRes SzDecodeLzma2(CSzCoderInfo *coder, UInt64 inSize, ILookInStream *inStream, + Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain) +{ + CLzma2Dec state; + SRes res = SZ_OK; + + Lzma2Dec_Construct(&state); + if (coder->Props.size != 1) + return SZ_ERROR_DATA; + RINOK(Lzma2Dec_AllocateProbs(&state, coder->Props.data[0], allocMain)); + state.decoder.dic = outBuffer; + state.decoder.dicBufSize = outSize; + Lzma2Dec_Init(&state); + + for (;;) + { + Byte *inBuf = NULL; + size_t lookahead = (1 << 18); + if (lookahead > inSize) + lookahead = (size_t)inSize; + res = inStream->Look((void *)inStream, (const void **)&inBuf, &lookahead); + if (res != SZ_OK) + break; + + { + SizeT inProcessed = (SizeT)lookahead, dicPos = state.decoder.dicPos; + ELzmaStatus status; + res = Lzma2Dec_DecodeToDic(&state, outSize, inBuf, &inProcessed, LZMA_FINISH_END, &status); + lookahead -= inProcessed; + inSize -= inProcessed; + if (res != SZ_OK) + break; + if (state.decoder.dicPos == state.decoder.dicBufSize || (inProcessed == 0 && dicPos == state.decoder.dicPos)) + { + if (state.decoder.dicBufSize != outSize || lookahead != 0 || + (status != LZMA_STATUS_FINISHED_WITH_MARK)) + res = SZ_ERROR_DATA; + break; + } + res = inStream->Skip((void *)inStream, inProcessed); + if (res != SZ_OK) + break; + } + } + + Lzma2Dec_FreeProbs(&state, allocMain); + return res; +} + +static SRes SzDecodeCopy(UInt64 inSize, ILookInStream *inStream, Byte *outBuffer) +{ + while (inSize > 0) + { + void *inBuf; + size_t curSize = (1 << 18); + if (curSize > inSize) + curSize = (size_t)inSize; + RINOK(inStream->Look((void *)inStream, (const void **)&inBuf, &curSize)); + if (curSize == 0) + return SZ_ERROR_INPUT_EOF; + memcpy(outBuffer, inBuf, curSize); + outBuffer += curSize; + inSize -= curSize; + RINOK(inStream->Skip((void *)inStream, curSize)); + } + return SZ_OK; +} + +static Bool IS_MAIN_METHOD(UInt32 m) +{ + switch(m) + { + case k_Copy: + case k_LZMA: + case k_LZMA2: + #ifdef _7ZIP_PPMD_SUPPPORT + case k_PPMD: + #endif + return True; + } + return False; +} + +static Bool IS_SUPPORTED_CODER(const CSzCoderInfo *c) +{ + return + c->NumInStreams == 1 && + c->NumOutStreams == 1 && + c->MethodID <= (UInt32)0xFFFFFFFF && + IS_MAIN_METHOD((UInt32)c->MethodID); +} + +#define IS_BCJ2(c) ((c)->MethodID == k_BCJ2 && (c)->NumInStreams == 4 && (c)->NumOutStreams == 1) + +static SRes CheckSupportedFolder(const CSzFolder *f) +{ + if (f->NumCoders < 1 || f->NumCoders > 4) + return SZ_ERROR_UNSUPPORTED; + if (!IS_SUPPORTED_CODER(&f->Coders[0])) + return SZ_ERROR_UNSUPPORTED; + if (f->NumCoders == 1) + { + if (f->NumPackStreams != 1 || f->PackStreams[0] != 0 || f->NumBindPairs != 0) + return SZ_ERROR_UNSUPPORTED; + return SZ_OK; + } + if (f->NumCoders == 2) + { + CSzCoderInfo *c = &f->Coders[1]; + if (c->MethodID > (UInt32)0xFFFFFFFF || + c->NumInStreams != 1 || + c->NumOutStreams != 1 || + f->NumPackStreams != 1 || + f->PackStreams[0] != 0 || + f->NumBindPairs != 1 || + f->BindPairs[0].InIndex != 1 || + f->BindPairs[0].OutIndex != 0) + return SZ_ERROR_UNSUPPORTED; + switch ((UInt32)c->MethodID) + { + case k_BCJ: + case k_ARM: + break; + default: + return SZ_ERROR_UNSUPPORTED; + } + return SZ_OK; + } + if (f->NumCoders == 4) + { + if (!IS_SUPPORTED_CODER(&f->Coders[1]) || + !IS_SUPPORTED_CODER(&f->Coders[2]) || + !IS_BCJ2(&f->Coders[3])) + return SZ_ERROR_UNSUPPORTED; + if (f->NumPackStreams != 4 || + f->PackStreams[0] != 2 || + f->PackStreams[1] != 6 || + f->PackStreams[2] != 1 || + f->PackStreams[3] != 0 || + f->NumBindPairs != 3 || + f->BindPairs[0].InIndex != 5 || f->BindPairs[0].OutIndex != 0 || + f->BindPairs[1].InIndex != 4 || f->BindPairs[1].OutIndex != 1 || + f->BindPairs[2].InIndex != 3 || f->BindPairs[2].OutIndex != 2) + return SZ_ERROR_UNSUPPORTED; + return SZ_OK; + } + return SZ_ERROR_UNSUPPORTED; +} + +static UInt64 GetSum(const UInt64 *values, UInt32 index) +{ + UInt64 sum = 0; + UInt32 i; + for (i = 0; i < index; i++) + sum += values[i]; + return sum; +} + +#define CASE_BRA_CONV(isa) case k_ ## isa: isa ## _Convert(outBuffer, outSize, 0, 0); break; + +static SRes SzFolder_Decode2(const CSzFolder *folder, const UInt64 *packSizes, + ILookInStream *inStream, UInt64 startPos, + Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain, + Byte *tempBuf[]) +{ + UInt32 ci; + SizeT tempSizes[3] = { 0, 0, 0}; + SizeT tempSize3 = 0; + Byte *tempBuf3 = 0; + + RINOK(CheckSupportedFolder(folder)); + + for (ci = 0; ci < folder->NumCoders; ci++) + { + CSzCoderInfo *coder = &folder->Coders[ci]; + + if (IS_MAIN_METHOD((UInt32)coder->MethodID)) + { + UInt32 si = 0; + UInt64 offset; + UInt64 inSize; + Byte *outBufCur = outBuffer; + SizeT outSizeCur = outSize; + if (folder->NumCoders == 4) + { + UInt32 indices[] = { 3, 2, 0 }; + UInt64 unpackSize = folder->UnpackSizes[ci]; + si = indices[ci]; + if (ci < 2) + { + Byte *temp; + outSizeCur = (SizeT)unpackSize; + if (outSizeCur != unpackSize) + return SZ_ERROR_MEM; + temp = (Byte *)IAlloc_Alloc(allocMain, outSizeCur); + if (temp == 0 && outSizeCur != 0) + return SZ_ERROR_MEM; + outBufCur = tempBuf[1 - ci] = temp; + tempSizes[1 - ci] = outSizeCur; + } + else if (ci == 2) + { + if (unpackSize > outSize) /* check it */ + return SZ_ERROR_PARAM; + tempBuf3 = outBufCur = outBuffer + (outSize - (size_t)unpackSize); + tempSize3 = outSizeCur = (SizeT)unpackSize; + } + else + return SZ_ERROR_UNSUPPORTED; + } + offset = GetSum(packSizes, si); + inSize = packSizes[si]; + RINOK(LookInStream_SeekTo(inStream, startPos + offset)); + + if (coder->MethodID == k_Copy) + { + if (inSize != outSizeCur) /* check it */ + return SZ_ERROR_DATA; + RINOK(SzDecodeCopy(inSize, inStream, outBufCur)); + } + else if (coder->MethodID == k_LZMA) + { + RINOK(SzDecodeLzma(coder, inSize, inStream, outBufCur, outSizeCur, allocMain)); + } + else if (coder->MethodID == k_LZMA2) + { + RINOK(SzDecodeLzma2(coder, inSize, inStream, outBufCur, outSizeCur, allocMain)); + } + else + { + #ifdef _7ZIP_PPMD_SUPPPORT + RINOK(SzDecodePpmd(coder, inSize, inStream, outBufCur, outSizeCur, allocMain)); + #else + return SZ_ERROR_UNSUPPORTED; + #endif + } + } + else if (coder->MethodID == k_BCJ2) + { + UInt64 offset = GetSum(packSizes, 1); + UInt64 s3Size = packSizes[1]; + SRes res; + if (ci != 3) + return SZ_ERROR_UNSUPPORTED; + RINOK(LookInStream_SeekTo(inStream, startPos + offset)); + tempSizes[2] = (SizeT)s3Size; + if (tempSizes[2] != s3Size) + return SZ_ERROR_MEM; + tempBuf[2] = (Byte *)IAlloc_Alloc(allocMain, tempSizes[2]); + if (tempBuf[2] == 0 && tempSizes[2] != 0) + return SZ_ERROR_MEM; + res = SzDecodeCopy(s3Size, inStream, tempBuf[2]); + RINOK(res) + + res = Bcj2_Decode( + tempBuf3, tempSize3, + tempBuf[0], tempSizes[0], + tempBuf[1], tempSizes[1], + tempBuf[2], tempSizes[2], + outBuffer, outSize); + RINOK(res) + } + else + { + if (ci != 1) + return SZ_ERROR_UNSUPPORTED; + switch(coder->MethodID) + { + case k_BCJ: + { + UInt32 state; + x86_Convert_Init(state); + x86_Convert(outBuffer, outSize, 0, &state, 0); + break; + } + CASE_BRA_CONV(ARM) + default: + return SZ_ERROR_UNSUPPORTED; + } + } + } + return SZ_OK; +} + +SRes SzFolder_Decode(const CSzFolder *folder, const UInt64 *packSizes, + ILookInStream *inStream, UInt64 startPos, + Byte *outBuffer, size_t outSize, ISzAlloc *allocMain) +{ + Byte *tempBuf[3] = { 0, 0, 0}; + int i; + SRes res = SzFolder_Decode2(folder, packSizes, inStream, startPos, + outBuffer, (SizeT)outSize, allocMain, tempBuf); + for (i = 0; i < 3; i++) + IAlloc_Free(allocMain, tempBuf[i]); + return res; +} diff --git a/fex/7z_C/7zIn.c b/fex/7z_C/7zIn.c new file mode 100644 index 0000000..ec93a43 --- /dev/null +++ b/fex/7z_C/7zIn.c @@ -0,0 +1,1402 @@ +/* 7zIn.c -- 7z Input functions +2010-10-29 : Igor Pavlov : Public domain */ + +#include + +#include "7z.h" +#include "7zCrc.h" +#include "CpuArch.h" + +Byte k7zSignature[k7zSignatureSize] = {'7', 'z', 0xBC, 0xAF, 0x27, 0x1C}; + +#define RINOM(x) { if ((x) == 0) return SZ_ERROR_MEM; } + +#define NUM_FOLDER_CODERS_MAX 32 +#define NUM_CODER_STREAMS_MAX 32 + +void SzCoderInfo_Init(CSzCoderInfo *p) +{ + Buf_Init(&p->Props); +} + +void SzCoderInfo_Free(CSzCoderInfo *p, ISzAlloc *alloc) +{ + Buf_Free(&p->Props, alloc); + SzCoderInfo_Init(p); +} + +void SzFolder_Init(CSzFolder *p) +{ + p->Coders = 0; + p->BindPairs = 0; + p->PackStreams = 0; + p->UnpackSizes = 0; + p->NumCoders = 0; + p->NumBindPairs = 0; + p->NumPackStreams = 0; + p->UnpackCRCDefined = 0; + p->UnpackCRC = 0; + p->NumUnpackStreams = 0; +} + +void SzFolder_Free(CSzFolder *p, ISzAlloc *alloc) +{ + UInt32 i; + if (p->Coders) + for (i = 0; i < p->NumCoders; i++) + SzCoderInfo_Free(&p->Coders[i], alloc); + IAlloc_Free(alloc, p->Coders); + IAlloc_Free(alloc, p->BindPairs); + IAlloc_Free(alloc, p->PackStreams); + IAlloc_Free(alloc, p->UnpackSizes); + SzFolder_Init(p); +} + +UInt32 SzFolder_GetNumOutStreams(CSzFolder *p) +{ + UInt32 result = 0; + UInt32 i; + for (i = 0; i < p->NumCoders; i++) + result += p->Coders[i].NumOutStreams; + return result; +} + +int SzFolder_FindBindPairForInStream(CSzFolder *p, UInt32 inStreamIndex) +{ + UInt32 i; + for (i = 0; i < p->NumBindPairs; i++) + if (p->BindPairs[i].InIndex == inStreamIndex) + return i; + return -1; +} + + +int SzFolder_FindBindPairForOutStream(CSzFolder *p, UInt32 outStreamIndex) +{ + UInt32 i; + for (i = 0; i < p->NumBindPairs; i++) + if (p->BindPairs[i].OutIndex == outStreamIndex) + return i; + return -1; +} + +UInt64 SzFolder_GetUnpackSize(CSzFolder *p) +{ + int i = (int)SzFolder_GetNumOutStreams(p); + if (i == 0) + return 0; + for (i--; i >= 0; i--) + if (SzFolder_FindBindPairForOutStream(p, i) < 0) + return p->UnpackSizes[i]; + /* throw 1; */ + return 0; +} + +void SzFile_Init(CSzFileItem *p) +{ + p->HasStream = 1; + p->IsDir = 0; + p->IsAnti = 0; + p->CrcDefined = 0; + p->MTimeDefined = 0; +} + +void SzAr_Init(CSzAr *p) +{ + p->PackSizes = 0; + p->PackCRCsDefined = 0; + p->PackCRCs = 0; + p->Folders = 0; + p->Files = 0; + p->NumPackStreams = 0; + p->NumFolders = 0; + p->NumFiles = 0; +} + +void SzAr_Free(CSzAr *p, ISzAlloc *alloc) +{ + UInt32 i; + if (p->Folders) + for (i = 0; i < p->NumFolders; i++) + SzFolder_Free(&p->Folders[i], alloc); + + IAlloc_Free(alloc, p->PackSizes); + IAlloc_Free(alloc, p->PackCRCsDefined); + IAlloc_Free(alloc, p->PackCRCs); + IAlloc_Free(alloc, p->Folders); + IAlloc_Free(alloc, p->Files); + SzAr_Init(p); +} + + +void SzArEx_Init(CSzArEx *p) +{ + SzAr_Init(&p->db); + p->FolderStartPackStreamIndex = 0; + p->PackStreamStartPositions = 0; + p->FolderStartFileIndex = 0; + p->FileIndexToFolderIndexMap = 0; + p->FileNameOffsets = 0; + Buf_Init(&p->FileNames); +} + +void SzArEx_Free(CSzArEx *p, ISzAlloc *alloc) +{ + IAlloc_Free(alloc, p->FolderStartPackStreamIndex); + IAlloc_Free(alloc, p->PackStreamStartPositions); + IAlloc_Free(alloc, p->FolderStartFileIndex); + IAlloc_Free(alloc, p->FileIndexToFolderIndexMap); + + IAlloc_Free(alloc, p->FileNameOffsets); + Buf_Free(&p->FileNames, alloc); + + SzAr_Free(&p->db, alloc); + SzArEx_Init(p); +} + +/* +UInt64 GetFolderPackStreamSize(int folderIndex, int streamIndex) const +{ + return PackSizes[FolderStartPackStreamIndex[folderIndex] + streamIndex]; +} + +UInt64 GetFilePackSize(int fileIndex) const +{ + int folderIndex = FileIndexToFolderIndexMap[fileIndex]; + if (folderIndex >= 0) + { + const CSzFolder &folderInfo = Folders[folderIndex]; + if (FolderStartFileIndex[folderIndex] == fileIndex) + return GetFolderFullPackSize(folderIndex); + } + return 0; +} +*/ + +#define MY_ALLOC(T, p, size, alloc) { if ((size) == 0) p = 0; else \ + if ((p = (T *)IAlloc_Alloc(alloc, (size) * sizeof(T))) == 0) return SZ_ERROR_MEM; } + +static SRes SzArEx_Fill(CSzArEx *p, ISzAlloc *alloc) +{ + UInt32 startPos = 0; + UInt64 startPosSize = 0; + UInt32 i; + UInt32 folderIndex = 0; + UInt32 indexInFolder = 0; + MY_ALLOC(UInt32, p->FolderStartPackStreamIndex, p->db.NumFolders, alloc); + for (i = 0; i < p->db.NumFolders; i++) + { + p->FolderStartPackStreamIndex[i] = startPos; + startPos += p->db.Folders[i].NumPackStreams; + } + + MY_ALLOC(UInt64, p->PackStreamStartPositions, p->db.NumPackStreams, alloc); + + for (i = 0; i < p->db.NumPackStreams; i++) + { + p->PackStreamStartPositions[i] = startPosSize; + startPosSize += p->db.PackSizes[i]; + } + + MY_ALLOC(UInt32, p->FolderStartFileIndex, p->db.NumFolders, alloc); + MY_ALLOC(UInt32, p->FileIndexToFolderIndexMap, p->db.NumFiles, alloc); + + for (i = 0; i < p->db.NumFiles; i++) + { + CSzFileItem *file = p->db.Files + i; + int emptyStream = !file->HasStream; + if (emptyStream && indexInFolder == 0) + { + p->FileIndexToFolderIndexMap[i] = (UInt32)-1; + continue; + } + if (indexInFolder == 0) + { + /* + v3.13 incorrectly worked with empty folders + v4.07: Loop for skipping empty folders + */ + for (;;) + { + if (folderIndex >= p->db.NumFolders) + return SZ_ERROR_ARCHIVE; + p->FolderStartFileIndex[folderIndex] = i; + if (p->db.Folders[folderIndex].NumUnpackStreams != 0) + break; + folderIndex++; + } + } + p->FileIndexToFolderIndexMap[i] = folderIndex; + if (emptyStream) + continue; + indexInFolder++; + if (indexInFolder >= p->db.Folders[folderIndex].NumUnpackStreams) + { + folderIndex++; + indexInFolder = 0; + } + } + return SZ_OK; +} + + +UInt64 SzArEx_GetFolderStreamPos(const CSzArEx *p, UInt32 folderIndex, UInt32 indexInFolder) +{ + return p->dataPos + + p->PackStreamStartPositions[p->FolderStartPackStreamIndex[folderIndex] + indexInFolder]; +} + +int SzArEx_GetFolderFullPackSize(const CSzArEx *p, UInt32 folderIndex, UInt64 *resSize) +{ + UInt32 packStreamIndex = p->FolderStartPackStreamIndex[folderIndex]; + CSzFolder *folder = p->db.Folders + folderIndex; + UInt64 size = 0; + UInt32 i; + for (i = 0; i < folder->NumPackStreams; i++) + { + UInt64 t = size + p->db.PackSizes[packStreamIndex + i]; + if (t < size) /* check it */ + return SZ_ERROR_FAIL; + size = t; + } + *resSize = size; + return SZ_OK; +} + + +/* +SRes SzReadTime(const CObjectVector &dataVector, + CObjectVector &files, UInt64 type) +{ + CBoolVector boolVector; + RINOK(ReadBoolVector2(files.Size(), boolVector)) + + CStreamSwitch streamSwitch; + RINOK(streamSwitch.Set(this, &dataVector)); + + for (int i = 0; i < files.Size(); i++) + { + CSzFileItem &file = files[i]; + CArchiveFileTime fileTime; + bool defined = boolVector[i]; + if (defined) + { + UInt32 low, high; + RINOK(SzReadUInt32(low)); + RINOK(SzReadUInt32(high)); + fileTime.dwLowDateTime = low; + fileTime.dwHighDateTime = high; + } + switch(type) + { + case k7zIdCTime: file.IsCTimeDefined = defined; if (defined) file.CTime = fileTime; break; + case k7zIdATime: file.IsATimeDefined = defined; if (defined) file.ATime = fileTime; break; + case k7zIdMTime: file.IsMTimeDefined = defined; if (defined) file.MTime = fileTime; break; + } + } + return SZ_OK; +} +*/ + +static int TestSignatureCandidate(Byte *testBytes) +{ + size_t i; + for (i = 0; i < k7zSignatureSize; i++) + if (testBytes[i] != k7zSignature[i]) + return 0; + return 1; +} + +typedef struct _CSzState +{ + Byte *Data; + size_t Size; +}CSzData; + +static SRes SzReadByte(CSzData *sd, Byte *b) +{ + if (sd->Size == 0) + return SZ_ERROR_ARCHIVE; + sd->Size--; + *b = *sd->Data++; + return SZ_OK; +} + +static SRes SzReadBytes(CSzData *sd, Byte *data, size_t size) +{ + size_t i; + for (i = 0; i < size; i++) + { + RINOK(SzReadByte(sd, data + i)); + } + return SZ_OK; +} + +static SRes SzReadUInt32(CSzData *sd, UInt32 *value) +{ + int i; + *value = 0; + for (i = 0; i < 4; i++) + { + Byte b; + RINOK(SzReadByte(sd, &b)); + *value |= ((UInt32)(b) << (8 * i)); + } + return SZ_OK; +} + +static SRes SzReadNumber(CSzData *sd, UInt64 *value) +{ + Byte firstByte; + Byte mask = 0x80; + int i; + RINOK(SzReadByte(sd, &firstByte)); + *value = 0; + for (i = 0; i < 8; i++) + { + Byte b; + if ((firstByte & mask) == 0) + { + UInt64 highPart = firstByte & (mask - 1); + *value += (highPart << (8 * i)); + return SZ_OK; + } + RINOK(SzReadByte(sd, &b)); + *value |= ((UInt64)b << (8 * i)); + mask >>= 1; + } + return SZ_OK; +} + +static SRes SzReadNumber32(CSzData *sd, UInt32 *value) +{ + UInt64 value64; + RINOK(SzReadNumber(sd, &value64)); + if (value64 >= 0x80000000) + return SZ_ERROR_UNSUPPORTED; + if (value64 >= ((UInt64)(1) << ((sizeof(size_t) - 1) * 8 + 2))) + return SZ_ERROR_UNSUPPORTED; + *value = (UInt32)value64; + return SZ_OK; +} + +static SRes SzReadID(CSzData *sd, UInt64 *value) +{ + return SzReadNumber(sd, value); +} + +static SRes SzSkeepDataSize(CSzData *sd, UInt64 size) +{ + if (size > sd->Size) + return SZ_ERROR_ARCHIVE; + sd->Size -= (size_t)size; + sd->Data += (size_t)size; + return SZ_OK; +} + +static SRes SzSkeepData(CSzData *sd) +{ + UInt64 size; + RINOK(SzReadNumber(sd, &size)); + return SzSkeepDataSize(sd, size); +} + +static SRes SzReadArchiveProperties(CSzData *sd) +{ + for (;;) + { + UInt64 type; + RINOK(SzReadID(sd, &type)); + if (type == k7zIdEnd) + break; + SzSkeepData(sd); + } + return SZ_OK; +} + +static SRes SzWaitAttribute(CSzData *sd, UInt64 attribute) +{ + for (;;) + { + UInt64 type; + RINOK(SzReadID(sd, &type)); + if (type == attribute) + return SZ_OK; + if (type == k7zIdEnd) + return SZ_ERROR_ARCHIVE; + RINOK(SzSkeepData(sd)); + } +} + +static SRes SzReadBoolVector(CSzData *sd, size_t numItems, Byte **v, ISzAlloc *alloc) +{ + Byte b = 0; + Byte mask = 0; + size_t i; + MY_ALLOC(Byte, *v, numItems, alloc); + for (i = 0; i < numItems; i++) + { + if (mask == 0) + { + RINOK(SzReadByte(sd, &b)); + mask = 0x80; + } + (*v)[i] = (Byte)(((b & mask) != 0) ? 1 : 0); + mask >>= 1; + } + return SZ_OK; +} + +static SRes SzReadBoolVector2(CSzData *sd, size_t numItems, Byte **v, ISzAlloc *alloc) +{ + Byte allAreDefined; + size_t i; + RINOK(SzReadByte(sd, &allAreDefined)); + if (allAreDefined == 0) + return SzReadBoolVector(sd, numItems, v, alloc); + MY_ALLOC(Byte, *v, numItems, alloc); + for (i = 0; i < numItems; i++) + (*v)[i] = 1; + return SZ_OK; +} + +static SRes SzReadHashDigests( + CSzData *sd, + size_t numItems, + Byte **digestsDefined, + UInt32 **digests, + ISzAlloc *alloc) +{ + size_t i; + RINOK(SzReadBoolVector2(sd, numItems, digestsDefined, alloc)); + MY_ALLOC(UInt32, *digests, numItems, alloc); + for (i = 0; i < numItems; i++) + if ((*digestsDefined)[i]) + { + RINOK(SzReadUInt32(sd, (*digests) + i)); + } + return SZ_OK; +} + +static SRes SzReadPackInfo( + CSzData *sd, + UInt64 *dataOffset, + UInt32 *numPackStreams, + UInt64 **packSizes, + Byte **packCRCsDefined, + UInt32 **packCRCs, + ISzAlloc *alloc) +{ + UInt32 i; + RINOK(SzReadNumber(sd, dataOffset)); + RINOK(SzReadNumber32(sd, numPackStreams)); + + RINOK(SzWaitAttribute(sd, k7zIdSize)); + + MY_ALLOC(UInt64, *packSizes, (size_t)*numPackStreams, alloc); + + for (i = 0; i < *numPackStreams; i++) + { + RINOK(SzReadNumber(sd, (*packSizes) + i)); + } + + for (;;) + { + UInt64 type; + RINOK(SzReadID(sd, &type)); + if (type == k7zIdEnd) + break; + if (type == k7zIdCRC) + { + RINOK(SzReadHashDigests(sd, (size_t)*numPackStreams, packCRCsDefined, packCRCs, alloc)); + continue; + } + RINOK(SzSkeepData(sd)); + } + if (*packCRCsDefined == 0) + { + MY_ALLOC(Byte, *packCRCsDefined, (size_t)*numPackStreams, alloc); + MY_ALLOC(UInt32, *packCRCs, (size_t)*numPackStreams, alloc); + for (i = 0; i < *numPackStreams; i++) + { + (*packCRCsDefined)[i] = 0; + (*packCRCs)[i] = 0; + } + } + return SZ_OK; +} + +static SRes SzReadSwitch(CSzData *sd) +{ + Byte external; + RINOK(SzReadByte(sd, &external)); + return (external == 0) ? SZ_OK: SZ_ERROR_UNSUPPORTED; +} + +static SRes SzGetNextFolderItem(CSzData *sd, CSzFolder *folder, ISzAlloc *alloc) +{ + UInt32 numCoders, numBindPairs, numPackStreams, i; + UInt32 numInStreams = 0, numOutStreams = 0; + + RINOK(SzReadNumber32(sd, &numCoders)); + if (numCoders > NUM_FOLDER_CODERS_MAX) + return SZ_ERROR_UNSUPPORTED; + folder->NumCoders = numCoders; + + MY_ALLOC(CSzCoderInfo, folder->Coders, (size_t)numCoders, alloc); + + for (i = 0; i < numCoders; i++) + SzCoderInfo_Init(folder->Coders + i); + + for (i = 0; i < numCoders; i++) + { + Byte mainByte; + CSzCoderInfo *coder = folder->Coders + i; + { + unsigned idSize, j; + Byte longID[15]; + RINOK(SzReadByte(sd, &mainByte)); + idSize = (unsigned)(mainByte & 0xF); + RINOK(SzReadBytes(sd, longID, idSize)); + if (idSize > sizeof(coder->MethodID)) + return SZ_ERROR_UNSUPPORTED; + coder->MethodID = 0; + for (j = 0; j < idSize; j++) + coder->MethodID |= (UInt64)longID[idSize - 1 - j] << (8 * j); + + if ((mainByte & 0x10) != 0) + { + RINOK(SzReadNumber32(sd, &coder->NumInStreams)); + RINOK(SzReadNumber32(sd, &coder->NumOutStreams)); + if (coder->NumInStreams > NUM_CODER_STREAMS_MAX || + coder->NumOutStreams > NUM_CODER_STREAMS_MAX) + return SZ_ERROR_UNSUPPORTED; + } + else + { + coder->NumInStreams = 1; + coder->NumOutStreams = 1; + } + if ((mainByte & 0x20) != 0) + { + UInt64 propertiesSize = 0; + RINOK(SzReadNumber(sd, &propertiesSize)); + if (!Buf_Create(&coder->Props, (size_t)propertiesSize, alloc)) + return SZ_ERROR_MEM; + RINOK(SzReadBytes(sd, coder->Props.data, (size_t)propertiesSize)); + } + } + while ((mainByte & 0x80) != 0) + { + RINOK(SzReadByte(sd, &mainByte)); + RINOK(SzSkeepDataSize(sd, (mainByte & 0xF))); + if ((mainByte & 0x10) != 0) + { + UInt32 n; + RINOK(SzReadNumber32(sd, &n)); + RINOK(SzReadNumber32(sd, &n)); + } + if ((mainByte & 0x20) != 0) + { + UInt64 propertiesSize = 0; + RINOK(SzReadNumber(sd, &propertiesSize)); + RINOK(SzSkeepDataSize(sd, propertiesSize)); + } + } + numInStreams += coder->NumInStreams; + numOutStreams += coder->NumOutStreams; + } + + if (numOutStreams == 0) + return SZ_ERROR_UNSUPPORTED; + + folder->NumBindPairs = numBindPairs = numOutStreams - 1; + MY_ALLOC(CSzBindPair, folder->BindPairs, (size_t)numBindPairs, alloc); + + for (i = 0; i < numBindPairs; i++) + { + CSzBindPair *bp = folder->BindPairs + i; + RINOK(SzReadNumber32(sd, &bp->InIndex)); + RINOK(SzReadNumber32(sd, &bp->OutIndex)); + } + + if (numInStreams < numBindPairs) + return SZ_ERROR_UNSUPPORTED; + + folder->NumPackStreams = numPackStreams = numInStreams - numBindPairs; + MY_ALLOC(UInt32, folder->PackStreams, (size_t)numPackStreams, alloc); + + if (numPackStreams == 1) + { + for (i = 0; i < numInStreams ; i++) + if (SzFolder_FindBindPairForInStream(folder, i) < 0) + break; + if (i == numInStreams) + return SZ_ERROR_UNSUPPORTED; + folder->PackStreams[0] = i; + } + else + for (i = 0; i < numPackStreams; i++) + { + RINOK(SzReadNumber32(sd, folder->PackStreams + i)); + } + return SZ_OK; +} + +static SRes SzReadUnpackInfo( + CSzData *sd, + UInt32 *numFolders, + CSzFolder **folders, /* for alloc */ + ISzAlloc *alloc, + ISzAlloc *allocTemp) +{ + UInt32 i; + RINOK(SzWaitAttribute(sd, k7zIdFolder)); + RINOK(SzReadNumber32(sd, numFolders)); + { + RINOK(SzReadSwitch(sd)); + + MY_ALLOC(CSzFolder, *folders, (size_t)*numFolders, alloc); + + for (i = 0; i < *numFolders; i++) + SzFolder_Init((*folders) + i); + + for (i = 0; i < *numFolders; i++) + { + RINOK(SzGetNextFolderItem(sd, (*folders) + i, alloc)); + } + } + + RINOK(SzWaitAttribute(sd, k7zIdCodersUnpackSize)); + + for (i = 0; i < *numFolders; i++) + { + UInt32 j; + CSzFolder *folder = (*folders) + i; + UInt32 numOutStreams = SzFolder_GetNumOutStreams(folder); + + MY_ALLOC(UInt64, folder->UnpackSizes, (size_t)numOutStreams, alloc); + + for (j = 0; j < numOutStreams; j++) + { + RINOK(SzReadNumber(sd, folder->UnpackSizes + j)); + } + } + + for (;;) + { + UInt64 type; + RINOK(SzReadID(sd, &type)); + if (type == k7zIdEnd) + return SZ_OK; + if (type == k7zIdCRC) + { + SRes res; + Byte *crcsDefined = 0; + UInt32 *crcs = 0; + res = SzReadHashDigests(sd, *numFolders, &crcsDefined, &crcs, allocTemp); + if (res == SZ_OK) + { + for (i = 0; i < *numFolders; i++) + { + CSzFolder *folder = (*folders) + i; + folder->UnpackCRCDefined = crcsDefined[i]; + folder->UnpackCRC = crcs[i]; + } + } + IAlloc_Free(allocTemp, crcs); + IAlloc_Free(allocTemp, crcsDefined); + RINOK(res); + continue; + } + RINOK(SzSkeepData(sd)); + } +} + +static SRes SzReadSubStreamsInfo( + CSzData *sd, + UInt32 numFolders, + CSzFolder *folders, + UInt32 *numUnpackStreams, + UInt64 **unpackSizes, + Byte **digestsDefined, + UInt32 **digests, + ISzAlloc *allocTemp) +{ + UInt64 type = 0; + UInt32 i; + UInt32 si = 0; + UInt32 numDigests = 0; + + for (i = 0; i < numFolders; i++) + folders[i].NumUnpackStreams = 1; + *numUnpackStreams = numFolders; + + for (;;) + { + RINOK(SzReadID(sd, &type)); + if (type == k7zIdNumUnpackStream) + { + *numUnpackStreams = 0; + for (i = 0; i < numFolders; i++) + { + UInt32 numStreams; + RINOK(SzReadNumber32(sd, &numStreams)); + folders[i].NumUnpackStreams = numStreams; + *numUnpackStreams += numStreams; + } + continue; + } + if (type == k7zIdCRC || type == k7zIdSize) + break; + if (type == k7zIdEnd) + break; + RINOK(SzSkeepData(sd)); + } + + if (*numUnpackStreams == 0) + { + *unpackSizes = 0; + *digestsDefined = 0; + *digests = 0; + } + else + { + *unpackSizes = (UInt64 *)IAlloc_Alloc(allocTemp, (size_t)*numUnpackStreams * sizeof(UInt64)); + RINOM(*unpackSizes); + *digestsDefined = (Byte *)IAlloc_Alloc(allocTemp, (size_t)*numUnpackStreams * sizeof(Byte)); + RINOM(*digestsDefined); + *digests = (UInt32 *)IAlloc_Alloc(allocTemp, (size_t)*numUnpackStreams * sizeof(UInt32)); + RINOM(*digests); + } + + for (i = 0; i < numFolders; i++) + { + /* + v3.13 incorrectly worked with empty folders + v4.07: we check that folder is empty + */ + UInt64 sum = 0; + UInt32 j; + UInt32 numSubstreams = folders[i].NumUnpackStreams; + if (numSubstreams == 0) + continue; + if (type == k7zIdSize) + for (j = 1; j < numSubstreams; j++) + { + UInt64 size; + RINOK(SzReadNumber(sd, &size)); + (*unpackSizes)[si++] = size; + sum += size; + } + (*unpackSizes)[si++] = SzFolder_GetUnpackSize(folders + i) - sum; + } + if (type == k7zIdSize) + { + RINOK(SzReadID(sd, &type)); + } + + for (i = 0; i < *numUnpackStreams; i++) + { + (*digestsDefined)[i] = 0; + (*digests)[i] = 0; + } + + + for (i = 0; i < numFolders; i++) + { + UInt32 numSubstreams = folders[i].NumUnpackStreams; + if (numSubstreams != 1 || !folders[i].UnpackCRCDefined) + numDigests += numSubstreams; + } + + + si = 0; + for (;;) + { + if (type == k7zIdCRC) + { + int digestIndex = 0; + Byte *digestsDefined2 = 0; + UInt32 *digests2 = 0; + SRes res = SzReadHashDigests(sd, numDigests, &digestsDefined2, &digests2, allocTemp); + if (res == SZ_OK) + { + for (i = 0; i < numFolders; i++) + { + CSzFolder *folder = folders + i; + UInt32 numSubstreams = folder->NumUnpackStreams; + if (numSubstreams == 1 && folder->UnpackCRCDefined) + { + (*digestsDefined)[si] = 1; + (*digests)[si] = folder->UnpackCRC; + si++; + } + else + { + UInt32 j; + for (j = 0; j < numSubstreams; j++, digestIndex++) + { + (*digestsDefined)[si] = digestsDefined2[digestIndex]; + (*digests)[si] = digests2[digestIndex]; + si++; + } + } + } + } + IAlloc_Free(allocTemp, digestsDefined2); + IAlloc_Free(allocTemp, digests2); + RINOK(res); + } + else if (type == k7zIdEnd) + return SZ_OK; + else + { + RINOK(SzSkeepData(sd)); + } + RINOK(SzReadID(sd, &type)); + } +} + + +static SRes SzReadStreamsInfo( + CSzData *sd, + UInt64 *dataOffset, + CSzAr *p, + UInt32 *numUnpackStreams, + UInt64 **unpackSizes, /* allocTemp */ + Byte **digestsDefined, /* allocTemp */ + UInt32 **digests, /* allocTemp */ + ISzAlloc *alloc, + ISzAlloc *allocTemp) +{ + for (;;) + { + UInt64 type; + RINOK(SzReadID(sd, &type)); + if ((UInt64)(int)type != type) + return SZ_ERROR_UNSUPPORTED; + switch((int)type) + { + case k7zIdEnd: + return SZ_OK; + case k7zIdPackInfo: + { + RINOK(SzReadPackInfo(sd, dataOffset, &p->NumPackStreams, + &p->PackSizes, &p->PackCRCsDefined, &p->PackCRCs, alloc)); + break; + } + case k7zIdUnpackInfo: + { + RINOK(SzReadUnpackInfo(sd, &p->NumFolders, &p->Folders, alloc, allocTemp)); + break; + } + case k7zIdSubStreamsInfo: + { + RINOK(SzReadSubStreamsInfo(sd, p->NumFolders, p->Folders, + numUnpackStreams, unpackSizes, digestsDefined, digests, allocTemp)); + break; + } + default: + return SZ_ERROR_UNSUPPORTED; + } + } +} + +size_t SzArEx_GetFileNameUtf16(const CSzArEx *p, size_t fileIndex, UInt16 *dest) +{ + size_t len = p->FileNameOffsets[fileIndex + 1] - p->FileNameOffsets[fileIndex]; + if (dest != 0) + { + size_t i; + const Byte *src = p->FileNames.data + (p->FileNameOffsets[fileIndex] * 2); + for (i = 0; i < len; i++) + dest[i] = GetUi16(src + i * 2); + } + return len; +} + +static SRes SzReadFileNames(const Byte *p, size_t size, UInt32 numFiles, size_t *sizes) +{ + UInt32 i; + size_t pos = 0; + for (i = 0; i < numFiles; i++) + { + sizes[i] = pos; + for (;;) + { + if (pos >= size) + return SZ_ERROR_ARCHIVE; + if (p[pos * 2] == 0 && p[pos * 2 + 1] == 0) + break; + pos++; + } + pos++; + } + sizes[i] = pos; + return (pos == size) ? SZ_OK : SZ_ERROR_ARCHIVE; +} + +static SRes SzReadHeader2( + CSzArEx *p, /* allocMain */ + CSzData *sd, + UInt64 **unpackSizes, /* allocTemp */ + Byte **digestsDefined, /* allocTemp */ + UInt32 **digests, /* allocTemp */ + Byte **emptyStreamVector, /* allocTemp */ + Byte **emptyFileVector, /* allocTemp */ + Byte **lwtVector, /* allocTemp */ + ISzAlloc *allocMain, + ISzAlloc *allocTemp) +{ + UInt64 type; + UInt32 numUnpackStreams = 0; + UInt32 numFiles = 0; + CSzFileItem *files = 0; + UInt32 numEmptyStreams = 0; + UInt32 i; + + RINOK(SzReadID(sd, &type)); + + if (type == k7zIdArchiveProperties) + { + RINOK(SzReadArchiveProperties(sd)); + RINOK(SzReadID(sd, &type)); + } + + + if (type == k7zIdMainStreamsInfo) + { + RINOK(SzReadStreamsInfo(sd, + &p->dataPos, + &p->db, + &numUnpackStreams, + unpackSizes, + digestsDefined, + digests, allocMain, allocTemp)); + p->dataPos += p->startPosAfterHeader; + RINOK(SzReadID(sd, &type)); + } + + if (type == k7zIdEnd) + return SZ_OK; + if (type != k7zIdFilesInfo) + return SZ_ERROR_ARCHIVE; + + RINOK(SzReadNumber32(sd, &numFiles)); + p->db.NumFiles = numFiles; + + MY_ALLOC(CSzFileItem, files, (size_t)numFiles, allocMain); + + p->db.Files = files; + for (i = 0; i < numFiles; i++) + SzFile_Init(files + i); + + for (;;) + { + UInt64 type; + UInt64 size; + RINOK(SzReadID(sd, &type)); + if (type == k7zIdEnd) + break; + RINOK(SzReadNumber(sd, &size)); + if (size > sd->Size) + return SZ_ERROR_ARCHIVE; + if ((UInt64)(int)type != type) + { + RINOK(SzSkeepDataSize(sd, size)); + } + else + switch((int)type) + { + case k7zIdName: + { + size_t namesSize; + RINOK(SzReadSwitch(sd)); + namesSize = (size_t)size - 1; + if ((namesSize & 1) != 0) + return SZ_ERROR_ARCHIVE; + if (!Buf_Create(&p->FileNames, namesSize, allocMain)) + return SZ_ERROR_MEM; + MY_ALLOC(size_t, p->FileNameOffsets, numFiles + 1, allocMain); + memcpy(p->FileNames.data, sd->Data, namesSize); + RINOK(SzReadFileNames(sd->Data, namesSize >> 1, numFiles, p->FileNameOffsets)) + RINOK(SzSkeepDataSize(sd, namesSize)); + break; + } + case k7zIdEmptyStream: + { + RINOK(SzReadBoolVector(sd, numFiles, emptyStreamVector, allocTemp)); + numEmptyStreams = 0; + for (i = 0; i < numFiles; i++) + if ((*emptyStreamVector)[i]) + numEmptyStreams++; + break; + } + case k7zIdEmptyFile: + { + RINOK(SzReadBoolVector(sd, numEmptyStreams, emptyFileVector, allocTemp)); + break; + } + case k7zIdWinAttributes: + { + RINOK(SzReadBoolVector2(sd, numFiles, lwtVector, allocTemp)); + RINOK(SzReadSwitch(sd)); + for (i = 0; i < numFiles; i++) + { + CSzFileItem *f = &files[i]; + Byte defined = (*lwtVector)[i]; + f->AttribDefined = defined; + f->Attrib = 0; + if (defined) + { + RINOK(SzReadUInt32(sd, &f->Attrib)); + } + } + IAlloc_Free(allocTemp, *lwtVector); + *lwtVector = NULL; + break; + } + case k7zIdMTime: + { + RINOK(SzReadBoolVector2(sd, numFiles, lwtVector, allocTemp)); + RINOK(SzReadSwitch(sd)); + for (i = 0; i < numFiles; i++) + { + CSzFileItem *f = &files[i]; + Byte defined = (*lwtVector)[i]; + f->MTimeDefined = defined; + f->MTime.Low = f->MTime.High = 0; + if (defined) + { + RINOK(SzReadUInt32(sd, &f->MTime.Low)); + RINOK(SzReadUInt32(sd, &f->MTime.High)); + } + } + IAlloc_Free(allocTemp, *lwtVector); + *lwtVector = NULL; + break; + } + default: + { + RINOK(SzSkeepDataSize(sd, size)); + } + } + } + + { + UInt32 emptyFileIndex = 0; + UInt32 sizeIndex = 0; + for (i = 0; i < numFiles; i++) + { + CSzFileItem *file = files + i; + file->IsAnti = 0; + if (*emptyStreamVector == 0) + file->HasStream = 1; + else + file->HasStream = (Byte)((*emptyStreamVector)[i] ? 0 : 1); + if (file->HasStream) + { + file->IsDir = 0; + file->Size = (*unpackSizes)[sizeIndex]; + file->Crc = (*digests)[sizeIndex]; + file->CrcDefined = (Byte)(*digestsDefined)[sizeIndex]; + sizeIndex++; + } + else + { + if (*emptyFileVector == 0) + file->IsDir = 1; + else + file->IsDir = (Byte)((*emptyFileVector)[emptyFileIndex] ? 0 : 1); + emptyFileIndex++; + file->Size = 0; + file->Crc = 0; + file->CrcDefined = 0; + } + } + } + return SzArEx_Fill(p, allocMain); +} + +static SRes SzReadHeader( + CSzArEx *p, + CSzData *sd, + ISzAlloc *allocMain, + ISzAlloc *allocTemp) +{ + UInt64 *unpackSizes = 0; + Byte *digestsDefined = 0; + UInt32 *digests = 0; + Byte *emptyStreamVector = 0; + Byte *emptyFileVector = 0; + Byte *lwtVector = 0; + SRes res = SzReadHeader2(p, sd, + &unpackSizes, &digestsDefined, &digests, + &emptyStreamVector, &emptyFileVector, &lwtVector, + allocMain, allocTemp); + IAlloc_Free(allocTemp, unpackSizes); + IAlloc_Free(allocTemp, digestsDefined); + IAlloc_Free(allocTemp, digests); + IAlloc_Free(allocTemp, emptyStreamVector); + IAlloc_Free(allocTemp, emptyFileVector); + IAlloc_Free(allocTemp, lwtVector); + return res; +} + +static SRes SzReadAndDecodePackedStreams2( + ILookInStream *inStream, + CSzData *sd, + CBuf *outBuffer, + UInt64 baseOffset, + CSzAr *p, + UInt64 **unpackSizes, + Byte **digestsDefined, + UInt32 **digests, + ISzAlloc *allocTemp) +{ + + UInt32 numUnpackStreams = 0; + UInt64 dataStartPos; + CSzFolder *folder; + UInt64 unpackSize; + SRes res; + + RINOK(SzReadStreamsInfo(sd, &dataStartPos, p, + &numUnpackStreams, unpackSizes, digestsDefined, digests, + allocTemp, allocTemp)); + + dataStartPos += baseOffset; + if (p->NumFolders != 1) + return SZ_ERROR_ARCHIVE; + + folder = p->Folders; + unpackSize = SzFolder_GetUnpackSize(folder); + + RINOK(LookInStream_SeekTo(inStream, dataStartPos)); + + if (!Buf_Create(outBuffer, (size_t)unpackSize, allocTemp)) + return SZ_ERROR_MEM; + + res = SzFolder_Decode(folder, p->PackSizes, + inStream, dataStartPos, + outBuffer->data, (size_t)unpackSize, allocTemp); + RINOK(res); + if (folder->UnpackCRCDefined) + if (CrcCalc(outBuffer->data, (size_t)unpackSize) != folder->UnpackCRC) + return SZ_ERROR_CRC; + return SZ_OK; +} + +static SRes SzReadAndDecodePackedStreams( + ILookInStream *inStream, + CSzData *sd, + CBuf *outBuffer, + UInt64 baseOffset, + ISzAlloc *allocTemp) +{ + CSzAr p; + UInt64 *unpackSizes = 0; + Byte *digestsDefined = 0; + UInt32 *digests = 0; + SRes res; + SzAr_Init(&p); + res = SzReadAndDecodePackedStreams2(inStream, sd, outBuffer, baseOffset, + &p, &unpackSizes, &digestsDefined, &digests, + allocTemp); + SzAr_Free(&p, allocTemp); + IAlloc_Free(allocTemp, unpackSizes); + IAlloc_Free(allocTemp, digestsDefined); + IAlloc_Free(allocTemp, digests); + return res; +} + +static SRes SzArEx_Open2( + CSzArEx *p, + ILookInStream *inStream, + ISzAlloc *allocMain, + ISzAlloc *allocTemp) +{ + Byte header[k7zStartHeaderSize]; + Int64 startArcPos; + UInt64 nextHeaderOffset, nextHeaderSize; + size_t nextHeaderSizeT; + UInt32 nextHeaderCRC; + CBuf buffer; + SRes res; + + startArcPos = 0; + RINOK(inStream->Seek(inStream, &startArcPos, SZ_SEEK_CUR)); + + RINOK(LookInStream_Read2(inStream, header, k7zStartHeaderSize, SZ_ERROR_NO_ARCHIVE)); + + if (!TestSignatureCandidate(header)) + return SZ_ERROR_NO_ARCHIVE; + if (header[6] != k7zMajorVersion) + return SZ_ERROR_UNSUPPORTED; + + nextHeaderOffset = GetUi64(header + 12); + nextHeaderSize = GetUi64(header + 20); + nextHeaderCRC = GetUi32(header + 28); + + p->startPosAfterHeader = startArcPos + k7zStartHeaderSize; + + if (CrcCalc(header + 12, 20) != GetUi32(header + 8)) + return SZ_ERROR_CRC; + + nextHeaderSizeT = (size_t)nextHeaderSize; + if (nextHeaderSizeT != nextHeaderSize) + return SZ_ERROR_MEM; + if (nextHeaderSizeT == 0) + return SZ_OK; + if (nextHeaderOffset > nextHeaderOffset + nextHeaderSize || + nextHeaderOffset > nextHeaderOffset + nextHeaderSize + k7zStartHeaderSize) + return SZ_ERROR_NO_ARCHIVE; + + { + Int64 pos = 0; + RINOK(inStream->Seek(inStream, &pos, SZ_SEEK_END)); + if ((UInt64)pos < startArcPos + nextHeaderOffset || + (UInt64)pos < startArcPos + k7zStartHeaderSize + nextHeaderOffset || + (UInt64)pos < startArcPos + k7zStartHeaderSize + nextHeaderOffset + nextHeaderSize) + return SZ_ERROR_INPUT_EOF; + } + + RINOK(LookInStream_SeekTo(inStream, startArcPos + k7zStartHeaderSize + nextHeaderOffset)); + + if (!Buf_Create(&buffer, nextHeaderSizeT, allocTemp)) + return SZ_ERROR_MEM; + + res = LookInStream_Read(inStream, buffer.data, nextHeaderSizeT); + if (res == SZ_OK) + { + res = SZ_ERROR_ARCHIVE; + if (CrcCalc(buffer.data, nextHeaderSizeT) == nextHeaderCRC) + { + CSzData sd; + UInt64 type; + sd.Data = buffer.data; + sd.Size = buffer.size; + res = SzReadID(&sd, &type); + if (res == SZ_OK) + { + if (type == k7zIdEncodedHeader) + { + CBuf outBuffer; + Buf_Init(&outBuffer); + res = SzReadAndDecodePackedStreams(inStream, &sd, &outBuffer, p->startPosAfterHeader, allocTemp); + if (res != SZ_OK) + Buf_Free(&outBuffer, allocTemp); + else + { + Buf_Free(&buffer, allocTemp); + buffer.data = outBuffer.data; + buffer.size = outBuffer.size; + sd.Data = buffer.data; + sd.Size = buffer.size; + res = SzReadID(&sd, &type); + } + } + } + if (res == SZ_OK) + { + if (type == k7zIdHeader) + res = SzReadHeader(p, &sd, allocMain, allocTemp); + else + res = SZ_ERROR_UNSUPPORTED; + } + } + } + Buf_Free(&buffer, allocTemp); + return res; +} + +SRes SzArEx_Open(CSzArEx *p, ILookInStream *inStream, ISzAlloc *allocMain, ISzAlloc *allocTemp) +{ + SRes res = SzArEx_Open2(p, inStream, allocMain, allocTemp); + if (res != SZ_OK) + SzArEx_Free(p, allocMain); + return res; +} + +SRes SzArEx_Extract( + const CSzArEx *p, + ILookInStream *inStream, + UInt32 fileIndex, + UInt32 *blockIndex, + Byte **outBuffer, + size_t *outBufferSize, + size_t *offset, + size_t *outSizeProcessed, + ISzAlloc *allocMain, + ISzAlloc *allocTemp) +{ + UInt32 folderIndex = p->FileIndexToFolderIndexMap[fileIndex]; + SRes res = SZ_OK; + *offset = 0; + *outSizeProcessed = 0; + if (folderIndex == (UInt32)-1) + { + IAlloc_Free(allocMain, *outBuffer); + *blockIndex = folderIndex; + *outBuffer = 0; + *outBufferSize = 0; + return SZ_OK; + } + + if (*outBuffer == 0 || *blockIndex != folderIndex) + { + CSzFolder *folder = p->db.Folders + folderIndex; + UInt64 unpackSizeSpec = SzFolder_GetUnpackSize(folder); + size_t unpackSize = (size_t)unpackSizeSpec; + UInt64 startOffset = SzArEx_GetFolderStreamPos(p, folderIndex, 0); + + if (unpackSize != unpackSizeSpec) + return SZ_ERROR_MEM; + *blockIndex = folderIndex; + IAlloc_Free(allocMain, *outBuffer); + *outBuffer = 0; + + RINOK(LookInStream_SeekTo(inStream, startOffset)); + + if (res == SZ_OK) + { + *outBufferSize = unpackSize; + if (unpackSize != 0) + { + *outBuffer = (Byte *)IAlloc_Alloc(allocMain, unpackSize); + if (*outBuffer == 0) + res = SZ_ERROR_MEM; + } + if (res == SZ_OK) + { + res = SzFolder_Decode(folder, + p->db.PackSizes + p->FolderStartPackStreamIndex[folderIndex], + inStream, startOffset, + *outBuffer, unpackSize, allocTemp); + if (res == SZ_OK) + { + if (folder->UnpackCRCDefined) + { + if (CrcCalc(*outBuffer, unpackSize) != folder->UnpackCRC) + res = SZ_ERROR_CRC; + } + } + } + } + } + if (res == SZ_OK) + { + UInt32 i; + CSzFileItem *fileItem = p->db.Files + fileIndex; + *offset = 0; + for (i = p->FolderStartFileIndex[folderIndex]; i < fileIndex; i++) + *offset += (UInt32)p->db.Files[i].Size; + *outSizeProcessed = (size_t)fileItem->Size; + if (*offset + *outSizeProcessed > *outBufferSize) + return SZ_ERROR_FAIL; + if (fileItem->CrcDefined && CrcCalc(*outBuffer + *offset, *outSizeProcessed) != fileItem->Crc) + res = SZ_ERROR_CRC; + } + return res; +} diff --git a/fex/7z_C/7zStream.c b/fex/7z_C/7zStream.c new file mode 100644 index 0000000..0ebb7b5 --- /dev/null +++ b/fex/7z_C/7zStream.c @@ -0,0 +1,169 @@ +/* 7zStream.c -- 7z Stream functions +2010-03-11 : Igor Pavlov : Public domain */ + +#include + +#include "Types.h" + +SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType) +{ + while (size != 0) + { + size_t processed = size; + RINOK(stream->Read(stream, buf, &processed)); + if (processed == 0) + return errorType; + buf = (void *)((Byte *)buf + processed); + size -= processed; + } + return SZ_OK; +} + +SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size) +{ + return SeqInStream_Read2(stream, buf, size, SZ_ERROR_INPUT_EOF); +} + +SRes SeqInStream_ReadByte(ISeqInStream *stream, Byte *buf) +{ + size_t processed = 1; + RINOK(stream->Read(stream, buf, &processed)); + return (processed == 1) ? SZ_OK : SZ_ERROR_INPUT_EOF; +} + +SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset) +{ + Int64 t = offset; + return stream->Seek(stream, &t, SZ_SEEK_SET); +} + +SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size) +{ + const void *lookBuf; + if (*size == 0) + return SZ_OK; + RINOK(stream->Look(stream, &lookBuf, size)); + memcpy(buf, lookBuf, *size); + return stream->Skip(stream, *size); +} + +SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType) +{ + while (size != 0) + { + size_t processed = size; + RINOK(stream->Read(stream, buf, &processed)); + if (processed == 0) + return errorType; + buf = (void *)((Byte *)buf + processed); + size -= processed; + } + return SZ_OK; +} + +SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size) +{ + return LookInStream_Read2(stream, buf, size, SZ_ERROR_INPUT_EOF); +} + +static SRes LookToRead_Look_Lookahead(void *pp, const void **buf, size_t *size) +{ + SRes res = SZ_OK; + CLookToRead *p = (CLookToRead *)pp; + size_t size2 = p->size - p->pos; + if (size2 == 0 && *size > 0) + { + p->pos = 0; + size2 = LookToRead_BUF_SIZE; + res = p->realStream->Read(p->realStream, p->buf, &size2); + p->size = size2; + } + if (size2 < *size) + *size = size2; + *buf = p->buf + p->pos; + return res; +} + +static SRes LookToRead_Look_Exact(void *pp, const void **buf, size_t *size) +{ + SRes res = SZ_OK; + CLookToRead *p = (CLookToRead *)pp; + size_t size2 = p->size - p->pos; + if (size2 == 0 && *size > 0) + { + p->pos = 0; + if (*size > LookToRead_BUF_SIZE) + *size = LookToRead_BUF_SIZE; + res = p->realStream->Read(p->realStream, p->buf, size); + size2 = p->size = *size; + } + if (size2 < *size) + *size = size2; + *buf = p->buf + p->pos; + return res; +} + +static SRes LookToRead_Skip(void *pp, size_t offset) +{ + CLookToRead *p = (CLookToRead *)pp; + p->pos += offset; + return SZ_OK; +} + +static SRes LookToRead_Read(void *pp, void *buf, size_t *size) +{ + CLookToRead *p = (CLookToRead *)pp; + size_t rem = p->size - p->pos; + if (rem == 0) + return p->realStream->Read(p->realStream, buf, size); + if (rem > *size) + rem = *size; + memcpy(buf, p->buf + p->pos, rem); + p->pos += rem; + *size = rem; + return SZ_OK; +} + +static SRes LookToRead_Seek(void *pp, Int64 *pos, ESzSeek origin) +{ + CLookToRead *p = (CLookToRead *)pp; + p->pos = p->size = 0; + return p->realStream->Seek(p->realStream, pos, origin); +} + +void LookToRead_CreateVTable(CLookToRead *p, int lookahead) +{ + p->s.Look = lookahead ? + LookToRead_Look_Lookahead : + LookToRead_Look_Exact; + p->s.Skip = LookToRead_Skip; + p->s.Read = LookToRead_Read; + p->s.Seek = LookToRead_Seek; +} + +void LookToRead_Init(CLookToRead *p) +{ + p->pos = p->size = 0; +} + +static SRes SecToLook_Read(void *pp, void *buf, size_t *size) +{ + CSecToLook *p = (CSecToLook *)pp; + return LookInStream_LookRead(p->realStream, buf, size); +} + +void SecToLook_CreateVTable(CSecToLook *p) +{ + p->s.Read = SecToLook_Read; +} + +static SRes SecToRead_Read(void *pp, void *buf, size_t *size) +{ + CSecToRead *p = (CSecToRead *)pp; + return p->realStream->Read(p->realStream, buf, size); +} + +void SecToRead_CreateVTable(CSecToRead *p) +{ + p->s.Read = SecToRead_Read; +} diff --git a/fex/7z_C/Bcj2.c b/fex/7z_C/Bcj2.c new file mode 100644 index 0000000..20199ce --- /dev/null +++ b/fex/7z_C/Bcj2.c @@ -0,0 +1,132 @@ +/* Bcj2.c -- Converter for x86 code (BCJ2) +2008-10-04 : Igor Pavlov : Public domain */ + +#include "Bcj2.h" + +#ifdef _LZMA_PROB32 +#define CProb UInt32 +#else +#define CProb UInt16 +#endif + +#define IsJcc(b0, b1) ((b0) == 0x0F && ((b1) & 0xF0) == 0x80) +#define IsJ(b0, b1) ((b1 & 0xFE) == 0xE8 || IsJcc(b0, b1)) + +#define kNumTopBits 24 +#define kTopValue ((UInt32)1 << kNumTopBits) + +#define kNumBitModelTotalBits 11 +#define kBitModelTotal (1 << kNumBitModelTotalBits) +#define kNumMoveBits 5 + +#define RC_READ_BYTE (*buffer++) +#define RC_TEST { if (buffer == bufferLim) return SZ_ERROR_DATA; } +#define RC_INIT2 code = 0; range = 0xFFFFFFFF; \ + { int i; for (i = 0; i < 5; i++) { RC_TEST; code = (code << 8) | RC_READ_BYTE; }} + +#define NORMALIZE if (range < kTopValue) { RC_TEST; range <<= 8; code = (code << 8) | RC_READ_BYTE; } + +#define IF_BIT_0(p) ttt = *(p); bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) +#define UPDATE_0(p) range = bound; *(p) = (CProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); NORMALIZE; +#define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CProb)(ttt - (ttt >> kNumMoveBits)); NORMALIZE; + +int Bcj2_Decode( + const Byte *buf0, SizeT size0, + const Byte *buf1, SizeT size1, + const Byte *buf2, SizeT size2, + const Byte *buf3, SizeT size3, + Byte *outBuf, SizeT outSize) +{ + CProb p[256 + 2]; + SizeT inPos = 0, outPos = 0; + + const Byte *buffer, *bufferLim; + UInt32 range, code; + Byte prevByte = 0; + + unsigned int i; + for (i = 0; i < sizeof(p) / sizeof(p[0]); i++) + p[i] = kBitModelTotal >> 1; + + buffer = buf3; + bufferLim = buffer + size3; + RC_INIT2 + + if (outSize == 0) + return SZ_OK; + + for (;;) + { + Byte b; + CProb *prob; + UInt32 bound; + UInt32 ttt; + + SizeT limit = size0 - inPos; + if (outSize - outPos < limit) + limit = outSize - outPos; + while (limit != 0) + { + Byte b = buf0[inPos]; + outBuf[outPos++] = b; + if (IsJ(prevByte, b)) + break; + inPos++; + prevByte = b; + limit--; + } + + if (limit == 0 || outPos == outSize) + break; + + b = buf0[inPos++]; + + if (b == 0xE8) + prob = p + prevByte; + else if (b == 0xE9) + prob = p + 256; + else + prob = p + 257; + + IF_BIT_0(prob) + { + UPDATE_0(prob) + prevByte = b; + } + else + { + UInt32 dest; + const Byte *v; + UPDATE_1(prob) + if (b == 0xE8) + { + v = buf1; + if (size1 < 4) + return SZ_ERROR_DATA; + buf1 += 4; + size1 -= 4; + } + else + { + v = buf2; + if (size2 < 4) + return SZ_ERROR_DATA; + buf2 += 4; + size2 -= 4; + } + dest = (((UInt32)v[0] << 24) | ((UInt32)v[1] << 16) | + ((UInt32)v[2] << 8) | ((UInt32)v[3])) - ((UInt32)outPos + 4); + outBuf[outPos++] = (Byte)dest; + if (outPos == outSize) + break; + outBuf[outPos++] = (Byte)(dest >> 8); + if (outPos == outSize) + break; + outBuf[outPos++] = (Byte)(dest >> 16); + if (outPos == outSize) + break; + outBuf[outPos++] = prevByte = (Byte)(dest >> 24); + } + } + return (outPos == outSize) ? SZ_OK : SZ_ERROR_DATA; +} diff --git a/fex/7z_C/Bcj2.h b/fex/7z_C/Bcj2.h new file mode 100644 index 0000000..dbc0541 --- /dev/null +++ b/fex/7z_C/Bcj2.h @@ -0,0 +1,38 @@ +/* Bcj2.h -- Converter for x86 code (BCJ2) +2009-02-07 : Igor Pavlov : Public domain */ + +#ifndef __BCJ2_H +#define __BCJ2_H + +#include "Types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* +Conditions: + outSize <= FullOutputSize, + where FullOutputSize is full size of output stream of x86_2 filter. + +If buf0 overlaps outBuf, there are two required conditions: + 1) (buf0 >= outBuf) + 2) (buf0 + size0 >= outBuf + FullOutputSize). + +Returns: + SZ_OK + SZ_ERROR_DATA - Data error +*/ + +int Bcj2_Decode( + const Byte *buf0, SizeT size0, + const Byte *buf1, SizeT size1, + const Byte *buf2, SizeT size2, + const Byte *buf3, SizeT size3, + Byte *outBuf, SizeT outSize); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/fex/7z_C/Bra.c b/fex/7z_C/Bra.c new file mode 100644 index 0000000..2e47b14 --- /dev/null +++ b/fex/7z_C/Bra.c @@ -0,0 +1,133 @@ +/* Bra.c -- Converters for RISC code +2010-04-16 : Igor Pavlov : Public domain */ + +#include "Bra.h" + +SizeT ARM_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) +{ + SizeT i; + if (size < 4) + return 0; + size -= 4; + ip += 8; + for (i = 0; i <= size; i += 4) + { + if (data[i + 3] == 0xEB) + { + UInt32 dest; + UInt32 src = ((UInt32)data[i + 2] << 16) | ((UInt32)data[i + 1] << 8) | (data[i + 0]); + src <<= 2; + if (encoding) + dest = ip + (UInt32)i + src; + else + dest = src - (ip + (UInt32)i); + dest >>= 2; + data[i + 2] = (Byte)(dest >> 16); + data[i + 1] = (Byte)(dest >> 8); + data[i + 0] = (Byte)dest; + } + } + return i; +} + +SizeT ARMT_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) +{ + SizeT i; + if (size < 4) + return 0; + size -= 4; + ip += 4; + for (i = 0; i <= size; i += 2) + { + if ((data[i + 1] & 0xF8) == 0xF0 && + (data[i + 3] & 0xF8) == 0xF8) + { + UInt32 dest; + UInt32 src = + (((UInt32)data[i + 1] & 0x7) << 19) | + ((UInt32)data[i + 0] << 11) | + (((UInt32)data[i + 3] & 0x7) << 8) | + (data[i + 2]); + + src <<= 1; + if (encoding) + dest = ip + (UInt32)i + src; + else + dest = src - (ip + (UInt32)i); + dest >>= 1; + + data[i + 1] = (Byte)(0xF0 | ((dest >> 19) & 0x7)); + data[i + 0] = (Byte)(dest >> 11); + data[i + 3] = (Byte)(0xF8 | ((dest >> 8) & 0x7)); + data[i + 2] = (Byte)dest; + i += 2; + } + } + return i; +} + +SizeT PPC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) +{ + SizeT i; + if (size < 4) + return 0; + size -= 4; + for (i = 0; i <= size; i += 4) + { + if ((data[i] >> 2) == 0x12 && (data[i + 3] & 3) == 1) + { + UInt32 src = ((UInt32)(data[i + 0] & 3) << 24) | + ((UInt32)data[i + 1] << 16) | + ((UInt32)data[i + 2] << 8) | + ((UInt32)data[i + 3] & (~3)); + + UInt32 dest; + if (encoding) + dest = ip + (UInt32)i + src; + else + dest = src - (ip + (UInt32)i); + data[i + 0] = (Byte)(0x48 | ((dest >> 24) & 0x3)); + data[i + 1] = (Byte)(dest >> 16); + data[i + 2] = (Byte)(dest >> 8); + data[i + 3] &= 0x3; + data[i + 3] |= dest; + } + } + return i; +} + +SizeT SPARC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) +{ + UInt32 i; + if (size < 4) + return 0; + size -= 4; + for (i = 0; i <= size; i += 4) + { + if ((data[i] == 0x40 && (data[i + 1] & 0xC0) == 0x00) || + (data[i] == 0x7F && (data[i + 1] & 0xC0) == 0xC0)) + { + UInt32 src = + ((UInt32)data[i + 0] << 24) | + ((UInt32)data[i + 1] << 16) | + ((UInt32)data[i + 2] << 8) | + ((UInt32)data[i + 3]); + UInt32 dest; + + src <<= 2; + if (encoding) + dest = ip + i + src; + else + dest = src - (ip + i); + dest >>= 2; + + dest = (((0 - ((dest >> 22) & 1)) << 22) & 0x3FFFFFFF) | (dest & 0x3FFFFF) | 0x40000000; + + data[i + 0] = (Byte)(dest >> 24); + data[i + 1] = (Byte)(dest >> 16); + data[i + 2] = (Byte)(dest >> 8); + data[i + 3] = (Byte)dest; + } + } + return i; +} diff --git a/fex/7z_C/Bra.h b/fex/7z_C/Bra.h new file mode 100644 index 0000000..5748c1c --- /dev/null +++ b/fex/7z_C/Bra.h @@ -0,0 +1,68 @@ +/* Bra.h -- Branch converters for executables +2009-02-07 : Igor Pavlov : Public domain */ + +#ifndef __BRA_H +#define __BRA_H + +#include "Types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* +These functions convert relative addresses to absolute addresses +in CALL instructions to increase the compression ratio. + + In: + data - data buffer + size - size of data + ip - current virtual Instruction Pinter (IP) value + state - state variable for x86 converter + encoding - 0 (for decoding), 1 (for encoding) + + Out: + state - state variable for x86 converter + + Returns: + The number of processed bytes. If you call these functions with multiple calls, + you must start next call with first byte after block of processed bytes. + + Type Endian Alignment LookAhead + + x86 little 1 4 + ARMT little 2 2 + ARM little 4 0 + PPC big 4 0 + SPARC big 4 0 + IA64 little 16 0 + + size must be >= Alignment + LookAhead, if it's not last block. + If (size < Alignment + LookAhead), converter returns 0. + + Example: + + UInt32 ip = 0; + for () + { + ; size must be >= Alignment + LookAhead, if it's not last block + SizeT processed = Convert(data, size, ip, 1); + data += processed; + size -= processed; + ip += processed; + } +*/ + +#define x86_Convert_Init(state) { state = 0; } +SizeT x86_Convert(Byte *data, SizeT size, UInt32 ip, UInt32 *state, int encoding); +SizeT ARM_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); +SizeT ARMT_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); +SizeT PPC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); +SizeT SPARC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); +SizeT IA64_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/fex/7z_C/Bra86.c b/fex/7z_C/Bra86.c new file mode 100644 index 0000000..1ee0e70 --- /dev/null +++ b/fex/7z_C/Bra86.c @@ -0,0 +1,85 @@ +/* Bra86.c -- Converter for x86 code (BCJ) +2008-10-04 : Igor Pavlov : Public domain */ + +#include "Bra.h" + +#define Test86MSByte(b) ((b) == 0 || (b) == 0xFF) + +const Byte kMaskToAllowedStatus[8] = {1, 1, 1, 0, 1, 0, 0, 0}; +const Byte kMaskToBitNumber[8] = {0, 1, 2, 2, 3, 3, 3, 3}; + +SizeT x86_Convert(Byte *data, SizeT size, UInt32 ip, UInt32 *state, int encoding) +{ + SizeT bufferPos = 0, prevPosT; + UInt32 prevMask = *state & 0x7; + if (size < 5) + return 0; + ip += 5; + prevPosT = (SizeT)0 - 1; + + for (;;) + { + Byte *p = data + bufferPos; + Byte *limit = data + size - 4; + for (; p < limit; p++) + if ((*p & 0xFE) == 0xE8) + break; + bufferPos = (SizeT)(p - data); + if (p >= limit) + break; + prevPosT = bufferPos - prevPosT; + if (prevPosT > 3) + prevMask = 0; + else + { + prevMask = (prevMask << ((int)prevPosT - 1)) & 0x7; + if (prevMask != 0) + { + Byte b = p[4 - kMaskToBitNumber[prevMask]]; + if (!kMaskToAllowedStatus[prevMask] || Test86MSByte(b)) + { + prevPosT = bufferPos; + prevMask = ((prevMask << 1) & 0x7) | 1; + bufferPos++; + continue; + } + } + } + prevPosT = bufferPos; + + if (Test86MSByte(p[4])) + { + UInt32 src = ((UInt32)p[4] << 24) | ((UInt32)p[3] << 16) | ((UInt32)p[2] << 8) | ((UInt32)p[1]); + UInt32 dest; + for (;;) + { + Byte b; + int index; + if (encoding) + dest = (ip + (UInt32)bufferPos) + src; + else + dest = src - (ip + (UInt32)bufferPos); + if (prevMask == 0) + break; + index = kMaskToBitNumber[prevMask] * 8; + b = (Byte)(dest >> (24 - index)); + if (!Test86MSByte(b)) + break; + src = dest ^ ((1 << (32 - index)) - 1); + } + p[4] = (Byte)(~(((dest >> 24) & 1) - 1)); + p[3] = (Byte)(dest >> 16); + p[2] = (Byte)(dest >> 8); + p[1] = (Byte)dest; + bufferPos += 5; + } + else + { + prevMask = ((prevMask << 1) & 0x7) | 1; + bufferPos++; + } + } + prevPosT = bufferPos - prevPosT; + *state = ((prevPosT > 3) ? 0 : ((prevMask << ((int)prevPosT - 1)) & 0x7)); + return bufferPos; +} diff --git a/fex/7z_C/CpuArch.c b/fex/7z_C/CpuArch.c new file mode 100644 index 0000000..689109e --- /dev/null +++ b/fex/7z_C/CpuArch.c @@ -0,0 +1,173 @@ +/* CpuArch.c -- CPU specific code +2010-10-26: Igor Pavlov : Public domain */ + +#include "CpuArch.h" + +#ifdef MY_CPU_X86_OR_AMD64 + +#if (defined(_MSC_VER) && !defined(MY_CPU_AMD64)) || defined(__GNUC__) +#define USE_ASM +#endif + +#if defined(USE_ASM) && !defined(MY_CPU_AMD64) +static UInt32 CheckFlag(UInt32 flag) +{ + #ifdef _MSC_VER + __asm pushfd; + __asm pop EAX; + __asm mov EDX, EAX; + __asm xor EAX, flag; + __asm push EAX; + __asm popfd; + __asm pushfd; + __asm pop EAX; + __asm xor EAX, EDX; + __asm push EDX; + __asm popfd; + __asm and flag, EAX; + #else + __asm__ __volatile__ ( + "pushf\n\t" + "pop %%EAX\n\t" + "movl %%EAX,%%EDX\n\t" + "xorl %0,%%EAX\n\t" + "push %%EAX\n\t" + "popf\n\t" + "pushf\n\t" + "pop %%EAX\n\t" + "xorl %%EDX,%%EAX\n\t" + "push %%EDX\n\t" + "popf\n\t" + "andl %%EAX, %0\n\t": + "=c" (flag) : "c" (flag)); + #endif + return flag; +} +#define CHECK_CPUID_IS_SUPPORTED if (CheckFlag(1 << 18) == 0 || CheckFlag(1 << 21) == 0) return False; +#else +#define CHECK_CPUID_IS_SUPPORTED +#endif + +static void MyCPUID(UInt32 function, UInt32 *a, UInt32 *b, UInt32 *c, UInt32 *d) +{ + #ifdef USE_ASM + + #ifdef _MSC_VER + + UInt32 a2, b2, c2, d2; + __asm xor EBX, EBX; + __asm xor ECX, ECX; + __asm xor EDX, EDX; + __asm mov EAX, function; + __asm cpuid; + __asm mov a2, EAX; + __asm mov b2, EBX; + __asm mov c2, ECX; + __asm mov d2, EDX; + + *a = a2; + *b = b2; + *c = c2; + *d = d2; + + #else + + // Mac cross-compile compiler: + // can't find register in class 'BREG' while reloading 'asm' + // so use class 'r' register var binding + register _b asm("%bx"); + __asm__ __volatile__ ( + "cpuid" + : "=a" (*a) , + "=r" (_b) , + "=c" (*c) , + "=d" (*d) + : "0" (function)) ; + *b = _b; + + #endif + + #else + + int CPUInfo[4]; + __cpuid(CPUInfo, function); + *a = CPUInfo[0]; + *b = CPUInfo[1]; + *c = CPUInfo[2]; + *d = CPUInfo[3]; + + #endif +} + +Bool x86cpuid_CheckAndRead(Cx86cpuid *p) +{ + CHECK_CPUID_IS_SUPPORTED + MyCPUID(0, &p->maxFunc, &p->vendor[0], &p->vendor[2], &p->vendor[1]); + MyCPUID(1, &p->ver, &p->b, &p->c, &p->d); + return True; +} + +static UInt32 kVendors[][3] = +{ + { 0x756E6547, 0x49656E69, 0x6C65746E}, + { 0x68747541, 0x69746E65, 0x444D4163}, + { 0x746E6543, 0x48727561, 0x736C7561} +}; + +int x86cpuid_GetFirm(const Cx86cpuid *p) +{ + unsigned i; + for (i = 0; i < sizeof(kVendors) / sizeof(kVendors[i]); i++) + { + const UInt32 *v = kVendors[i]; + if (v[0] == p->vendor[0] && + v[1] == p->vendor[1] && + v[2] == p->vendor[2]) + return (int)i; + } + return -1; +} + +Bool CPU_Is_InOrder() +{ + Cx86cpuid p; + int firm; + UInt32 family, model; + if (!x86cpuid_CheckAndRead(&p)) + return True; + family = x86cpuid_GetFamily(&p); + model = x86cpuid_GetModel(&p); + firm = x86cpuid_GetFirm(&p); + switch (firm) + { + case CPU_FIRM_INTEL: return (family < 6 || (family == 6 && model == 0x100C)); + case CPU_FIRM_AMD: return (family < 5 || (family == 5 && (model < 6 || model == 0xA))); + case CPU_FIRM_VIA: return (family < 6 || (family == 6 && model < 0xF)); + } + return True; +} + +#if !defined(MY_CPU_AMD64) && defined(_WIN32) +static Bool CPU_Sys_Is_SSE_Supported() +{ + OSVERSIONINFO vi; + vi.dwOSVersionInfoSize = sizeof(vi); + if (!GetVersionEx(&vi)) + return False; + return (vi.dwMajorVersion >= 5); +} +#define CHECK_SYS_SSE_SUPPORT if (!CPU_Sys_Is_SSE_Supported()) return False; +#else +#define CHECK_SYS_SSE_SUPPORT +#endif + +Bool CPU_Is_Aes_Supported() +{ + Cx86cpuid p; + CHECK_SYS_SSE_SUPPORT + if (!x86cpuid_CheckAndRead(&p)) + return False; + return (p.c >> 25) & 1; +} + +#endif diff --git a/fex/7z_C/CpuArch.h b/fex/7z_C/CpuArch.h new file mode 100644 index 0000000..01930c7 --- /dev/null +++ b/fex/7z_C/CpuArch.h @@ -0,0 +1,155 @@ +/* CpuArch.h -- CPU specific code +2010-10-26: Igor Pavlov : Public domain */ + +#ifndef __CPU_ARCH_H +#define __CPU_ARCH_H + +#include "Types.h" + +EXTERN_C_BEGIN + +/* +MY_CPU_LE means that CPU is LITTLE ENDIAN. +If MY_CPU_LE is not defined, we don't know about that property of platform (it can be LITTLE ENDIAN). + +MY_CPU_LE_UNALIGN means that CPU is LITTLE ENDIAN and CPU supports unaligned memory accesses. +If MY_CPU_LE_UNALIGN is not defined, we don't know about these properties of platform. +*/ + +#if defined(_M_X64) || defined(_M_AMD64) || defined(__x86_64__) +#define MY_CPU_AMD64 +#endif + +#if defined(MY_CPU_AMD64) || defined(_M_IA64) +#define MY_CPU_64BIT +#endif + +#if defined(_M_IX86) || defined(__i386__) +#define MY_CPU_X86 +#endif + +#if defined(MY_CPU_X86) || defined(MY_CPU_AMD64) +#define MY_CPU_X86_OR_AMD64 +#endif + +#if defined(MY_CPU_X86) || defined(_M_ARM) +#define MY_CPU_32BIT +#endif + +#if defined(_WIN32) && defined(_M_ARM) +#define MY_CPU_ARM_LE +#endif + +#if defined(_WIN32) && defined(_M_IA64) +#define MY_CPU_IA64_LE +#endif + +#if defined(MY_CPU_X86_OR_AMD64) +#define MY_CPU_LE_UNALIGN +#endif + +#if defined(MY_CPU_X86_OR_AMD64) || defined(MY_CPU_ARM_LE) || defined(MY_CPU_IA64_LE) || defined(__ARMEL__) || defined(__MIPSEL__) || defined(__LITTLE_ENDIAN__) +#define MY_CPU_LE +#endif + +#if defined(__BIG_ENDIAN__) +#define MY_CPU_BE +#endif + +#if defined(MY_CPU_LE) && defined(MY_CPU_BE) +Stop_Compiling_Bad_Endian +#endif + +#ifdef MY_CPU_LE_UNALIGN + +#define GetUi16(p) (*(const UInt16 *)(p)) +#define GetUi32(p) (*(const UInt32 *)(p)) +#define GetUi64(p) (*(const UInt64 *)(p)) +#define SetUi16(p, d) *(UInt16 *)(p) = (d); +#define SetUi32(p, d) *(UInt32 *)(p) = (d); +#define SetUi64(p, d) *(UInt64 *)(p) = (d); + +#else + +#define GetUi16(p) (((const Byte *)(p))[0] | ((UInt16)((const Byte *)(p))[1] << 8)) + +#define GetUi32(p) ( \ + ((const Byte *)(p))[0] | \ + ((UInt32)((const Byte *)(p))[1] << 8) | \ + ((UInt32)((const Byte *)(p))[2] << 16) | \ + ((UInt32)((const Byte *)(p))[3] << 24)) + +#define GetUi64(p) (GetUi32(p) | ((UInt64)GetUi32(((const Byte *)(p)) + 4) << 32)) + +#define SetUi16(p, d) { UInt32 _x_ = (d); \ + ((Byte *)(p))[0] = (Byte)_x_; \ + ((Byte *)(p))[1] = (Byte)(_x_ >> 8); } + +#define SetUi32(p, d) { UInt32 _x_ = (d); \ + ((Byte *)(p))[0] = (Byte)_x_; \ + ((Byte *)(p))[1] = (Byte)(_x_ >> 8); \ + ((Byte *)(p))[2] = (Byte)(_x_ >> 16); \ + ((Byte *)(p))[3] = (Byte)(_x_ >> 24); } + +#define SetUi64(p, d) { UInt64 _x64_ = (d); \ + SetUi32(p, (UInt32)_x64_); \ + SetUi32(((Byte *)(p)) + 4, (UInt32)(_x64_ >> 32)); } + +#endif + +#if defined(MY_CPU_LE_UNALIGN) && defined(_WIN64) && (_MSC_VER >= 1300) + +#pragma intrinsic(_byteswap_ulong) +#pragma intrinsic(_byteswap_uint64) +#define GetBe32(p) _byteswap_ulong(*(const UInt32 *)(const Byte *)(p)) +#define GetBe64(p) _byteswap_uint64(*(const UInt64 *)(const Byte *)(p)) + +#else + +#define GetBe32(p) ( \ + ((UInt32)((const Byte *)(p))[0] << 24) | \ + ((UInt32)((const Byte *)(p))[1] << 16) | \ + ((UInt32)((const Byte *)(p))[2] << 8) | \ + ((const Byte *)(p))[3] ) + +#define GetBe64(p) (((UInt64)GetBe32(p) << 32) | GetBe32(((const Byte *)(p)) + 4)) + +#endif + +#define GetBe16(p) (((UInt16)((const Byte *)(p))[0] << 8) | ((const Byte *)(p))[1]) + + +#ifdef MY_CPU_X86_OR_AMD64 + +typedef struct +{ + UInt32 maxFunc; + UInt32 vendor[3]; + UInt32 ver; + UInt32 b; + UInt32 c; + UInt32 d; +} Cx86cpuid; + +enum +{ + CPU_FIRM_INTEL, + CPU_FIRM_AMD, + CPU_FIRM_VIA +}; + +Bool x86cpuid_CheckAndRead(Cx86cpuid *p); +int x86cpuid_GetFirm(const Cx86cpuid *p); + +#define x86cpuid_GetFamily(p) (((p)->ver >> 8) & 0xFF00F) +#define x86cpuid_GetModel(p) (((p)->ver >> 4) & 0xF00F) +#define x86cpuid_GetStepping(p) ((p)->ver & 0xF) + +Bool CPU_Is_InOrder(); +Bool CPU_Is_Aes_Supported(); + +#endif + +EXTERN_C_END + +#endif diff --git a/fex/7z_C/Lzma2Dec.c b/fex/7z_C/Lzma2Dec.c new file mode 100644 index 0000000..7ea1cc9 --- /dev/null +++ b/fex/7z_C/Lzma2Dec.c @@ -0,0 +1,356 @@ +/* Lzma2Dec.c -- LZMA2 Decoder +2009-05-03 : Igor Pavlov : Public domain */ + +/* #define SHOW_DEBUG_INFO */ + +#ifdef SHOW_DEBUG_INFO +#include +#endif + +#include + +#include "Lzma2Dec.h" + +/* +00000000 - EOS +00000001 U U - Uncompressed Reset Dic +00000010 U U - Uncompressed No Reset +100uuuuu U U P P - LZMA no reset +101uuuuu U U P P - LZMA reset state +110uuuuu U U P P S - LZMA reset state + new prop +111uuuuu U U P P S - LZMA reset state + new prop + reset dic + + u, U - Unpack Size + P - Pack Size + S - Props +*/ + +#define LZMA2_CONTROL_LZMA (1 << 7) +#define LZMA2_CONTROL_COPY_NO_RESET 2 +#define LZMA2_CONTROL_COPY_RESET_DIC 1 +#define LZMA2_CONTROL_EOF 0 + +#define LZMA2_IS_UNCOMPRESSED_STATE(p) (((p)->control & LZMA2_CONTROL_LZMA) == 0) + +#define LZMA2_GET_LZMA_MODE(p) (((p)->control >> 5) & 3) +#define LZMA2_IS_THERE_PROP(mode) ((mode) >= 2) + +#define LZMA2_LCLP_MAX 4 +#define LZMA2_DIC_SIZE_FROM_PROP(p) (((UInt32)2 | ((p) & 1)) << ((p) / 2 + 11)) + +#ifdef SHOW_DEBUG_INFO +#define PRF(x) x +#else +#define PRF(x) +#endif + +typedef enum +{ + LZMA2_STATE_CONTROL, + LZMA2_STATE_UNPACK0, + LZMA2_STATE_UNPACK1, + LZMA2_STATE_PACK0, + LZMA2_STATE_PACK1, + LZMA2_STATE_PROP, + LZMA2_STATE_DATA, + LZMA2_STATE_DATA_CONT, + LZMA2_STATE_FINISHED, + LZMA2_STATE_ERROR +} ELzma2State; + +static SRes Lzma2Dec_GetOldProps(Byte prop, Byte *props) +{ + UInt32 dicSize; + if (prop > 40) + return SZ_ERROR_UNSUPPORTED; + dicSize = (prop == 40) ? 0xFFFFFFFF : LZMA2_DIC_SIZE_FROM_PROP(prop); + props[0] = (Byte)LZMA2_LCLP_MAX; + props[1] = (Byte)(dicSize); + props[2] = (Byte)(dicSize >> 8); + props[3] = (Byte)(dicSize >> 16); + props[4] = (Byte)(dicSize >> 24); + return SZ_OK; +} + +SRes Lzma2Dec_AllocateProbs(CLzma2Dec *p, Byte prop, ISzAlloc *alloc) +{ + Byte props[LZMA_PROPS_SIZE]; + RINOK(Lzma2Dec_GetOldProps(prop, props)); + return LzmaDec_AllocateProbs(&p->decoder, props, LZMA_PROPS_SIZE, alloc); +} + +SRes Lzma2Dec_Allocate(CLzma2Dec *p, Byte prop, ISzAlloc *alloc) +{ + Byte props[LZMA_PROPS_SIZE]; + RINOK(Lzma2Dec_GetOldProps(prop, props)); + return LzmaDec_Allocate(&p->decoder, props, LZMA_PROPS_SIZE, alloc); +} + +void Lzma2Dec_Init(CLzma2Dec *p) +{ + p->state = LZMA2_STATE_CONTROL; + p->needInitDic = True; + p->needInitState = True; + p->needInitProp = True; + LzmaDec_Init(&p->decoder); +} + +static ELzma2State Lzma2Dec_UpdateState(CLzma2Dec *p, Byte b) +{ + switch(p->state) + { + case LZMA2_STATE_CONTROL: + p->control = b; + PRF(printf("\n %4X ", p->decoder.dicPos)); + PRF(printf(" %2X", b)); + if (p->control == 0) + return LZMA2_STATE_FINISHED; + if (LZMA2_IS_UNCOMPRESSED_STATE(p)) + { + if ((p->control & 0x7F) > 2) + return LZMA2_STATE_ERROR; + p->unpackSize = 0; + } + else + p->unpackSize = (UInt32)(p->control & 0x1F) << 16; + return LZMA2_STATE_UNPACK0; + + case LZMA2_STATE_UNPACK0: + p->unpackSize |= (UInt32)b << 8; + return LZMA2_STATE_UNPACK1; + + case LZMA2_STATE_UNPACK1: + p->unpackSize |= (UInt32)b; + p->unpackSize++; + PRF(printf(" %8d", p->unpackSize)); + return (LZMA2_IS_UNCOMPRESSED_STATE(p)) ? LZMA2_STATE_DATA : LZMA2_STATE_PACK0; + + case LZMA2_STATE_PACK0: + p->packSize = (UInt32)b << 8; + return LZMA2_STATE_PACK1; + + case LZMA2_STATE_PACK1: + p->packSize |= (UInt32)b; + p->packSize++; + PRF(printf(" %8d", p->packSize)); + return LZMA2_IS_THERE_PROP(LZMA2_GET_LZMA_MODE(p)) ? LZMA2_STATE_PROP: + (p->needInitProp ? LZMA2_STATE_ERROR : LZMA2_STATE_DATA); + + case LZMA2_STATE_PROP: + { + int lc, lp; + if (b >= (9 * 5 * 5)) + return LZMA2_STATE_ERROR; + lc = b % 9; + b /= 9; + p->decoder.prop.pb = b / 5; + lp = b % 5; + if (lc + lp > LZMA2_LCLP_MAX) + return LZMA2_STATE_ERROR; + p->decoder.prop.lc = lc; + p->decoder.prop.lp = lp; + p->needInitProp = False; + return LZMA2_STATE_DATA; + } + } + return LZMA2_STATE_ERROR; +} + +static void LzmaDec_UpdateWithUncompressed(CLzmaDec *p, const Byte *src, SizeT size) +{ + memcpy(p->dic + p->dicPos, src, size); + p->dicPos += size; + if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= size) + p->checkDicSize = p->prop.dicSize; + p->processedPos += (UInt32)size; +} + +void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState); + +SRes Lzma2Dec_DecodeToDic(CLzma2Dec *p, SizeT dicLimit, + const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) +{ + SizeT inSize = *srcLen; + *srcLen = 0; + *status = LZMA_STATUS_NOT_SPECIFIED; + + while (p->state != LZMA2_STATE_FINISHED) + { + SizeT dicPos = p->decoder.dicPos; + if (p->state == LZMA2_STATE_ERROR) + return SZ_ERROR_DATA; + if (dicPos == dicLimit && finishMode == LZMA_FINISH_ANY) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_OK; + } + if (p->state != LZMA2_STATE_DATA && p->state != LZMA2_STATE_DATA_CONT) + { + if (*srcLen == inSize) + { + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + (*srcLen)++; + p->state = Lzma2Dec_UpdateState(p, *src++); + continue; + } + { + SizeT destSizeCur = dicLimit - dicPos; + SizeT srcSizeCur = inSize - *srcLen; + ELzmaFinishMode curFinishMode = LZMA_FINISH_ANY; + + if (p->unpackSize <= destSizeCur) + { + destSizeCur = (SizeT)p->unpackSize; + curFinishMode = LZMA_FINISH_END; + } + + if (LZMA2_IS_UNCOMPRESSED_STATE(p)) + { + if (*srcLen == inSize) + { + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + + if (p->state == LZMA2_STATE_DATA) + { + Bool initDic = (p->control == LZMA2_CONTROL_COPY_RESET_DIC); + if (initDic) + p->needInitProp = p->needInitState = True; + else if (p->needInitDic) + return SZ_ERROR_DATA; + p->needInitDic = False; + LzmaDec_InitDicAndState(&p->decoder, initDic, False); + } + + if (srcSizeCur > destSizeCur) + srcSizeCur = destSizeCur; + + if (srcSizeCur == 0) + return SZ_ERROR_DATA; + + LzmaDec_UpdateWithUncompressed(&p->decoder, src, srcSizeCur); + + src += srcSizeCur; + *srcLen += srcSizeCur; + p->unpackSize -= (UInt32)srcSizeCur; + p->state = (p->unpackSize == 0) ? LZMA2_STATE_CONTROL : LZMA2_STATE_DATA_CONT; + } + else + { + SizeT outSizeProcessed; + SRes res; + + if (p->state == LZMA2_STATE_DATA) + { + int mode = LZMA2_GET_LZMA_MODE(p); + Bool initDic = (mode == 3); + Bool initState = (mode > 0); + if ((!initDic && p->needInitDic) || (!initState && p->needInitState)) + return SZ_ERROR_DATA; + + LzmaDec_InitDicAndState(&p->decoder, initDic, initState); + p->needInitDic = False; + p->needInitState = False; + p->state = LZMA2_STATE_DATA_CONT; + } + if (srcSizeCur > p->packSize) + srcSizeCur = (SizeT)p->packSize; + + res = LzmaDec_DecodeToDic(&p->decoder, dicPos + destSizeCur, src, &srcSizeCur, curFinishMode, status); + + src += srcSizeCur; + *srcLen += srcSizeCur; + p->packSize -= (UInt32)srcSizeCur; + + outSizeProcessed = p->decoder.dicPos - dicPos; + p->unpackSize -= (UInt32)outSizeProcessed; + + RINOK(res); + if (*status == LZMA_STATUS_NEEDS_MORE_INPUT) + return res; + + if (srcSizeCur == 0 && outSizeProcessed == 0) + { + if (*status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK || + p->unpackSize != 0 || p->packSize != 0) + return SZ_ERROR_DATA; + p->state = LZMA2_STATE_CONTROL; + } + if (*status == LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK) + *status = LZMA_STATUS_NOT_FINISHED; + } + } + } + *status = LZMA_STATUS_FINISHED_WITH_MARK; + return SZ_OK; +} + +SRes Lzma2Dec_DecodeToBuf(CLzma2Dec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) +{ + SizeT outSize = *destLen, inSize = *srcLen; + *srcLen = *destLen = 0; + for (;;) + { + SizeT srcSizeCur = inSize, outSizeCur, dicPos; + ELzmaFinishMode curFinishMode; + SRes res; + if (p->decoder.dicPos == p->decoder.dicBufSize) + p->decoder.dicPos = 0; + dicPos = p->decoder.dicPos; + if (outSize > p->decoder.dicBufSize - dicPos) + { + outSizeCur = p->decoder.dicBufSize; + curFinishMode = LZMA_FINISH_ANY; + } + else + { + outSizeCur = dicPos + outSize; + curFinishMode = finishMode; + } + + res = Lzma2Dec_DecodeToDic(p, outSizeCur, src, &srcSizeCur, curFinishMode, status); + src += srcSizeCur; + inSize -= srcSizeCur; + *srcLen += srcSizeCur; + outSizeCur = p->decoder.dicPos - dicPos; + memcpy(dest, p->decoder.dic + dicPos, outSizeCur); + dest += outSizeCur; + outSize -= outSizeCur; + *destLen += outSizeCur; + if (res != 0) + return res; + if (outSizeCur == 0 || outSize == 0) + return SZ_OK; + } +} + +SRes Lzma2Decode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, + Byte prop, ELzmaFinishMode finishMode, ELzmaStatus *status, ISzAlloc *alloc) +{ + CLzma2Dec decoder; + SRes res; + SizeT outSize = *destLen, inSize = *srcLen; + Byte props[LZMA_PROPS_SIZE]; + + Lzma2Dec_Construct(&decoder); + + *destLen = *srcLen = 0; + *status = LZMA_STATUS_NOT_SPECIFIED; + decoder.decoder.dic = dest; + decoder.decoder.dicBufSize = outSize; + + RINOK(Lzma2Dec_GetOldProps(prop, props)); + RINOK(LzmaDec_AllocateProbs(&decoder.decoder, props, LZMA_PROPS_SIZE, alloc)); + + *srcLen = inSize; + res = Lzma2Dec_DecodeToDic(&decoder, outSize, src, srcLen, finishMode, status); + *destLen = decoder.decoder.dicPos; + if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT) + res = SZ_ERROR_INPUT_EOF; + + LzmaDec_FreeProbs(&decoder.decoder, alloc); + return res; +} diff --git a/fex/7z_C/Lzma2Dec.h b/fex/7z_C/Lzma2Dec.h new file mode 100644 index 0000000..6bc07bb --- /dev/null +++ b/fex/7z_C/Lzma2Dec.h @@ -0,0 +1,84 @@ +/* Lzma2Dec.h -- LZMA2 Decoder +2009-05-03 : Igor Pavlov : Public domain */ + +#ifndef __LZMA2_DEC_H +#define __LZMA2_DEC_H + +#include "LzmaDec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* ---------- State Interface ---------- */ + +typedef struct +{ + CLzmaDec decoder; + UInt32 packSize; + UInt32 unpackSize; + int state; + Byte control; + Bool needInitDic; + Bool needInitState; + Bool needInitProp; +} CLzma2Dec; + +#define Lzma2Dec_Construct(p) LzmaDec_Construct(&(p)->decoder) +#define Lzma2Dec_FreeProbs(p, alloc) LzmaDec_FreeProbs(&(p)->decoder, alloc); +#define Lzma2Dec_Free(p, alloc) LzmaDec_Free(&(p)->decoder, alloc); + +SRes Lzma2Dec_AllocateProbs(CLzma2Dec *p, Byte prop, ISzAlloc *alloc); +SRes Lzma2Dec_Allocate(CLzma2Dec *p, Byte prop, ISzAlloc *alloc); +void Lzma2Dec_Init(CLzma2Dec *p); + + +/* +finishMode: + It has meaning only if the decoding reaches output limit (*destLen or dicLimit). + LZMA_FINISH_ANY - use smallest number of input bytes + LZMA_FINISH_END - read EndOfStream marker after decoding + +Returns: + SZ_OK + status: + LZMA_STATUS_FINISHED_WITH_MARK + LZMA_STATUS_NOT_FINISHED + LZMA_STATUS_NEEDS_MORE_INPUT + SZ_ERROR_DATA - Data error +*/ + +SRes Lzma2Dec_DecodeToDic(CLzma2Dec *p, SizeT dicLimit, + const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); + +SRes Lzma2Dec_DecodeToBuf(CLzma2Dec *p, Byte *dest, SizeT *destLen, + const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); + + +/* ---------- One Call Interface ---------- */ + +/* +finishMode: + It has meaning only if the decoding reaches output limit (*destLen). + LZMA_FINISH_ANY - use smallest number of input bytes + LZMA_FINISH_END - read EndOfStream marker after decoding + +Returns: + SZ_OK + status: + LZMA_STATUS_FINISHED_WITH_MARK + LZMA_STATUS_NOT_FINISHED + SZ_ERROR_DATA - Data error + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_UNSUPPORTED - Unsupported properties + SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src). +*/ + +SRes Lzma2Decode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, + Byte prop, ELzmaFinishMode finishMode, ELzmaStatus *status, ISzAlloc *alloc); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/fex/7z_C/LzmaDec.c b/fex/7z_C/LzmaDec.c new file mode 100644 index 0000000..2036761 --- /dev/null +++ b/fex/7z_C/LzmaDec.c @@ -0,0 +1,999 @@ +/* LzmaDec.c -- LZMA Decoder +2009-09-20 : Igor Pavlov : Public domain */ + +#include "LzmaDec.h" + +#include + +#define kNumTopBits 24 +#define kTopValue ((UInt32)1 << kNumTopBits) + +#define kNumBitModelTotalBits 11 +#define kBitModelTotal (1 << kNumBitModelTotalBits) +#define kNumMoveBits 5 + +#define RC_INIT_SIZE 5 + +#define NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } + +#define IF_BIT_0(p) ttt = *(p); NORMALIZE; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) +#define UPDATE_0(p) range = bound; *(p) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); +#define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits)); +#define GET_BIT2(p, i, A0, A1) IF_BIT_0(p) \ + { UPDATE_0(p); i = (i + i); A0; } else \ + { UPDATE_1(p); i = (i + i) + 1; A1; } +#define GET_BIT(p, i) GET_BIT2(p, i, ; , ;) + +#define TREE_GET_BIT(probs, i) { GET_BIT((probs + i), i); } +#define TREE_DECODE(probs, limit, i) \ + { i = 1; do { TREE_GET_BIT(probs, i); } while (i < limit); i -= limit; } + +/* #define _LZMA_SIZE_OPT */ + +#ifdef _LZMA_SIZE_OPT +#define TREE_6_DECODE(probs, i) TREE_DECODE(probs, (1 << 6), i) +#else +#define TREE_6_DECODE(probs, i) \ + { i = 1; \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + i -= 0x40; } +#endif + +#define NORMALIZE_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } + +#define IF_BIT_0_CHECK(p) ttt = *(p); NORMALIZE_CHECK; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) +#define UPDATE_0_CHECK range = bound; +#define UPDATE_1_CHECK range -= bound; code -= bound; +#define GET_BIT2_CHECK(p, i, A0, A1) IF_BIT_0_CHECK(p) \ + { UPDATE_0_CHECK; i = (i + i); A0; } else \ + { UPDATE_1_CHECK; i = (i + i) + 1; A1; } +#define GET_BIT_CHECK(p, i) GET_BIT2_CHECK(p, i, ; , ;) +#define TREE_DECODE_CHECK(probs, limit, i) \ + { i = 1; do { GET_BIT_CHECK(probs + i, i) } while (i < limit); i -= limit; } + + +#define kNumPosBitsMax 4 +#define kNumPosStatesMax (1 << kNumPosBitsMax) + +#define kLenNumLowBits 3 +#define kLenNumLowSymbols (1 << kLenNumLowBits) +#define kLenNumMidBits 3 +#define kLenNumMidSymbols (1 << kLenNumMidBits) +#define kLenNumHighBits 8 +#define kLenNumHighSymbols (1 << kLenNumHighBits) + +#define LenChoice 0 +#define LenChoice2 (LenChoice + 1) +#define LenLow (LenChoice2 + 1) +#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) +#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) +#define kNumLenProbs (LenHigh + kLenNumHighSymbols) + + +#define kNumStates 12 +#define kNumLitStates 7 + +#define kStartPosModelIndex 4 +#define kEndPosModelIndex 14 +#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) + +#define kNumPosSlotBits 6 +#define kNumLenToPosStates 4 + +#define kNumAlignBits 4 +#define kAlignTableSize (1 << kNumAlignBits) + +#define kMatchMinLen 2 +#define kMatchSpecLenStart (kMatchMinLen + kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) + +#define IsMatch 0 +#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) +#define IsRepG0 (IsRep + kNumStates) +#define IsRepG1 (IsRepG0 + kNumStates) +#define IsRepG2 (IsRepG1 + kNumStates) +#define IsRep0Long (IsRepG2 + kNumStates) +#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) +#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) +#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) +#define LenCoder (Align + kAlignTableSize) +#define RepLenCoder (LenCoder + kNumLenProbs) +#define Literal (RepLenCoder + kNumLenProbs) + +#define LZMA_BASE_SIZE 1846 +#define LZMA_LIT_SIZE 768 + +#define LzmaProps_GetNumProbs(p) ((UInt32)LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((p)->lc + (p)->lp))) + +#if Literal != LZMA_BASE_SIZE +StopCompilingDueBUG +#endif + +#define LZMA_DIC_MIN (1 << 12) + +/* First LZMA-symbol is always decoded. +And it decodes new LZMA-symbols while (buf < bufLimit), but "buf" is without last normalization +Out: + Result: + SZ_OK - OK + SZ_ERROR_DATA - Error + p->remainLen: + < kMatchSpecLenStart : normal remain + = kMatchSpecLenStart : finished + = kMatchSpecLenStart + 1 : Flush marker + = kMatchSpecLenStart + 2 : State Init Marker +*/ + +static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte *bufLimit) +{ + CLzmaProb *probs = p->probs; + + unsigned state = p->state; + UInt32 rep0 = p->reps[0], rep1 = p->reps[1], rep2 = p->reps[2], rep3 = p->reps[3]; + unsigned pbMask = ((unsigned)1 << (p->prop.pb)) - 1; + unsigned lpMask = ((unsigned)1 << (p->prop.lp)) - 1; + unsigned lc = p->prop.lc; + + Byte *dic = p->dic; + SizeT dicBufSize = p->dicBufSize; + SizeT dicPos = p->dicPos; + + UInt32 processedPos = p->processedPos; + UInt32 checkDicSize = p->checkDicSize; + unsigned len = 0; + + const Byte *buf = p->buf; + UInt32 range = p->range; + UInt32 code = p->code; + + do + { + CLzmaProb *prob; + UInt32 bound; + unsigned ttt; + unsigned posState = processedPos & pbMask; + + prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; + IF_BIT_0(prob) + { + unsigned symbol; + UPDATE_0(prob); + prob = probs + Literal; + if (checkDicSize != 0 || processedPos != 0) + prob += (LZMA_LIT_SIZE * (((processedPos & lpMask) << lc) + + (dic[(dicPos == 0 ? dicBufSize : dicPos) - 1] >> (8 - lc)))); + + if (state < kNumLitStates) + { + state -= (state < 4) ? state : 3; + symbol = 1; + do { GET_BIT(prob + symbol, symbol) } while (symbol < 0x100); + } + else + { + unsigned matchByte = p->dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; + unsigned offs = 0x100; + state -= (state < 10) ? 3 : 6; + symbol = 1; + do + { + unsigned bit; + CLzmaProb *probLit; + matchByte <<= 1; + bit = (matchByte & offs); + probLit = prob + offs + bit + symbol; + GET_BIT2(probLit, symbol, offs &= ~bit, offs &= bit) + } + while (symbol < 0x100); + } + dic[dicPos++] = (Byte)symbol; + processedPos++; + continue; + } + else + { + UPDATE_1(prob); + prob = probs + IsRep + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + state += kNumStates; + prob = probs + LenCoder; + } + else + { + UPDATE_1(prob); + if (checkDicSize == 0 && processedPos == 0) + return SZ_ERROR_DATA; + prob = probs + IsRepG0 + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; + IF_BIT_0(prob) + { + UPDATE_0(prob); + dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; + dicPos++; + processedPos++; + state = state < kNumLitStates ? 9 : 11; + continue; + } + UPDATE_1(prob); + } + else + { + UInt32 distance; + UPDATE_1(prob); + prob = probs + IsRepG1 + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + distance = rep1; + } + else + { + UPDATE_1(prob); + prob = probs + IsRepG2 + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + distance = rep2; + } + else + { + UPDATE_1(prob); + distance = rep3; + rep3 = rep2; + } + rep2 = rep1; + } + rep1 = rep0; + rep0 = distance; + } + state = state < kNumLitStates ? 8 : 11; + prob = probs + RepLenCoder; + } + { + unsigned limit, offset; + CLzmaProb *probLen = prob + LenChoice; + IF_BIT_0(probLen) + { + UPDATE_0(probLen); + probLen = prob + LenLow + (posState << kLenNumLowBits); + offset = 0; + limit = (1 << kLenNumLowBits); + } + else + { + UPDATE_1(probLen); + probLen = prob + LenChoice2; + IF_BIT_0(probLen) + { + UPDATE_0(probLen); + probLen = prob + LenMid + (posState << kLenNumMidBits); + offset = kLenNumLowSymbols; + limit = (1 << kLenNumMidBits); + } + else + { + UPDATE_1(probLen); + probLen = prob + LenHigh; + offset = kLenNumLowSymbols + kLenNumMidSymbols; + limit = (1 << kLenNumHighBits); + } + } + TREE_DECODE(probLen, limit, len); + len += offset; + } + + if (state >= kNumStates) + { + UInt32 distance; + prob = probs + PosSlot + + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits); + TREE_6_DECODE(prob, distance); + if (distance >= kStartPosModelIndex) + { + unsigned posSlot = (unsigned)distance; + int numDirectBits = (int)(((distance >> 1) - 1)); + distance = (2 | (distance & 1)); + if (posSlot < kEndPosModelIndex) + { + distance <<= numDirectBits; + prob = probs + SpecPos + distance - posSlot - 1; + { + UInt32 mask = 1; + unsigned i = 1; + do + { + GET_BIT2(prob + i, i, ; , distance |= mask); + mask <<= 1; + } + while (--numDirectBits != 0); + } + } + else + { + numDirectBits -= kNumAlignBits; + do + { + NORMALIZE + range >>= 1; + + { + UInt32 t; + code -= range; + t = (0 - ((UInt32)code >> 31)); /* (UInt32)((Int32)code >> 31) */ + distance = (distance << 1) + (t + 1); + code += range & t; + } + /* + distance <<= 1; + if (code >= range) + { + code -= range; + distance |= 1; + } + */ + } + while (--numDirectBits != 0); + prob = probs + Align; + distance <<= kNumAlignBits; + { + unsigned i = 1; + GET_BIT2(prob + i, i, ; , distance |= 1); + GET_BIT2(prob + i, i, ; , distance |= 2); + GET_BIT2(prob + i, i, ; , distance |= 4); + GET_BIT2(prob + i, i, ; , distance |= 8); + } + if (distance == (UInt32)0xFFFFFFFF) + { + len += kMatchSpecLenStart; + state -= kNumStates; + break; + } + } + } + rep3 = rep2; + rep2 = rep1; + rep1 = rep0; + rep0 = distance + 1; + if (checkDicSize == 0) + { + if (distance >= processedPos) + return SZ_ERROR_DATA; + } + else if (distance >= checkDicSize) + return SZ_ERROR_DATA; + state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3; + } + + len += kMatchMinLen; + + if (limit == dicPos) + return SZ_ERROR_DATA; + { + SizeT rem = limit - dicPos; + unsigned curLen = ((rem < len) ? (unsigned)rem : len); + SizeT pos = (dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0); + + processedPos += curLen; + + len -= curLen; + if (pos + curLen <= dicBufSize) + { + Byte *dest = dic + dicPos; + ptrdiff_t src = (ptrdiff_t)pos - (ptrdiff_t)dicPos; + const Byte *lim = dest + curLen; + dicPos += curLen; + do + *(dest) = (Byte)*(dest + src); + while (++dest != lim); + } + else + { + do + { + dic[dicPos++] = dic[pos]; + if (++pos == dicBufSize) + pos = 0; + } + while (--curLen != 0); + } + } + } + } + while (dicPos < limit && buf < bufLimit); + NORMALIZE; + p->buf = buf; + p->range = range; + p->code = code; + p->remainLen = len; + p->dicPos = dicPos; + p->processedPos = processedPos; + p->reps[0] = rep0; + p->reps[1] = rep1; + p->reps[2] = rep2; + p->reps[3] = rep3; + p->state = state; + + return SZ_OK; +} + +static void MY_FAST_CALL LzmaDec_WriteRem(CLzmaDec *p, SizeT limit) +{ + if (p->remainLen != 0 && p->remainLen < kMatchSpecLenStart) + { + Byte *dic = p->dic; + SizeT dicPos = p->dicPos; + SizeT dicBufSize = p->dicBufSize; + unsigned len = p->remainLen; + UInt32 rep0 = p->reps[0]; + if (limit - dicPos < len) + len = (unsigned)(limit - dicPos); + + if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= len) + p->checkDicSize = p->prop.dicSize; + + p->processedPos += len; + p->remainLen -= len; + while (len-- != 0) + { + dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; + dicPos++; + } + p->dicPos = dicPos; + } +} + +static int MY_FAST_CALL LzmaDec_DecodeReal2(CLzmaDec *p, SizeT limit, const Byte *bufLimit) +{ + do + { + SizeT limit2 = limit; + if (p->checkDicSize == 0) + { + UInt32 rem = p->prop.dicSize - p->processedPos; + if (limit - p->dicPos > rem) + limit2 = p->dicPos + rem; + } + RINOK(LzmaDec_DecodeReal(p, limit2, bufLimit)); + if (p->processedPos >= p->prop.dicSize) + p->checkDicSize = p->prop.dicSize; + LzmaDec_WriteRem(p, limit); + } + while (p->dicPos < limit && p->buf < bufLimit && p->remainLen < kMatchSpecLenStart); + + if (p->remainLen > kMatchSpecLenStart) + { + p->remainLen = kMatchSpecLenStart; + } + return 0; +} + +typedef enum +{ + DUMMY_ERROR, /* unexpected end of input stream */ + DUMMY_LIT, + DUMMY_MATCH, + DUMMY_REP +} ELzmaDummy; + +static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inSize) +{ + UInt32 range = p->range; + UInt32 code = p->code; + const Byte *bufLimit = buf + inSize; + CLzmaProb *probs = p->probs; + unsigned state = p->state; + ELzmaDummy res; + + { + CLzmaProb *prob; + UInt32 bound; + unsigned ttt; + unsigned posState = (p->processedPos) & ((1 << p->prop.pb) - 1); + + prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK + + /* if (bufLimit - buf >= 7) return DUMMY_LIT; */ + + prob = probs + Literal; + if (p->checkDicSize != 0 || p->processedPos != 0) + prob += (LZMA_LIT_SIZE * + ((((p->processedPos) & ((1 << (p->prop.lp)) - 1)) << p->prop.lc) + + (p->dic[(p->dicPos == 0 ? p->dicBufSize : p->dicPos) - 1] >> (8 - p->prop.lc)))); + + if (state < kNumLitStates) + { + unsigned symbol = 1; + do { GET_BIT_CHECK(prob + symbol, symbol) } while (symbol < 0x100); + } + else + { + unsigned matchByte = p->dic[p->dicPos - p->reps[0] + + ((p->dicPos < p->reps[0]) ? p->dicBufSize : 0)]; + unsigned offs = 0x100; + unsigned symbol = 1; + do + { + unsigned bit; + CLzmaProb *probLit; + matchByte <<= 1; + bit = (matchByte & offs); + probLit = prob + offs + bit + symbol; + GET_BIT2_CHECK(probLit, symbol, offs &= ~bit, offs &= bit) + } + while (symbol < 0x100); + } + res = DUMMY_LIT; + } + else + { + unsigned len; + UPDATE_1_CHECK; + + prob = probs + IsRep + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + state = 0; + prob = probs + LenCoder; + res = DUMMY_MATCH; + } + else + { + UPDATE_1_CHECK; + res = DUMMY_REP; + prob = probs + IsRepG0 + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + NORMALIZE_CHECK; + return DUMMY_REP; + } + else + { + UPDATE_1_CHECK; + } + } + else + { + UPDATE_1_CHECK; + prob = probs + IsRepG1 + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + } + else + { + UPDATE_1_CHECK; + prob = probs + IsRepG2 + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + } + else + { + UPDATE_1_CHECK; + } + } + } + state = kNumStates; + prob = probs + RepLenCoder; + } + { + unsigned limit, offset; + CLzmaProb *probLen = prob + LenChoice; + IF_BIT_0_CHECK(probLen) + { + UPDATE_0_CHECK; + probLen = prob + LenLow + (posState << kLenNumLowBits); + offset = 0; + limit = 1 << kLenNumLowBits; + } + else + { + UPDATE_1_CHECK; + probLen = prob + LenChoice2; + IF_BIT_0_CHECK(probLen) + { + UPDATE_0_CHECK; + probLen = prob + LenMid + (posState << kLenNumMidBits); + offset = kLenNumLowSymbols; + limit = 1 << kLenNumMidBits; + } + else + { + UPDATE_1_CHECK; + probLen = prob + LenHigh; + offset = kLenNumLowSymbols + kLenNumMidSymbols; + limit = 1 << kLenNumHighBits; + } + } + TREE_DECODE_CHECK(probLen, limit, len); + len += offset; + } + + if (state < 4) + { + unsigned posSlot; + prob = probs + PosSlot + + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << + kNumPosSlotBits); + TREE_DECODE_CHECK(prob, 1 << kNumPosSlotBits, posSlot); + if (posSlot >= kStartPosModelIndex) + { + int numDirectBits = ((posSlot >> 1) - 1); + + /* if (bufLimit - buf >= 8) return DUMMY_MATCH; */ + + if (posSlot < kEndPosModelIndex) + { + prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits) - posSlot - 1; + } + else + { + numDirectBits -= kNumAlignBits; + do + { + NORMALIZE_CHECK + range >>= 1; + code -= range & (((code - range) >> 31) - 1); + /* if (code >= range) code -= range; */ + } + while (--numDirectBits != 0); + prob = probs + Align; + numDirectBits = kNumAlignBits; + } + { + unsigned i = 1; + do + { + GET_BIT_CHECK(prob + i, i); + } + while (--numDirectBits != 0); + } + } + } + } + } + NORMALIZE_CHECK; + return res; +} + + +static void LzmaDec_InitRc(CLzmaDec *p, const Byte *data) +{ + p->code = ((UInt32)data[1] << 24) | ((UInt32)data[2] << 16) | ((UInt32)data[3] << 8) | ((UInt32)data[4]); + p->range = 0xFFFFFFFF; + p->needFlush = 0; +} + +void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState) +{ + p->needFlush = 1; + p->remainLen = 0; + p->tempBufSize = 0; + + if (initDic) + { + p->processedPos = 0; + p->checkDicSize = 0; + p->needInitState = 1; + } + if (initState) + p->needInitState = 1; +} + +void LzmaDec_Init(CLzmaDec *p) +{ + p->dicPos = 0; + LzmaDec_InitDicAndState(p, True, True); +} + +static void LzmaDec_InitStateReal(CLzmaDec *p) +{ + UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (p->prop.lc + p->prop.lp)); + UInt32 i; + CLzmaProb *probs = p->probs; + for (i = 0; i < numProbs; i++) + probs[i] = kBitModelTotal >> 1; + p->reps[0] = p->reps[1] = p->reps[2] = p->reps[3] = 1; + p->state = 0; + p->needInitState = 0; +} + +SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen, + ELzmaFinishMode finishMode, ELzmaStatus *status) +{ + SizeT inSize = *srcLen; + (*srcLen) = 0; + LzmaDec_WriteRem(p, dicLimit); + + *status = LZMA_STATUS_NOT_SPECIFIED; + + while (p->remainLen != kMatchSpecLenStart) + { + int checkEndMarkNow; + + if (p->needFlush != 0) + { + for (; inSize > 0 && p->tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--) + p->tempBuf[p->tempBufSize++] = *src++; + if (p->tempBufSize < RC_INIT_SIZE) + { + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + if (p->tempBuf[0] != 0) + return SZ_ERROR_DATA; + + LzmaDec_InitRc(p, p->tempBuf); + p->tempBufSize = 0; + } + + checkEndMarkNow = 0; + if (p->dicPos >= dicLimit) + { + if (p->remainLen == 0 && p->code == 0) + { + *status = LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK; + return SZ_OK; + } + if (finishMode == LZMA_FINISH_ANY) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_OK; + } + if (p->remainLen != 0) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_ERROR_DATA; + } + checkEndMarkNow = 1; + } + + if (p->needInitState) + LzmaDec_InitStateReal(p); + + if (p->tempBufSize == 0) + { + SizeT processed; + const Byte *bufLimit; + if (inSize < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) + { + int dummyRes = LzmaDec_TryDummy(p, src, inSize); + if (dummyRes == DUMMY_ERROR) + { + memcpy(p->tempBuf, src, inSize); + p->tempBufSize = (unsigned)inSize; + (*srcLen) += inSize; + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + if (checkEndMarkNow && dummyRes != DUMMY_MATCH) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_ERROR_DATA; + } + bufLimit = src; + } + else + bufLimit = src + inSize - LZMA_REQUIRED_INPUT_MAX; + p->buf = src; + if (LzmaDec_DecodeReal2(p, dicLimit, bufLimit) != 0) + return SZ_ERROR_DATA; + processed = (SizeT)(p->buf - src); + (*srcLen) += processed; + src += processed; + inSize -= processed; + } + else + { + unsigned rem = p->tempBufSize, lookAhead = 0; + while (rem < LZMA_REQUIRED_INPUT_MAX && lookAhead < inSize) + p->tempBuf[rem++] = src[lookAhead++]; + p->tempBufSize = rem; + if (rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) + { + int dummyRes = LzmaDec_TryDummy(p, p->tempBuf, rem); + if (dummyRes == DUMMY_ERROR) + { + (*srcLen) += lookAhead; + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + if (checkEndMarkNow && dummyRes != DUMMY_MATCH) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_ERROR_DATA; + } + } + p->buf = p->tempBuf; + if (LzmaDec_DecodeReal2(p, dicLimit, p->buf) != 0) + return SZ_ERROR_DATA; + lookAhead -= (rem - (unsigned)(p->buf - p->tempBuf)); + (*srcLen) += lookAhead; + src += lookAhead; + inSize -= lookAhead; + p->tempBufSize = 0; + } + } + if (p->code == 0) + *status = LZMA_STATUS_FINISHED_WITH_MARK; + return (p->code == 0) ? SZ_OK : SZ_ERROR_DATA; +} + +SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) +{ + SizeT outSize = *destLen; + SizeT inSize = *srcLen; + *srcLen = *destLen = 0; + for (;;) + { + SizeT inSizeCur = inSize, outSizeCur, dicPos; + ELzmaFinishMode curFinishMode; + SRes res; + if (p->dicPos == p->dicBufSize) + p->dicPos = 0; + dicPos = p->dicPos; + if (outSize > p->dicBufSize - dicPos) + { + outSizeCur = p->dicBufSize; + curFinishMode = LZMA_FINISH_ANY; + } + else + { + outSizeCur = dicPos + outSize; + curFinishMode = finishMode; + } + + res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status); + src += inSizeCur; + inSize -= inSizeCur; + *srcLen += inSizeCur; + outSizeCur = p->dicPos - dicPos; + memcpy(dest, p->dic + dicPos, outSizeCur); + dest += outSizeCur; + outSize -= outSizeCur; + *destLen += outSizeCur; + if (res != 0) + return res; + if (outSizeCur == 0 || outSize == 0) + return SZ_OK; + } +} + +void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->probs); + p->probs = 0; +} + +static void LzmaDec_FreeDict(CLzmaDec *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->dic); + p->dic = 0; +} + +void LzmaDec_Free(CLzmaDec *p, ISzAlloc *alloc) +{ + LzmaDec_FreeProbs(p, alloc); + LzmaDec_FreeDict(p, alloc); +} + +SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size) +{ + UInt32 dicSize; + Byte d; + + if (size < LZMA_PROPS_SIZE) + return SZ_ERROR_UNSUPPORTED; + else + dicSize = data[1] | ((UInt32)data[2] << 8) | ((UInt32)data[3] << 16) | ((UInt32)data[4] << 24); + + if (dicSize < LZMA_DIC_MIN) + dicSize = LZMA_DIC_MIN; + p->dicSize = dicSize; + + d = data[0]; + if (d >= (9 * 5 * 5)) + return SZ_ERROR_UNSUPPORTED; + + p->lc = d % 9; + d /= 9; + p->pb = d / 5; + p->lp = d % 5; + + return SZ_OK; +} + +static SRes LzmaDec_AllocateProbs2(CLzmaDec *p, const CLzmaProps *propNew, ISzAlloc *alloc) +{ + UInt32 numProbs = LzmaProps_GetNumProbs(propNew); + if (p->probs == 0 || numProbs != p->numProbs) + { + LzmaDec_FreeProbs(p, alloc); + p->probs = (CLzmaProb *)alloc->Alloc(alloc, numProbs * sizeof(CLzmaProb)); + p->numProbs = numProbs; + if (p->probs == 0) + return SZ_ERROR_MEM; + } + return SZ_OK; +} + +SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) +{ + CLzmaProps propNew; + RINOK(LzmaProps_Decode(&propNew, props, propsSize)); + RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); + p->prop = propNew; + return SZ_OK; +} + +SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) +{ + CLzmaProps propNew; + SizeT dicBufSize; + RINOK(LzmaProps_Decode(&propNew, props, propsSize)); + RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); + dicBufSize = propNew.dicSize; + if (p->dic == 0 || dicBufSize != p->dicBufSize) + { + LzmaDec_FreeDict(p, alloc); + p->dic = (Byte *)alloc->Alloc(alloc, dicBufSize); + if (p->dic == 0) + { + LzmaDec_FreeProbs(p, alloc); + return SZ_ERROR_MEM; + } + } + p->dicBufSize = dicBufSize; + p->prop = propNew; + return SZ_OK; +} + +SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, + const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, + ELzmaStatus *status, ISzAlloc *alloc) +{ + CLzmaDec p; + SRes res; + SizeT inSize = *srcLen; + SizeT outSize = *destLen; + *srcLen = *destLen = 0; + if (inSize < RC_INIT_SIZE) + return SZ_ERROR_INPUT_EOF; + + LzmaDec_Construct(&p); + res = LzmaDec_AllocateProbs(&p, propData, propSize, alloc); + if (res != 0) + return res; + p.dic = dest; + p.dicBufSize = outSize; + + LzmaDec_Init(&p); + + *srcLen = inSize; + res = LzmaDec_DecodeToDic(&p, outSize, src, srcLen, finishMode, status); + + if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT) + res = SZ_ERROR_INPUT_EOF; + + (*destLen) = p.dicPos; + LzmaDec_FreeProbs(&p, alloc); + return res; +} diff --git a/fex/7z_C/LzmaDec.h b/fex/7z_C/LzmaDec.h new file mode 100644 index 0000000..bf7f084 --- /dev/null +++ b/fex/7z_C/LzmaDec.h @@ -0,0 +1,231 @@ +/* LzmaDec.h -- LZMA Decoder +2009-02-07 : Igor Pavlov : Public domain */ + +#ifndef __LZMA_DEC_H +#define __LZMA_DEC_H + +#include "Types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* #define _LZMA_PROB32 */ +/* _LZMA_PROB32 can increase the speed on some CPUs, + but memory usage for CLzmaDec::probs will be doubled in that case */ + +#ifdef _LZMA_PROB32 +#define CLzmaProb UInt32 +#else +#define CLzmaProb UInt16 +#endif + + +/* ---------- LZMA Properties ---------- */ + +#define LZMA_PROPS_SIZE 5 + +typedef struct _CLzmaProps +{ + unsigned lc, lp, pb; + UInt32 dicSize; +} CLzmaProps; + +/* LzmaProps_Decode - decodes properties +Returns: + SZ_OK + SZ_ERROR_UNSUPPORTED - Unsupported properties +*/ + +SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size); + + +/* ---------- LZMA Decoder state ---------- */ + +/* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case. + Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */ + +#define LZMA_REQUIRED_INPUT_MAX 20 + +typedef struct +{ + CLzmaProps prop; + CLzmaProb *probs; + Byte *dic; + const Byte *buf; + UInt32 range, code; + SizeT dicPos; + SizeT dicBufSize; + UInt32 processedPos; + UInt32 checkDicSize; + unsigned state; + UInt32 reps[4]; + unsigned remainLen; + int needFlush; + int needInitState; + UInt32 numProbs; + unsigned tempBufSize; + Byte tempBuf[LZMA_REQUIRED_INPUT_MAX]; +} CLzmaDec; + +#define LzmaDec_Construct(p) { (p)->dic = 0; (p)->probs = 0; } + +void LzmaDec_Init(CLzmaDec *p); + +/* There are two types of LZMA streams: + 0) Stream with end mark. That end mark adds about 6 bytes to compressed size. + 1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */ + +typedef enum +{ + LZMA_FINISH_ANY, /* finish at any point */ + LZMA_FINISH_END /* block must be finished at the end */ +} ELzmaFinishMode; + +/* ELzmaFinishMode has meaning only if the decoding reaches output limit !!! + + You must use LZMA_FINISH_END, when you know that current output buffer + covers last bytes of block. In other cases you must use LZMA_FINISH_ANY. + + If LZMA decoder sees end marker before reaching output limit, it returns SZ_OK, + and output value of destLen will be less than output buffer size limit. + You can check status result also. + + You can use multiple checks to test data integrity after full decompression: + 1) Check Result and "status" variable. + 2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize. + 3) Check that output(srcLen) = compressedSize, if you know real compressedSize. + You must use correct finish mode in that case. */ + +typedef enum +{ + LZMA_STATUS_NOT_SPECIFIED, /* use main error code instead */ + LZMA_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */ + LZMA_STATUS_NOT_FINISHED, /* stream was not finished */ + LZMA_STATUS_NEEDS_MORE_INPUT, /* you must provide more input bytes */ + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK /* there is probability that stream was finished without end mark */ +} ELzmaStatus; + +/* ELzmaStatus is used only as output value for function call */ + + +/* ---------- Interfaces ---------- */ + +/* There are 3 levels of interfaces: + 1) Dictionary Interface + 2) Buffer Interface + 3) One Call Interface + You can select any of these interfaces, but don't mix functions from different + groups for same object. */ + + +/* There are two variants to allocate state for Dictionary Interface: + 1) LzmaDec_Allocate / LzmaDec_Free + 2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs + You can use variant 2, if you set dictionary buffer manually. + For Buffer Interface you must always use variant 1. + +LzmaDec_Allocate* can return: + SZ_OK + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_UNSUPPORTED - Unsupported properties +*/ + +SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc); +void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc); + +SRes LzmaDec_Allocate(CLzmaDec *state, const Byte *prop, unsigned propsSize, ISzAlloc *alloc); +void LzmaDec_Free(CLzmaDec *state, ISzAlloc *alloc); + +/* ---------- Dictionary Interface ---------- */ + +/* You can use it, if you want to eliminate the overhead for data copying from + dictionary to some other external buffer. + You must work with CLzmaDec variables directly in this interface. + + STEPS: + LzmaDec_Constr() + LzmaDec_Allocate() + for (each new stream) + { + LzmaDec_Init() + while (it needs more decompression) + { + LzmaDec_DecodeToDic() + use data from CLzmaDec::dic and update CLzmaDec::dicPos + } + } + LzmaDec_Free() +*/ + +/* LzmaDec_DecodeToDic + + The decoding to internal dictionary buffer (CLzmaDec::dic). + You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!! + +finishMode: + It has meaning only if the decoding reaches output limit (dicLimit). + LZMA_FINISH_ANY - Decode just dicLimit bytes. + LZMA_FINISH_END - Stream must be finished after dicLimit. + +Returns: + SZ_OK + status: + LZMA_STATUS_FINISHED_WITH_MARK + LZMA_STATUS_NOT_FINISHED + LZMA_STATUS_NEEDS_MORE_INPUT + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK + SZ_ERROR_DATA - Data error +*/ + +SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, + const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); + + +/* ---------- Buffer Interface ---------- */ + +/* It's zlib-like interface. + See LzmaDec_DecodeToDic description for information about STEPS and return results, + but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need + to work with CLzmaDec variables manually. + +finishMode: + It has meaning only if the decoding reaches output limit (*destLen). + LZMA_FINISH_ANY - Decode just destLen bytes. + LZMA_FINISH_END - Stream must be finished after (*destLen). +*/ + +SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, + const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); + + +/* ---------- One Call Interface ---------- */ + +/* LzmaDecode + +finishMode: + It has meaning only if the decoding reaches output limit (*destLen). + LZMA_FINISH_ANY - Decode just destLen bytes. + LZMA_FINISH_END - Stream must be finished after (*destLen). + +Returns: + SZ_OK + status: + LZMA_STATUS_FINISHED_WITH_MARK + LZMA_STATUS_NOT_FINISHED + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK + SZ_ERROR_DATA - Data error + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_UNSUPPORTED - Unsupported properties + SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src). +*/ + +SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, + const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, + ELzmaStatus *status, ISzAlloc *alloc); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/fex/7z_C/Ppmd.h b/fex/7z_C/Ppmd.h new file mode 100644 index 0000000..14344a7 --- /dev/null +++ b/fex/7z_C/Ppmd.h @@ -0,0 +1,81 @@ +/* Ppmd.h -- PPMD codec common code +2010-03-12 : Igor Pavlov : Public domain +This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */ + +#ifndef __PPMD_H +#define __PPMD_H + +#include "Types.h" +#include "CpuArch.h" + +EXTERN_C_BEGIN + +#ifdef MY_CPU_32BIT + #define PPMD_32BIT +#endif + +#define PPMD_INT_BITS 7 +#define PPMD_PERIOD_BITS 7 +#define PPMD_BIN_SCALE (1 << (PPMD_INT_BITS + PPMD_PERIOD_BITS)) + +#define PPMD_GET_MEAN_SPEC(summ, shift, round) (((summ) + (1 << ((shift) - (round)))) >> (shift)) +#define PPMD_GET_MEAN(summ) PPMD_GET_MEAN_SPEC((summ), PPMD_PERIOD_BITS, 2) +#define PPMD_UPDATE_PROB_0(prob) ((prob) + (1 << PPMD_INT_BITS) - PPMD_GET_MEAN(prob)) +#define PPMD_UPDATE_PROB_1(prob) ((prob) - PPMD_GET_MEAN(prob)) + +#define PPMD_N1 4 +#define PPMD_N2 4 +#define PPMD_N3 4 +#define PPMD_N4 ((128 + 3 - 1 * PPMD_N1 - 2 * PPMD_N2 - 3 * PPMD_N3) / 4) +#define PPMD_NUM_INDEXES (PPMD_N1 + PPMD_N2 + PPMD_N3 + PPMD_N4) + +/* SEE-contexts for PPM-contexts with masked symbols */ +typedef struct +{ + UInt16 Summ; /* Freq */ + Byte Shift; /* Speed of Freq change; low Shift is for fast change */ + Byte Count; /* Count to next change of Shift */ +} CPpmd_See; + +#define Ppmd_See_Update(p) if ((p)->Shift < PPMD_PERIOD_BITS && --(p)->Count == 0) \ + { (p)->Summ <<= 1; (p)->Count = (Byte)(3 << (p)->Shift++); } + +typedef struct +{ + Byte Symbol; + Byte Freq; + UInt16 SuccessorLow; + UInt16 SuccessorHigh; +} CPpmd_State; + +typedef + #ifdef PPMD_32BIT + CPpmd_State * + #else + UInt32 + #endif + CPpmd_State_Ref; + +typedef + #ifdef PPMD_32BIT + void * + #else + UInt32 + #endif + CPpmd_Void_Ref; + +typedef + #ifdef PPMD_32BIT + Byte * + #else + UInt32 + #endif + CPpmd_Byte_Ref; + +#define PPMD_SetAllBitsIn256Bytes(p) \ + { unsigned i; for (i = 0; i < 256 / sizeof(p[0]); i += 8) { \ + p[i+7] = p[i+6] = p[i+5] = p[i+4] = p[i+3] = p[i+2] = p[i+1] = p[i+0] = ~(size_t)0; }} + +EXTERN_C_END + +#endif diff --git a/fex/7z_C/Ppmd7.c b/fex/7z_C/Ppmd7.c new file mode 100644 index 0000000..4b160cf --- /dev/null +++ b/fex/7z_C/Ppmd7.c @@ -0,0 +1,708 @@ +/* Ppmd7.c -- PPMdH codec +2010-03-12 : Igor Pavlov : Public domain +This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */ + +#include + +#include "Ppmd7.h" + +const Byte PPMD7_kExpEscape[16] = { 25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 }; +static const UInt16 kInitBinEsc[] = { 0x3CDD, 0x1F3F, 0x59BF, 0x48F3, 0x64A1, 0x5ABC, 0x6632, 0x6051}; + +#define MAX_FREQ 124 +#define UNIT_SIZE 12 + +#define U2B(nu) ((UInt32)(nu) * UNIT_SIZE) +#define U2I(nu) (p->Units2Indx[(nu) - 1]) +#define I2U(indx) (p->Indx2Units[indx]) + +#ifdef PPMD_32BIT + #define REF(ptr) (ptr) +#else + #define REF(ptr) ((UInt32)((Byte *)(ptr) - (p)->Base)) +#endif + +#define STATS_REF(ptr) ((CPpmd_State_Ref)REF(ptr)) + +#define CTX(ref) ((CPpmd7_Context *)Ppmd7_GetContext(p, ref)) +#define STATS(ctx) Ppmd7_GetStats(p, ctx) +#define ONE_STATE(ctx) Ppmd7Context_OneState(ctx) +#define SUFFIX(ctx) CTX((ctx)->Suffix) + +typedef CPpmd7_Context * CTX_PTR; + +struct CPpmd7_Node_; + +typedef + #ifdef PPMD_32BIT + struct CPpmd7_Node_ * + #else + UInt32 + #endif + CPpmd7_Node_Ref; + +typedef struct CPpmd7_Node_ +{ + UInt16 Stamp; /* must be at offset 0 as CPpmd7_Context::NumStats. Stamp=0 means free */ + UInt16 NU; + CPpmd7_Node_Ref Next; /* must be at offset >= 4 */ + CPpmd7_Node_Ref Prev; +} CPpmd7_Node; + +#ifdef PPMD_32BIT + #define NODE(ptr) (ptr) +#else + #define NODE(offs) ((CPpmd7_Node *)(p->Base + (offs))) +#endif + +void Ppmd7_Construct(CPpmd7 *p) +{ + unsigned i, k, m; + + p->Base = 0; + + for (i = 0, k = 0; i < PPMD_NUM_INDEXES; i++) + { + unsigned step = (i >= 12 ? 4 : (i >> 2) + 1); + do { p->Units2Indx[k++] = (Byte)i; } while(--step); + p->Indx2Units[i] = (Byte)k; + } + + p->NS2BSIndx[0] = (0 << 1); + p->NS2BSIndx[1] = (1 << 1); + memset(p->NS2BSIndx + 2, (2 << 1), 9); + memset(p->NS2BSIndx + 11, (3 << 1), 256 - 11); + + for (i = 0; i < 3; i++) + p->NS2Indx[i] = (Byte)i; + for (m = i, k = 1; i < 256; i++) + { + p->NS2Indx[i] = (Byte)m; + if (--k == 0) + k = (++m) - 2; + } + + memset(p->HB2Flag, 0, 0x40); + memset(p->HB2Flag + 0x40, 8, 0x100 - 0x40); +} + +void Ppmd7_Free(CPpmd7 *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->Base); + p->Size = 0; + p->Base = 0; +} + +Bool Ppmd7_Alloc(CPpmd7 *p, UInt32 size, ISzAlloc *alloc) +{ + if (p->Base == 0 || p->Size != size) + { + Ppmd7_Free(p, alloc); + p->AlignOffset = + #ifdef PPMD_32BIT + (4 - size) & 3; + #else + 4 - (size & 3); + #endif + if ((p->Base = (Byte *)alloc->Alloc(alloc, p->AlignOffset + size + #ifndef PPMD_32BIT + + UNIT_SIZE + #endif + )) == 0) + return False; + p->Size = size; + } + return True; +} + +static void InsertNode(CPpmd7 *p, void *node, unsigned indx) +{ + *((CPpmd_Void_Ref *)node) = p->FreeList[indx]; + p->FreeList[indx] = REF(node); +} + +static void *RemoveNode(CPpmd7 *p, unsigned indx) +{ + CPpmd_Void_Ref *node = (CPpmd_Void_Ref *)Ppmd7_GetPtr(p, p->FreeList[indx]); + p->FreeList[indx] = *node; + return node; +} + +static void SplitBlock(CPpmd7 *p, void *ptr, unsigned oldIndx, unsigned newIndx) +{ + unsigned i, nu = I2U(oldIndx) - I2U(newIndx); + ptr = (Byte *)ptr + U2B(I2U(newIndx)); + if (I2U(i = U2I(nu)) != nu) + { + unsigned k = I2U(--i); + InsertNode(p, ((Byte *)ptr) + U2B(k), nu - k - 1); + } + InsertNode(p, ptr, i); +} + +static void GlueFreeBlocks(CPpmd7 *p) +{ + #ifdef PPMD_32BIT + CPpmd7_Node headItem; + CPpmd7_Node_Ref head = &headItem; + #else + CPpmd7_Node_Ref head = p->AlignOffset + p->Size; + #endif + + CPpmd7_Node_Ref n = head; + unsigned i; + + p->GlueCount = 255; + + /* create doubly-linked list of free blocks */ + for (i = 0; i < PPMD_NUM_INDEXES; i++) + { + UInt16 nu = I2U(i); + CPpmd7_Node_Ref next = (CPpmd7_Node_Ref)p->FreeList[i]; + p->FreeList[i] = 0; + while (next != 0) + { + CPpmd7_Node *node = NODE(next); + node->Next = n; + n = NODE(n)->Prev = next; + next = *(const CPpmd7_Node_Ref *)node; + node->Stamp = 0; + node->NU = (UInt16)nu; + } + } + NODE(head)->Stamp = 1; + NODE(head)->Next = n; + NODE(n)->Prev = head; + if (p->LoUnit != p->HiUnit) + ((CPpmd7_Node *)p->LoUnit)->Stamp = 1; + + /* Glue free blocks */ + while (n != head) + { + CPpmd7_Node *node = NODE(n); + UInt32 nu = (UInt32)node->NU; + for (;;) + { + CPpmd7_Node *node2 = NODE(n) + nu; + nu += node2->NU; + if (node2->Stamp != 0 || nu >= 0x10000) + break; + NODE(node2->Prev)->Next = node2->Next; + NODE(node2->Next)->Prev = node2->Prev; + node->NU = (UInt16)nu; + } + n = node->Next; + } + + /* Fill lists of free blocks */ + for (n = NODE(head)->Next; n != head;) + { + CPpmd7_Node *node = NODE(n); + unsigned nu; + CPpmd7_Node_Ref next = node->Next; + for (nu = node->NU; nu > 128; nu -= 128, node += 128) + InsertNode(p, node, PPMD_NUM_INDEXES - 1); + if (I2U(i = U2I(nu)) != nu) + { + unsigned k = I2U(--i); + InsertNode(p, node + k, nu - k - 1); + } + InsertNode(p, node, i); + n = next; + } +} + +static void *AllocUnitsRare(CPpmd7 *p, unsigned indx) +{ + unsigned i; + void *retVal; + if (p->GlueCount == 0) + { + GlueFreeBlocks(p); + if (p->FreeList[indx] != 0) + return RemoveNode(p, indx); + } + i = indx; + do + { + if (++i == PPMD_NUM_INDEXES) + { + UInt32 numBytes = U2B(I2U(indx)); + p->GlueCount--; + return ((UInt32)(p->UnitsStart - p->Text) > numBytes) ? (p->UnitsStart -= numBytes) : (NULL); + } + } + while (p->FreeList[i] == 0); + retVal = RemoveNode(p, i); + SplitBlock(p, retVal, i, indx); + return retVal; +} + +static void *AllocUnits(CPpmd7 *p, unsigned indx) +{ + UInt32 numBytes; + if (p->FreeList[indx] != 0) + return RemoveNode(p, indx); + numBytes = U2B(I2U(indx)); + if (numBytes <= (UInt32)(p->HiUnit - p->LoUnit)) + { + void *retVal = p->LoUnit; + p->LoUnit += numBytes; + return retVal; + } + return AllocUnitsRare(p, indx); +} + +#define MyMem12Cpy(dest, src, num) \ + { UInt32 *d = (UInt32 *)dest; const UInt32 *s = (const UInt32 *)src; UInt32 n = num; \ + do { d[0] = s[0]; d[1] = s[1]; d[2] = s[2]; s += 3; d += 3; } while(--n); } + +static void *ShrinkUnits(CPpmd7 *p, void *oldPtr, unsigned oldNU, unsigned newNU) +{ + unsigned i0 = U2I(oldNU); + unsigned i1 = U2I(newNU); + if (i0 == i1) + return oldPtr; + if (p->FreeList[i1] != 0) + { + void *ptr = RemoveNode(p, i1); + MyMem12Cpy(ptr, oldPtr, newNU); + InsertNode(p, oldPtr, i0); + return ptr; + } + SplitBlock(p, oldPtr, i0, i1); + return oldPtr; +} + +#define SUCCESSOR(p) ((CPpmd_Void_Ref)((p)->SuccessorLow | ((UInt32)(p)->SuccessorHigh << 16))) + +static void SetSuccessor(CPpmd_State *p, CPpmd_Void_Ref v) +{ + (p)->SuccessorLow = (UInt16)((UInt32)(v) & 0xFFFF); + (p)->SuccessorHigh = (UInt16)(((UInt32)(v) >> 16) & 0xFFFF); +} + +static void RestartModel(CPpmd7 *p) +{ + unsigned i, k, m; + + memset(p->FreeList, 0, sizeof(p->FreeList)); + p->Text = p->Base + p->AlignOffset; + p->HiUnit = p->Text + p->Size; + p->LoUnit = p->UnitsStart = p->HiUnit - p->Size / 8 / UNIT_SIZE * 7 * UNIT_SIZE; + p->GlueCount = 0; + + p->OrderFall = p->MaxOrder; + p->RunLength = p->InitRL = -(Int32)((p->MaxOrder < 12) ? p->MaxOrder : 12) - 1; + p->PrevSuccess = 0; + + p->MinContext = p->MaxContext = (CTX_PTR)(p->HiUnit -= UNIT_SIZE); /* AllocContext(p); */ + p->MinContext->Suffix = 0; + p->MinContext->NumStats = 256; + p->MinContext->SummFreq = 256 + 1; + p->FoundState = (CPpmd_State *)p->LoUnit; /* AllocUnits(p, PPMD_NUM_INDEXES - 1); */ + p->LoUnit += U2B(256 / 2); + p->MinContext->Stats = REF(p->FoundState); + for (i = 0; i < 256; i++) + { + CPpmd_State *s = &p->FoundState[i]; + s->Symbol = (Byte)i; + s->Freq = 1; + SetSuccessor(s, 0); + } + + for (i = 0; i < 128; i++) + for (k = 0; k < 8; k++) + { + UInt16 *dest = p->BinSumm[i] + k; + UInt16 val = (UInt16)(PPMD_BIN_SCALE - kInitBinEsc[k] / (i + 2)); + for (m = 0; m < 64; m += 8) + dest[m] = val; + } + + for (i = 0; i < 25; i++) + for (k = 0; k < 16; k++) + { + CPpmd_See *s = &p->See[i][k]; + s->Summ = (UInt16)((5 * i + 10) << (s->Shift = PPMD_PERIOD_BITS - 4)); + s->Count = 4; + } +} + +void Ppmd7_Init(CPpmd7 *p, unsigned maxOrder) +{ + p->MaxOrder = maxOrder; + RestartModel(p); + p->DummySee.Shift = PPMD_PERIOD_BITS; + p->DummySee.Summ = 0; /* unused */ + p->DummySee.Count = 64; /* unused */ +} + +static CTX_PTR CreateSuccessors(CPpmd7 *p, Bool skip) +{ + CPpmd_State upState; + CTX_PTR c = p->MinContext; + CPpmd_Byte_Ref upBranch = (CPpmd_Byte_Ref)SUCCESSOR(p->FoundState); + CPpmd_State *ps[PPMD7_MAX_ORDER]; + unsigned numPs = 0; + + if (!skip) + ps[numPs++] = p->FoundState; + + while (c->Suffix) + { + CPpmd_Void_Ref successor; + CPpmd_State *s; + c = SUFFIX(c); + if (c->NumStats != 1) + { + for (s = STATS(c); s->Symbol != p->FoundState->Symbol; s++); + } + else + s = ONE_STATE(c); + successor = SUCCESSOR(s); + if (successor != upBranch) + { + c = CTX(successor); + if (numPs == 0) + return c; + break; + } + ps[numPs++] = s; + } + + upState.Symbol = *(const Byte *)Ppmd7_GetPtr(p, upBranch); + SetSuccessor(&upState, upBranch + 1); + + if (c->NumStats == 1) + upState.Freq = ONE_STATE(c)->Freq; + else + { + UInt32 cf, s0; + CPpmd_State *s; + for (s = STATS(c); s->Symbol != upState.Symbol; s++); + cf = s->Freq - 1; + s0 = c->SummFreq - c->NumStats - cf; + upState.Freq = (Byte)(1 + ((2 * cf <= s0) ? (5 * cf > s0) : ((2 * cf + 3 * s0 - 1) / (2 * s0)))); + } + + do + { + /* Create Child */ + CTX_PTR c1; /* = AllocContext(p); */ + if (p->HiUnit != p->LoUnit) + c1 = (CTX_PTR)(p->HiUnit -= UNIT_SIZE); + else if (p->FreeList[0] != 0) + c1 = (CTX_PTR)RemoveNode(p, 0); + else + { + c1 = (CTX_PTR)AllocUnitsRare(p, 0); + if (!c1) + return NULL; + } + c1->NumStats = 1; + *ONE_STATE(c1) = upState; + c1->Suffix = REF(c); + SetSuccessor(ps[--numPs], REF(c1)); + c = c1; + } + while (numPs != 0); + + return c; +} + +static void SwapStates(CPpmd_State *t1, CPpmd_State *t2) +{ + CPpmd_State tmp = *t1; + *t1 = *t2; + *t2 = tmp; +} + +static void UpdateModel(CPpmd7 *p) +{ + CPpmd_Void_Ref successor, fSuccessor = SUCCESSOR(p->FoundState); + CTX_PTR c; + unsigned s0, ns; + + if (p->FoundState->Freq < MAX_FREQ / 4 && p->MinContext->Suffix != 0) + { + c = SUFFIX(p->MinContext); + + if (c->NumStats == 1) + { + CPpmd_State *s = ONE_STATE(c); + if (s->Freq < 32) + s->Freq++; + } + else + { + CPpmd_State *s = STATS(c); + if (s->Symbol != p->FoundState->Symbol) + { + do { s++; } while (s->Symbol != p->FoundState->Symbol); + if (s[0].Freq >= s[-1].Freq) + { + SwapStates(&s[0], &s[-1]); + s--; + } + } + if (s->Freq < MAX_FREQ - 9) + { + s->Freq += 2; + c->SummFreq += 2; + } + } + } + + if (p->OrderFall == 0) + { + p->MinContext = p->MaxContext = CreateSuccessors(p, True); + if (p->MinContext == 0) + { + RestartModel(p); + return; + } + SetSuccessor(p->FoundState, REF(p->MinContext)); + return; + } + + *p->Text++ = p->FoundState->Symbol; + successor = REF(p->Text); + if (p->Text >= p->UnitsStart) + { + RestartModel(p); + return; + } + + if (fSuccessor) + { + if (fSuccessor <= successor) + { + CTX_PTR cs = CreateSuccessors(p, False); + if (cs == NULL) + { + RestartModel(p); + return; + } + fSuccessor = REF(cs); + } + if (--p->OrderFall == 0) + { + successor = fSuccessor; + p->Text -= (p->MaxContext != p->MinContext); + } + } + else + { + SetSuccessor(p->FoundState, successor); + fSuccessor = REF(p->MinContext); + } + + s0 = p->MinContext->SummFreq - (ns = p->MinContext->NumStats) - (p->FoundState->Freq - 1); + + for (c = p->MaxContext; c != p->MinContext; c = SUFFIX(c)) + { + unsigned ns1; + UInt32 cf, sf; + if ((ns1 = c->NumStats) != 1) + { + if ((ns1 & 1) == 0) + { + /* Expand for one UNIT */ + unsigned oldNU = ns1 >> 1; + unsigned i = U2I(oldNU); + if (i != U2I(oldNU + 1)) + { + void *ptr = AllocUnits(p, i + 1); + void *oldPtr; + if (!ptr) + { + RestartModel(p); + return; + } + oldPtr = STATS(c); + MyMem12Cpy(ptr, oldPtr, oldNU); + InsertNode(p, oldPtr, i); + c->Stats = STATS_REF(ptr); + } + } + c->SummFreq = (UInt16)(c->SummFreq + (2 * ns1 < ns) + 2 * ((4 * ns1 <= ns) & (c->SummFreq <= 8 * ns1))); + } + else + { + CPpmd_State *s = (CPpmd_State*)AllocUnits(p, 0); + if (!s) + { + RestartModel(p); + return; + } + *s = *ONE_STATE(c); + c->Stats = REF(s); + if (s->Freq < MAX_FREQ / 4 - 1) + s->Freq <<= 1; + else + s->Freq = MAX_FREQ - 4; + c->SummFreq = (UInt16)(s->Freq + p->InitEsc + (ns > 3)); + } + cf = 2 * (UInt32)p->FoundState->Freq * (c->SummFreq + 6); + sf = (UInt32)s0 + c->SummFreq; + if (cf < 6 * sf) + { + cf = 1 + (cf > sf) + (cf >= 4 * sf); + c->SummFreq += 3; + } + else + { + cf = 4 + (cf >= 9 * sf) + (cf >= 12 * sf) + (cf >= 15 * sf); + c->SummFreq = (UInt16)(c->SummFreq + cf); + } + { + CPpmd_State *s = STATS(c) + ns1; + SetSuccessor(s, successor); + s->Symbol = p->FoundState->Symbol; + s->Freq = (Byte)cf; + c->NumStats = (UInt16)(ns1 + 1); + } + } + p->MaxContext = p->MinContext = CTX(fSuccessor); +} + +static void Rescale(CPpmd7 *p) +{ + unsigned i, adder, sumFreq, escFreq; + CPpmd_State *stats = STATS(p->MinContext); + CPpmd_State *s = p->FoundState; + { + CPpmd_State tmp = *s; + for (; s != stats; s--) + s[0] = s[-1]; + *s = tmp; + } + escFreq = p->MinContext->SummFreq - s->Freq; + s->Freq += 4; + adder = (p->OrderFall != 0); + s->Freq = (Byte)((s->Freq + adder) >> 1); + sumFreq = s->Freq; + + i = p->MinContext->NumStats - 1; + do + { + escFreq -= (++s)->Freq; + s->Freq = (Byte)((s->Freq + adder) >> 1); + sumFreq += s->Freq; + if (s[0].Freq > s[-1].Freq) + { + CPpmd_State *s1 = s; + CPpmd_State tmp = *s1; + do + s1[0] = s1[-1]; + while (--s1 != stats && tmp.Freq > s1[-1].Freq); + *s1 = tmp; + } + } + while (--i); + + if (s->Freq == 0) + { + unsigned numStats = p->MinContext->NumStats; + unsigned n0, n1; + do { i++; } while ((--s)->Freq == 0); + escFreq += i; + p->MinContext->NumStats = (UInt16)(p->MinContext->NumStats - i); + if (p->MinContext->NumStats == 1) + { + CPpmd_State tmp = *stats; + do + { + tmp.Freq = (Byte)(tmp.Freq - (tmp.Freq >> 1)); + escFreq >>= 1; + } + while (escFreq > 1); + InsertNode(p, stats, U2I(((numStats + 1) >> 1))); + *(p->FoundState = ONE_STATE(p->MinContext)) = tmp; + return; + } + n0 = (numStats + 1) >> 1; + n1 = (p->MinContext->NumStats + 1) >> 1; + if (n0 != n1) + p->MinContext->Stats = STATS_REF(ShrinkUnits(p, stats, n0, n1)); + } + p->MinContext->SummFreq = (UInt16)(sumFreq + escFreq - (escFreq >> 1)); + p->FoundState = STATS(p->MinContext); +} + +CPpmd_See *Ppmd7_MakeEscFreq(CPpmd7 *p, unsigned numMasked, UInt32 *escFreq) +{ + CPpmd_See *see; + unsigned nonMasked = p->MinContext->NumStats - numMasked; + if (p->MinContext->NumStats != 256) + { + see = p->See[p->NS2Indx[nonMasked - 1]] + + (nonMasked < (unsigned)SUFFIX(p->MinContext)->NumStats - p->MinContext->NumStats) + + 2 * (p->MinContext->SummFreq < 11 * p->MinContext->NumStats) + + 4 * (numMasked > nonMasked) + + p->HiBitsFlag; + { + unsigned r = (see->Summ >> see->Shift); + see->Summ = (UInt16)(see->Summ - r); + *escFreq = r + (r == 0); + } + } + else + { + see = &p->DummySee; + *escFreq = 1; + } + return see; +} + +static void NextContext(CPpmd7 *p) +{ + CTX_PTR c = CTX(SUCCESSOR(p->FoundState)); + if (p->OrderFall == 0 && (Byte *)c > p->Text) + p->MinContext = p->MaxContext = c; + else + UpdateModel(p); +} + +void Ppmd7_Update1(CPpmd7 *p) +{ + CPpmd_State *s = p->FoundState; + s->Freq += 4; + p->MinContext->SummFreq += 4; + if (s[0].Freq > s[-1].Freq) + { + SwapStates(&s[0], &s[-1]); + p->FoundState = --s; + if (s->Freq > MAX_FREQ) + Rescale(p); + } + NextContext(p); +} + +void Ppmd7_Update1_0(CPpmd7 *p) +{ + p->PrevSuccess = (2 * p->FoundState->Freq > p->MinContext->SummFreq); + p->RunLength += p->PrevSuccess; + p->MinContext->SummFreq += 4; + if ((p->FoundState->Freq += 4) > MAX_FREQ) + Rescale(p); + NextContext(p); +} + +void Ppmd7_UpdateBin(CPpmd7 *p) +{ + p->FoundState->Freq = (Byte)(p->FoundState->Freq + (p->FoundState->Freq < 128 ? 1: 0)); + p->PrevSuccess = 1; + p->RunLength++; + NextContext(p); +} + +void Ppmd7_Update2(CPpmd7 *p) +{ + p->MinContext->SummFreq += 4; + if ((p->FoundState->Freq += 4) > MAX_FREQ) + Rescale(p); + p->RunLength = p->InitRL; + UpdateModel(p); +} diff --git a/fex/7z_C/Ppmd7.h b/fex/7z_C/Ppmd7.h new file mode 100644 index 0000000..56e81eb --- /dev/null +++ b/fex/7z_C/Ppmd7.h @@ -0,0 +1,140 @@ +/* Ppmd7.h -- PPMdH compression codec +2010-03-12 : Igor Pavlov : Public domain +This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */ + +/* This code supports virtual RangeDecoder and includes the implementation +of RangeCoder from 7z, instead of RangeCoder from original PPMd var.H. +If you need the compatibility with original PPMd var.H, you can use external RangeDecoder */ + +#ifndef __PPMD7_H +#define __PPMD7_H + +#include "Ppmd.h" + +EXTERN_C_BEGIN + +#define PPMD7_MIN_ORDER 2 +#define PPMD7_MAX_ORDER 64 + +#define PPMD7_MIN_MEM_SIZE (1 << 11) +#define PPMD7_MAX_MEM_SIZE (0xFFFFFFFF - 12 * 3) + +struct CPpmd7_Context_; + +typedef + #ifdef PPMD_32BIT + struct CPpmd7_Context_ * + #else + UInt32 + #endif + CPpmd7_Context_Ref; + +typedef struct CPpmd7_Context_ +{ + UInt16 NumStats; + UInt16 SummFreq; + CPpmd_State_Ref Stats; + CPpmd7_Context_Ref Suffix; +} CPpmd7_Context; + +#define Ppmd7Context_OneState(p) ((CPpmd_State *)&(p)->SummFreq) + +typedef struct +{ + CPpmd7_Context *MinContext, *MaxContext; + CPpmd_State *FoundState; + unsigned OrderFall, InitEsc, PrevSuccess, MaxOrder, HiBitsFlag; + Int32 RunLength, InitRL; /* must be 32-bit at least */ + + UInt32 Size; + UInt32 GlueCount; + Byte *Base, *LoUnit, *HiUnit, *Text, *UnitsStart; + UInt32 AlignOffset; + + Byte Indx2Units[PPMD_NUM_INDEXES]; + Byte Units2Indx[128]; + CPpmd_Void_Ref FreeList[PPMD_NUM_INDEXES]; + Byte NS2Indx[256], NS2BSIndx[256], HB2Flag[256]; + CPpmd_See DummySee, See[25][16]; + UInt16 BinSumm[128][64]; +} CPpmd7; + +void Ppmd7_Construct(CPpmd7 *p); +Bool Ppmd7_Alloc(CPpmd7 *p, UInt32 size, ISzAlloc *alloc); +void Ppmd7_Free(CPpmd7 *p, ISzAlloc *alloc); +void Ppmd7_Init(CPpmd7 *p, unsigned maxOrder); +#define Ppmd7_WasAllocated(p) ((p)->Base != NULL) + + +/* ---------- Internal Functions ---------- */ + +extern const Byte PPMD7_kExpEscape[16]; + +#ifdef PPMD_32BIT + #define Ppmd7_GetPtr(p, ptr) (ptr) + #define Ppmd7_GetContext(p, ptr) (ptr) + #define Ppmd7_GetStats(p, ctx) ((ctx)->Stats) +#else + #define Ppmd7_GetPtr(p, offs) ((void *)((p)->Base + (offs))) + #define Ppmd7_GetContext(p, offs) ((CPpmd7_Context *)Ppmd7_GetPtr((p), (offs))) + #define Ppmd7_GetStats(p, ctx) ((CPpmd_State *)Ppmd7_GetPtr((p), ((ctx)->Stats))) +#endif + +void Ppmd7_Update1(CPpmd7 *p); +void Ppmd7_Update1_0(CPpmd7 *p); +void Ppmd7_Update2(CPpmd7 *p); +void Ppmd7_UpdateBin(CPpmd7 *p); + +#define Ppmd7_GetBinSumm(p) \ + &p->BinSumm[Ppmd7Context_OneState(p->MinContext)->Freq - 1][p->PrevSuccess + \ + p->NS2BSIndx[Ppmd7_GetContext(p, p->MinContext->Suffix)->NumStats - 1] + \ + (p->HiBitsFlag = p->HB2Flag[p->FoundState->Symbol]) + \ + 2 * p->HB2Flag[Ppmd7Context_OneState(p->MinContext)->Symbol] + \ + ((p->RunLength >> 26) & 0x20)] + +CPpmd_See *Ppmd7_MakeEscFreq(CPpmd7 *p, unsigned numMasked, UInt32 *scale); + + +/* ---------- Decode ---------- */ + +typedef struct +{ + UInt32 (*GetThreshold)(void *p, UInt32 total); + void (*Decode)(void *p, UInt32 start, UInt32 size); + UInt32 (*DecodeBit)(void *p, UInt32 size0); +} IPpmd7_RangeDec; + +typedef struct +{ + IPpmd7_RangeDec p; + UInt32 Range; + UInt32 Code; + IByteIn *Stream; +} CPpmd7z_RangeDec; + +void Ppmd7z_RangeDec_CreateVTable(CPpmd7z_RangeDec *p); +Bool Ppmd7z_RangeDec_Init(CPpmd7z_RangeDec *p); +#define Ppmd7z_RangeDec_IsFinishedOK(p) ((p)->Code == 0) + +int Ppmd7_DecodeSymbol(CPpmd7 *p, IPpmd7_RangeDec *rc); + + +/* ---------- Encode ---------- */ + +typedef struct +{ + UInt64 Low; + UInt32 Range; + Byte Cache; + UInt64 CacheSize; + IByteOut *Stream; +} CPpmd7z_RangeEnc; + +void Ppmd7z_RangeEnc_Init(CPpmd7z_RangeEnc *p); +void Ppmd7z_RangeEnc_FlushData(CPpmd7z_RangeEnc *p); + +void Ppmd7_EncodeSymbol(CPpmd7 *p, CPpmd7z_RangeEnc *rc, int symbol); + +EXTERN_C_END + +#endif diff --git a/fex/7z_C/Ppmd7Dec.c b/fex/7z_C/Ppmd7Dec.c new file mode 100644 index 0000000..d6608e8 --- /dev/null +++ b/fex/7z_C/Ppmd7Dec.c @@ -0,0 +1,187 @@ +/* Ppmd7Dec.c -- PPMdH Decoder +2010-03-12 : Igor Pavlov : Public domain +This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */ + +#include "Ppmd7.h" + +#define kTopValue (1 << 24) + +Bool Ppmd7z_RangeDec_Init(CPpmd7z_RangeDec *p) +{ + unsigned i; + p->Code = 0; + p->Range = 0xFFFFFFFF; + if (p->Stream->Read((void *)p->Stream) != 0) + return False; + for (i = 0; i < 4; i++) + p->Code = (p->Code << 8) | p->Stream->Read((void *)p->Stream); + return (p->Code < 0xFFFFFFFF); +} + +static UInt32 Range_GetThreshold(void *pp, UInt32 total) +{ + CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp; + return (p->Code) / (p->Range /= total); +} + +static void Range_Normalize(CPpmd7z_RangeDec *p) +{ + if (p->Range < kTopValue) + { + p->Code = (p->Code << 8) | p->Stream->Read((void *)p->Stream); + p->Range <<= 8; + if (p->Range < kTopValue) + { + p->Code = (p->Code << 8) | p->Stream->Read((void *)p->Stream); + p->Range <<= 8; + } + } +} + +static void Range_Decode(void *pp, UInt32 start, UInt32 size) +{ + CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp; + p->Code -= start * p->Range; + p->Range *= size; + Range_Normalize(p); +} + +static UInt32 Range_DecodeBit(void *pp, UInt32 size0) +{ + CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp; + UInt32 newBound = (p->Range >> 14) * size0; + UInt32 symbol; + if (p->Code < newBound) + { + symbol = 0; + p->Range = newBound; + } + else + { + symbol = 1; + p->Code -= newBound; + p->Range -= newBound; + } + Range_Normalize(p); + return symbol; +} + +void Ppmd7z_RangeDec_CreateVTable(CPpmd7z_RangeDec *p) +{ + p->p.GetThreshold = Range_GetThreshold; + p->p.Decode = Range_Decode; + p->p.DecodeBit = Range_DecodeBit; +} + + +#define MASK(sym) ((signed char *)charMask)[sym] + +int Ppmd7_DecodeSymbol(CPpmd7 *p, IPpmd7_RangeDec *rc) +{ + size_t charMask[256 / sizeof(size_t)]; + if (p->MinContext->NumStats != 1) + { + CPpmd_State *s = Ppmd7_GetStats(p, p->MinContext); + unsigned i; + UInt32 count, hiCnt; + if ((count = rc->GetThreshold(rc, p->MinContext->SummFreq)) < (hiCnt = s->Freq)) + { + Byte symbol; + rc->Decode(rc, 0, s->Freq); + p->FoundState = s; + symbol = s->Symbol; + Ppmd7_Update1_0(p); + return symbol; + } + p->PrevSuccess = 0; + i = p->MinContext->NumStats - 1; + do + { + if ((hiCnt += (++s)->Freq) > count) + { + Byte symbol; + rc->Decode(rc, hiCnt - s->Freq, s->Freq); + p->FoundState = s; + symbol = s->Symbol; + Ppmd7_Update1(p); + return symbol; + } + } + while (--i); + if (count >= p->MinContext->SummFreq) + return -2; + p->HiBitsFlag = p->HB2Flag[p->FoundState->Symbol]; + rc->Decode(rc, hiCnt, p->MinContext->SummFreq - hiCnt); + PPMD_SetAllBitsIn256Bytes(charMask); + MASK(s->Symbol) = 0; + i = p->MinContext->NumStats - 1; + do { MASK((--s)->Symbol) = 0; } while (--i); + } + else + { + UInt16 *prob = Ppmd7_GetBinSumm(p); + if (rc->DecodeBit(rc, *prob) == 0) + { + Byte symbol; + *prob = (UInt16)PPMD_UPDATE_PROB_0(*prob); + symbol = (p->FoundState = Ppmd7Context_OneState(p->MinContext))->Symbol; + Ppmd7_UpdateBin(p); + return symbol; + } + *prob = (UInt16)PPMD_UPDATE_PROB_1(*prob); + p->InitEsc = PPMD7_kExpEscape[*prob >> 10]; + PPMD_SetAllBitsIn256Bytes(charMask); + MASK(Ppmd7Context_OneState(p->MinContext)->Symbol) = 0; + p->PrevSuccess = 0; + } + for (;;) + { + CPpmd_State *ps[256], *s; + UInt32 freqSum, count, hiCnt; + CPpmd_See *see; + unsigned i, num, numMasked = p->MinContext->NumStats; + do + { + p->OrderFall++; + if (!p->MinContext->Suffix) + return -1; + p->MinContext = Ppmd7_GetContext(p, p->MinContext->Suffix); + } + while (p->MinContext->NumStats == numMasked); + hiCnt = 0; + s = Ppmd7_GetStats(p, p->MinContext); + i = 0; + num = p->MinContext->NumStats - numMasked; + do + { + int k = (int)(MASK(s->Symbol)); + hiCnt += (s->Freq & k); + ps[i] = s++; + i -= k; + } + while (i != num); + + see = Ppmd7_MakeEscFreq(p, numMasked, &freqSum); + freqSum += hiCnt; + count = rc->GetThreshold(rc, freqSum); + + if (count < hiCnt) + { + Byte symbol; + CPpmd_State **pps = ps; + for (hiCnt = 0; (hiCnt += (*pps)->Freq) <= count; pps++); + s = *pps; + rc->Decode(rc, hiCnt - s->Freq, s->Freq); + Ppmd_See_Update(see); + p->FoundState = s; + symbol = s->Symbol; + Ppmd7_Update2(p); + return symbol; + } + if (count >= freqSum) + return -2; + rc->Decode(rc, hiCnt, freqSum - hiCnt); + see->Summ = (UInt16)(see->Summ + freqSum); + do { MASK(ps[--i]->Symbol) = 0; } while (i != 0); + } +} diff --git a/fex/7z_C/Types.h b/fex/7z_C/Types.h new file mode 100644 index 0000000..7732c24 --- /dev/null +++ b/fex/7z_C/Types.h @@ -0,0 +1,254 @@ +/* Types.h -- Basic types +2010-10-09 : Igor Pavlov : Public domain */ + +#ifndef __7Z_TYPES_H +#define __7Z_TYPES_H + +#include + +#ifdef _WIN32 +#include +#endif + +#ifndef EXTERN_C_BEGIN +#ifdef __cplusplus +#define EXTERN_C_BEGIN extern "C" { +#define EXTERN_C_END } +#else +#define EXTERN_C_BEGIN +#define EXTERN_C_END +#endif +#endif + +EXTERN_C_BEGIN + +#define SZ_OK 0 + +#define SZ_ERROR_DATA 1 +#define SZ_ERROR_MEM 2 +#define SZ_ERROR_CRC 3 +#define SZ_ERROR_UNSUPPORTED 4 +#define SZ_ERROR_PARAM 5 +#define SZ_ERROR_INPUT_EOF 6 +#define SZ_ERROR_OUTPUT_EOF 7 +#define SZ_ERROR_READ 8 +#define SZ_ERROR_WRITE 9 +#define SZ_ERROR_PROGRESS 10 +#define SZ_ERROR_FAIL 11 +#define SZ_ERROR_THREAD 12 + +#define SZ_ERROR_ARCHIVE 16 +#define SZ_ERROR_NO_ARCHIVE 17 + +typedef int SRes; + +#ifdef _WIN32 +typedef DWORD WRes; +#else +typedef int WRes; +#endif + +#ifndef RINOK +#define RINOK(x) { int __result__ = (x); if (__result__ != 0) return __result__; } +#endif + +typedef unsigned char Byte; +typedef short Int16; +typedef unsigned short UInt16; + +#ifdef _LZMA_UINT32_IS_ULONG +typedef long Int32; +typedef unsigned long UInt32; +#else +typedef int Int32; +typedef unsigned int UInt32; +#endif + +#ifdef _SZ_NO_INT_64 + +/* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers. + NOTES: Some code will work incorrectly in that case! */ + +typedef long Int64; +typedef unsigned long UInt64; + +#else + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef __int64 Int64; +typedef unsigned __int64 UInt64; +#define UINT64_CONST(n) n +#else +typedef long long int Int64; +typedef unsigned long long int UInt64; +#define UINT64_CONST(n) n ## ULL +#endif + +#endif + +#ifdef _LZMA_NO_SYSTEM_SIZE_T +typedef UInt32 SizeT; +#else +typedef size_t SizeT; +#endif + +typedef int Bool; +#define True 1 +#define False 0 + + +#ifdef _WIN32 +#define MY_STD_CALL __stdcall +#else +#define MY_STD_CALL +#endif + +#ifdef _MSC_VER + +#if _MSC_VER >= 1300 +#define MY_NO_INLINE __declspec(noinline) +#else +#define MY_NO_INLINE +#endif + +#define MY_CDECL __cdecl +#define MY_FAST_CALL __fastcall + +#else + +#define MY_CDECL +#define MY_FAST_CALL + +#endif + + +/* The following interfaces use first parameter as pointer to structure */ + +typedef struct +{ + Byte (*Read)(void *p); /* reads one byte, returns 0 in case of EOF or error */ +} IByteIn; + +typedef struct +{ + void (*Write)(void *p, Byte b); +} IByteOut; + +typedef struct +{ + SRes (*Read)(void *p, void *buf, size_t *size); + /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. + (output(*size) < input(*size)) is allowed */ +} ISeqInStream; + +/* it can return SZ_ERROR_INPUT_EOF */ +SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size); +SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType); +SRes SeqInStream_ReadByte(ISeqInStream *stream, Byte *buf); + +typedef struct +{ + size_t (*Write)(void *p, const void *buf, size_t size); + /* Returns: result - the number of actually written bytes. + (result < size) means error */ +} ISeqOutStream; + +typedef enum +{ + SZ_SEEK_SET = 0, + SZ_SEEK_CUR = 1, + SZ_SEEK_END = 2 +} ESzSeek; + +typedef struct +{ + SRes (*Read)(void *p, void *buf, size_t *size); /* same as ISeqInStream::Read */ + SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); +} ISeekInStream; + +typedef struct +{ + SRes (*Look)(void *p, const void **buf, size_t *size); + /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. + (output(*size) > input(*size)) is not allowed + (output(*size) < input(*size)) is allowed */ + SRes (*Skip)(void *p, size_t offset); + /* offset must be <= output(*size) of Look */ + + SRes (*Read)(void *p, void *buf, size_t *size); + /* reads directly (without buffer). It's same as ISeqInStream::Read */ + SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); +} ILookInStream; + +SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size); +SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset); + +/* reads via ILookInStream::Read */ +SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType); +SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size); + +#define LookToRead_BUF_SIZE (1 << 14) + +typedef struct +{ + ILookInStream s; + ISeekInStream *realStream; + size_t pos; + size_t size; + Byte buf[LookToRead_BUF_SIZE]; +} CLookToRead; + +void LookToRead_CreateVTable(CLookToRead *p, int lookahead); +void LookToRead_Init(CLookToRead *p); + +typedef struct +{ + ISeqInStream s; + ILookInStream *realStream; +} CSecToLook; + +void SecToLook_CreateVTable(CSecToLook *p); + +typedef struct +{ + ISeqInStream s; + ILookInStream *realStream; +} CSecToRead; + +void SecToRead_CreateVTable(CSecToRead *p); + +typedef struct +{ + SRes (*Progress)(void *p, UInt64 inSize, UInt64 outSize); + /* Returns: result. (result != SZ_OK) means break. + Value (UInt64)(Int64)-1 for size means unknown value. */ +} ICompressProgress; + +typedef struct +{ + void *(*Alloc)(void *p, size_t size); + void (*Free)(void *p, void *address); /* address can be 0 */ +} ISzAlloc; + +#define IAlloc_Alloc(p, size) (p)->Alloc((p), size) +#define IAlloc_Free(p, a) (p)->Free((p), a) + +#ifdef _WIN32 + +#define CHAR_PATH_SEPARATOR '\\' +#define WCHAR_PATH_SEPARATOR L'\\' +#define STRING_PATH_SEPARATOR "\\" +#define WSTRING_PATH_SEPARATOR L"\\" + +#else + +#define CHAR_PATH_SEPARATOR '/' +#define WCHAR_PATH_SEPARATOR L'/' +#define STRING_PATH_SEPARATOR "/" +#define WSTRING_PATH_SEPARATOR L"/" + +#endif + +EXTERN_C_END + +#endif diff --git a/fex/7z_C/lzma.txt b/fex/7z_C/lzma.txt new file mode 100644 index 0000000..6593232 --- /dev/null +++ b/fex/7z_C/lzma.txt @@ -0,0 +1,598 @@ +LZMA SDK 9.20 +------------- + +LZMA SDK provides the documentation, samples, header files, libraries, +and tools you need to develop applications that use LZMA compression. + +LZMA is default and general compression method of 7z format +in 7-Zip compression program (www.7-zip.org). LZMA provides high +compression ratio and very fast decompression. + +LZMA is an improved version of famous LZ77 compression algorithm. +It was improved in way of maximum increasing of compression ratio, +keeping high decompression speed and low memory requirements for +decompressing. + + + +LICENSE +------- + +LZMA SDK is written and placed in the public domain by Igor Pavlov. + +Some code in LZMA SDK is based on public domain code from another developers: + 1) PPMd var.H (2001): Dmitry Shkarin + 2) SHA-256: Wei Dai (Crypto++ library) + + +LZMA SDK Contents +----------------- + +LZMA SDK includes: + + - ANSI-C/C++/C#/Java source code for LZMA compressing and decompressing + - Compiled file->file LZMA compressing/decompressing program for Windows system + + +UNIX/Linux version +------------------ +To compile C++ version of file->file LZMA encoding, go to directory +CPP/7zip/Bundles/LzmaCon +and call make to recompile it: + make -f makefile.gcc clean all + +In some UNIX/Linux versions you must compile LZMA with static libraries. +To compile with static libraries, you can use +LIB = -lm -static + + +Files +--------------------- +lzma.txt - LZMA SDK description (this file) +7zFormat.txt - 7z Format description +7zC.txt - 7z ANSI-C Decoder description +methods.txt - Compression method IDs for .7z +lzma.exe - Compiled file->file LZMA encoder/decoder for Windows +7zr.exe - 7-Zip with 7z/lzma/xz support. +history.txt - history of the LZMA SDK + + +Source code structure +--------------------- + +C/ - C files + 7zCrc*.* - CRC code + Alloc.* - Memory allocation functions + Bra*.* - Filters for x86, IA-64, ARM, ARM-Thumb, PowerPC and SPARC code + LzFind.* - Match finder for LZ (LZMA) encoders + LzFindMt.* - Match finder for LZ (LZMA) encoders for multithreading encoding + LzHash.h - Additional file for LZ match finder + LzmaDec.* - LZMA decoding + LzmaEnc.* - LZMA encoding + LzmaLib.* - LZMA Library for DLL calling + Types.h - Basic types for another .c files + Threads.* - The code for multithreading. + + LzmaLib - LZMA Library (.DLL for Windows) + + LzmaUtil - LZMA Utility (file->file LZMA encoder/decoder). + + Archive - files related to archiving + 7z - 7z ANSI-C Decoder + +CPP/ -- CPP files + + Common - common files for C++ projects + Windows - common files for Windows related code + + 7zip - files related to 7-Zip Project + + Common - common files for 7-Zip + + Compress - files related to compression/decompression + + Archive - files related to archiving + + Common - common files for archive handling + 7z - 7z C++ Encoder/Decoder + + Bundles - Modules that are bundles of other modules + + Alone7z - 7zr.exe: Standalone version of 7z.exe that supports only 7z/LZMA/BCJ/BCJ2 + LzmaCon - lzma.exe: LZMA compression/decompression + Format7zR - 7zr.dll: Reduced version of 7za.dll: extracting/compressing to 7z/LZMA/BCJ/BCJ2 + Format7zExtractR - 7zxr.dll: Reduced version of 7zxa.dll: extracting from 7z/LZMA/BCJ/BCJ2. + + UI - User Interface files + + Client7z - Test application for 7za.dll, 7zr.dll, 7zxr.dll + Common - Common UI files + Console - Code for console archiver + + + +CS/ - C# files + 7zip + Common - some common files for 7-Zip + Compress - files related to compression/decompression + LZ - files related to LZ (Lempel-Ziv) compression algorithm + LZMA - LZMA compression/decompression + LzmaAlone - file->file LZMA compression/decompression + RangeCoder - Range Coder (special code of compression/decompression) + +Java/ - Java files + SevenZip + Compression - files related to compression/decompression + LZ - files related to LZ (Lempel-Ziv) compression algorithm + LZMA - LZMA compression/decompression + RangeCoder - Range Coder (special code of compression/decompression) + + +C/C++ source code of LZMA SDK is part of 7-Zip project. +7-Zip source code can be downloaded from 7-Zip's SourceForge page: + + http://sourceforge.net/projects/sevenzip/ + + + +LZMA features +------------- + - Variable dictionary size (up to 1 GB) + - Estimated compressing speed: about 2 MB/s on 2 GHz CPU + - Estimated decompressing speed: + - 20-30 MB/s on 2 GHz Core 2 or AMD Athlon 64 + - 1-2 MB/s on 200 MHz ARM, MIPS, PowerPC or other simple RISC + - Small memory requirements for decompressing (16 KB + DictionarySize) + - Small code size for decompressing: 5-8 KB + +LZMA decoder uses only integer operations and can be +implemented in any modern 32-bit CPU (or on 16-bit CPU with some conditions). + +Some critical operations that affect the speed of LZMA decompression: + 1) 32*16 bit integer multiply + 2) Misspredicted branches (penalty mostly depends from pipeline length) + 3) 32-bit shift and arithmetic operations + +The speed of LZMA decompressing mostly depends from CPU speed. +Memory speed has no big meaning. But if your CPU has small data cache, +overall weight of memory speed will slightly increase. + + +How To Use +---------- + +Using LZMA encoder/decoder executable +-------------------------------------- + +Usage: LZMA inputFile outputFile [...] + + e: encode file + + d: decode file + + b: Benchmark. There are two tests: compressing and decompressing + with LZMA method. Benchmark shows rating in MIPS (million + instructions per second). Rating value is calculated from + measured speed and it is normalized with Intel's Core 2 results. + Also Benchmark checks possible hardware errors (RAM + errors in most cases). Benchmark uses these settings: + (-a1, -d21, -fb32, -mfbt4). You can change only -d parameter. + Also you can change the number of iterations. Example for 30 iterations: + LZMA b 30 + Default number of iterations is 10. + + + + + -a{N}: set compression mode 0 = fast, 1 = normal + default: 1 (normal) + + d{N}: Sets Dictionary size - [0, 30], default: 23 (8MB) + The maximum value for dictionary size is 1 GB = 2^30 bytes. + Dictionary size is calculated as DictionarySize = 2^N bytes. + For decompressing file compressed by LZMA method with dictionary + size D = 2^N you need about D bytes of memory (RAM). + + -fb{N}: set number of fast bytes - [5, 273], default: 128 + Usually big number gives a little bit better compression ratio + and slower compression process. + + -lc{N}: set number of literal context bits - [0, 8], default: 3 + Sometimes lc=4 gives gain for big files. + + -lp{N}: set number of literal pos bits - [0, 4], default: 0 + lp switch is intended for periodical data when period is + equal 2^N. For example, for 32-bit (4 bytes) + periodical data you can use lp=2. Often it's better to set lc0, + if you change lp switch. + + -pb{N}: set number of pos bits - [0, 4], default: 2 + pb switch is intended for periodical data + when period is equal 2^N. + + -mf{MF_ID}: set Match Finder. Default: bt4. + Algorithms from hc* group doesn't provide good compression + ratio, but they often works pretty fast in combination with + fast mode (-a0). + + Memory requirements depend from dictionary size + (parameter "d" in table below). + + MF_ID Memory Description + + bt2 d * 9.5 + 4MB Binary Tree with 2 bytes hashing. + bt3 d * 11.5 + 4MB Binary Tree with 3 bytes hashing. + bt4 d * 11.5 + 4MB Binary Tree with 4 bytes hashing. + hc4 d * 7.5 + 4MB Hash Chain with 4 bytes hashing. + + -eos: write End Of Stream marker. By default LZMA doesn't write + eos marker, since LZMA decoder knows uncompressed size + stored in .lzma file header. + + -si: Read data from stdin (it will write End Of Stream marker). + -so: Write data to stdout + + +Examples: + +1) LZMA e file.bin file.lzma -d16 -lc0 + +compresses file.bin to file.lzma with 64 KB dictionary (2^16=64K) +and 0 literal context bits. -lc0 allows to reduce memory requirements +for decompression. + + +2) LZMA e file.bin file.lzma -lc0 -lp2 + +compresses file.bin to file.lzma with settings suitable +for 32-bit periodical data (for example, ARM or MIPS code). + +3) LZMA d file.lzma file.bin + +decompresses file.lzma to file.bin. + + +Compression ratio hints +----------------------- + +Recommendations +--------------- + +To increase the compression ratio for LZMA compressing it's desirable +to have aligned data (if it's possible) and also it's desirable to locate +data in such order, where code is grouped in one place and data is +grouped in other place (it's better than such mixing: code, data, code, +data, ...). + + +Filters +------- +You can increase the compression ratio for some data types, using +special filters before compressing. For example, it's possible to +increase the compression ratio on 5-10% for code for those CPU ISAs: +x86, IA-64, ARM, ARM-Thumb, PowerPC, SPARC. + +You can find C source code of such filters in C/Bra*.* files + +You can check the compression ratio gain of these filters with such +7-Zip commands (example for ARM code): +No filter: + 7z a a1.7z a.bin -m0=lzma + +With filter for little-endian ARM code: + 7z a a2.7z a.bin -m0=arm -m1=lzma + +It works in such manner: +Compressing = Filter_encoding + LZMA_encoding +Decompressing = LZMA_decoding + Filter_decoding + +Compressing and decompressing speed of such filters is very high, +so it will not increase decompressing time too much. +Moreover, it reduces decompression time for LZMA_decoding, +since compression ratio with filtering is higher. + +These filters convert CALL (calling procedure) instructions +from relative offsets to absolute addresses, so such data becomes more +compressible. + +For some ISAs (for example, for MIPS) it's impossible to get gain from such filter. + + +LZMA compressed file format +--------------------------- +Offset Size Description + 0 1 Special LZMA properties (lc,lp, pb in encoded form) + 1 4 Dictionary size (little endian) + 5 8 Uncompressed size (little endian). -1 means unknown size + 13 Compressed data + + +ANSI-C LZMA Decoder +~~~~~~~~~~~~~~~~~~~ + +Please note that interfaces for ANSI-C code were changed in LZMA SDK 4.58. +If you want to use old interfaces you can download previous version of LZMA SDK +from sourceforge.net site. + +To use ANSI-C LZMA Decoder you need the following files: +1) LzmaDec.h + LzmaDec.c + Types.h +LzmaUtil/LzmaUtil.c is example application that uses these files. + + +Memory requirements for LZMA decoding +------------------------------------- + +Stack usage of LZMA decoding function for local variables is not +larger than 200-400 bytes. + +LZMA Decoder uses dictionary buffer and internal state structure. +Internal state structure consumes + state_size = (4 + (1.5 << (lc + lp))) KB +by default (lc=3, lp=0), state_size = 16 KB. + + +How To decompress data +---------------------- + +LZMA Decoder (ANSI-C version) now supports 2 interfaces: +1) Single-call Decompressing +2) Multi-call State Decompressing (zlib-like interface) + +You must use external allocator: +Example: +void *SzAlloc(void *p, size_t size) { p = p; return malloc(size); } +void SzFree(void *p, void *address) { p = p; free(address); } +ISzAlloc alloc = { SzAlloc, SzFree }; + +You can use p = p; operator to disable compiler warnings. + + +Single-call Decompressing +------------------------- +When to use: RAM->RAM decompressing +Compile files: LzmaDec.h + LzmaDec.c + Types.h +Compile defines: no defines +Memory Requirements: + - Input buffer: compressed size + - Output buffer: uncompressed size + - LZMA Internal Structures: state_size (16 KB for default settings) + +Interface: + int LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, + const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, + ELzmaStatus *status, ISzAlloc *alloc); + In: + dest - output data + destLen - output data size + src - input data + srcLen - input data size + propData - LZMA properties (5 bytes) + propSize - size of propData buffer (5 bytes) + finishMode - It has meaning only if the decoding reaches output limit (*destLen). + LZMA_FINISH_ANY - Decode just destLen bytes. + LZMA_FINISH_END - Stream must be finished after (*destLen). + You can use LZMA_FINISH_END, when you know that + current output buffer covers last bytes of stream. + alloc - Memory allocator. + + Out: + destLen - processed output size + srcLen - processed input size + + Output: + SZ_OK + status: + LZMA_STATUS_FINISHED_WITH_MARK + LZMA_STATUS_NOT_FINISHED + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK + SZ_ERROR_DATA - Data error + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_UNSUPPORTED - Unsupported properties + SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src). + + If LZMA decoder sees end_marker before reaching output limit, it returns OK result, + and output value of destLen will be less than output buffer size limit. + + You can use multiple checks to test data integrity after full decompression: + 1) Check Result and "status" variable. + 2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize. + 3) Check that output(srcLen) = compressedSize, if you know real compressedSize. + You must use correct finish mode in that case. */ + + +Multi-call State Decompressing (zlib-like interface) +---------------------------------------------------- + +When to use: file->file decompressing +Compile files: LzmaDec.h + LzmaDec.c + Types.h + +Memory Requirements: + - Buffer for input stream: any size (for example, 16 KB) + - Buffer for output stream: any size (for example, 16 KB) + - LZMA Internal Structures: state_size (16 KB for default settings) + - LZMA dictionary (dictionary size is encoded in LZMA properties header) + +1) read LZMA properties (5 bytes) and uncompressed size (8 bytes, little-endian) to header: + unsigned char header[LZMA_PROPS_SIZE + 8]; + ReadFile(inFile, header, sizeof(header) + +2) Allocate CLzmaDec structures (state + dictionary) using LZMA properties + + CLzmaDec state; + LzmaDec_Constr(&state); + res = LzmaDec_Allocate(&state, header, LZMA_PROPS_SIZE, &g_Alloc); + if (res != SZ_OK) + return res; + +3) Init LzmaDec structure before any new LZMA stream. And call LzmaDec_DecodeToBuf in loop + + LzmaDec_Init(&state); + for (;;) + { + ... + int res = LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, + const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode); + ... + } + + +4) Free all allocated structures + LzmaDec_Free(&state, &g_Alloc); + +For full code example, look at C/LzmaUtil/LzmaUtil.c code. + + +How To compress data +-------------------- + +Compile files: LzmaEnc.h + LzmaEnc.c + Types.h + +LzFind.c + LzFind.h + LzFindMt.c + LzFindMt.h + LzHash.h + +Memory Requirements: + - (dictSize * 11.5 + 6 MB) + state_size + +Lzma Encoder can use two memory allocators: +1) alloc - for small arrays. +2) allocBig - for big arrays. + +For example, you can use Large RAM Pages (2 MB) in allocBig allocator for +better compression speed. Note that Windows has bad implementation for +Large RAM Pages. +It's OK to use same allocator for alloc and allocBig. + + +Single-call Compression with callbacks +-------------------------------------- + +Check C/LzmaUtil/LzmaUtil.c as example, + +When to use: file->file decompressing + +1) you must implement callback structures for interfaces: +ISeqInStream +ISeqOutStream +ICompressProgress +ISzAlloc + +static void *SzAlloc(void *p, size_t size) { p = p; return MyAlloc(size); } +static void SzFree(void *p, void *address) { p = p; MyFree(address); } +static ISzAlloc g_Alloc = { SzAlloc, SzFree }; + + CFileSeqInStream inStream; + CFileSeqOutStream outStream; + + inStream.funcTable.Read = MyRead; + inStream.file = inFile; + outStream.funcTable.Write = MyWrite; + outStream.file = outFile; + + +2) Create CLzmaEncHandle object; + + CLzmaEncHandle enc; + + enc = LzmaEnc_Create(&g_Alloc); + if (enc == 0) + return SZ_ERROR_MEM; + + +3) initialize CLzmaEncProps properties; + + LzmaEncProps_Init(&props); + + Then you can change some properties in that structure. + +4) Send LZMA properties to LZMA Encoder + + res = LzmaEnc_SetProps(enc, &props); + +5) Write encoded properties to header + + Byte header[LZMA_PROPS_SIZE + 8]; + size_t headerSize = LZMA_PROPS_SIZE; + UInt64 fileSize; + int i; + + res = LzmaEnc_WriteProperties(enc, header, &headerSize); + fileSize = MyGetFileLength(inFile); + for (i = 0; i < 8; i++) + header[headerSize++] = (Byte)(fileSize >> (8 * i)); + MyWriteFileAndCheck(outFile, header, headerSize) + +6) Call encoding function: + res = LzmaEnc_Encode(enc, &outStream.funcTable, &inStream.funcTable, + NULL, &g_Alloc, &g_Alloc); + +7) Destroy LZMA Encoder Object + LzmaEnc_Destroy(enc, &g_Alloc, &g_Alloc); + + +If callback function return some error code, LzmaEnc_Encode also returns that code +or it can return the code like SZ_ERROR_READ, SZ_ERROR_WRITE or SZ_ERROR_PROGRESS. + + +Single-call RAM->RAM Compression +-------------------------------- + +Single-call RAM->RAM Compression is similar to Compression with callbacks, +but you provide pointers to buffers instead of pointers to stream callbacks: + +HRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, + CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, + ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); + +Return code: + SZ_OK - OK + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_PARAM - Incorrect paramater + SZ_ERROR_OUTPUT_EOF - output buffer overflow + SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) + + + +Defines +------- + +_LZMA_SIZE_OPT - Enable some optimizations in LZMA Decoder to get smaller executable code. + +_LZMA_PROB32 - It can increase the speed on some 32-bit CPUs, but memory usage for + some structures will be doubled in that case. + +_LZMA_UINT32_IS_ULONG - Define it if int is 16-bit on your compiler and long is 32-bit. + +_LZMA_NO_SYSTEM_SIZE_T - Define it if you don't want to use size_t type. + + +_7ZIP_PPMD_SUPPPORT - Define it if you don't want to support PPMD method in AMSI-C .7z decoder. + + +C++ LZMA Encoder/Decoder +~~~~~~~~~~~~~~~~~~~~~~~~ +C++ LZMA code use COM-like interfaces. So if you want to use it, +you can study basics of COM/OLE. +C++ LZMA code is just wrapper over ANSI-C code. + + +C++ Notes +~~~~~~~~~~~~~~~~~~~~~~~~ +If you use some C++ code folders in 7-Zip (for example, C++ code for .7z handling), +you must check that you correctly work with "new" operator. +7-Zip can be compiled with MSVC 6.0 that doesn't throw "exception" from "new" operator. +So 7-Zip uses "CPP\Common\NewHandler.cpp" that redefines "new" operator: +operator new(size_t size) +{ + void *p = ::malloc(size); + if (p == 0) + throw CNewException(); + return p; +} +If you use MSCV that throws exception for "new" operator, you can compile without +"NewHandler.cpp". So standard exception will be used. Actually some code of +7-Zip catches any exception in internal code and converts it to HRESULT code. +So you don't need to catch CNewException, if you call COM interfaces of 7-Zip. + +--- + +http://www.7-zip.org +http://www.7-zip.org/sdk.html +http://www.7-zip.org/support.html diff --git a/fex/File_Extractor2008.vcproj b/fex/File_Extractor2008.vcproj new file mode 100644 index 0000000..b981776 --- /dev/null +++ b/fex/File_Extractor2008.vcproj @@ -0,0 +1,395 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fex/File_Extractor2010.sln b/fex/File_Extractor2010.sln new file mode 100644 index 0000000..68da2ea --- /dev/null +++ b/fex/File_Extractor2010.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "File_Extractor", "File_Extractor2010.vcxproj", "{7AEC599C-7C82-4F00-AA60-411E0A359CB0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7AEC599C-7C82-4F00-AA60-411E0A359CB0}.Debug|Win32.ActiveCfg = Debug|Win32 + {7AEC599C-7C82-4F00-AA60-411E0A359CB0}.Debug|Win32.Build.0 = Debug|Win32 + {7AEC599C-7C82-4F00-AA60-411E0A359CB0}.Release|Win32.ActiveCfg = Release|Win32 + {7AEC599C-7C82-4F00-AA60-411E0A359CB0}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/fex/File_Extractor2010.vcxproj b/fex/File_Extractor2010.vcxproj new file mode 100644 index 0000000..a6b30b4 --- /dev/null +++ b/fex/File_Extractor2010.vcxproj @@ -0,0 +1,151 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + File_Extractor + {7AEC599C-7C82-4F00-AA60-411E0A359CB0} + File_Extractor + + + + StaticLibrary + true + + + StaticLibrary + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + AllRules.ruleset + + + AllRules.ruleset + + + + + + + Disabled + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + EditAndContinue + $(ProjectDir)..\..\dependencies\zlib;$(ProjectDir);$(ProjectDir)7z_C;%(AdditionalIncludeDirectories) + + + + + + $(ProjectDir)..\..\dependencies\zlib;$(ProjectDir);%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + AnySuitable + true + Speed + true + StreamingSIMDExtensions + true + true + true + Fast + false + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {3e03c179-8251-46e4-81f4-466f114bac63} + + + + + + \ No newline at end of file diff --git a/fex/File_Extractor2010.vcxproj.filters b/fex/File_Extractor2010.vcxproj.filters new file mode 100644 index 0000000..ea69944 --- /dev/null +++ b/fex/File_Extractor2010.vcxproj.filters @@ -0,0 +1,190 @@ + + + + + {40ba751f-4ce2-4162-85a2-0c2ae2f33ae2} + + + {4f3646e4-ddc5-4030-9ba1-7629a947e5db} + + + + + fex + + + fex + + + fex + + + fex + + + fex + + + fex + + + fex + + + fex + + + fex + + + fex + + + fex + + + fex + + + 7z_C + + + 7z_C + + + 7z_C + + + 7z_C + + + 7z_C + + + 7z_C + + + 7z_C + + + 7z_C + + + 7z_C + + + 7z_C + + + 7z_C + + + 7z_C + + + 7z_C + + + 7z_C + + + 7z_C + + + + + fex + + + fex + + + fex + + + fex + + + fex + + + fex + + + fex + + + fex + + + fex + + + fex + + + fex + + + fex + + + fex + + + fex + + + fex + + + 7z_C + + + 7z_C + + + 7z_C + + + 7z_C + + + 7z_C + + + 7z_C + + + 7z_C + + + 7z_C + + + 7z_C + + + 7z_C + + + 7z_C + + + 7z_C + + + + + + + + + + 7z_C + + + 7z_C + + + \ No newline at end of file diff --git a/fex/File_Extractor2010.vcxproj.user b/fex/File_Extractor2010.vcxproj.user new file mode 100644 index 0000000..695b5c7 --- /dev/null +++ b/fex/File_Extractor2010.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/fex/changes.txt b/fex/changes.txt new file mode 100644 index 0000000..924b898 --- /dev/null +++ b/fex/changes.txt @@ -0,0 +1,71 @@ +File_Extractor Change Log +------------------------- +- Change that might break code. ++ Improvement that is unlikely to break any code. +* Other changes. + + +File_Extractor 1.0.0 (2009-10-12) +-------------------- +- Added fex_stat() which MUST be called before getting current file +information beyond name, like fex_size(). + +- Changed fex_*() functions to always report error via return value, +rather than sometimes via a parameter. This will break user code that +uses fex_data(), fex_open(), fex_open_type(), and fex_identify_file(). +See demos for usage. + +- Deprecated C++ interface. Use fex.h and nothing else to access library +from user code. + +- Removed archive types (fex_zip_type, etc.) from interface. Use +fex_identify_extension() to get particular type. + +- Removed fex_mini.c, unzip.h, and unrarlib.h for now, as maintaining +them was too taxing. If others express desire for them, I can re-add +them. + +- Removed fex_scan_only() and fex_read_once(), as they don't improve +performance anymore. Use fex_read() in place of fex_read_once(). + +- Removed fex_remain(). Use fex_size()-fex_tell() to find number of +bytes remaining. + +- Removed fex_set_user_data() and related functions, as they didn't seem +useful to anyone. + +- Removed fex_type_t structure from interface and added accessors +instead (fex_type_name(), fex_type_extension()). + ++ Improved archive file type determination to reject other archive types +not handled by the library, rather than opening them as binary files. + ++ Added Doxygen compatibility to fex.h. + ++ Added fex_crc32() to quickly get CRC-32 of current file from archive +header, without having to read entire file to calculate it. + ++ Added fex_err_code() to get numeric error code, along with other +helpful error-related functions. Also added more documentation on how to +handle library errors in user code. + ++ Added fex_init() for use in multi-threaded programs. + ++ Added fex_seek_arc() to seek to particular file in archive. + ++ Added fex_wname() to get Unicode name of current file. + ++ Added support for building as DLL. + ++ Added support for wide-character file paths on Windows, enabled with +BLARGG_UTF8_PATHS (thanks to byuu for the idea). This is necessary to +support file paths on non-English Windows systems. + ++ Started using unit testing during development. + ++ Updated to 7-zip 4.65, unrar_core 3.8.5. + + +File_Extractor 0.4.3 (2008-12-08) +-------------------- +* Limited release diff --git a/fex/fex.txt b/fex/fex.txt new file mode 100644 index 0000000..533df88 --- /dev/null +++ b/fex/fex.txt @@ -0,0 +1,330 @@ +File_Extractor 1.0.0 +-------------------- +Author : Shay Green +Website : http://code.google.com/p/file-extractor/ +License : GNU LGPL 2.1 or later for all except unrar +Language: C interface, C++ implementation + + +Contents +-------- +* Overview +* Limitations +* Extracting file data +* Archive file type handling +* Using in multiple threads +* Error handling +* Solving problems +* Thanks + + +Overview +-------- +File_Exactor (fex) allows you to write one version of file-opening code +that handles normal files and archives of files. It presents each as a +series of files that you can scan and optionally extract; a single file +is made to act like an archive of just one file, so your code doesn't +need to do anything special to handle it. + +Basic steps for scanning and extracting from an archive: + + * Open an archive or normal file using fex_open(). + * Scanning/extraction loop: + - Exit loop if fex_done() returns true. + - Get current file's name with fex_name(). + - If more file information is needed, call fex_stat() first. + - If extracting, use fex_data() or fex_read(). + - Go to next file in archive with fex_next(). + * Close archive and free memory with fex_close(). + +You can stop scanning an archive at any point, for example once you've +found the file you're looking for. If you need to go back to the first +file, call fex_rewind() at any time. Be sure to check error codes +returned by most functions. + + +Limitations +----------- +All archives: +* A file's checksum is verified only after ALL its data is extracted. +* Encryption, segmentation, files larger than 2GB, and other extra +features are not supported. + +GZ archives: +* Only gzip archives of a single file are supported. If it has multiple +files, the reported size will be wrong. Multi-file gzip archives are +very rare. + +ZIP archives: +* Supports files compressed using either deflation or store +(uncompressed). Other compression schemes like BZip2 and Deflate64 are +not supported. +* Archive must have a valid directory structure at the end. + +RAR archives: +* Support for really old 1.x archives might not work. If you have some +of these old archives, send them to me so I can test them. + +7-zip: +* Solid archives can currently use lots of memory when open. + + +Extracting file data +-------------------- +A file's data can be extracted with one or more calls to fex_read(), as +you would read from a normal file. Use fex_tell() to find out how much +has already been read. Use this if you need the data read into your own +structure in memory. + +File data can also be extracted to memory by the library with +fex_data(). The pointer returned is valid only until you go to another +file or close the archive, so this is only useful if you need to examine +or process the data immediately and not keep it around for later. +Archive extractors naturally keep a copy of the extracted data in memory +already for solid archive types (currently 7-zip and RAR), so this +function is optimized to avoid making a second copy of it in those +cases. + +Use fex_size() to find the size of the extracted data. Remember that +fex_stat() or fex_data() must be called BEFORE calling fex_size(). + + +Archive file type handling +-------------------------- +By default, fex uses the filename extension and header to determine +archive type. If the filename extension is unrecognized or it lacks an +extension, fex examines the first few bytes of the file. If still +unrecognized, fex opens it as binary. Fex also checks for common archive +types that it doesn't support, so that it can reject as unsupported them +rather than unhelpfully opening them as binary. + +Your file format might itself be an archive, for example your files end +in ".rsn" yet are normal RAR archives, or they end in ".vgz" and are +gzipped. This is why fex checks the headers of files with unknown +filename extensions, rather than treating them as binary or rejecting +them. + +Type identification can be customized by using the various +identification functions and fex_open_type(). For example, you could +avoid the header check: + + fex_t* fex; + fex_type_t type = fex_identify_extension( path ); + if ( type == NULL ) + error( "Unsupported archive type" ); + + error( fex_open_type( &fex, path, type ) ); + +Note that you'll only get a NULL type for known archive type that fex +doesn't handle; you won't get it for your own files, for example +fex_identify_extension("myfile.foo") won't return NULL (unless for some +reason you've disabled binary file support). + +Use fex_type_list() to get a list of the types fex supports, for example +to tell the user what archive types your program supports: + + const fex_type_t* t; + for ( t = fex_type_list(); *t; t++ ) + printf( "%s\n", fex_type_name( *t ) ); + +To get the fex_type_t for a particular archive type, use +fex_identify_extension(): + + fex_type_t zip_type = fex_identify_extension( ".zip" ); + if ( zip_type == NULL ) + error( "ZIP isn't supported" ); + +Be sure to check the result as shown, rather than assuming the library +supports a particular archive type. Use an extension of "" to get the +type for binary files: + + fex_type_t bin_type = fex_identify_extension( "" ); + if ( bin_type == NULL ) + error( "Binary files aren't supported?!?" ); + + +Using in multiple threads +------------------------- +Fex supports multi-threaded programs. If only one thread at a time is +using the library, nothing special needs to be done. If more than one +thread is using the library, the following must be done: + +* Call fex_init() from the main thread and ensure it completes before +any other threads use any fex functions. This initializes shared data +tables used by the extractors. + +* For each archive opened, only access it from one thread at a time. +Different archives can be accessed from different threads without any +synchronization, since fex uses no global variables. If the same archive +must be accessed from multiple threads, all calls to any fex functions +must be in critical section(s). + + +Unicode file paths on Windows +----------------------------- +If using Windows and your program supports Unicode file paths, enable +BLARGG_UTF8_PATHS in blargg_config.h, and convert your wide-character +paths to UTF-8 before passing them to fex.h functions: + + /* Wide-character path that could have come from system */ + wchar_t wide_path [] = L"demo.zip"; + + /* Convert from wide path and check for error */ + char* path = fex_wide_to_path( wide_path ); + if ( path == NULL ) + error( "Out of memory" ); + + /* Use converted path for fex call */ + error( fex_open( &fex, path ) ); + + /* Free memory used by path */ + fex_free_path( path ); + +The converted path can be used with any of the fex functions that take +paths, for example fex_identify_extension() or fex_has_extension(). + + +Error handling +-------------- +Most functions that can fail return fex_err_t, a pointer type. On +failure they return a pointer to an error object, and on success they +return NULL. Use fex_err_code() to get a conventional error code, or +fex_err_str() to get a string suitable for reporting to the user. + +There are two basic approches that your code can use to handle library +errors. It can return errors, or report them and exit the function via +some other means. + +Your code can return errors as the library does, using fex_err_t: + + #define RETURN_ERR( expr ) \ + do {\ + fex_err_t err = (expr);\ + if ( err != NULL )\ + return err;\ + } while ( 0 ) + + fex_err_t my_func() + { + RETURN_ERR( fex_foo() ); + RETURN_ERR( fex_bar() ); + return NULL; + } + +If you have your own error codes, you can convert fex's errors to them: + + // error codes that differ from library's + enum { + my_ok = 0, + my_generic_error = 123, + my_out_of_memory = 456, + my_file_not_found = 789 + // ... + }; + + int convert_error( fex_err_t err ) + { + switch ( fex_err_code( err ) ) + { + case fex_ok: return my_ok; + case fex_err_generic: return my_generic_error; + case fex_err_memory: return my_out_of_memory; + case fex_err_file_missing: return my_file_not_found; + // ... + default: return my_generic_error; + } + } + + #define RETURN_ERR( expr ) \ + do {\ + fex_err_t err = (expr);\ + if ( err != NULL )\ + return convert_error( err );\ + } while ( 0 ) + + int my_func() + { + RETURN_ERR( fex_foo() ); + RETURN_ERR( fex_bar() ); + return my_ok; + } + +The other approach is to pass all errors to an error handler function +that never returns if passed a non-success error value: + + // never returns if err != NULL + void handle_error( fex_err_t err ); + + void my_func() + { + handle_error( fex_foo() ); + handle_error( fex_bar() ); + } + +handle_error() could print the error and exit the program: + + void handle_error( fex_err_t err ) + { + if ( err != NULL ) + { + const char* str = fex_err_str( err ); + printf( "Error: %s\n", str ); + exit( EXIT_FAILURE ); + } + } + +handle_error() could also throw a C++ exception (or equivalently in C, +longmp() back to a setjmp() done inside caller()): + + void handle_error( fex_err_t err ) + { + switch ( fex_err_code( err ) ) + { + case fex_ok: return; + case fex_err_memory: throw std::bad_alloc(); + // ... + case fex_err_generic: + default: + throw std::runtime_error( fex_err_str( err ) ); + } + } + + void caller() + { + try + { + my_func(); + } + catch ( const std::exception& e ) + { + printf( "Error: %s\n", e.what() ); + } + } + + +Solving problems +---------------- +If you're having problems, try the following: + +* Enable debugging support in your environment. This enables assertions +and other run-time checks. In particular, be sure NDEBUG isn't defined. + +* Turn the compiler's optimizer is off. Sometimes an optimizer generates +bad code. + +* If multiple threads are being used, ensure that only one at a time is +accessing a given set of objects from the library. This library is not +in general thread-safe, though independent objects can be used in +separate threads. + +* If all else fails, see if the demo works. + + +Thanks +------ +Thanks to Richard Bannister, Kode54, byuu, Cless, and DJRobX for testing +and giving feedback for the library. Thanks to the authors of zlib, +unrar, and 7-zip. + +-- +Shay Green diff --git a/fex/fex/Binary_Extractor.cpp b/fex/fex/Binary_Extractor.cpp new file mode 100644 index 0000000..8c85b99 --- /dev/null +++ b/fex/fex/Binary_Extractor.cpp @@ -0,0 +1,77 @@ +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "Binary_Extractor.h" + +/* Copyright (C) 2005-2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +// TODO: could close file once data has been read into memory + +static File_Extractor* new_binary() +{ + return BLARGG_NEW Binary_Extractor; +} + +fex_type_t_ const fex_bin_type [1] = {{ + "", + &new_binary, + "file", + NULL +}}; + +Binary_Extractor::Binary_Extractor() : + File_Extractor( fex_bin_type ) +{ } + +Binary_Extractor::~Binary_Extractor() +{ + close(); +} + +blargg_err_t Binary_Extractor::open_path_v() +{ + set_name( arc_path() ); + return blargg_ok; +} + +blargg_err_t Binary_Extractor::open_v() +{ + set_name( arc_path() ); + set_info( arc().remain(), 0, 0 ); + return blargg_ok; +} + +void Binary_Extractor::close_v() +{ } + +blargg_err_t Binary_Extractor::next_v() +{ + return blargg_ok; +} + +blargg_err_t Binary_Extractor::rewind_v() +{ + return open_path_v(); +} + +blargg_err_t Binary_Extractor::stat_v() +{ + RETURN_ERR( open_arc_file() ); + RETURN_ERR( arc().seek( 0 ) ); + return open_v(); +} + +blargg_err_t Binary_Extractor::extract_v( void* p, int n ) +{ + return arc().read( p, n ); +} diff --git a/fex/fex/Binary_Extractor.h b/fex/fex/Binary_Extractor.h new file mode 100644 index 0000000..339a087 --- /dev/null +++ b/fex/fex/Binary_Extractor.h @@ -0,0 +1,26 @@ +// Presents a single file as an "archive" of just that file. + +// File_Extractor 1.0.0 +#ifndef BINARY_EXTRACTOR_H +#define BINARY_EXTRACTOR_H + +#include "File_Extractor.h" + +class Binary_Extractor : public File_Extractor { +public: + Binary_Extractor(); + virtual ~Binary_Extractor(); + +protected: + virtual blargg_err_t open_path_v(); + virtual blargg_err_t open_v(); + virtual void close_v(); + + virtual blargg_err_t next_v(); + virtual blargg_err_t rewind_v(); + + virtual blargg_err_t stat_v(); + virtual blargg_err_t extract_v( void*, int ); +}; + +#endif diff --git a/fex/fex/Data_Reader.cpp b/fex/fex/Data_Reader.cpp new file mode 100644 index 0000000..a461f78 --- /dev/null +++ b/fex/fex/Data_Reader.cpp @@ -0,0 +1,765 @@ +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "Data_Reader.h" + +#include "blargg_endian.h" +#include +#include +#include + +#if BLARGG_UTF8_PATHS + #include +#endif + +/* Copyright (C) 2005-2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +// Data_Reader + +blargg_err_t Data_Reader::read( void* p, int n ) +{ + assert( n >= 0 ); + + if ( n < 0 ) + return blargg_err_caller; + + if ( n <= 0 ) + return blargg_ok; + + if ( n > remain() ) + return blargg_err_file_eof; + + blargg_err_t err = read_v( p, n ); + if ( !err ) + remain_ -= n; + + return err; +} + +blargg_err_t Data_Reader::read_avail( void* p, int* n_ ) +{ + assert( *n_ >= 0 ); + + int n = min( *n_, remain() ); + *n_ = 0; + + if ( n < 0 ) + return blargg_err_caller; + + if ( n <= 0 ) + return blargg_ok; + + blargg_err_t err = read_v( p, n ); + if ( !err ) + { + remain_ -= n; + *n_ = n; + } + + return err; +} + +blargg_err_t Data_Reader::read_avail( void* p, long* n ) +{ + int i = STATIC_CAST(int, *n); + blargg_err_t err = read_avail( p, &i ); + *n = i; + return err; +} + +blargg_err_t Data_Reader::skip_v( int count ) +{ + char buf [512]; + while ( count ) + { + int n = min( count, (int) sizeof buf ); + count -= n; + RETURN_ERR( read_v( buf, n ) ); + } + return blargg_ok; +} + +blargg_err_t Data_Reader::skip( int n ) +{ + assert( n >= 0 ); + + if ( n < 0 ) + return blargg_err_caller; + + if ( n <= 0 ) + return blargg_ok; + + if ( n > remain() ) + return blargg_err_file_eof; + + blargg_err_t err = skip_v( n ); + if ( !err ) + remain_ -= n; + + return err; +} + + +// File_Reader + +blargg_err_t File_Reader::seek( int n ) +{ + assert( n >= 0 ); + + if ( n < 0 ) + return blargg_err_caller; + + if ( n == tell() ) + return blargg_ok; + + if ( n > size() ) + return blargg_err_file_eof; + + blargg_err_t err = seek_v( n ); + if ( !err ) + set_tell( n ); + + return err; +} + +blargg_err_t File_Reader::skip_v( int n ) +{ + return seek_v( tell() + n ); +} + + +// Subset_Reader + +Subset_Reader::Subset_Reader( Data_Reader* dr, int size ) : + in( dr ) +{ + set_remain( min( size, dr->remain() ) ); +} + +blargg_err_t Subset_Reader::read_v( void* p, int s ) +{ + return in->read( p, s ); +} + + +// Remaining_Reader + +Remaining_Reader::Remaining_Reader( void const* h, int size, Data_Reader* r ) : + in( r ) +{ + header = h; + header_remain = size; + + set_remain( size + r->remain() ); +} + +blargg_err_t Remaining_Reader::read_v( void* out, int count ) +{ + int first = min( count, header_remain ); + if ( first ) + { + memcpy( out, header, first ); + header = STATIC_CAST(char const*, header) + first; + header_remain -= first; + } + + return in->read( STATIC_CAST(char*, out) + first, count - first ); +} + + +// Mem_File_Reader + +Mem_File_Reader::Mem_File_Reader( const void* p, long s ) : + begin( STATIC_CAST(const char*, p) ) +{ + set_size( s ); +} + +blargg_err_t Mem_File_Reader::read_v( void* p, int s ) +{ + memcpy( p, begin + tell(), s ); + return blargg_ok; +} + +blargg_err_t Mem_File_Reader::seek_v( int ) +{ + return blargg_ok; +} + + +// Callback_Reader + +Callback_Reader::Callback_Reader( callback_t c, long s, void* d ) : + callback( c ), + user_data( d ) +{ + set_remain( s ); +} + +blargg_err_t Callback_Reader::read_v( void* out, int count ) +{ + return callback( user_data, out, count ); +} + + +// Callback_File_Reader + +Callback_File_Reader::Callback_File_Reader( callback_t c, long s, void* d ) : + callback( c ), + user_data( d ) +{ + set_size( s ); +} + +blargg_err_t Callback_File_Reader::read_v( void* out, int count ) +{ + return callback( user_data, out, count, tell() ); +} + +blargg_err_t Callback_File_Reader::seek_v( int ) +{ + return blargg_ok; +} + +static const BOOST::uint8_t mask_tab[6]={0x80,0xE0,0xF0,0xF8,0xFC,0xFE}; + +static const BOOST::uint8_t val_tab[6]={0,0xC0,0xE0,0xF0,0xF8,0xFC}; + +size_t utf8_char_len_from_header( char p_c ) +{ + BOOST::uint8_t c = (BOOST::uint8_t)p_c; + + size_t cnt = 0; + for(;;) + { + if ( ( p_c & mask_tab[cnt] ) == val_tab[cnt] ) break; + if ( ++cnt >= 6 ) return 0; + } + + return cnt + 1; +} + +size_t utf8_decode_char( const char *p_utf8, unsigned & wide, size_t mmax ) +{ + const BOOST::uint8_t * utf8 = ( const BOOST::uint8_t* )p_utf8; + + if ( mmax == 0 ) + { + wide = 0; + return 0; + } + + if ( utf8[0] < 0x80 ) + { + wide = utf8[0]; + return utf8[0]>0 ? 1 : 0; + } + if ( mmax > 6 ) mmax = 6; + wide = 0; + + unsigned res=0; + unsigned n; + unsigned cnt=0; + for(;;) + { + if ( ( *utf8 & mask_tab[cnt] ) == val_tab[cnt] ) break; + if ( ++cnt >= mmax ) return 0; + } + cnt++; + + if ( cnt==2 && !( *utf8 & 0x1E ) ) return 0; + + if ( cnt == 1 ) + res = *utf8; + else + res = ( 0xFF >> ( cnt + 1 ) ) & *utf8; + + for ( n = 1; n < cnt; n++ ) + { + if ( ( utf8[n] & 0xC0 ) != 0x80 ) + return 0; + if ( !res && n == 2 && !( ( utf8[n] & 0x7F ) >> ( 7 - cnt ) ) ) + return 0; + + res = ( res << 6 ) | ( utf8[n] & 0x3F ); + } + + wide = res; + + return cnt; +} + +size_t utf8_encode_char( unsigned wide, char * target ) +{ + size_t count; + + if ( wide < 0x80 ) + count = 1; + else if ( wide < 0x800 ) + count = 2; + else if ( wide < 0x10000 ) + count = 3; + else if ( wide < 0x200000 ) + count = 4; + else if ( wide < 0x4000000 ) + count = 5; + else if ( wide <= 0x7FFFFFFF ) + count = 6; + else + return 0; + + if ( target == 0 ) + return count; + + switch ( count ) + { + case 6: + target[5] = 0x80 | ( wide & 0x3F ); + wide = wide >> 6; + wide |= 0x4000000; + case 5: + target[4] = 0x80 | ( wide & 0x3F ); + wide = wide >> 6; + wide |= 0x200000; + case 4: + target[3] = 0x80 | ( wide & 0x3F ); + wide = wide >> 6; + wide |= 0x10000; + case 3: + target[2] = 0x80 | ( wide & 0x3F ); + wide = wide >> 6; + wide |= 0x800; + case 2: + target[1] = 0x80 | ( wide & 0x3F ); + wide = wide >> 6; + wide |= 0xC0; + case 1: + target[0] = wide; + } + + return count; +} + +size_t utf16_encode_char( unsigned cur_wchar, wchar_t * out ) +{ + if ( cur_wchar < 0x10000 ) + { + if ( out ) *out = (wchar_t) cur_wchar; return 1; + } + else if ( cur_wchar < ( 1 << 20 ) ) + { + unsigned c = cur_wchar - 0x10000; + //MSDN: + //The first (high) surrogate is a 16-bit code value in the range U+D800 to U+DBFF. The second (low) surrogate is a 16-bit code value in the range U+DC00 to U+DFFF. Using surrogates, Unicode can support over one million characters. For more details about surrogates, refer to The Unicode Standard, version 2.0. + if ( out ) + { + out[0] = ( wchar_t )( 0xD800 | ( 0x3FF & ( c >> 10 ) ) ); + out[1] = ( wchar_t )( 0xDC00 | ( 0x3FF & c ) ) ; + } + return 2; + } + else + { + if ( out ) *out = '?'; return 1; + } +} + +size_t utf16_decode_char( const wchar_t * p_source, unsigned * p_out, size_t p_source_length ) +{ + if ( p_source_length == 0 ) return 0; + else if ( p_source_length == 1 ) + { + *p_out = p_source[0]; + return 1; + } + else + { + size_t retval = 0; + unsigned decoded = p_source[0]; + if ( decoded != 0 ) + { + retval = 1; + if ( ( decoded & 0xFC00 ) == 0xD800 ) + { + unsigned low = p_source[1]; + if ( ( low & 0xFC00 ) == 0xDC00 ) + { + decoded = 0x10000 + ( ( ( decoded & 0x3FF ) << 10 ) | ( low & 0x3FF ) ); + retval = 2; + } + } + } + *p_out = decoded; + return retval; + } +} + +// Converts wide-character path to UTF-8. Free result with free(). Only supported on Windows. +char* blargg_to_utf8( const wchar_t* wpath ) +{ + if ( wpath == NULL ) + return NULL; + + size_t needed = 0; + size_t mmax = wcslen( wpath ); + if ( mmax <= 0 ) + return NULL; + + size_t ptr = 0; + while ( ptr < mmax ) + { + unsigned wide = 0; + size_t char_len = utf16_decode_char( wpath + ptr, &wide, mmax - ptr ); + if ( !char_len ) break; + ptr += char_len; + needed += utf8_encode_char( wide, 0 ); + } + if ( needed <= 0 ) + return NULL; + + char* path = (char*) calloc( needed + 1, 1 ); + if ( path == NULL ) + return NULL; + + ptr = 0; + size_t actual = 0; + while ( ptr < mmax && actual < needed ) + { + unsigned wide = 0; + size_t char_len = utf16_decode_char( wpath + ptr, &wide, mmax - ptr ); + if ( !char_len ) break; + ptr += char_len; + actual += utf8_encode_char( wide, path + actual ); + } + + if ( actual == 0 ) + { + free( path ); + return NULL; + } + + assert( actual == needed ); + return path; +} + +// Converts UTF-8 path to wide-character. Free result with free() Only supported on Windows. +wchar_t* blargg_to_wide( const char* path ) +{ + if ( path == NULL ) + return NULL; + + size_t mmax = strlen( path ); + if ( mmax <= 0 ) + return NULL; + + size_t needed = 0; + size_t ptr = 0; + while ( ptr < mmax ) + { + unsigned wide = 0; + size_t char_len = utf8_decode_char( path + ptr, wide, mmax - ptr ); + if ( !char_len ) break; + ptr += char_len; + needed += utf16_encode_char( wide, 0 ); + } + if ( needed <= 0 ) + return NULL; + + wchar_t* wpath = (wchar_t*) calloc( needed + 1, sizeof *wpath ); + if ( wpath == NULL ) + return NULL; + + ptr = 0; + size_t actual = 0; + while ( ptr < mmax && actual < needed ) + { + unsigned wide = 0; + size_t char_len = utf8_decode_char( path + ptr, wide, mmax - ptr ); + if ( !char_len ) break; + ptr += char_len; + actual += utf16_encode_char( wide, wpath + actual ); + } + if ( actual == 0 ) + { + free( wpath ); + return NULL; + } + + assert( actual == needed ); + return wpath; +} + +#ifdef _WIN32 + +static FILE* blargg_fopen( const char path [], const char mode [] ) +{ + FILE* file = NULL; + wchar_t* wmode = NULL; + wchar_t* wpath = NULL; + + wpath = blargg_to_wide( path ); + if ( wpath ) + { + wmode = blargg_to_wide( mode ); + if ( wmode ) + file = _wfopen( wpath, wmode ); + } + + // Save and restore errno in case free() clears it + int saved_errno = errno; + free( wmode ); + free( wpath ); + errno = saved_errno; + + return file; +} + +#else + +static inline FILE* blargg_fopen( const char path [], const char mode [] ) +{ + return fopen( path, mode ); +} + +#endif + + +// Std_File_Reader + +Std_File_Reader::Std_File_Reader() +{ + file_ = NULL; +} + +Std_File_Reader::~Std_File_Reader() +{ + close(); +} + +static blargg_err_t blargg_fopen( FILE** out, const char path [] ) +{ + errno = 0; + *out = blargg_fopen( path, "rb" ); + if ( !*out ) + { + #ifdef ENOENT + if ( errno == ENOENT ) + return blargg_err_file_missing; + #endif + #ifdef ENOMEM + if ( errno == ENOMEM ) + return blargg_err_memory; + #endif + return blargg_err_file_read; + } + + return blargg_ok; +} + +static blargg_err_t blargg_fsize( FILE* f, long* out ) +{ + if ( fseek( f, 0, SEEK_END ) ) + return blargg_err_file_io; + + *out = ftell( f ); + if ( *out < 0 ) + return blargg_err_file_io; + + if ( fseek( f, 0, SEEK_SET ) ) + return blargg_err_file_io; + + return blargg_ok; +} + +blargg_err_t Std_File_Reader::open( const char path [] ) +{ + close(); + + FILE* f; + RETURN_ERR( blargg_fopen( &f, path ) ); + + long s; + blargg_err_t err = blargg_fsize( f, &s ); + if ( err ) + { + fclose( f ); + return err; + } + + file_ = f; + set_size( s ); + + return blargg_ok; +} + +void Std_File_Reader::make_unbuffered() +{ + if ( setvbuf( STATIC_CAST(FILE*, file_), NULL, _IONBF, 0 ) ) + check( false ); // shouldn't fail, but OK if it does +} + +blargg_err_t Std_File_Reader::read_v( void* p, int s ) +{ + if ( (size_t) s != fread( p, 1, s, STATIC_CAST(FILE*, file_) ) ) + { + // Data_Reader's wrapper should prevent EOF + check( !feof( STATIC_CAST(FILE*, file_) ) ); + + return blargg_err_file_io; + } + + return blargg_ok; +} + +blargg_err_t Std_File_Reader::seek_v( int n ) +{ + if ( fseek( STATIC_CAST(FILE*, file_), n, SEEK_SET ) ) + { + // Data_Reader's wrapper should prevent EOF + check( !feof( STATIC_CAST(FILE*, file_) ) ); + + return blargg_err_file_io; + } + + return blargg_ok; +} + +void Std_File_Reader::close() +{ + if ( file_ ) + { + fclose( STATIC_CAST(FILE*, file_) ); + file_ = NULL; + } +} + + +// Gzip_File_Reader + +#ifdef HAVE_ZLIB_H + +#include "zlib.h" + +static const char* get_gzip_eof( const char path [], long* eof ) +{ + FILE* file; + RETURN_ERR( blargg_fopen( &file, path ) ); + + int const h_size = 4; + unsigned char h [h_size]; + + // read four bytes to ensure that we can seek to -4 later + if ( fread( h, 1, h_size, file ) != (size_t) h_size || h[0] != 0x1F || h[1] != 0x8B ) + { + // Not gzipped + if ( ferror( file ) ) + return blargg_err_file_io; + + if ( fseek( file, 0, SEEK_END ) ) + return blargg_err_file_io; + + *eof = ftell( file ); + if ( *eof < 0 ) + return blargg_err_file_io; + } + else + { + // Gzipped; get uncompressed size from end + if ( fseek( file, -h_size, SEEK_END ) ) + return blargg_err_file_io; + + if ( fread( h, 1, h_size, file ) != (size_t) h_size ) + return blargg_err_file_io; + + *eof = get_le32( h ); + } + + if ( fclose( file ) ) + check( false ); + + return blargg_ok; +} + +Gzip_File_Reader::Gzip_File_Reader() +{ + file_ = NULL; +} + +Gzip_File_Reader::~Gzip_File_Reader() +{ + close(); +} + +blargg_err_t Gzip_File_Reader::open( const char path [] ) +{ + close(); + + long s; + RETURN_ERR( get_gzip_eof( path, &s ) ); + + file_ = gzopen( path, "rb" ); + if ( !file_ ) + return blargg_err_file_read; + + set_size( s ); + return blargg_ok; +} + +static blargg_err_t convert_gz_error( gzFile file ) +{ + int err; + gzerror( file, &err ); + + switch ( err ) + { + case Z_STREAM_ERROR: break; + case Z_DATA_ERROR: return blargg_err_file_corrupt; + case Z_MEM_ERROR: return blargg_err_memory; + case Z_BUF_ERROR: break; + } + return blargg_err_internal; +} + +blargg_err_t Gzip_File_Reader::read_v( void* p, int s ) +{ + int result = gzread( (gzFile)file_, p, s ); + if ( result != s ) + { + if ( result < 0 ) + return convert_gz_error( (gzFile)file_ ); + + return blargg_err_file_corrupt; + } + + return blargg_ok; +} + +blargg_err_t Gzip_File_Reader::seek_v( int n ) +{ + if ( gzseek( (gzFile)file_, n, SEEK_SET ) < 0 ) + return convert_gz_error( (gzFile)file_ ); + + return blargg_ok; +} + +void Gzip_File_Reader::close() +{ + if ( file_ ) + { + if ( gzclose( (gzFile)file_ ) ) + check( false ); + file_ = NULL; + } +} + +#endif diff --git a/fex/fex/Data_Reader.h b/fex/fex/Data_Reader.h new file mode 100644 index 0000000..be206f7 --- /dev/null +++ b/fex/fex/Data_Reader.h @@ -0,0 +1,264 @@ +// Lightweight interface for reading data from byte stream + +// File_Extractor 1.0.0 +#ifndef DATA_READER_H +#define DATA_READER_H + +#include "blargg_common.h" + +/* Some functions accept a long instead of int for convenience where caller has +a long due to some other interface, and would otherwise have to get a warning, +or cast it (and verify that it wasn't outside the range of an int). + +To really support huge (>2GB) files, long isn't a solution, since there's no +guarantee it's more than 32 bits. We'd need to use long long (if available), or +something compiler-specific, and change all places file sizes or offsets are +used. */ + +// Supports reading and finding out how many bytes are remaining +class Data_Reader { +public: + + // Reads min(*n,remain()) bytes and sets *n to this number, thus trying to read more + // tham remain() bytes doesn't result in error, just *n being set to remain(). + blargg_err_t read_avail( void* p, int* n ); + blargg_err_t read_avail( void* p, long* n ); + + // Reads exactly n bytes, or returns error if they couldn't ALL be read. + // Reading past end of file results in blargg_err_file_eof. + blargg_err_t read( void* p, int n ); + + // Number of bytes remaining until end of file + int remain() const { return remain_; } + + // Reads and discards n bytes. Skipping past end of file results in blargg_err_file_eof. + blargg_err_t skip( int n ); + + virtual ~Data_Reader() { } + +private: + // noncopyable + Data_Reader( const Data_Reader& ); + Data_Reader& operator = ( const Data_Reader& ); + +// Derived interface +protected: + Data_Reader() : remain_( 0 ) { } + + // Sets remain + void set_remain( int n ) { assert( n >= 0 ); remain_ = n; } + + // Do same as read(). Guaranteed that 0 < n <= remain(). Value of remain() is updated + // AFTER this call succeeds, not before. set_remain() should NOT be called from this. + virtual blargg_err_t read_v( void*, int n ) BLARGG_PURE( { (void)n; return blargg_ok; } ) + + // Do same as skip(). Guaranteed that 0 < n <= remain(). Default just reads data + // and discards it. Value of remain() is updated AFTER this call succeeds, not + // before. set_remain() should NOT be called from this. + virtual blargg_err_t skip_v( int n ); + +// Implementation +public: + BLARGG_DISABLE_NOTHROW + +private: + int remain_; +}; + + +// Supports seeking in addition to Data_Reader operations +class File_Reader : public Data_Reader { +public: + + // Size of file + int size() const { return size_; } + + // Current position in file + int tell() const { return size_ - remain(); } + + // Goes to new position + blargg_err_t seek( int ); + +// Derived interface +protected: + // Sets size and resets position + void set_size( int n ) { size_ = n; Data_Reader::set_remain( n ); } + void set_size( long n ) { set_size( STATIC_CAST(int, n) ); } + + // Sets reported position + void set_tell( int i ) { assert( 0 <= i && i <= size_ ); Data_Reader::set_remain( size_ - i ); } + + // Do same as seek(). Guaranteed that 0 <= n <= size(). Value of tell() is updated + // AFTER this call succeeds, not before. set_* functions should NOT be called from this. + virtual blargg_err_t seek_v( int n ) BLARGG_PURE( { (void)n; return blargg_ok; } ) + +// Implementation +protected: + File_Reader() : size_( 0 ) { } + + virtual blargg_err_t skip_v( int ); + +private: + int size_; + + void set_remain(); // avoid accidental use of set_remain +}; + + +// Reads from file on disk +class Std_File_Reader : public File_Reader { +public: + + // Opens file + blargg_err_t open( const char path [] ); + + // Closes file if one was open + void close(); + + // Switches to unbuffered mode. Useful if buffering is already being + // done at a higher level. + void make_unbuffered(); + +// Implementation +public: + Std_File_Reader(); + virtual ~Std_File_Reader(); + +protected: + virtual blargg_err_t read_v( void*, int ); + virtual blargg_err_t seek_v( int ); + +private: + void* file_; +}; + + +// Treats range of memory as a file +class Mem_File_Reader : public File_Reader { +public: + + Mem_File_Reader( const void* begin, long size ); + +// Implementation +protected: + virtual blargg_err_t read_v( void*, int ); + virtual blargg_err_t seek_v( int ); + +private: + const char* const begin; +}; + + +// Allows only count bytes to be read from reader passed +class Subset_Reader : public Data_Reader { +public: + + Subset_Reader( Data_Reader*, int count ); + +// Implementation +protected: + virtual blargg_err_t read_v( void*, int ); + +private: + Data_Reader* const in; +}; + + +// Joins already-read header and remaining data into original file. +// Meant for cases where you've already read header and don't want +// to seek and re-read data (for efficiency). +class Remaining_Reader : public Data_Reader { +public: + + Remaining_Reader( void const* header, int header_size, Data_Reader* ); + +// Implementation +protected: + virtual blargg_err_t read_v( void*, int ); + +private: + Data_Reader* const in; + void const* header; + int header_remain; +}; + + +// Invokes callback function to read data +extern "C" { // necessary to be usable from C + typedef const char* (*callback_reader_func_t)( + void* user_data, // Same value passed to constructor + void* out, // Buffer to place data into + int count // Number of bytes to read + ); +} +class Callback_Reader : public Data_Reader { +public: + typedef callback_reader_func_t callback_t; + Callback_Reader( callback_t, long size, void* user_data ); + +// Implementation +protected: + virtual blargg_err_t read_v( void*, int ); + +private: + callback_t const callback; + void* const user_data; +}; + + +// Invokes callback function to read data +extern "C" { // necessary to be usable from C + typedef const char* (*callback_file_reader_func_t)( + void* user_data, // Same value passed to constructor + void* out, // Buffer to place data into + int count, // Number of bytes to read + int pos // Position in file to read from + ); +} +class Callback_File_Reader : public File_Reader { +public: + typedef callback_file_reader_func_t callback_t; + Callback_File_Reader( callback_t, long size, void* user_data ); + +// Implementation +protected: + virtual blargg_err_t read_v( void*, int ); + virtual blargg_err_t seek_v( int ); + +private: + callback_t const callback; + void* const user_data; +}; + + +#ifdef HAVE_ZLIB_H + +// Reads file compressed with gzip (or uncompressed) +class Gzip_File_Reader : public File_Reader { +public: + + // Opens possibly gzipped file + blargg_err_t open( const char path [] ); + + // Closes file if one was open + void close(); + +// Implementation +public: + Gzip_File_Reader(); + ~Gzip_File_Reader(); + +protected: + virtual blargg_err_t read_v( void*, int ); + virtual blargg_err_t seek_v( int ); + +private: + // void* so "zlib.h" doesn't have to be included here + void* file_; +}; +#endif + +char* blargg_to_utf8( const wchar_t* ); +wchar_t* blargg_to_wide( const char* ); + +#endif diff --git a/fex/fex/File_Extractor.cpp b/fex/fex/File_Extractor.cpp new file mode 100644 index 0000000..e060e09 --- /dev/null +++ b/fex/fex/File_Extractor.cpp @@ -0,0 +1,341 @@ +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "File_Extractor.h" + +/* Copyright (C) 2005-2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +File_Extractor::fex_t( fex_type_t t ) : + type_( t ) +{ + own_file_ = NULL; + + close_(); +} + +// Open + +blargg_err_t File_Extractor::set_path( const char* path ) +{ + if ( !path ) + path = ""; + + RETURN_ERR( path_.resize( strlen( path ) + 1 ) ); + memcpy( path_.begin(), path, path_.size() ); + return blargg_ok; +} + +blargg_err_t File_Extractor::open( const char path [] ) +{ + close(); + + RETURN_ERR( set_path( path ) ); + + blargg_err_t err = open_path_v(); + if ( err ) + close(); + else + opened_ = true; + + return err; +} + +blargg_err_t File_Extractor::open_path_v() +{ + RETURN_ERR( open_arc_file() ); + + return open_v(); +} + +inline +static void make_unbuffered( Std_File_Reader* r ) +{ + r->make_unbuffered(); +} + +inline +static void make_unbuffered( void* ) +{ } + +blargg_err_t File_Extractor::open_arc_file( bool unbuffered ) +{ + if ( reader_ ) + return blargg_ok; + + FEX_FILE_READER* in = BLARGG_NEW FEX_FILE_READER; + CHECK_ALLOC( in ); + + blargg_err_t err = in->open( arc_path() ); + if ( err ) + { + delete in; + } + else + { + reader_ = in; + own_file(); + if ( unbuffered ) + make_unbuffered( in ); + } + + return err; +} + +blargg_err_t File_Extractor::open( File_Reader* input, const char* path ) +{ + close(); + + RETURN_ERR( set_path( path ) ); + + RETURN_ERR( input->seek( 0 ) ); + + reader_ = input; + blargg_err_t err = open_v(); + if ( err ) + close(); + else + opened_ = true; + + return err; +} + +// Close + +void File_Extractor::close() +{ + close_v(); + close_(); +} + +void File_Extractor::close_() +{ + delete own_file_; + own_file_ = NULL; + + tell_ = 0; + reader_ = NULL; + opened_ = false; + + path_.clear(); + clear_file(); +} + +File_Extractor::~fex_t() +{ + check( !opened() ); // fails if derived destructor didn't call close() + + delete own_file_; +} + +// Scanning + +void File_Extractor::clear_file() +{ + name_ = NULL; + wname_ = NULL; + done_ = true; + stat_called = false; + data_ptr_ = NULL; + + set_info( 0 ); + own_data_.clear(); + clear_file_v(); +} + +void File_Extractor::set_name( const char new_name [], const wchar_t* new_wname ) +{ + name_ = new_name; + wname_ = new_wname; + done_ = false; +} + +void File_Extractor::set_info( int new_size, unsigned date, unsigned crc ) +{ + size_ = new_size; + date_ = (date != 0xFFFFFFFF ? date : 0); + crc32_ = crc; + set_remain( new_size ); +} + +blargg_err_t File_Extractor::next_() +{ + tell_++; + clear_file(); + + blargg_err_t err = next_v(); + if ( err ) + clear_file(); + + return err; +} + +blargg_err_t File_Extractor::next() +{ + assert( !done() ); + return next_(); +} + +blargg_err_t File_Extractor::rewind() +{ + assert( opened() ); + + tell_ = 0; + clear_file(); + + blargg_err_t err = rewind_v(); + if ( err ) + clear_file(); + + return err; +} + +blargg_err_t File_Extractor::stat() +{ + assert( !done() ); + + if ( !stat_called ) + { + RETURN_ERR( stat_v() ); + stat_called = true; + } + return blargg_ok; +} + +// Tell/seek + +int const pos_offset = 1; + +fex_pos_t File_Extractor::tell_arc() const +{ + assert( opened() ); + + fex_pos_t pos = tell_arc_v(); + assert( pos >= 0 ); + + return pos + pos_offset; +} + +blargg_err_t File_Extractor::seek_arc( fex_pos_t pos ) +{ + assert( opened() ); + assert( pos != 0 ); + + clear_file(); + + blargg_err_t err = seek_arc_v( pos - pos_offset ); + if ( err ) + clear_file(); + + return err; +} + +fex_pos_t File_Extractor::tell_arc_v() const +{ + return tell_; +} + +blargg_err_t File_Extractor::seek_arc_v( fex_pos_t pos ) +{ + // >= because seeking to current file should always reset read pointer etc. + if ( tell_ >= pos ) + RETURN_ERR( rewind() ); + + while ( tell_ < pos ) + { + RETURN_ERR( next_() ); + + if ( done() ) + { + assert( false ); + return blargg_err_caller; + } + } + + assert( tell_ == pos ); + + return blargg_ok; +} + +// Extraction + +blargg_err_t File_Extractor::rewind_file() +{ + RETURN_ERR( stat() ); + + if ( tell() > 0 ) + { + if ( data_ptr_ ) + { + set_remain( size() ); + } + else + { + RETURN_ERR( seek_arc( tell_arc() ) ); + RETURN_ERR( stat() ); + } + } + + return blargg_ok; +} + +blargg_err_t File_Extractor::data( const void** data_out ) +{ + assert( !done() ); + + *data_out = NULL; + if ( !data_ptr_ ) + { + int old_tell = tell(); + + RETURN_ERR( rewind_file() ); + + void const* ptr; + RETURN_ERR( data_v( &ptr ) ); + data_ptr_ = ptr; + + // Now that data is in memory, we can seek by simply setting remain + set_remain( size() - old_tell ); + } + + *data_out = data_ptr_; + return blargg_ok; +} + +blargg_err_t File_Extractor::data_v( void const** out ) +{ + RETURN_ERR( own_data_.resize( size() ) ); + *out = own_data_.begin(); + + blargg_err_t err = extract_v( own_data_.begin(), own_data_.size() ); + if ( err ) + own_data_.clear(); + + return err; +} + +blargg_err_t File_Extractor::extract_v( void* out, int count ) +{ + void const* p; + RETURN_ERR( data( &p ) ); + memcpy( out, STATIC_CAST(char const*,p) + (size() - remain()), count ); + + return blargg_ok; +} + +blargg_err_t File_Extractor::read_v( void* out, int count ) +{ + if ( data_ptr_ ) + return File_Extractor::extract_v( out, count ); + + return extract_v( out, count ); +} diff --git a/fex/fex/File_Extractor.h b/fex/fex/File_Extractor.h new file mode 100644 index 0000000..ad25d5f --- /dev/null +++ b/fex/fex/File_Extractor.h @@ -0,0 +1,191 @@ +// Compressed file archive interface + +// File_Extractor 1.0.0 +#ifndef FILE_EXTRACTOR_H +#define FILE_EXTRACTOR_H + +#include "blargg_common.h" +#include "Data_Reader.h" +#include "fex.h" + +struct fex_t : private Data_Reader { +public: + virtual ~fex_t(); + +// Open/close + + // Opens archive from custom data source. Keeps pointer until close(). + blargg_err_t open( File_Reader* input, const char* path = NULL ); + + // Takes ownership of File_Reader* passed to open(), so that close() + // will delete it. + void own_file() { own_file_ = reader_; } + + // See fex.h + blargg_err_t open( const char path [] ); + fex_type_t type() const { return type_; } + void close(); + +// Scanning + + // See fex.h + bool done() const { return done_; } + blargg_err_t next(); + blargg_err_t rewind(); + fex_pos_t tell_arc() const; + blargg_err_t seek_arc( fex_pos_t ); + +// Info + + // See fex.h + const char* name() const { return name_; } + const wchar_t* wname() const { return wname_; } + blargg_err_t stat(); + int size() const { assert( stat_called ); return size_; } + unsigned int dos_date() const { return date_; } + unsigned int crc32() const { return crc32_; } + +// Extraction + + // Data_Reader to current file's data, so standard Data_Reader interface can + // be used, rather than having to treat archives specially. stat() must have + // been called. + Data_Reader& reader() { assert( stat_called ); return *this; } + + // See fex.h + blargg_err_t data( const void** data_out ); + int tell() const { return size_ - remain(); } + +// Derived interface +protected: + + // Sets type of object + fex_t( fex_type_t ); + + // Path to archive file, or "" if none supplied + const char* arc_path() const { return path_.begin(); } + + // Opens archive file if it's not already. If unbuffered is true, opens file + // without any buffering. + blargg_err_t open_arc_file( bool unbuffered = false ); + + // Archive file + File_Reader& arc() const { return *reader_; } + + // Sets current file name + void set_name( const char name [], const wchar_t* wname = NULL ); + + // Sets current file information + void set_info( int size, unsigned date = 0, unsigned crc = 0 ); + +// User overrides + + // Overrides must do indicated task. Non-pure functions have reasonable default + // implementation. Overrides should avoid calling public functions like + // next() and rewind(). + + // Open archive using file_path(). OK to delay actual file opening until later. + // Default just calls open_arc_file(), then open_v(). + virtual blargg_err_t open_path_v(); + + // Open archive using file() for source data. If unsupported, return error. + virtual blargg_err_t open_v() BLARGG_PURE( ; ) + + // Go to next file in archive and call set_name() and optionally set_info() + virtual blargg_err_t next_v() BLARGG_PURE( ; ) + + // Go back to first file in archive + virtual blargg_err_t rewind_v() BLARGG_PURE( ; ) + + // Close archive. Called even if open_path_v() or open_v() return unsuccessfully. + virtual void close_v() BLARGG_PURE( ; ) + + // Clear any fields related to current file + virtual void clear_file_v() { } + + // Call set_info() if not already called by next_v() + virtual blargg_err_t stat_v() { return blargg_ok; } + + // Return value that allows later return to this file. Result must be >= 0. + virtual fex_pos_t tell_arc_v() const; + + // Return to previously saved position + virtual blargg_err_t seek_arc_v( fex_pos_t ); + + // One or both of the following must be overridden + + // Provide pointer to data for current file in archive + virtual blargg_err_t data_v( const void** out ); + + // Extract next n bytes + virtual blargg_err_t extract_v( void* out, int n ); + +// Implementation +public: + BLARGG_DISABLE_NOTHROW + +private: + fex_type_t const type_; + + // Archive file + blargg_vector path_; + File_Reader* reader_; + File_Reader* own_file_; + bool opened_; + + // Position in archive + fex_pos_t tell_; // only used by default implementation of tell/seek + bool done_; + + // Info for current file in archive + const char* name_; + const wchar_t* wname_; + unsigned date_; + unsigned crc32_; + int size_; + bool stat_called; + + // Current file contents + void const* data_ptr_; // NULL if not read into memory + blargg_vector own_data_; + + bool opened() const { return opened_; } + void clear_file(); + void close_(); + blargg_err_t set_path( const char* path ); + blargg_err_t rewind_file(); + blargg_err_t next_(); + + // Data_Reader overrides + // TODO: override skip_v? + virtual blargg_err_t read_v( void* out, int n ); +}; + +struct fex_type_t_ +{ + const char* extension; + File_Extractor* (*new_fex)(); + const char* name; + blargg_err_t (*init)(); // Called by fex_init(). Can be NULL. +}; + +extern const fex_type_t_ + fex_7z_type [1], + fex_gz_type [1], + fex_rar_type [1], + fex_zip_type [1], + fex_bin_type [1]; + +inline blargg_err_t File_Extractor::open_v() { return blargg_ok; } +inline blargg_err_t File_Extractor::next_v() { return blargg_ok; } +inline blargg_err_t File_Extractor::rewind_v() { return blargg_ok; } +inline void File_Extractor::close_v() { } + +// Default to Std_File_Reader for archive access +#ifndef FEX_FILE_READER + #define FEX_FILE_READER Std_File_Reader +#elif defined (FEX_FILE_READER_INCLUDE) + #include FEX_FILE_READER_INCLUDE +#endif + +#endif diff --git a/fex/fex/Gzip_Extractor.cpp b/fex/fex/Gzip_Extractor.cpp new file mode 100644 index 0000000..f169fed --- /dev/null +++ b/fex/fex/Gzip_Extractor.cpp @@ -0,0 +1,98 @@ +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "Gzip_Extractor.h" + +/* Copyright (C) 2005-2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +// TODO: could close file once data has been read into memory + +static blargg_err_t init_gzip_file() +{ + get_crc_table(); // initialize zlib's CRC-32 tables + return blargg_ok; +} + +static File_Extractor* new_gzip() +{ + return BLARGG_NEW Gzip_Extractor; +} + +fex_type_t_ const fex_gz_type [1] = {{ + ".gz", + &new_gzip, + "gzipped file", + &init_gzip_file +}}; + +Gzip_Extractor::Gzip_Extractor() : + File_Extractor( fex_gz_type ) +{ } + +Gzip_Extractor::~Gzip_Extractor() +{ + close(); +} + +blargg_err_t Gzip_Extractor::open_path_v() +{ + // skip opening file + return open_v(); +} + +blargg_err_t Gzip_Extractor::stat_v() +{ + RETURN_ERR( open_arc_file( true ) ); + if ( !gr.opened() || gr.tell() != 0 ) + RETURN_ERR( gr.open( &arc() ) ); + + set_info( gr.remain(), 0, gr.crc32() ); + return blargg_ok; +} + +blargg_err_t Gzip_Extractor::open_v() +{ + // Remove .gz suffix + size_t len = strlen( arc_path() ); + if ( fex_has_extension( arc_path(), ".gz" ) ) + len -= 3; + + RETURN_ERR( name.resize( len + 1 ) ); + memcpy( name.begin(), arc_path(), name.size() ); + name [name.size() - 1] = '\0'; + + set_name( name.begin() ); + return blargg_ok; +} + +void Gzip_Extractor::close_v() +{ + name.clear(); + gr.close(); +} + +blargg_err_t Gzip_Extractor::next_v() +{ + return blargg_ok; +} + +blargg_err_t Gzip_Extractor::rewind_v() +{ + set_name( name.begin() ); + return blargg_ok; +} + +blargg_err_t Gzip_Extractor::extract_v( void* p, int n ) +{ + return gr.read( p, n ); +} diff --git a/fex/fex/Gzip_Extractor.h b/fex/fex/Gzip_Extractor.h new file mode 100644 index 0000000..814dc9b --- /dev/null +++ b/fex/fex/Gzip_Extractor.h @@ -0,0 +1,34 @@ +// Presents a gzipped file as an "archive" of just that file. +// Also handles non-gzipped files. + +// File_Extractor 1.0.0 +#ifndef GZIP_EXTRACTOR_H +#define GZIP_EXTRACTOR_H + +#include "File_Extractor.h" +#include "Gzip_Reader.h" + +class Gzip_Extractor : public File_Extractor { +public: + Gzip_Extractor(); + virtual ~Gzip_Extractor(); + +protected: + virtual blargg_err_t open_path_v(); + virtual blargg_err_t open_v(); + virtual void close_v(); + + virtual blargg_err_t next_v(); + virtual blargg_err_t rewind_v(); + + virtual blargg_err_t stat_v(); + virtual blargg_err_t extract_v( void*, int ); + +private: + Gzip_Reader gr; + blargg_vector name; + + void set_info_(); +}; + +#endif diff --git a/fex/fex/Gzip_Reader.cpp b/fex/fex/Gzip_Reader.cpp new file mode 100644 index 0000000..2aad302 --- /dev/null +++ b/fex/fex/Gzip_Reader.cpp @@ -0,0 +1,85 @@ +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "Gzip_Reader.h" + +#include "blargg_endian.h" + +/* Copyright (C) 2006-2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +Gzip_Reader::Gzip_Reader() +{ + close(); +} + +Gzip_Reader::~Gzip_Reader() +{ } + +static blargg_err_t gzip_reader_read( void* file, void* out, int* count ) +{ + return STATIC_CAST(File_Reader*,file)->read_avail( out, count ); +} + +blargg_err_t Gzip_Reader::calc_size() +{ + size_ = in->size(); + crc32_ = 0; + if ( inflater.deflated() ) + { + byte trailer [8]; + int old_pos = in->tell(); + RETURN_ERR( in->seek( size_ - sizeof trailer ) ); + RETURN_ERR( in->read( trailer, sizeof trailer ) ); + RETURN_ERR( in->seek( old_pos ) ); + crc32_ = get_le32( trailer + 0 ); + + unsigned n = get_le32( trailer + 4 ); + if ( n > INT_MAX ) + return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "gzip larger than 2GB" ); + + size_ = n; + } + return blargg_ok; +} + +blargg_err_t Gzip_Reader::open( File_Reader* new_in ) +{ + close(); + + in = new_in; + RETURN_ERR( in->seek( 0 ) ); + RETURN_ERR( inflater.begin( gzip_reader_read, new_in ) ); + RETURN_ERR( inflater.set_mode( inflater.mode_auto ) ); + RETURN_ERR( calc_size() ); + set_remain( size_ ); + + return blargg_ok; +} + +void Gzip_Reader::close() +{ + in = NULL; + inflater.end(); +} + +blargg_err_t Gzip_Reader::read_v( void* out, int count ) +{ + assert( in ); + int actual = count; + RETURN_ERR( inflater.read( out, &actual ) ); + + if ( actual != count ) + return blargg_err_file_corrupt; + + return blargg_ok; +} diff --git a/fex/fex/Gzip_Reader.h b/fex/fex/Gzip_Reader.h new file mode 100644 index 0000000..a9b2d6a --- /dev/null +++ b/fex/fex/Gzip_Reader.h @@ -0,0 +1,46 @@ +// Transparently decompresses gzip files, as well as uncompressed + +// File_Extractor 1.0.0 +#ifndef GZIP_READER_H +#define GZIP_READER_H + +#include "Data_Reader.h" +#include "Zlib_Inflater.h" + +class Gzip_Reader : public Data_Reader { +public: + // Keeps pointer to reader until close(). If + blargg_err_t open( File_Reader* ); + + // True if file is open + bool opened() const { return in != NULL; } + + // Frees memory + void close(); + + // True if file is compressed + bool deflated() const { return inflater.deflated(); } + + // CRC-32 of data, of 0 if unavailable + unsigned int crc32() const { return crc32_; } + + // Number of bytes read since opening + int tell() const { return size_ - remain(); } + +public: + Gzip_Reader(); + virtual ~Gzip_Reader(); + +protected: + virtual blargg_err_t read_v( void*, int ); + +private: + File_Reader* in; + unsigned crc32_; + int size_; + Zlib_Inflater inflater; + + blargg_err_t calc_size(); +}; + +#endif diff --git a/fex/fex/Rar_Extractor.cpp b/fex/fex/Rar_Extractor.cpp new file mode 100644 index 0000000..afade7f --- /dev/null +++ b/fex/fex/Rar_Extractor.cpp @@ -0,0 +1,197 @@ +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "blargg_common.h" + +#if FEX_ENABLE_RAR + +#include "Rar_Extractor.h" + +/* Copyright (C) 2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +static blargg_err_t init_rar() +{ + unrar_init(); + return blargg_ok; +} + +static File_Extractor* new_rar() +{ + return BLARGG_NEW Rar_Extractor; +} + +fex_type_t_ const fex_rar_type [1] = {{ + ".rar", + &new_rar, + "RAR archive", + &init_rar +}}; + +blargg_err_t Rar_Extractor::convert_err( unrar_err_t err ) +{ + blargg_err_t reader_err = reader.err; + reader.err = blargg_ok; + if ( reader_err ) + check( err == unrar_next_err ); + + switch ( err ) + { + case unrar_ok: return blargg_ok; + case unrar_err_memory: return blargg_err_memory; + case unrar_err_open: return blargg_err_file_read; + case unrar_err_not_arc: return blargg_err_file_type; + case unrar_err_corrupt: return blargg_err_file_corrupt; + case unrar_err_io: return blargg_err_file_io; + case unrar_err_arc_eof: return blargg_err_internal; + case unrar_err_encrypted: return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "RAR encryption not supported" ); + case unrar_err_segmented: return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "RAR segmentation not supported" ); + case unrar_err_huge: return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "Huge RAR files not supported" ); + case unrar_err_old_algo: return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "Old RAR compression not supported" ); + case unrar_err_new_algo: return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "RAR uses unknown newer compression" ); + case unrar_next_err: break; + default: + check( false ); // unhandled RAR error + } + + if ( reader_err ) + return reader_err; + + check( false ); + return BLARGG_ERR( BLARGG_ERR_INTERNAL, "RAR archive" ); +} + +static inline unrar_err_t handle_err( Rar_Extractor::read_callback_t* h, blargg_err_t err ) +{ + if ( !err ) + return unrar_ok; + + h->err = err; + return unrar_next_err; +} + +extern "C" +{ + static unrar_err_t my_unrar_read( void* data, void* out, int* count, unrar_pos_t pos ) + { + // TODO: 64-bit file support + + Rar_Extractor::read_callback_t* h = STATIC_CAST(Rar_Extractor::read_callback_t*,data); + if ( h->pos != pos ) + { + blargg_err_t err = h->in->seek( pos ); + if ( err ) + return handle_err( h, err ); + + h->pos = pos; + } + + blargg_err_t err = h->in->read_avail( out, count ); + if ( err ) + return handle_err( h, err ); + + h->pos += *count; + + return unrar_ok; + } +} + +Rar_Extractor::Rar_Extractor() : + File_Extractor( fex_rar_type ) +{ + unrar = NULL; +} + +Rar_Extractor::~Rar_Extractor() +{ + close(); +} + +blargg_err_t Rar_Extractor::open_v() +{ + reader.pos = 0; + reader.in = &arc(); + reader.err = blargg_ok; + + RETURN_ERR( arc().seek( 0 ) ); + RETURN_ERR( convert_err( unrar_open_custom( &unrar, &my_unrar_read, &reader ) ) ); + return skip_unextractables(); +} + +void Rar_Extractor::close_v() +{ + unrar_close( unrar ); + + unrar = NULL; + reader.in = NULL; +} + +blargg_err_t Rar_Extractor::skip_unextractables() +{ + while ( !unrar_done( unrar ) && unrar_try_extract( unrar ) ) + RETURN_ERR( next_raw() ); + + if ( !unrar_done( unrar ) ) + { + unrar_info_t const* info = unrar_info( unrar ); + + set_name( info->name, (info->name_w && *info->name_w) ? info->name_w : NULL ); + set_info( info->size, info->dos_date, (info->is_crc32 ? info->crc : 0) ); + } + + return blargg_ok; +} + +blargg_err_t Rar_Extractor::next_raw() +{ + return convert_err( unrar_next( unrar ) ); +} + +blargg_err_t Rar_Extractor::next_v() +{ + RETURN_ERR( next_raw() ); + return skip_unextractables(); +} + +blargg_err_t Rar_Extractor::rewind_v() +{ + RETURN_ERR( convert_err( unrar_rewind( unrar ) ) ); + return skip_unextractables(); +} + +fex_pos_t Rar_Extractor::tell_arc_v() const +{ + return unrar_tell( unrar ); +} + +blargg_err_t Rar_Extractor::seek_arc_v( fex_pos_t pos ) +{ + RETURN_ERR( convert_err( unrar_seek( unrar, pos ) ) ); + return skip_unextractables(); +} + +blargg_err_t Rar_Extractor::data_v( void const** out ) +{ + return convert_err( unrar_extract_mem( unrar, out ) ); +} + +blargg_err_t Rar_Extractor::extract_v( void* out, int count ) +{ + // We can read entire file directly into user buffer + if ( count == size() ) + return convert_err( unrar_extract( unrar, out, count ) ); + + // This will call data_v() and copy from that buffer for us + return File_Extractor::extract_v( out, count ); +} + +#endif diff --git a/fex/fex/Rar_Extractor.h b/fex/fex/Rar_Extractor.h new file mode 100644 index 0000000..9a74dea --- /dev/null +++ b/fex/fex/Rar_Extractor.h @@ -0,0 +1,43 @@ +// RAR archive extractor + +// File_Extractor 1.0.0 +#ifndef RAR_EXTRACTOR_H +#define RAR_EXTRACTOR_H + +#include "File_Extractor.h" +#include "unrar/unrar.h" + +class Rar_Extractor : public File_Extractor { +public: + Rar_Extractor(); + virtual ~Rar_Extractor(); + + struct read_callback_t + { + const char* err; + int pos; + File_Reader* in; + }; + +protected: + virtual blargg_err_t open_v(); + virtual void close_v(); + + virtual blargg_err_t next_v(); + virtual blargg_err_t rewind_v(); + virtual fex_pos_t tell_arc_v() const; + virtual blargg_err_t seek_arc_v( fex_pos_t ); + + virtual blargg_err_t data_v( void const** ); + virtual blargg_err_t extract_v( void*, int ); + +private: + unrar_t* unrar; + read_callback_t reader; + + blargg_err_t convert_err( unrar_err_t ); + blargg_err_t skip_unextractables(); + blargg_err_t next_raw(); +}; + +#endif diff --git a/fex/fex/Zip7_Extractor.cpp b/fex/fex/Zip7_Extractor.cpp new file mode 100644 index 0000000..1b6071d --- /dev/null +++ b/fex/fex/Zip7_Extractor.cpp @@ -0,0 +1,354 @@ +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "Zip7_Extractor.h" + +extern "C" { +#include "7z_C/7z.h" +#include "7z_C/7zAlloc.h" +#include "7z_C/7zCrc.h" +} + +#include + +/* Copyright (C) 2005-2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +static ISzAlloc zip7_alloc = { SzAlloc, SzFree }; +static ISzAlloc zip7_alloc_temp = { SzAllocTemp, SzFreeTemp }; + +struct Zip7_Extractor_Impl : + ISeekInStream +{ + CLookToRead look; + CSzArEx db; + + // SzExtract state + UInt32 block_index; + Byte* buf; + size_t buf_size; + + File_Reader* in; + const char* in_err; +}; + +extern "C" +{ + // 7-zip callbacks pass an ISeekInStream* for data, so we must cast it + // back to ISeekInStream* FIRST, then cast to our Impl structure + + static SRes zip7_read_( void* vstream, void* out, size_t* size ) + { + assert( out && size ); + ISeekInStream* stream = STATIC_CAST(ISeekInStream*,vstream); + Zip7_Extractor_Impl* impl = STATIC_CAST(Zip7_Extractor_Impl*,stream); + + long lsize = *size; + blargg_err_t err = impl->in->read_avail( out, &lsize ); + if ( err ) + { + *size = 0; + impl->in_err = err; + return SZ_ERROR_READ; + } + + *size = lsize; + return SZ_OK; + } + + static SRes zip7_seek_( void* vstream, Int64* pos, ESzSeek mode ) + { + ISeekInStream* stream = STATIC_CAST(ISeekInStream*,vstream); + Zip7_Extractor_Impl* impl = STATIC_CAST(Zip7_Extractor_Impl*,stream); + + // assert( mode != SZ_SEEK_CUR ); // never used + + if ( mode == SZ_SEEK_END ) + { + assert( *pos == 0 ); // only used to find file length + *pos = impl->in->size(); + return SZ_OK; + } + + // assert( mode == SZ_SEEK_SET ); + blargg_err_t err = impl->in->seek( *pos ); + if ( err ) + { + // don't set in_err in this case, since it might be benign + if ( err == blargg_err_file_eof ) + return SZ_ERROR_INPUT_EOF; + + impl->in_err = err; + return SZ_ERROR_READ; + } + + return SZ_OK; + } +} + +blargg_err_t Zip7_Extractor::zip7_err( int err ) +{ + // TODO: ignore in_err in some cases? unsure about which error to use + blargg_err_t in_err = impl->in_err; + impl->in_err = NULL; + if ( in_err ) + { + check( err != SZ_OK ); + return in_err; + } + + switch ( err ) + { + case SZ_OK: return blargg_ok; + case SZ_ERROR_MEM: return blargg_err_memory; + case SZ_ERROR_READ: return blargg_err_file_io; + case SZ_ERROR_CRC: + case SZ_ERROR_DATA: + case SZ_ERROR_INPUT_EOF: + case SZ_ERROR_ARCHIVE: return blargg_err_file_corrupt; + case SZ_ERROR_UNSUPPORTED: return blargg_err_file_feature; + case SZ_ERROR_NO_ARCHIVE: return blargg_err_file_type; + } + + return blargg_err_generic; +} + +static blargg_err_t init_7z() +{ + static bool inited; + if ( !inited ) + { + inited = true; + CrcGenerateTable(); + } + return blargg_ok; +} + +static File_Extractor* new_7z() +{ + return BLARGG_NEW Zip7_Extractor; +} + +fex_type_t_ const fex_7z_type [1] = {{ + ".7z", + &new_7z, + "7-zip archive", + &init_7z +}}; + +Zip7_Extractor::Zip7_Extractor() : + File_Extractor( fex_7z_type ) +{ + impl = NULL; +} + +Zip7_Extractor::~Zip7_Extractor() +{ + close(); +} + +blargg_err_t Zip7_Extractor::open_v() +{ + RETURN_ERR( init_7z() ); + + if ( !impl ) + { + impl = (Zip7_Extractor_Impl*) malloc( sizeof *impl ); + CHECK_ALLOC( impl ); + } + + impl->in = &arc(); + impl->block_index = (UInt32) -1; + impl->buf = NULL; + impl->buf_size = 0; + + LookToRead_CreateVTable( &impl->look, false ); + impl->ISeekInStream::Read = zip7_read_; + impl->ISeekInStream::Seek = zip7_seek_; + impl->look.realStream = impl; + LookToRead_Init( &impl->look ); + + SzArEx_Init( &impl->db ); + + impl->in_err = NULL; + RETURN_ERR( zip7_err( SzArEx_Open( &impl->db, &impl->look.s, + &zip7_alloc, &zip7_alloc_temp ) ) ); + + return seek_arc_v( 0 ); +} + +void Zip7_Extractor::close_v() +{ + if ( impl ) + { + if ( impl->in ) + { + impl->in = NULL; + SzArEx_Free( &impl->db, &zip7_alloc ); + } + IAlloc_Free( &zip7_alloc, impl->buf ); + free( impl ); + impl = NULL; + } +} + +// This method was taken from ogre-7z (thanks), and is thus LGPL +bool Zip7_Extractor::utf16ToUtf8( unsigned char* dest, size_t* destLen, const short* src, size_t srcLen ) +{ + static const unsigned char sUtf8Limits[5] = { 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + + size_t destPos = 0, srcPos = 0; + for(;;) + { + unsigned int numAdds; + unsigned long value; + if( srcPos == srcLen ) + { + *destLen = destPos; + return true; + } + + value = src[srcPos++]; + if( value < 0x80 ) + { + if( dest ) + { + dest[destPos] = (char)value; + } + destPos++; + continue; + } + + if( value >= 0xD800 && value < 0xE000 ) + { + unsigned long c2; + if( value >= 0xDC00 || srcPos == srcLen ) + break; + + c2 = src[srcPos++]; + if( c2 < 0xDC00 || c2 >= 0xE000 ) + break; + + value = (((value - 0xD800) << 10) | (c2 - 0xDC00)) + 0x10000; + } + + for( numAdds = 1; numAdds < 5; numAdds++ ) + { + if( value < (((UInt32)1) << (numAdds * 5 + 6)) ) + break; + } + + if( dest ) + { + dest[destPos] = (char)(sUtf8Limits[numAdds - 1] + (value >> (6 * numAdds))); + } + + destPos++; + do + { + numAdds--; + if( dest ) + { + dest[destPos] = (char)(0x80 + ((value >> (6 * numAdds)) & 0x3F)); + } + destPos++; + } + while( numAdds != 0 ); + } + + *destLen = destPos; + return false; +} + +blargg_err_t Zip7_Extractor::next_v() +{ + while ( ++index < (int) impl->db.db.NumFiles ) + { + CSzFileItem const& item = impl->db.db.Files [index]; + if ( !item.IsDir ) + { + unsigned long date = 0; + if ( item.MTimeDefined ) + { + const UInt64 epoch = ((UInt64)0x019db1de << 32) + 0xd53e8000; + /* 0x019db1ded53e8000ULL: 1970-01-01 00:00:00 (UTC) */ + struct tm tm; + + UInt64 time = ((UInt64)item.MTime.High << 32) + item.MTime.Low - epoch; + time /= 1000000; + + time_t _time = time; + + #ifdef _WIN32 + tm = *localtime( &_time ); + #else + localtime_r( &_time, &tm ); + #endif + + date = (( tm.tm_sec >> 1 ) & 0x1F) | + (( tm.tm_min & 0x3F ) << 5 ) | + (( tm.tm_hour & 0x1F ) << 11 ) | + (( tm.tm_mday & 0x1F ) << 16 ) | + (( ( tm.tm_mon + 1 ) & 0x0F ) << 21 ) | + (( ( tm.tm_year - 80 ) & 0x7F ) << 25 ); + } + + size_t name_length = SzArEx_GetFileNameUtf16( &impl->db, index, 0 ); + size_t utf8_length = 0; + name16.resize( name_length ); + SzArEx_GetFileNameUtf16( &impl->db, index, ( UInt16 * ) name16.begin() ); + unsigned char temp[1024]; + utf16ToUtf8( temp, &utf8_length, (const short*)name16.begin(), name_length - 1 ); + temp[utf8_length] = '\0'; + + name8.resize( utf8_length + 1 ); + memcpy( name8.begin(), temp, utf8_length + 1 ); + set_name( name8.begin(), name16.begin() ); + set_info( item.Size, 0, (item.CrcDefined ? item.Crc : 0) ); + break; + } + } + + return blargg_ok; +} + +blargg_err_t Zip7_Extractor::rewind_v() +{ + return seek_arc_v( 0 ); +} + +fex_pos_t Zip7_Extractor::tell_arc_v() const +{ + return index; +} + +blargg_err_t Zip7_Extractor::seek_arc_v( fex_pos_t pos ) +{ + assert( 0 <= pos && pos <= (int) impl->db.db.NumFiles ); + + index = pos - 1; + return next_v(); +} + +blargg_err_t Zip7_Extractor::data_v( void const** out ) +{ + impl->in_err = NULL; + size_t offset = 0; + size_t count = 0; + RETURN_ERR( zip7_err( SzArEx_Extract( &impl->db, &impl->look.s, index, + &impl->block_index, &impl->buf, &impl->buf_size, + &offset, &count, &zip7_alloc, &zip7_alloc_temp ) ) ); + assert( count == (size_t) size() ); + + *out = impl->buf + offset; + return blargg_ok; +} diff --git a/fex/fex/Zip7_Extractor.h b/fex/fex/Zip7_Extractor.h new file mode 100644 index 0000000..a1d7e01 --- /dev/null +++ b/fex/fex/Zip7_Extractor.h @@ -0,0 +1,38 @@ +// 7-zip archive extractor + +// File_Extractor 1.0.0 +#ifndef ZIP7_EXTRACTOR_H +#define ZIP7_EXTRACTOR_H + +#include "File_Extractor.h" + +struct Zip7_Extractor_Impl; + +class Zip7_Extractor : public File_Extractor { +public: + Zip7_Extractor(); + virtual ~Zip7_Extractor(); + +protected: + virtual blargg_err_t open_v(); + virtual void close_v(); + + virtual blargg_err_t next_v(); + virtual blargg_err_t rewind_v(); + virtual fex_pos_t tell_arc_v() const; + virtual blargg_err_t seek_arc_v( fex_pos_t ); + + virtual blargg_err_t data_v( void const** out ); + + bool utf16ToUtf8( unsigned char* dest, size_t* destLen, const short* src, size_t srcLen ); + +private: + Zip7_Extractor_Impl* impl; + int index; + blargg_vector name8; + blargg_vector name16; + + blargg_err_t zip7_err( int err ); +}; + +#endif diff --git a/fex/fex/Zip_Extractor.cpp b/fex/fex/Zip_Extractor.cpp new file mode 100644 index 0000000..8bcc61c --- /dev/null +++ b/fex/fex/Zip_Extractor.cpp @@ -0,0 +1,390 @@ +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "Zip_Extractor.h" + +#include "blargg_endian.h" + +/* Copyright (C) 2005-2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +/* To avoid copying filename string from catalog, I terminate it by modifying +catalog data. This potentially requires moving the first byte of the type +of the next entry elsewhere; I move it to the first byte of made_by. Kind +of hacky, but I'd rather not have to allocate memory for a copy of it. */ + +#include "blargg_source.h" + +/* Reads this much from end of file when first opening. Only this much is +searched for the end catalog entry. If whole catalog is within this data, +nothing more needs to be read on open. */ +int const end_read_size = 8 * 1024; + +/* Reads are are made using file offset that's a multiple of this, +increasing performance. */ +int const disk_block_size = 4 * 1024; + +// Read buffer used for extracting file data +int const read_buf_size = 16 * 1024; + +struct header_t +{ + char type [4]; + byte vers [2]; + byte flags [2]; + byte method [2]; + byte date [4]; + byte crc [4]; + byte raw_size [4]; + byte size [4]; + byte filename_len [2]; + byte extra_len [2]; + char filename [2]; // [filename_len] + //char extra [extra_len]; +}; +int const header_size = 30; + +struct entry_t +{ + char type [4]; + byte made_by [2]; + byte vers [2]; + byte flags [2]; + byte method [2]; + byte date [4]; + byte crc [4]; + byte raw_size [4]; + byte size [4]; + byte filename_len [2]; + byte extra_len [2]; + byte comment_len [2]; + byte disk [2]; + byte int_attrib [2]; + byte ext_attrib [4]; + byte file_offset [4]; + char filename [2]; // [filename_len] + //char extra [extra_len]; + //char comment [comment_len]; +}; +int const entry_size = 46; + +struct end_entry_t +{ + char type [4]; + byte disk [2]; + byte first_disk [2]; + byte disk_entry_count [2]; + byte entry_count [2]; + byte dir_size [4]; + byte dir_offset [4]; + byte comment_len [2]; + char comment [2]; // [comment_len] +}; +int const end_entry_size = 22; + +static blargg_err_t init_zip() +{ + get_crc_table(); // initialize zlib's CRC-32 tables + return blargg_ok; +} + +static File_Extractor* new_zip() +{ + return BLARGG_NEW Zip_Extractor; +} + +fex_type_t_ const fex_zip_type [1] = {{ + ".zip", + &new_zip, + "ZIP archive", + &init_zip +}}; + +Zip_Extractor::Zip_Extractor() : + File_Extractor( fex_zip_type ) +{ + Zip_Extractor::clear_file_v(); + + // If these fail, structures had extra padding inserted by compiler + assert( offsetof (header_t,filename) == header_size ); + assert( offsetof (entry_t,filename) == entry_size ); + assert( offsetof (end_entry_t,comment) == end_entry_size ); +} + +Zip_Extractor::~Zip_Extractor() +{ + close(); +} + +blargg_err_t Zip_Extractor::open_path_v() +{ + RETURN_ERR( open_arc_file( true ) ); + return File_Extractor::open_path_v(); +} + +inline +void Zip_Extractor::reorder_entry_header( int offset ) +{ + catalog [offset + 0] = 0; + catalog [offset + 4] = 'P'; +} + +blargg_err_t Zip_Extractor::open_v() +{ + if ( arc().size() < end_entry_size ) + return blargg_err_file_type; + + // Read final end_read_size bytes of file + int file_pos = max( 0, arc().size() - end_read_size ); + file_pos -= file_pos % disk_block_size; + RETURN_ERR( catalog.resize( arc().size() - file_pos ) ); + RETURN_ERR( arc().seek( file_pos ) ); + RETURN_ERR( arc().read( catalog.begin(), catalog.size() ) ); + + // Find end-of-catalog entry + int end_pos = catalog.size() - end_entry_size; + while ( end_pos >= 0 && memcmp( &catalog [end_pos], "PK\5\6", 4 ) ) + end_pos--; + if ( end_pos < 0 ) + return blargg_err_file_type; + end_entry_t const& end_entry = (end_entry_t&) catalog [end_pos]; + end_pos += file_pos; + + // some idiotic zip compressors add data to end of zip without setting comment len +// check( arc().size() == end_pos + end_entry_size + get_le16( end_entry.comment_len ) ); + + // Find file offset of beginning of catalog + catalog_begin = get_le32( end_entry.dir_offset ); + int catalog_size = end_pos - catalog_begin; + if ( catalog_size < 0 ) + return blargg_err_file_corrupt; + catalog_size += end_entry_size; + + // See if catalog is entirely contained in bytes already read + int begin_offset = catalog_begin - file_pos; + if ( begin_offset >= 0 ) + memmove( catalog.begin(), &catalog [begin_offset], catalog_size ); + + RETURN_ERR( catalog.resize( catalog_size ) ); + if ( begin_offset < 0 ) + { + // Catalog begins before bytes read, so it needs to be read + RETURN_ERR( arc().seek( catalog_begin ) ); + RETURN_ERR( arc().read( catalog.begin(), catalog.size() ) ); + } + + // First entry in catalog should be a file or end of archive + if ( memcmp( catalog.begin(), "PK\1\2", 4 ) && memcmp( catalog.begin(), "PK\5\6", 4 ) ) + return blargg_err_file_type; + + reorder_entry_header( 0 ); + return rewind_v(); +} + +void Zip_Extractor::close_v() +{ + catalog.clear(); +} + +// Scanning + +inline +static bool is_normal_file( entry_t const& e, unsigned len ) +{ + int last_char = (len ? e.filename [len - 1] : '/'); + bool is_dir = (last_char == '/' || last_char == '\\'); + if ( is_dir && get_le32( e.size ) == 0 ) + return false; + check( !is_dir ); + + // Mac OS X puts meta-information in separate files with normal extensions, + // so they must be filtered out or caller will mistake them for normal files. + if ( e.made_by[1] == 3 ) + { + const char* dir = strrchr( e.filename, '/' ); + if ( dir ) + dir++; + else + dir = e.filename; + + if ( *dir == '.' ) + return false; + + if ( !strcmp( dir, "Icon\x0D" ) ) + return false; + } + + return true; +} + +blargg_err_t Zip_Extractor::update_info( bool advance_first ) +{ + while ( 1 ) + { + entry_t& e = (entry_t&) catalog [catalog_pos]; + + if ( memcmp( e.type, "\0K\1\2P", 5 ) && memcmp( e.type, "PK\1\2", 4 ) ) + { + check( !memcmp( e.type, "\0K\5\6P", 5 ) ); + break; + } + + unsigned len = get_le16( e.filename_len ); + int next_offset = catalog_pos + entry_size + len + get_le16( e.extra_len ) + + get_le16( e.comment_len ); + if ( (unsigned) next_offset > catalog.size() - end_entry_size ) + return blargg_err_file_corrupt; + + if ( catalog [next_offset] == 'P' ) + reorder_entry_header( next_offset ); + + if ( !advance_first ) + { + e.filename [len] = 0; // terminate name + + if ( is_normal_file( e, len ) ) + { + set_name( e.filename ); + set_info( get_le32( e.size ), get_le32( e.date ), get_le32( e.crc ) ); + break; + } + } + + catalog_pos = next_offset; + advance_first = false; + } + + return blargg_ok; +} + +blargg_err_t Zip_Extractor::next_v() +{ + return update_info( true ); +} + +blargg_err_t Zip_Extractor::rewind_v() +{ + return seek_arc_v( 0 ); +} + +fex_pos_t Zip_Extractor::tell_arc_v() const +{ + return catalog_pos; +} + +blargg_err_t Zip_Extractor::seek_arc_v( fex_pos_t pos ) +{ + assert( 0 <= pos && (size_t) pos <= catalog.size() - end_entry_size ); + + catalog_pos = pos; + return update_info( false ); +} + +// Reading + +void Zip_Extractor::clear_file_v() +{ + buf.end(); +} + +blargg_err_t Zip_Extractor::inflater_read( void* data, void* out, int* count ) +{ + Zip_Extractor& self = *STATIC_CAST(Zip_Extractor*,data); + + if ( *count > self.raw_remain ) + *count = self.raw_remain; + + self.raw_remain -= *count; + + return self.arc().read( out, *count ); +} + +blargg_err_t Zip_Extractor::fill_buf( int offset, int buf_size, int initial_read ) +{ + raw_remain = arc().size() - offset; + RETURN_ERR( arc().seek( offset ) ); + return buf.begin( inflater_read, this, buf_size, initial_read ); +} + +blargg_err_t Zip_Extractor::first_read( int count ) +{ + entry_t const& e = (entry_t&) catalog [catalog_pos]; + + // Determine compression + { + int method = get_le16( e.method ); + if ( (method && method != Z_DEFLATED) || get_le16( e.vers ) > 20 ) + return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "compression method" ); + file_deflated = (method != 0); + } + + int raw_size = get_le32( e.raw_size ); + + int file_offset = get_le32( e.file_offset ); + int align = file_offset % disk_block_size; + { + // read header + int buf_size = 3 * disk_block_size - 1 + raw_size; // space for all raw data + buf_size -= buf_size % disk_block_size; + int initial_read = buf_size; + if ( !file_deflated || count < size() ) + { + buf_size = read_buf_size; + initial_read = disk_block_size * 2; + } + // TODO: avoid re-reading if buffer already has data we want? + RETURN_ERR( fill_buf( file_offset - align, buf_size, initial_read ) ); + } + header_t const& h = (header_t&) buf.data() [align]; + if ( buf.filled() < align + header_size || memcmp( h.type, "PK\3\4", 4 ) ) + return blargg_err_file_corrupt; + + // CRCs of header and file data + correct_crc = get_le32( h.crc ); + if ( !correct_crc ) + correct_crc = get_le32( e.crc ); + check( correct_crc == get_le32( e.crc ) ); // catalog CRC should match + crc = ::crc32( 0, NULL, 0 ); + + // Data offset + int data_offset = file_offset + header_size + + get_le16( h.filename_len ) + get_le16( h.extra_len ); + if ( data_offset + raw_size > catalog_begin ) + return blargg_err_file_corrupt; + + // Refill buffer if there's lots of extra data after header + int buf_offset = data_offset - file_offset + align; + if ( buf_offset > buf.filled() ) + { + // TODO: this will almost never occur, making it a good place for bugs + buf_offset = data_offset % disk_block_size; + RETURN_ERR( fill_buf( data_offset - buf_offset, read_buf_size, disk_block_size ) ); + } + + raw_remain = raw_size - (buf.filled() - buf_offset); + return buf.set_mode( (file_deflated ? buf.mode_raw_deflate : buf.mode_copy), buf_offset ); +} + +blargg_err_t Zip_Extractor::extract_v( void* out, int count ) +{ + if ( tell() == 0 ) + RETURN_ERR( first_read( count ) ); + + int actual = count; + RETURN_ERR( buf.read( out, &actual ) ); + if ( actual < count ) + return blargg_err_file_corrupt; + + crc = ::crc32( crc, (byte const*) out, count ); + if ( count == reader().remain() && crc != correct_crc ) + return blargg_err_file_corrupt; + + return blargg_ok; +} diff --git a/fex/fex/Zip_Extractor.h b/fex/fex/Zip_Extractor.h new file mode 100644 index 0000000..9742df9 --- /dev/null +++ b/fex/fex/Zip_Extractor.h @@ -0,0 +1,45 @@ +// ZIP archive extractor. Only supports deflation and store (no compression). + +// File_Extractor 1.0.0 +#ifndef ZIP_EXTRACTOR_H +#define ZIP_EXTRACTOR_H + +#include "File_Extractor.h" +#include "Zlib_Inflater.h" + +class Zip_Extractor : public File_Extractor { +public: + Zip_Extractor(); + virtual ~Zip_Extractor(); + +protected: + virtual blargg_err_t open_path_v(); + virtual blargg_err_t open_v(); + virtual void close_v(); + + virtual void clear_file_v(); + virtual blargg_err_t next_v(); + virtual blargg_err_t rewind_v(); + virtual fex_pos_t tell_arc_v() const; + virtual blargg_err_t seek_arc_v( fex_pos_t ); + + virtual blargg_err_t extract_v( void*, int ); + +private: + blargg_vector catalog; + int catalog_begin; // offset of first catalog entry in file (to detect corruption) + int catalog_pos; // position of current entry in catalog + int raw_remain; // bytes remaining to be read from zip file for current file + unsigned crc; // ongoing CRC of extracted bytes + unsigned correct_crc; + bool file_deflated; + Zlib_Inflater buf; + + blargg_err_t fill_buf( int offset, int buf_size, int initial_read ); + blargg_err_t update_info( bool advance_first ); + blargg_err_t first_read( int count ); + void reorder_entry_header( int offset ); + static blargg_err_t inflater_read( void* data, void* out, int* count ); +}; + +#endif diff --git a/fex/fex/Zlib_Inflater.cpp b/fex/fex/Zlib_Inflater.cpp new file mode 100644 index 0000000..8d31b51 --- /dev/null +++ b/fex/fex/Zlib_Inflater.cpp @@ -0,0 +1,257 @@ +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "Zlib_Inflater.h" + +/* Copyright (C) 2006-2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +int const block_size = 4096; + +static const char* get_zlib_err( int code ) +{ + assert( code != Z_OK ); + switch ( code ) + { + case Z_MEM_ERROR: return blargg_err_memory; + case Z_DATA_ERROR: return blargg_err_file_corrupt; + // TODO: handle more error codes + } + + const char* str = zError( code ); + if ( !str ) + str = BLARGG_ERR( BLARGG_ERR_GENERIC, "problem unzipping data" ); + + return str; +} + +void Zlib_Inflater::end() +{ + if ( deflated_ ) + { + deflated_ = false; + if ( inflateEnd( &zbuf ) ) + check( false ); + } + buf.clear(); + + static z_stream const empty = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + memcpy( &zbuf, &empty, sizeof zbuf ); +} + +Zlib_Inflater::Zlib_Inflater() +{ + deflated_ = false; + end(); // initialize things +} + +Zlib_Inflater::~Zlib_Inflater() +{ + end(); +} + +blargg_err_t Zlib_Inflater::fill_buf( int count ) +{ + byte* out = buf.end() - count; + RETURN_ERR( callback( user_data, out, &count ) ); + zbuf.avail_in = count; + zbuf.next_in = out; + return blargg_ok; +} + +blargg_err_t Zlib_Inflater::begin( callback_t new_callback, void* new_user_data, + int new_buf_size, int initial_read ) +{ + callback = new_callback; + user_data = new_user_data; + + end(); + + // TODO: decide whether using different size on alloc failure is a good idea + //RETURN_ERR( buf.resize( new_buf_size ? new_buf_size : 4 * block_size ) ); + if ( new_buf_size && buf.resize( new_buf_size ) ) + { + ACK_FAILURE(); + new_buf_size = 0; + } + + if ( !new_buf_size ) + { + RETURN_ERR( buf.resize( 4 * block_size ) ); + initial_read = 0; + } + + // Fill buffer with some data, less than normal buffer size since caller might + // just be examining beginning of file. + return fill_buf( initial_read ? initial_read : block_size ); +} + +blargg_err_t Zlib_Inflater::set_mode( mode_t mode, int data_offset ) +{ + zbuf.next_in += data_offset; + zbuf.avail_in -= data_offset; + + if ( mode == mode_auto ) + { + // examine buffer for gzip header + mode = mode_copy; + unsigned const min_gzip_size = 2 + 8 + 8; + if ( zbuf.avail_in >= min_gzip_size && + zbuf.next_in [0] == 0x1F && zbuf.next_in [1] == 0x8B ) + mode = mode_ungz; + } + + if ( mode != mode_copy ) + { + int wb = MAX_WBITS + 16; // have zlib handle gzip header + if ( mode == mode_raw_deflate ) + wb = -MAX_WBITS; + + int zerr = inflateInit2( &zbuf, wb ); + if ( zerr ) + { + zbuf.next_in = NULL; + return get_zlib_err( zerr ); + } + + deflated_ = true; + } + return blargg_ok; +} + +/* +// Reads/inflates entire stream. All input must be in buffer, and count must be total +// of all output. +blargg_err_t read_all( void* out, int count ); + + +// zlib automatically applies this optimization (uses inflateFast) +// TODO: remove +blargg_err_t Zlib_Inflater::read_all( void* out, int count ) +{ + if ( deflated_ ) + { + zbuf.next_out = (Bytef*) out; + zbuf.avail_out = count; + + int err = inflate( &zbuf, Z_FINISH ); + + if ( zbuf.avail_out || err != Z_STREAM_END ) + return blargg_err_file_corrupt; + } + else + { + if ( zbuf.avail_in < count ) + return blargg_err_file_corrupt; + + memcpy( out, zbuf.next_in, count ); + + zbuf.next_in += count; + zbuf.avail_in -= count; + } + + return blargg_ok; +} +*/ + +blargg_err_t Zlib_Inflater::read( void* out, int* count_io ) +{ + int remain = *count_io; + if ( remain && zbuf.next_in ) + { + if ( deflated_ ) + { + zbuf.next_out = (Bytef*) out; + zbuf.avail_out = remain; + + while ( 1 ) + { + uInt old_avail_in = zbuf.avail_in; + int err = inflate( &zbuf, Z_NO_FLUSH ); + if ( err == Z_STREAM_END ) + { + remain = zbuf.avail_out; + end(); + break; // no more data to inflate + } + + if ( err && (err != Z_BUF_ERROR || old_avail_in) ) + return get_zlib_err( err ); + + if ( !zbuf.avail_out ) + { + remain = 0; + break; // requested number of bytes inflated + } + + if ( zbuf.avail_in ) + { + // inflate() should never leave input if there's still space for output + check( false ); + return blargg_err_file_corrupt; + } + + RETURN_ERR( fill_buf( buf.size() ) ); + if ( !zbuf.avail_in ) + return blargg_err_file_corrupt; // stream didn't end but there's no more data + } + } + else + { + while ( 1 ) + { + // copy buffered data + if ( zbuf.avail_in ) + { + long count = zbuf.avail_in; + if ( count > remain ) + count = remain; + memcpy( out, zbuf.next_in, count ); + zbuf.total_out += count; + out = (char*) out + count; + remain -= count; + zbuf.next_in += count; + zbuf.avail_in -= count; + } + + if ( !zbuf.avail_in && zbuf.next_in < buf.end() ) + { + end(); + break; + } + + // read large request directly + if ( remain + zbuf.total_out % block_size >= buf.size() ) + { + int count = remain; + RETURN_ERR( callback( user_data, out, &count ) ); + zbuf.total_out += count; + out = (char*) out + count; + remain -= count; + + if ( remain ) + { + end(); + break; + } + } + + if ( !remain ) + break; + + RETURN_ERR( fill_buf( buf.size() - zbuf.total_out % block_size ) ); + } + } + } + *count_io -= remain; + return blargg_ok; +} diff --git a/fex/fex/Zlib_Inflater.h b/fex/fex/Zlib_Inflater.h new file mode 100644 index 0000000..a585b08 --- /dev/null +++ b/fex/fex/Zlib_Inflater.h @@ -0,0 +1,70 @@ +// Simplifies use of zlib for inflating data + +// File_Extractor 1.0.0 +#ifndef ZLIB_INFLATER_H +#define ZLIB_INFLATER_H + +#include "blargg_common.h" +#include "Data_Reader.h" +#include "zlib.h" + +class Zlib_Inflater { +public: + + // Reads at most min(*count,bytes_until_eof()) bytes into *out and set *count + // to that number, or returns error if that many can't be read. + typedef blargg_err_t (*callback_t)( void* user_data, void* out, int* count ); + + // Begins by setting callback and filling buffer. Default buffer is 16K and + // filled to 4K, or specify buf_size and initial_read for custom buffer size + // and how much to read initially. + blargg_err_t begin( callback_t, void* user_data, + int buf_size = 0, int initial_read = 0 ); + + // Data read into buffer by begin() + const unsigned char* data() const { return zbuf.next_in; } + int filled() const { return zbuf.avail_in; } + + // Begins inflation using specified mode. Using mode_auto selects between + // mode_copy and mode_ungz by examining first two bytes of buffer. Use + // buf_offset to specify where data begins in buffer, in case there is + // header data that should be skipped. + enum mode_t { mode_copy, mode_ungz, mode_raw_deflate, mode_auto }; + blargg_err_t set_mode( mode_t, int buf_offset = 0 ); + + // True if set_mode() has been called with mode_ungz or mode_raw_deflate + bool deflated() const { return deflated_; } + + // Reads/inflates at most *count_io bytes into *out and sets *count_io to actual + // number of bytes read (less than requested if end of data was reached). + // Buffers source data internally, even in copy mode, so input file can be + // unbuffered without sacrificing performance. + blargg_err_t read( void* out, int* count_io ); + + // Total number of bytes read since begin() + int tell() const { return zbuf.total_out; } + + // Ends inflation and frees memory + void end(); + +private: + // noncopyable + Zlib_Inflater( const Zlib_Inflater& ); + Zlib_Inflater& operator = ( const Zlib_Inflater& ); + +// Implementation +public: + Zlib_Inflater(); + ~Zlib_Inflater(); + +private: + z_stream_s zbuf; + blargg_vector buf; + bool deflated_; + callback_t callback; + void* user_data; + + blargg_err_t fill_buf( int count ); +}; + +#endif diff --git a/fex/fex/blargg_common.cpp b/fex/fex/blargg_common.cpp new file mode 100644 index 0000000..9f3e9eb --- /dev/null +++ b/fex/fex/blargg_common.cpp @@ -0,0 +1,51 @@ +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "blargg_common.h" + +/* Copyright (C) 2008-2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +void blargg_vector_::init() +{ + begin_ = NULL; + size_ = 0; +} + +void blargg_vector_::clear() +{ + void* p = begin_; + begin_ = NULL; + size_ = 0; + free( p ); +} + +blargg_err_t blargg_vector_::resize_( size_t n, size_t elem_size ) +{ + if ( n != size_ ) + { + if ( n == 0 ) + { + // Simpler to handle explicitly. Realloc will handle a size of 0, + // but then we have to avoid raising an error for a NULL return. + clear(); + } + else + { + void* p = realloc( begin_, n * elem_size ); + CHECK_ALLOC( p ); + begin_ = p; + size_ = n; + } + } + return blargg_ok; +} diff --git a/fex/fex/blargg_common.h b/fex/fex/blargg_common.h new file mode 100644 index 0000000..8c22fef --- /dev/null +++ b/fex/fex/blargg_common.h @@ -0,0 +1,201 @@ +// Sets up common environment for Shay Green's libraries. +// To change configuration options, modify blargg_config.h, not this file. + +// File_Extractor 1.0.0 +#ifndef BLARGG_COMMON_H +#define BLARGG_COMMON_H + +#include +#include +#include + +typedef const char* blargg_err_t; // 0 on success, otherwise error string + +// Success; no error +int const blargg_ok = 0; + +// BLARGG_RESTRICT: equivalent to C99's restrict, where supported +#if __GNUC__ >= 3 || _MSC_VER >= 1100 + #define BLARGG_RESTRICT __restrict +#else + #define BLARGG_RESTRICT +#endif + +#if __cplusplus >= 199711 + #define BLARGG_MUTABLE mutable +#else + #define BLARGG_MUTABLE +#endif + +/* BLARGG_4CHAR('a','b','c','d') = 'abcd' (four character integer constant). +I don't just use 'abcd' because that's implementation-dependent. */ +#define BLARGG_4CHAR( a, b, c, d ) \ + ((a&0xFF)*0x1000000 + (b&0xFF)*0x10000 + (c&0xFF)*0x100 + (d&0xFF)) + +/* BLARGG_STATIC_ASSERT( expr ): Generates compile error if expr is 0. +Can be used at file, function, or class scope. */ +#ifdef _MSC_VER + // MSVC6 (_MSC_VER < 1300) __LINE__ fails when /Zl is specified + #define BLARGG_STATIC_ASSERT( expr ) \ + void blargg_failed_( int (*arg) [2 / (int) !!(expr) - 1] ) +#else + // Others fail when declaring same function multiple times in class, + // so differentiate them by line + #define BLARGG_STATIC_ASSERT( expr ) \ + void blargg_failed_( int (*arg) [2 / !!(expr) - 1] [__LINE__] ) +#endif + +/* Pure virtual functions cause a vtable entry to a "called pure virtual" +error handler, requiring linkage to the C++ runtime library. This macro is +used in place of the "= 0", and simply expands to its argument. During +development, it expands to "= 0", allowing detection of missing overrides. */ +#define BLARGG_PURE( def ) def + +/* My code depends on ASCII anywhere a character or string constant is +compared with data read from a file, and anywhere file data is read and +treated as a string. */ +#if '\n'!=0x0A || ' '!=0x20 || '0'!=0x30 || 'A'!=0x41 || 'a'!=0x61 + #error "ASCII character set required" +#endif + +/* My code depends on int being at least 32 bits. Almost everything these days +uses at least 32-bit ints, so it's hard to even find a system with 16-bit ints +to test with. The issue can't be gotten around by using a suitable blargg_int +everywhere either, because int is often converted to implicitly when doing +arithmetic on smaller types. */ +#if UINT_MAX < 0xFFFFFFFF + #error "int must be at least 32 bits" +#endif + +// In case compiler doesn't support these properly. Used rarely. +#define STATIC_CAST(T,expr) static_cast (expr) +#define CONST_CAST( T,expr) const_cast (expr) + +// User configuration can override the above macros if necessary +#include "blargg_config.h" + +/* BLARGG_DEPRECATED [_TEXT] for any declarations/text to be removed in a +future version. In GCC, we can let the compiler warn. In other compilers, +we strip it out unless BLARGG_LEGACY is true. */ +#if BLARGG_LEGACY + // Allow old client code to work without warnings + #define BLARGG_DEPRECATED_TEXT( text ) text + #define BLARGG_DEPRECATED( text ) text +#elif __GNUC__ >= 4 + // In GCC, we can mark declarations and let the compiler warn + #define BLARGG_DEPRECATED_TEXT( text ) text + #define BLARGG_DEPRECATED( text ) __attribute__ ((deprecated)) text +#else + // By default, deprecated items are removed, to avoid use in new code + #define BLARGG_DEPRECATED_TEXT( text ) + #define BLARGG_DEPRECATED( text ) +#endif + +/* BOOST::int8_t, BOOST::int32_t, etc. +I used BOOST since I originally was going to allow use of the boost library +for prividing the definitions. If I'm defining them, they must be scoped or +else they could conflict with the standard ones at global scope. Even if +HAVE_STDINT_H isn't defined, I can't assume the typedefs won't exist at +global scope already. */ +#if defined (HAVE_STDINT_H) || \ + UCHAR_MAX != 0xFF || USHRT_MAX != 0xFFFF || UINT_MAX != 0xFFFFFFFF + #include + #define BOOST +#else + struct BOOST + { + typedef signed char int8_t; + typedef unsigned char uint8_t; + typedef short int16_t; + typedef unsigned short uint16_t; + typedef int int32_t; + typedef unsigned int uint32_t; + }; +#endif + +/* My code is not written with exceptions in mind, so either uses new (nothrow) +OR overrides operator new in my classes. The former is best since clients +creating objects will get standard exceptions on failure, but that causes it +to require the standard C++ library. So, when the client is using the C +interface, I override operator new to use malloc. */ + +// BLARGG_DISABLE_NOTHROW is put inside classes +#ifndef BLARGG_DISABLE_NOTHROW + // throw spec mandatory in ISO C++ if NULL can be returned + #if __cplusplus >= 199711 || __GNUC__ >= 3 || _MSC_VER >= 1300 + #define BLARGG_THROWS_NOTHING throw () + #else + #define BLARGG_THROWS_NOTHING + #endif + + #define BLARGG_DISABLE_NOTHROW \ + void* operator new ( size_t s ) BLARGG_THROWS_NOTHING { return malloc( s ); }\ + void operator delete( void* p ) BLARGG_THROWS_NOTHING { free( p ); } + + #define BLARGG_NEW new +#else + // BLARGG_NEW is used in place of new in library code + #include + #define BLARGG_NEW new (std::nothrow) +#endif + + class blargg_vector_ { + protected: + void* begin_; + size_t size_; + void init(); + blargg_err_t resize_( size_t n, size_t elem_size ); + public: + size_t size() const { return size_; } + void clear(); + }; + +// Very lightweight vector for POD types (no constructor/destructor) +template +class blargg_vector : public blargg_vector_ { + union T_must_be_pod { T t; }; // fails if T is not POD +public: + blargg_vector() { init(); } + ~blargg_vector() { clear(); } + + blargg_err_t resize( size_t n ) { return resize_( n, sizeof (T) ); } + + T* begin() { return static_cast (begin_); } + const T* begin() const { return static_cast (begin_); } + + T* end() { return static_cast (begin_) + size_; } + const T* end() const { return static_cast (begin_) + size_; } + + T& operator [] ( size_t n ) + { + assert( n < size_ ); + return static_cast (begin_) [n]; + } + + const T& operator [] ( size_t n ) const + { + assert( n < size_ ); + return static_cast (begin_) [n]; + } +}; + +// Callback function with user data. +// blargg_callback set_callback; // for user, this acts like... +// void set_callback( T func, void* user_data = NULL ); // ...this +// To call function, do set_callback.f( .. set_callback.data ... ); +template +struct blargg_callback +{ + T f; + void* data; + blargg_callback() { f = NULL; } + void operator () ( T callback, void* user_data = NULL ) { f = callback; data = user_data; } +}; + +BLARGG_DEPRECATED( typedef signed int blargg_long; ) +BLARGG_DEPRECATED( typedef unsigned int blargg_ulong; ) +#if BLARGG_LEGACY + #define BOOST_STATIC_ASSERT BLARGG_STATIC_ASSERT +#endif + +#endif diff --git a/fex/fex/blargg_config.h b/fex/fex/blargg_config.h new file mode 100644 index 0000000..2282f1d --- /dev/null +++ b/fex/fex/blargg_config.h @@ -0,0 +1,31 @@ +// Library configuration. Modify this file as necessary. + +// File_Extractor 1.0.0 +#ifndef BLARGG_CONFIG_H +#define BLARGG_CONFIG_H + +// Uncomment a #define line below to have effect described. + +// Enable RAR archive support. Doing so adds extra licensing restrictions +// to this library (see unrar/readme.txt for more information). +//#define FEX_ENABLE_RAR 1 + +// Accept file paths encoded as UTF-8. Currently only affects Windows, +// as Unix/Linux/Mac OS X already use UTF-8 paths. +//#define BLARGG_UTF8_PATHS 1 + +// Enable support for as building DLL on Windows. +//#define BLARGG_BUILD_DLL 1 + +// Support only the listed archive types. Remove any you don't need. +#define FEX_TYPE_LIST \ + fex_7z_type,\ + fex_gz_type,\ + fex_zip_type, + +// Use standard config.h if present +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#endif diff --git a/fex/fex/blargg_endian.h b/fex/fex/blargg_endian.h new file mode 100644 index 0000000..c32c12f --- /dev/null +++ b/fex/fex/blargg_endian.h @@ -0,0 +1,185 @@ +// CPU Byte Order Utilities + +// File_Extractor 1.0.0 +#ifndef BLARGG_ENDIAN_H +#define BLARGG_ENDIAN_H + +#include "blargg_common.h" + +// BLARGG_CPU_CISC: Defined if CPU has very few general-purpose registers (< 16) +#if defined (__i386__) || defined (__x86_64__) || defined (_M_IX86) || defined (_M_X64) + #define BLARGG_CPU_X86 1 + #define BLARGG_CPU_CISC 1 +#endif + +#if defined (__powerpc__) || defined (__ppc__) || defined (__ppc64__) || \ + defined (__POWERPC__) || defined (__powerc) + #define BLARGG_CPU_POWERPC 1 + #define BLARGG_CPU_RISC 1 +#endif + +// BLARGG_BIG_ENDIAN, BLARGG_LITTLE_ENDIAN: Determined automatically, otherwise only +// one may be #defined to 1. Only needed if something actually depends on byte order. +#if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN) +#ifdef __GLIBC__ + // GCC handles this for us + #include + #if __BYTE_ORDER == __LITTLE_ENDIAN + #define BLARGG_LITTLE_ENDIAN 1 + #elif __BYTE_ORDER == __BIG_ENDIAN + #define BLARGG_BIG_ENDIAN 1 + #endif +#else + +#if defined (LSB_FIRST) || defined (__LITTLE_ENDIAN__) || BLARGG_CPU_X86 || \ + (defined (LITTLE_ENDIAN) && LITTLE_ENDIAN+0 != 1234) + #define BLARGG_LITTLE_ENDIAN 1 +#endif + +#if defined (MSB_FIRST) || defined (__BIG_ENDIAN__) || defined (WORDS_BIGENDIAN) || \ + defined (__sparc__) || BLARGG_CPU_POWERPC || \ + (defined (BIG_ENDIAN) && BIG_ENDIAN+0 != 4321) + #define BLARGG_BIG_ENDIAN 1 +#elif !defined (__mips__) + // No endian specified; assume little-endian, since it's most common + #define BLARGG_LITTLE_ENDIAN 1 +#endif +#endif +#endif + +#if BLARGG_LITTLE_ENDIAN && BLARGG_BIG_ENDIAN + #undef BLARGG_LITTLE_ENDIAN + #undef BLARGG_BIG_ENDIAN +#endif + +inline void blargg_verify_byte_order() +{ + #ifndef NDEBUG + #if BLARGG_BIG_ENDIAN + volatile int i = 1; + assert( *(volatile char*) &i == 0 ); + #elif BLARGG_LITTLE_ENDIAN + volatile int i = 1; + assert( *(volatile char*) &i != 0 ); + #endif + #endif +} + +inline unsigned get_le16( void const* p ) +{ + return (unsigned) ((unsigned char const*) p) [1] << 8 | + (unsigned) ((unsigned char const*) p) [0]; +} + +inline unsigned get_be16( void const* p ) +{ + return (unsigned) ((unsigned char const*) p) [0] << 8 | + (unsigned) ((unsigned char const*) p) [1]; +} + +inline unsigned get_le32( void const* p ) +{ + return (unsigned) ((unsigned char const*) p) [3] << 24 | + (unsigned) ((unsigned char const*) p) [2] << 16 | + (unsigned) ((unsigned char const*) p) [1] << 8 | + (unsigned) ((unsigned char const*) p) [0]; +} + +inline unsigned get_be32( void const* p ) +{ + return (unsigned) ((unsigned char const*) p) [0] << 24 | + (unsigned) ((unsigned char const*) p) [1] << 16 | + (unsigned) ((unsigned char const*) p) [2] << 8 | + (unsigned) ((unsigned char const*) p) [3]; +} + +inline void set_le16( void* p, unsigned n ) +{ + ((unsigned char*) p) [1] = (unsigned char) (n >> 8); + ((unsigned char*) p) [0] = (unsigned char) n; +} + +inline void set_be16( void* p, unsigned n ) +{ + ((unsigned char*) p) [0] = (unsigned char) (n >> 8); + ((unsigned char*) p) [1] = (unsigned char) n; +} + +inline void set_le32( void* p, unsigned n ) +{ + ((unsigned char*) p) [0] = (unsigned char) n; + ((unsigned char*) p) [1] = (unsigned char) (n >> 8); + ((unsigned char*) p) [2] = (unsigned char) (n >> 16); + ((unsigned char*) p) [3] = (unsigned char) (n >> 24); +} + +inline void set_be32( void* p, unsigned n ) +{ + ((unsigned char*) p) [3] = (unsigned char) n; + ((unsigned char*) p) [2] = (unsigned char) (n >> 8); + ((unsigned char*) p) [1] = (unsigned char) (n >> 16); + ((unsigned char*) p) [0] = (unsigned char) (n >> 24); +} + +#if BLARGG_NONPORTABLE + // Optimized implementation if byte order is known + #if BLARGG_LITTLE_ENDIAN + #define GET_LE16( addr ) (*(BOOST::uint16_t const*) (addr)) + #define GET_LE32( addr ) (*(BOOST::uint32_t const*) (addr)) + #define SET_LE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) + #define SET_LE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) + #elif BLARGG_BIG_ENDIAN + #define GET_BE16( addr ) (*(BOOST::uint16_t const*) (addr)) + #define GET_BE32( addr ) (*(BOOST::uint32_t const*) (addr)) + #define SET_BE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) + #define SET_BE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) + + #if BLARGG_CPU_POWERPC + // PowerPC has special byte-reversed instructions + #if defined (__MWERKS__) + #define GET_LE16( addr ) (__lhbrx( addr, 0 )) + #define GET_LE32( addr ) (__lwbrx( addr, 0 )) + #define SET_LE16( addr, in ) (__sthbrx( in, addr, 0 )) + #define SET_LE32( addr, in ) (__stwbrx( in, addr, 0 )) + #elif defined (__GNUC__) + #define GET_LE16( addr ) ({unsigned short ppc_lhbrx_; __asm__ volatile( "lhbrx %0,0,%1" : "=r" (ppc_lhbrx_) : "r" (addr) : "memory" ); ppc_lhbrx_;}) + #define GET_LE32( addr ) ({unsigned short ppc_lwbrx_; __asm__ volatile( "lwbrx %0,0,%1" : "=r" (ppc_lwbrx_) : "r" (addr) : "memory" ); ppc_lwbrx_;}) + #define SET_LE16( addr, in ) ({__asm__ volatile( "sthbrx %0,0,%1" : : "r" (in), "r" (addr) : "memory" );}) + #define SET_LE32( addr, in ) ({__asm__ volatile( "stwbrx %0,0,%1" : : "r" (in), "r" (addr) : "memory" );}) + #endif + #endif + #endif +#endif + +#ifndef GET_LE16 + #define GET_LE16( addr ) get_le16( addr ) + #define SET_LE16( addr, data ) set_le16( addr, data ) +#endif + +#ifndef GET_LE32 + #define GET_LE32( addr ) get_le32( addr ) + #define SET_LE32( addr, data ) set_le32( addr, data ) +#endif + +#ifndef GET_BE16 + #define GET_BE16( addr ) get_be16( addr ) + #define SET_BE16( addr, data ) set_be16( addr, data ) +#endif + +#ifndef GET_BE32 + #define GET_BE32( addr ) get_be32( addr ) + #define SET_BE32( addr, data ) set_be32( addr, data ) +#endif + +// auto-selecting versions + +inline void set_le( BOOST::uint16_t* p, unsigned n ) { SET_LE16( p, n ); } +inline void set_le( BOOST::uint32_t* p, unsigned n ) { SET_LE32( p, n ); } +inline void set_be( BOOST::uint16_t* p, unsigned n ) { SET_BE16( p, n ); } +inline void set_be( BOOST::uint32_t* p, unsigned n ) { SET_BE32( p, n ); } +inline unsigned get_le( BOOST::uint16_t const* p ) { return GET_LE16( p ); } +inline unsigned get_le( BOOST::uint32_t const* p ) { return GET_LE32( p ); } +inline unsigned get_be( BOOST::uint16_t const* p ) { return GET_BE16( p ); } +inline unsigned get_be( BOOST::uint32_t const* p ) { return GET_BE32( p ); } + +#endif diff --git a/fex/fex/blargg_errors.cpp b/fex/fex/blargg_errors.cpp new file mode 100644 index 0000000..14076cd --- /dev/null +++ b/fex/fex/blargg_errors.cpp @@ -0,0 +1,113 @@ +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "blargg_errors.h" + +/* Copyright (C) 2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +blargg_err_def_t blargg_err_generic = BLARGG_ERR_GENERIC; +blargg_err_def_t blargg_err_memory = BLARGG_ERR_MEMORY; +blargg_err_def_t blargg_err_caller = BLARGG_ERR_CALLER; +blargg_err_def_t blargg_err_internal = BLARGG_ERR_INTERNAL; +blargg_err_def_t blargg_err_limitation = BLARGG_ERR_LIMITATION; + +blargg_err_def_t blargg_err_file_missing = BLARGG_ERR_FILE_MISSING; +blargg_err_def_t blargg_err_file_read = BLARGG_ERR_FILE_READ; +blargg_err_def_t blargg_err_file_write = BLARGG_ERR_FILE_WRITE; +blargg_err_def_t blargg_err_file_io = BLARGG_ERR_FILE_IO; +blargg_err_def_t blargg_err_file_full = BLARGG_ERR_FILE_FULL; +blargg_err_def_t blargg_err_file_eof = BLARGG_ERR_FILE_EOF; + +blargg_err_def_t blargg_err_file_type = BLARGG_ERR_FILE_TYPE; +blargg_err_def_t blargg_err_file_feature = BLARGG_ERR_FILE_FEATURE; +blargg_err_def_t blargg_err_file_corrupt = BLARGG_ERR_FILE_CORRUPT; + +const char* blargg_err_str( blargg_err_t err ) +{ + if ( !err ) + return ""; + + if ( *err == BLARGG_ERR_TYPE("")[0] ) + return err + 1; + + return err; +} + +bool blargg_is_err_type( blargg_err_t err, const char type [] ) +{ + if ( err ) + { + // True if first strlen(type) characters of err match type + char const* p = err; + while ( *type && *type == *p ) + { + type++; + p++; + } + + if ( !*type ) + return true; + } + + return false; +} + +const char* blargg_err_details( blargg_err_t err ) +{ + const char* p = err; + if ( !p ) + { + p = ""; + } + else if ( *p == BLARGG_ERR_TYPE("")[0] ) + { + while ( *p && *p != ';' ) + p++; + + // Skip ; and space after it + if ( *p ) + { + p++; + + check( *p == ' ' ); + if ( *p ) + p++; + } + } + return p; +} + +int blargg_err_to_code( blargg_err_t err, blargg_err_to_code_t const codes [] ) +{ + if ( !err ) + return 0; + + while ( codes->str && !blargg_is_err_type( err, codes->str ) ) + codes++; + + return codes->code; +} + +blargg_err_t blargg_code_to_err( int code, blargg_err_to_code_t const codes [] ) +{ + if ( !code ) + return blargg_ok; + + while ( codes->str && codes->code != code ) + codes++; + + if ( !codes->str ) + return blargg_err_generic; + + return codes->str; +} diff --git a/fex/fex/blargg_errors.h b/fex/fex/blargg_errors.h new file mode 100644 index 0000000..9c5206d --- /dev/null +++ b/fex/fex/blargg_errors.h @@ -0,0 +1,80 @@ +// Error strings and conversion functions + +// File_Extractor 1.0.0 +#ifndef BLARGG_ERRORS_H +#define BLARGG_ERRORS_H + +#ifndef BLARGG_COMMON_H + #include "blargg_common.h" +#endif + +typedef const char blargg_err_def_t []; + +// Basic errors +extern blargg_err_def_t blargg_err_generic; +extern blargg_err_def_t blargg_err_memory; +extern blargg_err_def_t blargg_err_caller; +extern blargg_err_def_t blargg_err_internal; +extern blargg_err_def_t blargg_err_limitation; + +// File low-level +extern blargg_err_def_t blargg_err_file_missing; // not found +extern blargg_err_def_t blargg_err_file_read; +extern blargg_err_def_t blargg_err_file_write; +extern blargg_err_def_t blargg_err_file_io; +extern blargg_err_def_t blargg_err_file_full; +extern blargg_err_def_t blargg_err_file_eof; + +// File high-level +extern blargg_err_def_t blargg_err_file_type; // wrong file type +extern blargg_err_def_t blargg_err_file_feature; +extern blargg_err_def_t blargg_err_file_corrupt; + +// C string describing error, or "" if err == NULL +const char* blargg_err_str( blargg_err_t err ); + +// True iff error is of given type, or false if err == NULL +bool blargg_is_err_type( blargg_err_t, const char type [] ); + +// Details of error without describing main cause, or "" if err == NULL +const char* blargg_err_details( blargg_err_t err ); + +// Converts error string to integer code using mapping table. Calls blargg_is_err_type() +// for each str and returns code on first match. Returns 0 if err == NULL. +struct blargg_err_to_code_t { + const char* str; + int code; +}; +int blargg_err_to_code( blargg_err_t err, blargg_err_to_code_t const [] ); + +// Converts error code back to string. If code == 0, returns NULL. If not in table, +// returns blargg_err_generic. +blargg_err_t blargg_code_to_err( int code, blargg_err_to_code_t const [] ); + +// Generates error string literal with details of cause +#define BLARGG_ERR( type, str ) (type "; " str) + +// Extra space to make it clear when blargg_err_str() isn't called to get +// printable version of error. At some point, I might prefix error strings +// with a code, to speed conversion to a code. +#define BLARGG_ERR_TYPE( str ) " " str + +// Error types to pass to BLARGG_ERR macro +#define BLARGG_ERR_GENERIC BLARGG_ERR_TYPE( "operation failed" ) +#define BLARGG_ERR_MEMORY BLARGG_ERR_TYPE( "out of memory" ) +#define BLARGG_ERR_CALLER BLARGG_ERR_TYPE( "internal usage bug" ) +#define BLARGG_ERR_INTERNAL BLARGG_ERR_TYPE( "internal bug" ) +#define BLARGG_ERR_LIMITATION BLARGG_ERR_TYPE( "exceeded limitation" ) + +#define BLARGG_ERR_FILE_MISSING BLARGG_ERR_TYPE( "file not found" ) +#define BLARGG_ERR_FILE_READ BLARGG_ERR_TYPE( "couldn't open file" ) +#define BLARGG_ERR_FILE_WRITE BLARGG_ERR_TYPE( "couldn't modify file" ) +#define BLARGG_ERR_FILE_IO BLARGG_ERR_TYPE( "read/write error" ) +#define BLARGG_ERR_FILE_FULL BLARGG_ERR_TYPE( "disk full" ) +#define BLARGG_ERR_FILE_EOF BLARGG_ERR_TYPE( "truncated file" ) + +#define BLARGG_ERR_FILE_TYPE BLARGG_ERR_TYPE( "wrong file type" ) +#define BLARGG_ERR_FILE_FEATURE BLARGG_ERR_TYPE( "unsupported file feature" ) +#define BLARGG_ERR_FILE_CORRUPT BLARGG_ERR_TYPE( "corrupt file" ) + +#endif diff --git a/fex/fex/blargg_source.h b/fex/fex/blargg_source.h new file mode 100644 index 0000000..659f34c --- /dev/null +++ b/fex/fex/blargg_source.h @@ -0,0 +1,125 @@ +/* Included at the beginning of library source files, AFTER all other #include +lines. Sets up helpful macros and services used in my source code. Since this +is only "active" in my source code, I don't have to worry about polluting the +global namespace with unprefixed names. */ + +// File_Extractor 1.0.0 +#ifndef BLARGG_SOURCE_H +#define BLARGG_SOURCE_H + +#ifndef BLARGG_COMMON_H // optimization only + #include "blargg_common.h" +#endif +#include "blargg_errors.h" + +#include /* memcpy(), memset(), memmove() */ +#include /* offsetof() */ + +/* The following four macros are for debugging only. Some or all might be +defined to do nothing, depending on the circumstances. Described is what +happens when a particular macro is defined to do something. When defined to +do nothing, the macros do NOT evaluate their argument(s). */ + +/* If expr is false, prints file and line number, then aborts program. Meant +for checking internal state and consistency. A failed assertion indicates a bug +in MY code. + +void assert( bool expr ); */ +#include + +/* If expr is false, prints file and line number, then aborts program. Meant +for checking caller-supplied parameters and operations that are outside the +control of the module. A failed requirement probably indicates a bug in YOUR +code. + +void require( bool expr ); */ +#undef require +#define require( expr ) assert( expr ) + +/* Like printf() except output goes to debugging console/file. + +void dprintf( const char format [], ... ); */ +static inline void blargg_dprintf_( const char [], ... ) { } +#undef dprintf +#define dprintf (1) ? (void) 0 : blargg_dprintf_ + +/* If expr is false, prints file and line number to debug console/log, then +continues execution normally. Meant for flagging potential problems or things +that should be looked into, but that aren't serious problems. + +void check( bool expr ); */ +#undef check +#define check( expr ) ((void) 0) + +/* If expr yields non-NULL error string, returns it from current function, +otherwise continues normally. */ +#undef RETURN_ERR +#define RETURN_ERR( expr ) \ + do {\ + blargg_err_t blargg_return_err_ = (expr);\ + if ( blargg_return_err_ )\ + return blargg_return_err_;\ + } while ( 0 ) + +/* If ptr is NULL, returns out-of-memory error, otherwise continues normally. */ +#undef CHECK_ALLOC +#define CHECK_ALLOC( ptr ) \ + do {\ + if ( !(ptr) )\ + return blargg_err_memory;\ + } while ( 0 ) + +/* The usual min/max functions for built-in types. + +template T min( T x, T y ) { return x < y ? x : y; } +template T max( T x, T y ) { return x > y ? x : y; } */ +#define BLARGG_DEF_MIN_MAX( type ) \ + static inline type blargg_min( type x, type y ) { if ( y < x ) x = y; return x; }\ + static inline type blargg_max( type x, type y ) { if ( x < y ) x = y; return x; } + +BLARGG_DEF_MIN_MAX( int ) +BLARGG_DEF_MIN_MAX( unsigned ) +BLARGG_DEF_MIN_MAX( long ) +BLARGG_DEF_MIN_MAX( unsigned long ) +BLARGG_DEF_MIN_MAX( float ) +BLARGG_DEF_MIN_MAX( double ) + +#undef min +#define min blargg_min + +#undef max +#define max blargg_max + +// typedef unsigned char byte; +typedef unsigned char blargg_byte; +#undef byte +#define byte blargg_byte + +#ifndef BLARGG_EXPORT + #if defined (_WIN32) && BLARGG_BUILD_DLL + #define BLARGG_EXPORT __declspec(dllexport) + #elif defined (__GNUC__) + // can always set visibility, even when not building DLL + #define BLARGG_EXPORT __attribute__ ((visibility ("default"))) + #else + #define BLARGG_EXPORT + #endif +#endif + +#if BLARGG_LEGACY + #define BLARGG_CHECK_ALLOC CHECK_ALLOC + #define BLARGG_RETURN_ERR RETURN_ERR +#endif + +// Called after failed operation when overall operation may still complete OK. +// Only used by unit testing framework. +#undef ACK_FAILURE +#define ACK_FAILURE() ((void)0) + +/* BLARGG_SOURCE_BEGIN: If defined, #included, allowing redefition of dprintf etc. +and check */ +#ifdef BLARGG_SOURCE_BEGIN + #include BLARGG_SOURCE_BEGIN +#endif + +#endif diff --git a/fex/fex/fex.cpp b/fex/fex/fex.cpp new file mode 100644 index 0000000..ab3f153 --- /dev/null +++ b/fex/fex/fex.cpp @@ -0,0 +1,321 @@ +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "fex.h" + +#include "File_Extractor.h" +#include "blargg_endian.h" +#include +#include + +/* Copyright (C) 2005-2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + + +//// Types + +BLARGG_EXPORT const fex_type_t* fex_type_list( void ) +{ + static fex_type_t const fex_type_list_ [] = + { + #ifdef FEX_TYPE_LIST + FEX_TYPE_LIST + #else + // Modify blargg_config.h to change type list, NOT this file + fex_7z_type, + fex_gz_type, + #if FEX_ENABLE_RAR + fex_rar_type, + #endif + fex_zip_type, + #endif + fex_bin_type, + NULL + }; + + return fex_type_list_; +} + +BLARGG_EXPORT fex_err_t fex_init( void ) +{ + static bool inited; + if ( !inited ) + { + for ( fex_type_t const* t = fex_type_list(); *t != NULL; ++t ) + { + if ( (*t)->init ) + RETURN_ERR( (*t)->init() ); + } + inited = true; + } + return blargg_ok; +} + +BLARGG_EXPORT const char* fex_identify_header( void const* header ) +{ + unsigned four = get_be32( header ); + switch ( four ) + { + case 0x52457E5E: + case 0x52617221: return ".rar"; + + case 0x377ABCAF: return ".7z"; + + case 0x504B0304: + case 0x504B0506: return ".zip"; + + case 0x53495421: return ".sit"; + case 0x41724301: return ".arc"; + case 0x4D534346: return ".cab"; + case 0x5A4F4F20: return ".zoo"; + } + + unsigned three = four >> 8; + switch ( three ) + { + case 0x425A68: return ".bz2"; + } + + unsigned two = four >> 16; + switch ( two ) + { + case 0x1F8B: return ".gz"; + case 0x60EA: return ".arj"; + } + + unsigned skip_first_two = four & 0xFFFF; + if ( skip_first_two == 0x2D6C ) + return ".lha"; + + return ""; +} + +static int fex_has_extension_( const char str [], const char suffix [], size_t str_len ) +{ + size_t suffix_len = strlen( suffix ); + if ( str_len >= suffix_len ) + { + str += str_len - suffix_len; + while ( *str && tolower( (unsigned char) *str ) == *suffix ) + { + str++; + suffix++; + } + } + return *suffix == 0; +} + +BLARGG_EXPORT int fex_has_extension( const char str [], const char suffix [] ) +{ + return fex_has_extension_( str, suffix, strlen( str ) ); +} + +static int is_archive_extension( const char str [] ) +{ + static const char exts [] [6] = { + ".7z", + ".arc", + ".arj", + ".bz2", + ".cab", + ".dmg", + ".gz", + ".lha", + ".lz", + ".lzh", + ".lzma", + ".lzo", + ".lzx", + ".pea", + ".rar", + ".sit", + ".sitx", + ".tgz", + ".tlz", + ".z", + ".zip", + ".zoo", + "" + }; + + size_t str_len = strlen( str ); + const char (*ext) [6] = exts; + for ( ; **ext; ext++ ) + { + if ( fex_has_extension_( str, *ext, str_len ) ) + return 1; + } + return 0; +} + +BLARGG_EXPORT fex_type_t fex_identify_extension( const char str [] ) +{ + size_t str_len = strlen( str ); + for ( fex_type_t const* types = fex_type_list(); *types; types++ ) + { + if ( fex_has_extension_( str, (*types)->extension, str_len ) ) + { + // Avoid treating known archive type as binary + if ( *(*types)->extension || !is_archive_extension( str ) ) + return *types; + } + } + return NULL; +} + +BLARGG_EXPORT fex_err_t fex_identify_file( fex_type_t* type_out, const char path [] ) +{ + *type_out = NULL; + + fex_type_t type = fex_identify_extension( path ); + + // Unsupported extension? + if ( !type ) + return blargg_ok; // reject + + // Unknown/no extension? + if ( !*(type->extension) ) + { + // Examine header + FEX_FILE_READER in; + RETURN_ERR( in.open( path ) ); + if ( in.remain() >= fex_identify_header_size ) + { + char h [fex_identify_header_size]; + RETURN_ERR( in.read( h, sizeof h ) ); + + type = fex_identify_extension( fex_identify_header( h ) ); + } + } + + *type_out = type; + return blargg_ok; +} + +BLARGG_EXPORT fex_err_t fex_open_type( fex_t** fe_out, const char path [], fex_type_t type ) +{ + *fe_out = NULL; + + if ( !type ) + return blargg_err_file_type; + + fex_t* fe = type->new_fex(); + CHECK_ALLOC( fe ); + + fex_err_t err = fe->open( path ); + if ( err ) + { + delete fe; + return err; + } + + *fe_out = fe; + return blargg_ok; +} + +BLARGG_EXPORT fex_err_t fex_open( fex_t** fe_out, const char path [] ) +{ + *fe_out = NULL; + + fex_type_t type; + RETURN_ERR( fex_identify_file( &type, path ) ); + + return fex_open_type( fe_out, path, type ); +} + + +//// Wide paths + +char* fex_wide_to_path( const wchar_t* wide ) +{ + return blargg_to_utf8( wide ); +} + +void fex_free_path( char* path ) +{ + free( path ); +} + + +//// Errors + +#define ENTRY( name ) { blargg_err_##name, fex_err_##name } +static blargg_err_to_code_t const fex_codes [] = +{ + ENTRY( generic ), + ENTRY( memory ), + ENTRY( caller ), + ENTRY( internal ), + ENTRY( limitation ), + + ENTRY( file_missing ), + ENTRY( file_read ), + ENTRY( file_io ), + ENTRY( file_eof ), + + ENTRY( file_type ), + ENTRY( file_feature ), + ENTRY( file_corrupt ), + + { 0, -1 } +}; +#undef ENTRY + +static int err_code( fex_err_t err ) +{ + return blargg_err_to_code( err, fex_codes ); +} + +BLARGG_EXPORT int fex_err_code( fex_err_t err ) +{ + int code = err_code( err ); + return (code >= 0 ? code : fex_err_generic); +} + +BLARGG_EXPORT fex_err_t fex_code_to_err( int code ) +{ + return blargg_code_to_err( code, fex_codes ); +} + +BLARGG_EXPORT const char* fex_err_details( fex_err_t err ) +{ + // If we don't have error code assigned, return entire string + return (err_code( err ) >= 0 ? blargg_err_details( err ) : blargg_err_str( err )); +} + + +//// Wrappers + +BLARGG_EXPORT fex_err_t fex_read( fex_t* fe, void* out, int count ) +{ + RETURN_ERR( fe->stat() ); + return fe->reader().read( out, count ); +} + +BLARGG_EXPORT void fex_close ( fex_t* fe ) { delete fe; } +BLARGG_EXPORT fex_type_t fex_type ( const fex_t* fe ) { return fe->type(); } +BLARGG_EXPORT int fex_done ( const fex_t* fe ) { return fe->done(); } +BLARGG_EXPORT const char* fex_name ( const fex_t* fe ) { return fe->name(); } +BLARGG_EXPORT const wchar_t* fex_wname ( const fex_t* fe ) { return fe->wname(); } +BLARGG_EXPORT int fex_size ( const fex_t* fe ) { return fe->size(); } +BLARGG_EXPORT unsigned fex_dos_date ( const fex_t* fe ) { return fe->dos_date(); } +BLARGG_EXPORT unsigned fex_crc32 ( const fex_t* fe ) { return fe->crc32(); } +BLARGG_EXPORT fex_err_t fex_stat ( fex_t* fe ) { return fe->stat(); } +BLARGG_EXPORT fex_err_t fex_next ( fex_t* fe ) { return fe->next(); } +BLARGG_EXPORT fex_err_t fex_rewind ( fex_t* fe ) { return fe->rewind(); } +BLARGG_EXPORT int fex_tell ( const fex_t* fe ) { return fe->tell(); } +BLARGG_EXPORT fex_pos_t fex_tell_arc ( const fex_t* fe ) { return fe->tell_arc(); } +BLARGG_EXPORT fex_err_t fex_seek_arc ( fex_t* fe, fex_pos_t pos ) { return fe->seek_arc( pos ); } +BLARGG_EXPORT const char* fex_type_extension ( fex_type_t t ) { return t->extension; } +BLARGG_EXPORT const char* fex_type_name ( fex_type_t t ) { return t->name; } +BLARGG_EXPORT fex_err_t fex_data ( fex_t* fe, const void** data_out ) { return fe->data( data_out ); } +BLARGG_EXPORT const char* fex_err_str ( fex_err_t err ) { return blargg_err_str( err ); } diff --git a/fex/fex/fex.h b/fex/fex/fex.h new file mode 100644 index 0000000..8b6173a --- /dev/null +++ b/fex/fex/fex.h @@ -0,0 +1,204 @@ +/** Uniform access to zip, gzip, 7-zip, and RAR compressed archives \file */ + +/* File_Extractor 1.0.0 */ +#ifndef FEX_H +#define FEX_H + +#include + +#ifdef __cplusplus + extern "C" { +#endif + + +/** First parameter of most functions is fex_t*, or const fex_t* if nothing is +changed. Once one of these functions returns an error, the archive should not +be used any further, other than to close it. One exception is +fex_error_file_eof; the archive may still be used after this. */ +typedef struct fex_t fex_t; + +/** Pointer to error, or NULL if function was successful. See error functions +below. */ +#ifndef fex_err_t /* (#ifndef allows better testing of library) */ + typedef const char* fex_err_t; +#endif + + +/**** File types ****/ + +/** Archive file type identifier. Can also hold NULL. */ +typedef const struct fex_type_t_* fex_type_t; + +/** Array of supported types, with NULL at end */ +const fex_type_t* fex_type_list( void ); + +/** Name of this archive type, e.g. "ZIP archive", "file" */ +const char* fex_type_name( fex_type_t ); + +/** Usual file extension for type, e.g. ".zip", ".7z". For binary file type, +returns "", since it can open any file. */ +const char* fex_type_extension( fex_type_t ); + + +/**** Wide-character file paths ****/ + +/** Converts wide-character path to form suitable for use with fex functions. */ +char* fex_wide_to_path( const wchar_t* wide ); + +/** Frees converted path. OK to pass NULL. */ +void fex_free_path( char* ); + + +/**** Identification ****/ + +/** True if str ends in extension. If extension is "", always returns true. +Converts str to lowercase before comparison, so extension should ALREADY be +lowercase (i.e. pass ".zip", NOT ".ZIP"). */ +int fex_has_extension( const char str [], const char extension [] ); + +/** Determines type based on first fex_identify_header_size bytes of file. +Returns usual file extension this should have (e.g. ".zip", ".gz", etc.). +Returns "" if file header is not recognized. */ +const char* fex_identify_header( const void* header ); +enum { fex_identify_header_size = 16 }; + +/** Determines type based on extension of a file path, or just a lone extension +(must include '.', e.g. ".zip", NOT just "zip"). Returns NULL if extension is +for an unsupported type (e.g. ".lzh"). */ +fex_type_t fex_identify_extension( const char path_or_extension [] ); + +/** Determines type based on filename extension and/or file header. Sets *out +to determined type, or NULL if type is not supported. */ +fex_err_t fex_identify_file( fex_type_t* out, const char path [] ); + +/** Type of an already-opened archive */ +fex_type_t fex_type( const fex_t* ); + + +/**** Open/close ****/ + +/** Initializes static tables used by library. Automatically called by +fex_open(). OK to call more than once. */ +fex_err_t fex_init( void ); + +/** Opens archive and points *out at it. If error, sets *out to NULL. */ +fex_err_t fex_open( fex_t** out, const char path [] ); + +/** Opens archive of specified type and sets *out. Returns error if file is not +of that archive type. If error, sets *out to NULL. */ +fex_err_t fex_open_type( fex_t** out, const char path [], fex_type_t ); + +/** Closes archive and frees memory. OK to pass NULL. */ +void fex_close( fex_t* ); + + +/**** Scanning ****/ + +/** True if at end of archive. Must be called after fex_open() or fex_rewind(), +as an archive might contain no files. */ +int fex_done( const fex_t* ); + +/** Goes to next file in archive. If there are no more files, fex_done() will +now return true. */ +fex_err_t fex_next( fex_t* ); + +/** Goes back to first file in archive, as if it were just opened with +fex_open() */ +fex_err_t fex_rewind( fex_t* ); + +/** Saved position in archive. Can also store zero. */ +typedef int fex_pos_t; + +/** Position of current file in archive. Never returns zero. */ +fex_pos_t fex_tell_arc( const fex_t* ); + +/** Returns to file at previously-saved position */ +fex_err_t fex_seek_arc( fex_t*, fex_pos_t ); + + +/**** Info ****/ + +/** Name of current file */ +const char* fex_name( const fex_t* ); + +/** Wide-character name of current file, or NULL if unavailable */ +const wchar_t* fex_wname( const fex_t* ); + +/** Makes further information available for file */ +fex_err_t fex_stat( fex_t* ); + +/** Size of current file. fex_stat() or fex_data() must have been called. */ +int fex_size( const fex_t* ); + +/** Modification date of current file (MS-DOS format), or 0 if unavailable. +fex_stat() must have been called. */ +unsigned int fex_dos_date( const fex_t* ); + +/** CRC-32 checksum of current file's contents, or 0 if unavailable. Doesn't +require calculation; simply gets it from file's header. fex_stat() must have +been called. */ +unsigned int fex_crc32( const fex_t* ); + + +/**** Extraction ****/ + +/** Reads n bytes from current file. Reading past end of file results in +fex_err_file_eof. */ +fex_err_t fex_read( fex_t*, void* out, int n ); + +/** Number of bytes read from current file */ +int fex_tell( const fex_t* ); + +/** Points *out at current file's data in memory. Pointer is valid until +fex_next(), fex_rewind(), fex_seek_arc(), or fex_close() is called. Pointer +must NOT be freed(); library frees it automatically. If error, sets *out to +NULL. */ +fex_err_t fex_data( fex_t*, const void** out ); + + +/**** Errors ****/ + +/** Error string associated with err. Returns "" if err is NULL. Returns err +unchanged if it isn't a fex_err_t returned by library. */ +const char* fex_err_str( fex_err_t err ); + +/** Details of error beyond main cause, or "" if none or err is NULL. Returns +err unchanged if it isn't a fex_err_t returned by library. */ +const char* fex_err_details( fex_err_t err ); + +/** Numeric code corresponding to err. Returns fex_ok if err is NULL. Returns +fex_err_generic if err isn't a fex_err_t returned by library. */ +int fex_err_code( fex_err_t err ); + +enum { + fex_ok = 0,/**< Successful call. Guaranteed to be zero. */ + fex_err_generic = 0x01,/**< Error of unspecified type */ + fex_err_memory = 0x02,/**< Out of memory */ + fex_err_caller = 0x03,/**< Caller called function with bad args */ + fex_err_internal = 0x04,/**< Internal problem, bug, etc. */ + fex_err_limitation = 0x05,/**< Exceeded program limit */ + + fex_err_file_missing = 0x20,/**< File not found at specified path */ + fex_err_file_read = 0x21,/**< Couldn't open file for reading */ + fex_err_file_io = 0x23,/**< Read/write error */ + fex_err_file_eof = 0x25,/**< Tried to read past end of file */ + + fex_err_file_type = 0x30,/**< File is of wrong type */ + fex_err_file_feature = 0x32,/**< File requires unsupported feature */ + fex_err_file_corrupt = 0x33 /**< File is corrupt */ +}; + +/** fex_err_t corresponding to numeric code. Note that this might not recover +the original fex_err_t before it was converted to a numeric code; in +particular, fex_err_details(fex_code_to_err(code)) will be "" in most cases. */ +fex_err_t fex_code_to_err( int code ); + + +/* Deprecated */ +typedef fex_t File_Extractor; + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/fex/internals.txt b/fex/internals.txt new file mode 100644 index 0000000..92a6bca --- /dev/null +++ b/fex/internals.txt @@ -0,0 +1,91 @@ +File_Extractor library internals +-------------------------------- +This describes the implementation and design. + +Contents +-------- +* Framework +* Archive abstraction +* 7-ZIP +* ZIP +* RAR +* GZIP +* Binary + + +Framework +--------- +This library is essentially: + +* Several archive readers +* Wrappers to provide common interface +* File type detection + +The File_Extractor base class implements the common aspects of the +interface and filters out invalid calls. It also provides default +implementations for some operations. Each derived *_Extractor class then +wraps the particular library. + +Then fex.h provides a stable, C-compatible wrapper over File_Extractor, +and does file type identification. + + +Archive abstraction +------------------- +An extractor is abstracted to these fundamental operations: + +* Open (either from path or custom reader) +* Iterate over each file +* Extract file's data (either gradually, or all at once into memory) +* Seek to particular file in archive +* Close + +The extractor can choose whether to open a file path immediately, or +defer until the first stat() call. When extracting data, it can +implement one or both of normal read and "give me a pointer to the data +already in memory". If it only implements one, File_Extractor will +implement the other in terms of it. + + +7-ZIP +----- +A fairly simple wrapper over a slightly-modified LZMA library. +Unfortunately you probably won't be able to drop a later version of the +LZMA library sources in and recompile, as the interface and file +arrangement tends to change with every release. The main change I've +made is to declare functions extern "C" in headers, so C++ code can call +them. + + +ZIP +--- +A complete implementation I wrote. Has nifty optimization that makes all +disk reads begin and end on a multiple of 4K. Also minimizes number of +accesses when initially reading catalog, and extracting file data. +Correctly handles empty zip archives, unlike the popular unzip library +which reports it as a bad archive. + + +RAR +--- +Wrapper over heavily-modified UnRAR extraction library based on the +official UnRAR sources. The library is available separately with +documentation and demos, in case you want to use it without the +File_Extractor front-end (see unrar/changes.txt for more). + + +GZIP +---- +Uses zlib's built-in parsing. Also works with non-gzipped files, based +on absence of two-byte Gzip header. Defers opening of file until +fex_stat(), speeding mere scanning of filenames. Gzip_Reader and +Zlib_Reader further divide the implementation into more manageable and +reusable components. + + +Binary +------ +Just a minimal wrapper over C-style FILE. + +-- +Shay Green diff --git a/fex/license.txt b/fex/license.txt new file mode 100644 index 0000000..5ab7695 --- /dev/null +++ b/fex/license.txt @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/fex/readme.txt b/fex/readme.txt new file mode 100644 index 0000000..7456647 --- /dev/null +++ b/fex/readme.txt @@ -0,0 +1,59 @@ +File_Extractor 1.0.0 +-------------------- +File_Extractor is a modular archive scanning and extraction library that +supports several popular compressed file formats. It gives a common +interface to the supported formats, allowing one version of user code. + +Features: +* Simple C interface. +* Supports ZIP, GZIP, 7-Zip (7Z), and RAR[1] archive formats. +* Non-archive files act like archive of that one file, simplifying code. +* Modular design allows removal of support for unneeded archive formats. +* Optionally supports wide-character paths on Windows. +* Archive file type identification can be customized + +[1] RAR support must be enabled before use, due to its special +licensing. + +Author : Shay Green +Website : http://code.google.com/p/file-extractor/ +License : GNU LGPL 2.1 or later for all except unrar +Language: C interface, C++ implementation + + +Getting Started +--------------- +Build the demo by typing "make" at the command-line. If that doesn't +work, manually build a program from demo.c and all *.c and *.cpp files +in fex/, 7z_C/, and zlib/. Run demo with test.zip in the same directory. + +To enable RAR archive support, edit fex/blargg_config.h. + +See fex.h for reference and fex.txt for documentation. + + +Files +----- +fex.txt Manual +license.txt GNU LGPL 2.1 license + +makefile Builds libfex.a and demo + +demo.c Basic usage +demo_read.c Uses fex_read() to extract data +demo_rewind.c Uses fex_rewind() to re-scan archive +demo_seek.c Uses fex_seek_arc() to go back to files +demo_directory.c Recursively scans directory for archives +demo.zip Test archive used by demos + +fex/ + blargg_config.h Configuration (modify as needed) + fex.h C interface (also usable from C++) + (all other files) Library sources + +zlib/ Zip/Gzip (can use your system's instead) +7z_C/ 7-Zip +unrar/ RAR + +-- +Shay Green diff --git a/logo.png b/logo.png new file mode 100644 index 0000000..13c9973 Binary files /dev/null and b/logo.png differ diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..97ba3bd --- /dev/null +++ b/manifest.json @@ -0,0 +1,15 @@ +{ + "name": "emanuelesorce.qvbam", + "description": "gba emulator", + "framework": "ubuntu-sdk-15.04.5-qml", + "architecture": "armhf", + "title": "QVBAM", + "hooks": { + "qvbam": { + "apparmor": "qvbam.json", + "desktop": "qvbam.desktop" + } + }, + "version": "0.2.0", + "maintainer": "Emanuele Sorce " +} diff --git a/package.sh b/package.sh new file mode 100755 index 0000000..150ba1c --- /dev/null +++ b/package.sh @@ -0,0 +1,9 @@ +#! /bin/bash +mkdir package +cp build/qvbam build/qml logo.png manifest.json qvbam.desktop qvbam.json ./package -rf +cd package +click build . +cp *.click .. +cd .. +rm -rf package + diff --git a/qvbam.desktop b/qvbam.desktop new file mode 100644 index 0000000..7160db0 --- /dev/null +++ b/qvbam.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Name=QVBAM +Exec=./qvbam +Icon=./logo.png +Terminal=false +Type=Application +X-Ubuntu-Touch=true diff --git a/qvbam.json b/qvbam.json new file mode 100644 index 0000000..ec5f671 --- /dev/null +++ b/qvbam.json @@ -0,0 +1,8 @@ +{ + "policy_groups": [ + "networking", + "audio", + "content_exchange" + ], + "policy_version": 1.3 +} diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..9954a2e --- /dev/null +++ b/run.sh @@ -0,0 +1,2 @@ +cd build && ./qvbam --desktop_file_hint=`pwd`/../qvbam.desktop +cd .. diff --git a/src/AutoBuild.h b/src/AutoBuild.h new file mode 100644 index 0000000..0380d6c --- /dev/null +++ b/src/AutoBuild.h @@ -0,0 +1,29 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef __AUTOBUILD_H__ +#define __AUTOBUILD_H__ +#include "version.h" +//change the FALSE to TRUE for autoincrement of build number +#define INCREMENT_VERSION FALSE +#define FILEVER 1,8,0,600 +#define PRODUCTVER 1,8,0,600 +#define STRFILEVER "1, 8, 0, 600\0" +#define STRPRODUCTVER "1, 8, 0, 600\0" +#endif //__AUTOBUILD_H__ diff --git a/src/NLS.h b/src/NLS.h new file mode 100644 index 0000000..f8e903e --- /dev/null +++ b/src/NLS.h @@ -0,0 +1,45 @@ +#define N_(String) (String) + +#define MSG_UNSUPPORTED_VBA_SGM 1 +#define MSG_CANNOT_LOAD_SGM 2 +#define MSG_SAVE_GAME_NOT_USING_BIOS 3 +#define MSG_SAVE_GAME_USING_BIOS 4 +#define MSG_UNSUPPORTED_SAVE_TYPE 5 +#define MSG_CANNOT_OPEN_FILE 6 +#define MSG_BAD_ZIP_FILE 7 +#define MSG_NO_IMAGE_ON_ZIP 8 +#define MSG_ERROR_OPENING_IMAGE 9 +#define MSG_ERROR_READING_IMAGE 10 +#define MSG_UNSUPPORTED_BIOS_FUNCTION 11 +#define MSG_INVALID_BIOS_FILE_SIZE 12 +#define MSG_INVALID_CHEAT_CODE 13 +#define MSG_UNKNOWN_ARM_OPCODE 14 +#define MSG_UNKNOWN_THUMB_OPCODE 15 +#define MSG_ERROR_CREATING_FILE 16 +#define MSG_FAILED_TO_READ_SGM 17 +#define MSG_FAILED_TO_READ_RTC 18 +#define MSG_UNSUPPORTED_VB_SGM 19 +#define MSG_CANNOT_LOAD_SGM_FOR 20 +#define MSG_ERROR_OPENING_IMAGE_FROM 21 +#define MSG_ERROR_READING_IMAGE_FROM 22 +#define MSG_UNSUPPORTED_ROM_SIZE 23 +#define MSG_UNSUPPORTED_RAM_SIZE 24 +#define MSG_UNKNOWN_CARTRIDGE_TYPE 25 +#define MSG_MAXIMUM_NUMBER_OF_CHEATS 26 +#define MSG_INVALID_GAMESHARK_CODE 27 +#define MSG_INVALID_GAMEGENIE_CODE 28 +#define MSG_INVALID_CHEAT_TO_REMOVE 29 +#define MSG_INVALID_CHEAT_CODE_ADDRESS 30 +#define MSG_UNSUPPORTED_CHEAT_LIST_VERSION 31 +#define MSG_UNSUPPORTED_CHEAT_LIST_TYPE 32 +#define MSG_INVALID_GSA_CODE 33 +#define MSG_CANNOT_IMPORT_SNAPSHOT_FOR 34 +#define MSG_UNSUPPORTED_SNAPSHOT_FILE 35 +#define MSG_UNSUPPORTED_ARM_MODE 36 +#define MSG_UNSUPPORTED_CODE_FILE 37 +#define MSG_GBA_CODE_WARNING 38 +#define MSG_INVALID_CBA_CODE 39 +#define MSG_CBA_CODE_WARNING 40 +#define MSG_OUT_OF_MEMORY 41 +#define MSG_WRONG_GAMESHARK_CODE 42 +#define MSG_UNSUPPORTED_GAMESHARK_CODE 43 diff --git a/src/System.h b/src/System.h new file mode 100644 index 0000000..b0102fd --- /dev/null +++ b/src/System.h @@ -0,0 +1,95 @@ +#ifndef SYSTEM_H +#define SYSTEM_H +#include "common/Types.h" +#include + +class SoundDriver; + +struct EmulatedSystem { + // main emulation function + void (*emuMain)(int); + // reset emulator + void (*emuReset)(); + // clean up memory + void (*emuCleanUp)(); + // load battery file + bool (*emuReadBattery)(const char *); + // write battery file + bool (*emuWriteBattery)(const char *); +#ifdef __LIBRETRO__ + // load state + bool (*emuReadState)(const u8*, unsigned); + // load state + unsigned (*emuWriteState)(u8*, unsigned); +#else + // load state + bool (*emuReadState)(const char *); + // save state + bool (*emuWriteState)(const char *); +#endif + // load memory state (rewind) + bool (*emuReadMemState)(char *, int); + // write memory state (rewind) + bool (*emuWriteMemState)(char *, int); + // write PNG file + bool (*emuWritePNG)(const char *); + // write BMP file + bool (*emuWriteBMP)(const char *); + // emulator update CPSR (ARM only) + void (*emuUpdateCPSR)(); + // emulator has debugger + bool emuHasDebugger; + // clock ticks to emulate + int emuCount; +}; + +extern void log(const char *,...); +extern bool systemPauseOnFrame(); +extern void systemGbPrint(u8 *,int,int,int,int,int); +extern void systemScreenCapture(int); +extern void systemDrawScreen(); +// updates the joystick data +extern bool systemReadJoypads(); +// return information about the given joystick, -1 for default joystick +extern u32 systemReadJoypad(int); +extern u32 systemGetClock(); +extern void systemMessage(int, const char *, ...); +extern void systemSetTitle(const char *); +extern SoundDriver * systemSoundInit(); +extern void systemOnWriteDataToSoundBuffer(const u16 *finalWave, int length); +extern void systemOnSoundShutdown(); +extern void systemScreenMessage(const char *); +extern void systemUpdateMotionSensor(); +extern int systemGetSensorX(); +extern int systemGetSensorY(); +extern bool systemCanChangeSoundQuality(); +extern void systemShowSpeed(int); +extern void system10Frames(int); +extern void systemFrame(); +extern void systemGbBorderOn(); +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); +extern u16 systemColorMap16[0x10000]; +extern u32 systemColorMap32[0x10000]; +extern u16 systemGbPalette[24]; +extern int systemRedShift; +extern int systemGreenShift; +extern int systemBlueShift; +extern int systemColorDepth; +extern int systemDebug; +extern int systemVerbose; +extern int systemFrameSkip; +extern int systemSaveUpdateCounter; +extern int systemSpeed; +#define SYSTEM_SAVE_UPDATED 30 +#define SYSTEM_SAVE_NOT_UPDATED 0 +#endif // SYSTEM_H diff --git a/src/Util.cpp b/src/Util.cpp new file mode 100644 index 0000000..25f3df4 --- /dev/null +++ b/src/Util.cpp @@ -0,0 +1,722 @@ +#include +#include +#include +#include + +#ifndef NO_PNG +extern "C" { +#include +} +#endif + +#include "System.h" +#include "NLS.h" +#include "Util.h" +#include "gba/Flash.h" +#include "gba/GBA.h" +#include "gba/Globals.h" +#include "gba/RTC.h" +#include "common/Port.h" + +#include "fex/fex.h" + +extern "C" { +#include "common/memgzio.h" +} + +#include "gba/gbafilter.h" +#include "gb/gbGlobals.h" + +#ifndef _MSC_VER +#define _stricmp strcasecmp +#endif // ! _MSC_VER + +extern int systemColorDepth; +extern int systemRedShift; +extern int systemGreenShift; +extern int systemBlueShift; + +extern u16 systemColorMap16[0x10000]; +extern u32 systemColorMap32[0x10000]; + +static int (ZEXPORT *utilGzWriteFunc)(gzFile, const voidp, unsigned int) = NULL; +static int (ZEXPORT *utilGzReadFunc)(gzFile, voidp, unsigned int) = NULL; +static int (ZEXPORT *utilGzCloseFunc)(gzFile) = NULL; +static z_off_t (ZEXPORT *utilGzSeekFunc)(gzFile, z_off_t, int) = NULL; + +bool utilWritePNGFile(const char *fileName, int w, int h, u8 *pix) +{ +#ifndef NO_PNG + u8 writeBuffer[512 * 3]; + + FILE *fp = fopen(fileName,"wb"); + + if(!fp) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), fileName); + return false; + } + + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, + NULL, + NULL, + NULL); + if(!png_ptr) { + fclose(fp); + return false; + } + + png_infop info_ptr = png_create_info_struct(png_ptr); + + if(!info_ptr) { + png_destroy_write_struct(&png_ptr,NULL); + fclose(fp); + return false; + } + + if(setjmp(png_jmpbuf(png_ptr))) { + png_destroy_write_struct(&png_ptr,NULL); + fclose(fp); + return false; + } + + png_init_io(png_ptr,fp); + + png_set_IHDR(png_ptr, + info_ptr, + w, + h, + 8, + PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + png_write_info(png_ptr,info_ptr); + + u8 *b = writeBuffer; + + int sizeX = w; + int sizeY = h; + + switch(systemColorDepth) { + case 16: + { + u16 *p = (u16 *)(pix+(w+2)*2); // skip first black line + for(int y = 0; y < sizeY; y++) { + for(int x = 0; x < sizeX; x++) { + u16 v = *p++; + + *b++ = ((v >> systemRedShift) & 0x001f) << 3; // R + *b++ = ((v >> systemGreenShift) & 0x001f) << 3; // G + *b++ = ((v >> systemBlueShift) & 0x01f) << 3; // B + } + p++; // skip black pixel for filters + p++; // skip black pixel for filters + png_write_row(png_ptr,writeBuffer); + + b = writeBuffer; + } + } + break; + case 24: + { + u8 *pixU8 = (u8 *)pix; + for(int y = 0; y < sizeY; y++) { + for(int x = 0; x < sizeX; x++) { + if(systemRedShift < systemBlueShift) { + *b++ = *pixU8++; // R + *b++ = *pixU8++; // G + *b++ = *pixU8++; // B + } else { + int blue = *pixU8++; + int green = *pixU8++; + int red = *pixU8++; + + *b++ = red; + *b++ = green; + *b++ = blue; + } + } + png_write_row(png_ptr,writeBuffer); + + b = writeBuffer; + } + } + break; + case 32: + { + u32 *pixU32 = (u32 *)(pix+4*(w+1)); + for(int y = 0; y < sizeY; y++) { + for(int x = 0; x < sizeX; x++) { + u32 v = *pixU32++; + + *b++ = ((v >> systemRedShift) & 0x001f) << 3; // R + *b++ = ((v >> systemGreenShift) & 0x001f) << 3; // G + *b++ = ((v >> systemBlueShift) & 0x001f) << 3; // B + } + pixU32++; + + png_write_row(png_ptr,writeBuffer); + + b = writeBuffer; + } + } + break; + } + + png_write_end(png_ptr, info_ptr); + + png_destroy_write_struct(&png_ptr, &info_ptr); + + fclose(fp); + + return true; +#else + return false; +#endif +} + +void utilPutDword(u8 *p, u32 value) +{ + *p++ = value & 255; + *p++ = (value >> 8) & 255; + *p++ = (value >> 16) & 255; + *p = (value >> 24) & 255; +} + +void utilPutWord(u8 *p, u16 value) +{ + *p++ = value & 255; + *p = (value >> 8) & 255; +} + +bool utilWriteBMPFile(const char *fileName, int w, int h, u8 *pix) +{ + u8 writeBuffer[512 * 3]; + + FILE *fp = fopen(fileName,"wb"); + + if(!fp) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), fileName); + return false; + } + + struct { + u8 ident[2]; + u8 filesize[4]; + u8 reserved[4]; + u8 dataoffset[4]; + u8 headersize[4]; + u8 width[4]; + u8 height[4]; + u8 planes[2]; + u8 bitsperpixel[2]; + u8 compression[4]; + u8 datasize[4]; + u8 hres[4]; + u8 vres[4]; + u8 colors[4]; + u8 importantcolors[4]; + // u8 pad[2]; + } bmpheader; + memset(&bmpheader, 0, sizeof(bmpheader)); + + bmpheader.ident[0] = 'B'; + bmpheader.ident[1] = 'M'; + + u32 fsz = sizeof(bmpheader) + w*h*3; + utilPutDword(bmpheader.filesize, fsz); + utilPutDword(bmpheader.dataoffset, 0x36); + utilPutDword(bmpheader.headersize, 0x28); + utilPutDword(bmpheader.width, w); + utilPutDword(bmpheader.height, h); + utilPutDword(bmpheader.planes, 1); + utilPutDword(bmpheader.bitsperpixel, 24); + utilPutDword(bmpheader.datasize, 3*w*h); + + fwrite(&bmpheader, 1, sizeof(bmpheader), fp); + + u8 *b = writeBuffer; + + int sizeX = w; + int sizeY = h; + + switch(systemColorDepth) { + case 16: + { + u16 *p = (u16 *)(pix+(w+2)*(h)*2); // skip first black line + for(int y = 0; y < sizeY; y++) { + for(int x = 0; x < sizeX; x++) { + u16 v = *p++; + + *b++ = ((v >> systemBlueShift) & 0x01f) << 3; // B + *b++ = ((v >> systemGreenShift) & 0x001f) << 3; // G + *b++ = ((v >> systemRedShift) & 0x001f) << 3; // R + } + p++; // skip black pixel for filters + p++; // skip black pixel for filters + p -= 2*(w+2); + fwrite(writeBuffer, 1, 3*w, fp); + + b = writeBuffer; + } + } + break; + case 24: + { + u8 *pixU8 = (u8 *)pix+3*w*(h-1); + for(int y = 0; y < sizeY; y++) { + for(int x = 0; x < sizeX; x++) { + if(systemRedShift > systemBlueShift) { + *b++ = *pixU8++; // B + *b++ = *pixU8++; // G + *b++ = *pixU8++; // R + } else { + int red = *pixU8++; + int green = *pixU8++; + int blue = *pixU8++; + + *b++ = blue; + *b++ = green; + *b++ = red; + } + } + pixU8 -= 2*3*w; + fwrite(writeBuffer, 1, 3*w, fp); + + b = writeBuffer; + } + } + break; + case 32: + { + u32 *pixU32 = (u32 *)(pix+4*(w+1)*(h)); + for(int y = 0; y < sizeY; y++) { + for(int x = 0; x < sizeX; x++) { + u32 v = *pixU32++; + + *b++ = ((v >> systemBlueShift) & 0x001f) << 3; // B + *b++ = ((v >> systemGreenShift) & 0x001f) << 3; // G + *b++ = ((v >> systemRedShift) & 0x001f) << 3; // R + } + pixU32++; + pixU32 -= 2*(w+1); + + fwrite(writeBuffer, 1, 3*w, fp); + + b = writeBuffer; + } + } + break; + } + + fclose(fp); + + return true; +} + +extern bool cpuIsMultiBoot; + +bool utilIsGBAImage(const char * file) +{ + cpuIsMultiBoot = false; + if(strlen(file) > 4) { + const char * p = strrchr(file,'.'); + + if(p != NULL) { + if((_stricmp(p, ".agb") == 0) || + (_stricmp(p, ".gba") == 0) || + (_stricmp(p, ".bin") == 0) || + (_stricmp(p, ".elf") == 0)) + return true; + if(_stricmp(p, ".mb") == 0) { + cpuIsMultiBoot = true; + return true; + } + } + } + + return false; +} + +bool utilIsGBImage(const char * file) +{ + if(strlen(file) > 4) { + const char * p = strrchr(file,'.'); + + if(p != NULL) { + if((_stricmp(p, ".dmg") == 0) || + (_stricmp(p, ".gb") == 0) || + (_stricmp(p, ".gbc") == 0) || + (_stricmp(p, ".cgb") == 0) || + (_stricmp(p, ".sgb") == 0)) + return true; + } + } + + return false; +} + +bool utilIsGzipFile(const char *file) +{ + if(strlen(file) > 3) { + const char * p = strrchr(file,'.'); + + if(p != NULL) { + if(_stricmp(p, ".gz") == 0) + return true; + if(_stricmp(p, ".z") == 0) + return true; + } + } + + return false; +} + +// strip .gz or .z off end +void utilStripDoubleExtension(const char *file, char *buffer) +{ + if(buffer != file) // allows conversion in place + strcpy(buffer, file); + + if(utilIsGzipFile(file)) { + char *p = strrchr(buffer, '.'); + + if(p) + *p = 0; + } +} + +// Opens and scans archive using accept(). Returns fex_t if found. +// If error or not found, displays message and returns NULL. +static fex_t* scan_arc(const char *file, bool (*accept)(const char *), + char (&buffer) [2048] ) +{ + fex_t* fe; + fex_err_t err = fex_open( &fe, file ); + if(!fe) + { + systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s: %s"), file, err); + return NULL; + } + + // Scan filenames + bool found=false; + while(!fex_done(fe)) { + strncpy(buffer,fex_name(fe),sizeof buffer); + buffer [sizeof buffer-1] = '\0'; + + utilStripDoubleExtension(buffer, buffer); + + if(accept(buffer)) { + found = true; + break; + } + + fex_err_t err = fex_next(fe); + if(err) { + systemMessage(MSG_BAD_ZIP_FILE, N_("Cannot read archive %s: %s"), file, err); + fex_close(fe); + return NULL; + } + } + + if(!found) { + systemMessage(MSG_NO_IMAGE_ON_ZIP, + N_("No image found in file %s"), file); + fex_close(fe); + return NULL; + } + return fe; +} + +static bool utilIsImage(const char *file) +{ + return utilIsGBAImage(file) || utilIsGBImage(file); +} + +#ifdef WIN32 +#include +#endif + +IMAGE_TYPE utilFindType(const char *file, char (&buffer)[2048]); + +IMAGE_TYPE utilFindType(const char *file) +{ + char buffer [2048]; + return 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; + pwText = new wchar_t[dwNum]; + if(!pwText) + { + return IMAGE_UNKNOWN; + } + MultiByteToWideChar (CP_ACP, 0, file, -1, pwText, dwNum ); + char* file_conv = fex_wide_to_path( pwText); +// 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? +// { + 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; +} + +static int utilGetSize(int size) +{ + int res = 1; + while(res < size) + res <<= 1; + return res; +} + +u8 *utilLoad(const char *file, + bool (*accept)(const char *), + u8 *data, + int &size) +{ + // find image file + char buffer [2048]; +#ifdef WIN32 + DWORD dwNum = MultiByteToWideChar (CP_ACP, 0, file, -1, NULL, 0); + wchar_t *pwText; + pwText = new wchar_t[dwNum]; + if(!pwText) + { + return NULL; + } + MultiByteToWideChar (CP_ACP, 0, file, -1, pwText, dwNum ); + char* file_conv = fex_wide_to_path( pwText); + delete []pwText; + fex_t *fe = scan_arc(file_conv,accept,buffer); + if(!fe) + return NULL; + free(file_conv); +#else + fex_t *fe = scan_arc(file,accept,buffer); + if(!fe) + return NULL; +#endif + // Allocate space for image + fex_err_t err = fex_stat(fe); + int fileSize = fex_size(fe); + if(size == 0) + size = fileSize; + + u8 *image = data; + + if(image == NULL) { + // allocate buffer memory if none was passed to the function + image = (u8 *)malloc(utilGetSize(size)); + if(image == NULL) { + fex_close(fe); + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "data"); + return NULL; + } + size = fileSize; + } + + // Read image + int read = fileSize <= size ? fileSize : size; // do not read beyond file + err = fex_read(fe, image, read); + fex_close(fe); + if(err) { + systemMessage(MSG_ERROR_READING_IMAGE, + N_("Error reading image from %s: %s"), buffer, err); + if(data == NULL) + free(image); + return NULL; + } + + size = fileSize; + + return image; +} + +void utilWriteInt(gzFile gzFile, int i) +{ + utilGzWrite(gzFile, &i, sizeof(int)); +} + +int utilReadInt(gzFile gzFile) +{ + int i = 0; + utilGzRead(gzFile, &i, sizeof(int)); + return i; +} + +void utilReadData(gzFile gzFile, variable_desc* data) +{ + while(data->address) { + utilGzRead(gzFile, data->address, data->size); + data++; + } +} + +void utilReadDataSkip(gzFile gzFile, variable_desc* data) +{ + while(data->address) { + utilGzSeek(gzFile, data->size, SEEK_CUR); + data++; + } +} + +void utilWriteData(gzFile gzFile, variable_desc *data) +{ + while(data->address) { + utilGzWrite(gzFile, data->address, data->size); + data++; + } +} + +gzFile utilGzOpen(const char *file, const char *mode) +{ + utilGzWriteFunc = (int (ZEXPORT *)(gzFile, void * const, unsigned int))gzwrite; + utilGzReadFunc = gzread; + utilGzCloseFunc = gzclose; + utilGzSeekFunc = gzseek; + + return gzopen(file, mode); +} + +gzFile utilMemGzOpen(char *memory, int available, const char *mode) +{ + utilGzWriteFunc = memgzwrite; + utilGzReadFunc = memgzread; + utilGzCloseFunc = memgzclose; + utilGzSeekFunc = memgzseek; + + return memgzopen(memory, available, mode); +} + +int utilGzWrite(gzFile file, const voidp buffer, unsigned int len) +{ + return utilGzWriteFunc(file, buffer, len); +} + +int utilGzRead(gzFile file, voidp buffer, unsigned int len) +{ + return utilGzReadFunc(file, buffer, len); +} + +int utilGzClose(gzFile file) +{ + return utilGzCloseFunc(file); +} + +z_off_t utilGzSeek(gzFile file, z_off_t offset, int whence) +{ + return utilGzSeekFunc(file, offset, whence); +} + +long utilGzMemTell(gzFile file) +{ + return memtell(file); +} + +void utilGBAFindSave(const u8 *data, const int size) +{ + u32 *p = (u32 *)data; + u32 *end = (u32 *)(data + size); + int saveType = 0; + int flashSize = 0x10000; + bool rtcFound = false; + + while(p < end) { + u32 d = READ32LE(p); + + if(d == 0x52504545) { + if(memcmp(p, "EEPROM_", 7) == 0) { + if(saveType == 0) + saveType = 3; + } + } else if (d == 0x4D415253) { + if(memcmp(p, "SRAM_", 5) == 0) { + if(saveType == 0) + saveType = 1; + } + } else if (d == 0x53414C46) { + if(memcmp(p, "FLASH1M_", 8) == 0) { + if(saveType == 0) { + saveType = 2; + flashSize = 0x20000; + } + } else if(memcmp(p, "FLASH", 5) == 0) { + if(saveType == 0) { + saveType = 2; + flashSize = 0x10000; + } + } + } else if (d == 0x52494953) { + if(memcmp(p, "SIIRTC_V", 8) == 0) + rtcFound = true; + } + p++; + } + // if no matches found, then set it to NONE + if(saveType == 0) { + saveType = 5; + } + rtcEnable(rtcFound); + cpuSaveType = saveType; + flashSetSize(flashSize); +} + +void utilUpdateSystemColorMaps(bool lcd) +{ + switch(systemColorDepth) { + case 16: + { + for(int i = 0; i < 0x10000; i++) { + systemColorMap16[i] = ((i & 0x1f) << systemRedShift) | + (((i & 0x3e0) >> 5) << systemGreenShift) | + (((i & 0x7c00) >> 10) << systemBlueShift); + } + if (lcd) gbafilter_pal(systemColorMap16, 0x10000); + } + break; + case 24: + case 32: + { + for(int i = 0; i < 0x10000; i++) { + systemColorMap32[i] = ((i & 0x1f) << systemRedShift) | + (((i & 0x3e0) >> 5) << systemGreenShift) | + (((i & 0x7c00) >> 10) << systemBlueShift); + } + if (lcd) gbafilter_pal32(systemColorMap32, 0x10000); + } + break; + } +} + +// Check for existence of file. +bool utilFileExists( const char *filename ) +{ + FILE *f = fopen( filename, "r" ); + if( f == NULL ) { + return false; + } else { + fclose( f ); + return true; + } +} diff --git a/src/Util.h b/src/Util.h new file mode 100644 index 0000000..1292b85 --- /dev/null +++ b/src/Util.h @@ -0,0 +1,57 @@ +#ifndef UTIL_H +#define UTIL_H + +#include "System.h" + +enum IMAGE_TYPE { + IMAGE_UNKNOWN = -1, + IMAGE_GBA = 0, + IMAGE_GB = 1 +}; + +// save game +typedef struct { + void *address; + int size; +} variable_desc; +bool utilWritePNGFile(const char *, int, int, u8 *); +bool utilWriteBMPFile(const char *, int, int, u8 *); +void utilApplyIPS(const char *ips, uint8_t **rom, int *size); +bool utilIsGBAImage(const char *); +bool utilIsGBImage(const char *); +bool utilIsGzipFile(const char *); +bool utilIsZipFile(const char *); +void utilStripDoubleExtension(const char *, char *); +IMAGE_TYPE utilFindType(const char *); +uint8_t *utilLoad(const char *, bool (*)(const char*), uint8_t *, int &); + +void utilPutDword(uint8_t *, uint32_t); +void utilPutWord(uint8_t *, uint16_t); +void utilWriteData(gzFile, variable_desc *); +void utilReadData(gzFile, variable_desc *); +void utilReadDataSkip(gzFile, variable_desc *); +int utilReadInt(gzFile); +void utilWriteInt(gzFile, int); +gzFile utilGzOpen(const char *file, const char *mode); +gzFile utilMemGzOpen(char *memory, int available, const char *mode); +int utilGzWrite(gzFile file, const voidp buffer, unsigned int len); +int utilGzRead(gzFile file, voidp buffer, unsigned int len); +int utilGzClose(gzFile file); +z_off_t utilGzSeek(gzFile file, z_off_t offset, int whence); +long utilGzMemTell(gzFile file); +void utilGBAFindSave(const u8 *, const int); +void utilUpdateSystemColorMaps(bool lcd = false); +bool utilFileExists( const char *filename ); + + +#ifdef __LIBRETRO__ +void utilWriteIntMem(uint8_t *& data, int); +void utilWriteMem(uint8_t *& data, const void *in_data, unsigned size); +void utilWriteDataMem(uint8_t *& data, variable_desc *); + +int utilReadIntMem(const uint8_t *& data); +void utilReadMem(void *buf, const uint8_t *& data, unsigned size); +void utilReadDataMem(const uint8_t *& data, variable_desc *); +#endif + +#endif // UTIL_H diff --git a/src/apu/Blip_Buffer.cpp b/src/apu/Blip_Buffer.cpp new file mode 100644 index 0000000..098ab57 --- /dev/null +++ b/src/apu/Blip_Buffer.cpp @@ -0,0 +1,465 @@ +// Blip_Buffer 0.4.1. http://www.slack.net/~ant/ + +#include "Blip_Buffer.h" + +#include +#include +#include +#include +#include + +/* Copyright (C) 2003-2007 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +// TODO: use scoped for variables in treble_eq() + +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif + +int const silent_buf_size = 1; // size used for Silent_Blip_Buffer + +Blip_Buffer::Blip_Buffer() +{ + factor_ = (blip_ulong)LONG_MAX; + buffer_ = 0; + buffer_size_ = 0; + sample_rate_ = 0; + bass_shift_ = 0; + clock_rate_ = 0; + bass_freq_ = 16; + length_ = 0; + + // assumptions code makes about implementation-defined features + #ifndef NDEBUG + // right shift of negative value preserves sign + buf_t_ i = -0x7FFFFFFE; + assert( (i >> 1) == -0x3FFFFFFF ); + + // casting to short truncates to 16 bits and sign-extends + i = 0x18000; + assert( (short) i == -0x8000 ); + #endif + + clear(); +} + +Blip_Buffer::~Blip_Buffer() +{ + if ( buffer_size_ != silent_buf_size ) + free( buffer_ ); +} + +Silent_Blip_Buffer::Silent_Blip_Buffer() +{ + factor_ = 0; + buffer_ = buf; + buffer_size_ = silent_buf_size; + clear(); +} + +void Blip_Buffer::clear( int entire_buffer ) +{ + offset_ = 0; + reader_accum_ = 0; + modified_ = 0; + if ( buffer_ ) + { + long count = (entire_buffer ? buffer_size_ : samples_avail()); + memset( buffer_, 0, (count + blip_buffer_extra_) * sizeof (buf_t_) ); + } +} + +Blip_Buffer::blargg_err_t Blip_Buffer::set_sample_rate( long new_rate, int msec ) +{ + if ( buffer_size_ == silent_buf_size ) + { + assert( 0 ); + return "Internal (tried to resize Silent_Blip_Buffer)"; + } + + // start with maximum length that resampled time can represent + long new_size = (ULONG_MAX >> BLIP_BUFFER_ACCURACY) - blip_buffer_extra_ - 64; + if ( msec != blip_max_length ) + { + long s = (new_rate * (msec + 1) + 999) / 1000; + if ( s < new_size ) + new_size = s; + else + assert( 0 ); // fails if requested buffer length exceeds limit + } + + if ( buffer_size_ != new_size ) + { + void* p = realloc( buffer_, (new_size + blip_buffer_extra_) * sizeof *buffer_ ); + if ( !p ) + return "Out of memory"; + buffer_ = (buf_t_*) p; + } + + buffer_size_ = new_size; + assert( buffer_size_ != silent_buf_size ); // size should never happen to match this + + // update things based on the sample rate + sample_rate_ = new_rate; + length_ = new_size * 1000 / new_rate - 1; + if ( msec ) + assert( length_ == msec ); // ensure length is same as that passed in + + // update these since they depend on sample rate + if ( clock_rate_ ) + clock_rate( clock_rate_ ); + bass_freq( bass_freq_ ); + + clear(); + + return 0; // success +} + +blip_resampled_time_t Blip_Buffer::clock_rate_factor( long rate ) const +{ + double ratio = (double) sample_rate_ / rate; + blip_long factor = (blip_long) floor( ratio * (1L << BLIP_BUFFER_ACCURACY) + 0.5 ); + assert( factor > 0 || !sample_rate_ ); // fails if clock/output ratio is too large + return (blip_resampled_time_t) factor; +} + +void Blip_Buffer::bass_freq( int freq ) +{ + bass_freq_ = freq; + int shift = 31; + if ( freq > 0 ) + { + shift = 13; + long f = (freq << 16) / sample_rate_; + while ( (f >>= 1) && --shift ) { } + } + bass_shift_ = shift; +} + +void Blip_Buffer::end_frame( blip_time_t t ) +{ + offset_ += t * factor_; + assert( samples_avail() <= (long) buffer_size_ ); // fails if time is past end of buffer +} + +long Blip_Buffer::count_samples( blip_time_t t ) const +{ + blip_resampled_time_t last_sample = resampled_time( t ) >> BLIP_BUFFER_ACCURACY; + blip_resampled_time_t first_sample = offset_ >> BLIP_BUFFER_ACCURACY; + return long (last_sample - first_sample); +} + +blip_time_t Blip_Buffer::count_clocks( long count ) const +{ + if ( !factor_ ) + { + assert( 0 ); // sample rate and clock rates must be set first + return 0; + } + + if ( count > buffer_size_ ) + count = buffer_size_; + blip_resampled_time_t time = (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY; + return (blip_time_t) ((time - offset_ + factor_ - 1) / factor_); +} + +void Blip_Buffer::remove_samples( long count ) +{ + if ( count ) + { + remove_silence( count ); + + // copy remaining samples to beginning and clear old samples + long remain = samples_avail() + blip_buffer_extra_; + memmove( buffer_, buffer_ + count, remain * sizeof *buffer_ ); + memset( buffer_ + remain, 0, count * sizeof *buffer_ ); + } +} + +// Blip_Synth_ + +Blip_Synth_Fast_::Blip_Synth_Fast_() +{ + buf = 0; + last_amp = 0; + delta_factor = 0; +} + +void Blip_Synth_Fast_::volume_unit( double new_unit ) +{ + delta_factor = int (new_unit * (1L << blip_sample_bits) + 0.5); +} + +#if !BLIP_BUFFER_FAST + +Blip_Synth_::Blip_Synth_( short* p, int w ) : + impulses( p ), + width( w ) +{ + volume_unit_ = 0.0; + kernel_unit = 0; + buf = 0; + last_amp = 0; + delta_factor = 0; +} + +#undef PI +#define PI 3.1415926535897932384626433832795029 + +static void gen_sinc( float* out, int count, double oversample, double treble, double cutoff ) +{ + if ( cutoff >= 0.999 ) + cutoff = 0.999; + + if ( treble < -300.0 ) + treble = -300.0; + if ( treble > 5.0 ) + treble = 5.0; + + double const maxh = 4096.0; + double const rolloff = pow( 10.0, 1.0 / (maxh * 20.0) * treble / (1.0 - cutoff) ); + double const pow_a_n = pow( rolloff, maxh - maxh * cutoff ); + double const to_angle = PI / 2 / maxh / oversample; + for ( int i = 0; i < count; i++ ) + { + double angle = ((i - count) * 2 + 1) * to_angle; + double c = rolloff * cos( (maxh - 1.0) * angle ) - cos( maxh * angle ); + double cos_nc_angle = cos( maxh * cutoff * angle ); + double cos_nc1_angle = cos( (maxh * cutoff - 1.0) * angle ); + double cos_angle = cos( angle ); + + c = c * pow_a_n - rolloff * cos_nc1_angle + cos_nc_angle; + double d = 1.0 + rolloff * (rolloff - cos_angle - cos_angle); + double b = 2.0 - cos_angle - cos_angle; + double a = 1.0 - cos_angle - cos_nc_angle + cos_nc1_angle; + + out [i] = (float) ((a * d + c * b) / (b * d)); // a / b + c / d + } +} + +void blip_eq_t::generate( float* out, int count ) const +{ + // lower cutoff freq for narrow kernels with their wider transition band + // (8 points->1.49, 16 points->1.15) + double oversample = blip_res * 2.25 / count + 0.85; + double half_rate = sample_rate * 0.5; + if ( cutoff_freq ) + oversample = half_rate / cutoff_freq; + double cutoff = rolloff_freq * oversample / half_rate; + + gen_sinc( out, count, blip_res * oversample, treble, cutoff ); + + // apply (half of) hamming window + double to_fraction = PI / (count - 1); + for ( int i = count; i--; ) + out [i] *= 0.54f - 0.46f * (float) cos( i * to_fraction ); +} + +void Blip_Synth_::adjust_impulse() +{ + // sum pairs for each phase and add error correction to end of first half + int const size = impulses_size(); + for ( int p = blip_res; p-- >= blip_res / 2; ) + { + int p2 = blip_res - 2 - p; + long error = kernel_unit; + for ( int i = 1; i < size; i += blip_res ) + { + error -= impulses [i + p ]; + error -= impulses [i + p2]; + } + if ( p == p2 ) + error /= 2; // phase = 0.5 impulse uses same half for both sides + impulses [size - blip_res + p] += (short) error; + //printf( "error: %ld\n", error ); + } + + //for ( int i = blip_res; i--; printf( "\n" ) ) + // for ( int j = 0; j < width / 2; j++ ) + // printf( "%5ld,", impulses [j * blip_res + i + 1] ); +} + +void Blip_Synth_::treble_eq( blip_eq_t const& eq ) +{ + float fimpulse [blip_res / 2 * (blip_widest_impulse_ - 1) + blip_res * 2]; + + int const half_size = blip_res / 2 * (width - 1); + eq.generate( &fimpulse [blip_res], half_size ); + + int i; + + // need mirror slightly past center for calculation + for ( i = blip_res; i--; ) + fimpulse [blip_res + half_size + i] = fimpulse [blip_res + half_size - 1 - i]; + + // starts at 0 + for ( i = 0; i < blip_res; i++ ) + fimpulse [i] = 0.0f; + + // find rescale factor + double total = 0.0; + for ( i = 0; i < half_size; i++ ) + total += fimpulse [blip_res + i]; + + //double const base_unit = 44800.0 - 128 * 18; // allows treble up to +0 dB + //double const base_unit = 37888.0; // allows treble to +5 dB + double const base_unit = 32768.0; // necessary for blip_unscaled to work + double rescale = base_unit / 2 / total; + kernel_unit = (long) base_unit; + + // integrate, first difference, rescale, convert to int + double sum = 0.0; + double next = 0.0; + int const size = this->impulses_size(); + for ( i = 0; i < size; i++ ) + { + impulses [i] = (short) (int) floor( (next - sum) * rescale + 0.5 ); + sum += fimpulse [i]; + next += fimpulse [i + blip_res]; + } + adjust_impulse(); + + // volume might require rescaling + double vol = volume_unit_; + if ( vol ) + { + volume_unit_ = 0.0; + volume_unit( vol ); + } +} + +void Blip_Synth_::volume_unit( double new_unit ) +{ + if ( new_unit != volume_unit_ ) + { + // use default eq if it hasn't been set yet + if ( !kernel_unit ) + treble_eq( -8.0 ); + + volume_unit_ = new_unit; + double factor = new_unit * (1L << blip_sample_bits) / kernel_unit; + + if ( factor > 0.0 ) + { + int shift = 0; + + // if unit is really small, might need to attenuate kernel + while ( factor < 2.0 ) + { + shift++; + factor *= 2.0; + } + + if ( shift ) + { + kernel_unit >>= shift; + assert( kernel_unit > 0 ); // fails if volume unit is too low + + // keep values positive to avoid round-towards-zero of sign-preserving + // right shift for negative values + long offset = 0x8000 + (1 << (shift - 1)); + long offset2 = 0x8000 >> shift; + for ( int i = impulses_size(); i--; ) + impulses [i] = (short) (int) (((impulses [i] + offset) >> shift) - offset2); + adjust_impulse(); + } + } + delta_factor = (int) floor( factor + 0.5 ); + //printf( "delta_factor: %d, kernel_unit: %d\n", delta_factor, kernel_unit ); + } +} +#endif + +long Blip_Buffer::read_samples( blip_sample_t* out_, long max_samples, int stereo ) +{ + long count = samples_avail(); + if ( count > max_samples ) + count = max_samples; + + if ( count ) + { + int const bass = BLIP_READER_BASS( *this ); + BLIP_READER_BEGIN( reader, *this ); + BLIP_READER_ADJ_( reader, count ); + blip_sample_t* BLIP_RESTRICT out = out_ + count; + blip_long offset = (blip_long) -count; + + if ( !stereo ) + { + do + { + blip_long s = BLIP_READER_READ( reader ); + BLIP_READER_NEXT_IDX_( reader, bass, offset ); + BLIP_CLAMP( s, s ); + out [offset] = (blip_sample_t) s; + } + while ( ++offset ); + } + else + { + do + { + blip_long s = BLIP_READER_READ( reader ); + BLIP_READER_NEXT_IDX_( reader, bass, offset ); + BLIP_CLAMP( s, s ); + out [offset * 2] = (blip_sample_t) s; + } + while ( ++offset ); + } + + BLIP_READER_END( reader, *this ); + + remove_samples( count ); + } + return count; +} + +void Blip_Buffer::mix_samples( blip_sample_t const* in, long count ) +{ + if ( buffer_size_ == silent_buf_size ) + { + assert( 0 ); + return; + } + + buf_t_* out = buffer_ + (offset_ >> BLIP_BUFFER_ACCURACY) + blip_widest_impulse_ / 2; + + int const sample_shift = blip_sample_bits - 16; + int prev = 0; + while ( count-- ) + { + blip_long s = (blip_long) *in++ << sample_shift; + *out += s - prev; + prev = s; + ++out; + } + *out -= prev; +} + +blip_ulong const subsample_mask = (1L << BLIP_BUFFER_ACCURACY) - 1; + +void Blip_Buffer::save_state( blip_buffer_state_t* out ) +{ + assert( samples_avail() == 0 ); + out->offset_ = offset_; + out->reader_accum_ = reader_accum_; + memcpy( out->buf, &buffer_ [offset_ >> BLIP_BUFFER_ACCURACY], sizeof out->buf ); +} + +void Blip_Buffer::load_state( blip_buffer_state_t const& in ) +{ + clear( false ); + + offset_ = in.offset_; + reader_accum_ = in.reader_accum_; + memcpy( buffer_, in.buf, sizeof in.buf ); +} diff --git a/src/apu/Blip_Buffer.h b/src/apu/Blip_Buffer.h new file mode 100644 index 0000000..699bff7 --- /dev/null +++ b/src/apu/Blip_Buffer.h @@ -0,0 +1,556 @@ +// Band-limited sound synthesis buffer + +// Blip_Buffer 0.4.1 +#ifndef BLIP_BUFFER_H +#define BLIP_BUFFER_H + + // internal + #include + #if INT_MAX < 0x7FFFFFFF || LONG_MAX == 0x7FFFFFFF + typedef long blip_long; + typedef unsigned long blip_ulong; + #else + typedef int blip_long; + typedef unsigned blip_ulong; + #endif + +// Time unit at source clock rate +typedef blip_long blip_time_t; + +// Output samples are 16-bit signed, with a range of -32768 to 32767 +typedef short blip_sample_t; +enum { blip_sample_max = 32767 }; + +struct blip_buffer_state_t; + +class Blip_Buffer { +public: + typedef const char* blargg_err_t; + + // Sets output sample rate and buffer length in milliseconds (1/1000 sec, defaults + // to 1/4 second) and clears buffer. If there isn't enough memory, leaves buffer + // untouched and returns "Out of memory", otherwise returns NULL. + blargg_err_t set_sample_rate( long samples_per_sec, int msec_length = 1000 / 4 ); + + // Sets number of source time units per second + void clock_rate( long clocks_per_sec ); + + // Ends current time frame of specified duration and makes its samples available + // (along with any still-unread samples) for reading with read_samples(). Begins + // a new time frame at the end of the current frame. + void end_frame( blip_time_t time ); + + // Reads at most 'max_samples' out of buffer into 'dest', removing them from + // the buffer. Returns number of samples actually read and removed. If stereo is + // true, increments 'dest' one extra time after writing each sample, to allow + // easy interleving of two channels into a stereo output buffer. + long read_samples( blip_sample_t* dest, long max_samples, int stereo = 0 ); + +// Additional features + + // Removes all available samples and clear buffer to silence. If 'entire_buffer' is + // false, just clears out any samples waiting rather than the entire buffer. + void clear( int entire_buffer = 1 ); + + // Number of samples available for reading with read_samples() + long samples_avail() const; + + // Removes 'count' samples from those waiting to be read + void remove_samples( long count ); + + // Sets frequency high-pass filter frequency, where higher values reduce bass more + void bass_freq( int frequency ); + + // Current output sample rate + long sample_rate() const; + + // Length of buffer in milliseconds + int length() const; + + // Number of source time units per second + long clock_rate() const; + +// Experimental features + + // Saves state, including high-pass filter and tails of last deltas. + // All samples must have been read from buffer before calling this. + void save_state( blip_buffer_state_t* out ); + + // Loads state. State must have been saved from Blip_Buffer with same + // settings during same run of program. States can NOT be stored on disk. + // Clears buffer before loading state. + void load_state( blip_buffer_state_t const& in ); + + // Number of samples delay from synthesis to samples read out + int output_latency() const; + + // Counts number of clocks needed until 'count' samples will be available. + // If buffer can't even hold 'count' samples, returns number of clocks until + // buffer becomes full. + blip_time_t count_clocks( long count ) const; + + // Number of raw samples that can be mixed within frame of specified duration. + long count_samples( blip_time_t duration ) const; + + // Mixes in 'count' samples from 'buf_in' + void mix_samples( blip_sample_t const* buf_in, long count ); + + + // Signals that sound has been added to buffer. Could be done automatically in + // Blip_Synth, but that would affect performance more, as you can arrange that + // this is called only once per time frame rather than for every delta. + void set_modified() { modified_ = this; } + + // not documented yet + blip_ulong unsettled() const; + Blip_Buffer* clear_modified() { Blip_Buffer* b = modified_; modified_ = 0; return b; } + void remove_silence( long count ); + typedef blip_ulong blip_resampled_time_t; + blip_resampled_time_t resampled_duration( int t ) const { return t * factor_; } + blip_resampled_time_t resampled_time( blip_time_t t ) const { return t * factor_ + offset_; } + blip_resampled_time_t clock_rate_factor( long clock_rate ) const; +public: + Blip_Buffer(); + ~Blip_Buffer(); + + // Deprecated + typedef blip_resampled_time_t resampled_time_t; + blargg_err_t sample_rate( long r ) { return set_sample_rate( r ); } + blargg_err_t sample_rate( long r, int msec ) { return set_sample_rate( r, msec ); } +private: + // noncopyable + Blip_Buffer( const Blip_Buffer& ); + Blip_Buffer& operator = ( const Blip_Buffer& ); +public: + typedef blip_long buf_t_; + blip_ulong factor_; + blip_resampled_time_t offset_; + buf_t_* buffer_; + blip_long buffer_size_; + blip_long reader_accum_; + int bass_shift_; +private: + long sample_rate_; + long clock_rate_; + int bass_freq_; + int length_; + Blip_Buffer* modified_; // non-zero = true (more optimal than using bool, heh) + friend class Blip_Reader; +}; + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +// Number of bits in resample ratio fraction. Higher values give a more accurate ratio +// but reduce maximum buffer size. +#ifndef BLIP_BUFFER_ACCURACY + #define BLIP_BUFFER_ACCURACY 16 +#endif + +// Number bits in phase offset. Fewer than 6 bits (64 phase offsets) results in +// noticeable broadband noise when synthesizing high frequency square waves. +// Affects size of Blip_Synth objects since they store the waveform directly. +#ifndef BLIP_PHASE_BITS + #if BLIP_BUFFER_FAST + #define BLIP_PHASE_BITS 8 + #else + #define BLIP_PHASE_BITS 6 + #endif +#endif + + // Internal + typedef blip_ulong blip_resampled_time_t; + int const blip_widest_impulse_ = 16; + int const blip_buffer_extra_ = blip_widest_impulse_ + 2; + int const blip_res = 1 << BLIP_PHASE_BITS; + class blip_eq_t; + + class Blip_Synth_Fast_ { + public: + Blip_Buffer* buf; + int last_amp; + int delta_factor; + + void volume_unit( double ); + Blip_Synth_Fast_(); + void treble_eq( blip_eq_t const& ) { } + }; + + class Blip_Synth_ { + public: + Blip_Buffer* buf; + int last_amp; + int delta_factor; + + void volume_unit( double ); + Blip_Synth_( short* impulses, int width ); + void treble_eq( blip_eq_t const& ); + private: + double volume_unit_; + short* const impulses; + int const width; + blip_long kernel_unit; + int impulses_size() const { return blip_res / 2 * width + 1; } + void adjust_impulse(); + }; + +// Quality level, better = slower. In general, use blip_good_quality. +const int blip_med_quality = 8; +const int blip_good_quality = 12; +const int blip_high_quality = 16; + +// Range specifies the greatest expected change in amplitude. Calculate it +// by finding the difference between the maximum and minimum expected +// amplitudes (max - min). +template +class Blip_Synth { +public: + // Sets overall volume of waveform + void volume( double v ) { impl.volume_unit( v * (1.0 / (range < 0 ? -range : range)) ); } + + // Configures low-pass filter (see blip_buffer.txt) + void treble_eq( blip_eq_t const& eq ) { impl.treble_eq( eq ); } + + // Gets/sets Blip_Buffer used for output + Blip_Buffer* output() const { return impl.buf; } + void output( Blip_Buffer* b ) { impl.buf = b; impl.last_amp = 0; } + + // Updates amplitude of waveform at given time. Using this requires a separate + // Blip_Synth for each waveform. + void update( blip_time_t time, int amplitude ); + +// Low-level interface + + // Adds an amplitude transition of specified delta, optionally into specified buffer + // rather than the one set with output(). Delta can be positive or negative. + // The actual change in amplitude is delta * (volume / range) + void offset( blip_time_t, int delta, Blip_Buffer* ) const; + void offset( blip_time_t t, int delta ) const { offset( t, delta, impl.buf ); } + + // Works directly in terms of fractional output samples. Contact author for more info. + void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const; + + // Same as offset(), except code is inlined for higher performance + void offset_inline( blip_time_t t, int delta, Blip_Buffer* buf ) const { + offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); + } + void offset_inline( blip_time_t t, int delta ) const { + offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf ); + } + +private: +#if BLIP_BUFFER_FAST + Blip_Synth_Fast_ impl; +#else + Blip_Synth_ impl; + typedef short imp_t; + imp_t impulses [blip_res * (quality / 2) + 1]; +public: + Blip_Synth() : impl( impulses, quality ) { } +#endif +}; + +// Low-pass equalization parameters +class blip_eq_t { +public: + // Logarithmic rolloff to treble dB at half sampling rate. Negative values reduce + // treble, small positive values (0 to 5.0) increase treble. + blip_eq_t( double treble_db = 0 ); + + // See blip_buffer.txt + blip_eq_t( double treble, long rolloff_freq, long sample_rate, long cutoff_freq = 0 ); + +private: + double treble; + long rolloff_freq; + long sample_rate; + long cutoff_freq; + void generate( float* out, int count ) const; + friend class Blip_Synth_; +}; + +int const blip_sample_bits = 30; + +// Dummy Blip_Buffer to direct sound output to, for easy muting without +// having to stop sound code. +class Silent_Blip_Buffer : public Blip_Buffer { + buf_t_ buf [blip_buffer_extra_ + 1]; +public: + // The following cannot be used (an assertion will fail if attempted): + blargg_err_t set_sample_rate( long samples_per_sec, int msec_length ); + blip_time_t count_clocks( long count ) const; + void mix_samples( blip_sample_t const* buf, long count ); + + Silent_Blip_Buffer(); +}; + + #if __GNUC__ >= 3 || _MSC_VER >= 1100 + #define BLIP_RESTRICT __restrict + #else + #define BLIP_RESTRICT + #endif + +// Optimized reading from Blip_Buffer, for use in custom sample output + +// Begins reading from buffer. Name should be unique to the current block. +#define BLIP_READER_BEGIN( name, blip_buffer ) \ + const Blip_Buffer::buf_t_* BLIP_RESTRICT name##_reader_buf = (blip_buffer).buffer_;\ + blip_long name##_reader_accum = (blip_buffer).reader_accum_ + +// Gets value to pass to BLIP_READER_NEXT() +#define BLIP_READER_BASS( blip_buffer ) ((blip_buffer).bass_shift_) + +// Constant value to use instead of BLIP_READER_BASS(), for slightly more optimal +// code at the cost of having no bass control +int const blip_reader_default_bass = 9; + +// Current sample +#define BLIP_READER_READ( name ) (name##_reader_accum >> (blip_sample_bits - 16)) + +// Current raw sample in full internal resolution +#define BLIP_READER_READ_RAW( name ) (name##_reader_accum) + +// Advances to next sample +#define BLIP_READER_NEXT( name, bass ) \ + (void) (name##_reader_accum += *name##_reader_buf++ - (name##_reader_accum >> (bass))) + +// Ends reading samples from buffer. The number of samples read must now be removed +// using Blip_Buffer::remove_samples(). +#define BLIP_READER_END( name, blip_buffer ) \ + (void) ((blip_buffer).reader_accum_ = name##_reader_accum) + + +// experimental +#define BLIP_READER_ADJ_( name, offset ) (name##_reader_buf += offset) + +blip_long const blip_reader_idx_factor = sizeof (Blip_Buffer::buf_t_); + +#define BLIP_READER_NEXT_IDX_( name, bass, idx ) {\ + name##_reader_accum -= name##_reader_accum >> (bass);\ + name##_reader_accum += name##_reader_buf [(idx)];\ +} + +#define BLIP_READER_NEXT_RAW_IDX_( name, bass, idx ) {\ + name##_reader_accum -= name##_reader_accum >> (bass);\ + name##_reader_accum +=\ + *(Blip_Buffer::buf_t_ const*) ((char const*) name##_reader_buf + (idx));\ +} + +// Compatibility with older version +const long blip_unscaled = 65535; +const int blip_low_quality = blip_med_quality; +const int blip_best_quality = blip_high_quality; + +// Deprecated; use BLIP_READER macros as follows: +// Blip_Reader r; r.begin( buf ); -> BLIP_READER_BEGIN( r, buf ); +// int bass = r.begin( buf ) -> BLIP_READER_BEGIN( r, buf ); int bass = BLIP_READER_BASS( buf ); +// r.read() -> BLIP_READER_READ( r ) +// r.read_raw() -> BLIP_READER_READ_RAW( r ) +// r.next( bass ) -> BLIP_READER_NEXT( r, bass ) +// r.next() -> BLIP_READER_NEXT( r, blip_reader_default_bass ) +// r.end( buf ) -> BLIP_READER_END( r, buf ) +class Blip_Reader { +public: + int begin( Blip_Buffer& ); + blip_long read() const { return accum >> (blip_sample_bits - 16); } + blip_long read_raw() const { return accum; } + void next( int bass_shift = 9 ) { accum += *buf++ - (accum >> bass_shift); } + void end( Blip_Buffer& b ) { b.reader_accum_ = accum; } +private: + const Blip_Buffer::buf_t_* buf; + blip_long accum; +}; + +#if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \ + defined (__x86_64__) || defined (__ia64__) || defined (__i386__) + #define BLIP_CLAMP_( in ) in < -0x8000 || 0x7FFF < in +#else + #define BLIP_CLAMP_( in ) (blip_sample_t) in != in +#endif + +// Clamp sample to blip_sample_t range +#define BLIP_CLAMP( sample, out )\ + { if ( BLIP_CLAMP_( (sample) ) ) (out) = ((sample) >> 24) ^ 0x7FFF; } + +struct blip_buffer_state_t +{ + blip_resampled_time_t offset_; + blip_long reader_accum_; + blip_long buf [blip_buffer_extra_]; +}; + +// End of public interface + +#ifndef assert + #include +#endif + +template +inline void Blip_Synth::offset_resampled( blip_resampled_time_t time, + int delta, Blip_Buffer* blip_buf ) const +{ + // If this assertion fails, it means that an attempt was made to add a delta + // at a negative time or past the end of the buffer. + assert( (blip_long) (time >> BLIP_BUFFER_ACCURACY) < blip_buf->buffer_size_ ); + + delta *= impl.delta_factor; + blip_long* BLIP_RESTRICT buf = blip_buf->buffer_ + (time >> BLIP_BUFFER_ACCURACY); + int phase = (int) (time >> (BLIP_BUFFER_ACCURACY - BLIP_PHASE_BITS) & (blip_res - 1)); + +#if BLIP_BUFFER_FAST + blip_long left = buf [0] + delta; + + // Kind of crappy, but doing shift after multiply results in overflow. + // Alternate way of delaying multiply by delta_factor results in worse + // sub-sample resolution. + blip_long right = (delta >> BLIP_PHASE_BITS) * phase; + left -= right; + right += buf [1]; + + buf [0] = left; + buf [1] = right; +#else + + int const fwd = (blip_widest_impulse_ - quality) / 2; + int const rev = fwd + quality - 2; + int const mid = quality / 2 - 1; + + imp_t const* BLIP_RESTRICT imp = impulses + blip_res - phase; + + #if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \ + defined (__x86_64__) || defined (__ia64__) || defined (__i386__) + + // this straight forward version gave in better code on GCC for x86 + + #define ADD_IMP( out, in ) \ + buf [out] += (blip_long) imp [blip_res * (in)] * delta + + #define BLIP_FWD( i ) {\ + ADD_IMP( fwd + i, i );\ + ADD_IMP( fwd + 1 + i, i + 1 );\ + } + #define BLIP_REV( r ) {\ + ADD_IMP( rev - r, r + 1 );\ + ADD_IMP( rev + 1 - r, r );\ + } + + BLIP_FWD( 0 ) + if ( quality > 8 ) BLIP_FWD( 2 ) + if ( quality > 12 ) BLIP_FWD( 4 ) + { + ADD_IMP( fwd + mid - 1, mid - 1 ); + ADD_IMP( fwd + mid , mid ); + imp = impulses + phase; + } + if ( quality > 12 ) BLIP_REV( 6 ) + if ( quality > 8 ) BLIP_REV( 4 ) + BLIP_REV( 2 ) + + ADD_IMP( rev , 1 ); + ADD_IMP( rev + 1, 0 ); + + #undef ADD_IMP + + #else + + // for RISC processors, help compiler by reading ahead of writes + + #define BLIP_FWD( i ) {\ + blip_long t0 = i0 * delta + buf [fwd + i];\ + blip_long t1 = imp [blip_res * (i + 1)] * delta + buf [fwd + 1 + i];\ + i0 = imp [blip_res * (i + 2)];\ + buf [fwd + i] = t0;\ + buf [fwd + 1 + i] = t1;\ + } + #define BLIP_REV( r ) {\ + blip_long t0 = i0 * delta + buf [rev - r];\ + blip_long t1 = imp [blip_res * r] * delta + buf [rev + 1 - r];\ + i0 = imp [blip_res * (r - 1)];\ + buf [rev - r] = t0;\ + buf [rev + 1 - r] = t1;\ + } + + blip_long i0 = *imp; + BLIP_FWD( 0 ) + if ( quality > 8 ) BLIP_FWD( 2 ) + if ( quality > 12 ) BLIP_FWD( 4 ) + { + blip_long t0 = i0 * delta + buf [fwd + mid - 1]; + blip_long t1 = imp [blip_res * mid] * delta + buf [fwd + mid ]; + imp = impulses + phase; + i0 = imp [blip_res * mid]; + buf [fwd + mid - 1] = t0; + buf [fwd + mid ] = t1; + } + if ( quality > 12 ) BLIP_REV( 6 ) + if ( quality > 8 ) BLIP_REV( 4 ) + BLIP_REV( 2 ) + + blip_long t0 = i0 * delta + buf [rev ]; + blip_long t1 = *imp * delta + buf [rev + 1]; + buf [rev ] = t0; + buf [rev + 1] = t1; + #endif + +#endif +} + +#undef BLIP_FWD +#undef BLIP_REV + +template +#if BLIP_BUFFER_FAST + inline +#endif +void Blip_Synth::offset( blip_time_t t, int delta, Blip_Buffer* buf ) const +{ + offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); +} + +template +#if BLIP_BUFFER_FAST + inline +#endif +void Blip_Synth::update( blip_time_t t, int amp ) +{ + int delta = amp - impl.last_amp; + impl.last_amp = amp; + offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf ); +} + +inline blip_eq_t::blip_eq_t( double t ) : + treble( t ), rolloff_freq( 0 ), sample_rate( 44100 ), cutoff_freq( 0 ) { } +inline blip_eq_t::blip_eq_t( double t, long rf, long sr, long cf ) : + treble( t ), rolloff_freq( rf ), sample_rate( sr ), cutoff_freq( cf ) { } + +inline int Blip_Buffer::length() const { return length_; } +inline long Blip_Buffer::samples_avail() const { return (long) (offset_ >> BLIP_BUFFER_ACCURACY); } +inline long Blip_Buffer::sample_rate() const { return sample_rate_; } +inline int Blip_Buffer::output_latency() const { return blip_widest_impulse_ / 2; } +inline long Blip_Buffer::clock_rate() const { return clock_rate_; } +inline void Blip_Buffer::clock_rate( long cps ) { factor_ = clock_rate_factor( clock_rate_ = cps ); } + +inline int Blip_Reader::begin( Blip_Buffer& blip_buf ) +{ + buf = blip_buf.buffer_; + accum = blip_buf.reader_accum_; + return blip_buf.bass_shift_; +} + +inline void Blip_Buffer::remove_silence( long count ) +{ + // fails if you try to remove more samples than available + assert( count <= samples_avail() ); + offset_ -= (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY; +} + +inline blip_ulong Blip_Buffer::unsettled() const +{ + return reader_accum_ >> (blip_sample_bits - 16); +} + +int const blip_max_length = 0; +int const blip_default_length = 250; // 1/4 second + +#endif diff --git a/src/apu/Effects_Buffer.cpp b/src/apu/Effects_Buffer.cpp new file mode 100644 index 0000000..3e04966 --- /dev/null +++ b/src/apu/Effects_Buffer.cpp @@ -0,0 +1,638 @@ +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Effects_Buffer.h" + +#include + +/* Copyright (C) 2006-2007 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +int const fixed_shift = 12; +#define TO_FIXED( f ) fixed_t ((f) * ((fixed_t) 1 << fixed_shift)) +#define FROM_FIXED( f ) ((f) >> fixed_shift) + +int const max_read = 2560; // determines minimum delay + +Effects_Buffer::Effects_Buffer( int max_bufs, long echo_size_ ) : Multi_Buffer( stereo ) +{ + echo_size = max( max_read * (long) stereo, echo_size_ & ~1 ); + clock_rate_ = 0; + bass_freq_ = 90; + bufs = 0; + bufs_size = 0; + bufs_max = max( max_bufs, (int) extra_chans ); + no_echo = true; + no_effects = true; + + // defaults + config_.enabled = false; + config_.delay [0] = 120; + config_.delay [1] = 122; + config_.feedback = 0.2f; + config_.treble = 0.4f; + + static float const sep = 0.8f; + config_.side_chans [0].pan = -sep; + config_.side_chans [1].pan = +sep; + config_.side_chans [0].vol = 1.0f; + config_.side_chans [1].vol = 1.0f; + + memset( &s, 0, sizeof s ); + clear(); +} + +Effects_Buffer::~Effects_Buffer() +{ + delete_bufs(); +} + +// avoid using new [] +blargg_err_t Effects_Buffer::new_bufs( int size ) +{ + bufs = (buf_t*) malloc( size * sizeof *bufs ); + CHECK_ALLOC( bufs ); + for ( int i = 0; i < size; i++ ) + new (bufs + i) buf_t; + bufs_size = size; + return 0; +} + +void Effects_Buffer::delete_bufs() +{ + if ( bufs ) + { + for ( int i = bufs_size; --i >= 0; ) + bufs [i].~buf_t(); + free( bufs ); + bufs = 0; + } + bufs_size = 0; +} + +blargg_err_t Effects_Buffer::set_sample_rate( long rate, int msec ) +{ + // extra to allow farther past-the-end pointers + mixer.samples_read = 0; + RETURN_ERR( echo.resize( echo_size + stereo ) ); + return Multi_Buffer::set_sample_rate( rate, msec ); +} + +void Effects_Buffer::clock_rate( long rate ) +{ + clock_rate_ = rate; + for ( int i = bufs_size; --i >= 0; ) + bufs [i].clock_rate( clock_rate_ ); +} + +void Effects_Buffer::bass_freq( int freq ) +{ + bass_freq_ = freq; + for ( int i = bufs_size; --i >= 0; ) + bufs [i].bass_freq( bass_freq_ ); +} + +blargg_err_t Effects_Buffer::set_channel_count( int count, int const* types ) +{ + RETURN_ERR( Multi_Buffer::set_channel_count( count, types ) ); + + delete_bufs(); + + mixer.samples_read = 0; + + RETURN_ERR( chans.resize( count + extra_chans ) ); + + RETURN_ERR( new_bufs( min( bufs_max, count + extra_chans ) ) ); + + for ( int i = bufs_size; --i >= 0; ) + RETURN_ERR( bufs [i].set_sample_rate( sample_rate(), length() ) ); + + for ( int i = chans.size(); --i >= 0; ) + { + chan_t& ch = chans [i]; + ch.cfg.vol = 1.0f; + ch.cfg.pan = 0.0f; + ch.cfg.surround = false; + ch.cfg.echo = false; + } + // side channels with echo + chans [2].cfg.echo = true; + chans [3].cfg.echo = true; + + clock_rate( clock_rate_ ); + bass_freq( bass_freq_ ); + apply_config(); + clear(); + + return 0; +} + +void Effects_Buffer::clear_echo() +{ + if ( echo.size() ) + memset( echo.begin(), 0, echo.size() * sizeof echo [0] ); +} + +void Effects_Buffer::clear() +{ + echo_pos = 0; + s.low_pass [0] = 0; + s.low_pass [1] = 0; + mixer.samples_read = 0; + + for ( int i = bufs_size; --i >= 0; ) + bufs [i].clear(); + clear_echo(); +} + +Effects_Buffer::channel_t Effects_Buffer::channel( int i ) +{ + i += extra_chans; + require( extra_chans <= i && i < (int) chans.size() ); + return chans [i].channel; +} + + +// Configuration + +// 3 wave positions with/without surround, 2 multi (one with same config as wave) +int const simple_bufs = 3 * 2 + 2 - 1; + +Simple_Effects_Buffer::Simple_Effects_Buffer() : + Effects_Buffer( extra_chans + simple_bufs, 18 * 1024L ) +{ + config_.echo = 0.20f; + config_.stereo = 0.20f; + config_.surround = true; + config_.enabled = false; +} + +void Simple_Effects_Buffer::apply_config() +{ + Effects_Buffer::config_t& c = Effects_Buffer::config(); + + c.enabled = config_.enabled; + if ( c.enabled ) + { + c.delay [0] = 120; + c.delay [1] = 122; + c.feedback = config_.echo * 0.7f; + c.treble = 0.6f - 0.3f * config_.echo; + + float sep = config_.stereo + 0.80f; + if ( sep > 1.0f ) + sep = 1.0f; + + c.side_chans [0].pan = -sep; + c.side_chans [1].pan = +sep; + + for ( int i = channel_count(); --i >= 0; ) + { + chan_config_t& ch = Effects_Buffer::chan_config( i ); + + ch.pan = 0.0f; + ch.surround = config_.surround; + ch.echo = false; + + int const type = (channel_types() ? channel_types() [i] : 0); + if ( !(type & noise_type) ) + { + int index = (type & type_index_mask) % 6 - 3; + if ( index < 0 ) + { + index += 3; + ch.surround = false; + ch.echo = true; + } + if ( index >= 1 ) + { + ch.pan = config_.stereo; + if ( index == 1 ) + ch.pan = -ch.pan; + } + } + else if ( type & 1 ) + { + ch.surround = false; + } + } + } + + Effects_Buffer::apply_config(); +} + +int Effects_Buffer::min_delay() const +{ + require( sample_rate() ); + return max_read * 1000L / sample_rate(); +} + +int Effects_Buffer::max_delay() const +{ + require( sample_rate() ); + return (echo_size / stereo - max_read) * 1000L / sample_rate(); +} + +void Effects_Buffer::apply_config() +{ + int i; + + if ( !bufs_size ) + return; + + s.treble = TO_FIXED( config_.treble ); + + bool echo_dirty = false; + + fixed_t old_feedback = s.feedback; + s.feedback = TO_FIXED( config_.feedback ); + if ( !old_feedback && s.feedback ) + echo_dirty = true; + + // delays + for ( i = stereo; --i >= 0; ) + { + long delay = config_.delay [i] * sample_rate() / 1000 * stereo; + delay = max( delay, long (max_read * stereo) ); + delay = min( delay, long (echo_size - max_read * stereo) ); + if ( s.delay [i] != delay ) + { + s.delay [i] = delay; + echo_dirty = true; + } + } + + // side channels + for ( i = 2; --i >= 0; ) + { + chans [i+2].cfg.vol = chans [i].cfg.vol = config_.side_chans [i].vol * 0.5f; + chans [i+2].cfg.pan = chans [i].cfg.pan = config_.side_chans [i].pan; + } + + // convert volumes + for ( i = chans.size(); --i >= 0; ) + { + chan_t& ch = chans [i]; + ch.vol [0] = TO_FIXED( ch.cfg.vol - ch.cfg.vol * ch.cfg.pan ); + ch.vol [1] = TO_FIXED( ch.cfg.vol + ch.cfg.vol * ch.cfg.pan ); + if ( ch.cfg.surround ) + ch.vol [0] = -ch.vol [0]; + } + + assign_buffers(); + + // set side channels + for ( i = chans.size(); --i >= 0; ) + { + chan_t& ch = chans [i]; + ch.channel.left = chans [ch.cfg.echo*2 ].channel.center; + ch.channel.right = chans [ch.cfg.echo*2+1].channel.center; + } + + bool old_echo = !no_echo && !no_effects; + + // determine whether effects and echo are needed at all + no_effects = true; + no_echo = true; + for ( i = chans.size(); --i >= extra_chans; ) + { + chan_t& ch = chans [i]; + if ( ch.cfg.echo && s.feedback ) + no_echo = false; + + if ( ch.vol [0] != TO_FIXED( 1 ) || ch.vol [1] != TO_FIXED( 1 ) ) + no_effects = false; + } + if ( !no_echo ) + no_effects = false; + + if ( chans [0].vol [0] != TO_FIXED( 1 ) || + chans [0].vol [1] != TO_FIXED( 0 ) || + chans [1].vol [0] != TO_FIXED( 0 ) || + chans [1].vol [1] != TO_FIXED( 1 ) ) + no_effects = false; + + if ( !config_.enabled ) + no_effects = true; + + if ( no_effects ) + { + for ( i = chans.size(); --i >= 0; ) + { + chan_t& ch = chans [i]; + ch.channel.center = &bufs [2]; + ch.channel.left = &bufs [0]; + ch.channel.right = &bufs [1]; + } + } + + mixer.bufs [0] = &bufs [0]; + mixer.bufs [1] = &bufs [1]; + mixer.bufs [2] = &bufs [2]; + + if ( echo_dirty || (!old_echo && (!no_echo && !no_effects)) ) + clear_echo(); + + channels_changed(); +} + +void Effects_Buffer::assign_buffers() +{ + // assign channels to buffers + int buf_count = 0; + for ( int i = 0; i < (int) chans.size(); i++ ) + { + // put second two side channels at end to give priority to main channels + // in case closest matching is necessary + int x = i; + if ( i > 1 ) + x += 2; + if ( x >= (int) chans.size() ) + x -= (chans.size() - 2); + chan_t& ch = chans [x]; + + int b = 0; + for ( ; b < buf_count; b++ ) + { + if ( ch.vol [0] == bufs [b].vol [0] && + ch.vol [1] == bufs [b].vol [1] && + (ch.cfg.echo == bufs [b].echo || !s.feedback) ) + break; + } + + if ( b >= buf_count ) + { + if ( buf_count < bufs_max ) + { + bufs [b].vol [0] = ch.vol [0]; + bufs [b].vol [1] = ch.vol [1]; + bufs [b].echo = ch.cfg.echo; + buf_count++; + } + else + { + // TODO: this is a mess, needs refinement + dprintf( "Effects_Buffer ran out of buffers; using closest match\n" ); + b = 0; + fixed_t best_dist = TO_FIXED( 8 ); + for ( int h = buf_count; --h >= 0; ) + { + #define CALC_LEVELS( vols, sum, diff, surround ) \ + fixed_t sum, diff;\ + bool surround = false;\ + {\ + fixed_t vol_0 = vols [0];\ + if ( vol_0 < 0 ) vol_0 = -vol_0, surround = true;\ + fixed_t vol_1 = vols [1];\ + if ( vol_1 < 0 ) vol_1 = -vol_1, surround = true;\ + sum = vol_0 + vol_1;\ + diff = vol_0 - vol_1;\ + } + CALC_LEVELS( ch.vol, ch_sum, ch_diff, ch_surround ); + CALC_LEVELS( bufs [h].vol, buf_sum, buf_diff, buf_surround ); + + fixed_t dist = abs( ch_sum - buf_sum ) + abs( ch_diff - buf_diff ); + + if ( ch_surround != buf_surround ) + dist += TO_FIXED( 1 ) / 2; + + if ( s.feedback && ch.cfg.echo != bufs [h].echo ) + dist += TO_FIXED( 1 ) / 2; + + if ( best_dist > dist ) + { + best_dist = dist; + b = h; + } + } + } + } + + //dprintf( "ch %d->buf %d\n", x, b ); + ch.channel.center = &bufs [b]; + } +} + + +// Mixing + +void Effects_Buffer::end_frame( blip_time_t time ) +{ + for ( int i = bufs_size; --i >= 0; ) + bufs [i].end_frame( time ); +} + +long Effects_Buffer::read_samples( blip_sample_t* out, long out_size ) +{ + out_size = min( out_size, samples_avail() ); + + int pair_count = int (out_size >> 1); + require( pair_count * stereo == out_size ); // must read an even number of samples + if ( pair_count ) + { + if ( no_effects ) + { + mixer.read_pairs( out, pair_count ); + } + else + { + int pairs_remain = pair_count; + do + { + // mix at most max_read pairs at a time + int count = max_read; + if ( count > pairs_remain ) + count = pairs_remain; + + if ( no_echo ) + { + // optimization: clear echo here to keep mix_effects() a leaf function + echo_pos = 0; + memset( echo.begin(), 0, count * stereo * sizeof echo [0] ); + } + mix_effects( out, count ); + + blargg_long new_echo_pos = echo_pos + count * stereo; + if ( new_echo_pos >= echo_size ) + new_echo_pos -= echo_size; + echo_pos = new_echo_pos; + assert( echo_pos < echo_size ); + + out += count * stereo; + mixer.samples_read += count; + pairs_remain -= count; + } + while ( pairs_remain ); + } + + if ( samples_avail() <= 0 || immediate_removal() ) + { + for ( int i = bufs_size; --i >= 0; ) + { + buf_t& b = bufs [i]; + // TODO: might miss non-silence settling since it checks END of last read + if ( b.non_silent() ) + b.remove_samples( mixer.samples_read ); + else + b.remove_silence( mixer.samples_read ); + } + mixer.samples_read = 0; + } + } + return out_size; +} + +void Effects_Buffer::mix_effects( blip_sample_t* out_, int pair_count ) +{ + typedef fixed_t stereo_fixed_t [stereo]; + + // add channels with echo, do echo, add channels without echo, then convert to 16-bit and output + int echo_phase = 1; + do + { + // mix any modified buffers + { + buf_t* buf = bufs; + int bufs_remain = bufs_size; + do + { + if ( buf->non_silent() && ( buf->echo == !!echo_phase ) ) + { + stereo_fixed_t* BLIP_RESTRICT out = (stereo_fixed_t*) &echo [echo_pos]; + int const bass = BLIP_READER_BASS( *buf ); + BLIP_READER_BEGIN( in, *buf ); + BLIP_READER_ADJ_( in, mixer.samples_read ); + fixed_t const vol_0 = buf->vol [0]; + fixed_t const vol_1 = buf->vol [1]; + + int count = unsigned (echo_size - echo_pos) / stereo; + int remain = pair_count; + if ( count > remain ) + count = remain; + do + { + remain -= count; + BLIP_READER_ADJ_( in, count ); + + out += count; + int offset = -count; + do + { + fixed_t s = BLIP_READER_READ( in ); + BLIP_READER_NEXT_IDX_( in, bass, offset ); + + out [offset] [0] += s * vol_0; + out [offset] [1] += s * vol_1; + } + while ( ++offset ); + + out = (stereo_fixed_t*) echo.begin(); + count = remain; + } + while ( remain ); + + BLIP_READER_END( in, *buf ); + } + buf++; + } + while ( --bufs_remain ); + } + + // add echo + if ( echo_phase && !no_echo ) + { + fixed_t const feedback = s.feedback; + fixed_t const treble = s.treble; + + int i = 1; + do + { + fixed_t low_pass = s.low_pass [i]; + + fixed_t* echo_end = &echo [echo_size + i]; + fixed_t const* BLIP_RESTRICT in_pos = &echo [echo_pos + i]; + blargg_long out_offset = echo_pos + i + s.delay [i]; + if ( out_offset >= echo_size ) + out_offset -= echo_size; + assert( out_offset < echo_size ); + fixed_t* BLIP_RESTRICT out_pos = &echo [out_offset]; + + // break into up to three chunks to avoid having to handle wrap-around + // in middle of core loop + int remain = pair_count; + do + { + fixed_t const* pos = in_pos; + if ( pos < out_pos ) + pos = out_pos; + int count = blargg_ulong ((char*) echo_end - (char const*) pos) / + unsigned (stereo * sizeof (fixed_t)); + if ( count > remain ) + count = remain; + remain -= count; + + in_pos += count * stereo; + out_pos += count * stereo; + int offset = -count; + do + { + low_pass += FROM_FIXED( in_pos [offset * stereo] - low_pass ) * treble; + out_pos [offset * stereo] = FROM_FIXED( low_pass ) * feedback; + } + while ( ++offset ); + + if ( in_pos >= echo_end ) in_pos -= echo_size; + if ( out_pos >= echo_end ) out_pos -= echo_size; + } + while ( remain ); + + s.low_pass [i] = low_pass; + } + while ( --i >= 0 ); + } + } + while ( --echo_phase >= 0 ); + + // clamp to 16 bits + { + stereo_fixed_t const* BLIP_RESTRICT in = (stereo_fixed_t*) &echo [echo_pos]; + typedef blip_sample_t stereo_blip_sample_t [stereo]; + stereo_blip_sample_t* BLIP_RESTRICT out = (stereo_blip_sample_t*) out_; + int count = unsigned (echo_size - echo_pos) / (unsigned) stereo; + int remain = pair_count; + if ( count > remain ) + count = remain; + do + { + remain -= count; + in += count; + out += count; + int offset = -count; + do + { + fixed_t in_0 = FROM_FIXED( in [offset] [0] ); + fixed_t in_1 = FROM_FIXED( in [offset] [1] ); + + BLIP_CLAMP( in_0, in_0 ); + out [offset] [0] = (blip_sample_t) in_0; + + BLIP_CLAMP( in_1, in_1 ); + out [offset] [1] = (blip_sample_t) in_1; + } + while ( ++offset ); + + in = (stereo_fixed_t*) echo.begin(); + count = remain; + } + while ( remain ); + } +} diff --git a/src/apu/Effects_Buffer.h b/src/apu/Effects_Buffer.h new file mode 100644 index 0000000..790325d --- /dev/null +++ b/src/apu/Effects_Buffer.h @@ -0,0 +1,143 @@ +// Multi-channel effects buffer with echo and individual panning for each channel + +// Game_Music_Emu $vers +#ifndef EFFECTS_BUFFER_H +#define EFFECTS_BUFFER_H + +#include "Multi_Buffer.h" + +// See Simple_Effects_Buffer (below) for a simpler interface + +class Effects_Buffer : public Multi_Buffer { +public: + // To reduce memory usage, fewer buffers can be used (with a best-fit + // approach if there are too few), and maximum echo delay can be reduced + Effects_Buffer( int max_bufs = 32, long echo_size = 24 * 1024L ); + + struct pan_vol_t + { + float vol; // 0.0 = silent, 0.5 = half volume, 1.0 = normal + float pan; // -1.0 = left, 0.0 = center, +1.0 = right + }; + + // Global configuration + struct config_t + { + bool enabled; // false = disable all effects + + // Current sound is echoed at adjustable left/right delay, + // with reduced treble and volume (feedback). + float treble; // 1.0 = full treble, 0.1 = very little, 0.0 = silent + int delay [2]; // left, right delays (msec) + float feedback; // 0.0 = no echo, 0.5 = each echo half previous, 1.0 = cacophony + pan_vol_t side_chans [2]; // left and right side channel volume and pan + }; + config_t& config() { return config_; } + + // Limits of delay (msec) + int min_delay() const; + int max_delay() const; + + // Per-channel configuration. Two or more channels with matching parameters are + // optimized to internally use the same buffer. + struct chan_config_t : pan_vol_t + { + // (inherited from pan_vol_t) + //float vol; // these only affect center channel + //float pan; + bool surround; // if true, negates left volume to put sound in back + bool echo; // false = channel doesn't have any echo + }; + chan_config_t& chan_config( int i ) { return chans [i + extra_chans].cfg; } + + // Apply any changes made to config() and chan_config() + virtual void apply_config(); + +public: + ~Effects_Buffer(); + blargg_err_t set_sample_rate( long samples_per_sec, int msec = blip_default_length ); + blargg_err_t set_channel_count( int, int const* = 0 ); + void clock_rate( long ); + void bass_freq( int ); + void clear(); + channel_t channel( int ); + void end_frame( blip_time_t ); + long read_samples( blip_sample_t*, long ); + long samples_avail() const { return (bufs [0].samples_avail() - mixer.samples_read) * 2; } + enum { stereo = 2 }; + typedef blargg_long fixed_t; +protected: + enum { extra_chans = stereo * stereo }; +private: + config_t config_; + long clock_rate_; + int bass_freq_; + + blargg_long echo_size; + + struct chan_t + { + fixed_t vol [stereo]; + chan_config_t cfg; + channel_t channel; + }; + blargg_vector chans; + + struct buf_t : Tracked_Blip_Buffer + { + fixed_t vol [stereo]; + bool echo; + + void* operator new ( size_t, void* p ) { return p; } + void operator delete ( void* ) { } + + ~buf_t() { } + }; + buf_t* bufs; + int bufs_size; + int bufs_max; // bufs_size <= bufs_max, to limit memory usage + Stereo_Mixer mixer; + + struct { + long delay [stereo]; + fixed_t treble; + fixed_t feedback; + fixed_t low_pass [stereo]; + } s; + + blargg_vector echo; + blargg_long echo_pos; + + bool no_effects; + bool no_echo; + + void assign_buffers(); + void clear_echo(); + void mix_effects( blip_sample_t* out, int pair_count ); + blargg_err_t new_bufs( int size ); + void delete_bufs(); +}; + +// Simpler interface and lower memory usage +class Simple_Effects_Buffer : public Effects_Buffer { +public: + struct config_t + { + bool enabled; // false = disable all effects + float echo; // 0.0 = none, 1.0 = lots + float stereo; // 0.0 = channels in center, 1.0 = channels on left/right + bool surround; // true = put some channels in back + }; + config_t& config() { return config_; } + + // Apply any changes made to config() + void apply_config(); + +public: + Simple_Effects_Buffer(); +private: + config_t config_; + void chan_config(); // hide +}; + +#endif diff --git a/src/apu/Gb_Apu.cpp b/src/apu/Gb_Apu.cpp new file mode 100644 index 0000000..14004d5 --- /dev/null +++ b/src/apu/Gb_Apu.cpp @@ -0,0 +1,394 @@ +// Gb_Snd_Emu 0.2.0. http://www.slack.net/~ant/ + +#include "Gb_Apu.h" + +/* Copyright (C) 2003-2007 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +unsigned const vol_reg = 0xFF24; +unsigned const stereo_reg = 0xFF25; +unsigned const status_reg = 0xFF26; +unsigned const wave_ram = 0xFF30; + +int const power_mask = 0x80; + +void Gb_Apu::treble_eq( blip_eq_t const& eq ) +{ + good_synth.treble_eq( eq ); + med_synth .treble_eq( eq ); +} + +inline int Gb_Apu::calc_output( int osc ) const +{ + int bits = regs [stereo_reg - start_addr] >> osc; + return (bits >> 3 & 2) | (bits & 1); +} + +void Gb_Apu::set_output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right, int osc ) +{ + // Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL) + require( !center || (center && !left && !right) || (center && left && right) ); + require( (unsigned) osc <= osc_count ); // fails if you pass invalid osc index + + if ( !center || !left || !right ) + { + left = center; + right = center; + } + + int i = (unsigned) osc % osc_count; + do + { + Gb_Osc& o = *oscs [i]; + o.outputs [1] = right; + o.outputs [2] = left; + o.outputs [3] = center; + o.output = o.outputs [calc_output( i )]; + } + while ( ++i < osc ); +} + +void Gb_Apu::synth_volume( int iv ) +{ + double v = volume_ * 0.60 / osc_count / 15 /*steps*/ / 8 /*master vol range*/ * iv; + good_synth.volume( v ); + med_synth .volume( v ); +} + +void Gb_Apu::apply_volume() +{ + // TODO: Doesn't handle differing left and right volumes (panning). + // Not worth the complexity. + int data = regs [vol_reg - start_addr]; + int left = data >> 4 & 7; + int right = data & 7; + //if ( data & 0x88 ) dprintf( "Vin: %02X\n", data & 0x88 ); + //if ( left != right ) dprintf( "l: %d r: %d\n", left, right ); + synth_volume( max( left, right ) + 1 ); +} + +void Gb_Apu::volume( double v ) +{ + if ( volume_ != v ) + { + volume_ = v; + apply_volume(); + } +} + +void Gb_Apu::reset_regs() +{ + for ( int i = 0; i < 0x20; i++ ) + regs [i] = 0; + + square1.reset(); + square2.reset(); + wave .reset(); + noise .reset(); + + apply_volume(); +} + +void Gb_Apu::reset_lengths() +{ + square1.length_ctr = 64; + square2.length_ctr = 64; + wave .length_ctr = 256; + noise .length_ctr = 64; +} + +void Gb_Apu::reduce_clicks( bool reduce ) +{ + reduce_clicks_ = reduce; + + // Click reduction makes DAC off generate same output as volume 0 + int dac_off_amp = 0; + if ( reduce && wave.mode != mode_agb ) // AGB already eliminates clicks + dac_off_amp = -Gb_Osc::dac_bias; + + for ( int i = 0; i < osc_count; i++ ) + oscs [i]->dac_off_amp = dac_off_amp; + + // AGB always eliminates clicks on wave channel using same method + if ( wave.mode == mode_agb ) + wave.dac_off_amp = -Gb_Osc::dac_bias; +} + +void Gb_Apu::reset( mode_t mode, bool agb_wave ) +{ + // Hardware mode + if ( agb_wave ) + mode = mode_agb; // using AGB wave features implies AGB hardware + wave.agb_mask = agb_wave ? 0xFF : 0; + for ( int i = 0; i < osc_count; i++ ) + oscs [i]->mode = mode; + reduce_clicks( reduce_clicks_ ); + + // Reset state + frame_time = 0; + last_time = 0; + frame_phase = 0; + + reset_regs(); + reset_lengths(); + + // Load initial wave RAM + static byte const initial_wave [2] [16] = { + {0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C,0x60,0x59,0x59,0xB0,0x34,0xB8,0x2E,0xDA}, + {0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF}, + }; + for ( int b = 2; --b >= 0; ) + { + // Init both banks (does nothing if not in AGB mode) + // TODO: verify that this works + write_register( 0, 0xFF1A, b * 0x40 ); + for ( unsigned i = 0; i < sizeof initial_wave [0]; i++ ) + write_register( 0, i + wave_ram, initial_wave [(mode != mode_dmg)] [i] ); + } +} + +void Gb_Apu::set_tempo( double t ) +{ + frame_period = 4194304 / 512; // 512 Hz + if ( t != 1.0 ) + frame_period = blip_time_t (frame_period / t); +} + +Gb_Apu::Gb_Apu() +{ + wave.wave_ram = ®s [wave_ram - start_addr]; + + oscs [0] = &square1; + oscs [1] = &square2; + oscs [2] = &wave; + oscs [3] = &noise; + + for ( int i = osc_count; --i >= 0; ) + { + Gb_Osc& o = *oscs [i]; + o.regs = ®s [i * 5]; + o.output = 0; + o.outputs [0] = 0; + o.outputs [1] = 0; + o.outputs [2] = 0; + o.outputs [3] = 0; + o.good_synth = &good_synth; + o.med_synth = &med_synth; + } + + reduce_clicks_ = false; + set_tempo( 1.0 ); + volume_ = 1.0; + reset(); +} + +void Gb_Apu::run_until_( blip_time_t end_time ) +{ + while ( true ) + { + // run oscillators + blip_time_t time = end_time; + if ( time > frame_time ) + time = frame_time; + + square1.run( last_time, time ); + square2.run( last_time, time ); + wave .run( last_time, time ); + noise .run( last_time, time ); + last_time = time; + + if ( time == end_time ) + break; + + // run frame sequencer + frame_time += frame_period * Gb_Osc::clk_mul; + switch ( frame_phase++ ) + { + case 2: + case 6: + // 128 Hz + square1.clock_sweep(); + case 0: + case 4: + // 256 Hz + square1.clock_length(); + square2.clock_length(); + wave .clock_length(); + noise .clock_length(); + break; + + case 7: + // 64 Hz + frame_phase = 0; + square1.clock_envelope(); + square2.clock_envelope(); + noise .clock_envelope(); + } + } +} + +inline void Gb_Apu::run_until( blip_time_t time ) +{ + require( time >= last_time ); // end_time must not be before previous time + if ( time > last_time ) + run_until_( time ); +} + +void Gb_Apu::end_frame( blip_time_t end_time ) +{ + if ( end_time > last_time ) + run_until( end_time ); + + frame_time -= end_time; + assert( frame_time >= 0 ); + + last_time -= end_time; + assert( last_time >= 0 ); +} + +void Gb_Apu::silence_osc( Gb_Osc& o ) +{ + int delta = -o.last_amp; + if ( delta ) + { + o.last_amp = 0; + if ( o.output ) + { + o.output->set_modified(); + med_synth.offset( last_time, delta, o.output ); + } + } +} + +void Gb_Apu::apply_stereo() +{ + for ( int i = osc_count; --i >= 0; ) + { + Gb_Osc& o = *oscs [i]; + Blip_Buffer* out = o.outputs [calc_output( i )]; + if ( o.output != out ) + { + silence_osc( o ); + o.output = out; + } + } +} + +void Gb_Apu::write_register( blip_time_t time, unsigned addr, int data ) +{ + require( (unsigned) data < 0x100 ); + + int reg = addr - start_addr; + if ( (unsigned) reg >= register_count ) + { + require( false ); + return; + } + + if ( addr < status_reg && !(regs [status_reg - start_addr] & power_mask) ) + { + // Power is off + + // length counters can only be written in DMG mode + if ( wave.mode != mode_dmg || (reg != 1 && reg != 5+1 && reg != 10+1 && reg != 15+1) ) + return; + + if ( reg < 10 ) + data &= 0x3F; // clear square duty + } + + run_until( time ); + + if ( addr >= wave_ram ) + { + wave.write( addr, data ); + } + else + { + int old_data = regs [reg]; + regs [reg] = data; + + if ( addr < vol_reg ) + { + // Oscillator + write_osc( reg / 5, reg, old_data, data ); + } + else if ( addr == vol_reg && data != old_data ) + { + // Master volume + for ( int i = osc_count; --i >= 0; ) + silence_osc( *oscs [i] ); + + apply_volume(); + } + else if ( addr == stereo_reg ) + { + // Stereo panning + apply_stereo(); + } + else if ( addr == status_reg && (data ^ old_data) & power_mask ) + { + // Power control + frame_phase = 0; + for ( int i = osc_count; --i >= 0; ) + silence_osc( *oscs [i] ); + + reset_regs(); + if ( wave.mode != mode_dmg ) + reset_lengths(); + + regs [status_reg - start_addr] = data; + } + } +} + +int Gb_Apu::read_register( blip_time_t time, unsigned addr ) +{ + run_until( time ); + + int reg = addr - start_addr; + if ( (unsigned) reg >= register_count ) + { + require( false ); + return 0; + } + + if ( addr >= wave_ram ) + return wave.read( addr ); + + // Value read back has some bits always set + static byte const masks [] = { + 0x80,0x3F,0x00,0xFF,0xBF, + 0xFF,0x3F,0x00,0xFF,0xBF, + 0x7F,0xFF,0x9F,0xFF,0xBF, + 0xFF,0xFF,0x00,0x00,0xBF, + 0x00,0x00,0x70, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + }; + int mask = masks [reg]; + if ( wave.agb_mask && (reg == 10 || reg == 12) ) + mask = 0x1F; // extra implemented bits in wave regs on AGB + int data = regs [reg] | mask; + + // Status register + if ( addr == status_reg ) + { + data &= 0xF0; + data |= (int) square1.enabled << 0; + data |= (int) square2.enabled << 1; + data |= (int) wave .enabled << 2; + data |= (int) noise .enabled << 3; + } + + return data; +} diff --git a/src/apu/Gb_Apu.h b/src/apu/Gb_Apu.h new file mode 100644 index 0000000..3f13209 --- /dev/null +++ b/src/apu/Gb_Apu.h @@ -0,0 +1,182 @@ +// Nintendo Game Boy sound hardware emulator with save state support + +// Gb_Snd_Emu 0.2.0 +#ifndef GB_APU_H +#define GB_APU_H + +#include "Gb_Oscs.h" + +struct gb_apu_state_t; + +class Gb_Apu { +public: +// Basics + + // Clock rate that sound hardware runs at. + enum { clock_rate = 4194304 * GB_APU_OVERCLOCK }; + + // Sets buffer(s) to generate sound into. If left and right are NULL, output is mono. + // If all are NULL, no output is generated but other emulation still runs. + // If chan is specified, only that channel's output is changed, otherwise all are. + enum { osc_count = 4 }; // 0: Square 1, 1: Square 2, 2: Wave, 3: Noise + void set_output( Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL, + int chan = osc_count ); + + // Resets hardware to initial power on state BEFORE boot ROM runs. Mode selects + // sound hardware. Additional AGB wave features are enabled separately. + enum mode_t { + mode_dmg, // Game Boy monochrome + mode_cgb, // Game Boy Color + mode_agb // Game Boy Advance + }; + void reset( mode_t mode = mode_cgb, bool agb_wave = false ); + + // Reads and writes must be within the start_addr to end_addr range, inclusive. + // Addresses outside this range are not mapped to the sound hardware. + enum { start_addr = 0xFF10 }; + enum { end_addr = 0xFF3F }; + enum { register_count = end_addr - start_addr + 1 }; + + // Times are specified as the number of clocks since the beginning of the + // current time frame. + + // Emulates CPU write of data to addr at specified time. + void write_register( blip_time_t time, unsigned addr, int data ); + + // Emulates CPU read from addr at specified time. + int read_register( blip_time_t time, unsigned addr ); + + // Emulates sound hardware up to specified time, ends current time frame, then + // starts a new frame at time 0. + void end_frame( blip_time_t frame_length ); + +// Sound adjustments + + // Sets overall volume, where 1.0 is normal. + void volume( double ); + + // If true, reduces clicking by disabling DAC biasing. Note that this reduces + // emulation accuracy, since the clicks are authentic. + void reduce_clicks( bool reduce = true ); + + // Sets treble equalization. + void treble_eq( blip_eq_t const& ); + + // Treble and bass values for various hardware. + enum { + speaker_treble = -47, // speaker on system + speaker_bass = 2000, + dmg_treble = 0, // headphones on each system + dmg_bass = 30, + cgb_treble = 0, + cgb_bass = 300, // CGB has much less bass + agb_treble = 0, + agb_bass = 30 + }; + + // Sets frame sequencer rate, where 1.0 is normal. Meant for adjusting the + // tempo in a game music player. + void set_tempo( double ); + +// Save states + + // Saves full emulation state to state_out. Data format is portable and + // includes some extra space to avoid expansion in case more state needs + // to be stored in the future. + void save_state( gb_apu_state_t* state_out ); + + // Loads state. You should call reset() BEFORE this. + blargg_err_t load_state( gb_apu_state_t const& in ); + +public: + Gb_Apu(); + + // Use set_output() in place of these + BLARGG_DEPRECATED void output ( Blip_Buffer* c ) { set_output( c, c, c ); } + BLARGG_DEPRECATED void output ( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) { set_output( c, l, r ); } + BLARGG_DEPRECATED void osc_output( int i, Blip_Buffer* c ) { set_output( c, c, c, i ); } + BLARGG_DEPRECATED void osc_output( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) { set_output( c, l, r, i ); } + +private: + // noncopyable + Gb_Apu( const Gb_Apu& ); + Gb_Apu& operator = ( const Gb_Apu& ); + + Gb_Osc* oscs [osc_count]; + blip_time_t last_time; // time sound emulator has been run to + blip_time_t frame_period; // clocks between each frame sequencer step + double volume_; + bool reduce_clicks_; + + Gb_Sweep_Square square1; + Gb_Square square2; + Gb_Wave wave; + Gb_Noise noise; + blip_time_t frame_time; // time of next frame sequencer action + int frame_phase; // phase of next frame sequencer step + enum { regs_size = register_count + 0x10 }; + BOOST::uint8_t regs [regs_size];// last values written to registers + + // large objects after everything else + Gb_Osc::Good_Synth good_synth; + Gb_Osc::Med_Synth med_synth; + + void reset_lengths(); + void reset_regs(); + int calc_output( int osc ) const; + void apply_stereo(); + void apply_volume(); + void synth_volume( int ); + void run_until_( blip_time_t ); + void run_until( blip_time_t ); + void silence_osc( Gb_Osc& ); + void write_osc( int index, int reg, int old_data, int data ); + const char* save_load( gb_apu_state_t*, bool save ); + void save_load2( gb_apu_state_t*, bool save ); + friend class Gb_Apu_Tester; +}; + +// Format of save state. Should be stable across versions of the library, +// with earlier versions properly opening later save states. Includes some +// room for expansion so the state size shouldn't increase. +struct gb_apu_state_t +{ +#if GB_APU_CUSTOM_STATE + // Values stored as plain int so your code can read/write them easily. + // Structure can NOT be written to disk, since format is not portable. + typedef int val_t; +#else + // Values written in portable little-endian format, allowing structure + // to be written directly to disk. + typedef unsigned char val_t [4]; +#endif + + enum { format0 = 0x50414247 }; + + val_t format; // format of all following data + val_t version; // later versions just add fields to end + + unsigned char regs [0x40]; + val_t frame_time; + val_t frame_phase; + + val_t sweep_freq; + val_t sweep_delay; + val_t sweep_enabled; + val_t sweep_neg; + val_t noise_divider; + val_t wave_buf; + + val_t delay [4]; + val_t length_ctr [4]; + val_t phase [4]; + val_t enabled [4]; + + val_t env_delay [3]; + val_t env_volume [3]; + val_t env_enabled [3]; + + val_t unused [13]; // for future expansion +}; + +#endif diff --git a/src/apu/Gb_Apu_State.cpp b/src/apu/Gb_Apu_State.cpp new file mode 100644 index 0000000..6a52346 --- /dev/null +++ b/src/apu/Gb_Apu_State.cpp @@ -0,0 +1,119 @@ +// Gb_Snd_Emu $vers. http://www.slack.net/~ant/ + +#include "Gb_Apu.h" + +#include + +/* Copyright (C) 2007 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +#if GB_APU_CUSTOM_STATE + #define REFLECT( x, y ) (save ? (io->y) = (x) : (x) = (io->y) ) +#else + #define REFLECT( x, y ) (save ? set_val( io->y, x ) : (void) ((x) = get_val( io->y ))) + + static blargg_ulong get_val( byte const* p ) + { + return p [3] * 0x1000000 + p [2] * 0x10000 + p [1] * 0x100 + p [0]; + } + + static void set_val( byte* p, blargg_ulong n ) + { + p [0] = (byte) (n ); + p [1] = (byte) (n >> 8); + p [2] = (byte) (n >> 16); + p [3] = (byte) (n >> 24); + } +#endif + +inline const char* Gb_Apu::save_load( gb_apu_state_t* io, bool save ) +{ + #if !GB_APU_CUSTOM_STATE + assert( sizeof (gb_apu_state_t) == 256 ); + #endif + + int format = io->format0; + REFLECT( format, format ); + if ( format != io->format0 ) + return "Unsupported sound save state format"; + + int version = 0; + REFLECT( version, version ); + + // Registers and wave RAM + assert( regs_size == sizeof io->regs ); + if ( save ) + memcpy( io->regs, regs, sizeof io->regs ); + else + memcpy( regs, io->regs, sizeof regs ); + + // Frame sequencer + REFLECT( frame_time, frame_time ); + REFLECT( frame_phase, frame_phase ); + + REFLECT( square1.sweep_freq, sweep_freq ); + REFLECT( square1.sweep_delay, sweep_delay ); + REFLECT( square1.sweep_enabled, sweep_enabled ); + REFLECT( square1.sweep_neg, sweep_neg ); + + REFLECT( noise.divider, noise_divider ); + REFLECT( wave.sample_buf, wave_buf ); + + return 0; +} + +// second function to avoid inline limits of some compilers +inline void Gb_Apu::save_load2( gb_apu_state_t* io, bool save ) +{ + for ( int i = osc_count; --i >= 0; ) + { + Gb_Osc& osc = *oscs [i]; + REFLECT( osc.delay, delay [i] ); + REFLECT( osc.length_ctr, length_ctr [i] ); + REFLECT( osc.phase, phase [i] ); + REFLECT( osc.enabled, enabled [i] ); + + if ( i != 2 ) + { + int j = min( i, 2 ); + Gb_Env& env = STATIC_CAST(Gb_Env&,osc); + REFLECT( env.env_delay, env_delay [j] ); + REFLECT( env.volume, env_volume [j] ); + REFLECT( env.env_enabled, env_enabled [j] ); + } + } +} + +void Gb_Apu::save_state( gb_apu_state_t* out ) +{ + (void) save_load( out, true ); + save_load2( out, true ); + + #if !GB_APU_CUSTOM_STATE + memset( out->unused, 0, sizeof out->unused ); + #endif +} + +blargg_err_t Gb_Apu::load_state( gb_apu_state_t const& in ) +{ + RETURN_ERR( save_load( CONST_CAST(gb_apu_state_t*,&in), false ) ); + save_load2( CONST_CAST(gb_apu_state_t*,&in), false ); + + apply_stereo(); + synth_volume( 0 ); // suppress output for the moment + run_until_( last_time ); // get last_amp updated + apply_volume(); // now use correct volume + + return 0; +} + diff --git a/src/apu/Gb_Oscs.cpp b/src/apu/Gb_Oscs.cpp new file mode 100644 index 0000000..72ec804 --- /dev/null +++ b/src/apu/Gb_Oscs.cpp @@ -0,0 +1,665 @@ +// Gb_Snd_Emu 0.2.0. http://www.slack.net/~ant/ + +#include "Gb_Apu.h" + +/* Copyright (C) 2003-2007 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +bool const cgb_02 = false; // enables bug in early CGB units that causes problems in some games +bool const cgb_05 = false; // enables CGB-05 zombie behavior + +int const trigger_mask = 0x80; +int const length_enabled = 0x40; + +void Gb_Osc::reset() +{ + output = 0; + last_amp = 0; + delay = 0; + phase = 0; + enabled = false; +} + +inline void Gb_Osc::update_amp( blip_time_t time, int new_amp ) +{ + output->set_modified(); + int delta = new_amp - last_amp; + if ( delta ) + { + last_amp = new_amp; + med_synth->offset( time, delta, output ); + } +} + +// Units + +void Gb_Osc::clock_length() +{ + if ( (regs [4] & length_enabled) && length_ctr ) + { + if ( --length_ctr <= 0 ) + enabled = false; + } +} + +inline int Gb_Env::reload_env_timer() +{ + int raw = regs [2] & 7; + env_delay = (raw ? raw : 8); + return raw; +} + +void Gb_Env::clock_envelope() +{ + if ( env_enabled && --env_delay <= 0 && reload_env_timer() ) + { + int v = volume + (regs [2] & 0x08 ? +1 : -1); + if ( 0 <= v && v <= 15 ) + volume = v; + else + env_enabled = false; + } +} + +inline void Gb_Sweep_Square::reload_sweep_timer() +{ + sweep_delay = (regs [0] & period_mask) >> 4; + if ( !sweep_delay ) + sweep_delay = 8; +} + +void Gb_Sweep_Square::calc_sweep( bool update ) +{ + int const shift = regs [0] & shift_mask; + int const delta = sweep_freq >> shift; + sweep_neg = (regs [0] & 0x08) != 0; + int const freq = sweep_freq + (sweep_neg ? -delta : delta); + + if ( freq > 0x7FF ) + { + enabled = false; + } + else if ( shift && update ) + { + sweep_freq = freq; + + regs [3] = freq & 0xFF; + regs [4] = (regs [4] & ~0x07) | (freq >> 8 & 0x07); + } +} + +void Gb_Sweep_Square::clock_sweep() +{ + if ( --sweep_delay <= 0 ) + { + reload_sweep_timer(); + if ( sweep_enabled && (regs [0] & period_mask) ) + { + calc_sweep( true ); + calc_sweep( false ); + } + } +} + +int Gb_Wave::access( unsigned addr ) const +{ + if ( enabled && mode != Gb_Apu::mode_agb ) + { + addr = phase & (bank_size - 1); + if ( mode == Gb_Apu::mode_dmg ) + { + addr++; + if ( delay > clk_mul ) + return -1; // can only access within narrow time window while playing + } + addr >>= 1; + } + return addr & 0x0F; +} + +// write_register + +int Gb_Osc::write_trig( int frame_phase, int max_len, int old_data ) +{ + int data = regs [4]; + + if ( (frame_phase & 1) && !(old_data & length_enabled) && length_ctr ) + { + if ( (data & length_enabled) || cgb_02 ) + length_ctr--; + } + + if ( data & trigger_mask ) + { + enabled = true; + if ( !length_ctr ) + { + length_ctr = max_len; + if ( (frame_phase & 1) && (data & length_enabled) ) + length_ctr--; + } + } + + if ( !length_ctr ) + enabled = false; + + return data & trigger_mask; +} + +inline void Gb_Env::zombie_volume( int old, int data ) +{ + int v = volume; + if ( mode == Gb_Apu::mode_agb || cgb_05 ) + { + // CGB-05 behavior, very close to AGB behavior as well + if ( (old ^ data) & 8 ) + { + if ( !(old & 8) ) + { + v++; + if ( old & 7 ) + v++; + } + + v = 16 - v; + } + else if ( (old & 0x0F) == 8 ) + { + v++; + } + } + else + { + // CGB-04&02 behavior, very close to MGB behavior as well + if ( !(old & 7) && env_enabled ) + v++; + else if ( !(old & 8) ) + v += 2; + + if ( (old ^ data) & 8 ) + v = 16 - v; + } + volume = v & 0x0F; +} + +bool Gb_Env::write_register( int frame_phase, int reg, int old, int data ) +{ + int const max_len = 64; + + switch ( reg ) + { + case 1: + length_ctr = max_len - (data & (max_len - 1)); + break; + + case 2: + if ( !dac_enabled() ) + enabled = false; + + zombie_volume( old, data ); + + if ( (data & 7) && env_delay == 8 ) + { + env_delay = 1; + clock_envelope(); // TODO: really happens at next length clock + } + break; + + case 4: + if ( write_trig( frame_phase, max_len, old ) ) + { + volume = regs [2] >> 4; + reload_env_timer(); + env_enabled = true; + if ( frame_phase == 7 ) + env_delay++; + if ( !dac_enabled() ) + enabled = false; + return true; + } + } + return false; +} + +bool Gb_Square::write_register( int frame_phase, int reg, int old_data, int data ) +{ + bool result = Gb_Env::write_register( frame_phase, reg, old_data, data ); + if ( result ) + delay = (delay & (4 * clk_mul - 1)) + period(); + return result; +} + +inline void Gb_Noise::write_register( int frame_phase, int reg, int old_data, int data ) +{ + if ( Gb_Env::write_register( frame_phase, reg, old_data, data ) ) + { + phase = 0x7FFF; + delay += 8 * clk_mul; + } +} + +inline void Gb_Sweep_Square::write_register( int frame_phase, int reg, int old_data, int data ) +{ + if ( reg == 0 && sweep_enabled && sweep_neg && !(data & 0x08) ) + enabled = false; // sweep negate disabled after used + + if ( Gb_Square::write_register( frame_phase, reg, old_data, data ) ) + { + sweep_freq = frequency(); + sweep_neg = false; + reload_sweep_timer(); + sweep_enabled = (regs [0] & (period_mask | shift_mask)) != 0; + if ( regs [0] & shift_mask ) + calc_sweep( false ); + } +} + +void Gb_Wave::corrupt_wave() +{ + int pos = ((phase + 1) & (bank_size - 1)) >> 1; + if ( pos < 4 ) + wave_ram [0] = wave_ram [pos]; + else + for ( int i = 4; --i >= 0; ) + wave_ram [i] = wave_ram [(pos & ~3) + i]; +} + +inline void Gb_Wave::write_register( int frame_phase, int reg, int old_data, int data ) +{ + int const max_len = 256; + + switch ( reg ) + { + case 0: + if ( !dac_enabled() ) + enabled = false; + break; + + case 1: + length_ctr = max_len - data; + break; + + case 4: + bool was_enabled = enabled; + if ( write_trig( frame_phase, max_len, old_data ) ) + { + if ( !dac_enabled() ) + enabled = false; + else if ( mode == Gb_Apu::mode_dmg && was_enabled && + (unsigned) (delay - 2 * clk_mul) < 2 * clk_mul ) + corrupt_wave(); + + phase = 0; + delay = period() + 6 * clk_mul; + } + } +} + +void Gb_Apu::write_osc( int index, int reg, int old_data, int data ) +{ + reg -= index * 5; + switch ( index ) + { + case 0: square1.write_register( frame_phase, reg, old_data, data ); break; + case 1: square2.write_register( frame_phase, reg, old_data, data ); break; + case 2: wave .write_register( frame_phase, reg, old_data, data ); break; + case 3: noise .write_register( frame_phase, reg, old_data, data ); break; + } +} + +// Synthesis + +void Gb_Square::run( blip_time_t time, blip_time_t end_time ) +{ + // Calc duty and phase + static byte const duty_offsets [4] = { 1, 1, 3, 7 }; + static byte const duties [4] = { 1, 2, 4, 6 }; + int const duty_code = regs [1] >> 6; + int duty_offset = duty_offsets [duty_code]; + int duty = duties [duty_code]; + if ( mode == Gb_Apu::mode_agb ) + { + // AGB uses inverted duty + duty_offset -= duty; + duty = 8 - duty; + } + int ph = (this->phase + duty_offset) & 7; + + // Determine what will be generated + int vol = 0; + Blip_Buffer* const out = this->output; + if ( out ) + { + int amp = dac_off_amp; + if ( dac_enabled() ) + { + if ( enabled ) + vol = this->volume; + + amp = -dac_bias; + if ( mode == Gb_Apu::mode_agb ) + amp = -(vol >> 1); + + // Play inaudible frequencies as constant amplitude + if ( frequency() >= 0x7FA && delay < 32 * clk_mul ) + { + amp += (vol * duty) >> 3; + vol = 0; + } + + if ( ph < duty ) + { + amp += vol; + vol = -vol; + } + } + update_amp( time, amp ); + } + + // Generate wave + time += delay; + if ( time < end_time ) + { + int const per = this->period(); + if ( !vol ) + { + // Maintain phase when not playing + int count = (end_time - time + per - 1) / per; + ph += count; // will be masked below + time += (blip_time_t) count * per; + } + else + { + // Output amplitude transitions + int delta = vol; + do + { + ph = (ph + 1) & 7; + if ( ph == 0 || ph == duty ) + { + good_synth->offset_inline( time, delta, out ); + delta = -delta; + } + time += per; + } + while ( time < end_time ); + + if ( delta != vol ) + last_amp -= delta; + } + this->phase = (ph - duty_offset) & 7; + } + delay = time - end_time; +} + +// Quickly runs LFSR for a large number of clocks. For use when noise is generating +// no sound. +static unsigned run_lfsr( unsigned s, unsigned mask, int count ) +{ + bool const optimized = true; // set to false to use only unoptimized loop in middle + + // optimization used in several places: + // ((s & (1 << b)) << n) ^ ((s & (1 << b)) << (n + 1)) = (s & (1 << b)) * (3 << n) + + if ( mask == 0x4000 && optimized ) + { + if ( count >= 32767 ) + count %= 32767; + + // Convert from Fibonacci to Galois configuration, + // shifted left 1 bit + s ^= (s & 1) * 0x8000; + + // Each iteration is equivalent to clocking LFSR 255 times + while ( (count -= 255) > 0 ) + s ^= ((s & 0xE) << 12) ^ ((s & 0xE) << 11) ^ (s >> 3); + count += 255; + + // Each iteration is equivalent to clocking LFSR 15 times + // (interesting similarity to single clocking below) + while ( (count -= 15) > 0 ) + s ^= ((s & 2) * (3 << 13)) ^ (s >> 1); + count += 15; + + // Remaining singles + while ( --count >= 0 ) + s = ((s & 2) * (3 << 13)) ^ (s >> 1); + + // Convert back to Fibonacci configuration + s &= 0x7FFF; + } + else if ( count < 8 || !optimized ) + { + // won't fully replace upper 8 bits, so have to do the unoptimized way + while ( --count >= 0 ) + s = (s >> 1 | mask) ^ (mask & (0 - ((s - 1) & 2))); + } + else + { + if ( count > 127 ) + { + count %= 127; + if ( !count ) + count = 127; // must run at least once + } + + // Need to keep one extra bit of history + s = s << 1 & 0xFF; + + // Convert from Fibonacci to Galois configuration, + // shifted left 2 bits + s ^= (s & 2) * 0x80; + + // Each iteration is equivalent to clocking LFSR 7 times + // (interesting similarity to single clocking below) + while ( (count -= 7) > 0 ) + s ^= ((s & 4) * (3 << 5)) ^ (s >> 1); + count += 7; + + // Remaining singles + while ( --count >= 0 ) + s = ((s & 4) * (3 << 5)) ^ (s >> 1); + + // Convert back to Fibonacci configuration and + // repeat last 8 bits above significant 7 + s = (s << 7 & 0x7F80) | (s >> 1 & 0x7F); + } + + return s; +} + +void Gb_Noise::run( blip_time_t time, blip_time_t end_time ) +{ + // Determine what will be generated + int vol = 0; + Blip_Buffer* const out = this->output; + if ( out ) + { + int amp = dac_off_amp; + if ( dac_enabled() ) + { + if ( enabled ) + vol = this->volume; + + amp = -dac_bias; + if ( mode == Gb_Apu::mode_agb ) + amp = -(vol >> 1); + + if ( !(phase & 1) ) + { + amp += vol; + vol = -vol; + } + } + + // AGB negates final output + if ( mode == Gb_Apu::mode_agb ) + { + vol = -vol; + amp = -amp; + } + + update_amp( time, amp ); + } + + // Run timer and calculate time of next LFSR clock + static byte const period1s [8] = { 1, 2, 4, 6, 8, 10, 12, 14 }; + int const period1 = period1s [regs [3] & 7] * clk_mul; + { + int extra = (end_time - time) - delay; + int const per2 = this->period2(); + time += delay + ((divider ^ (per2 >> 1)) & (per2 - 1)) * period1; + + int count = (extra < 0 ? 0 : (extra + period1 - 1) / period1); + divider = (divider - count) & period2_mask; + delay = count * period1 - extra; + } + + // Generate wave + if ( time < end_time ) + { + unsigned const mask = this->lfsr_mask(); + unsigned bits = this->phase; + + int per = period2( period1 * 8 ); + if ( period2_index() >= 0xE ) + { + time = end_time; + } + else if ( !vol ) + { + // Maintain phase when not playing + int count = (end_time - time + per - 1) / per; + time += (blip_time_t) count * per; + bits = run_lfsr( bits, ~mask, count ); + } + else + { + // Output amplitude transitions + int delta = -vol; + do + { + unsigned changed = bits + 1; + bits = bits >> 1 & mask; + if ( changed & 2 ) + { + bits |= ~mask; + delta = -delta; + med_synth->offset_inline( time, delta, out ); + } + time += per; + } + while ( time < end_time ); + + if ( delta == vol ) + last_amp += delta; + } + this->phase = bits; + } +} + +void Gb_Wave::run( blip_time_t time, blip_time_t end_time ) +{ + // Calc volume + static byte const volumes [8] = { 0, 4, 2, 1, 3, 3, 3, 3 }; + int const volume_shift = 2; + int const volume_idx = regs [2] >> 5 & (agb_mask | 3); // 2 bits on DMG/CGB, 3 on AGB + int const volume_mul = volumes [volume_idx]; + + // Determine what will be generated + int playing = false; + Blip_Buffer* const out = this->output; + if ( out ) + { + int amp = dac_off_amp; + if ( dac_enabled() ) + { + // Play inaudible frequencies as constant amplitude + amp = 8 << 4; // really depends on average of all samples in wave + + // if delay is larger, constant amplitude won't start yet + if ( frequency() <= 0x7FB || delay > 15 * clk_mul ) + { + if ( volume_mul ) + playing = (int) enabled; + + amp = (sample_buf << (phase << 2 & 4) & 0xF0) * playing; + } + + amp = ((amp * volume_mul) >> (volume_shift + 4)) - dac_bias; + } + update_amp( time, amp ); + } + + // Generate wave + time += delay; + if ( time < end_time ) + { + byte const* wave = this->wave_ram; + + // wave size and bank + int const size20_mask = 0x20; + int const flags = regs [0] & agb_mask; + int const wave_mask = (flags & size20_mask) | 0x1F; + int swap_banks = 0; + if ( flags & bank40_mask ) + { + swap_banks = flags & size20_mask; + wave += bank_size/2 - (swap_banks >> 1); + } + + int ph = this->phase ^ swap_banks; + ph = (ph + 1) & wave_mask; // pre-advance + + int const per = this->period(); + if ( !playing ) + { + // Maintain phase when not playing + int count = (end_time - time + per - 1) / per; + ph += count; // will be masked below + time += (blip_time_t) count * per; + } + else + { + // Output amplitude transitions + int lamp = this->last_amp + dac_bias; + do + { + // Extract nybble + int nybble = wave [ph >> 1] << (ph << 2 & 4) & 0xF0; + ph = (ph + 1) & wave_mask; + + // Scale by volume + int amp = (nybble * volume_mul) >> (volume_shift + 4); + + int delta = amp - lamp; + if ( delta ) + { + lamp = amp; + med_synth->offset_inline( time, delta, out ); + } + time += per; + } + while ( time < end_time ); + this->last_amp = lamp - dac_bias; + } + ph = (ph - 1) & wave_mask; // undo pre-advance and mask position + + // Keep track of last byte read + if ( enabled ) + sample_buf = wave [ph >> 1]; + + this->phase = ph ^ swap_banks; // undo swapped banks + } + delay = time - end_time; +} diff --git a/src/apu/Gb_Oscs.h b/src/apu/Gb_Oscs.h new file mode 100644 index 0000000..52459be --- /dev/null +++ b/src/apu/Gb_Oscs.h @@ -0,0 +1,191 @@ +// Private oscillators used by Gb_Apu + +// Gb_Snd_Emu 0.2.0 +#ifndef GB_OSCS_H +#define GB_OSCS_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +#ifndef GB_APU_OVERCLOCK + #define GB_APU_OVERCLOCK 1 +#endif + +#if GB_APU_OVERCLOCK & (GB_APU_OVERCLOCK - 1) + #error "GB_APU_OVERCLOCK must be a power of 2" +#endif + +class Gb_Osc { +protected: + + // 11-bit frequency in NRx3 and NRx4 + int frequency() const { return (regs [4] & 7) * 0x100 + regs [3]; } + + void update_amp( blip_time_t, int new_amp ); + int write_trig( int frame_phase, int max_len, int old_data ); +public: + + enum { clk_mul = GB_APU_OVERCLOCK }; + enum { dac_bias = 7 }; + + Blip_Buffer* outputs [4];// NULL, right, left, center + Blip_Buffer* output; // where to output sound + BOOST::uint8_t* regs; // osc's 5 registers + int mode; // mode_dmg, mode_cgb, mode_agb + int dac_off_amp;// amplitude when DAC is off + int last_amp; // current amplitude in Blip_Buffer + typedef Blip_Synth Good_Synth; + typedef Blip_Synth Med_Synth; + Good_Synth const* good_synth; + Med_Synth const* med_synth; + + int delay; // clocks until frequency timer expires + int length_ctr; // length counter + unsigned phase; // waveform phase (or equivalent) + bool enabled; // internal enabled flag + + void clock_length(); + void reset(); +}; + +class Gb_Env : public Gb_Osc { +public: + Gb_Env() : env_enabled(false), env_delay(0) {} + int env_delay; + int volume; + bool env_enabled; + + void clock_envelope(); + bool write_register( int frame_phase, int reg, int old_data, int data ); + + void reset() + { + env_delay = 0; + volume = 0; + Gb_Osc::reset(); + } +protected: + // Non-zero if DAC is enabled + int dac_enabled() const { return regs [2] & 0xF8; } +private: + void zombie_volume( int old, int data ); + int reload_env_timer(); +}; + +class Gb_Square : public Gb_Env { +public: + bool write_register( int frame_phase, int reg, int old_data, int data ); + void run( blip_time_t, blip_time_t ); + + void reset() + { + Gb_Env::reset(); + delay = 0x40000000; // TODO: something less hacky (never clocked until first trigger) + } +private: + // Frequency timer period + int period() const { return (2048 - frequency()) * (4 * clk_mul); } +}; + +class Gb_Sweep_Square : public Gb_Square { +public: + int sweep_freq; + int sweep_delay; + bool sweep_enabled; + bool sweep_neg; + + void clock_sweep(); + void write_register( int frame_phase, int reg, int old_data, int data ); + + void reset() + { + sweep_freq = 0; + sweep_delay = 0; + sweep_enabled = false; + sweep_neg = false; + Gb_Square::reset(); + } +private: + enum { period_mask = 0x70 }; + enum { shift_mask = 0x07 }; + + void calc_sweep( bool update ); + void reload_sweep_timer(); +}; + +class Gb_Noise : public Gb_Env { +public: + + int divider; // noise has more complex frequency divider setup + + void run( blip_time_t, blip_time_t ); + void write_register( int frame_phase, int reg, int old_data, int data ); + + void reset() + { + divider = 0; + Gb_Env::reset(); + delay = 4 * clk_mul; // TODO: remove? + } +private: + enum { period2_mask = 0x1FFFF }; + + int period2_index() const { return regs [3] >> 4; } + int period2( int base = 8 ) const { return base << period2_index(); } + unsigned lfsr_mask() const { return (regs [3] & 0x08) ? ~0x4040 : ~0x4000; } +}; + +class Gb_Wave : public Gb_Osc { +public: + int sample_buf; // last wave RAM byte read (hardware has this as well) + + void write_register( int frame_phase, int reg, int old_data, int data ); + void run( blip_time_t, blip_time_t ); + + // Reads/writes wave RAM + int read( unsigned addr ) const; + void write( unsigned addr, int data ); + + void reset() + { + sample_buf = 0; + Gb_Osc::reset(); + } + +private: + enum { bank40_mask = 0x40 }; + enum { bank_size = 32 }; + + int agb_mask; // 0xFF if AGB features enabled, 0 otherwise + BOOST::uint8_t* wave_ram; // 32 bytes (64 nybbles), stored in APU + + friend class Gb_Apu; + + // Frequency timer period + int period() const { return (2048 - frequency()) * (2 * clk_mul); } + + // Non-zero if DAC is enabled + int dac_enabled() const { return regs [0] & 0x80; } + + void corrupt_wave(); + + BOOST::uint8_t* wave_bank() const { return &wave_ram [(~regs [0] & bank40_mask) >> 2 & agb_mask]; } + + // Wave index that would be accessed, or -1 if no access would occur + int access( unsigned addr ) const; +}; + +inline int Gb_Wave::read( unsigned addr ) const +{ + int index = access( addr ); + return (index < 0 ? 0xFF : wave_bank() [index]); +} + +inline void Gb_Wave::write( unsigned addr, int data ) +{ + int index = access( addr ); + if ( index >= 0 ) + wave_bank() [index] = data;; +} + +#endif diff --git a/src/apu/Multi_Buffer.cpp b/src/apu/Multi_Buffer.cpp new file mode 100644 index 0000000..0923766 --- /dev/null +++ b/src/apu/Multi_Buffer.cpp @@ -0,0 +1,281 @@ +// Blip_Buffer 0.4.1. http://www.slack.net/~ant/ + +#include "Multi_Buffer.h" + +/* Copyright (C) 2003-2007 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif + +Multi_Buffer::Multi_Buffer( int spf ) : samples_per_frame_( spf ) +{ + length_ = 0; + sample_rate_ = 0; + channels_changed_count_ = 1; + channel_types_ = 0; + channel_count_ = 0; + immediate_removal_ = true; +} + +Multi_Buffer::channel_t Multi_Buffer::channel( int /*index*/ ) +{ + static channel_t const ch = { 0, 0, 0 }; + return ch; +} + +// Silent_Buffer + +Silent_Buffer::Silent_Buffer() : Multi_Buffer( 1 ) // 0 channels would probably confuse +{ + // TODO: better to use empty Blip_Buffer so caller never has to check for NULL? + chan.left = 0; + chan.center = 0; + chan.right = 0; +} + +// Mono_Buffer + +Mono_Buffer::Mono_Buffer() : Multi_Buffer( 1 ) +{ + chan.center = &buf; + chan.left = &buf; + chan.right = &buf; +} + +Mono_Buffer::~Mono_Buffer() { } + +blargg_err_t Mono_Buffer::set_sample_rate( long rate, int msec ) +{ + RETURN_ERR( buf.set_sample_rate( rate, msec ) ); + return Multi_Buffer::set_sample_rate( buf.sample_rate(), buf.length() ); +} + + +// Tracked_Blip_Buffer + +Tracked_Blip_Buffer::Tracked_Blip_Buffer() +{ + last_non_silence = 0; +} + +void Tracked_Blip_Buffer::clear() +{ + last_non_silence = 0; + Blip_Buffer::clear(); +} + +void Tracked_Blip_Buffer::end_frame( blip_time_t t ) +{ + Blip_Buffer::end_frame( t ); + if ( clear_modified() ) + last_non_silence = samples_avail() + blip_buffer_extra_; +} + +blip_ulong Tracked_Blip_Buffer::non_silent() const +{ + return last_non_silence | unsettled(); +} + +inline void Tracked_Blip_Buffer::remove_( long n ) +{ + if ( (last_non_silence -= n) < 0 ) + last_non_silence = 0; +} + +void Tracked_Blip_Buffer::remove_silence( long n ) +{ + remove_( n ); + Blip_Buffer::remove_silence( n ); +} + +void Tracked_Blip_Buffer::remove_samples( long n ) +{ + remove_( n ); + Blip_Buffer::remove_samples( n ); +} + +void Tracked_Blip_Buffer::remove_all_samples() +{ + long avail = samples_avail(); + if ( !non_silent() ) + remove_silence( avail ); + else + remove_samples( avail ); +} + +long Tracked_Blip_Buffer::read_samples( blip_sample_t* out, long count ) +{ + count = Blip_Buffer::read_samples( out, count ); + remove_( count ); + return count; +} + +// Stereo_Buffer + +int const stereo = 2; + +Stereo_Buffer::Stereo_Buffer() : Multi_Buffer( 2 ) +{ + chan.center = mixer.bufs [2] = &bufs [2]; + chan.left = mixer.bufs [0] = &bufs [0]; + chan.right = mixer.bufs [1] = &bufs [1]; + mixer.samples_read = 0; +} + +Stereo_Buffer::~Stereo_Buffer() { } + +blargg_err_t Stereo_Buffer::set_sample_rate( long rate, int msec ) +{ + mixer.samples_read = 0; + for ( int i = bufs_size; --i >= 0; ) + RETURN_ERR( bufs [i].set_sample_rate( rate, msec ) ); + return Multi_Buffer::set_sample_rate( bufs [0].sample_rate(), bufs [0].length() ); +} + +void Stereo_Buffer::clock_rate( long rate ) +{ + for ( int i = bufs_size; --i >= 0; ) + bufs [i].clock_rate( rate ); +} + +void Stereo_Buffer::bass_freq( int bass ) +{ + for ( int i = bufs_size; --i >= 0; ) + bufs [i].bass_freq( bass ); +} + +void Stereo_Buffer::clear() +{ + mixer.samples_read = 0; + for ( int i = bufs_size; --i >= 0; ) + bufs [i].clear(); +} + +void Stereo_Buffer::end_frame( blip_time_t time ) +{ + for ( int i = bufs_size; --i >= 0; ) + bufs [i].end_frame( time ); +} + +long Stereo_Buffer::read_samples( blip_sample_t* out, long out_size ) +{ + require( (out_size & 1) == 0 ); // must read an even number of samples + out_size = min( out_size, samples_avail() ); + + int pair_count = int (out_size >> 1); + if ( pair_count ) + { + mixer.read_pairs( out, pair_count ); + + if ( samples_avail() <= 0 || immediate_removal() ) + { + for ( int i = bufs_size; --i >= 0; ) + { + buf_t& b = bufs [i]; + // TODO: might miss non-silence settling since it checks END of last read + if ( !b.non_silent() ) + b.remove_silence( mixer.samples_read ); + else + b.remove_samples( mixer.samples_read ); + } + mixer.samples_read = 0; + } + } + return out_size; +} + + +// Stereo_Mixer + +// mixers use a single index value to improve performance on register-challenged processors +// offset goes from negative to zero + +void Stereo_Mixer::read_pairs( blip_sample_t* out, int count ) +{ + // TODO: if caller never marks buffers as modified, uses mono + // except that buffer isn't cleared, so caller can encounter + // subtle problems and not realize the cause. + samples_read += count; + if ( bufs [0]->non_silent() | bufs [1]->non_silent() ) + mix_stereo( out, count ); + else + mix_mono( out, count ); +} + +void Stereo_Mixer::mix_mono( blip_sample_t* out_, int count ) +{ + int const bass = BLIP_READER_BASS( *bufs [2] ); + BLIP_READER_BEGIN( center, *bufs [2] ); + BLIP_READER_ADJ_( center, samples_read ); + + typedef blip_sample_t stereo_blip_sample_t [stereo]; + stereo_blip_sample_t* BLIP_RESTRICT out = (stereo_blip_sample_t*) out_ + count; + int offset = -count; + do + { + blargg_long s = BLIP_READER_READ( center ); + BLIP_READER_NEXT_IDX_( center, bass, offset ); + BLIP_CLAMP( s, s ); + + out [offset] [0] = (blip_sample_t) s; + out [offset] [1] = (blip_sample_t) s; + } + while ( ++offset ); + + BLIP_READER_END( center, *bufs [2] ); +} + +void Stereo_Mixer::mix_stereo( blip_sample_t* out_, int count ) +{ + blip_sample_t* BLIP_RESTRICT out = out_ + count * stereo; + + // do left + center and right + center separately to reduce register load + Tracked_Blip_Buffer* const* buf = &bufs [2]; + while ( true ) // loop runs twice + { + --buf; + --out; + + int const bass = BLIP_READER_BASS( *bufs [2] ); + BLIP_READER_BEGIN( side, **buf ); + BLIP_READER_BEGIN( center, *bufs [2] ); + + BLIP_READER_ADJ_( side, samples_read ); + BLIP_READER_ADJ_( center, samples_read ); + + int offset = -count; + do + { + blargg_long s = BLIP_READER_READ_RAW( center ) + BLIP_READER_READ_RAW( side ); + s >>= blip_sample_bits - 16; + BLIP_READER_NEXT_IDX_( side, bass, offset ); + BLIP_READER_NEXT_IDX_( center, bass, offset ); + BLIP_CLAMP( s, s ); + + ++offset; // before write since out is decremented to slightly before end + out [offset * stereo] = (blip_sample_t) s; + } + while ( offset ); + + BLIP_READER_END( side, **buf ); + + if ( buf != bufs ) + continue; + + // only end center once + BLIP_READER_END( center, *bufs [2] ); + break; + } +} diff --git a/src/apu/Multi_Buffer.h b/src/apu/Multi_Buffer.h new file mode 100644 index 0000000..d22075d --- /dev/null +++ b/src/apu/Multi_Buffer.h @@ -0,0 +1,205 @@ +// Multi-channel sound buffer interface, and basic mono and stereo buffers + +// Blip_Buffer 0.4.1 +#ifndef MULTI_BUFFER_H +#define MULTI_BUFFER_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +// Interface to one or more Blip_Buffers mapped to one or more channels +// consisting of left, center, and right buffers. +class Multi_Buffer { +public: + Multi_Buffer( int samples_per_frame ); + virtual ~Multi_Buffer() { } + + // Sets the number of channels available and optionally their types + // (type information used by Effects_Buffer) + enum { type_index_mask = 0xFF }; + enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type }; + virtual blargg_err_t set_channel_count( int, int const* types = 0 ); + int channel_count() const { return channel_count_; } + + // Gets indexed channel, from 0 to channel count - 1 + struct channel_t { + Blip_Buffer* center; + Blip_Buffer* left; + Blip_Buffer* right; + }; + virtual channel_t channel( int index ) BLARGG_PURE( ; ) + + // See Blip_Buffer.h + virtual blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ) BLARGG_PURE( ; ) + virtual void clock_rate( long ) BLARGG_PURE( { } ) + virtual void bass_freq( int ) BLARGG_PURE( { } ) + virtual void clear() BLARGG_PURE( { } ) + long sample_rate() const; + + // Length of buffer, in milliseconds + int length() const; + + // See Blip_Buffer.h + virtual void end_frame( blip_time_t ) BLARGG_PURE( { } ) + + // Number of samples per output frame (1 = mono, 2 = stereo) + int samples_per_frame() const; + + // Count of changes to channel configuration. Incremented whenever + // a change is made to any of the Blip_Buffers for any channel. + unsigned channels_changed_count() { return channels_changed_count_; } + + // See Blip_Buffer.h + virtual long read_samples( blip_sample_t*, long ) BLARGG_PURE( { return 0; } ) + virtual long samples_avail() const BLARGG_PURE( { return 0; } ) + +public: + BLARGG_DISABLE_NOTHROW + void disable_immediate_removal() { immediate_removal_ = false; } +protected: + bool immediate_removal() const { return immediate_removal_; } + int const* channel_types() const { return channel_types_; } + void channels_changed() { channels_changed_count_++; } +private: + // noncopyable + Multi_Buffer( const Multi_Buffer& ); + Multi_Buffer& operator = ( const Multi_Buffer& ); + + unsigned channels_changed_count_; + long sample_rate_; + int length_; + int channel_count_; + int const samples_per_frame_; + int const* channel_types_; + bool immediate_removal_; +}; + +// Uses a single buffer and outputs mono samples. +class Mono_Buffer : public Multi_Buffer { + Blip_Buffer buf; + channel_t chan; +public: + // Buffer used for all channels + Blip_Buffer* center() { return &buf; } + +public: + Mono_Buffer(); + ~Mono_Buffer(); + blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ); + void clock_rate( long rate ) { buf.clock_rate( rate ); } + void bass_freq( int freq ) { buf.bass_freq( freq ); } + void clear() { buf.clear(); } + long samples_avail() const { return buf.samples_avail(); } + long read_samples( blip_sample_t* p, long s ) { return buf.read_samples( p, s ); } + channel_t channel( int ) { return chan; } + void end_frame( blip_time_t t ) { buf.end_frame( t ); } +}; + + class Tracked_Blip_Buffer : public Blip_Buffer { + public: + // Non-zero if buffer still has non-silent samples in it. Requires that you call + // set_modified() appropriately. + blip_ulong non_silent() const; + + // remove_samples( samples_avail() ) + void remove_all_samples(); + + public: + BLARGG_DISABLE_NOTHROW + + long read_samples( blip_sample_t*, long ); + void remove_silence( long ); + void remove_samples( long ); + Tracked_Blip_Buffer(); + void clear(); + void end_frame( blip_time_t ); + private: + blip_long last_non_silence; + void remove_( long ); + }; + + class Stereo_Mixer { + public: + Tracked_Blip_Buffer* bufs [3]; + blargg_long samples_read; + + Stereo_Mixer() : samples_read( 0 ) { } + void read_pairs( blip_sample_t* out, int count ); + private: + void mix_mono ( blip_sample_t* out, int pair_count ); + void mix_stereo( blip_sample_t* out, int pair_count ); + }; + +// Uses three buffers (one for center) and outputs stereo sample pairs. +class Stereo_Buffer : public Multi_Buffer { +public: + + // Buffers used for all channels + Blip_Buffer* center() { return &bufs [2]; } + Blip_Buffer* left() { return &bufs [0]; } + Blip_Buffer* right() { return &bufs [1]; } + +public: + Stereo_Buffer(); + ~Stereo_Buffer(); + blargg_err_t set_sample_rate( long, int msec = blip_default_length ); + void clock_rate( long ); + void bass_freq( int ); + void clear(); + channel_t channel( int ) { return chan; } + void end_frame( blip_time_t ); + + long samples_avail() const { return (bufs [0].samples_avail() - mixer.samples_read) * 2; } + long read_samples( blip_sample_t*, long ); + +private: + enum { bufs_size = 3 }; + typedef Tracked_Blip_Buffer buf_t; + buf_t bufs [bufs_size]; + Stereo_Mixer mixer; + channel_t chan; + long samples_avail_; +}; + +// Silent_Buffer generates no samples, useful where no sound is wanted +class Silent_Buffer : public Multi_Buffer { + channel_t chan; +public: + Silent_Buffer(); + blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ); + void clock_rate( long ) { } + void bass_freq( int ) { } + void clear() { } + channel_t channel( int ) { return chan; } + void end_frame( blip_time_t ) { } + long samples_avail() const { return 0; } + long read_samples( blip_sample_t*, long ) { return 0; } +}; + + +inline blargg_err_t Multi_Buffer::set_sample_rate( long rate, int msec ) +{ + sample_rate_ = rate; + length_ = msec; + return 0; +} + +inline blargg_err_t Silent_Buffer::set_sample_rate( long rate, int msec ) +{ + return Multi_Buffer::set_sample_rate( rate, msec ); +} + +inline int Multi_Buffer::samples_per_frame() const { return samples_per_frame_; } + +inline long Multi_Buffer::sample_rate() const { return sample_rate_; } + +inline int Multi_Buffer::length() const { return length_; } + +inline blargg_err_t Multi_Buffer::set_channel_count( int n, int const* types ) +{ + channel_count_ = n; + channel_types_ = types; + return 0; +} + +#endif diff --git a/src/apu/blargg_common.h b/src/apu/blargg_common.h new file mode 100644 index 0000000..1203d38 --- /dev/null +++ b/src/apu/blargg_common.h @@ -0,0 +1,206 @@ +// Sets up common environment for Shay Green's libraries. +// To change configuration options, modify blargg_config.h, not this file. + +// Gb_Snd_Emu 0.2.0 +#ifndef BLARGG_COMMON_H +#define BLARGG_COMMON_H + +#include +#include +#include +#include + +#undef BLARGG_COMMON_H +// allow blargg_config.h to #include blargg_common.h +#include "blargg_config.h" +#ifndef BLARGG_COMMON_H +#define BLARGG_COMMON_H + +// BLARGG_RESTRICT: equivalent to restrict, where supported +#if __GNUC__ >= 3 || _MSC_VER >= 1100 + #define BLARGG_RESTRICT __restrict +#else + #define BLARGG_RESTRICT +#endif + +// STATIC_CAST(T,expr): Used in place of static_cast (expr) +// CONST_CAST( T,expr): Used in place of const_cast (expr) +#ifndef STATIC_CAST + #if __GNUC__ >= 4 + #define STATIC_CAST(T,expr) static_cast (expr) + #define CONST_CAST( T,expr) const_cast (expr) + #else + #define STATIC_CAST(T,expr) ((T) (expr)) + #define CONST_CAST( T,expr) ((T) (expr)) + #endif +#endif + +// blargg_err_t (0 on success, otherwise error string) +#ifndef blargg_err_t + typedef const char* blargg_err_t; +#endif + +// blargg_vector - very lightweight vector of POD types (no constructor/destructor) +template +class blargg_vector { + T* begin_; + size_t size_; +public: + blargg_vector() : begin_( 0 ), size_( 0 ) { } + ~blargg_vector() { free( begin_ ); } + size_t size() const { return size_; } + T* begin() const { return begin_; } + T* end() const { return begin_ + size_; } + blargg_err_t resize( size_t n ) + { + // TODO: blargg_common.cpp to hold this as an outline function, ugh + void* p = realloc( begin_, n * sizeof (T) ); + if ( p ) + begin_ = (T*) p; + else if ( n > size_ ) // realloc failure only a problem if expanding + return "Out of memory"; + size_ = n; + return 0; + } + void clear() { void* p = begin_; begin_ = 0; size_ = 0; free( p ); } + T& operator [] ( size_t n ) const + { + assert( n <= size_ ); // <= to allow past-the-end value + return begin_ [n]; + } +}; + +#ifndef BLARGG_DISABLE_NOTHROW + // throw spec mandatory in ISO C++ if operator new can return NULL + #if __cplusplus >= 199711 || __GNUC__ >= 3 + #define BLARGG_THROWS( spec ) throw spec + #else + #define BLARGG_THROWS( spec ) + #endif + #define BLARGG_DISABLE_NOTHROW \ + void* operator new ( size_t s ) BLARGG_THROWS(()) { return malloc( s ); }\ + void operator delete ( void* p ) { free( p ); } + #define BLARGG_NEW new +#else + #include + #define BLARGG_NEW new (std::nothrow) +#endif + +// BLARGG_4CHAR('a','b','c','d') = 'abcd' (four character integer constant) +#define BLARGG_4CHAR( a, b, c, d ) \ + ((a&0xFF)*0x1000000 + (b&0xFF)*0x10000 + (c&0xFF)*0x100 + (d&0xFF)) + +// BOOST_STATIC_ASSERT( expr ): Generates compile error if expr is 0. +#ifndef BOOST_STATIC_ASSERT + #ifdef _MSC_VER + // MSVC6 (_MSC_VER < 1300) fails for use of __LINE__ when /Zl is specified + #define BOOST_STATIC_ASSERT( expr ) \ + void blargg_failed_( int (*arg) [2 / (int) !!(expr) - 1] ) + #else + // Some other compilers fail when declaring same function multiple times in class, + // so differentiate them by line + #define BOOST_STATIC_ASSERT( expr ) \ + void blargg_failed_( int (*arg) [2 / !!(expr) - 1] [__LINE__] ) + #endif +#endif + +// BLARGG_COMPILER_HAS_BOOL: If 0, provides bool support for old compiler. If 1, +// compiler is assumed to support bool. If undefined, availability is determined. +#ifndef BLARGG_COMPILER_HAS_BOOL + #if defined (__MWERKS__) + #if !__option(bool) + #define BLARGG_COMPILER_HAS_BOOL 0 + #endif + #elif defined (_MSC_VER) + #if _MSC_VER < 1100 + #define BLARGG_COMPILER_HAS_BOOL 0 + #endif + #elif defined (__GNUC__) + // supports bool + #elif __cplusplus < 199711 + #define BLARGG_COMPILER_HAS_BOOL 0 + #endif +#endif +#if defined (BLARGG_COMPILER_HAS_BOOL) && !BLARGG_COMPILER_HAS_BOOL + // If you get errors here, modify your blargg_config.h file + typedef int bool; + const bool true = 1; + const bool false = 0; +#endif + +// blargg_long/blargg_ulong = at least 32 bits, int if it's big enough + +#if INT_MAX < 0x7FFFFFFF || LONG_MAX == 0x7FFFFFFF + typedef long blargg_long; +#else + typedef int blargg_long; +#endif + +#if UINT_MAX < 0xFFFFFFFF || ULONG_MAX == 0xFFFFFFFF + typedef unsigned long blargg_ulong; +#else + typedef unsigned blargg_ulong; +#endif + +// BOOST::int8_t etc. + +// HAVE_STDINT_H: If defined, use for int8_t etc. +#if defined (HAVE_STDINT_H) + #include + #define BOOST + +// HAVE_INTTYPES_H: If defined, use for int8_t etc. +#elif defined (HAVE_INTTYPES_H) + #include + #define BOOST + +#else + struct BOOST + { + #if UCHAR_MAX == 0xFF && SCHAR_MAX == 0x7F + typedef signed char int8_t; + typedef unsigned char uint8_t; + #else + // No suitable 8-bit type available + typedef struct see_blargg_common_h int8_t; + typedef struct see_blargg_common_h uint8_t; + #endif + + #if USHRT_MAX == 0xFFFF + typedef short int16_t; + typedef unsigned short uint16_t; + #else + // No suitable 16-bit type available + typedef struct see_blargg_common_h int16_t; + typedef struct see_blargg_common_h uint16_t; + #endif + + #if ULONG_MAX == 0xFFFFFFFF + typedef long int32_t; + typedef unsigned long uint32_t; + #elif UINT_MAX == 0xFFFFFFFF + typedef int int32_t; + typedef unsigned int uint32_t; + #else + // No suitable 32-bit type available + typedef struct see_blargg_common_h int32_t; + typedef struct see_blargg_common_h uint32_t; + #endif + }; +#endif + +#if __GNUC__ >= 3 + #define BLARGG_DEPRECATED __attribute__ ((deprecated)) +#else + #define BLARGG_DEPRECATED +#endif + +// Use in place of "= 0;" for a pure virtual, since these cause calls to std C++ lib. +// During development, BLARGG_PURE( x ) expands to = 0; +// virtual int func() BLARGG_PURE( { return 0; } ) +#ifndef BLARGG_PURE + #define BLARGG_PURE( def ) def +#endif + +#endif +#endif diff --git a/src/apu/blargg_config.h b/src/apu/blargg_config.h new file mode 100644 index 0000000..961e045 --- /dev/null +++ b/src/apu/blargg_config.h @@ -0,0 +1,33 @@ +// $package user configuration file. Don't replace when updating library. + +#ifndef BLARGG_CONFIG_H +#define BLARGG_CONFIG_H + +// Uncomment to have Gb_Apu run at 4x normal clock rate (16777216 Hz), useful in +// a Game Boy Advance emulator. +#define GB_APU_OVERCLOCK 4 + +#define GB_APU_CUSTOM_STATE 1 + +// Uncomment to enable platform-specific (and possibly non-portable) optimizations. +//#define BLARGG_NONPORTABLE 1 + +// Uncomment if automatic byte-order determination doesn't work +//#define BLARGG_BIG_ENDIAN 1 + +// Uncomment to use zlib for transparent decompression of gzipped files +//#define HAVE_ZLIB_H + +// Uncomment if you get errors in the bool section of blargg_common.h +//#define BLARGG_COMPILER_HAS_BOOL 1 + +// Uncomment to disable out-of-memory exceptions +//#include +//#define BLARGG_NEW new (std::nothrow) + +// Use standard config.h if present +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#endif diff --git a/src/apu/blargg_source.h b/src/apu/blargg_source.h new file mode 100644 index 0000000..ddef37d --- /dev/null +++ b/src/apu/blargg_source.h @@ -0,0 +1,92 @@ +/* Included at the beginning of library source files, AFTER all other #include lines. +Sets up helpful macros and services used in my source code. Since this is only "active" +in my source code, I don't have to worry about polluting the global namespace with +unprefixed names. */ + +// Gb_Snd_Emu 0.2.0 +#ifndef BLARGG_SOURCE_H +#define BLARGG_SOURCE_H + +// The following four macros are for debugging only. Some or all might be defined +// to do nothing, depending on the circumstances. Described is what happens when +// a particular macro is defined to do something. When defined to do nothing, the +// macros do NOT evaluate their argument(s). + +// If expr is false, prints file and line number, then aborts program. Meant for +// checking internal state and consistency. A failed assertion indicates a bug +// in MY code. +// +// void assert( bool expr ); +#include + +// If expr is false, prints file and line number, then aborts program. Meant for +// checking caller-supplied parameters and operations that are outside the control +// of the module. A failed requirement probably indicates a bug in YOUR code. +// +// void require( bool expr ); +#undef require +#define require( expr ) assert( expr ) + +// Like printf() except output goes to debugging console/file. +// +// void dprintf( const char* format, ... ); +static inline void blargg_dprintf_( const char*, ... ) { } +#undef dprintf +#define dprintf (1) ? (void) 0 : blargg_dprintf_ + +// If expr is false, prints file and line number to debug console/log, then +// continues execution normally. Meant for flagging potential problems or things +// that should be looked into, but that aren't serious problems. +// +// void check( bool expr ); +#undef check +#define check( expr ) ((void) 0) + +// If expr yields non-NULL error string, returns it from current function, +// otherwise continues normally. +#undef RETURN_ERR +#define RETURN_ERR( expr ) do { \ + blargg_err_t blargg_return_err_ = (expr); \ + if ( blargg_return_err_ ) return blargg_return_err_; \ + } while ( 0 ) + +// If ptr is NULL, returns "Out of memory" error string, otherwise continues normally. +#undef CHECK_ALLOC +#define CHECK_ALLOC( ptr ) do { if ( (ptr) == 0 ) return "Out of memory"; } while ( 0 ) + +// The usual min/max functions for built-in types. +// +// template T min( T x, T y ) { return x < y ? x : y; } +// template T max( T x, T y ) { return x > y ? x : y; } +#define BLARGG_DEF_MIN_MAX( type ) \ + static inline type blargg_min( type x, type y ) { if ( y < x ) x = y; return x; }\ + static inline type blargg_max( type x, type y ) { if ( x < y ) x = y; return x; } + +BLARGG_DEF_MIN_MAX( int ) +BLARGG_DEF_MIN_MAX( unsigned ) +BLARGG_DEF_MIN_MAX( long ) +BLARGG_DEF_MIN_MAX( unsigned long ) +BLARGG_DEF_MIN_MAX( float ) +BLARGG_DEF_MIN_MAX( double ) + +#undef min +#define min blargg_min + +#undef max +#define max blargg_max + +// typedef unsigned char byte; +typedef unsigned char blargg_byte; +#undef byte +#define byte blargg_byte + +// deprecated +#define BLARGG_CHECK_ALLOC CHECK_ALLOC +#define BLARGG_RETURN_ERR RETURN_ERR + +// BLARGG_SOURCE_BEGIN: If defined, #included, allowing redefition of dprintf and check +#ifdef BLARGG_SOURCE_BEGIN + #include BLARGG_SOURCE_BEGIN +#endif + +#endif diff --git a/src/common/Array.h b/src/common/Array.h new file mode 100644 index 0000000..f01806e --- /dev/null +++ b/src/common/Array.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef ARRAY_H +#define ARRAY_H + +#include + +template +class Array { + T *a; + std::size_t sz; + + Array(const Array &ar); + +public: + Array(const std::size_t size = 0) : a(size ? new T[size] : 0), sz(size) {} + ~Array() { delete []a; } + void reset(const std::size_t size) { delete []a; a = size ? new T[size] : 0; sz = size; } + std::size_t size() const { return sz; } + operator T*() { return a; } + operator const T*() const { return a; } +}; + +#endif diff --git a/src/common/Patch.cpp b/src/common/Patch.cpp new file mode 100644 index 0000000..ac4893a --- /dev/null +++ b/src/common/Patch.cpp @@ -0,0 +1,459 @@ +#include +#include +#include +#include + +#include "Patch.h" + + +#ifdef __GNUC__ +#if defined(__APPLE__) || defined (BSD) || defined (__NetBSD__) +typedef off_t __off64_t; /* off_t is 64 bits on BSD. */ +#define fseeko64 fseeko +#define ftello64 ftello +#else +typedef off64_t __off64_t; +#endif /* __APPLE__ || BSD */ +#endif /* __GNUC__ */ + +#ifndef _MSC_VER +#define _stricmp strcasecmp +#endif // ! _MSC_VER + +#ifdef _MSC_VER +#define fseeko64 _fseeki64 +#define ftello64 _ftelli64 +typedef __int64 __off64_t; +#endif + +static int readInt2(FILE *f) +{ + int res = 0; + int c = fgetc(f); + if(c == EOF) + return -1; + res = c; + c = fgetc(f); + if(c == EOF) + return -1; + return c + (res<<8); +} + +static int readInt3(FILE *f) +{ + int res = 0; + int c = fgetc(f); + if(c == EOF) + return -1; + res = c; + c = fgetc(f); + if(c == EOF) + return -1; + res = c + (res<<8); + c = fgetc(f); + if(c == EOF) + return -1; + return c + (res<<8); +} + +static s64 readInt4(FILE *f) +{ + s64 tmp, res = 0; + int c; + + for (int i = 0; i < 4; i++) { + c = fgetc(f); + if (c == EOF) + return -1; + tmp = c; + res = res + (tmp << (i*8)); + } + + return res; +} + +static s64 readInt8(FILE *f) +{ + s64 tmp, res = 0; + int c; + + for (int i = 0; i < 8; i++) { + c = fgetc(f); + if (c == EOF) + return -1; + tmp = c; + res = res + (tmp << (i*8)); + } + + return res; +} + +static s64 readVarPtr(FILE *f) +{ + s64 offset = 0, shift = 1; + for (;;) { + int c = fgetc(f); + if (c == EOF) return 0; + offset += (c & 0x7F) * shift; + if (c & 0x80) break; + shift <<= 7; + offset += shift; + } + return offset; +} + +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif + +static uLong computePatchCRC(FILE *f, unsigned int size) +{ + Bytef buf[4096]; + long readed; + + uLong crc = crc32(0L, Z_NULL, 0); + do { + readed = fread(buf, 1, MIN(size, sizeof(buf)), f); + crc = crc32(crc, buf, readed); + size -= readed; + } while (readed > 0); + return crc; +} + +static bool patchApplyIPS(const char *patchname, u8 **r, int *s) +{ + // from the IPS spec at http://zerosoft.zophar.net/ips.htm + FILE *f = fopen(patchname, "rb"); + if(!f) + return false; + + bool result = false; + + u8 *rom = *r; + int size = *s; + if(fgetc(f) == 'P' && + fgetc(f) == 'A' && + fgetc(f) == 'T' && + fgetc(f) == 'C' && + fgetc(f) == 'H') { + int b; + int offset; + int len; + + result = true; + + for(;;) { + // read offset + offset = readInt3(f); + // if offset == EOF, end of patch + if(offset == 0x454f46 || offset == -1) + break; + // read length + len = readInt2(f); + if(!len) { + // len == 0, RLE block + len = readInt2(f); + // byte to fill + int c = fgetc(f); + if(c == -1) + break; + b = (u8)c; + } else + b= -1; + // check if we need to reallocate our ROM + if((offset + len) >= size) { + size *= 2; + rom = (u8 *)realloc(rom, size); + *r = rom; + *s = size; + } + if(b == -1) { + // normal block, just read the data + if(fread(&rom[offset], 1, len, f) != (size_t)len) + break; + } else { + // fill the region with the given byte + while(len--) { + rom[offset++] = b; + } + } + } + } + // close the file + fclose(f); + + return result; +} + +static bool patchApplyUPS(const char *patchname, u8 **rom, int *size) +{ + s64 srcCRC, dstCRC, patchCRC; + + FILE *f = fopen(patchname, "rb"); + if (!f) + return false; + + fseeko64(f, 0, SEEK_END); + __off64_t patchSize = ftello64(f); + if (patchSize < 20) { + fclose(f); + return false; + } + + fseeko64(f, 0, SEEK_SET); + if(fgetc(f) != 'U' || fgetc(f) != 'P' || fgetc(f) != 'S' || fgetc(f) != '1') { + fclose(f); + return false; + } + + fseeko64(f, -12, SEEK_END); + srcCRC = readInt4(f); + dstCRC = readInt4(f); + patchCRC = readInt4(f); + if (srcCRC == -1 || dstCRC == -1 || patchCRC == -1) { + fclose(f); + return false; + } + + fseeko64(f, 0, SEEK_SET); + u32 crc = computePatchCRC(f, patchSize-4); + + if (crc != patchCRC) { + fclose(f); + return false; + } + + crc = crc32(0L, Z_NULL, 0); + crc = crc32(crc, *rom, *size); + + fseeko64(f, 4, SEEK_SET); + s64 dataSize; + s64 srcSize = readVarPtr(f); + s64 dstSize = readVarPtr(f); + + if (crc == srcCRC) { + if (srcSize != *size) { + fclose(f); + return false; + } + dataSize = dstSize; + } else if (crc == dstCRC) { + if (dstSize != *size) { + fclose(f); + return false; + } + dataSize = srcSize; + } else { + fclose(f); + return false; + } + if (dataSize > *size) { + *rom = (u8*)realloc(*rom, dataSize); + memset(*rom + *size, 0, dataSize - *size); + *size = dataSize; + } + + s64 relative = 0; + u8 *mem; + while(ftello64(f) < patchSize - 12) { + relative += readVarPtr(f); + if (relative > dataSize) continue; + mem = *rom + relative; + for(s64 i = relative; i < dataSize; i++) { + int x = fgetc(f); + relative++; + if (!x) break; + if (i < dataSize) { + *mem++ ^= x; + } + } + } + + fclose(f); + return true; +} + +static int ppfVersion(FILE *f) +{ + fseeko64(f, 0, SEEK_SET); + if (fgetc(f) != 'P' || fgetc(f) != 'P' || fgetc(f) != 'F') //-V501 + return 0; + switch(fgetc(f)){ + case '1': return 1; + case '2': return 2; + case '3': return 3; + default: return 0; + } +} + +static int ppfFileIdLen(FILE *f, int version) +{ + if (version == 2) { + fseeko64(f, -8, SEEK_END); + } else { + fseeko64(f, -6, SEEK_END); + } + + if (fgetc(f) != '.' || fgetc(f) != 'D' || fgetc(f) != 'I' || fgetc(f) != 'Z') + return 0; + + return (version == 2) ? readInt4(f) : readInt2(f); +} + +static bool patchApplyPPF1(FILE *f, u8 **rom, int *size) +{ + fseek(f, 0, SEEK_END); + int count = ftell(f); + if (count < 56) + return false; + count -= 56; + + fseek(f, 56, SEEK_SET); + + u8 *mem = *rom; + + while (count > 0) { + int offset = readInt4(f); + if (offset == -1) + break; + int len = fgetc(f); + if (len == EOF) + break; + if (offset+len > *size) + break; + if (fread(&mem[offset], 1, len, f) != (size_t)len) + break; + count -= 4 + 1 + len; + } + + return (count == 0); +} + +static bool patchApplyPPF2(FILE *f, u8 **rom, int *size) +{ + fseek(f, 0, SEEK_END); + int count = ftell(f); + if (count < 56+4+1024) + return false; + count -= 56+4+1024; + + fseek(f, 56, SEEK_SET); + + int datalen = readInt4(f); + if (datalen != *size) + return false; + + u8 *mem = *rom; + + u8 block[1024]; + fread(&block, 1, 1024, f); + if (memcmp(&mem[0x9320], &block, 1024) != 0) + return false; + + int idlen = ppfFileIdLen(f, 2); + if (idlen > 0) + count -= 16 + 16 + idlen; + + fseek(f, 56+4+1024, SEEK_SET); + + while (count > 0) { + int offset = readInt4(f); + if (offset == -1) + break; + int len = fgetc(f); + if (len == EOF) + break; + if (offset+len > *size) + break; + if (fread(&mem[offset], 1, len, f) != (size_t)len) + break; + count -= 4 + 1 + len; + } + + return (count == 0); +} + +static bool patchApplyPPF3(FILE *f, u8 **rom, int *size) +{ + fseek(f, 0, SEEK_END); + int count = ftell(f); + if (count < 56+4+1024) + return false; + count -= 56+4; + + fseek(f, 56, SEEK_SET); + + int imagetype = fgetc(f); + int blockcheck = fgetc(f); + int undo = fgetc(f); + fgetc(f); + + u8 *mem = *rom; + + if (blockcheck) { + u8 block[1024]; + fread(&block, 1, 1024, f); + if (memcmp(&mem[(imagetype == 0) ? 0x9320 : 0x80A0], &block, 1024) != 0) + return false; + count -= 1024; + } + + int idlen = ppfFileIdLen(f, 2); + if (idlen > 0) + count -= 16 + 16 + idlen; + + fseek(f, 56+4+(blockcheck ? 1024 : 0), SEEK_SET); + + while (count > 0) { + __off64_t offset = readInt8(f); + if (offset == -1) + break; + int len = fgetc(f); + if (len == EOF) + break; + if (offset+len > *size) + break; + if (fread(&mem[offset], 1, len, f) != (size_t)len) + break; + if (undo) fseeko64(f, len, SEEK_CUR); + count -= 8 + 1 + len; + if (undo) count -= len; + } + + return (count == 0); +} + +static bool patchApplyPPF(const char *patchname, u8 **rom, int *size) +{ + FILE *f = fopen(patchname, "rb"); + if (!f) + return false; + + bool res = false; + + int version = ppfVersion(f); + switch (version) { + case 1: res = patchApplyPPF1(f, rom, size); break; + case 2: res = patchApplyPPF2(f, rom, size); break; + case 3: res = patchApplyPPF3(f, rom, size); break; + } + + fclose(f); + return res; +} + +bool applyPatch(const char *patchname, u8 **rom, int *size) +{ + if (strlen(patchname) < 5) + return false; + const char * p = strrchr(patchname, '.'); + if (p == NULL) + return false; + if (_stricmp(p, ".ips") == 0) + return patchApplyIPS(patchname, rom, size); + if (_stricmp(p, ".ups") == 0) + return patchApplyUPS(patchname, rom, size); + if (_stricmp(p, ".ppf") == 0) + return patchApplyPPF(patchname, rom, size); + return false; +} diff --git a/src/common/Patch.h b/src/common/Patch.h new file mode 100644 index 0000000..5757a63 --- /dev/null +++ b/src/common/Patch.h @@ -0,0 +1,8 @@ +#ifndef PATCH_H +#define PATCH_H + +#include "Types.h" + +bool applyPatch(const char *patchname, u8 **rom, int *size); + +#endif // PATCH_H diff --git a/src/common/Port.h b/src/common/Port.h new file mode 100644 index 0000000..0e134a2 --- /dev/null +++ b/src/common/Port.h @@ -0,0 +1,68 @@ +#ifndef PORT_H +#define PORT_H + +#ifdef __CELLOS_LV2__ +/* PlayStation3 */ +#include +#endif + +#ifdef _XBOX360 +/* XBox 360 */ +#include +#endif + +#include "Types.h" + +// swaps a 16-bit value +static inline u16 swap16(u16 v) +{ + return (v<<8)|(v>>8); +} + +// swaps a 32-bit value +static inline u32 swap32(u32 v) +{ + return (v<<24)|((v<<8)&0xff0000)|((v>>8)&0xff00)|(v>>24); +} + +#ifdef WORDS_BIGENDIAN +#if defined(__GNUC__) && defined(__ppc__) + +#define READ16LE(base) \ + ({ unsigned short lhbrxResult; \ + __asm__ ("lhbrx %0, 0, %1" : "=r" (lhbrxResult) : "r" (base) : "memory"); \ + lhbrxResult; }) + +#define READ32LE(base) \ + ({ unsigned long lwbrxResult; \ + __asm__ ("lwbrx %0, 0, %1" : "=r" (lwbrxResult) : "r" (base) : "memory"); \ + lwbrxResult; }) + +#define WRITE16LE(base, value) \ + __asm__ ("sthbrx %0, 0, %1" : : "r" (value), "r" (base) : "memory") + +#define WRITE32LE(base, value) \ + __asm__ ("stwbrx %0, 0, %1" : : "r" (value), "r" (base) : "memory") + +#else +#define READ16LE(x) \ + swap16(*((u16 *)(x))) +#define READ32LE(x) \ + swap32(*((u32 *)(x))) +#define WRITE16LE(x,v) \ + *((u16 *)x) = swap16((v)) +#define WRITE32LE(x,v) \ + *((u32 *)x) = swap32((v)) +#endif +#else +#define READ16LE(x) \ + *((u16 *)x) +#define READ32LE(x) \ + *((u32 *)x) +#define WRITE16LE(x,v) \ + *((u16 *)x) = (v) +#define WRITE32LE(x,v) \ + *((u32 *)x) = (v) +#endif + +#endif // PORT_H diff --git a/src/common/RingBuffer.h b/src/common/RingBuffer.h new file mode 100644 index 0000000..137df0a --- /dev/null +++ b/src/common/RingBuffer.h @@ -0,0 +1,112 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef RINGBUFFER_H +#define RINGBUFFER_H + +#include "Array.h" +#include +#include +#include + +template +class RingBuffer { + Array buf; + std::size_t sz; + std::size_t rpos; + std::size_t wpos; + +public: + RingBuffer(const std::size_t sz_in = 0) : sz(0), rpos(0), wpos(0) { reset(sz_in); } + + std::size_t avail() const { + return (wpos < rpos ? 0 : sz) + rpos - wpos - 1; + } + + void clear() { + wpos = rpos = 0; + } + + void fill(T value); + + void read(T *out, std::size_t num); + + void reset(std::size_t sz_in); + + std::size_t size() const { + return sz - 1; + } + + std::size_t used() const { + return (wpos < rpos ? sz : 0) + wpos - rpos; + } + + void write(const T *in, std::size_t num); +}; + +template +void RingBuffer::fill(const T value) { + std::fill(buf + 0, buf + sz, value); + rpos = 0; + wpos = sz - 1; +} + +template +void RingBuffer::read(T *out, std::size_t num) { + if (rpos + num > sz) { + const std::size_t n = sz - rpos; + + std::memcpy(out, buf + rpos, n * sizeof(T)); + + rpos = 0; + num -= n; + out += n; + } + + std::memcpy(out, buf + rpos, num * sizeof(T)); + + if ((rpos += num) == sz) + rpos = 0; +} + +template +void RingBuffer::reset(const std::size_t sz_in) { + sz = sz_in + 1; + rpos = wpos = 0; + buf.reset(sz_in ? sz : 0); +} + +template +void RingBuffer::write(const T *in, std::size_t num) { + if (wpos + num > sz) { + const std::size_t n = sz - wpos; + + std::memcpy(buf + wpos, in, n * sizeof(T)); + + wpos = 0; + num -= n; + in += n; + } + + std::memcpy(buf + wpos, in, num * sizeof(T)); + + if ((wpos += num) == sz) + wpos = 0; +} + +#endif diff --git a/src/common/SoundDriver.h b/src/common/SoundDriver.h new file mode 100644 index 0000000..b42936c --- /dev/null +++ b/src/common/SoundDriver.h @@ -0,0 +1,65 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 2008 VBA-M development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef __VBA_SOUND_DRIVER_H__ +#define __VBA_SOUND_DRIVER_H__ + +#include "Types.h" + +/** + * Sound driver abstract interface for the core to use to output sound. + * Subclass this to implement a new sound driver. + */ +class SoundDriver +{ +public: + + /** + * Destructor. Free the resources allocated by the sound driver. + */ + virtual ~SoundDriver() { }; + + /** + * Initialize the sound driver. + * @param sampleRate In Hertz + */ + virtual bool init(long sampleRate) = 0; + + /** + * Tell the driver that the sound stream has paused + */ + virtual void pause() = 0; + + /** + * Reset the sound driver + */ + virtual void reset() = 0; + + /** + * Tell the driver that the sound stream has resumed + */ + virtual void resume() = 0; + + /** + * Write length bytes of data from the finalWave buffer to the driver output buffer. + */ + virtual void write(u16 * finalWave, int length) = 0; + + virtual void setThrottle(unsigned short throttle) { }; +}; + +#endif // __VBA_SOUND_DRIVER_H__ diff --git a/src/common/SoundSDL.cpp b/src/common/SoundSDL.cpp new file mode 100644 index 0000000..6f5376a --- /dev/null +++ b/src/common/SoundSDL.cpp @@ -0,0 +1,171 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 2008 VBA-M development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "SoundSDL.h" + +extern int emulating; +extern bool speedup; + +// Hold up to 100 ms of data in the ring buffer +const float SoundSDL::_delay = 0.1f; + +SoundSDL::SoundSDL(): + _rbuf(0), + _initialized(false) +{ + +} + +void SoundSDL::soundCallback(void *data, u8 *stream, int len) +{ + reinterpret_cast(data)->read(reinterpret_cast(stream), len); +} + +void SoundSDL::read(u16 * stream, int length) +{ + if (!_initialized || length <= 0 || !emulating) + return; + + + /* since this is running in a different thread, speedup and + * throttle can change at any time; save the value so locks + * stay in sync */ + bool lock = (emulating && !speedup) ? true : false; + + if (lock) + SDL_SemWait (_semBufferFull); + + SDL_mutexP(_mutex); + + _rbuf.read(stream, std::min(static_cast(length) / 2, _rbuf.used())); + + SDL_mutexV(_mutex); + + SDL_SemPost (_semBufferEmpty); +} + +void SoundSDL::write(u16 * finalWave, int length) +{ + if (!_initialized) + return; + + if (SDL_GetAudioStatus() != SDL_AUDIO_PLAYING) + SDL_PauseAudio(0); + + SDL_mutexP(_mutex); + + unsigned int samples = length / 4; + + std::size_t avail; + while ((avail = _rbuf.avail() / 2) < samples) + { + bool lock = (emulating && !speedup) ? true : false; + + _rbuf.write(finalWave, avail * 2); + + finalWave += avail * 2; + samples -= avail; + + SDL_mutexV(_mutex); + SDL_SemPost(_semBufferFull); + if (lock) + { + SDL_SemWait(_semBufferEmpty); + } + else + { + // Drop the remaining of the audio data + return; + } + SDL_mutexP(_mutex); + } + + _rbuf.write(finalWave, samples * 2); + + SDL_mutexV(_mutex); +} + +bool SoundSDL::init(long sampleRate) +{ + SDL_AudioSpec audio; + audio.freq = sampleRate; + audio.format = AUDIO_S16SYS; + audio.channels = 2; + audio.samples = 1024; + audio.callback = soundCallback; + audio.userdata = this; + + if(SDL_OpenAudio(&audio, NULL)) + { + fprintf(stderr,"Failed to open audio: %s\n", SDL_GetError()); + return false; + } + + _rbuf.reset(_delay * sampleRate * 2); + + _mutex = SDL_CreateMutex(); + _semBufferFull = SDL_CreateSemaphore (0); + _semBufferEmpty = SDL_CreateSemaphore (1); + _initialized = true; + + return true; +} + +SoundSDL::~SoundSDL() +{ + if (!_initialized) + return; + + SDL_mutexP(_mutex); + int iSave = emulating; + emulating = 0; + SDL_SemPost(_semBufferFull); + SDL_SemPost(_semBufferEmpty); + SDL_mutexV(_mutex); + + SDL_DestroySemaphore(_semBufferFull); + SDL_DestroySemaphore(_semBufferEmpty); + _semBufferFull = NULL; + _semBufferEmpty = NULL; + + SDL_DestroyMutex(_mutex); + _mutex = NULL; + + SDL_CloseAudio(); + + emulating = iSave; +} + +void SoundSDL::pause() +{ + if (!_initialized) + return; + + SDL_PauseAudio(1); +} + +void SoundSDL::resume() +{ + if (!_initialized) + return; + + SDL_PauseAudio(0); +} + +void SoundSDL::reset() +{ +} diff --git a/src/common/SoundSDL.h b/src/common/SoundSDL.h new file mode 100644 index 0000000..bff5f42 --- /dev/null +++ b/src/common/SoundSDL.h @@ -0,0 +1,54 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 2008 VBA-M development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef __VBA_SOUND_SDL_H__ +#define __VBA_SOUND_SDL_H__ + +#include "SoundDriver.h" +#include "RingBuffer.h" + +#include + +class SoundSDL: public SoundDriver +{ +public: + SoundSDL(); + virtual ~SoundSDL(); + + virtual bool init(long sampleRate); + virtual void pause(); + virtual void reset(); + virtual void resume(); + virtual void write(u16 * finalWave, int length); + +private: + RingBuffer _rbuf; + + SDL_mutex * _mutex; + SDL_sem *_semBufferFull; + SDL_sem *_semBufferEmpty; + + bool _initialized; + + // Defines what delay in seconds we keep in the sound buffer + static const float _delay; + + static void soundCallback(void *data, u8 *stream, int length); + virtual void read(u16 * stream, int length); +}; + +#endif // __VBA_SOUND_SDL_H__ diff --git a/src/common/Types.h b/src/common/Types.h new file mode 100644 index 0000000..d8bd4c1 --- /dev/null +++ b/src/common/Types.h @@ -0,0 +1,33 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 2008 VBA-M development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef __VBA_TYPES_H__ +#define __VBA_TYPES_H__ + +#include + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef int8_t s8; +typedef int16_t s16; +typedef int32_t s32; +typedef int64_t s64; + +#endif // __VBA_TYPES_H__ diff --git a/src/common/ffmpeg.cpp b/src/common/ffmpeg.cpp new file mode 100644 index 0000000..3c67e40 --- /dev/null +++ b/src/common/ffmpeg.cpp @@ -0,0 +1,481 @@ +// this code has been partially lifted from the output-example.c program in +// libavformat. Not much of that original code remains. + +// unlike the rest of the wx code, this has no wx dependency at all, and +// could be used by other front ends as well. + +#define __STDC_LIMIT_MACROS // required for ffmpeg +#define __STDC_CONSTANT_MACROS // required for ffmpeg + +#include "../gba/Sound.h" +extern "C" { +#include +#include +#include +#include +#include +#ifndef AV_PKT_FLAG_KEY +#define AV_PKT_FLAG_KEY PKT_FLAG_KEY +#endif +#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(52,96,0) +// note that there is no sane way to easily free a context w/o free_context() +// so this will probably leak +static void avformat_free_context(AVFormatContext *ctx) +{ + if(ctx->pb) + url_fclose(ctx->pb); + for(int i = 0; i < ctx->nb_streams; i++) { + if(ctx->streams[i]->codec) + avcodec_close(ctx->streams[i]->codec); + av_freep(&ctx->streams[i]->codec); + av_freep(&ctx->streams[i]); + } + av_free(ctx); +} +#endif +#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(52,45,0) +#define av_guess_format guess_format +#endif +#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(52,105,0) +#define avio_open url_fopen +#define avio_close url_fclose +#endif +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(52,64,0) +#define AVMEDIA_TYPE_VIDEO CODEC_TYPE_VIDEO +#define AVMEDIA_TYPE_AUDIO CODEC_TYPE_AUDIO +#endif +#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(50,1,0) +// this will almost definitely fail on big-endian systems +#define PIX_FMT_RGB565LE PIX_FMT_RGB565 +#endif +#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(50,38,0) +#define AV_SAMPLE_FMT_S16 SAMPLE_FMT_S16 +#endif +} + +#define priv_AVFormatContext AVFormatContext +#define priv_AVStream AVStream +#define priv_AVOutputFormat AVOutputFormat +#define priv_AVFrame AVFrame +#define priv_SwsContext SwsContext +#define priv_PixelFormat PixelFormat +#include "ffmpeg.h" + +// I have no idea what size to make these buffers +// I don't see any ffmpeg functions to guess the size, either + +// use frame size, or FF_MIN_BUFFER_SIZE (that seems to be what it wants) +#define AUDIO_BUF_LEN (frame_len > FF_MIN_BUFFER_SIZE ? frame_len : FF_MIN_BUFFER_SIZE) +// use maximum frame size * 32 bpp * 2 for good measure +#define VIDEO_BUF_LEN (FF_MIN_BUFFER_SIZE + 256 * 244 * 4 * 2) + +bool MediaRecorder::did_init = false; + +MediaRecorder::MediaRecorder() : oc(0), vid_st(0), aud_st(0), video_buf(0), + audio_buf(0), audio_buf2(0), converter(0), convpic(0) +{ + if(!did_init) { + did_init = true; + av_register_all(); + } + pic = avcodec_alloc_frame(); +} + +MediaRet MediaRecorder::setup_sound_stream(const char *fname, AVOutputFormat *fmt) +{ + oc = avformat_alloc_context(); + if(!oc) + return MRET_ERR_NOMEM; + oc->oformat = fmt; + strncpy(oc->filename, fname, sizeof(oc->filename) - 1); + oc->filename[sizeof(oc->filename) - 1] = 0; + if(fmt->audio_codec == CODEC_ID_NONE) + return MRET_OK; + + AVCodecContext *ctx; +#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53,10,0) + aud_st = av_new_stream(oc, 1); +#else + aud_st = avformat_new_stream(oc, NULL); +#endif + if(!aud_st) { + avformat_free_context(oc); + oc = NULL; + return MRET_ERR_NOMEM; + } + ctx = aud_st->codec; + ctx->codec_id = fmt->audio_codec; + ctx->codec_type = AVMEDIA_TYPE_AUDIO; + ctx->sample_fmt = AV_SAMPLE_FMT_S16; + ctx->bit_rate = 128000; // arbitrary; in case we're generating mp3 + ctx->sample_rate = soundGetSampleRate(); + ctx->channels = 2; + ctx->time_base.den = 60; + ctx->time_base.num = 1; + if(fmt->flags & AVFMT_GLOBALHEADER) + ctx->flags |= CODEC_FLAG_GLOBAL_HEADER; + + AVCodec *codec = avcodec_find_encoder(fmt->audio_codec); +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(53,6,0) + if(!codec || avcodec_open(ctx, codec)) { +#else + if(!codec || avcodec_open2(ctx, codec, NULL)) { +#endif + avformat_free_context(oc); + oc = NULL; + return MRET_ERR_NOCODEC; + } + + return MRET_OK; +} + +MediaRet MediaRecorder::setup_video_stream(const char *fname, int w, int h, int d) +{ + AVCodecContext *ctx; +#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53,10,0) + vid_st = av_new_stream(oc, 0); +#else + vid_st = avformat_new_stream(oc, NULL); +#endif + if(!vid_st) { + avformat_free_context(oc); + oc = NULL; + return MRET_ERR_NOMEM; + } + ctx = vid_st->codec; + ctx->codec_id = oc->oformat->video_codec; + ctx->codec_type = AVMEDIA_TYPE_VIDEO; + ctx->width = w; + ctx->height = h; + ctx->time_base.den = 60; + ctx->time_base.num = 1; + // dunno if any of these help; some output just looks plain crappy + // will have to investigate further + ctx->bit_rate = 400000; + ctx->gop_size = 12; + ctx->max_b_frames = 2; + switch(d) { + case 16: + // FIXME: test & make endian-neutral + pixfmt = PIX_FMT_RGB565LE; + break; + case 24: + pixfmt = PIX_FMT_RGB24; + break; + case 32: + default: // should never be anything else + pixfmt = PIX_FMT_RGBA; + break; + } + ctx->pix_fmt = pixfmt; + pixsize = d >> 3; + linesize = pixsize * w; + ctx->max_b_frames = 2; + if(oc->oformat->flags & AVFMT_GLOBALHEADER) + ctx->flags |= CODEC_FLAG_GLOBAL_HEADER; + + AVCodec *codec = avcodec_find_encoder(oc->oformat->video_codec); + // make sure RGB is supported (mostly not) + if(codec->pix_fmts) { + const enum PixelFormat *p; + int64_t mask = 0; + for(p = codec->pix_fmts; *p != -1; p++) { + // may get complaints about 1LL; thus the cast + mask |= ((int64_t)1) << *p; + if(*p == pixfmt) + break; + } + if(*p == -1) { + // if not supported, use a converter to the next best format + // this is swscale, the converter used by the output demo + enum PixelFormat dp = (PixelFormat)avcodec_find_best_pix_fmt(mask, pixfmt, 0, NULL); + if(dp == -1) + dp = codec->pix_fmts[0]; + if(!(convpic = avcodec_alloc_frame()) || + avpicture_alloc((AVPicture *)convpic, dp, w, h) < 0) { + avformat_free_context(oc); + oc = NULL; + return MRET_ERR_NOMEM; + } +#if LIBSWSCALE_VERSION_INT < AV_VERSION_INT(0, 12, 0) + converter = sws_getContext(w, h, pixfmt, w, h, dp, SWS_BICUBIC, + NULL, NULL, NULL); +#else + converter = sws_alloc_context(); + // what a convoluted, inefficient way to set options + av_opt_set_int(converter, "sws_flags", SWS_BICUBIC, 0); + av_opt_set_int(converter, "srcw", w, 0); + av_opt_set_int(converter, "srch", h, 0); + av_opt_set_int(converter, "dstw", w, 0); + av_opt_set_int(converter, "dsth", h, 0); + av_opt_set_int(converter, "src_format", pixfmt, 0); + av_opt_set_int(converter, "dst_format", dp, 0); + sws_init_context(converter, NULL, NULL); +#endif + ctx->pix_fmt = dp; + } + } +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(53,6,0) + if(!codec || avcodec_open(ctx, codec)) { +#else + if(!codec || avcodec_open2(ctx, codec, NULL)) { +#endif + avformat_free_context(oc); + oc = NULL; + return MRET_ERR_NOCODEC; + } + + return MRET_OK; +} + +MediaRet MediaRecorder::finish_setup(const char *fname) +{ + if(audio_buf) + free(audio_buf); + if(audio_buf2) + free(audio_buf2); + audio_buf2 = NULL; + in_audio_buf2 = 0; + if(aud_st) { + frame_len = aud_st->codec->frame_size * 4; + sample_len = soundGetSampleRate() * 4 / 60; + switch(aud_st->codec->codec_id) { + case CODEC_ID_PCM_S16LE: + case CODEC_ID_PCM_S16BE: + case CODEC_ID_PCM_U16LE: + case CODEC_ID_PCM_U16BE: + frame_len = sample_len; + } + audio_buf = (u8 *)malloc(AUDIO_BUF_LEN); + if(!audio_buf) { + avformat_free_context(oc); + oc = NULL; + return MRET_ERR_NOMEM; + } + if(frame_len != sample_len && (frame_len > sample_len || sample_len % frame_len)) { + audio_buf2 = (u16 *)malloc(frame_len); + if(!audio_buf2) { + avformat_free_context(oc); + oc = NULL; + return MRET_ERR_NOMEM; + } + } + } else + audio_buf = NULL; + if(video_buf) + free(video_buf); + if(vid_st) { + video_buf = (u8 *)malloc(VIDEO_BUF_LEN); + if(!video_buf) { + avformat_free_context(oc); + oc = NULL; + return MRET_ERR_NOMEM; + } + } else { + video_buf = NULL; + } + if(!(oc->oformat->flags & AVFMT_NOFILE)) { + if(avio_open(&oc->pb, fname, AVIO_FLAG_WRITE) < 0) { + avformat_free_context(oc); + oc = NULL; + return MRET_ERR_FERR; + } + } + avformat_write_header(oc, NULL); + return MRET_OK; +} + +MediaRet MediaRecorder::Record(const char *fname, int width, int height, int depth) +{ + if(oc) + return MRET_ERR_RECORDING; + aud_st = vid_st = NULL; + AVOutputFormat *fmt = av_guess_format(NULL, fname, NULL); + if(!fmt) + fmt = av_guess_format("avi", NULL, NULL); + if(!fmt || fmt->video_codec == CODEC_ID_NONE) + return MRET_ERR_FMTGUESS; + MediaRet ret; + if((ret = setup_sound_stream(fname, fmt)) == MRET_OK && + (ret = setup_video_stream(fname, width, height, depth)) == MRET_OK) + ret = finish_setup(fname); + return ret; +} + +MediaRet MediaRecorder::Record(const char *fname) +{ + if(oc) + return MRET_ERR_RECORDING; + aud_st = vid_st = NULL; + AVOutputFormat *fmt = av_guess_format(NULL, fname, NULL); + if(!fmt) + fmt = av_guess_format("wav", NULL, NULL); + if(!fmt || fmt->audio_codec == CODEC_ID_NONE) + return MRET_ERR_FMTGUESS; + MediaRet ret; + if((ret = setup_sound_stream(fname, fmt)) == MRET_OK) + ret = finish_setup(fname); + return ret; +} + +void MediaRecorder::Stop() +{ + if(oc) { + if(in_audio_buf2) + AddFrame((u16 *)0); + av_write_trailer(oc); + avformat_free_context(oc); + oc = NULL; + } + if(audio_buf) { + free(audio_buf); + audio_buf = NULL; + } + if(video_buf) { + free(video_buf); + video_buf = NULL; + } + if(audio_buf2) { + free(audio_buf2); + audio_buf2 = NULL; + } + if(convpic) { + avpicture_free((AVPicture *)convpic); + av_free(convpic); + convpic = NULL; + } + if(converter) { + sws_freeContext(converter); + converter = NULL; + } +} + +MediaRecorder::~MediaRecorder() +{ + Stop(); +} + +MediaRet MediaRecorder::AddFrame(const u8 *vid) +{ + if(!oc || !vid_st) + return MRET_OK; + + AVCodecContext *ctx = vid_st->codec; + AVPacket pkt; + + // strip borders. inconsistent between depths for some reason + // but fortunately consistent between gb/gba. + int tbord, rbord; + switch(pixsize) { + case 2: + // 16-bit: 2 @ right, 1 @ top + tbord = 1; rbord = 2; break; + case 3: + // 24-bit: no border + tbord = rbord = 0; break; + case 4: + // 32-bit: 1 @ right, 1 @ top + tbord = 1; rbord = 1; break; + } + avpicture_fill((AVPicture *)pic, (uint8_t *)vid + tbord * (linesize + pixsize * rbord), + (PixelFormat)pixfmt, ctx->width + rbord, ctx->height); + // satisfy stupid sws_scale()'s integrity check + pic->data[1] = pic->data[2] = pic->data[3] = pic->data[0]; + pic->linesize[1] = pic->linesize[2] = pic->linesize[3] = pic->linesize[0]; + + AVFrame *f = pic; + + if(converter) { + sws_scale(converter, pic->data, pic->linesize, 0, ctx->height, + convpic->data, convpic->linesize); + f = convpic; + } + av_init_packet(&pkt); + pkt.stream_index = vid_st->index; + if(oc->oformat->flags & AVFMT_RAWPICTURE) { + // this won't work due to border + // not sure what formats set this, anyway + pkt.flags |= AV_PKT_FLAG_KEY; + pkt.data = f->data[0]; + pkt.size = linesize * ctx->height; + } else { + pkt.size = avcodec_encode_video(ctx, video_buf, VIDEO_BUF_LEN, f); + if(!pkt.size) + return MRET_OK; + if(ctx->coded_frame && ctx->coded_frame->pts != AV_NOPTS_VALUE) + pkt.pts = av_rescale_q(ctx->coded_frame->pts, ctx->time_base, vid_st->time_base); + if(pkt.size > VIDEO_BUF_LEN) { + avformat_free_context(oc); + oc = NULL; + return MRET_ERR_BUFSIZE; + } + if(ctx->coded_frame->key_frame) + pkt.flags |= AV_PKT_FLAG_KEY; + pkt.data = video_buf; + } + if(av_interleaved_write_frame(oc, &pkt) < 0) { + avformat_free_context(oc); + oc = NULL; + // yeah, err might not be a file error, but if it isn't, it's a + // coding error rather than a user-controllable error + // and better resolved using debugging + return MRET_ERR_FERR; + } + return MRET_OK; +} + +MediaRet MediaRecorder::AddFrame(const u16 *aud) +{ + if(!oc || !aud_st) + return MRET_OK; + // aud == NULL means just flush out last frame + if(!aud && !in_audio_buf2) + return MRET_OK; + AVCodecContext *ctx = aud_st->codec; + AVPacket pkt; + + int len = sample_len; + if(in_audio_buf2) { + int ncpy = frame_len - in_audio_buf2; + if(ncpy > len) + ncpy = len; + if(aud) { + memcpy(audio_buf2 + in_audio_buf2/2, aud, ncpy); + len -= ncpy; + aud += ncpy / 2; + } else { + memset(audio_buf2 + in_audio_buf2/2, 0, ncpy); + len = 0; + } + in_audio_buf2 += ncpy; + } + while(len + in_audio_buf2 >= frame_len) { + av_init_packet(&pkt); + pkt.size = avcodec_encode_audio(ctx, audio_buf, frame_len, + (const short *)(in_audio_buf2 ? audio_buf2 : aud)); + if(ctx->coded_frame && ctx->coded_frame->pts != AV_NOPTS_VALUE) + pkt.pts = av_rescale_q(ctx->coded_frame->pts, ctx->time_base, aud_st->time_base); + pkt.flags |= AV_PKT_FLAG_KEY; + pkt.stream_index = aud_st->index; + pkt.data = audio_buf; + if(av_interleaved_write_frame(oc, &pkt) < 0) { + avformat_free_context(oc); + oc = NULL; + // yeah, err might not be a file error, but if it isn't, it's a + // coding error rather than a user-controllable error + // and better resolved using debugging + return MRET_ERR_FERR; + } + if(in_audio_buf2) + in_audio_buf2 = 0; + else { + aud += frame_len / 2; + len -= frame_len; + } + } + if(len > 0) { + memcpy(audio_buf2, aud, len); + in_audio_buf2 = len; + } + return MRET_OK; +} diff --git a/src/common/ffmpeg.h b/src/common/ffmpeg.h new file mode 100644 index 0000000..f27bb3d --- /dev/null +++ b/src/common/ffmpeg.h @@ -0,0 +1,79 @@ +#ifndef WX_FFMPEG_H +#define WX_FFMPEG_H + +// simplified interface for recording audio and/or video from emulator + +// unlike the rest of the wx code, this has no wx dependency at all, and +// could be used by other front ends as well. + +// this only supports selecting output format via file name extensions; +// maybe some future version will support specifying a format. wx-2.9 +// has an extra widget for the file selector, but 2.8 doesn't. + +// the only missing piece that I couldn't figure out how to do generically +// is the code to find the available formats & associated extensions for +// the file dialog. + +#include "../common/Types.h" + +// return codes +// probably ought to put in own namespace, but this is good enough +enum MediaRet { + MRET_OK, // no errors + MRET_ERR_NOMEM, // error allocating buffers or structures + MRET_ERR_NOCODEC, // error opening codec + MRET_ERR_FERR, // error writing output file + MRET_ERR_RECORDING, // attempt to start recording when already doing it + MRET_ERR_FMTGUESS, // can't guess format from file name + MRET_ERR_BUFSIZE // buffer overflow (fatal) +}; + +class MediaRecorder +{ +public: + MediaRecorder(); + virtual ~MediaRecorder(); + + // start audio+video (also video-only codecs) + MediaRet Record(const char *fname, int width, int height, int depth); + // start audio only + MediaRet Record(const char *fname); + // stop both + void Stop(); + bool IsRecording() { return oc != NULL; } + // add a frame of video; width+height+depth already given + // assumes a 1-pixel border on top & right + // always assumes being passed 1/60th of a second of video + MediaRet AddFrame(const u8 *vid); + // add a frame of audio; uses current sample rate to know length + // always assumes being passed 1/60th of a second of audio. + MediaRet AddFrame(const u16 *aud); + +private: + static bool did_init; + + // these are to avoid polluting things with avcodec includes +#ifndef priv_AVFormatContext +#define priv_AVFormatContext void +#define priv_AVStream void +#define priv_AVOutputFormat void +#define priv_AVFrame void +#define priv_SwsContext void +#define priv_PixelFormat int +#endif + priv_AVFormatContext *oc; + priv_AVStream *vid_st, *aud_st; + u8 *audio_buf, *video_buf; + u16 *audio_buf2; + int frame_len, sample_len, in_audio_buf2; + int linesize, pixsize; + priv_PixelFormat pixfmt; + priv_AVFrame *pic, *convpic; + priv_SwsContext *converter; + + MediaRet setup_sound_stream(const char *fname, priv_AVOutputFormat *fmt); + MediaRet setup_video_stream(const char *fname, int w, int h, int d); + MediaRet finish_setup(const char *fname); +}; + +#endif /* WX_FFMPEG_H */ diff --git a/src/common/memgzio.c b/src/common/memgzio.c new file mode 100644 index 0000000..b526b1e --- /dev/null +++ b/src/common/memgzio.c @@ -0,0 +1,719 @@ +/* gzio.c -- IO on .gz files + * Copyright (C) 1995-2002 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Compile this file with -DNO_DEFLATE to avoid the compression code. + */ + +/* memgzio.c - IO on .gz files in memory + * Adapted from original gzio.c from zlib library by Forgotten + */ + +/* @(#) $Id: memgzio.c 1213 2013-09-21 13:57:40Z kode54 $ */ + +#include +#include +#include +#include +#include + +#include "memgzio.h" + +#ifndef local +#define local static +#endif + +#ifndef DEF_MEM_LEVEL +# define DEF_MEM_LEVEL 8 +#endif + +#ifndef OS_CODE +#define OS_CODE 3 +#endif + +#ifndef zmemcpy +#define zmemcpy memcpy +#endif + + +/*struct internal_state {int dummy;};*/ /* for buggy compilers */ + +#ifndef Z_BUFSIZE +# ifdef MAXSEG_64K +# define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */ +# else +# define Z_BUFSIZE 16384 +# endif +#endif +#ifndef Z_PRINTF_BUFSIZE +# define Z_PRINTF_BUFSIZE 4096 +#endif + +#define ALLOC(size) malloc(size) +#define TRYFREE(p) {if (p) free(p);} + +static int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define RESERVED 0xE0 /* bits 5..7: reserved */ + +typedef struct _MemFile { + char *memory; + char *next; + int available; + int error; + char mode; +} MEMFILE; + +typedef struct mem_stream { + z_stream stream; + int z_err; /* error code for last stream operation */ + int z_eof; /* set if end of input file */ + MEMFILE *file; /* memoru file */ + Byte *inbuf; /* input buffer */ + Byte *outbuf; /* output buffer */ + uLong crc; /* crc32 of uncompressed data */ + char *msg; /* error message */ + int transparent; /* 1 if input file is not a .gz file */ + char mode; /* 'w' or 'r' */ + long startpos; /* start of compressed data in file (header skipped) */ +} mem_stream; + + +local gzFile gz_open OF((char *memory, const int available, const char *mode)); +local int do_flush OF((gzFile file, int flush)); +local int get_byte OF((mem_stream *s)); +local void check_header OF((mem_stream *s)); +local int destroy OF((mem_stream *s)); +local void putLong OF((MEMFILE *file, uLong x)); +local uLong getLong OF((mem_stream *s)); + +local MEMFILE *memOpen(char *memory, int available, char mode) +{ + MEMFILE *f; + + if(available <= 8) + return NULL; + + if(mode != 'w' && mode != 'r') + return NULL; + + f = (MEMFILE *)malloc(sizeof(MEMFILE)); + + f->memory = memory; + f->mode = mode; + f->error = 0; + + if(mode == 'w') { + f->available = available - 8; + f->next = memory + 8; + memory[0] = 'V'; + memory[1] = 'B'; + memory[2] = 'A'; + memory[3] = ' '; + *((int *)(memory+4)) = 0; + } else { + if(memory[0] != 'V' || memory[1] != 'B' || memory[2] != 'A' || + memory[3] != ' ') { + free(f); + return NULL; + } + f->available = *((int *)(memory+4)); + f->next = memory+8; + } + + return f; +} + +local size_t memWrite(const void *buffer, size_t size, size_t count, + MEMFILE *file) +{ + size_t total = size*count; + + if(file->mode != 'w') { + file->error = 1; + return 0; + } + + if(total > (size_t)file->available) { + total = file->available; + } + memcpy(file->next, buffer, total); + file->available -= (int)total; + file->next += total; + return total; +} + +local size_t memRead(void *buffer, size_t size, size_t count, + MEMFILE *file) +{ + size_t total = size*count; + + if(file->mode != 'r') { + file->error = 1; + return 0; + } + + if(file->available == 0) + return -1; + + if(total > (size_t)file->available) { + total = file->available; + } + memcpy(buffer, file->next, total); + file->available -= (int)total; + file->next += total; + return total; +} + +local int memPutc(int c, MEMFILE *file) +{ + if(file->mode != 'w') { + file->error = 1; + return -1; + } + + if(file->available >= 1) { + *file->next++ = c; + file->available--; + } else + return -1; + + return c; +} + +local long memTell(MEMFILE *f) +{ + return (long)(f->next - f->memory) - 8; +} + +local int memError(MEMFILE *f) +{ + return f->error; +} + +local int memClose(MEMFILE *f) +{ + if(f->mode == 'w') { + *((int *)(f->memory+4)) = memTell(f); + } + free(f); + return 0; +} + +local int memPrintf(MEMFILE *f, const char *format, ...) +{ + char buffer[80]; + va_list list; + int len; + + va_start(list, format); + len = vsprintf(buffer, format, list); + va_end(list); + + return (int)memWrite(buffer, 1, len, f); +} + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb"). The file is given either by file descriptor + or path name (if fd == -1). + gz_open return NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). +*/ +local gzFile gz_open (memory, available, mode) + char *memory; + const int available; + const char *mode; +{ + int err; + int level = Z_DEFAULT_COMPRESSION; /* compression level */ + int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */ + char *p = (char*)mode; + mem_stream *s; + char fmode[80]; /* copy of mode, without the compression level */ + char *m = fmode; + + s = (mem_stream *)ALLOC(sizeof(mem_stream)); + if (!s) return Z_NULL; + + s->stream.zalloc = (alloc_func)0; + s->stream.zfree = (free_func)0; + s->stream.opaque = (voidpf)0; + s->stream.next_in = s->inbuf = Z_NULL; + s->stream.next_out = s->outbuf = Z_NULL; + s->stream.avail_in = s->stream.avail_out = 0; + s->z_err = Z_OK; + s->z_eof = 0; + s->crc = crc32(0L, Z_NULL, 0); + s->msg = NULL; + s->transparent = 0; + s->file = NULL; + + s->mode = '\0'; + do { + if (*p == 'r') s->mode = 'r'; + if (*p == 'w' || *p == 'a') s->mode = 'w'; + if (*p >= '0' && *p <= '9') { + level = *p - '0'; + } else if (*p == 'f') { + strategy = Z_FILTERED; + } else if (*p == 'h') { + strategy = Z_HUFFMAN_ONLY; + } else { + *m++ = *p; /* copy the mode */ + } + } while (*p++ && m != fmode + sizeof(fmode)); + if (s->mode == '\0') return destroy(s), (gzFile)Z_NULL; + + if (s->mode == 'w') { +#ifdef NO_DEFLATE + err = Z_STREAM_ERROR; +#else + err = deflateInit2(&(s->stream), level, + Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy); + /* windowBits is passed < 0 to suppress zlib header */ + + s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); +#endif + if (err != Z_OK || s->outbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } else { + s->stream.next_in = s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); + + err = inflateInit2(&(s->stream), -MAX_WBITS); + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are + * present after the compressed stream. + */ + if (err != Z_OK || s->inbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } + s->stream.avail_out = Z_BUFSIZE; + + errno = 0; + s->file = memOpen(memory, available, s->mode); + + if (s->file == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + + if (s->mode == 'w') { + /* Write a very simple .gz header: + */ + memPrintf(s->file, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1], + Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE); + s->startpos = 10L; + /* We use 10L instead of ftell(s->file) to because ftell causes an + * fflush on some systems. This version of the library doesn't use + * startpos anyway in write mode, so this initialization is not + * necessary. + */ + } else { + check_header(s); /* skip the .gz header */ + s->startpos = (memTell(s->file) - s->stream.avail_in); + } + + return (gzFile)s; +} + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. +*/ +gzFile ZEXPORT memgzopen (memory, available, mode) + char *memory; + int available; + const char *mode; +{ + return gz_open (memory, available, mode); +} + +/* =========================================================================== + Read a byte from a mem_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ +local int get_byte(s) + mem_stream *s; +{ + if (s->z_eof) return EOF; + if (s->stream.avail_in == 0) { + errno = 0; + s->stream.avail_in = (uInt)memRead(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (memError(s->file)) s->z_err = Z_ERRNO; + return EOF; + } + s->stream.next_in = s->inbuf; + } + s->stream.avail_in--; + return *(s->stream.next_in)++; +} + +/* =========================================================================== + Check the gzip header of a mem_stream opened for reading. Set the stream + mode to transparent if the gzip magic header is not present; set s->err + to Z_DATA_ERROR if the magic header is present but the rest of the header + is incorrect. + IN assertion: the stream s has already been created sucessfully; + s->stream.avail_in is zero for the first time, but may be non-zero + for concatenated .gz files. +*/ +local void check_header(s) + mem_stream *s; +{ + int method; /* method byte */ + int flags; /* flags byte */ + uInt len; + int c; + + /* Check the gzip magic header */ + for (len = 0; len < 2; len++) { + c = get_byte(s); + if (c != gz_magic[len]) { + if (len != 0) s->stream.avail_in++, s->stream.next_in--; + if (c != EOF) { + s->stream.avail_in++, s->stream.next_in--; + s->transparent = 1; + } + s->z_err = s->stream.avail_in != 0 ? Z_OK : Z_STREAM_END; + return; + } + } + method = get_byte(s); + flags = get_byte(s); + if (method != Z_DEFLATED || (flags & RESERVED) != 0) { + s->z_err = Z_DATA_ERROR; + return; + } + + /* Discard time, xflags and OS code: */ + for (len = 0; len < 6; len++) (void)get_byte(s); + + if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */ + len = (uInt)get_byte(s); + len += ((uInt)get_byte(s))<<8; + /* len is garbage if EOF but the loop below will quit anyway */ + while (len-- != 0 && get_byte(s) != EOF) ; + } + if ((flags & ORIG_NAME) != 0) { /* skip the original file name */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & COMMENT) != 0) { /* skip the .gz file comment */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & HEAD_CRC) != 0) { /* skip the header crc */ + for (len = 0; len < 2; len++) (void)get_byte(s); + } + s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK; +} + + /* =========================================================================== + * Cleanup then free the given mem_stream. Return a zlib error code. + Try freeing in the reverse order of allocations. + */ +local int destroy (s) + mem_stream *s; +{ + int err = Z_OK; + + if (!s) return Z_STREAM_ERROR; + + TRYFREE(s->msg); + + if (s->stream.state != NULL) { + if (s->mode == 'w') { +#ifdef NO_DEFLATE + err = Z_STREAM_ERROR; +#else + err = deflateEnd(&(s->stream)); +#endif + } else if (s->mode == 'r') { + err = inflateEnd(&(s->stream)); + } + } + if (s->file != NULL && memClose(s->file)) { +#ifdef ESPIPE + if (errno != ESPIPE) /* fclose is broken for pipes in HP/UX */ +#endif + err = Z_ERRNO; + } + if (s->z_err < 0) err = s->z_err; + + TRYFREE(s->inbuf); + TRYFREE(s->outbuf); + TRYFREE(s); + return err; +} + +/* =========================================================================== + Reads the given number of uncompressed bytes from the compressed file. + gzread returns the number of bytes actually read (0 for end of file). +*/ +int ZEXPORT memgzread (file, buf, len) + gzFile file; + voidp buf; + unsigned len; +{ + mem_stream *s = (mem_stream*)file; + Bytef *start = (Bytef*)buf; /* starting point for crc computation */ + Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */ + + if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR; + + if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1; + if (s->z_err == Z_STREAM_END) return 0; /* EOF */ + + next_out = (Byte*)buf; + s->stream.next_out = (Bytef*)buf; + s->stream.avail_out = len; + + while (s->stream.avail_out != 0) { + + if (s->transparent) { + /* Copy first the lookahead bytes: */ + uInt n = s->stream.avail_in; + if (n > s->stream.avail_out) n = s->stream.avail_out; + if (n > 0) { + zmemcpy(s->stream.next_out, s->stream.next_in, n); + next_out += n; + s->stream.next_out = next_out; + s->stream.next_in += n; + s->stream.avail_out -= n; + s->stream.avail_in -= n; + } + if (s->stream.avail_out > 0) { + s->stream.avail_out -= (uInt)memRead(next_out, 1, s->stream.avail_out, s->file); + } + len -= s->stream.avail_out; + s->stream.total_in += (uLong)len; + s->stream.total_out += (uLong)len; + if (len == 0) s->z_eof = 1; + return (int)len; + } + if (s->stream.avail_in == 0 && !s->z_eof) { + + errno = 0; + s->stream.avail_in = (uInt)memRead(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (memError(s->file)) { + s->z_err = Z_ERRNO; + break; + } + } + s->stream.next_in = s->inbuf; + } + s->z_err = inflate(&(s->stream), Z_NO_FLUSH); + + if (s->z_err == Z_STREAM_END) { + /* Check CRC and original size */ + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + start = s->stream.next_out; + + if (getLong(s) != s->crc) { + s->z_err = Z_DATA_ERROR; + } else { + (void)getLong(s); + /* The uncompressed length returned by above getlong() may + * be different from s->stream.total_out) in case of + * concatenated .gz files. Check for such files: + */ + check_header(s); + if (s->z_err == Z_OK) { + uLong total_in = s->stream.total_in; + uLong total_out = s->stream.total_out; + + inflateReset(&(s->stream)); + s->stream.total_in = total_in; + s->stream.total_out = total_out; + s->crc = crc32(0L, Z_NULL, 0); + } + } + } + if (s->z_err != Z_OK || s->z_eof) break; + } + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + + return (int)(len - s->stream.avail_out); +} + + +#ifndef NO_DEFLATE +/* =========================================================================== + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of bytes actually written (0 in case of error). +*/ +int ZEXPORT memgzwrite (file, buf, len) + gzFile file; + const voidp buf; + unsigned len; +{ + mem_stream *s = (mem_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.next_in = (Bytef*)buf; + s->stream.avail_in = len; + + while (s->stream.avail_in != 0) { + + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (memWrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + break; + } + s->stream.avail_out = Z_BUFSIZE; + } + s->z_err = deflate(&(s->stream), Z_NO_FLUSH); + if (s->z_err != Z_OK) break; + } + s->crc = crc32(s->crc, (const Bytef *)buf, len); + + return (int)(len - s->stream.avail_in); +} +#endif +/* =========================================================================== + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. +*/ +local int do_flush (file, flush) + gzFile file; + int flush; +{ + uInt len; + int done = 0; + mem_stream *s = (mem_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.avail_in = 0; /* should be zero already anyway */ + + for (;;) { + len = Z_BUFSIZE - s->stream.avail_out; + + if (len != 0) { + if ((uInt)memWrite(s->outbuf, 1, len, s->file) != len) { + s->z_err = Z_ERRNO; + return Z_ERRNO; + } + s->stream.next_out = s->outbuf; + s->stream.avail_out = Z_BUFSIZE; + } + if (done) break; + s->z_err = deflate(&(s->stream), flush); + + /* Ignore the second of two consecutive flushes: */ + if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK; + + /* deflate has finished flushing only when it hasn't used up + * all the available space in the output buffer: + */ + done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END); + + if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break; + } + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} + +/* =========================================================================== + Outputs a long in LSB order to the given file +*/ +local void putLong (file, x) + MEMFILE *file; + uLong x; +{ + int n; + for (n = 0; n < 4; n++) { + memPutc((int)(x & 0xff), file); + x >>= 8; + } +} + +/* =========================================================================== + Reads a long in LSB order from the given mem_stream. Sets z_err in case + of error. +*/ +local uLong getLong (s) + mem_stream *s; +{ + uLong x = (uLong)get_byte(s); + int c; + + x += ((uLong)get_byte(s))<<8; + x += ((uLong)get_byte(s))<<16; + c = get_byte(s); + if (c == EOF) s->z_err = Z_DATA_ERROR; + x += ((uLong)c)<<24; + return x; +} + +/* =========================================================================== + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. +*/ +int ZEXPORT memgzclose (file) + gzFile file; +{ + int err; + mem_stream *s = (mem_stream*)file; + + if (s == NULL) return Z_STREAM_ERROR; + + if (s->mode == 'w') { +#ifdef NO_DEFLATE + return Z_STREAM_ERROR; +#else + err = do_flush (file, Z_FINISH); + if (err != Z_OK) return destroy((mem_stream*)file); + + putLong (s->file, s->crc); + putLong (s->file, s->stream.total_in); +#endif + } + return destroy((mem_stream*)file); +} + +long ZEXPORT memtell(file) + gzFile file; +{ + mem_stream *s = (mem_stream*)file; + + if (s == NULL) return Z_STREAM_ERROR; + + return memTell(s->file); +} + +z_off_t ZEXPORT memgzseek(gzFile file, z_off_t off, int whence) +{ + char buf[80]; + + if(whence != SEEK_CUR) { + fputs("FIXME: memgzio does not support seeking\n", stderr); + exit(1); + } + + // this is inefficient, but the best I can do without actually reading + // the above code + while(off > 0) { + int r = memgzread(file, buf, off > 80 ? 80 : off); + if(r <= 0) + return -1; + off -= r; + } + return memtell(file); +} diff --git a/src/common/memgzio.h b/src/common/memgzio.h new file mode 100644 index 0000000..90a3963 --- /dev/null +++ b/src/common/memgzio.h @@ -0,0 +1,24 @@ +#ifndef MEMGZIO_H +#define MEMGZIO_H + +/* gzio.c -- IO on .gz files + * Copyright (C) 1995-2002 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Compile this file with -DNO_DEFLATE to avoid the compression code. + */ + +/* memgzio.c - IO on .gz files in memory + * Adapted from original gzio.c from zlib library by Forgotten + */ + +#include + +gzFile ZEXPORT memgzopen(char *memory, int available, const char *mode); +int ZEXPORT memgzread(gzFile file, voidp buf, unsigned len); +int ZEXPORT memgzwrite(gzFile file, const voidp buf, unsigned len); +int ZEXPORT memgzclose(gzFile file); +long ZEXPORT memtell(gzFile file); +z_off_t ZEXPORT memgzseek(gzFile file, z_off_t off, int whence); + +#endif // MEMGZIO_H diff --git a/src/filters/2xSaI.cpp b/src/filters/2xSaI.cpp new file mode 100644 index 0000000..5dbe287 --- /dev/null +++ b/src/filters/2xSaI.cpp @@ -0,0 +1,1278 @@ +#include "../System.h" + +extern int RGB_LOW_BITS_MASK; + +extern "C" +{ + +#ifdef MMX + void _2xSaILine (u8 *srcPtr, u8 *deltaPtr, u32 srcPitch, + u32 width, u8 *dstPtr, u32 dstPitch); + void _2xSaISuperEagleLine (u8 *srcPtr, u8 *deltaPtr, + u32 srcPitch, u32 width, + u8 *dstPtr, u32 dstPitch); + void _2xSaISuper2xSaILine (u8 *srcPtr, u8 *deltaPtr, + u32 srcPitch, u32 width, + u8 *dstPtr, u32 dstPitch); + void Init_2xSaIMMX (u32 BitFormat); + void BilinearMMX (u16 * A, u16 * B, u16 * C, u16 * D, + u16 * dx, u16 * dy, u8 *dP); + void BilinearMMXGrid0 (u16 * A, u16 * B, u16 * C, u16 * D, + u16 * dx, u16 * dy, u8 *dP); + void BilinearMMXGrid1 (u16 * A, u16 * B, u16 * C, u16 * D, + u16 * dx, u16 * dy, u8 *dP); + void EndMMX (); + + bool cpu_mmx = 1; +#endif +} +static u32 colorMask = 0xF7DEF7DE; +static u32 lowPixelMask = 0x08210821; +static u32 qcolorMask = 0xE79CE79C; +static u32 qlowpixelMask = 0x18631863; +static u32 redblueMask = 0xF81F; +static u32 greenMask = 0x7E0; + +u32 qRGB_COLOR_MASK[2] = { 0xF7DEF7DE, 0xF7DEF7DE }; + +extern void hq2x_init(unsigned); + +int Init_2xSaI(u32 BitFormat) +{ + if(systemColorDepth == 16) { + if (BitFormat == 565) { + colorMask = 0xF7DEF7DE; + lowPixelMask = 0x08210821; + qcolorMask = 0xE79CE79C; + qlowpixelMask = 0x18631863; + redblueMask = 0xF81F; + greenMask = 0x7E0; + qRGB_COLOR_MASK[0] = qRGB_COLOR_MASK[1] = 0xF7DEF7DE; + hq2x_init(16); + RGB_LOW_BITS_MASK = 0x0821; + } else if (BitFormat == 555) { + colorMask = 0x7BDE7BDE; + lowPixelMask = 0x04210421; + qcolorMask = 0x739C739C; + qlowpixelMask = 0x0C630C63; + redblueMask = 0x7C1F; + greenMask = 0x3E0; + qRGB_COLOR_MASK[0] = qRGB_COLOR_MASK[1] = 0x7BDE7BDE; + hq2x_init(15); + RGB_LOW_BITS_MASK = 0x0421; + } else { + return 0; + } + } else if(systemColorDepth == 32) { + colorMask = 0xfefefe; + lowPixelMask = 0x010101; + qcolorMask = 0xfcfcfc; + qlowpixelMask = 0x030303; + qRGB_COLOR_MASK[0] = qRGB_COLOR_MASK[1] = 0xfefefe; + hq2x_init(32); + RGB_LOW_BITS_MASK = 0x010101; + } else + return 0; + +#ifdef MMX + Init_2xSaIMMX (BitFormat); +#endif + + return 1; +} + +static inline int GetResult1 (u32 A, u32 B, u32 C, u32 D, + u32 /* E */) +{ + int x = 0; + int y = 0; + int r = 0; + + if (A == C) + x += 1; + else if (B == C) + y += 1; + if (A == D) + x += 1; + else if (B == D) + y += 1; + if (x <= 1) + r += 1; + if (y <= 1) + r -= 1; + return r; +} + +static inline int GetResult2 (u32 A, u32 B, u32 C, u32 D, + u32 /* E */) +{ + int x = 0; + int y = 0; + int r = 0; + + if (A == C) + x += 1; + else if (B == C) + y += 1; + if (A == D) + x += 1; + else if (B == D) + y += 1; + if (x <= 1) + r -= 1; + if (y <= 1) + r += 1; + return r; +} + +static inline int GetResult (u32 A, u32 B, u32 C, u32 D) +{ + int x = 0; + int y = 0; + int r = 0; + + if (A == C) + x += 1; + else if (B == C) + y += 1; + if (A == D) + x += 1; + else if (B == D) + y += 1; + if (x <= 1) + r += 1; + if (y <= 1) + r -= 1; + return r; +} + +static inline u32 INTERPOLATE (u32 A, u32 B) +{ + if (A != B) { + return (((A & colorMask) >> 1) + ((B & colorMask) >> 1) + + (A & B & lowPixelMask)); + } else + return A; +} + +static inline u32 Q_INTERPOLATE (u32 A, u32 B, u32 C, u32 D) +{ + register u32 x = ((A & qcolorMask) >> 2) + + ((B & qcolorMask) >> 2) + + ((C & qcolorMask) >> 2) + ((D & qcolorMask) >> 2); + register u32 y = (A & qlowpixelMask) + + (B & qlowpixelMask) + (C & qlowpixelMask) + (D & qlowpixelMask); + + y = (y >> 2) & qlowpixelMask; + return x + y; +} + +static inline int GetResult1_32 (u32 A, u32 B, u32 C, u32 D, + u32 /* E */) +{ + int x = 0; + int y = 0; + int r = 0; + + if (A == C) + x += 1; + else if (B == C) + y += 1; + if (A == D) + x += 1; + else if (B == D) + y += 1; + if (x <= 1) + r += 1; + if (y <= 1) + r -= 1; + return r; +} + +static inline int GetResult2_32 (u32 A, u32 B, u32 C, u32 D, + u32 /* E */) +{ + int x = 0; + int y = 0; + int r = 0; + + if (A == C) + x += 1; + else if (B == C) + y += 1; + if (A == D) + x += 1; + else if (B == D) + y += 1; + if (x <= 1) + r -= 1; + if (y <= 1) + r += 1; + return r; +} + +#define BLUE_MASK565 0x001F001F +#define RED_MASK565 0xF800F800 +#define GREEN_MASK565 0x07E007E0 + +#define BLUE_MASK555 0x001F001F +#define RED_MASK555 0x7C007C00 +#define GREEN_MASK555 0x03E003E0 + +void Super2xSaI (u8 *srcPtr, u32 srcPitch, + u8 *deltaPtr, u8 *dstPtr, u32 dstPitch, + int width, int height) +{ + u16 *bP; + u8 *dP; + u32 inc_bP; + u32 Nextline = srcPitch >> 1; +#ifdef MMX + if (cpu_mmx) { + for (; height; height--) { + _2xSaISuper2xSaILine (srcPtr, deltaPtr, srcPitch, width, + dstPtr, dstPitch); + srcPtr += srcPitch; + dstPtr += dstPitch * 2; + deltaPtr += srcPitch; + } + } else +#endif + { + inc_bP = 1; + + for (; height; height--) { + bP = (u16 *) srcPtr; + dP = (u8 *) dstPtr; + + for (u32 finish = width; finish; finish -= inc_bP) { + u32 color4, color5, color6; + u32 color1, color2, color3; + u32 colorA0, colorA1, colorA2, colorA3, + colorB0, colorB1, colorB2, colorB3, colorS1, colorS2; + u32 product1a, product1b, product2a, product2b; + + //--------------------------------------- B1 B2 + // 4 5 6 S2 + // 1 2 3 S1 + // A1 A2 + + colorB0 = *(bP - Nextline - 1); + colorB1 = *(bP - Nextline); + colorB2 = *(bP - Nextline + 1); + colorB3 = *(bP - Nextline + 2); + + color4 = *(bP - 1); + color5 = *(bP); + color6 = *(bP + 1); + colorS2 = *(bP + 2); + + color1 = *(bP + Nextline - 1); + color2 = *(bP + Nextline); + color3 = *(bP + Nextline + 1); + colorS1 = *(bP + Nextline + 2); + + colorA0 = *(bP + Nextline + Nextline - 1); + colorA1 = *(bP + Nextline + Nextline); + colorA2 = *(bP + Nextline + Nextline + 1); + colorA3 = *(bP + Nextline + Nextline + 2); + + //-------------------------------------- + if (color2 == color6 && color5 != color3) { + product2b = product1b = color2; + } else if (color5 == color3 && color2 != color6) { + product2b = product1b = color5; + } else if (color5 == color3 && color2 == color6) { + register int r = 0; + + r += GetResult (color6, color5, color1, colorA1); + r += GetResult (color6, color5, color4, colorB1); + r += GetResult (color6, color5, colorA2, colorS1); + r += GetResult (color6, color5, colorB2, colorS2); + + if (r > 0) + product2b = product1b = color6; + else if (r < 0) + product2b = product1b = color5; + else { + product2b = product1b = INTERPOLATE (color5, color6); + } + } else { + if (color6 == color3 && color3 == colorA1 + && color2 != colorA2 && color3 != colorA0) + product2b = + Q_INTERPOLATE (color3, color3, color3, color2); + else if (color5 == color2 && color2 == colorA2 + && colorA1 != color3 && color2 != colorA3) + product2b = + Q_INTERPOLATE (color2, color2, color2, color3); + else + product2b = INTERPOLATE (color2, color3); + + if (color6 == color3 && color6 == colorB1 + && color5 != colorB2 && color6 != colorB0) + product1b = + Q_INTERPOLATE (color6, color6, color6, color5); + else if (color5 == color2 && color5 == colorB2 + && colorB1 != color6 && color5 != colorB3) + product1b = + Q_INTERPOLATE (color6, color5, color5, color5); + else + product1b = INTERPOLATE (color5, color6); + } + + if (color5 == color3 && color2 != color6 && color4 == color5 + && color5 != colorA2) + product2a = INTERPOLATE (color2, color5); + else + if (color5 == color1 && color6 == color5 + && color4 != color2 && color5 != colorA0) + product2a = INTERPOLATE (color2, color5); + else + product2a = color2; + + if (color2 == color6 && color5 != color3 && color1 == color2 + && color2 != colorB2) + product1a = INTERPOLATE (color2, color5); + else + if (color4 == color2 && color3 == color2 + && color1 != color5 && color2 != colorB0) + product1a = INTERPOLATE (color2, color5); + else + product1a = color5; + +#ifdef WORDS_BIGENDIAN + product1a = (product1a << 16) | product1b; + product2a = (product2a << 16) | product2b; +#else + product1a = product1a | (product1b << 16); + product2a = product2a | (product2b << 16); +#endif + + *((u32 *) dP) = product1a; + *((u32 *) (dP + dstPitch)) = product2a; + + bP += inc_bP; + dP += sizeof (u32); + } // end of for ( finish= width etc..) + + srcPtr += srcPitch; + dstPtr += dstPitch << 1; + deltaPtr += srcPitch; + } // endof: for (; height; height--) + } +} + +void Super2xSaI32 (u8 *srcPtr, u32 srcPitch, + u8 * /* deltaPtr */, u8 *dstPtr, u32 dstPitch, + int width, int height) +{ + u32 *bP; + u32 *dP; + u32 inc_bP; + u32 Nextline = srcPitch >> 2; + inc_bP = 1; + + for (; height; height--) { + bP = (u32 *) srcPtr; + dP = (u32 *) dstPtr; + + for (u32 finish = width; finish; finish -= inc_bP) { + u32 color4, color5, color6; + u32 color1, color2, color3; + u32 colorA0, colorA1, colorA2, colorA3, + colorB0, colorB1, colorB2, colorB3, colorS1, colorS2; + u32 product1a, product1b, product2a, product2b; + + //--------------------------------------- B1 B2 + // 4 5 6 S2 + // 1 2 3 S1 + // A1 A2 + + colorB0 = *(bP - Nextline - 1); + colorB1 = *(bP - Nextline); + colorB2 = *(bP - Nextline + 1); + colorB3 = *(bP - Nextline + 2); + + color4 = *(bP - 1); + color5 = *(bP); + color6 = *(bP + 1); + colorS2 = *(bP + 2); + + color1 = *(bP + Nextline - 1); + color2 = *(bP + Nextline); + color3 = *(bP + Nextline + 1); + colorS1 = *(bP + Nextline + 2); + + colorA0 = *(bP + Nextline + Nextline - 1); + colorA1 = *(bP + Nextline + Nextline); + colorA2 = *(bP + Nextline + Nextline + 1); + colorA3 = *(bP + Nextline + Nextline + 2); + + //-------------------------------------- + if (color2 == color6 && color5 != color3) { + product2b = product1b = color2; + } else if (color5 == color3 && color2 != color6) { + product2b = product1b = color5; + } else if (color5 == color3 && color2 == color6) { + register int r = 0; + + r += GetResult (color6, color5, color1, colorA1); + r += GetResult (color6, color5, color4, colorB1); + r += GetResult (color6, color5, colorA2, colorS1); + r += GetResult (color6, color5, colorB2, colorS2); + + if (r > 0) + product2b = product1b = color6; + else if (r < 0) + product2b = product1b = color5; + else { + product2b = product1b = INTERPOLATE (color5, color6); + } + } else { + if (color6 == color3 && color3 == colorA1 + && color2 != colorA2 && color3 != colorA0) + product2b = + Q_INTERPOLATE (color3, color3, color3, color2); + else if (color5 == color2 && color2 == colorA2 + && colorA1 != color3 && color2 != colorA3) + product2b = + Q_INTERPOLATE (color2, color2, color2, color3); + else + product2b = INTERPOLATE (color2, color3); + + if (color6 == color3 && color6 == colorB1 + && color5 != colorB2 && color6 != colorB0) + product1b = + Q_INTERPOLATE (color6, color6, color6, color5); + else if (color5 == color2 && color5 == colorB2 + && colorB1 != color6 && color5 != colorB3) + product1b = + Q_INTERPOLATE (color6, color5, color5, color5); + else + product1b = INTERPOLATE (color5, color6); + } + + if (color5 == color3 && color2 != color6 && color4 == color5 + && color5 != colorA2) + product2a = INTERPOLATE (color2, color5); + else + if (color5 == color1 && color6 == color5 + && color4 != color2 && color5 != colorA0) + product2a = INTERPOLATE (color2, color5); + else + product2a = color2; + + if (color2 == color6 && color5 != color3 && color1 == color2 + && color2 != colorB2) + product1a = INTERPOLATE (color2, color5); + else + if (color4 == color2 && color3 == color2 + && color1 != color5 && color2 != colorB0) + product1a = INTERPOLATE (color2, color5); + else + product1a = color5; + *(dP) = product1a; + *(dP+1) = product1b; + *(dP + (dstPitch >> 2)) = product2a; + *(dP + (dstPitch >> 2) + 1) = product2b; + + bP += inc_bP; + dP += 2; + } // end of for ( finish= width etc..) + + srcPtr += srcPitch; + dstPtr += dstPitch << 1; + // deltaPtr += srcPitch; + } // endof: for (; height; height--) +} + +void SuperEagle (u8 *srcPtr, u32 srcPitch, u8 *deltaPtr, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u8 *dP; + u16 *bP; + u16 *xP; + u32 inc_bP; + +#ifdef MMX + if (cpu_mmx) { + for (; height; height--) { + _2xSaISuperEagleLine (srcPtr, deltaPtr, srcPitch, width, + dstPtr, dstPitch); + srcPtr += srcPitch; + dstPtr += dstPitch * 2; + deltaPtr += srcPitch; + } + } else +#endif + { + inc_bP = 1; + + u32 Nextline = srcPitch >> 1; + + for (; height; height--) { + bP = (u16 *) srcPtr; + xP = (u16 *) deltaPtr; + dP = dstPtr; + for (u32 finish = width; finish; finish -= inc_bP) { + u32 color4, color5, color6; + u32 color1, color2, color3; + u32 colorA1, colorA2, colorB1, colorB2, colorS1, colorS2; + u32 product1a, product1b, product2a, product2b; + + colorB1 = *(bP - Nextline); + colorB2 = *(bP - Nextline + 1); + + color4 = *(bP - 1); + color5 = *(bP); + color6 = *(bP + 1); + colorS2 = *(bP + 2); + + color1 = *(bP + Nextline - 1); + color2 = *(bP + Nextline); + color3 = *(bP + Nextline + 1); + colorS1 = *(bP + Nextline + 2); + + colorA1 = *(bP + Nextline + Nextline); + colorA2 = *(bP + Nextline + Nextline + 1); + + // -------------------------------------- + if (color2 == color6 && color5 != color3) { + product1b = product2a = color2; + if ((color1 == color2) || (color6 == colorB2)) { + product1a = INTERPOLATE (color2, color5); + product1a = INTERPOLATE (color2, product1a); + // product1a = color2; + } else { + product1a = INTERPOLATE (color5, color6); + } + + if ((color6 == colorS2) || (color2 == colorA1)) { + product2b = INTERPOLATE (color2, color3); + product2b = INTERPOLATE (color2, product2b); + // product2b = color2; + } else { + product2b = INTERPOLATE (color2, color3); + } + } else if (color5 == color3 && color2 != color6) { + product2b = product1a = color5; + + if ((colorB1 == color5) || (color3 == colorS1)) { + product1b = INTERPOLATE (color5, color6); + product1b = INTERPOLATE (color5, product1b); + // product1b = color5; + } else { + product1b = INTERPOLATE (color5, color6); + } + + if ((color3 == colorA2) || (color4 == color5)) { + product2a = INTERPOLATE (color5, color2); + product2a = INTERPOLATE (color5, product2a); + // product2a = color5; + } else { + product2a = INTERPOLATE (color2, color3); + } + + } else if (color5 == color3 && color2 == color6) { + register int r = 0; + + r += GetResult (color6, color5, color1, colorA1); + r += GetResult (color6, color5, color4, colorB1); + r += GetResult (color6, color5, colorA2, colorS1); + r += GetResult (color6, color5, colorB2, colorS2); + + if (r > 0) { + product1b = product2a = color2; + product1a = product2b = INTERPOLATE (color5, color6); + } else if (r < 0) { + product2b = product1a = color5; + product1b = product2a = INTERPOLATE (color5, color6); + } else { + product2b = product1a = color5; + product1b = product2a = color2; + } + } else { + product2b = product1a = INTERPOLATE (color2, color6); + product2b = + Q_INTERPOLATE (color3, color3, color3, product2b); + product1a = + Q_INTERPOLATE (color5, color5, color5, product1a); + + product2a = product1b = INTERPOLATE (color5, color3); + product2a = + Q_INTERPOLATE (color2, color2, color2, product2a); + product1b = + Q_INTERPOLATE (color6, color6, color6, product1b); + + // product1a = color5; + // product1b = color6; + // product2a = color2; + // product2b = color3; + } +#ifdef WORDS_BIGENDIAN + product1a = (product1a << 16) | product1b; + product2a = (product2a << 16) | product2b; +#else + product1a = product1a | (product1b << 16); + product2a = product2a | (product2b << 16); +#endif + + *((u32 *) dP) = product1a; + *((u32 *) (dP + dstPitch)) = product2a; + *xP = color5; + + bP += inc_bP; + xP += inc_bP; + dP += sizeof (u32); + } // end of for ( finish= width etc..) + + srcPtr += srcPitch; + dstPtr += dstPitch << 1; + deltaPtr += srcPitch; + } // endof: for (height; height; height--) + } +} + +void SuperEagle32 (u8 *srcPtr, u32 srcPitch, u8 *deltaPtr, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u32 *dP; + u32 *bP; + u32 *xP; + u32 inc_bP; + + inc_bP = 1; + + u32 Nextline = srcPitch >> 2; + + for (; height; height--) { + bP = (u32 *) srcPtr; + xP = (u32 *) deltaPtr; + dP = (u32 *)dstPtr; + for (u32 finish = width; finish; finish -= inc_bP) { + u32 color4, color5, color6; + u32 color1, color2, color3; + u32 colorA1, colorA2, colorB1, colorB2, colorS1, colorS2; + u32 product1a, product1b, product2a, product2b; + + colorB1 = *(bP - Nextline); + colorB2 = *(bP - Nextline + 1); + + color4 = *(bP - 1); + color5 = *(bP); + color6 = *(bP + 1); + colorS2 = *(bP + 2); + + color1 = *(bP + Nextline - 1); + color2 = *(bP + Nextline); + color3 = *(bP + Nextline + 1); + colorS1 = *(bP + Nextline + 2); + + colorA1 = *(bP + Nextline + Nextline); + colorA2 = *(bP + Nextline + Nextline + 1); + + // -------------------------------------- + if (color2 == color6 && color5 != color3) { + product1b = product2a = color2; + if ((color1 == color2) || (color6 == colorB2)) { + product1a = INTERPOLATE (color2, color5); + product1a = INTERPOLATE (color2, product1a); + // product1a = color2; + } else { + product1a = INTERPOLATE (color5, color6); + } + + if ((color6 == colorS2) || (color2 == colorA1)) { + product2b = INTERPOLATE (color2, color3); + product2b = INTERPOLATE (color2, product2b); + // product2b = color2; + } else { + product2b = INTERPOLATE (color2, color3); + } + } else if (color5 == color3 && color2 != color6) { + product2b = product1a = color5; + + if ((colorB1 == color5) || (color3 == colorS1)) { + product1b = INTERPOLATE (color5, color6); + product1b = INTERPOLATE (color5, product1b); + // product1b = color5; + } else { + product1b = INTERPOLATE (color5, color6); + } + + if ((color3 == colorA2) || (color4 == color5)) { + product2a = INTERPOLATE (color5, color2); + product2a = INTERPOLATE (color5, product2a); + // product2a = color5; + } else { + product2a = INTERPOLATE (color2, color3); + } + + } else if (color5 == color3 && color2 == color6) { + register int r = 0; + + r += GetResult (color6, color5, color1, colorA1); + r += GetResult (color6, color5, color4, colorB1); + r += GetResult (color6, color5, colorA2, colorS1); + r += GetResult (color6, color5, colorB2, colorS2); + + if (r > 0) { + product1b = product2a = color2; + product1a = product2b = INTERPOLATE (color5, color6); + } else if (r < 0) { + product2b = product1a = color5; + product1b = product2a = INTERPOLATE (color5, color6); + } else { + product2b = product1a = color5; + product1b = product2a = color2; + } + } else { + product2b = product1a = INTERPOLATE (color2, color6); + product2b = + Q_INTERPOLATE (color3, color3, color3, product2b); + product1a = + Q_INTERPOLATE (color5, color5, color5, product1a); + + product2a = product1b = INTERPOLATE (color5, color3); + product2a = + Q_INTERPOLATE (color2, color2, color2, product2a); + product1b = + Q_INTERPOLATE (color6, color6, color6, product1b); + + // product1a = color5; + // product1b = color6; + // product2a = color2; + // product2b = color3; + } + *(dP) = product1a; + *(dP+1) = product1b; + *(dP + (dstPitch >> 2)) = product2a; + *(dP + (dstPitch >> 2) +1) = product2b; + *xP = color5; + + bP += inc_bP; + xP += inc_bP; + dP += 2; + } // end of for ( finish= width etc..) + + srcPtr += srcPitch; + dstPtr += dstPitch << 1; + deltaPtr += srcPitch; + } // endof: for (height; height; height--) +} + +void _2xSaI (u8 *srcPtr, u32 srcPitch, u8 *deltaPtr, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u8 *dP; + u16 *bP; + u32 inc_bP; + +#ifdef MMX + if (cpu_mmx) { + for (; height; height -= 1) { + _2xSaILine (srcPtr, deltaPtr, srcPitch, width, dstPtr, dstPitch); + srcPtr += srcPitch; + dstPtr += dstPitch * 2; + deltaPtr += srcPitch; + } + } else +#endif + { + inc_bP = 1; + + u32 Nextline = srcPitch >> 1; + + for (; height; height--) { + bP = (u16 *) srcPtr; + dP = dstPtr; + + for (u32 finish = width; finish; finish -= inc_bP) { + + register u32 colorA, colorB; + u32 colorC, colorD, + colorE, colorF, colorG, colorH, + colorI, colorJ, colorK, colorL, + + colorM, colorN, colorO, colorP; + u32 product, product1, product2; + + //--------------------------------------- + // Map of the pixels: I|E F|J + // G|A B|K + // H|C D|L + // M|N O|P + colorI = *(bP - Nextline - 1); + colorE = *(bP - Nextline); + colorF = *(bP - Nextline + 1); + colorJ = *(bP - Nextline + 2); + + colorG = *(bP - 1); + colorA = *(bP); + colorB = *(bP + 1); + colorK = *(bP + 2); + + colorH = *(bP + Nextline - 1); + colorC = *(bP + Nextline); + colorD = *(bP + Nextline + 1); + colorL = *(bP + Nextline + 2); + + colorM = *(bP + Nextline + Nextline - 1); + colorN = *(bP + Nextline + Nextline); + colorO = *(bP + Nextline + Nextline + 1); + colorP = *(bP + Nextline + Nextline + 2); + + if ((colorA == colorD) && (colorB != colorC)) { + if (((colorA == colorE) && (colorB == colorL)) || + ((colorA == colorC) && (colorA == colorF) + && (colorB != colorE) && (colorB == colorJ))) { + product = colorA; + } else { + product = INTERPOLATE (colorA, colorB); + } + + if (((colorA == colorG) && (colorC == colorO)) || + ((colorA == colorB) && (colorA == colorH) + && (colorG != colorC) && (colorC == colorM))) { + product1 = colorA; + } else { + product1 = INTERPOLATE (colorA, colorC); + } + product2 = colorA; + } else if ((colorB == colorC) && (colorA != colorD)) { + if (((colorB == colorF) && (colorA == colorH)) || + ((colorB == colorE) && (colorB == colorD) + && (colorA != colorF) && (colorA == colorI))) { + product = colorB; + } else { + product = INTERPOLATE (colorA, colorB); + } + + if (((colorC == colorH) && (colorA == colorF)) || + ((colorC == colorG) && (colorC == colorD) + && (colorA != colorH) && (colorA == colorI))) { + product1 = colorC; + } else { + product1 = INTERPOLATE (colorA, colorC); + } + product2 = colorB; + } else if ((colorA == colorD) && (colorB == colorC)) { + if (colorA == colorB) { + product = colorA; + product1 = colorA; + product2 = colorA; + } else { + register int r = 0; + + product1 = INTERPOLATE (colorA, colorC); + product = INTERPOLATE (colorA, colorB); + + r += + GetResult1 (colorA, colorB, colorG, colorE, + colorI); + r += + GetResult2 (colorB, colorA, colorK, colorF, + colorJ); + r += + GetResult2 (colorB, colorA, colorH, colorN, + colorM); + r += + GetResult1 (colorA, colorB, colorL, colorO, + colorP); + + if (r > 0) + product2 = colorA; + else if (r < 0) + product2 = colorB; + else { + product2 = + Q_INTERPOLATE (colorA, colorB, colorC, + colorD); + } + } + } else { + product2 = Q_INTERPOLATE (colorA, colorB, colorC, colorD); + + if ((colorA == colorC) && (colorA == colorF) + && (colorB != colorE) && (colorB == colorJ)) { + product = colorA; + } else if ((colorB == colorE) && (colorB == colorD) + && (colorA != colorF) && (colorA == colorI)) { + product = colorB; + } else { + product = INTERPOLATE (colorA, colorB); + } + + if ((colorA == colorB) && (colorA == colorH) + && (colorG != colorC) && (colorC == colorM)) { + product1 = colorA; + } else if ((colorC == colorG) && (colorC == colorD) + && (colorA != colorH) && (colorA == colorI)) { + product1 = colorC; + } else { + product1 = INTERPOLATE (colorA, colorC); + } + } + +#ifdef WORDS_BIGENDIAN + product = (colorA << 16) | product ; + product1 = (product1 << 16) | product2 ; +#else + product = colorA | (product << 16); + product1 = product1 | (product2 << 16); +#endif + *((s32 *) dP) = product; + *((u32 *) (dP + dstPitch)) = product1; + + bP += inc_bP; + dP += sizeof (u32); + } // end of for ( finish= width etc..) + + srcPtr += srcPitch; + dstPtr += dstPitch << 1; + deltaPtr += srcPitch; + } // endof: for (height; height; height--) + } +} + +void _2xSaI32 (u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u32 *dP; + u32 *bP; + u32 inc_bP = 1; + + u32 Nextline = srcPitch >> 2; + + for (; height; height--) { + bP = (u32 *) srcPtr; + dP = (u32 *) dstPtr; + + for (u32 finish = width; finish; finish -= inc_bP) { + register u32 colorA, colorB; + u32 colorC, colorD, + colorE, colorF, colorG, colorH, + colorI, colorJ, colorK, colorL, + + colorM, colorN, colorO, colorP; + u32 product, product1, product2; + + //--------------------------------------- + // Map of the pixels: I|E F|J + // G|A B|K + // H|C D|L + // M|N O|P + colorI = *(bP - Nextline - 1); + colorE = *(bP - Nextline); + colorF = *(bP - Nextline + 1); + colorJ = *(bP - Nextline + 2); + + colorG = *(bP - 1); + colorA = *(bP); + colorB = *(bP + 1); + colorK = *(bP + 2); + + colorH = *(bP + Nextline - 1); + colorC = *(bP + Nextline); + colorD = *(bP + Nextline + 1); + colorL = *(bP + Nextline + 2); + + colorM = *(bP + Nextline + Nextline - 1); + colorN = *(bP + Nextline + Nextline); + colorO = *(bP + Nextline + Nextline + 1); + colorP = *(bP + Nextline + Nextline + 2); + + if ((colorA == colorD) && (colorB != colorC)) { + if (((colorA == colorE) && (colorB == colorL)) || + ((colorA == colorC) && (colorA == colorF) + && (colorB != colorE) && (colorB == colorJ))) { + product = colorA; + } else { + product = INTERPOLATE (colorA, colorB); + } + + if (((colorA == colorG) && (colorC == colorO)) || + ((colorA == colorB) && (colorA == colorH) + && (colorG != colorC) && (colorC == colorM))) { + product1 = colorA; + } else { + product1 = INTERPOLATE (colorA, colorC); + } + product2 = colorA; + } else if ((colorB == colorC) && (colorA != colorD)) { + if (((colorB == colorF) && (colorA == colorH)) || + ((colorB == colorE) && (colorB == colorD) + && (colorA != colorF) && (colorA == colorI))) { + product = colorB; + } else { + product = INTERPOLATE (colorA, colorB); + } + + if (((colorC == colorH) && (colorA == colorF)) || + ((colorC == colorG) && (colorC == colorD) + && (colorA != colorH) && (colorA == colorI))) { + product1 = colorC; + } else { + product1 = INTERPOLATE (colorA, colorC); + } + product2 = colorB; + } else if ((colorA == colorD) && (colorB == colorC)) { + if (colorA == colorB) { + product = colorA; + product1 = colorA; + product2 = colorA; + } else { + register int r = 0; + + product1 = INTERPOLATE (colorA, colorC); + product = INTERPOLATE (colorA, colorB); + + r += + GetResult1 (colorA, colorB, colorG, colorE, + colorI); + r += + GetResult2 (colorB, colorA, colorK, colorF, + colorJ); + r += + GetResult2 (colorB, colorA, colorH, colorN, + colorM); + r += + GetResult1 (colorA, colorB, colorL, colorO, + colorP); + + if (r > 0) + product2 = colorA; + else if (r < 0) + product2 = colorB; + else { + product2 = + Q_INTERPOLATE (colorA, colorB, colorC, + colorD); + } + } + } else { + product2 = Q_INTERPOLATE (colorA, colorB, colorC, colorD); + + if ((colorA == colorC) && (colorA == colorF) + && (colorB != colorE) && (colorB == colorJ)) { + product = colorA; + } else if ((colorB == colorE) && (colorB == colorD) + && (colorA != colorF) && (colorA == colorI)) { + product = colorB; + } else { + product = INTERPOLATE (colorA, colorB); + } + + if ((colorA == colorB) && (colorA == colorH) + && (colorG != colorC) && (colorC == colorM)) { + product1 = colorA; + } else if ((colorC == colorG) && (colorC == colorD) + && (colorA != colorH) && (colorA == colorI)) { + product1 = colorC; + } else { + product1 = INTERPOLATE (colorA, colorC); + } + } + *(dP) = colorA; + *(dP + 1) = product; + *(dP + (dstPitch >> 2)) = product1; + *(dP + (dstPitch >> 2) + 1) = product2; + + bP += inc_bP; + dP += 2; + } // end of for ( finish= width etc..) + + srcPtr += srcPitch; + dstPtr += dstPitch << 1; + // deltaPtr += srcPitch; + } // endof: for (height; height; height--) +} + +static u32 Bilinear (u32 A, u32 B, u32 x) +{ + unsigned long areaA, areaB; + unsigned long result; + + if (A == B) + return A; + + areaB = (x >> 11) & 0x1f; // reduce 16 bit fraction to 5 bits + areaA = 0x20 - areaB; + + A = (A & redblueMask) | ((A & greenMask) << 16); + B = (B & redblueMask) | ((B & greenMask) << 16); + + result = ((areaA * A) + (areaB * B)) >> 5; + + return (result & redblueMask) | ((result >> 16) & greenMask); +} + +static u32 Bilinear4 (u32 A, u32 B, u32 C, u32 D, u32 x, + u32 y) +{ + unsigned long areaA, areaB, areaC, areaD; + unsigned long result, xy; + + x = (x >> 11) & 0x1f; + y = (y >> 11) & 0x1f; + xy = (x * y) >> 5; + + A = (A & redblueMask) | ((A & greenMask) << 16); + B = (B & redblueMask) | ((B & greenMask) << 16); + C = (C & redblueMask) | ((C & greenMask) << 16); + D = (D & redblueMask) | ((D & greenMask) << 16); + + areaA = 0x20 + xy - x - y; + areaB = x - xy; + areaC = y - xy; + areaD = xy; + + result = ((areaA * A) + (areaB * B) + (areaC * C) + (areaD * D)) >> 5; + + return (result & redblueMask) | ((result >> 16) & greenMask); +} + +void Scale_2xSaI (u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, + u8 *dstPtr, u32 dstPitch, + u32 dstWidth, u32 dstHeight, int width, int height) +{ + u8 *dP; + u16 *bP; + + u32 w; + u32 h; + u32 dw; + u32 dh; + u32 hfinish; + u32 wfinish; + + u32 Nextline = srcPitch >> 1; + + wfinish = (width - 1) << 16; // convert to fixed point + dw = wfinish / (dstWidth - 1); + hfinish = (height - 1) << 16; // convert to fixed point + dh = hfinish / (dstHeight - 1); + + for (h = 0; h < hfinish; h += dh) { + u32 y1, y2; + + y1 = h & 0xffff; // fraction part of fixed point + bP = (u16 *) (srcPtr + ((h >> 16) * srcPitch)); + dP = dstPtr; + y2 = 0x10000 - y1; + + w = 0; + + for (; w < wfinish;) { + u32 A, B, C, D; + u32 E, F, G, H; + u32 I, J, K, L; + u32 x1, x2, a1, f1, f2; + u32 position, product1; + + position = w >> 16; + A = bP[position]; // current pixel + B = bP[position + 1]; // next pixel + C = bP[position + Nextline]; + D = bP[position + Nextline + 1]; + E = bP[position - Nextline]; + F = bP[position - Nextline + 1]; + G = bP[position - 1]; + H = bP[position + Nextline - 1]; + I = bP[position + 2]; + J = bP[position + Nextline + 2]; + K = bP[position + Nextline + Nextline]; + L = bP[position + Nextline + Nextline + 1]; + + x1 = w & 0xffff; // fraction part of fixed point + x2 = 0x10000 - x1; + + /*0*/ + if (A == B && C == D && A == C) + product1 = A; + else /*1*/ if (A == D && B != C) { + f1 = (x1 >> 1) + (0x10000 >> 2); + f2 = (y1 >> 1) + (0x10000 >> 2); + if (y1 <= f1 && A == J && A != E) // close to B + { + a1 = f1 - y1; + product1 = Bilinear (A, B, a1); + } else if (y1 >= f1 && A == G && A != L) // close to C + { + a1 = y1 - f1; + product1 = Bilinear (A, C, a1); + } + else if (x1 >= f2 && A == E && A != J) // close to B + { + a1 = x1 - f2; + product1 = Bilinear (A, B, a1); + } + else if (x1 <= f2 && A == L && A != G) // close to C + { + a1 = f2 - x1; + product1 = Bilinear (A, C, a1); + } + else if (y1 >= x1) // close to C + { + a1 = y1 - x1; + product1 = Bilinear (A, C, a1); + } + else if (y1 <= x1) // close to B + { + a1 = x1 - y1; + product1 = Bilinear (A, B, a1); + } + } + else + /*2*/ + if (B == C && A != D) + { + f1 = (x1 >> 1) + (0x10000 >> 2); + f2 = (y1 >> 1) + (0x10000 >> 2); + if (y2 >= f1 && B == H && B != F) // close to A + { + a1 = y2 - f1; + product1 = Bilinear (B, A, a1); + } + else if (y2 <= f1 && B == I && B != K) // close to D + { + a1 = f1 - y2; + product1 = Bilinear (B, D, a1); + } + else if (x2 >= f2 && B == F && B != H) // close to A + { + a1 = x2 - f2; + product1 = Bilinear (B, A, a1); + } + else if (x2 <= f2 && B == K && B != I) // close to D + { + a1 = f2 - x2; + product1 = Bilinear (B, D, a1); + } + else if (y2 >= x1) // close to A + { + a1 = y2 - x1; + product1 = Bilinear (B, A, a1); + } + else if (y2 <= x1) // close to D + { + a1 = x1 - y2; + product1 = Bilinear (B, D, a1); + } + } + /*3*/ + else + { + product1 = Bilinear4 (A, B, C, D, x1, y1); + } + + //end First Pixel + *(u32 *) dP = product1; + dP += 2; + w += dw; + } + dstPtr += dstPitch; + } +} + diff --git a/src/filters/2xSaImmx.asm b/src/filters/2xSaImmx.asm new file mode 100644 index 0000000..931feba --- /dev/null +++ b/src/filters/2xSaImmx.asm @@ -0,0 +1,2116 @@ +; 2xSaI engine +; Copyright (C) 1999 - 2001 by Derek Liauw Kie Fa + +; This program is free software; you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation; either version 2, or(at your option) +; any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program; if not, write to the Free Software Foundation, +; Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +; The author released that file under GPLv2+ on the Fedora legal list : +; http://lists.fedoraproject.org/pipermail/legal/2009-October/000947.html + +;---------------------- +; 2xSaI version 0.59 WIP, soon to become version 0.60 +;---------------------- + +;%define FAR_POINTER + + + + BITS 32 +%ifdef __DJGPP__ + GLOBAL __2xSaILine + GLOBAL __2xSaISuperEagleLine + GLOBAL __2xSaISuper2xSaILine + GLOBAL _Init_2xSaIMMX +%else + GLOBAL _2xSaILine + GLOBAL _2xSaISuperEagleLine + GLOBAL _2xSaISuper2xSaILine + GLOBAL Init_2xSaIMMX +%endif + SECTION .text ALIGN = 32 + +%ifdef FAR_POINTER +;EXTERN_C void _2xSaILine (uint8 *srcPtr, uint32 srcPitch, uint32 width, +; uint8 *dstPtr, uint32 dstPitch, uint16 dstSegment); +%else +;EXTERN_C void _2xSaILine (uint8 *srcPtr, uint32 srcPitch, uint32 width, +; uint8 *dstPtr, uint32 dstPitch); +%endif + +srcPtr equ 8 +deltaPtr equ 12 +srcPitch equ 16 +width equ 20 +dstOffset equ 24 +dstPitch equ 28 +dstSegment equ 32 + + + + +colorB0 equ -2 +colorB1 equ 0 +colorB2 equ 2 +colorB3 equ 4 + +color7 equ -2 +color8 equ 0 +color9 equ 2 + +color4 equ -2 +color5 equ 0 +color6 equ 2 +colorS2 equ 4 + +color1 equ -2 +color2 equ 0 +color3 equ 2 +colorS1 equ 4 + +colorA0 equ -2 +colorA1 equ 0 +colorA2 equ 2 +colorA3 equ 4 + + + + +%ifdef __DJGPP__ +__2xSaISuper2xSaILine: +%else +_2xSaISuper2xSaILine: +%endif +; Store some stuff + push ebp + mov ebp, esp + pushad + +; Prepare the destination +%ifdef FAR_POINTER + ; Set the selector + mov eax, [ebp+dstSegment] + mov fs, ax +%endif + mov edx, [ebp+dstOffset] ; edx points to the screen +; Prepare the source + ; eax points to colorA + mov eax, [ebp+srcPtr] ;eax points to colorA + mov ebx, [ebp+srcPitch] ;ebx contains the source pitch + mov ecx, [ebp+width] ;ecx contains the number of pixels to process + ; eax now points to colorB1 + sub eax, ebx ;eax points to B1 which is the base + +; Main Loop +.Loop: push ecx + + ;-----Check Delta------------------ + mov ecx, [ebp+deltaPtr] + + + ;load source img + movq mm0, [eax+colorB0] + movq mm1, [eax+colorB3] + movq mm2, [eax+ebx+color4] + movq mm3, [eax+ebx+colorS2] + movq mm4, [eax+ebx+ebx+color1] + movq mm5, [eax+ebx+ebx+colorS1] + push eax + add eax, ebx + movq mm6, [eax+ebx+ebx+colorA0] + movq mm7, [eax+ebx+ebx+colorA3] + pop eax + + ;compare to delta + pcmpeqw mm0, [ecx+2+colorB0] + pcmpeqw mm1, [ecx+2+colorB3] + pcmpeqw mm2, [ecx+ebx+2+color4] + pcmpeqw mm3, [ecx+ebx+2+colorS2] + pcmpeqw mm4, [ecx+ebx+ebx+2+color1] + pcmpeqw mm5, [ecx+ebx+ebx+2+colorS1] + add ecx, ebx + pcmpeqw mm6, [ecx+ebx+ebx+2+colorA0] + pcmpeqw mm7, [ecx+ebx+ebx+2+colorA3] + sub ecx, ebx + + + ;compose results + pand mm0, mm1 + pand mm2, mm3 + pand mm4, mm5 + pand mm6, mm7 + pand mm0, mm2 + pand mm4, mm6 + pxor mm7, mm7 + pand mm0, mm4 + movq mm6, [eax+colorB0] + pcmpeqw mm7, mm0 ;did any compare give us a zero ? + + movq [ecx+2+colorB0], mm6 + + packsswb mm7, mm7 + movd ecx, mm7 + test ecx, ecx + jz near .SKIP_PROCESS ;no, so we can skip + + ;End Delta + + ;--------------------------------- + movq mm0, [eax+ebx+color5] + movq mm1, [eax+ebx+color6] + movq mm2, mm0 + movq mm3, mm1 + movq mm4, mm0 + movq mm5, mm1 + + pand mm0, [colorMask] + pand mm1, [colorMask] + + psrlw mm0, 1 + psrlw mm1, 1 + + pand mm3, [lowPixelMask] + paddw mm0, mm1 + + pand mm3, mm2 + paddw mm0, mm3 ;mm0 contains the interpolated values + movq [I56Pixel], mm0 + movq mm7, mm0 + + ;------------------- + movq mm0, mm7 + movq mm1, mm4 ;5,5,5,6 + movq mm2, mm0 + movq mm3, mm1 + + pand mm0, [colorMask] + pand mm1, [colorMask] + + psrlw mm0, 1 + psrlw mm1, 1 + + pand mm3, [lowPixelMask] + paddw mm0, mm1 + + pand mm3, mm2 + paddw mm0, mm3 ;mm0 contains the interpolated values + movq [I5556Pixel], mm0 + ;-------------------- + + movq mm0, mm7 + movq mm1, mm5 ;6,6,6,5 + movq mm2, mm0 + movq mm3, mm1 + + pand mm0, [colorMask] + pand mm1, [colorMask] + + psrlw mm0, 1 + psrlw mm1, 1 + + pand mm3, [lowPixelMask] + paddw mm0, mm1 + + pand mm3, mm2 + paddw mm0, mm3 + movq [I5666Pixel], mm0 + + ;------------------------- + ;------------------------- + movq mm0, [eax+ebx+ebx+color2] + movq mm1, [eax+ebx+ebx+color3] + movq mm2, mm0 + movq mm3, mm1 + movq mm4, mm0 + movq mm5, mm1 + + pand mm0, [colorMask] + pand mm1, [colorMask] + + psrlw mm0, 1 + psrlw mm1, 1 + + pand mm3, [lowPixelMask] + paddw mm0, mm1 + + pand mm3, mm2 + paddw mm0, mm3 + movq [I23Pixel], mm0 + movq mm7, mm0 + + ;--------------------- + movq mm0, mm7 + movq mm1, mm4 ;2,2,2,3 + movq mm2, mm0 + movq mm3, mm1 + + pand mm0, [colorMask] + pand mm1, [colorMask] + + psrlw mm0, 1 + psrlw mm1, 1 + + pand mm3, [lowPixelMask] + paddw mm0, mm1 + + pand mm3, mm2 + paddw mm0, mm3 + movq [I2223Pixel], mm0 + + ;---------------------- + movq mm0, mm7 + movq mm1, mm5 ;3,3,3,2 + movq mm2, mm0 + movq mm3, mm1 + + pand mm0, [colorMask] + pand mm1, [colorMask] + + psrlw mm0, 1 + psrlw mm1, 1 + + pand mm3, [lowPixelMask] + paddw mm0, mm1 + + pand mm3, mm2 + paddw mm0, mm3 + movq [I2333Pixel], mm0 + + + ;-------------------- +;//////////////////////////////// +; Decide which "branch" to take +;-------------------------------- + movq mm0, [eax+ebx+color5] + movq mm1, [eax+ebx+color6] + movq mm6, mm0 + movq mm7, mm1 + pcmpeqw mm0, [eax+ebx+ebx+color3] + pcmpeqw mm1, [eax+ebx+ebx+color2] + pcmpeqw mm6, mm7 + + movq mm2, mm0 + movq mm3, mm0 + + pand mm0, mm1 ;colorA == colorD && colorB == colorC + pxor mm7, mm7 + + pcmpeqw mm2, mm7 + pand mm6, mm0 + pand mm2, mm1 ;colorA != colorD && colorB == colorC + + pcmpeqw mm1, mm7 + + pand mm1, mm3 ;colorA == colorD && colorB != colorC + pxor mm0, mm6 + por mm1, mm6 + movq mm7, mm0 + movq [Mask26], mm2 + packsswb mm7, mm7 + movq [Mask35], mm1 + + movd ecx, mm7 + test ecx, ecx + jz near .SKIP_GUESS + +;--------------------------------------------- + movq mm6, mm0 + movq mm4, [eax+ebx+colorA] + movq mm5, [eax+ebx+colorB] + pxor mm7, mm7 + pand mm6, [ONE] + + movq mm0, [eax+colorE] + movq mm1, [eax+ebx+colorG] + movq mm2, mm0 + movq mm3, mm1 + pcmpeqw mm0, mm4 + pcmpeqw mm1, mm4 + pcmpeqw mm2, mm5 + pcmpeqw mm3, mm5 + pand mm0, mm6 + pand mm1, mm6 + pand mm2, mm6 + pand mm3, mm6 + paddw mm0, mm1 + paddw mm2, mm3 + + pxor mm3, mm3 + pcmpgtw mm0, mm6 + pcmpgtw mm2, mm6 + pcmpeqw mm0, mm3 + pcmpeqw mm2, mm3 + pand mm0, mm6 + pand mm2, mm6 + paddw mm7, mm0 + psubw mm7, mm2 + + movq mm0, [eax+colorF] + movq mm1, [eax+ebx+colorK] + movq mm2, mm0 + movq mm3, mm1 + pcmpeqw mm0, mm4 + pcmpeqw mm1, mm4 + pcmpeqw mm2, mm5 + pcmpeqw mm3, mm5 + pand mm0, mm6 + pand mm1, mm6 + pand mm2, mm6 + pand mm3, mm6 + paddw mm0, mm1 + paddw mm2, mm3 + + pxor mm3, mm3 + pcmpgtw mm0, mm6 + pcmpgtw mm2, mm6 + pcmpeqw mm0, mm3 + pcmpeqw mm2, mm3 + pand mm0, mm6 + pand mm2, mm6 + paddw mm7, mm0 + psubw mm7, mm2 + + push eax + add eax, ebx + movq mm0, [eax+ebx+colorH] + movq mm1, [eax+ebx+ebx+colorN] + movq mm2, mm0 + movq mm3, mm1 + pcmpeqw mm0, mm4 + pcmpeqw mm1, mm4 + pcmpeqw mm2, mm5 + pcmpeqw mm3, mm5 + pand mm0, mm6 + pand mm1, mm6 + pand mm2, mm6 + pand mm3, mm6 + paddw mm0, mm1 + paddw mm2, mm3 + + pxor mm3, mm3 + pcmpgtw mm0, mm6 + pcmpgtw mm2, mm6 + pcmpeqw mm0, mm3 + pcmpeqw mm2, mm3 + pand mm0, mm6 + pand mm2, mm6 + paddw mm7, mm0 + psubw mm7, mm2 + + movq mm0, [eax+ebx+colorL] + movq mm1, [eax+ebx+ebx+colorO] + movq mm2, mm0 + movq mm3, mm1 + pcmpeqw mm0, mm4 + pcmpeqw mm1, mm4 + pcmpeqw mm2, mm5 + pcmpeqw mm3, mm5 + pand mm0, mm6 + pand mm1, mm6 + pand mm2, mm6 + pand mm3, mm6 + paddw mm0, mm1 + paddw mm2, mm3 + + pxor mm3, mm3 + pcmpgtw mm0, mm6 + pcmpgtw mm2, mm6 + pcmpeqw mm0, mm3 + pcmpeqw mm2, mm3 + pand mm0, mm6 + pand mm2, mm6 + paddw mm7, mm0 + psubw mm7, mm2 + + pop eax + movq mm1, mm7 + pxor mm0, mm0 + pcmpgtw mm7, mm0 + pcmpgtw mm0, mm1 + + por mm7, [Mask35] + por mm0, [Mask26] + movq [Mask35], mm7 + movq [Mask26], mm0 + +.SKIP_GUESS: + + ;Start the ASSEMBLY !!! eh... compose all the results together to form the final image... + + + movq mm0, [eax+ebx+color5] + movq mm1, [eax+ebx+ebx+color2] + movq mm2, mm0 + movq mm3, mm1 + movq mm4, mm0 + movq mm5, mm1 + + pand mm0, [colorMask] + pand mm1, [colorMask] + + psrlw mm0, 1 + psrlw mm1, 1 + + pand mm3, [lowPixelMask] + paddw mm0, mm1 + + pand mm3, mm2 + paddw mm0, mm3 ;mm0 contains the interpolated values + ;--------------------------- + + + +%ifdef dfhsdfhsdahdsfhdsfh + + if (color5 == color3 && color2 != color6 && color4 == color5 && color5 != colorA2) + product2a = INTERPOLATE (color2, color5); + else + if (color5 == color1 && color6 == color5 && color4 != color2 && color5 != colorA0) + product2a = INTERPOLATE(color2, color5); + else + product2a = color2; + + if (color2 == color6 && color5 != color3 && color1 == color2 && color2 != colorB2) + product1a = INTERPOLATE (color2, color5); + else + if (color4 == color2 && color3 == color2 && color1 != color5 && color2 != colorB0) + product1a = INTERPOLATE(color2, color5); + else + product1a = color5; + +%endif + + + movq mm7, [Mask26] + movq mm6, [eax+colorB2] + movq mm5, [eax+ebx+ebx+color2] + movq mm4, [eax+ebx+ebx+color1] + pcmpeqw mm4, mm5 + pcmpeqw mm6, mm5 + pxor mm5, mm5 + pand mm7, mm4 + pcmpeqw mm6, mm5 + pand mm7, mm6 + + + + movq mm6, [eax+ebx+ebx+color3] + movq mm5, [eax+ebx+ebx+color2] + movq mm4, [eax+ebx+ebx+color1] + movq mm2, [eax+ebx+color5] + movq mm1, [eax+ebx+color4] + movq mm3, [eax+colorB0] + + pcmpeqw mm2, mm4 + pcmpeqw mm6, mm5 + pcmpeqw mm1, mm5 + pcmpeqw mm3, mm5 + pxor mm5, mm5 + pcmpeqw mm2, mm5 + pcmpeqw mm3, mm5 + pand mm6, mm1 + pand mm2, mm3 + pand mm6, mm2 + por mm7, mm6 + + + movq mm6, mm7 + pcmpeqw mm6, mm5 + pand mm7, mm0 + + movq mm1, [eax+ebx+color5] + pand mm6, mm1 + por mm7, mm6 + movq [final1a], mm7 ;finished 1a + + + + ;-------------------------------- + + movq mm7, [Mask35] + push eax + add eax, ebx + movq mm6, [eax+ebx+ebx+colorA2] + pop eax + movq mm5, [eax+ebx+color5] + movq mm4, [eax+ebx+color4] + pcmpeqw mm4, mm5 + pcmpeqw mm6, mm5 + pxor mm5, mm5 + pand mm7, mm4 + pcmpeqw mm6, mm5 + pand mm7, mm6 + + + + movq mm6, [eax+ebx+color6] + movq mm5, [eax+ebx+color5] + movq mm4, [eax+ebx+color4] + movq mm2, [eax+ebx+ebx+color2] + movq mm1, [eax+ebx+ebx+color1] + push eax + add eax, ebx + movq mm3, [eax+ebx+ebx+colorA0] + pop eax + + pcmpeqw mm2, mm4 + pcmpeqw mm6, mm5 + pcmpeqw mm1, mm5 + pcmpeqw mm3, mm5 + pxor mm5, mm5 + pcmpeqw mm2, mm5 + pcmpeqw mm3, mm5 + pand mm6, mm1 + pand mm2, mm3 + pand mm6, mm2 + por mm7, mm6 + + + movq mm6, mm7 + pcmpeqw mm6, mm5 + pand mm7, mm0 + + movq mm1, [eax+ebx+ebx+color2] + pand mm6, mm1 + por mm7, mm6 + movq [final2a], mm7 ;finished 2a + + + ;-------------------------------------------- + + +%ifdef dfhsdfhsdahdsfhdsfh + if (color6 == color3 && color3 == colorA1 && color2 != colorA2 && color3 != colorA0) + product2b = Q_INTERPOLATE (color3, color3, color3, color2); + else + if (color5 == color2 && color2 == colorA2 && colorA1 != color3 && color2 != colorA3) + product2b = Q_INTERPOLATE (color2, color2, color2, color3); + else + product2b = INTERPOLATE (color2, color3); + + if (color6 == color3 && color6 == colorB1 && color5 != colorB2 && color6 != colorB0) + product1b = Q_INTERPOLATE (color6, color6, color6, color5); + else + if (color5 == color2 && color5 == colorB2 && colorB1 != color6 && color5 != colorB3) + product1b = Q_INTERPOLATE (color6, color5, color5, color5); + else + product1b = INTERPOLATE (color5, color6); +%endif + + push eax + add eax, ebx + pxor mm7, mm7 + movq mm0, [eax+ebx+ebx+colorA0] + movq mm1, [eax+ebx+ebx+colorA1] + movq mm2, [eax+ebx+ebx+colorA2] + movq mm3, [eax+ebx+ebx+colorA3] + pop eax + movq mm4, [eax+ebx+ebx+color2] + movq mm5, [eax+ebx+ebx+color3] + movq mm6, [eax+ebx+color6] + + pcmpeqw mm6, mm5 + pcmpeqw mm1, mm5 + pcmpeqw mm4, mm2 + pcmpeqw mm0, mm5 + pcmpeqw mm4, mm7 + pcmpeqw mm0, mm7 + pand mm0, mm4 + pand mm6, mm1 + pand mm0, mm6 + + + push eax + add eax, ebx + movq mm1, [eax+ebx+ebx+colorA1] + pop eax + movq mm4, [eax+ebx+ebx+color2] + movq mm5, [eax+ebx+color5] + movq mm6, [eax+ebx+ebx+color3] + + pcmpeqw mm5, mm4 + pcmpeqw mm2, mm4 + pcmpeqw mm1, mm6 + pcmpeqw mm3, mm4 + pcmpeqw mm1, mm7 + pcmpeqw mm3, mm7 + pand mm2, mm5 + pand mm1, mm3 + pand mm1, mm2 + + + movq mm7, mm0 + por mm7, mm1 + + movq mm4, [Mask35] + movq mm3, [Mask26] + + movq mm6, mm4 + pand mm6, mm7 + pxor mm4, mm6 + + movq mm6, mm3 + pand mm6, mm7 + pxor mm3, mm6 + + movq mm2, mm0 + movq mm7, [I2333Pixel] + movq mm6, [I2223Pixel] + movq mm5, [I23Pixel] + + + por mm2, mm4 + pand mm4, [eax+ebx+ebx+color3] + por mm2, mm3 + pand mm3, [eax+ebx+ebx+color2] + por mm2, mm1 + pand mm0, mm7 + pand mm1, mm6 + pxor mm7, mm7 + pcmpeqw mm2, mm7 + por mm0, mm1 + por mm3, mm4 + pand mm2, mm5 + por mm0, mm3 + por mm0, mm2 + movq [final2b], mm0 + + ;----------------------------------- + + + pxor mm7, mm7 + movq mm0, [eax+colorB0] + movq mm1, [eax+colorB1] + movq mm2, [eax+colorB2] + movq mm3, [eax+colorB3] + movq mm4, [eax+ebx+color5] + movq mm5, [eax+ebx+color6] + movq mm6, [eax+ebx+ebx+color3] + + pcmpeqw mm6, mm5 + pcmpeqw mm1, mm5 + pcmpeqw mm4, mm2 + pcmpeqw mm0, mm5 + pcmpeqw mm4, mm7 + pcmpeqw mm0, mm7 + pand mm0, mm4 + pand mm6, mm1 + pand mm0, mm6 + + movq mm1, [eax+colorB1] + movq mm4, [eax+ebx+color5] + movq mm5, [eax+ebx+ebx+color2] + movq mm6, [eax+ebx+color6] + + pcmpeqw mm5, mm4 + pcmpeqw mm2, mm4 + pcmpeqw mm1, mm6 + pcmpeqw mm3, mm4 + pcmpeqw mm1, mm7 + pcmpeqw mm3, mm7 + pand mm2, mm5 + pand mm1, mm3 + pand mm1, mm2 + + + movq mm7, mm0 + por mm7, mm1 + + movq mm4, [Mask35] + movq mm3, [Mask26] + + movq mm6, mm4 + pand mm6, mm7 + pxor mm4, mm6 + + movq mm6, mm3 + pand mm6, mm7 + pxor mm3, mm6 + + movq mm2, mm0 + movq mm7, [I5666Pixel] + movq mm6, [I5556Pixel] + movq mm5, [I56Pixel] + + + por mm2, mm4 + pand mm4, [eax+ebx+color5] + por mm2, mm3 + pand mm3, [eax+ebx+color6] + por mm2, mm1 + pand mm0, mm7 + pand mm1, mm6 + pxor mm7, mm7 + pcmpeqw mm2, mm7 + por mm0, mm1 + por mm3, mm4 + pand mm2, mm5 + por mm0, mm3 + por mm0, mm2 + movq [final1b], mm0 + + ;--------- + + movq mm0, [final1a] + movq mm4, [final2a] + movq mm2, [final1b] + movq mm6, [final2b] + + + movq mm1, mm0 + movq mm5, mm4 + + + punpcklwd mm0, mm2 + punpckhwd mm1, mm2 + + punpcklwd mm4, mm6 + punpckhwd mm5, mm6 + + +%ifdef FAR_POINTER + movq [fs:edx], mm0 + movq [fs:edx+8], mm1 + push edx + add edx, [ebp+dstPitch] + movq [fs:edx], mm4 + movq [fs:edx+8], mm5 + pop edx +%else + movq [edx], mm0 + movq [edx+8], mm1 + push edx + add edx, [ebp+dstPitch] + movq [edx], mm4 + movq [edx+8], mm5 + pop edx +%endif +.SKIP_PROCESS: + mov ecx, [ebp+deltaPtr] + add ecx, 8 + mov [ebp+deltaPtr], ecx + add edx, 16 + add eax, 8 + + pop ecx + sub ecx, 4 + cmp ecx, 0 + jg near .Loop + +; Restore some stuff + popad + mov esp, ebp + pop ebp + emms + ret + + +;------------------------------------------------------------------------- +;------------------------------------------------------------------------- +;------------------------------------------------------------------------- +;------------------------------------------------------------------------- +;------------------------------------------------------------------------- +;------------------------------------------------------------------------- +;------------------------------------------------------------------------- + + + +%ifdef __DJGPP__ +__2xSaISuperEagleLine: +%else +_2xSaISuperEagleLine: +%endif +; Store some stuff + push ebp + mov ebp, esp + pushad + +; Prepare the destination +%ifdef FAR_POINTER + ; Set the selector + mov eax, [ebp+dstSegment] + mov fs, ax +%endif + mov edx, [ebp+dstOffset] ; edx points to the screen +; Prepare the source + ; eax points to colorA + mov eax, [ebp+srcPtr] + mov ebx, [ebp+srcPitch] + mov ecx, [ebp+width] + ; eax now points to colorB1 + sub eax, ebx + +; Main Loop +.Loop: push ecx + + ;-----Check Delta------------------ + mov ecx, [ebp+deltaPtr] + + movq mm0, [eax+colorB0] + movq mm1, [eax+colorB3] + movq mm2, [eax+ebx+color4] + movq mm3, [eax+ebx+colorS2] + movq mm4, [eax+ebx+ebx+color1] + movq mm5, [eax+ebx+ebx+colorS1] + push eax + add eax, ebx + movq mm6, [eax+ebx+ebx+colorA0] + movq mm7, [eax+ebx+ebx+colorA3] + pop eax + + pcmpeqw mm0, [ecx+2+colorB0] + pcmpeqw mm1, [ecx+2+colorB3] + pcmpeqw mm2, [ecx+ebx+2+color4] + pcmpeqw mm3, [ecx+ebx+2+colorS2] + pcmpeqw mm4, [ecx+ebx+ebx+2+color1] + pcmpeqw mm5, [ecx+ebx+ebx+2+colorS1] + add ecx, ebx + pcmpeqw mm6, [ecx+ebx+ebx+2+colorA0] + pcmpeqw mm7, [ecx+ebx+ebx+2+colorA3] + sub ecx, ebx + + + pand mm0, mm1 + pand mm2, mm3 + pand mm4, mm5 + pand mm6, mm7 + pand mm0, mm2 + pand mm4, mm6 + pxor mm7, mm7 + pand mm0, mm4 + movq mm6, [eax+colorB0] + pcmpeqw mm7, mm0 + + movq [ecx+2+colorB0], mm6 + + packsswb mm7, mm7 + movd ecx, mm7 + test ecx, ecx + jz near .SKIP_PROCESS + + ;End Delta + + ;--------------------------------- + movq mm0, [eax+ebx+color5] + movq mm1, [eax+ebx+color6] + movq mm2, mm0 + movq mm3, mm1 + movq mm4, mm0 + movq mm5, mm1 + + pand mm0, [colorMask] + pand mm1, [colorMask] + + psrlw mm0, 1 + psrlw mm1, 1 + + pand mm3, [lowPixelMask] + paddw mm0, mm1 + + pand mm3, mm2 + paddw mm0, mm3 ;mm0 contains the interpolated values + movq [I56Pixel], mm0 + movq mm7, mm0 + + ;------------------- + movq mm0, mm7 + movq mm1, mm4 ;5,5,5,6 + movq mm2, mm0 + movq mm3, mm1 + + pand mm0, [colorMask] + pand mm1, [colorMask] + + psrlw mm0, 1 + psrlw mm1, 1 + + pand mm3, [lowPixelMask] + paddw mm0, mm1 + + pand mm3, mm2 + paddw mm0, mm3 ;mm0 contains the interpolated values + movq [product1a], mm0 + ;-------------------- + + movq mm0, mm7 + movq mm1, mm5 ;6,6,6,5 + movq mm2, mm0 + movq mm3, mm1 + + pand mm0, [colorMask] + pand mm1, [colorMask] + + psrlw mm0, 1 + psrlw mm1, 1 + + pand mm3, [lowPixelMask] + paddw mm0, mm1 + + pand mm3, mm2 + paddw mm0, mm3 + movq [product1b], mm0 + + ;------------------------- + ;------------------------- + movq mm0, [eax+ebx+ebx+color2] + movq mm1, [eax+ebx+ebx+color3] + movq mm2, mm0 + movq mm3, mm1 + movq mm4, mm0 + movq mm5, mm1 + + pand mm0, [colorMask] + pand mm1, [colorMask] + + psrlw mm0, 1 + psrlw mm1, 1 + + pand mm3, [lowPixelMask] + paddw mm0, mm1 + + pand mm3, mm2 + paddw mm0, mm3 + movq [I23Pixel], mm0 + movq mm7, mm0 + + ;--------------------- + movq mm0, mm7 + movq mm1, mm4 ;2,2,2,3 + movq mm2, mm0 + movq mm3, mm1 + + pand mm0, [colorMask] + pand mm1, [colorMask] + + psrlw mm0, 1 + psrlw mm1, 1 + + pand mm3, [lowPixelMask] + paddw mm0, mm1 + + pand mm3, mm2 + paddw mm0, mm3 + movq [product2a], mm0 + + ;---------------------- + movq mm0, mm7 + movq mm1, mm5 ;3,3,3,2 + movq mm2, mm0 + movq mm3, mm1 + + pand mm0, [colorMask] + pand mm1, [colorMask] + + psrlw mm0, 1 + psrlw mm1, 1 + + pand mm3, [lowPixelMask] + paddw mm0, mm1 + + pand mm3, mm2 + paddw mm0, mm3 + movq [product2b], mm0 + + + ;//////////////////////////////// + ; Decide which "branch" to take + ;-------------------------------- + movq mm4, [eax+ebx+color5] + movq mm5, [eax+ebx+color6] + movq mm6, [eax+ebx+ebx+color3] + movq mm7, [eax+ebx+ebx+color2] + + pxor mm3, mm3 + movq mm0, mm4 + movq mm1, mm5 + + pcmpeqw mm0, mm6 + pcmpeqw mm1, mm7 + pcmpeqw mm1, mm3 + pand mm0, mm1 + movq [Mask35], mm0 + + movq mm0, [eax+ebx+ebx+colorS1] + movq mm1, [eax+ebx+color4] + push eax + add eax, ebx + movq mm2, [eax+ebx+ebx+colorA2] + pop eax + movq mm3, [eax+colorB1] + pcmpeqw mm0, mm4 + pcmpeqw mm1, mm4 + pcmpeqw mm2, mm4 + pcmpeqw mm3, mm4 + pand mm0, mm1 + pand mm2, mm3 + por mm0, mm2 + pand mm0, [Mask35] + movq [Mask35b], mm0 + + ;----------- + pxor mm3, mm3 + movq mm0, mm4 + movq mm1, mm5 + + pcmpeqw mm0, mm6 + pcmpeqw mm1, mm7 + pcmpeqw mm0, mm3 + pand mm0, mm1 + movq [Mask26], mm0 + + movq mm0, [eax+ebx+ebx+color1] + movq mm1, [eax+ebx+colorS2] + push eax + add eax, ebx + movq mm2, [eax+ebx+ebx+colorA1] + pop eax + movq mm3, [eax+colorB2] + pcmpeqw mm0, mm5 + pcmpeqw mm1, mm5 + pcmpeqw mm2, mm5 + pcmpeqw mm3, mm5 + pand mm0, mm1 + pand mm2, mm3 + por mm0, mm2 + pand mm0, [Mask26] + movq [Mask26b], mm0 + + ;-------------------- + movq mm0, mm4 + movq mm1, mm5 + movq mm2, mm0 + + pcmpeqw mm2, mm1 + pcmpeqw mm0, mm6 + pcmpeqw mm1, mm7 + pand mm0, mm1 + pand mm2, mm0 + pxor mm0, mm2 + movq mm7, mm0 + + ;------------------ + packsswb mm7, mm7 + movd ecx, mm7 + test ecx, ecx + jz near .SKIP_GUESS + +;--------------------------------------------- +; Map of the pixels: I|E F|J +; G|A B|K +; H|C D|L +; M|N O|P + movq mm6, mm0 + movq mm4, [eax+ebx+color5] + movq mm5, [eax+ebx+color6] + pxor mm7, mm7 + pand mm6, [ONE] + + movq mm0, [eax+colorB1] + movq mm1, [eax+ebx+color4] + movq mm2, mm0 + movq mm3, mm1 + pcmpeqw mm0, mm4 + pcmpeqw mm1, mm4 + pcmpeqw mm2, mm5 + pcmpeqw mm3, mm5 + pand mm0, mm6 + pand mm1, mm6 + pand mm2, mm6 + pand mm3, mm6 + paddw mm0, mm1 + paddw mm2, mm3 + + pxor mm3, mm3 + pcmpgtw mm0, mm6 + pcmpgtw mm2, mm6 + pcmpeqw mm0, mm3 + pcmpeqw mm2, mm3 + pand mm0, mm6 + pand mm2, mm6 + paddw mm7, mm0 + psubw mm7, mm2 + + movq mm0, [eax+colorB2] + movq mm1, [eax+ebx+colorS2] + movq mm2, mm0 + movq mm3, mm1 + pcmpeqw mm0, mm4 + pcmpeqw mm1, mm4 + pcmpeqw mm2, mm5 + pcmpeqw mm3, mm5 + pand mm0, mm6 + pand mm1, mm6 + pand mm2, mm6 + pand mm3, mm6 + paddw mm0, mm1 + paddw mm2, mm3 + + pxor mm3, mm3 + pcmpgtw mm0, mm6 + pcmpgtw mm2, mm6 + pcmpeqw mm0, mm3 + pcmpeqw mm2, mm3 + pand mm0, mm6 + pand mm2, mm6 + paddw mm7, mm0 + psubw mm7, mm2 + + push eax + add eax, ebx + movq mm0, [eax+ebx+color1] + movq mm1, [eax+ebx+ebx+colorA1] + movq mm2, mm0 + movq mm3, mm1 + pcmpeqw mm0, mm4 + pcmpeqw mm1, mm4 + pcmpeqw mm2, mm5 + pcmpeqw mm3, mm5 + pand mm0, mm6 + pand mm1, mm6 + pand mm2, mm6 + pand mm3, mm6 + paddw mm0, mm1 + paddw mm2, mm3 + + pxor mm3, mm3 + pcmpgtw mm0, mm6 + pcmpgtw mm2, mm6 + pcmpeqw mm0, mm3 + pcmpeqw mm2, mm3 + pand mm0, mm6 + pand mm2, mm6 + paddw mm7, mm0 + psubw mm7, mm2 + + movq mm0, [eax+ebx+colorS1] + movq mm1, [eax+ebx+ebx+colorA2] + movq mm2, mm0 + movq mm3, mm1 + pcmpeqw mm0, mm4 + pcmpeqw mm1, mm4 + pcmpeqw mm2, mm5 + pcmpeqw mm3, mm5 + pand mm0, mm6 + pand mm1, mm6 + pand mm2, mm6 + pand mm3, mm6 + paddw mm0, mm1 + paddw mm2, mm3 + + pxor mm3, mm3 + pcmpgtw mm0, mm6 + pcmpgtw mm2, mm6 + pcmpeqw mm0, mm3 + pcmpeqw mm2, mm3 + pand mm0, mm6 + pand mm2, mm6 + paddw mm7, mm0 + psubw mm7, mm2 + + pop eax + movq mm1, mm7 + pxor mm0, mm0 + pcmpgtw mm7, mm0 + pcmpgtw mm0, mm1 + + por mm7, [Mask35] + por mm0, [Mask26] + movq [Mask35], mm7 + movq [Mask26], mm0 + +.SKIP_GUESS: + ;Start the ASSEMBLY !!! + + movq mm4, [Mask35] + movq mm5, [Mask26] + movq mm6, [Mask35b] + movq mm7, [Mask26b] + + movq mm0, [eax+ebx+color5] + movq mm1, [eax+ebx+color6] + movq mm2, [eax+ebx+ebx+color2] + movq mm3, [eax+ebx+ebx+color3] + pcmpeqw mm0, mm2 + pcmpeqw mm1, mm3 + movq mm2, mm4 + movq mm3, mm5 + por mm0, mm1 + por mm2, mm3 + pand mm2, mm0 + pxor mm0, mm2 + movq mm3, mm0 + + movq mm2, mm0 + pxor mm0, mm0 + por mm2, mm4 + pxor mm4, mm6 + por mm2, mm5 + pxor mm5, mm7 + pcmpeqw mm2, mm0 + ;---------------- + + movq mm0, [eax+ebx+color5] + movq mm1, mm3 + por mm1, mm4 + por mm1, mm6 + pand mm0, mm1 + movq mm1, mm5 + pand mm1, [I56Pixel] + por mm0, mm1 + movq mm1, mm7 + pand mm1, [product1b] + por mm0, mm1 + movq mm1, mm2 + pand mm1, [product1a] + por mm0, mm1 + movq [final1a], mm0 + + movq mm0, [eax+ebx+color6] + movq mm1, mm3 + por mm1, mm5 + por mm1, mm7 + pand mm0, mm1 + movq mm1, mm4 + pand mm1, [I56Pixel] + por mm0, mm1 + movq mm1, mm6 + pand mm1, [product1a] + por mm0, mm1 + movq mm1, mm2 + pand mm1, [product1b] + por mm0, mm1 + movq [final1b], mm0 + + movq mm0, [eax+ebx+ebx+color2] + movq mm1, mm3 + por mm1, mm5 + por mm1, mm7 + pand mm0, mm1 + movq mm1, mm4 + pand mm1, [I23Pixel] + por mm0, mm1 + movq mm1, mm6 + pand mm1, [product2b] + por mm0, mm1 + movq mm1, mm2 + pand mm1, [product2a] + por mm0, mm1 + movq [final2a], mm0 + + movq mm0, [eax+ebx+ebx+color3] + movq mm1, mm3 + por mm1, mm4 + por mm1, mm6 + pand mm0, mm1 + movq mm1, mm5 + pand mm1, [I23Pixel] + por mm0, mm1 + movq mm1, mm7 + pand mm1, [product2a] + por mm0, mm1 + movq mm1, mm2 + pand mm1, [product2b] + por mm0, mm1 + movq [final2b], mm0 + + + movq mm0, [final1a] + movq mm2, [final1b] + movq mm1, mm0 + movq mm4, [final2a] + movq mm6, [final2b] + movq mm5, mm4 + punpcklwd mm0, mm2 + punpckhwd mm1, mm2 + punpcklwd mm4, mm6 + punpckhwd mm5, mm6 + + + + +%ifdef FAR_POINTER + movq [fs:edx], mm0 + movq [fs:edx+8], mm1 + push edx + add edx, [ebp+dstPitch] + movq [fs:edx], mm4 + movq [fs:edx+8], mm5 + pop edx +%else + movq [edx], mm0 + movq [edx+8], mm1 + push edx + add edx, [ebp+dstPitch] + movq [edx], mm4 + movq [edx+8], mm5 + pop edx +%endif +.SKIP_PROCESS: + mov ecx, [ebp+deltaPtr] + add ecx, 8 + mov [ebp+deltaPtr], ecx + add edx, 16 + add eax, 8 + + pop ecx + sub ecx, 4 + cmp ecx, 0 + jg near .Loop + +; Restore some stuff + popad + mov esp, ebp + pop ebp + emms + ret + + +;------------------------------------------------------------------------- +;------------------------------------------------------------------------- +;------------------------------------------------------------------------- +;------------------------------------------------------------------------- +;------------------------------------------------------------------------- +;------------------------------------------------------------------------- +;------------------------------------------------------------------------- + + +;This is version 0.50 +colorI equ -2 +colorE equ 0 +colorF equ 2 +colorJ equ 4 + +colorG equ -2 +colorA equ 0 +colorB equ 2 +colorK equ 4 + +colorH equ -2 +colorC equ 0 +colorD equ 2 +colorL equ 4 + +colorM equ -2 +colorN equ 0 +colorO equ 2 +colorP equ 4 + +%ifdef __DJGPP__ +__2xSaILine: +%else +_2xSaILine: +%endif +; Store some stuff + push ebp + mov ebp, esp + pushad + +; Prepare the destination +%ifdef FAR_POINTER + ; Set the selector + mov eax, [ebp+dstSegment] + mov fs, ax +%endif + mov edx, [ebp+dstOffset] ; edx points to the screen +; Prepare the source + ; eax points to colorA + mov eax, [ebp+srcPtr] + mov ebx, [ebp+srcPitch] + mov ecx, [ebp+width] + ; eax now points to colorE + sub eax, ebx + + +; Main Loop +.Loop: push ecx + + ;-----Check Delta------------------ + mov ecx, [ebp+deltaPtr] + + movq mm0, [eax+colorI] + movq mm1, [eax+colorJ] + movq mm2, [eax+ebx+colorG] + movq mm3, [eax+ebx+colorK] + movq mm4, [eax+ebx+ebx+colorH] + movq mm5, [eax+ebx+ebx+colorL] + push eax + add eax, ebx + movq mm6, [eax+ebx+ebx+colorM] + movq mm7, [eax+ebx+ebx+colorP] + pop eax + + pcmpeqw mm0, [ecx+2+colorI] + pcmpeqw mm1, [ecx+2+colorK] + pcmpeqw mm2, [ecx+ebx+2+colorG] + pcmpeqw mm3, [ecx+ebx+2+colorK] + pcmpeqw mm4, [ecx+ebx+ebx+2+colorH] + pcmpeqw mm5, [ecx+ebx+ebx+2+colorL] + add ecx, ebx + pcmpeqw mm6, [ecx+ebx+ebx+2+colorM] + pcmpeqw mm7, [ecx+ebx+ebx+2+colorP] + sub ecx, ebx + + + pand mm0, mm1 + pand mm2, mm3 + pand mm4, mm5 + pand mm6, mm7 + pand mm0, mm2 + pand mm4, mm6 + pxor mm7, mm7 + pand mm0, mm4 + movq mm6, [eax+colorI] + pcmpeqw mm7, mm0 + + movq [ecx+2+colorI], mm6 + + packsswb mm7, mm7 + movd ecx, mm7 + test ecx, ecx + jz near .SKIP_PROCESS + + ;End Delta + + ;--------------------------------- + + +;1 + ;if ((colorA == colorD) && (colorB != colorC) && (colorA == colorE) && (colorB == colorL) + movq mm0, [eax+ebx+colorA] ;mm0 and mm1 contain colorA + movq mm2, [eax+ebx+colorB] ;mm2 and mm3 contain colorB + + movq mm1, mm0 + movq mm3, mm2 + + pcmpeqw mm0, [eax+ebx+ebx+colorD] + pcmpeqw mm1, [eax+colorE] + pcmpeqw mm2, [eax+ebx+ebx+colorL] + pcmpeqw mm3, [eax+ebx+ebx+colorC] + + pand mm0, mm1 + pxor mm1, mm1 + pand mm0, mm2 + pcmpeqw mm3, mm1 + pand mm0, mm3 ;result in mm0 + + ;if ((colorA == colorC) && (colorB != colorE) && (colorA == colorF) && (colorB == colorJ) + movq mm4, [eax+ebx+colorA] ;mm4 and mm5 contain colorA + movq mm6, [eax+ebx+colorB] ;mm6 and mm7 contain colorB + movq mm5, mm4 + movq mm7, mm6 + + pcmpeqw mm4, [eax+ebx+ebx+colorC] + pcmpeqw mm5, [eax+colorF] + pcmpeqw mm6, [eax+colorJ] + pcmpeqw mm7, [eax+colorE] + + pand mm4, mm5 + pxor mm5, mm5 + pand mm4, mm6 + pcmpeqw mm7, mm5 + pand mm4, mm7 ;result in mm4 + + por mm0, mm4 ;combine the masks + movq [Mask1], mm0 + + ;-------------------------------------------- + +;2 + ;if ((colorB == colorC) && (colorA != colorD) && (colorB == colorF) && (colorA == colorH) + movq mm0, [eax+ebx+colorB] ;mm0 and mm1 contain colorB + movq mm2, [eax+ebx+colorA] ;mm2 and mm3 contain colorA + movq mm1, mm0 + movq mm3, mm2 + + pcmpeqw mm0, [eax+ebx+ebx+colorC] + pcmpeqw mm1, [eax+colorF] + pcmpeqw mm2, [eax+ebx+ebx+colorH] + pcmpeqw mm3, [eax+ebx+ebx+colorD] + + pand mm0, mm1 + pxor mm1, mm1 + pand mm0, mm2 + pcmpeqw mm3, mm1 + pand mm0, mm3 ;result in mm0 + + ;if ((colorB == colorE) && (colorB == colorD) && (colorA != colorF) && (colorA == colorI) + movq mm4, [eax+ebx+colorB] ;mm4 and mm5 contain colorB + movq mm6, [eax+ebx+colorA] ;mm6 and mm7 contain colorA + movq mm5, mm4 + movq mm7, mm6 + + pcmpeqw mm4, [eax+ebx+ebx+colorD] + pcmpeqw mm5, [eax+colorE] + pcmpeqw mm6, [eax+colorI] + pcmpeqw mm7, [eax+colorF] + + pand mm4, mm5 + pxor mm5, mm5 + pand mm4, mm6 + pcmpeqw mm7, mm5 + pand mm4, mm7 ;result in mm4 + + por mm0, mm4 ;combine the masks + movq [Mask2], mm0 + + +;interpolate colorA and colorB + movq mm0, [eax+ebx+colorA] + movq mm1, [eax+ebx+colorB] + + movq mm2, mm0 + movq mm3, mm1 + + pand mm0, [colorMask] + pand mm1, [colorMask] + + psrlw mm0, 1 + psrlw mm1, 1 + + pand mm3, [lowPixelMask] + paddw mm0, mm1 + + pand mm3, mm2 + paddw mm0, mm3 ;mm0 contains the interpolated values + + ;assemble the pixels + movq mm1, [eax+ebx+colorA] + movq mm2, [eax+ebx+colorB] + + movq mm3, [Mask1] + movq mm5, mm1 + movq mm4, [Mask2] + movq mm6, mm1 + + pand mm1, mm3 + por mm3, mm4 + pxor mm7, mm7 + pand mm2, mm4 + + pcmpeqw mm3, mm7 + por mm1, mm2 + pand mm0, mm3 + + por mm0, mm1 + + punpcklwd mm5, mm0 + punpckhwd mm6, mm0 + +%ifdef FAR_POINTER + movq [fs:edx], mm5 + movq [fs:edx+8], mm6 +%else + movq [edx], mm5 + movq [edx+8], mm6 +%endif + +;------------------------------------------------ +; Create the Nextline +;------------------------------------------------ +;3 ;if ((colorA == colorD) && (colorB != colorC) && (colorA == colorG) && (colorC == colorO) + movq mm0, [eax+ebx+colorA] ;mm0 and mm1 contain colorA + movq mm2, [eax+ebx+ebx+colorC] ;mm2 and mm3 contain colorC + movq mm1, mm0 + movq mm3, mm2 + + push eax + add eax, ebx + pcmpeqw mm0, [eax+ebx+colorD] + pcmpeqw mm1, [eax+colorG] + pcmpeqw mm2, [eax+ebx+ebx+colorO] + pcmpeqw mm3, [eax+colorB] + pop eax + + pand mm0, mm1 + pxor mm1, mm1 + pand mm0, mm2 + pcmpeqw mm3, mm1 + pand mm0, mm3 ;result in mm0 + + ;if ((colorA == colorB) && (colorG != colorC) && (colorA == colorH) && (colorC == colorM) + movq mm4, [eax+ebx+colorA] ;mm4 and mm5 contain colorA + movq mm6, [eax+ebx+ebx+colorC] ;mm6 and mm7 contain colorC + movq mm5, mm4 + movq mm7, mm6 + + push eax + add eax, ebx + pcmpeqw mm4, [eax+ebx+colorH] + pcmpeqw mm5, [eax+colorB] + pcmpeqw mm6, [eax+ebx+ebx+colorM] + pcmpeqw mm7, [eax+colorG] + pop eax + + pand mm4, mm5 + pxor mm5, mm5 + pand mm4, mm6 + pcmpeqw mm7, mm5 + pand mm4, mm7 ;result in mm4 + + por mm0, mm4 ;combine the masks + movq [Mask1], mm0 + ;-------------------------------------------- + +;4 + ;if ((colorB == colorC) && (colorA != colorD) && (colorC == colorH) && (colorA == colorF) + movq mm0, [eax+ebx+ebx+colorC] ;mm0 and mm1 contain colorC + movq mm2, [eax+ebx+colorA] ;mm2 and mm3 contain colorA + movq mm1, mm0 + movq mm3, mm2 + + pcmpeqw mm0, [eax+ebx+colorB] + pcmpeqw mm1, [eax+ebx+ebx+colorH] + pcmpeqw mm2, [eax+colorF] + pcmpeqw mm3, [eax+ebx+ebx+colorD] + + pand mm0, mm1 + pxor mm1, mm1 + pand mm0, mm2 + pcmpeqw mm3, mm1 + pand mm0, mm3 ;result in mm0 + + ;if ((colorC == colorG) && (colorC == colorD) && (colorA != colorH) && (colorA == colorI) + movq mm4, [eax+ebx+ebx+colorC] ;mm4 and mm5 contain colorC + movq mm6, [eax+ebx+colorA] ;mm6 and mm7 contain colorA + movq mm5, mm4 + movq mm7, mm6 + + pcmpeqw mm4, [eax+ebx+ebx+colorD] + pcmpeqw mm5, [eax+ebx+colorG] + pcmpeqw mm6, [eax+colorI] + pcmpeqw mm7, [eax+ebx+ebx+colorH] + + pand mm4, mm5 + pxor mm5, mm5 + pand mm4, mm6 + pcmpeqw mm7, mm5 + pand mm4, mm7 ;result in mm4 + + por mm0, mm4 ;combine the masks + movq [Mask2], mm0 + ;---------------------------------------------- + +;interpolate colorA and colorC + movq mm0, [eax+ebx+colorA] + movq mm1, [eax+ebx+ebx+colorC] + + movq mm2, mm0 + movq mm3, mm1 + + pand mm0, [colorMask] + pand mm1, [colorMask] + + psrlw mm0, 1 + psrlw mm1, 1 + + pand mm3, [lowPixelMask] + paddw mm0, mm1 + + pand mm3, mm2 + paddw mm0, mm3 ;mm0 contains the interpolated values + ;------------- + + ;assemble the pixels + movq mm1, [eax+ebx+colorA] + movq mm2, [eax+ebx+ebx+colorC] + + movq mm3, [Mask1] + movq mm4, [Mask2] + + pand mm1, mm3 + pand mm2, mm4 + + por mm3, mm4 + pxor mm7, mm7 + por mm1, mm2 + + pcmpeqw mm3, mm7 + pand mm0, mm3 + por mm0, mm1 + movq [ACPixel], mm0 + +;//////////////////////////////// +; Decide which "branch" to take +;-------------------------------- + movq mm0, [eax+ebx+colorA] + movq mm1, [eax+ebx+colorB] + movq mm6, mm0 + movq mm7, mm1 + pcmpeqw mm0, [eax+ebx+ebx+colorD] + pcmpeqw mm1, [eax+ebx+ebx+colorC] + pcmpeqw mm6, mm7 + + movq mm2, mm0 + movq mm3, mm0 + + pand mm0, mm1 ;colorA == colorD && colorB == colorC + pxor mm7, mm7 + + pcmpeqw mm2, mm7 + pand mm6, mm0 + pand mm2, mm1 ;colorA != colorD && colorB == colorC + + pcmpeqw mm1, mm7 + + pand mm1, mm3 ;colorA == colorD && colorB != colorC + pxor mm0, mm6 + por mm1, mm6 + movq mm7, mm0 + movq [Mask2], mm2 + packsswb mm7, mm7 + movq [Mask1], mm1 + + movd ecx, mm7 + test ecx, ecx + jz near .SKIP_GUESS + +;--------------------------------------------- +; Map of the pixels: I|E F|J +; G|A B|K +; H|C D|L +; M|N O|P + movq mm6, mm0 + movq mm4, [eax+ebx+colorA] + movq mm5, [eax+ebx+colorB] + pxor mm7, mm7 + pand mm6, [ONE] + + movq mm0, [eax+colorE] + movq mm1, [eax+ebx+colorG] + movq mm2, mm0 + movq mm3, mm1 + pcmpeqw mm0, mm4 + pcmpeqw mm1, mm4 + pcmpeqw mm2, mm5 + pcmpeqw mm3, mm5 + pand mm0, mm6 + pand mm1, mm6 + pand mm2, mm6 + pand mm3, mm6 + paddw mm0, mm1 + paddw mm2, mm3 + + pxor mm3, mm3 + pcmpgtw mm0, mm6 + pcmpgtw mm2, mm6 + pcmpeqw mm0, mm3 + pcmpeqw mm2, mm3 + pand mm0, mm6 + pand mm2, mm6 + paddw mm7, mm0 + psubw mm7, mm2 + + movq mm0, [eax+colorF] + movq mm1, [eax+ebx+colorK] + movq mm2, mm0 + movq mm3, mm1 + pcmpeqw mm0, mm4 + pcmpeqw mm1, mm4 + pcmpeqw mm2, mm5 + pcmpeqw mm3, mm5 + pand mm0, mm6 + pand mm1, mm6 + pand mm2, mm6 + pand mm3, mm6 + paddw mm0, mm1 + paddw mm2, mm3 + + pxor mm3, mm3 + pcmpgtw mm0, mm6 + pcmpgtw mm2, mm6 + pcmpeqw mm0, mm3 + pcmpeqw mm2, mm3 + pand mm0, mm6 + pand mm2, mm6 + paddw mm7, mm0 + psubw mm7, mm2 + + push eax + add eax, ebx + movq mm0, [eax+ebx+colorH] + movq mm1, [eax+ebx+ebx+colorN] + movq mm2, mm0 + movq mm3, mm1 + pcmpeqw mm0, mm4 + pcmpeqw mm1, mm4 + pcmpeqw mm2, mm5 + pcmpeqw mm3, mm5 + pand mm0, mm6 + pand mm1, mm6 + pand mm2, mm6 + pand mm3, mm6 + paddw mm0, mm1 + paddw mm2, mm3 + + pxor mm3, mm3 + pcmpgtw mm0, mm6 + pcmpgtw mm2, mm6 + pcmpeqw mm0, mm3 + pcmpeqw mm2, mm3 + pand mm0, mm6 + pand mm2, mm6 + paddw mm7, mm0 + psubw mm7, mm2 + + movq mm0, [eax+ebx+colorL] + movq mm1, [eax+ebx+ebx+colorO] + movq mm2, mm0 + movq mm3, mm1 + pcmpeqw mm0, mm4 + pcmpeqw mm1, mm4 + pcmpeqw mm2, mm5 + pcmpeqw mm3, mm5 + pand mm0, mm6 + pand mm1, mm6 + pand mm2, mm6 + pand mm3, mm6 + paddw mm0, mm1 + paddw mm2, mm3 + + pxor mm3, mm3 + pcmpgtw mm0, mm6 + pcmpgtw mm2, mm6 + pcmpeqw mm0, mm3 + pcmpeqw mm2, mm3 + pand mm0, mm6 + pand mm2, mm6 + paddw mm7, mm0 + psubw mm7, mm2 + + pop eax + movq mm1, mm7 + pxor mm0, mm0 + pcmpgtw mm7, mm0 + pcmpgtw mm0, mm1 + + por mm7, [Mask1] + por mm0, [Mask2] + movq [Mask1], mm7 + movq [Mask2], mm0 + +.SKIP_GUESS: + ;---------------------------- + ;interpolate A, B, C and D + movq mm0, [eax+ebx+colorA] + movq mm1, [eax+ebx+colorB] + movq mm4, mm0 + movq mm2, [eax+ebx+ebx+colorC] + movq mm5, mm1 + movq mm3, [qcolorMask] + movq mm6, mm2 + movq mm7, [qlowpixelMask] + + pand mm0, mm3 + pand mm1, mm3 + pand mm2, mm3 + pand mm3, [eax+ebx+ebx+colorD] + + psrlw mm0, 2 + pand mm4, mm7 + psrlw mm1, 2 + pand mm5, mm7 + psrlw mm2, 2 + pand mm6, mm7 + psrlw mm3, 2 + pand mm7, [eax+ebx+ebx+colorD] + + paddw mm0, mm1 + paddw mm2, mm3 + + paddw mm4, mm5 + paddw mm6, mm7 + + paddw mm4, mm6 + paddw mm0, mm2 + psrlw mm4, 2 + pand mm4, [qlowpixelMask] + paddw mm0, mm4 ;mm0 contains the interpolated value of A, B, C and D + +;\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + ;assemble the pixels + movq mm1, [Mask1] + movq mm2, [Mask2] + movq mm4, [eax+ebx+colorA] + movq mm5, [eax+ebx+colorB] + pand mm4, mm1 + pand mm5, mm2 + + pxor mm7, mm7 + por mm1, mm2 + por mm4, mm5 + pcmpeqw mm1, mm7 + pand mm0, mm1 + por mm4, mm0 ;mm4 contains the diagonal pixels + + movq mm0, [ACPixel] + movq mm1, mm0 + punpcklwd mm0, mm4 + punpckhwd mm1, mm4 + + push edx + add edx, [ebp+dstPitch] + +%ifdef FAR_POINTER + movq [fs:edx], mm0 + movq [fs:edx+8], mm1 +%else + movq [edx], mm0 + movq [edx+8], mm1 +%endif + pop edx + +.SKIP_PROCESS: + mov ecx, [ebp+deltaPtr] + add ecx, 8 + mov [ebp+deltaPtr], ecx + add edx, 16 + add eax, 8 + + pop ecx + sub ecx, 4 + cmp ecx, 0 + jg near .Loop + +; Restore some stuff + popad + mov esp, ebp + pop ebp + emms + ret + +;------------------------------------------------------------------------- +;------------------------------------------------------------------------- +;------------------------------------------------------------------------- +;------------------------------------------------------------------------- +;------------------------------------------------------------------------- +;------------------------------------------------------------------------- +;------------------------------------------------------------------------- + +%ifdef __DJGPP__ +_Init_2xSaIMMX: +%else +Init_2xSaIMMX: +%endif +; Store some stuff + push ebp + mov ebp, esp + push edx + + +;Damn thing doesn't work +; mov eax,1 +; cpuid +; test edx, 0x00800000 ;test bit 23 +; jz end2 ;bit not set => no MMX detected + + mov eax, [ebp+8] ;PixelFormat + cmp eax, 555 + jz Bits555 + cmp eax, 565 + jz Bits565 +end2: + mov eax, 1 + jmp end3 +Bits555: + mov edx, 0x7BDE7BDE + mov eax, colorMask + mov [eax], edx + mov [eax+4], edx + mov edx, 0x04210421 + mov eax, lowPixelMask + mov [eax], edx + mov [eax+4], edx + mov edx, 0x739C739C + mov eax, qcolorMask + mov [eax], edx + mov [eax+4], edx + mov edx, 0x0C630C63 + mov eax, qlowpixelMask + mov [eax], edx + mov [eax+4], edx + mov eax, 0 + jmp end3 +Bits565: + mov edx, 0xF7DEF7DE + mov eax, colorMask + mov [eax], edx + mov [eax+4], edx + mov edx, 0x08210821 + mov eax, lowPixelMask + mov [eax], edx + mov [eax+4], edx + mov edx, 0xE79CE79C + mov eax, qcolorMask + mov [eax], edx + mov [eax+4], edx + mov edx, 0x18631863 + mov eax, qlowpixelMask + mov [eax], edx + mov [eax+4], edx + mov eax, 0 + jmp end3 +end3: + pop edx + mov esp, ebp + pop ebp + ret + + +;------------------------------------------------------------------------- +;------------------------------------------------------------------------- +;------------------------------------------------------------------------- +;------------------------------------------------------------------------- +;------------------------------------------------------------------------- +;------------------------------------------------------------------------- +;------------------------------------------------------------------------- + + SECTION .data ALIGN = 32 +;Some constants +colorMask dd 0xF7DEF7DE,0xF7DEF7DE +lowPixelMask dd 0x08210821,0x08210821 + +qcolorMask dd 0xE79CE79C,0xE79CE79C +qlowpixelMask dd 0x18631863,0x18631863 + +darkenMask dd 0xC718C718,0xC718C718 +GreenMask dd 0x07E007E0,0x07E007E0 +RedBlueMask dd 0xF81FF81F,0xF81FF81F + +FALSE dd 0x00000000,0x00000000 +TRUE dd 0xffffffff,0xffffffff +ONE dd 0x00010001,0x00010001 + + + SECTION .bss ALIGN = 32 +ACPixel resb 8 +Mask1 resb 8 +Mask2 resb 8 + +I56Pixel resb 8 +I23Pixel resb 8 +I5556Pixel resb 8 +I2223Pixel resb 8 +I5666Pixel resb 8 +I2333Pixel resb 8 +Mask26 resb 8 +Mask35 resb 8 +Mask26b resb 8 +Mask35b resb 8 +product1a resb 8 +product1b resb 8 +product2a resb 8 +product2b resb 8 +final1a resb 8 +final1b resb 8 +final2a resb 8 +final2b resb 8 diff --git a/src/filters/admame.cpp b/src/filters/admame.cpp new file mode 100644 index 0000000..87e3201 --- /dev/null +++ b/src/filters/admame.cpp @@ -0,0 +1,1010 @@ +/* + * This file is part of the Advance project. + * + * Copyright (C) 1999-2002 Andrea Mazzoleni + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * In addition, as a special exception, Andrea Mazzoleni + * gives permission to link the code of this program with + * the MAME library (or with modified versions of MAME that use the + * same license as MAME), and distribute linked combinations including + * the two. You must obey the GNU General Public License in all + * respects for all of the code used other than MAME. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* + * Alternatively at the previous license terms, you are allowed to use this + * code in your program with these conditions: + * - the program is not used in commercial activities. + * - the whole source code of the program is released with the binary. + */ + +#include "../System.h" + +#ifdef MMX +extern "C" bool cpu_mmx; +#endif + +static void internal_scale2x_16_def(u16 *dst, const u16* src0, const u16* src1, const u16* src2, unsigned count) { + /* first pixel */ + dst[0] = src1[0]; + if (src1[1] == src0[0] && src2[0] != src0[0]) + dst[1] = src0[0]; + else + dst[1] = src1[0]; + ++src0; + ++src1; + ++src2; + dst += 2; + + /* central pixels */ + count -= 2; + while (count) { + if (src0[0] != src2[0] && src1[-1] != src1[1]) { + dst[0] = src1[-1] == src0[0] ? src0[0] : src1[0]; + dst[1] = src1[1] == src0[0] ? src0[0] : src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + } + + ++src0; + ++src1; + ++src2; + dst += 2; + --count; + } + + /* last pixel */ + if (src1[-1] == src0[0] && src2[0] != src0[0]) + dst[0] = src0[0]; + else + dst[0] = src1[0]; + dst[1] = src1[0]; +} + +static void internal_scale2x_32_def(u32* dst, + const u32* src0, + const u32* src1, + const u32* src2, + unsigned count) +{ + /* first pixel */ + dst[0] = src1[0]; + if (src1[1] == src0[0] && src2[0] != src0[0]) + dst[1] = src0[0]; + else + dst[1] = src1[0]; + ++src0; + ++src1; + ++src2; + dst += 2; + + /* central pixels */ + count -= 2; + while (count) { + if (src0[0] != src2[0] && src1[-1] != src1[1]) { + dst[0] = src1[-1] == src0[0] ? src0[0] : src1[0]; + dst[1] = src1[1] == src0[0] ? src0[0] : src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + } + + ++src0; + ++src1; + ++src2; + dst += 2; + --count; + } + + /* last pixel */ + if (src1[-1] == src0[0] && src2[0] != src0[0]) + dst[0] = src0[0]; + else + dst[0] = src1[0]; + dst[1] = src1[0]; +} + +#ifdef MMX +static void internal_scale2x_16_mmx_single(u16* dst, const u16* src0, const u16* src1, const u16* src2, unsigned count) { + /* always do the first and last run */ + count -= 2*4; + +#ifdef __GNUC__ + __asm__ __volatile__( + /* first run */ + /* set the current, current_pre, current_next registers */ + "movq 0(%1), %%mm0\n" + "movq 0(%1),%%mm7\n" + "movq 8(%1),%%mm1\n" + "psllq $48,%%mm0\n" + "psllq $48,%%mm1\n" + "psrlq $48, %%mm0\n" + "movq %%mm7,%%mm2\n" + "movq %%mm7,%%mm3\n" + "psllq $16,%%mm2\n" + "psrlq $16,%%mm3\n" + "por %%mm2,%%mm0\n" + "por %%mm3,%%mm1\n" + + /* current_upper */ + "movq (%0),%%mm6\n" + + /* compute the upper-left pixel for dst on %%mm2 */ + /* compute the upper-right pixel for dst on %%mm4 */ + "movq %%mm0,%%mm2\n" + "movq %%mm1,%%mm4\n" + "movq %%mm0,%%mm3\n" + "movq %%mm1,%%mm5\n" + "pcmpeqw %%mm6,%%mm2\n" + "pcmpeqw %%mm6,%%mm4\n" + "pcmpeqw (%2),%%mm3\n" + "pcmpeqw (%2),%%mm5\n" + "pandn %%mm2,%%mm3\n" + "pandn %%mm4,%%mm5\n" + "movq %%mm0,%%mm2\n" + "movq %%mm1,%%mm4\n" + "pcmpeqw %%mm1,%%mm2\n" + "pcmpeqw %%mm0,%%mm4\n" + "pandn %%mm3,%%mm2\n" + "pandn %%mm5,%%mm4\n" + "movq %%mm2,%%mm3\n" + "movq %%mm4,%%mm5\n" + "pand %%mm6,%%mm2\n" + "pand %%mm6,%%mm4\n" + "pandn %%mm7,%%mm3\n" + "pandn %%mm7,%%mm5\n" + "por %%mm3,%%mm2\n" + "por %%mm5,%%mm4\n" + + /* set *dst */ + "movq %%mm2,%%mm3\n" + "punpcklwd %%mm4,%%mm2\n" + "punpckhwd %%mm4,%%mm3\n" + "movq %%mm2,(%3)\n" + "movq %%mm3,8(%3)\n" + + /* next */ + "addl $8,%0\n" + "addl $8,%1\n" + "addl $8,%2\n" + "addl $16,%3\n" + + /* central runs */ + "shrl $2,%4\n" + "jz 1f\n" + + "0:\n" + + /* set the current, current_pre, current_next registers */ + "movq -8(%1),%%mm0\n" + "movq (%1),%%mm7\n" + "movq 8(%1),%%mm1\n" + "psrlq $48,%%mm0\n" + "psllq $48,%%mm1\n" + "movq %%mm7,%%mm2\n" + "movq %%mm7,%%mm3\n" + "psllq $16,%%mm2\n" + "psrlq $16,%%mm3\n" + "por %%mm2,%%mm0\n" + "por %%mm3,%%mm1\n" + + /* current_upper */ + "movq (%0),%%mm6\n" + + /* compute the upper-left pixel for dst on %%mm2 */ + /* compute the upper-right pixel for dst on %%mm4 */ + "movq %%mm0,%%mm2\n" + "movq %%mm1,%%mm4\n" + "movq %%mm0,%%mm3\n" + "movq %%mm1,%%mm5\n" + "pcmpeqw %%mm6,%%mm2\n" + "pcmpeqw %%mm6,%%mm4\n" + "pcmpeqw (%2),%%mm3\n" + "pcmpeqw (%2),%%mm5\n" + "pandn %%mm2,%%mm3\n" + "pandn %%mm4,%%mm5\n" + "movq %%mm0,%%mm2\n" + "movq %%mm1,%%mm4\n" + "pcmpeqw %%mm1,%%mm2\n" + "pcmpeqw %%mm0,%%mm4\n" + "pandn %%mm3,%%mm2\n" + "pandn %%mm5,%%mm4\n" + "movq %%mm2,%%mm3\n" + "movq %%mm4,%%mm5\n" + "pand %%mm6,%%mm2\n" + "pand %%mm6,%%mm4\n" + "pandn %%mm7,%%mm3\n" + "pandn %%mm7,%%mm5\n" + "por %%mm3,%%mm2\n" + "por %%mm5,%%mm4\n" + + /* set *dst */ + "movq %%mm2,%%mm3\n" + "punpcklwd %%mm4,%%mm2\n" + "punpckhwd %%mm4,%%mm3\n" + "movq %%mm2,(%3)\n" + "movq %%mm3,8(%3)\n" + + /* next */ + "addl $8,%0\n" + "addl $8,%1\n" + "addl $8,%2\n" + "addl $16,%3\n" + + "decl %4\n" + "jnz 0b\n" + "1:\n" + + /* final run */ + /* set the current, current_pre, current_next registers */ + "movq (%1),%%mm1\n" + "movq (%1),%%mm7\n" + "movq -8(%1),%%mm0\n" + "psrlq $48,%%mm1\n" + "psrlq $48,%%mm0\n" + "psllq $48,%%mm1\n" + "movq %%mm7,%%mm2\n" + "movq %%mm7,%%mm3\n" + "psllq $16,%%mm2\n" + "psrlq $16,%%mm3\n" + "por %%mm2,%%mm0\n" + "por %%mm3,%%mm1\n" + + /* current_upper */ + "movq (%0),%%mm6\n" + + /* compute the upper-left pixel for dst on %%mm2 */ + /* compute the upper-right pixel for dst on %%mm4 */ + "movq %%mm0,%%mm2\n" + "movq %%mm1,%%mm4\n" + "movq %%mm0,%%mm3\n" + "movq %%mm1,%%mm5\n" + "pcmpeqw %%mm6,%%mm2\n" + "pcmpeqw %%mm6,%%mm4\n" + "pcmpeqw (%2),%%mm3\n" + "pcmpeqw (%2),%%mm5\n" + "pandn %%mm2,%%mm3\n" + "pandn %%mm4,%%mm5\n" + "movq %%mm0,%%mm2\n" + "movq %%mm1,%%mm4\n" + "pcmpeqw %%mm1,%%mm2\n" + "pcmpeqw %%mm0,%%mm4\n" + "pandn %%mm3,%%mm2\n" + "pandn %%mm5,%%mm4\n" + "movq %%mm2,%%mm3\n" + "movq %%mm4,%%mm5\n" + "pand %%mm6,%%mm2\n" + "pand %%mm6,%%mm4\n" + "pandn %%mm7,%%mm3\n" + "pandn %%mm7,%%mm5\n" + "por %%mm3,%%mm2\n" + "por %%mm5,%%mm4\n" + + /* set *dst */ + "movq %%mm2,%%mm3\n" + "punpcklwd %%mm4,%%mm2\n" + "punpckhwd %%mm4,%%mm3\n" + "movq %%mm2,(%3)\n" + "movq %%mm3,8(%3)\n" + "emms\n" + + : "+r" (src0), "+r" (src1), "+r" (src2), "+r" (dst), "+r" (count) + : + : "cc" + ); +#else + __asm { + mov eax, src0; + mov ebx, src1; + mov ecx, src2; + mov edx, dst; + mov esi, count; + + /* first run */ + /* set the current, current_pre, current_next registers */ + movq mm0, qword ptr [ebx]; + movq mm7, qword ptr [ebx]; + movq mm1, qword ptr [ebx + 8]; + psllq mm0, 48; + psllq mm1, 48; + psrlq mm0, 48; + movq mm2, mm7; + movq mm3, mm7; + psllq mm2, 16; + psrlq mm3, 16; + por mm0, mm2; + por mm1, mm3; + + /* current_upper */ + movq mm6, qword ptr [eax]; + + /* compute the upper-left pixel for dst on %%mm2 */ + /* compute the upper-right pixel for dst on %%mm4 */ + movq mm2, mm0; + movq mm4, mm1; + movq mm3, mm0; + movq mm5, mm1; + pcmpeqw mm2, mm6; + pcmpeqw mm4, mm6; + pcmpeqw mm3, qword ptr [ecx]; + pcmpeqw mm5, qword ptr [ecx]; + pandn mm3,mm2; + pandn mm5,mm4; + movq mm2,mm0; + movq mm4,mm1; + pcmpeqw mm2,mm1; + pcmpeqw mm4,mm0; + pandn mm2,mm3; + pandn mm4,mm5; + movq mm3,mm2; + movq mm5,mm4; + pand mm2,mm6; + pand mm4,mm6; + pandn mm3,mm7; + pandn mm5,mm7; + por mm2,mm3; + por mm4,mm5; + + /* set *dst0 */ + movq mm3,mm2; + punpcklwd mm2,mm4; + punpckhwd mm3,mm4; + movq qword ptr [edx], mm2; + movq qword ptr [edx + 8], mm3; + + /* next */ + add eax, 8; + add ebx, 8; + add ecx, 8; + add edx, 16; + + /* central runs */ + shr esi, 2; + jz label1; + align 4; + label0: + + /* set the current, current_pre, current_next registers */ + movq mm0, qword ptr [ebx-8]; + movq mm7, qword ptr [ebx]; + movq mm1, qword ptr [ebx+8]; + psrlq mm0,48; + psllq mm1,48; + movq mm2,mm7; + movq mm3,mm7; + psllq mm2,16; + psrlq mm3,16; + por mm0,mm2; + por mm1,mm3; + + /* current_upper */ + movq mm6, qword ptr [eax]; + + /* compute the upper-left pixel for dst on %%mm2 */ + /* compute the upper-right pixel for dst on %%mm4 */ + movq mm2,mm0; + movq mm4,mm1; + movq mm3,mm0; + movq mm5,mm1; + pcmpeqw mm2,mm6; + pcmpeqw mm4,mm6; + pcmpeqw mm3, qword ptr [ecx]; + pcmpeqw mm5, qword ptr [ecx]; + pandn mm3,mm2; + pandn mm5,mm4; + movq mm2,mm0; + movq mm4,mm1; + pcmpeqw mm2,mm1; + pcmpeqw mm4,mm0; + pandn mm2,mm3; + pandn mm4,mm5; + movq mm3,mm2; + movq mm5,mm4; + pand mm2,mm6; + pand mm4,mm6; + pandn mm3,mm7; + pandn mm5,mm7; + por mm2,mm3; + por mm4,mm5; + + /* set *dst */ + movq mm3,mm2; + punpcklwd mm2,mm4; + punpckhwd mm3,mm4; + movq qword ptr [edx], mm2; + movq qword ptr [edx+8], mm3; + + /* next */ + add eax,8; + add ebx,8; + add ecx,8; + add edx,16; + + dec esi; + jnz label0; + label1: + + /* final run */ + /* set the current, current_pre, current_next registers */ + movq mm1, qword ptr [ebx]; + movq mm7, qword ptr [ebx]; + movq mm0, qword ptr [ebx-8]; + psrlq mm1,48; + psrlq mm0,48; + psllq mm1,48; + movq mm2,mm7; + movq mm3,mm7; + psllq mm2,16; + psrlq mm3,16; + por mm0,mm2; + por mm1,mm3; + + /* current_upper */ + movq mm6, qword ptr [eax]; + + /* compute the upper-left pixel for dst on %%mm2 */ + /* compute the upper-right pixel for dst on %%mm4 */ + movq mm2,mm0; + movq mm4,mm1; + movq mm3,mm0; + movq mm5,mm1; + pcmpeqw mm2,mm6; + pcmpeqw mm4,mm6; + pcmpeqw mm3, qword ptr [ecx]; + pcmpeqw mm5, qword ptr [ecx]; + pandn mm3,mm2; + pandn mm5,mm4; + movq mm2,mm0; + movq mm4,mm1; + pcmpeqw mm2,mm1; + pcmpeqw mm4,mm0; + pandn mm2,mm3; + pandn mm4,mm5; + movq mm3,mm2; + movq mm5,mm4; + pand mm2,mm6; + pand mm4,mm6; + pandn mm3,mm7; + pandn mm5,mm7; + por mm2,mm3; + por mm4,mm5; + + /* set *dst */ + movq mm3,mm2; + punpcklwd mm2,mm4; + punpckhwd mm3,mm4; + movq qword ptr [edx], mm2; + movq qword ptr [edx+8], mm3; + + mov src0, eax; + mov src1, ebx; + mov src2, ecx; + mov dst, edx; + mov count, esi; + + emms; + } +#endif +} + +static void internal_scale2x_32_mmx_single(u32* dst, const u32* src0, const u32* src1, const u32* src2, unsigned count) { + /* always do the first and last run */ + count -= 2*2; + +#ifdef __GNUC__ + __asm__ __volatile__( + /* first run */ + /* set the current, current_pre, current_next registers */ + "movq 0(%1),%%mm0\n" + "movq 0(%1),%%mm7\n" + "movq 8(%1),%%mm1\n" + "psllq $32,%%mm0\n" + "psllq $32,%%mm1\n" + "psrlq $32,%%mm0\n" + "movq %%mm7,%%mm2\n" + "movq %%mm7,%%mm3\n" + "psllq $32,%%mm2\n" + "psrlq $32,%%mm3\n" + "por %%mm2,%%mm0\n" + "por %%mm3,%%mm1\n" + + /* current_upper */ + "movq (%0),%%mm6\n" + + /* compute the upper-left pixel for dst on %%mm2 */ + /* compute the upper-right pixel for dst on %%mm4 */ + "movq %%mm0,%%mm2\n" + "movq %%mm1,%%mm4\n" + "movq %%mm0,%%mm3\n" + "movq %%mm1,%%mm5\n" + "pcmpeqd %%mm6,%%mm2\n" + "pcmpeqd %%mm6,%%mm4\n" + "pcmpeqd (%2),%%mm3\n" + "pcmpeqd (%2),%%mm5\n" + "pandn %%mm2,%%mm3\n" + "pandn %%mm4,%%mm5\n" + "movq %%mm0,%%mm2\n" + "movq %%mm1,%%mm4\n" + "pcmpeqd %%mm1,%%mm2\n" + "pcmpeqd %%mm0,%%mm4\n" + "pandn %%mm3,%%mm2\n" + "pandn %%mm5,%%mm4\n" + "movq %%mm2,%%mm3\n" + "movq %%mm4,%%mm5\n" + "pand %%mm6,%%mm2\n" + "pand %%mm6,%%mm4\n" + "pandn %%mm7,%%mm3\n" + "pandn %%mm7,%%mm5\n" + "por %%mm3,%%mm2\n" + "por %%mm5,%%mm4\n" + + /* set *dst */ + "movq %%mm2,%%mm3\n" + "punpckldq %%mm4,%%mm2\n" + "punpckhdq %%mm4,%%mm3\n" + "movq %%mm2,(%3)\n" + "movq %%mm3, 8(%3)\n" + + /* next */ + "addl $8,%0\n" + "addl $8,%1\n" + "addl $8,%2\n" + "addl $16,%3\n" + + /* central runs */ + "shrl $1,%4\n" + "jz 1f\n" + + "0:\n" + + /* set the current, current_pre, current_next registers */ + "movq -8(%1),%%mm0\n" + "movq (%1),%%mm7\n" + "movq 8(%1),%%mm1\n" + "psrlq $32,%%mm0\n" + "psllq $32,%%mm1\n" + "movq %%mm7,%%mm2\n" + "movq %%mm7,%%mm3\n" + "psllq $32,%%mm2\n" + "psrlq $32,%%mm3\n" + "por %%mm2,%%mm0\n" + "por %%mm3,%%mm1\n" + + /* current_upper */ + "movq (%0),%%mm6\n" + + /* compute the upper-left pixel for dst on %%mm2 */ + /* compute the upper-right pixel for dst on %%mm4 */ + "movq %%mm0,%%mm2\n" + "movq %%mm1,%%mm4\n" + "movq %%mm0,%%mm3\n" + "movq %%mm1,%%mm5\n" + "pcmpeqd %%mm6,%%mm2\n" + "pcmpeqd %%mm6,%%mm4\n" + "pcmpeqd (%2),%%mm3\n" + "pcmpeqd (%2),%%mm5\n" + "pandn %%mm2,%%mm3\n" + "pandn %%mm4,%%mm5\n" + "movq %%mm0,%%mm2\n" + "movq %%mm1,%%mm4\n" + "pcmpeqd %%mm1,%%mm2\n" + "pcmpeqd %%mm0,%%mm4\n" + "pandn %%mm3,%%mm2\n" + "pandn %%mm5,%%mm4\n" + "movq %%mm2,%%mm3\n" + "movq %%mm4,%%mm5\n" + "pand %%mm6,%%mm2\n" + "pand %%mm6,%%mm4\n" + "pandn %%mm7,%%mm3\n" + "pandn %%mm7,%%mm5\n" + "por %%mm3,%%mm2\n" + "por %%mm5,%%mm4\n" + + /* set *dst */ + "movq %%mm2,%%mm3\n" + "punpckldq %%mm4,%%mm2\n" + "punpckhdq %%mm4,%%mm3\n" + "movq %%mm2,(%3)\n" + "movq %%mm3,8(%3)\n" + + /* next */ + "addl $8,%0\n" + "addl $8,%1\n" + "addl $8,%2\n" + "addl $16,%3\n" + + "decl %4\n" + "jnz 0b\n" + "1:\n" + + /* final run */ + /* set the current, current_pre, current_next registers */ + "movq (%1),%%mm1\n" + "movq (%1),%%mm7\n" + "movq -8(%1), %%mm0\n" + "psrlq $32,%%mm1\n" + "psrlq $32,%%mm0\n" + "psllq $32,%%mm1\n" + "movq %%mm7,%%mm2\n" + "movq %%mm7,%%mm3\n" + "psllq $32,%%mm2\n" + "psrlq $32,%%mm3\n" + "por %%mm2,%%mm0\n" + "por %%mm3,%%mm1\n" + + /* current_upper */ + "movq (%0),%%mm6\n" + + /* compute the upper-left pixel for dst on %%mm2 */ + /* compute the upper-right pixel for dst on %%mm4 */ + "movq %%mm0,%%mm2\n" + "movq %%mm1,%%mm4\n" + "movq %%mm0,%%mm3\n" + "movq %%mm1,%%mm5\n" + "pcmpeqd %%mm6,%%mm2\n" + "pcmpeqd %%mm6,%%mm4\n" + "pcmpeqd (%2),%%mm3\n" + "pcmpeqd (%2),%%mm5\n" + "pandn %%mm2,%%mm3\n" + "pandn %%mm4,%%mm5\n" + "movq %%mm0,%%mm2\n" + "movq %%mm1,%%mm4\n" + "pcmpeqd %%mm1,%%mm2\n" + "pcmpeqd %%mm0,%%mm4\n" + "pandn %%mm3,%%mm2\n" + "pandn %%mm5,%%mm4\n" + "movq %%mm2,%%mm3\n" + "movq %%mm4,%%mm5\n" + "pand %%mm6,%%mm2\n" + "pand %%mm6,%%mm4\n" + "pandn %%mm7,%%mm3\n" + "pandn %%mm7,%%mm5\n" + "por %%mm3,%%mm2\n" + "por %%mm5,%%mm4\n" + + /* set *dst */ + "movq %%mm2,%%mm3\n" + "punpckldq %%mm4,%%mm2\n" + "punpckhdq %%mm4,%%mm3\n" + "movq %%mm2,(%3)\n" + "movq %%mm3,8(%3)\n" + "emms\n" + + : "+r" (src0), "+r" (src1), "+r" (src2), "+r" (dst), "+r" (count) + : + : "cc" + ); +#else + __asm { + mov eax, src0; + mov ebx, src1; + mov ecx, src2; + mov edx, dst; + mov esi, count; + + /* first run */ + /* set the current, current_pre, current_next registers */ + movq mm0,qword ptr [ebx]; + movq mm7,qword ptr [ebx]; + movq mm1,qword ptr [ebx + 8]; + psllq mm0,32; + psllq mm1,32; + psrlq mm0,32; + movq mm2,mm7; + movq mm3,mm7; + psllq mm2,32; + psrlq mm3,32; + por mm0,mm2; + por mm1,mm3; + + /* current_upper */ + movq mm6,qword ptr [eax]; + + /* compute the upper-left pixel for dst on %%mm2 */ + /* compute the upper-right pixel for dst on %%mm4 */ + movq mm2,mm0; + movq mm4,mm1; + movq mm3,mm0; + movq mm5,mm1; + pcmpeqd mm2,mm6; + pcmpeqd mm4,mm6; + pcmpeqd mm3,qword ptr [ecx]; + pcmpeqd mm5,qword ptr [ecx]; + pandn mm3,mm2; + pandn mm5,mm4; + movq mm2,mm0; + movq mm4,mm1; + pcmpeqd mm2,mm1; + pcmpeqd mm4,mm0; + pandn mm2,mm3; + pandn mm4,mm5; + movq mm3,mm2; + movq mm5,mm4; + pand mm2,mm6; + pand mm4,mm6; + pandn mm3,mm7; + pandn mm5,mm7; + por mm2,mm3; + por mm4,mm5; + + /* set *dst */ + movq mm3,mm2; + punpckldq mm2,mm4; + punpckhdq mm3,mm4; + movq qword ptr [edx],mm2; + movq qword ptr [edx+8],mm3; + + /* next */ + add eax,8; + add ebx,8; + add ecx,8; + add edx,16; + + /* central runs */ + shr esi,1; + jz label1; +label0: + + /* set the current, current_pre, current_next registers */ + movq mm0,qword ptr [ebx-8]; + movq mm7,qword ptr [ebx]; + movq mm1,qword ptr [ebx+8]; + psrlq mm0,32; + psllq mm1,32; + movq mm2,mm7; + movq mm3,mm7; + psllq mm2,32; + psrlq mm3,32; + por mm0,mm2; + por mm1,mm3; + + /* current_upper */ + movq mm6,qword ptr[eax]; + + /* compute the upper-left pixel for dst on %%mm2 */ + /* compute the upper-right pixel for dst on %%mm4 */ + movq mm2,mm0; + movq mm4,mm1; + movq mm3,mm0; + movq mm5,mm1; + pcmpeqd mm2,mm6; + pcmpeqd mm4,mm6; + pcmpeqd mm3,qword ptr[ecx]; + pcmpeqd mm5,qword ptr[ecx]; + pandn mm3,mm2; + pandn mm5,mm4; + movq mm2,mm0; + movq mm4,mm1; + pcmpeqd mm2,mm1; + pcmpeqd mm4,mm0; + pandn mm2,mm3; + pandn mm4,mm5; + movq mm3,mm2; + movq mm5,mm4; + pand mm2,mm6; + pand mm4,mm6; + pandn mm3,mm7; + pandn mm5,mm7; + por mm2,mm3; + por mm4,mm5; + + /* set *dst */ + movq mm3,mm2; + punpckldq mm2,mm4; + punpckhdq mm3,mm4; + movq qword ptr [edx],mm2; + movq qword ptr [edx+8],mm3; + + /* next */ + add eax,8; + add ebx,8; + add ecx,8; + add edx,16; + + dec esi; + jnz label0; +label1: + + /* final run */ + /* set the current, current_pre, current_next registers */ + movq mm1,qword ptr [ebx]; + movq mm7,qword ptr [ebx]; + movq mm0,qword ptr [ebx-8]; + psrlq mm1,32; + psrlq mm0,32; + psllq mm1,32; + movq mm2,mm7; + movq mm3,mm7; + psllq mm2,32; + psrlq mm3,32; + por mm0,mm2; + por mm1,mm3; + + /* current_upper */ + movq mm6,qword ptr [eax]; + + /* compute the upper-left pixel for dst on %%mm2 */ + /* compute the upper-right pixel for dst on %%mm4 */ + movq mm2,mm0; + movq mm4,mm1; + movq mm3,mm0; + movq mm5,mm1; + pcmpeqd mm2,mm6; + pcmpeqd mm4,mm6; + pcmpeqd mm3,qword ptr [ecx]; + pcmpeqd mm5,qword ptr [ecx]; + pandn mm3,mm2; + pandn mm5,mm4; + movq mm2,mm0; + movq mm4,mm1; + pcmpeqd mm2,mm1; + pcmpeqd mm4,mm0; + pandn mm2,mm3; + pandn mm4,mm5; + movq mm3,mm2; + movq mm5,mm4; + pand mm2,mm6; + pand mm4,mm6; + pandn mm3,mm7; + pandn mm5,mm7; + por mm2,mm3; + por mm4,mm5; + + /* set *dst */ + movq mm3,mm2; + punpckldq mm2,mm4; + punpckhdq mm3,mm4; + movq qword ptr [edx],mm2; + movq qword ptr [edx+8],mm3; + + mov src0, eax; + mov src1, ebx; + mov src2, ecx; + mov dst, edx; + mov count, esi; + + emms; + } +#endif +} + +static void internal_scale2x_16_mmx(u16* dst0, u16* dst1, const u16* src0, const u16* src1, const u16* src2, unsigned count) { + // assert( count >= 2*4 ); + internal_scale2x_16_mmx_single(dst0, src0, src1, src2, count); + internal_scale2x_16_mmx_single(dst1, src2, src1, src0, count); +} + +static void internal_scale2x_32_mmx(u32* dst0, u32* dst1, const u32* src0, const u32* src1, const u32* src2, unsigned count) { + // assert( count >= 2*2 ); + internal_scale2x_32_mmx_single(dst0, src0, src1, src2, count); + internal_scale2x_32_mmx_single(dst1, src2, src1, src0, count); +} +#endif + +void AdMame2x(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u16 *dst0 = (u16 *)dstPtr; + u16 *dst1 = dst0 + (dstPitch >> 1); + + u16 *src0 = (u16 *)srcPtr; + u16 *src1 = src0 + (srcPitch >> 1); + u16 *src2 = src1 + (srcPitch >> 1); +#ifdef MMX + if(cpu_mmx) { + internal_scale2x_16_mmx(dst0, dst1, src0, src0, src1, width); + + int count = height; + + count -= 2; + while(count) { + dst0 += dstPitch; + dst1 += dstPitch; + internal_scale2x_16_mmx(dst0, dst1, src0, src1, src2, width); + src0 = src1; + src1 = src2; + src2 += srcPitch >> 1; + --count; + } + dst0 += dstPitch; + dst1 += dstPitch; + internal_scale2x_16_mmx(dst0, dst1, src0, src1, src1, width); + } else { +#endif + internal_scale2x_16_def(dst0, src0, src0, src1, width); + internal_scale2x_16_def(dst1, src1, src0, src0, width); + + int count = height; + + count -= 2; + while(count) { + dst0 += dstPitch; + dst1 += dstPitch; + internal_scale2x_16_def(dst0, src0, src1, src2, width); + internal_scale2x_16_def(dst1, src2, src1, src0, width); + src0 = src1; + src1 = src2; + src2 += srcPitch >> 1; + --count; + } + dst0 += dstPitch; + dst1 += dstPitch; + internal_scale2x_16_def(dst0, src0, src1, src1, width); + internal_scale2x_16_def(dst1, src1, src1, src0, width); +#ifdef MMX + } +#endif +} + +void AdMame2x32(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u32 *dst0 = (u32 *)dstPtr; + u32 *dst1 = dst0 + (dstPitch >> 2); + + u32 *src0 = (u32 *)srcPtr; + u32 *src1 = src0 + (srcPitch >> 2); + u32 *src2 = src1 + (srcPitch >> 2); +#ifdef MMX + if(cpu_mmx) { + internal_scale2x_32_mmx(dst0, dst1, src0, src0, src1, width); + + int count = height; + + count -= 2; + while(count) { + dst0 += dstPitch >> 1; + dst1 += dstPitch >> 1; + internal_scale2x_32_mmx(dst0, dst1, src0, src1, src2, width); + src0 = src1; + src1 = src2; + src2 += srcPitch >> 2; + --count; + } + dst0 += dstPitch >> 1; + dst1 += dstPitch >> 1; + internal_scale2x_32_mmx(dst0, dst1, src0, src1, src1, width); + } else { +#endif + internal_scale2x_32_def(dst0, src0, src0, src1, width); + internal_scale2x_32_def(dst1, src1, src0, src0, width); + + int count = height; + + count -= 2; + while(count) { + dst0 += dstPitch >> 1; + dst1 += dstPitch >> 1; + internal_scale2x_32_def(dst0, src0, src1, src2, width); + internal_scale2x_32_def(dst1, src2, src1, src0, width); + src0 = src1; + src1 = src2; + src2 += srcPitch >> 2; + --count; + } + dst0 += dstPitch >> 1; + dst1 += dstPitch >> 1; + internal_scale2x_32_def(dst0, src0, src1, src1, width); + internal_scale2x_32_def(dst1, src1, src1, src0, width); +#ifdef MMX + } +#endif +} diff --git a/src/filters/bilinear.cpp b/src/filters/bilinear.cpp new file mode 100644 index 0000000..e2f9d1b --- /dev/null +++ b/src/filters/bilinear.cpp @@ -0,0 +1,414 @@ +/** Code adapted from Exult source code by Forgotten + ** Scale.cc - Trying to scale with bilinear interpolation. + ** + ** Written: 6/14/00 - JSF + **/ + +#include "../System.h" + +#define RGB(r,g,b) ((r)>>3) << systemRedShift |\ + ((g) >> 3) << systemGreenShift |\ + ((b) >> 3) << systemBlueShift\ + +static void fill_rgb_row_16(u16 *from, int src_width, u8 *row, int width) +{ + u8 *copy_start = row + src_width*3; + u8 *all_stop = row + width*3; + while (row < copy_start) { + u16 color = *from++; + *row++ = ((color >> systemRedShift) & 0x1f) << 3; + *row++ = ((color >> systemGreenShift) & 0x1f) << 3; + *row++ = ((color >> systemBlueShift) & 0x1f) << 3; + } + // any remaining elements to be written to 'row' are a replica of the + // preceding pixel + u8 *p = row-3; + while (row < all_stop) { + // we're guaranteed three elements per pixel; could unroll the loop + // further, especially with a Duff's Device, but the gains would be + // probably limited (judging by profiler output) + *row++ = *p++; + *row++ = *p++; + *row++ = *p++; + } +} + +static void fill_rgb_row_32(u32 *from, int src_width, u8 *row, int width) +{ + u8 *copy_start = row + src_width*3; + u8 *all_stop = row + width*3; + while (row < copy_start) { + u32 color = *from++; + *row++ = ((color >> systemRedShift) & 0x1f) << 3; + *row++ = ((color >> systemGreenShift) & 0x1f) << 3; + *row++ = ((color >> systemBlueShift) & 0x1f) << 3; + } + // any remaining elements to be written to 'row' are a replica of the + // preceding pixel + u8 *p = row-3; + while (row < all_stop) { + // we're guaranteed three elements per pixel; could unroll the loop + // further, especially with a Duff's Device, but the gains would be + // probably limited (judging by profiler output) + *row++ = *p++; + *row++ = *p++; + *row++ = *p++; + } +} + +void Bilinear(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u8 row_cur[3*322]; + u8 row_next[3*322]; + u8 *rgb_row_cur = row_cur; + u8 *rgb_row_next = row_next; + + u16 *to = (u16 *)dstPtr; + u16 *to_odd = (u16 *)(dstPtr + dstPitch); + + int from_width = width; + u16 *from = (u16 *)srcPtr; + fill_rgb_row_16(from, from_width, rgb_row_cur, width+1); + + for(int y = 0; y < height; y++) { + u16 *from_orig = from; + u16 *to_orig = to; + + if (y+1 < height) + fill_rgb_row_16(from+width+2, from_width, rgb_row_next, + width+1); + else + fill_rgb_row_16(from, from_width, rgb_row_next, width+1); + + // every pixel in the src region, is extended to 4 pixels in the + // destination, arranged in a square 'quad'; if the current src + // pixel is 'a', then in what follows 'b' is the src pixel to the + // right, 'c' is the src pixel below, and 'd' is the src pixel to + // the right and down + u8 *cur_row = rgb_row_cur; + u8 *next_row = rgb_row_next; + u8 *ar = cur_row++; + u8 *ag = cur_row++; + u8 *ab = cur_row++; + u8 *cr = next_row++; + u8 *cg = next_row++; + u8 *cb = next_row++; + for(int x=0; x < width; x++) { + u8 *br = cur_row++; + u8 *bg = cur_row++; + u8 *bb = cur_row++; + u8 *dr = next_row++; + u8 *dg = next_row++; + u8 *db = next_row++; + + // upper left pixel in quad: just copy it in + *to++ = RGB(*ar, *ag, *ab); + + // upper right + *to++ = RGB((*ar+*br)>>1, (*ag+*bg)>>1, (*ab+*bb)>>1); + + // lower left + *to_odd++ = RGB((*ar+*cr)>>1, (*ag+*cg)>>1, (*ab+*cb)>>1); + + // lower right + *to_odd++ = RGB((*ar+*br+*cr+*dr)>>2, + (*ag+*bg+*cg+*dg)>>2, + (*ab+*bb+*cb+*db)>>2); + + // 'b' becomes 'a', 'd' becomes 'c' + ar = br; + ag = bg; + ab = bb; + cr = dr; + cg = dg; + cb = db; + } + + // the "next" rgb row becomes the current; the old current rgb row is + // recycled and serves as the new "next" row + u8 *temp; + temp = rgb_row_cur; + rgb_row_cur = rgb_row_next; + rgb_row_next = temp; + + // update the pointers for start of next pair of lines + from = (u16 *)((u8 *)from_orig + srcPitch); + to = (u16 *)((u8 *)to_orig + (dstPitch << 1)); + to_odd = (u16 *)((u8 *)to + dstPitch); + } +} + +void BilinearPlus(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u8 row_cur[3*322]; + u8 row_next[3*322]; + u8 *rgb_row_cur = row_cur; + u8 *rgb_row_next = row_next; + + u16 *to = (u16 *)dstPtr; + u16 *to_odd = (u16 *)(dstPtr + dstPitch); + + int from_width = width; + u16 *from = (u16 *)srcPtr; + fill_rgb_row_16(from, from_width, rgb_row_cur, width+1); + + for(int y = 0; y < height; y++) { + u16 *from_orig = from; + u16 *to_orig = to; + + if (y+1 < height) + fill_rgb_row_16(from+width+2, from_width, rgb_row_next, + width+1); + else + fill_rgb_row_16(from, from_width, rgb_row_next, width+1); + + // every pixel in the src region, is extended to 4 pixels in the + // destination, arranged in a square 'quad'; if the current src + // pixel is 'a', then in what follows 'b' is the src pixel to the + // right, 'c' is the src pixel below, and 'd' is the src pixel to + // the right and down + u8 *cur_row = rgb_row_cur; + u8 *next_row = rgb_row_next; + u8 *ar = cur_row++; + u8 *ag = cur_row++; + u8 *ab = cur_row++; + u8 *cr = next_row++; + u8 *cg = next_row++; + u8 *cb = next_row++; + for(int x=0; x < width; x++) { + u8 *br = cur_row++; + u8 *bg = cur_row++; + u8 *bb = cur_row++; + u8 *dr = next_row++; + u8 *dg = next_row++; + u8 *db = next_row++; + + // upper left pixel in quad: just copy it in + //*to++ = manip.rgb(*ar, *ag, *ab); +#ifdef USE_ORIGINAL_BILINEAR_PLUS + *to++ = RGB( + (((*ar)<<2) +((*ar)) + (*cr+*br+*br) )>> 3, + (((*ag)<<2) +((*ag)) + (*cg+*bg+*bg) )>> 3, + (((*ab)<<2) +((*ab)) + (*cb+*bb+*bb) )>> 3); +#else + *to++ = RGB( + (((*ar)<<3) +((*ar)<<1) + (*cr+*br+*br+*cr) )>> 4, + (((*ag)<<3) +((*ag)<<1) + (*cg+*bg+*bg+*cg) )>> 4, + (((*ab)<<3) +((*ab)<<1) + (*cb+*bb+*bb+*cb) )>> 4); +#endif + + // upper right + *to++ = RGB((*ar+*br)>>1, (*ag+*bg)>>1, (*ab+*bb)>>1); + + // lower left + *to_odd++ = RGB((*ar+*cr)>>1, (*ag+*cg)>>1, (*ab+*cb)>>1); + + // lower right + *to_odd++ = RGB((*ar+*br+*cr+*dr)>>2, + (*ag+*bg+*cg+*dg)>>2, + (*ab+*bb+*cb+*db)>>2); + + // 'b' becomes 'a', 'd' becomes 'c' + ar = br; + ag = bg; + ab = bb; + cr = dr; + cg = dg; + cb = db; + } + + // the "next" rgb row becomes the current; the old current rgb row is + // recycled and serves as the new "next" row + u8 *temp; + temp = rgb_row_cur; + rgb_row_cur = rgb_row_next; + rgb_row_next = temp; + + // update the pointers for start of next pair of lines + from = (u16 *)((u8 *)from_orig + srcPitch); + to = (u16 *)((u8 *)to_orig + (dstPitch << 1)); + to_odd = (u16 *)((u8 *)to + dstPitch); + } +} + +void Bilinear32(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u8 row_cur[3*322]; + u8 row_next[3*322]; + u8 *rgb_row_cur = row_cur; + u8 *rgb_row_next = row_next; + + u32 *to = (u32 *)dstPtr; + u32 *to_odd = (u32 *)(dstPtr + dstPitch); + + int from_width = width; + + u32 *from = (u32 *)srcPtr; + fill_rgb_row_32(from, from_width, rgb_row_cur, width+1); + + for(int y = 0; y < height; y++) { + u32 *from_orig = from; + u32 *to_orig = to; + + if (y+1 < height) + fill_rgb_row_32(from+width+1, from_width, rgb_row_next, + width+1); + else + fill_rgb_row_32(from, from_width, rgb_row_next, width+1); + + // every pixel in the src region, is extended to 4 pixels in the + // destination, arranged in a square 'quad'; if the current src + // pixel is 'a', then in what follows 'b' is the src pixel to the + // right, 'c' is the src pixel below, and 'd' is the src pixel to + // the right and down + u8 *cur_row = rgb_row_cur; + u8 *next_row = rgb_row_next; + u8 *ar = cur_row++; + u8 *ag = cur_row++; + u8 *ab = cur_row++; + u8 *cr = next_row++; + u8 *cg = next_row++; + u8 *cb = next_row++; + for(int x=0; x < width; x++) { + u8 *br = cur_row++; + u8 *bg = cur_row++; + u8 *bb = cur_row++; + u8 *dr = next_row++; + u8 *dg = next_row++; + u8 *db = next_row++; + + // upper left pixel in quad: just copy it in + *to++ = RGB(*ar, *ag, *ab); + + // upper right + *to++ = RGB((*ar+*br)>>1, (*ag+*bg)>>1, (*ab+*bb)>>1); + + // lower left + *to_odd++ = RGB((*ar+*cr)>>1, (*ag+*cg)>>1, (*ab+*cb)>>1); + + // lower right + *to_odd++ = RGB((*ar+*br+*cr+*dr)>>2, + (*ag+*bg+*cg+*dg)>>2, + (*ab+*bb+*cb+*db)>>2); + + // 'b' becomes 'a', 'd' becomes 'c' + ar = br; + ag = bg; + ab = bb; + cr = dr; + cg = dg; + cb = db; + } + + // the "next" rgb row becomes the current; the old current rgb row is + // recycled and serves as the new "next" row + u8 *temp; + temp = rgb_row_cur; + rgb_row_cur = rgb_row_next; + rgb_row_next = temp; + + // update the pointers for start of next pair of lines + from = (u32 *)((u8 *)from_orig + srcPitch); + to = (u32 *)((u8 *)to_orig + (dstPitch << 1)); + to_odd = (u32 *)((u8 *)to + dstPitch); + } +} + +void BilinearPlus32(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u8 row_cur[3*322]; + u8 row_next[3*322]; + u8 *rgb_row_cur = row_cur; + u8 *rgb_row_next = row_next; + + u32 *to = (u32 *)dstPtr; + u32 *to_odd = (u32 *)(dstPtr + dstPitch); + + int from_width = width; + + u32 *from = (u32 *)srcPtr; + fill_rgb_row_32(from, from_width, rgb_row_cur, width+1); + + for(int y = 0; y < height; y++) { + u32 *from_orig = from; + u32 *to_orig = to; + + if (y+1 < height) + fill_rgb_row_32(from+width+1, from_width, rgb_row_next, + width+1); + else + fill_rgb_row_32(from, from_width, rgb_row_next, width+1); + + // every pixel in the src region, is extended to 4 pixels in the + // destination, arranged in a square 'quad'; if the current src + // pixel is 'a', then in what follows 'b' is the src pixel to the + // right, 'c' is the src pixel below, and 'd' is the src pixel to + // the right and down + u8 *cur_row = rgb_row_cur; + u8 *next_row = rgb_row_next; + u8 *ar = cur_row++; + u8 *ag = cur_row++; + u8 *ab = cur_row++; + u8 *cr = next_row++; + u8 *cg = next_row++; + u8 *cb = next_row++; + for(int x=0; x < width; x++) { + u8 *br = cur_row++; + u8 *bg = cur_row++; + u8 *bb = cur_row++; + u8 *dr = next_row++; + u8 *dg = next_row++; + u8 *db = next_row++; + + // upper left pixel in quad: just copy it in + //*to++ = manip.rgb(*ar, *ag, *ab); +#ifdef USE_ORIGINAL_BILINEAR_PLUS + *to++ = RGB( + (((*ar)<<2) +((*ar)) + (*cr+*br+*br) )>> 3, + (((*ag)<<2) +((*ag)) + (*cg+*bg+*bg) )>> 3, + (((*ab)<<2) +((*ab)) + (*cb+*bb+*bb) )>> 3); +#else + *to++ = RGB( + (((*ar)<<3) +((*ar)<<1) + (*cr+*br+*br+*cr) )>> 4, + (((*ag)<<3) +((*ag)<<1) + (*cg+*bg+*bg+*cg) )>> 4, + (((*ab)<<3) +((*ab)<<1) + (*cb+*bb+*bb+*cb) )>> 4); +#endif + + // upper right + *to++ = RGB((*ar+*br)>>1, (*ag+*bg)>>1, (*ab+*bb)>>1); + + // lower left + *to_odd++ = RGB((*ar+*cr)>>1, (*ag+*cg)>>1, (*ab+*cb)>>1); + + // lower right + *to_odd++ = RGB((*ar+*br+*cr+*dr)>>2, + (*ag+*bg+*cg+*dg)>>2, + (*ab+*bb+*cb+*db)>>2); + + // 'b' becomes 'a', 'd' becomes 'c' + ar = br; + ag = bg; + ab = bb; + cr = dr; + cg = dg; + cb = db; + } + + // the "next" rgb row becomes the current; the old current rgb row is + // recycled and serves as the new "next" row + u8 *temp; + temp = rgb_row_cur; + rgb_row_cur = rgb_row_next; + rgb_row_next = temp; + + // update the pointers for start of next pair of lines + from = (u32 *)((u8 *)from_orig + srcPitch); + to = (u32 *)((u8 *)to_orig + (dstPitch << 1)); + to_odd = (u32 *)((u8 *)to + dstPitch); + } +} + diff --git a/src/filters/hq/asm/hq3x32.cpp b/src/filters/hq/asm/hq3x32.cpp new file mode 100644 index 0000000..b6a07e7 --- /dev/null +++ b/src/filters/hq/asm/hq3x32.cpp @@ -0,0 +1,115 @@ +#define __STDC_CONSTANT_MACROS + +#include + +extern "C" +{ +void hq3x_16(unsigned char*, unsigned char*, uint32_t, uint32_t, uint32_t, uint32_t); +void hq3x_32(unsigned char*, unsigned char*, uint32_t, uint32_t, uint32_t, uint32_t); +void hq4x_16(unsigned char*, unsigned char*, uint32_t, uint32_t, uint32_t, uint32_t); +void hq4x_32(unsigned char*, unsigned char*, uint32_t, uint32_t, uint32_t, uint32_t); + +unsigned int LUT16to32[65536]; +unsigned int RGBtoYUV[65536]; +} + +void InitLUTs(void) +{ + int i, j, k, r, g, b, Y, u, v; + + for (i=0; i<65536; i++) + LUT16to32[i] = ((i & 0xF800) << 8) + ((i & 0x07E0) << 5) + ((i & 0x001F) << 3); + + for (i=0; i<32; i++) + for (j=0; j<64; j++) + for (k=0; k<32; k++) + { + r = i << 3; + g = j << 2; + b = k << 3; + Y = (r + g + b) >> 2; + u = 128 + ((r - b) >> 2); + v = 128 + ((-r + 2*g -b)>>3); + RGBtoYUV[ (i << 11) + (j << 5) + k ] = (Y<<16) + (u<<8) + v; + } +} + +int hq3xinited=0; + +//16 bit input, see below for 32 bit input +void hq3x32(unsigned char * pIn, unsigned int srcPitch, + unsigned char *, + unsigned char * pOut, unsigned int dstPitch, + int Xres, int Yres) +{ + if (!hq3xinited) + { + InitLUTs(); + hq3xinited=1; + } + hq3x_32( pIn, pOut, Xres, Yres, dstPitch, srcPitch - (Xres *2) ); +} + +void hq3x16(unsigned char * pIn, unsigned int srcPitch, + unsigned char *, + unsigned char * pOut, unsigned int dstPitch, + int Xres, int Yres) +{ + if (!hq3xinited) + { + InitLUTs(); + hq3xinited=1; + } + hq3x_16( pIn, pOut, Xres, Yres, dstPitch, srcPitch - (Xres *2)); +} + + +void hq4x16(unsigned char * pIn, unsigned int srcPitch, + unsigned char *, + unsigned char * pOut, unsigned int dstPitch, + int Xres, int Yres) +{ + if (!hq3xinited) + { + InitLUTs(); + hq3xinited=1; + } + hq4x_16( pIn, pOut, Xres, Yres, dstPitch, srcPitch - (Xres *2)); +} + +//16 bit input, see below for 32 bit input +void hq4x32(unsigned char * pIn, unsigned int srcPitch, + unsigned char *, + unsigned char * pOut, unsigned int dstPitch, + int Xres, int Yres) +{ + if (!hq3xinited) + { + InitLUTs(); + hq3xinited=1; + } + hq4x_32( pIn, pOut, Xres, Yres, dstPitch, srcPitch - (Xres *2)); +} + +static inline void convert32bpp_16bpp(unsigned char *pIn, unsigned int width) +{ + for (unsigned int i = 0; i < width; i+=4) + { + unsigned int p4 = ((unsigned int)pIn[i+2] << 16) | (unsigned int) (pIn[i+1] << 8) | pIn[i+0]; + unsigned short p2 = ((p4 >> 8)&0xF800) | ((p4 >> 5)&0x07E0) | ((p4 >> 3)&0x001F); + pIn[i/2] = (p2 >> 0); + pIn[i/2+1] = (p2 >> 8); + } +} + +void hq3x32_32(unsigned char *pIn, unsigned int srcPitch, unsigned char *, unsigned char *pOut, unsigned int dstPitch, int Xres, int Yres) +{ + convert32bpp_16bpp(pIn, srcPitch*Yres); + hq3x32(pIn, srcPitch/2, 0, pOut, dstPitch, Xres, Yres); +} + +void hq4x32_32(unsigned char *pIn, unsigned int srcPitch, unsigned char *, unsigned char *pOut, unsigned int dstPitch, int Xres, int Yres) +{ + convert32bpp_16bpp(pIn, srcPitch*Yres); + hq4x32(pIn, srcPitch/2, 0, pOut, dstPitch, Xres, Yres); +} diff --git a/src/filters/hq/asm/hq3x_16.asm b/src/filters/hq/asm/hq3x_16.asm new file mode 100644 index 0000000..85f0160 --- /dev/null +++ b/src/filters/hq/asm/hq3x_16.asm @@ -0,0 +1,2531 @@ +;hq3x filter (thread-safe version) +;16bpp output +;---------------------------------------------------------- +;Copyright (C) 2003 MaxSt ( maxst@hiend3d.com ) +; +;This program is free software; you can redistribute it and/or +;modify it under the terms of the GNU General Public License +;as published by the Free Software Foundation; either +;version 2 of the License, or (at your option) any later +;version. +; +;This program is distributed in the hope that it will be useful, +;but WITHOUT ANY WARRANTY; without even the implied warranty of +;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;GNU General Public License for more details. +; +;You should have received a copy of the GNU General Public License +;along with this program; if not, write to the Free Software +;Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +%include "macros.mac" + +EXTSYM LUT16to32,RGBtoYUV + +SECTION .bss + +SECTION .data + +reg_blank dd 0,0 +const7 dd 0x00070007,0x00000007 +threshold dd 0x00300706,0x00000000 +zerolowbits dd 0xF7DEF7DE + +SECTION .text + +%macro TestDiff 2 + xor ecx,ecx + mov edx,[%1] + cmp edx,[%2] + je %%fin + mov ecx,RGBtoYUV + movd mm1,[ecx+edx*4] + movq mm5,mm1 + mov edx,[%2] + movd mm2,[ecx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd ecx,mm1 +%%fin: +%endmacro + +%macro DiffOrNot 4 + TestDiff %1,%2 + test ecx,ecx + jz %%same + %3 + jmp %%fin +%%same: + %4 +%%fin +%endmacro + +%macro DiffOrNot 6 + TestDiff %1,%2 + test ecx,ecx + jz %%same + %3 + %4 + jmp %%fin +%%same: + %5 + %6 +%%fin +%endmacro + +%macro DiffOrNot 8 + TestDiff %1,%2 + test ecx,ecx + jz %%same + %3 + %4 + %5 + jmp %%fin +%%same: + %6 + %7 + %8 +%%fin +%endmacro + +%macro DiffOrNot 10 + TestDiff %1,%2 + test ecx,ecx + jz %%same + %3 + %4 + %5 + %6 + jmp %%fin +%%same: + %7 + %8 + %9 + %10 +%%fin +%endmacro + +%macro Interp1 3 + mov edx,%2 + mov ecx,%3 + cmp edx,ecx + je %%fin + and edx,[zerolowbits] + and ecx,[zerolowbits] + add ecx,edx + shr ecx,1 + add ecx,0x0821 + and ecx,[zerolowbits] + add edx,ecx + shr edx,1 +%%fin + mov %1,dx +%endmacro + +%macro Interp2 4 + mov edx,%3 + mov ecx,%4 + cmp edx,ecx + je %%fin1 + and edx,[zerolowbits] + and ecx,[zerolowbits] + add ecx,edx + shr ecx,1 + add ecx,0x0821 +%%fin1 + mov edx,%2 + cmp edx,ecx + je %%fin2 + and ecx,[zerolowbits] + and edx,[zerolowbits] + add edx,ecx + shr edx,1 +%%fin2 + mov %1,dx +%endmacro + +%macro Interp3 2 + mov ecx, LUT16to32 + movd mm1, [ecx+eax*4] + mov edx, %2 + movd mm2, [ecx+edx*4] + punpcklbw mm1, [reg_blank] + punpcklbw mm2, [reg_blank] + pmullw mm1, [const7] + paddw mm1, mm2 + psrlw mm1, 5 + packuswb mm1, [reg_blank] + movd edx, mm1 + shl dl, 2 + shr edx, 1 + shl dx, 3 + shr edx, 5 + mov %1, dx +%endmacro + +%macro Interp4 3 + mov ecx, LUT16to32 + movd mm1, [ecx+eax*4] + mov edx, %2 + movd mm2, [ecx+edx*4] + mov edx, %3 + movd mm3, [ecx+edx*4] + punpcklbw mm1, [reg_blank] + punpcklbw mm2, [reg_blank] + punpcklbw mm3, [reg_blank] + psllw mm1, 1 + paddw mm2, mm3 + pmullw mm2, [const7] + paddw mm1, mm2 + psrlw mm1, 6 + packuswb mm1, [reg_blank] + movd edx, mm1 + shl dl, 2 + shr edx, 1 + shl dx, 3 + shr edx, 5 + mov %1, dx +%endmacro + +%macro Interp5 3 + mov edx,%2 + mov ecx,%3 + cmp edx,ecx + je %%fin + and edx,[zerolowbits] + and ecx,[zerolowbits] + add edx,ecx + shr edx,1 +%%fin + mov %1,dx +%endmacro + +%macro PIXEL00_1M 0 + Interp1 [edi],eax,dword[ebp-w1] +%endmacro + +%macro PIXEL00_1U 0 + Interp1 [edi],eax,dword[ebp-w2] +%endmacro + +%macro PIXEL00_1L 0 + Interp1 [edi],eax,dword[ebp-w4] +%endmacro + +%macro PIXEL00_2 0 + Interp2 [edi],eax,dword[ebp-w4],dword[ebp-w2] +%endmacro + +%macro PIXEL00_4 0 + Interp4 [edi],dword[ebp-w4],dword[ebp-w2] +%endmacro + +%macro PIXEL00_5 0 + Interp5 [edi],dword[ebp-w4],dword[ebp-w2] +%endmacro + +%macro PIXEL00_C 0 + mov [edi],ax +%endmacro + +%macro PIXEL01_1 0 + Interp1 [edi+2],eax,dword[ebp-w2] +%endmacro + +%macro PIXEL01_3 0 + Interp3 [edi+2],dword[ebp-w2] +%endmacro + +%macro PIXEL01_6 0 + Interp1 [edi+2],dword[ebp-w2],eax +%endmacro + +%macro PIXEL01_C 0 + mov [edi+2],ax +%endmacro + +%macro PIXEL02_1M 0 + Interp1 [edi+4],eax,dword[ebp-w3] +%endmacro + +%macro PIXEL02_1U 0 + Interp1 [edi+4],eax,dword[ebp-w2] +%endmacro + +%macro PIXEL02_1R 0 + Interp1 [edi+4],eax,dword[ebp-w6] +%endmacro + +%macro PIXEL02_2 0 + Interp2 [edi+4],eax,dword[ebp-w2],dword[ebp-w6] +%endmacro + +%macro PIXEL02_4 0 + Interp4 [edi+4],dword[ebp-w2],dword[ebp-w6] +%endmacro + +%macro PIXEL02_5 0 + Interp5 [edi+4],dword[ebp-w2],dword[ebp-w6] +%endmacro + +%macro PIXEL02_C 0 + mov [edi+4],ax +%endmacro + +%macro PIXEL10_1 0 + Interp1 [edi+ebx],eax,dword[ebp-w4] +%endmacro + +%macro PIXEL10_3 0 + Interp3 [edi+ebx],dword[ebp-w4] +%endmacro + +%macro PIXEL10_6 0 + Interp1 [edi+ebx],dword[ebp-w4],eax +%endmacro + +%macro PIXEL10_C 0 + mov [edi+ebx],ax +%endmacro + +%macro PIXEL11 0 + mov [edi+ebx+2],ax +%endmacro + +%macro PIXEL12_1 0 + Interp1 [edi+ebx+4],eax,dword[ebp-w6] +%endmacro + +%macro PIXEL12_3 0 + Interp3 [edi+ebx+4],dword[ebp-w6] +%endmacro + +%macro PIXEL12_6 0 + Interp1 [edi+ebx+4],dword[ebp-w6],eax +%endmacro + +%macro PIXEL12_C 0 + mov [edi+ebx+4],ax +%endmacro + +%macro PIXEL20_1M 0 + Interp1 [edi+ebx*2],eax,dword[ebp-w7] +%endmacro + +%macro PIXEL20_1D 0 + Interp1 [edi+ebx*2],eax,dword[ebp-w8] +%endmacro + +%macro PIXEL20_1L 0 + Interp1 [edi+ebx*2],eax,dword[ebp-w4] +%endmacro + +%macro PIXEL20_2 0 + Interp2 [edi+ebx*2],eax,dword[ebp-w8],dword[ebp-w4] +%endmacro + +%macro PIXEL20_4 0 + Interp4 [edi+ebx*2],dword[ebp-w8],dword[ebp-w4] +%endmacro + +%macro PIXEL20_5 0 + Interp5 [edi+ebx*2],dword[ebp-w8],dword[ebp-w4] +%endmacro + +%macro PIXEL20_C 0 + mov [edi+ebx*2],ax +%endmacro + +%macro PIXEL21_1 0 + Interp1 [edi+ebx*2+2],eax,dword[ebp-w8] +%endmacro + +%macro PIXEL21_3 0 + Interp3 [edi+ebx*2+2],dword[ebp-w8] +%endmacro + +%macro PIXEL21_6 0 + Interp1 [edi+ebx*2+2],dword[ebp-w8],eax +%endmacro + +%macro PIXEL21_C 0 + mov [edi+ebx*2+2],ax +%endmacro + +%macro PIXEL22_1M 0 + Interp1 [edi+ebx*2+4],eax,dword[ebp-w9] +%endmacro + +%macro PIXEL22_1D 0 + Interp1 [edi+ebx*2+4],eax,dword[ebp-w8] +%endmacro + +%macro PIXEL22_1R 0 + Interp1 [edi+ebx*2+4],eax,dword[ebp-w6] +%endmacro + +%macro PIXEL22_2 0 + Interp2 [edi+ebx*2+4],eax,dword[ebp-w6],dword[ebp-w8] +%endmacro + +%macro PIXEL22_4 0 + Interp4 [edi+ebx*2+4],dword[ebp-w6],dword[ebp-w8] +%endmacro + +%macro PIXEL22_5 0 + Interp5 [edi+ebx*2+4],dword[ebp-w6],dword[ebp-w8] +%endmacro + +%macro PIXEL22_C 0 + mov [edi+ebx*2+4],ax +%endmacro + +inbuffer equ 8 +outbuffer equ 12 +Xres equ 16 +Yres equ 20 +pitch equ 24 +offset equ 28 + +linesleft equ 4 +xcounter equ 8 +cross equ 12 +nextline equ 16 +prevline equ 20 +w1 equ 24 +w2 equ 28 +w3 equ 32 +w4 equ 36 +w5 equ 40 +w6 equ 44 +w7 equ 48 +w8 equ 52 +w9 equ 56 +localsize equ 56 + +NEWSYM hq3x_16 + push ebp + mov ebp,esp + sub esp, localsize + pushad + + mov esi,[ebp+inbuffer] + mov edi,[ebp+outbuffer] + mov edx,[ebp+Yres] + mov [ebp-linesleft],edx + mov ebx,[ebp+Xres] + shl ebx,1 + mov dword[ebp-prevline],0 + mov eax, [ebp+offset] + add eax, ebx + mov dword[ebp-nextline],eax +.loopy + mov ecx,[ebp+Xres] + sub ecx,2 ; x={Xres-2, Xres-1} are special cases. + mov dword[ebp-xcounter],ecx + ; x=0 - special case + mov ebx,[ebp-prevline] + movq mm5,[esi+ebx] + movq mm6,[esi] + mov ebx,[ebp-nextline] + movq mm7,[esi+ebx] + movd eax,mm5 + movzx edx,ax + mov [ebp-w1],edx + mov [ebp-w2],edx + shr eax,16 + mov [ebp-w3],eax + movd eax,mm6 + movzx edx,ax + mov [ebp-w4],edx + mov [ebp-w5],edx + shr eax,16 + mov [ebp-w6],eax + movd eax,mm7 + movzx edx,ax + mov [ebp-w7],edx + mov [ebp-w8],edx + shr eax,16 + mov [ebp-w9],eax + jmp .flags +.loopx + mov ebx,[ebp-prevline] + movq mm5,[esi+ebx-2] + movq mm6,[esi-2] + mov ebx,[ebp-nextline] + movq mm7,[esi+ebx-2] + movd eax,mm5 + movzx edx,ax + mov [ebp-w1],edx + shr eax,16 + mov [ebp-w2],eax + psrlq mm5,32 + movd eax,mm5 + movzx edx,ax + mov [ebp-w3],edx + movd eax,mm6 + movzx edx,ax + mov [ebp-w4],edx + shr eax,16 + mov [ebp-w5],eax + psrlq mm6,32 + movd eax,mm6 + movzx edx,ax + mov [ebp-w6],edx + movd eax,mm7 + movzx edx,ax + mov [ebp-w7],edx + shr eax,16 + mov [ebp-w8],eax + psrlq mm7,32 + movd eax,mm7 + movzx edx,ax + mov [ebp-w9],edx +.flags + mov ebx,RGBtoYUV + mov eax,[ebp-w5] + xor ecx,ecx + movd mm5,[ebx+eax*4] + mov dword[ebp-cross],0 + + mov edx,[ebp-w2] + cmp eax,edx + je .noflag2 + or dword[ebp-cross],1 + movq mm1,mm5 + movd mm2,[ebx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 + test edx,edx + jz .noflag2 + or ecx,2 +.noflag2 + mov edx,[ebp-w4] + cmp eax,edx + je .noflag4 + or dword[ebp-cross],2 + movq mm1,mm5 + movd mm2,[ebx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 + test edx,edx + jz .noflag4 + or ecx,8 +.noflag4 + mov edx,[ebp-w6] + cmp eax,edx + je .noflag6 + or dword[ebp-cross],4 + movq mm1,mm5 + movd mm2,[ebx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 + test edx,edx + jz .noflag6 + or ecx,16 +.noflag6 + mov edx,[ebp-w8] + cmp eax,edx + je .noflag8 + or dword[ebp-cross],8 + movq mm1,mm5 + movd mm2,[ebx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 + test edx,edx + jz .noflag8 + or ecx,64 +.noflag8 + test ecx,ecx + jnz .testflag1 + mov ecx,[ebp-cross] + mov ebx,[ebp+pitch] + jmp [FuncTable2+ecx*4] +.testflag1 + mov edx,[ebp-w1] + cmp eax,edx + je .noflag1 + movq mm1,mm5 + movd mm2,[ebx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 + test edx,edx + jz .noflag1 + or ecx,1 +.noflag1 + mov edx,[ebp-w3] + cmp eax,edx + je .noflag3 + movq mm1,mm5 + movd mm2,[ebx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 + test edx,edx + jz .noflag3 + or ecx,4 +.noflag3 + mov edx,[ebp-w7] + cmp eax,edx + je .noflag7 + movq mm1,mm5 + movd mm2,[ebx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 + test edx,edx + jz .noflag7 + or ecx,32 +.noflag7 + mov edx,[ebp-w9] + cmp eax,edx + je .noflag9 + movq mm1,mm5 + movd mm2,[ebx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 + test edx,edx + jz .noflag9 + or ecx,128 +.noflag9 + mov ebx,[ebp+pitch] + jmp [FuncTable+ecx*4] + +..@flag0 +..@flag1 +..@flag4 +..@flag32 +..@flag128 +..@flag5 +..@flag132 +..@flag160 +..@flag33 +..@flag129 +..@flag36 +..@flag133 +..@flag164 +..@flag161 +..@flag37 +..@flag165 + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag2 +..@flag34 +..@flag130 +..@flag162 + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag16 +..@flag17 +..@flag48 +..@flag49 + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag64 +..@flag65 +..@flag68 +..@flag69 + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag8 +..@flag12 +..@flag136 +..@flag140 + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag3 +..@flag35 +..@flag131 +..@flag163 + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag6 +..@flag38 +..@flag134 +..@flag166 + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag20 +..@flag21 +..@flag52 +..@flag53 + PIXEL00_2 + PIXEL01_1 + PIXEL02_1U + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag144 +..@flag145 +..@flag176 +..@flag177 + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag192 +..@flag193 +..@flag196 +..@flag197 + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag96 +..@flag97 +..@flag100 +..@flag101 + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag40 +..@flag44 +..@flag168 +..@flag172 + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1D + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag9 +..@flag13 +..@flag137 +..@flag141 + PIXEL00_1U + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag18 +..@flag50 + PIXEL00_1M + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_1M,PIXEL12_C,PIXEL01_3,PIXEL02_4,PIXEL12_3 + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag80 +..@flag81 + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL20_1M + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL21_C,PIXEL22_1M,PIXEL12_3,PIXEL21_3,PIXEL22_4 + jmp .loopx_end +..@flag72 +..@flag76 + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_1M,PIXEL21_C,PIXEL10_3,PIXEL20_4,PIXEL21_3 + PIXEL22_1M + jmp .loopx_end +..@flag10 +..@flag138 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_1M,PIXEL01_C,PIXEL10_C,PIXEL00_4,PIXEL01_3,PIXEL10_3 + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag66 + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag24 + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag7 +..@flag39 +..@flag135 + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag148 +..@flag149 +..@flag180 + PIXEL00_2 + PIXEL01_1 + PIXEL02_1U + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag224 +..@flag228 +..@flag225 + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag41 +..@flag169 +..@flag45 + PIXEL00_1U + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1D + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag22 +..@flag54 + PIXEL00_1M + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_C,PIXEL12_C,PIXEL01_3,PIXEL02_4,PIXEL12_3 + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag208 +..@flag209 + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL20_1M + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL21_C,PIXEL22_C,PIXEL12_3,PIXEL21_3,PIXEL22_4 + jmp .loopx_end +..@flag104 +..@flag108 + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_C,PIXEL21_C,PIXEL10_3,PIXEL20_4,PIXEL21_3 + PIXEL22_1M + jmp .loopx_end +..@flag11 +..@flag139 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL10_C,PIXEL00_4,PIXEL01_3,PIXEL10_3 + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag19 +..@flag51 + DiffOrNot ebp-w2,ebp-w6,PIXEL00_1L,PIXEL01_C,PIXEL02_1M,PIXEL12_C,PIXEL00_2,PIXEL01_6,PIXEL02_5,PIXEL12_1 + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag146 +..@flag178 + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_1M,PIXEL12_C,PIXEL22_1D,PIXEL01_1,PIXEL02_5,PIXEL12_6,PIXEL22_2 + PIXEL00_1M + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + jmp .loopx_end +..@flag84 +..@flag85 + DiffOrNot ebp-w6,ebp-w8,PIXEL02_1U,PIXEL12_C,PIXEL21_C,PIXEL22_1M,PIXEL02_2,PIXEL12_6,PIXEL21_1,PIXEL22_5 + PIXEL00_2 + PIXEL01_1 + PIXEL10_1 + PIXEL11 + PIXEL20_1M + jmp .loopx_end +..@flag112 +..@flag113 + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL20_1L,PIXEL21_C,PIXEL22_1M,PIXEL12_1,PIXEL20_2,PIXEL21_6,PIXEL22_5 + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + jmp .loopx_end +..@flag200 +..@flag204 + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_1M,PIXEL21_C,PIXEL22_1R,PIXEL10_1,PIXEL20_5,PIXEL21_6,PIXEL22_2 + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + jmp .loopx_end +..@flag73 +..@flag77 + DiffOrNot ebp-w8,ebp-w4,PIXEL00_1U,PIXEL10_C,PIXEL20_1M,PIXEL21_C,PIXEL00_2,PIXEL10_6,PIXEL20_5,PIXEL21_1 + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + PIXEL22_1M + jmp .loopx_end +..@flag42 +..@flag170 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_1M,PIXEL01_C,PIXEL10_C,PIXEL20_1D,PIXEL00_5,PIXEL01_1,PIXEL10_6,PIXEL20_2 + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag14 +..@flag142 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_1M,PIXEL01_C,PIXEL02_1R,PIXEL10_C,PIXEL00_5,PIXEL01_6,PIXEL02_2,PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag67 + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag70 + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag28 + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag152 + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag194 + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag98 + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag56 + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag25 + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag26 +..@flag31 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL10_C,PIXEL00_4,PIXEL10_3 + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_C,PIXEL12_C,PIXEL02_4,PIXEL12_3 + PIXEL11 + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag82 +..@flag214 + PIXEL00_1M + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_C,PIXEL01_3,PIXEL02_4 + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1M + DiffOrNot ebp-w6,ebp-w8,PIXEL21_C,PIXEL22_C,PIXEL21_3,PIXEL22_4 + jmp .loopx_end +..@flag88 +..@flag248 + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL11 + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_C,PIXEL10_3,PIXEL20_4 + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL22_C,PIXEL12_3,PIXEL22_4 + jmp .loopx_end +..@flag74 +..@flag107 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL00_4,PIXEL01_3 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_1 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_C,PIXEL21_C,PIXEL20_4,PIXEL21_3 + PIXEL22_1M + jmp .loopx_end +..@flag27 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL10_C,PIXEL00_4,PIXEL01_3,PIXEL10_3 + PIXEL02_1M + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag86 + PIXEL00_1M + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_C,PIXEL12_C,PIXEL01_3,PIXEL02_4,PIXEL12_3 + PIXEL10_1 + PIXEL11 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag216 + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL20_1M + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL21_C,PIXEL22_C,PIXEL12_3,PIXEL21_3,PIXEL22_4 + jmp .loopx_end +..@flag106 + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL11 + PIXEL12_1 + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_C,PIXEL21_C,PIXEL10_3,PIXEL20_4,PIXEL21_3 + PIXEL22_1M + jmp .loopx_end +..@flag30 + PIXEL00_1M + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_C,PIXEL12_C,PIXEL01_3,PIXEL02_4,PIXEL12_3 + PIXEL10_C + PIXEL11 + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag210 + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL20_1M + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL21_C,PIXEL22_C,PIXEL12_3,PIXEL21_3,PIXEL22_4 + jmp .loopx_end +..@flag120 + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL11 + PIXEL12_C + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_C,PIXEL21_C,PIXEL10_3,PIXEL20_4,PIXEL21_3 + PIXEL22_1M + jmp .loopx_end +..@flag75 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL10_C,PIXEL00_4,PIXEL01_3,PIXEL10_3 + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag29 + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag198 + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag184 + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag99 + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag57 + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag71 + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag156 + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag226 + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag60 + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag195 + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag102 + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag153 + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag58 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_1M,PIXEL00_2 + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_1M,PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag83 + PIXEL00_1L + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_1M,PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_1M,PIXEL22_2 + jmp .loopx_end +..@flag92 + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + DiffOrNot ebp-w8,ebp-w4,PIXEL20_1M,PIXEL20_2 + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_1M,PIXEL22_2 + jmp .loopx_end +..@flag202 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_1M,PIXEL00_2 + PIXEL01_C + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_1 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_1M,PIXEL20_2 + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag78 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_1M,PIXEL00_2 + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_1M,PIXEL20_2 + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag154 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_1M,PIXEL00_2 + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_1M,PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag114 + PIXEL00_1M + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_1M,PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_1M,PIXEL22_2 + jmp .loopx_end +..@flag89 + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + DiffOrNot ebp-w8,ebp-w4,PIXEL20_1M,PIXEL20_2 + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_1M,PIXEL22_2 + jmp .loopx_end +..@flag90 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_1M,PIXEL00_2 + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_1M,PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_C + DiffOrNot ebp-w8,ebp-w4,PIXEL20_1M,PIXEL20_2 + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_1M,PIXEL22_2 + jmp .loopx_end +..@flag55 +..@flag23 + DiffOrNot ebp-w2,ebp-w6,PIXEL00_1L,PIXEL01_C,PIXEL02_C,PIXEL12_C,PIXEL00_2,PIXEL01_6,PIXEL02_5,PIXEL12_1 + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag182 +..@flag150 + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_C,PIXEL12_C,PIXEL22_1D,PIXEL01_1,PIXEL02_5,PIXEL12_6,PIXEL22_2 + PIXEL00_1M + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + jmp .loopx_end +..@flag213 +..@flag212 + DiffOrNot ebp-w6,ebp-w8,PIXEL02_1U,PIXEL12_C,PIXEL21_C,PIXEL22_C,PIXEL02_2,PIXEL12_6,PIXEL21_1,PIXEL22_5 + PIXEL00_2 + PIXEL01_1 + PIXEL10_1 + PIXEL11 + PIXEL20_1M + jmp .loopx_end +..@flag241 +..@flag240 + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL20_1L,PIXEL21_C,PIXEL22_C,PIXEL12_1,PIXEL20_2,PIXEL21_6,PIXEL22_5 + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + jmp .loopx_end +..@flag236 +..@flag232 + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_C,PIXEL21_C,PIXEL22_1R,PIXEL10_1,PIXEL20_5,PIXEL21_6,PIXEL22_2 + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + jmp .loopx_end +..@flag109 +..@flag105 + DiffOrNot ebp-w8,ebp-w4,PIXEL00_1U,PIXEL10_C,PIXEL20_C,PIXEL21_C,PIXEL00_2,PIXEL10_6,PIXEL20_5,PIXEL21_1 + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + PIXEL22_1M + jmp .loopx_end +..@flag171 +..@flag43 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL10_C,PIXEL20_1D,PIXEL00_5,PIXEL01_1,PIXEL10_6,PIXEL20_2 + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag143 +..@flag15 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL02_1R,PIXEL10_C,PIXEL00_5,PIXEL01_6,PIXEL02_2,PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag124 + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL11 + PIXEL12_C + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_C,PIXEL21_C,PIXEL10_3,PIXEL20_4,PIXEL21_3 + PIXEL22_1M + jmp .loopx_end +..@flag203 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL10_C,PIXEL00_4,PIXEL01_3,PIXEL10_3 + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag62 + PIXEL00_1M + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_C,PIXEL12_C,PIXEL01_3,PIXEL02_4,PIXEL12_3 + PIXEL10_C + PIXEL11 + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag211 + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL20_1M + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL21_C,PIXEL22_C,PIXEL12_3,PIXEL21_3,PIXEL22_4 + jmp .loopx_end +..@flag118 + PIXEL00_1M + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_C,PIXEL12_C,PIXEL01_3,PIXEL02_4,PIXEL12_3 + PIXEL10_1 + PIXEL11 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag217 + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL20_1M + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL21_C,PIXEL22_C,PIXEL12_3,PIXEL21_3,PIXEL22_4 + jmp .loopx_end +..@flag110 + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL11 + PIXEL12_1 + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_C,PIXEL21_C,PIXEL10_3,PIXEL20_4,PIXEL21_3 + PIXEL22_1M + jmp .loopx_end +..@flag155 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL10_C,PIXEL00_4,PIXEL01_3,PIXEL10_3 + PIXEL02_1M + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag188 + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag185 + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag61 + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag157 + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag103 + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag227 + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag230 + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag199 + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag220 + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_1M,PIXEL20_2 + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL21_C,PIXEL22_C,PIXEL12_3,PIXEL21_3,PIXEL22_4 + jmp .loopx_end +..@flag158 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_1M,PIXEL00_2 + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_C,PIXEL12_C,PIXEL01_3,PIXEL02_4,PIXEL12_3 + PIXEL10_C + PIXEL11 + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag234 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_1M,PIXEL00_2 + PIXEL01_C + PIXEL02_1M + PIXEL11 + PIXEL12_1 + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_C,PIXEL21_C,PIXEL10_3,PIXEL20_4,PIXEL21_3 + PIXEL22_1R + jmp .loopx_end +..@flag242 + PIXEL00_1M + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_1M,PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL20_1L + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL21_C,PIXEL22_C,PIXEL12_3,PIXEL21_3,PIXEL22_4 + jmp .loopx_end +..@flag59 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL10_C,PIXEL00_4,PIXEL01_3,PIXEL10_3 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_1M,PIXEL02_2 + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag121 + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL11 + PIXEL12_C + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_C,PIXEL21_C,PIXEL10_3,PIXEL20_4,PIXEL21_3 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_1M,PIXEL22_2 + jmp .loopx_end +..@flag87 + PIXEL00_1L + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_C,PIXEL12_C,PIXEL01_3,PIXEL02_4,PIXEL12_3 + PIXEL10_1 + PIXEL11 + PIXEL20_1M + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_1M,PIXEL22_2 + jmp .loopx_end +..@flag79 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL10_C,PIXEL00_4,PIXEL01_3,PIXEL10_3 + PIXEL02_1R + PIXEL11 + PIXEL12_1 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_1M,PIXEL20_2 + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag122 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_1M,PIXEL00_2 + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_1M,PIXEL02_2 + PIXEL11 + PIXEL12_C + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_C,PIXEL21_C,PIXEL10_3,PIXEL20_4,PIXEL21_3 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_1M,PIXEL22_2 + jmp .loopx_end +..@flag94 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_1M,PIXEL00_2 + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_C,PIXEL12_C,PIXEL01_3,PIXEL02_4,PIXEL12_3 + PIXEL10_C + PIXEL11 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_1M,PIXEL20_2 + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_1M,PIXEL22_2 + jmp .loopx_end +..@flag218 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_1M,PIXEL00_2 + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_1M,PIXEL02_2 + PIXEL10_C + PIXEL11 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_1M,PIXEL20_2 + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL21_C,PIXEL22_C,PIXEL12_3,PIXEL21_3,PIXEL22_4 + jmp .loopx_end +..@flag91 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL10_C,PIXEL00_4,PIXEL01_3,PIXEL10_3 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_1M,PIXEL02_2 + PIXEL11 + PIXEL12_C + DiffOrNot ebp-w8,ebp-w4,PIXEL20_1M,PIXEL20_2 + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_1M,PIXEL22_2 + jmp .loopx_end +..@flag229 + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag167 + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag173 + PIXEL00_1U + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1D + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag181 + PIXEL00_2 + PIXEL01_1 + PIXEL02_1U + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag186 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_1M,PIXEL00_2 + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_1M,PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag115 + PIXEL00_1L + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_1M,PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_1M,PIXEL22_2 + jmp .loopx_end +..@flag93 + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + DiffOrNot ebp-w8,ebp-w4,PIXEL20_1M,PIXEL20_2 + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_1M,PIXEL22_2 + jmp .loopx_end +..@flag206 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_1M,PIXEL00_2 + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_1M,PIXEL20_2 + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag205 +..@flag201 + PIXEL00_1U + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_1M,PIXEL20_2 + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag174 +..@flag46 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_1M,PIXEL00_2 + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1D + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag179 +..@flag147 + PIXEL00_1L + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_1M,PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag117 +..@flag116 + PIXEL00_2 + PIXEL01_1 + PIXEL02_1U + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_1M,PIXEL22_2 + jmp .loopx_end +..@flag189 + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag231 + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag126 + PIXEL00_1M + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_C,PIXEL12_C,PIXEL01_3,PIXEL02_4,PIXEL12_3 + PIXEL11 + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_C,PIXEL21_C,PIXEL10_3,PIXEL20_4,PIXEL21_3 + PIXEL22_1M + jmp .loopx_end +..@flag219 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL10_C,PIXEL00_4,PIXEL01_3,PIXEL10_3 + PIXEL02_1M + PIXEL11 + PIXEL20_1M + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL21_C,PIXEL22_C,PIXEL12_3,PIXEL21_3,PIXEL22_4 + jmp .loopx_end +..@flag125 + DiffOrNot ebp-w8,ebp-w4,PIXEL00_1U,PIXEL10_C,PIXEL20_C,PIXEL21_C,PIXEL00_2,PIXEL10_6,PIXEL20_5,PIXEL21_1 + PIXEL01_1 + PIXEL02_1U + PIXEL11 + PIXEL12_C + PIXEL22_1M + jmp .loopx_end +..@flag221 + DiffOrNot ebp-w6,ebp-w8,PIXEL02_1U,PIXEL12_C,PIXEL21_C,PIXEL22_C,PIXEL02_2,PIXEL12_6,PIXEL21_1,PIXEL22_5 + PIXEL00_1U + PIXEL01_1 + PIXEL10_C + PIXEL11 + PIXEL20_1M + jmp .loopx_end +..@flag207 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL02_1R,PIXEL10_C,PIXEL00_5,PIXEL01_6,PIXEL02_2,PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag238 + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_C,PIXEL21_C,PIXEL22_1R,PIXEL10_1,PIXEL20_5,PIXEL21_6,PIXEL22_2 + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL11 + PIXEL12_1 + jmp .loopx_end +..@flag190 + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_C,PIXEL12_C,PIXEL22_1D,PIXEL01_1,PIXEL02_5,PIXEL12_6,PIXEL22_2 + PIXEL00_1M + PIXEL10_C + PIXEL11 + PIXEL20_1D + PIXEL21_1 + jmp .loopx_end +..@flag187 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL10_C,PIXEL20_1D,PIXEL00_5,PIXEL01_1,PIXEL10_6,PIXEL20_2 + PIXEL02_1M + PIXEL11 + PIXEL12_C + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag243 + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL20_1L,PIXEL21_C,PIXEL22_C,PIXEL12_1,PIXEL20_2,PIXEL21_6,PIXEL22_5 + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + jmp .loopx_end +..@flag119 + DiffOrNot ebp-w2,ebp-w6,PIXEL00_1L,PIXEL01_C,PIXEL02_C,PIXEL12_C,PIXEL00_2,PIXEL01_6,PIXEL02_5,PIXEL12_1 + PIXEL10_1 + PIXEL11 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag237 +..@flag233 + PIXEL00_1U + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_C,PIXEL20_2 + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag175 +..@flag47 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL00_2 + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1D + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag183 +..@flag151 + PIXEL00_1L + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_C,PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag245 +..@flag244 + PIXEL00_2 + PIXEL01_1 + PIXEL02_1U + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_C,PIXEL22_2 + jmp .loopx_end +..@flag250 + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL11 + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_C,PIXEL10_3,PIXEL20_4 + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL22_C,PIXEL12_3,PIXEL22_4 + jmp .loopx_end +..@flag123 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL00_4,PIXEL01_3 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + DiffOrNot ebp-w8,ebp-w4,PIXEL20_C,PIXEL21_C,PIXEL20_4,PIXEL21_3 + PIXEL22_1M + jmp .loopx_end +..@flag95 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL10_C,PIXEL00_4,PIXEL10_3 + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_C,PIXEL12_C,PIXEL02_4,PIXEL12_3 + PIXEL11 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag222 + PIXEL00_1M + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_C,PIXEL01_3,PIXEL02_4 + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + DiffOrNot ebp-w6,ebp-w8,PIXEL21_C,PIXEL22_C,PIXEL21_3,PIXEL22_4 + jmp .loopx_end +..@flag252 + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL11 + PIXEL12_C + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_C,PIXEL10_3,PIXEL20_4 + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_C,PIXEL22_2 + jmp .loopx_end +..@flag249 + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_C,PIXEL20_2 + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL22_C,PIXEL12_3,PIXEL22_4 + jmp .loopx_end +..@flag235 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL00_4,PIXEL01_3 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_1 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_C,PIXEL20_2 + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag111 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL00_2 + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_C,PIXEL21_C,PIXEL20_4,PIXEL21_3 + PIXEL22_1M + jmp .loopx_end +..@flag63 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL00_2 + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_C,PIXEL12_C,PIXEL02_4,PIXEL12_3 + PIXEL10_C + PIXEL11 + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag159 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL10_C,PIXEL00_4,PIXEL10_3 + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_C,PIXEL02_2 + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag215 + PIXEL00_1L + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_C,PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1M + DiffOrNot ebp-w6,ebp-w8,PIXEL21_C,PIXEL22_C,PIXEL21_3,PIXEL22_4 + jmp .loopx_end +..@flag246 + PIXEL00_1M + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_C,PIXEL01_3,PIXEL02_4 + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_C,PIXEL22_2 + jmp .loopx_end +..@flag254 + PIXEL00_1M + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_C,PIXEL01_3,PIXEL02_4 + PIXEL11 + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_C,PIXEL10_3,PIXEL20_4 + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL21_C,PIXEL22_C,PIXEL12_3,PIXEL21_3,PIXEL22_2 + jmp .loopx_end +..@flag253 + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + DiffOrNot ebp-w8,ebp-w4,PIXEL20_C,PIXEL20_2 + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_C,PIXEL22_2 + jmp .loopx_end +..@flag251 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL00_4,PIXEL01_3 + PIXEL02_1M + PIXEL11 + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_C,PIXEL21_C,PIXEL10_3,PIXEL20_2,PIXEL21_3 + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL22_C,PIXEL12_3,PIXEL22_4 + jmp .loopx_end +..@flag239 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL00_2 + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_C,PIXEL20_2 + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag127 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL10_C,PIXEL00_2,PIXEL01_3,PIXEL10_3 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_C,PIXEL12_C,PIXEL02_4,PIXEL12_3 + PIXEL11 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_C,PIXEL21_C,PIXEL20_4,PIXEL21_3 + PIXEL22_1M + jmp .loopx_end +..@flag191 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL00_2 + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_C,PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag223 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL10_C,PIXEL00_4,PIXEL10_3 + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_C,PIXEL12_C,PIXEL01_3,PIXEL02_2,PIXEL12_3 + PIXEL11 + PIXEL20_1M + DiffOrNot ebp-w6,ebp-w8,PIXEL21_C,PIXEL22_C,PIXEL21_3,PIXEL22_4 + jmp .loopx_end +..@flag247 + PIXEL00_1L + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_C,PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_C,PIXEL22_2 + jmp .loopx_end +..@flag255 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL00_2 + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_C,PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_C + DiffOrNot ebp-w8,ebp-w4,PIXEL20_C,PIXEL20_2 + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_C,PIXEL22_2 + jmp .loopx_end + +..@cross0 + mov edx,eax + shl eax,16 + or eax,edx + mov [edi],eax + mov [edi+4],ax + mov [edi+ebx],eax + mov [edi+ebx+4],ax + mov [edi+ebx*2],eax + mov [edi+ebx*2+4],ax + jmp .loopx_end +..@cross1 + mov edx,eax + shl eax,16 + or eax,edx + mov ecx,[ebp-w2] + and edx,[zerolowbits] + and ecx,[zerolowbits] + add ecx,edx + shr ecx,1 + add ecx,0x0821 + and ecx,[zerolowbits] + add edx,ecx + shr edx,1 + mov [edi],dx + mov [edi+2],dx + mov [edi+4],dx + mov [edi+ebx],eax + mov [edi+ebx+4],ax + mov [edi+ebx*2],eax + mov [edi+ebx*2+4],ax + jmp .loopx_end +..@cross2 + mov edx,eax + shl eax,16 + or eax,edx + mov ecx,[ebp-w4] + and edx,[zerolowbits] + and ecx,[zerolowbits] + add ecx,edx + shr ecx,1 + add ecx,0x0821 + and ecx,[zerolowbits] + add edx,ecx + shr edx,1 + mov [edi],dx + mov [edi+2],eax + mov [edi+ebx],dx + mov [edi+ebx+2],eax + mov [edi+ebx*2],dx + mov [edi+ebx*2+2],eax + jmp .loopx_end +..@cross4 + mov edx,eax + shl eax,16 + or eax,edx + mov ecx,[ebp-w6] + and edx,[zerolowbits] + and ecx,[zerolowbits] + add ecx,edx + shr ecx,1 + add ecx,0x0821 + and ecx,[zerolowbits] + add edx,ecx + shr edx,1 + mov [edi],eax + mov [edi+4],dx + mov [edi+ebx],eax + mov [edi+ebx+4],dx + mov [edi+ebx*2],eax + mov [edi+ebx*2+4],dx + jmp .loopx_end +..@cross8 + mov edx,eax + shl eax,16 + or eax,edx + mov ecx,[ebp-w8] + and edx,[zerolowbits] + and ecx,[zerolowbits] + add ecx,edx + shr ecx,1 + add ecx,0x0821 + and ecx,[zerolowbits] + add edx,ecx + shr edx,1 + mov [edi],eax + mov [edi+4],ax + mov [edi+ebx],eax + mov [edi+ebx+4],ax + mov [edi+ebx*2],dx + mov [edi+ebx*2+2],dx + mov [edi+ebx*2+4],dx + jmp .loopx_end + +.loopx_end + add esi,2 + add edi,6 + dec dword[ebp-xcounter] + jle .xres_2 + jmp .loopx +.xres_2 + ; x=Xres-2 - special case + jl .xres_1 + mov ebx,[ebp-prevline] + movq mm5,[esi+ebx-4] + movq mm6,[esi-4] + mov ebx,[ebp-nextline] + movq mm7,[esi+ebx-4] + psrlq mm5,16 + psrlq mm6,16 + psrlq mm7,16 + movd eax,mm5 + movzx edx,ax + mov [ebp-w1],edx + shr eax,16 + mov [ebp-w2],eax + psrlq mm5,32 + movd eax,mm5 + mov [ebp-w3],eax + movd eax,mm6 + movzx edx,ax + mov [ebp-w4],edx + shr eax,16 + mov [ebp-w5],eax + psrlq mm6,32 + movd eax,mm6 + mov [ebp-w6],eax + movd eax,mm7 + movzx edx,ax + mov [ebp-w7],edx + shr eax,16 + mov [ebp-w8],eax + psrlq mm7,32 + movd eax,mm7 + mov [ebp-w9],eax + jmp .flags +.xres_1 + cmp dword[ebp-xcounter],-1 + jl .nexty + ; x=Xres-1 - special case + mov ebx,[ebp-prevline] + movq mm5,[esi+ebx-6] + movq mm6,[esi-6] + mov ebx,[ebp-nextline] + movq mm7,[esi+ebx-6] + psrlq mm5,32 + psrlq mm6,32 + psrlq mm7,32 + movd eax,mm5 + movzx edx,ax + mov [ebp-w1],edx + shr eax,16 + mov [ebp-w2],eax + mov [ebp-w3],eax + movd eax,mm6 + movzx edx,ax + mov [ebp-w4],edx + shr eax,16 + mov [ebp-w5],eax + mov [ebp-w6],eax + movd eax,mm7 + movzx edx,ax + mov [ebp-w7],edx + shr eax,16 + mov [ebp-w8],eax + mov [ebp-w9],eax + jmp .flags +.nexty + add esi,[ebp+offset] ; added - move source pointer past end-of-line blanks + add edi,ebx + add edi,ebx + add edi,ebx + mov ebx, [ebp+Xres] ; added, bug - need to add to destination offset + shl ebx, 1 + sub edi, ebx + sub edi, ebx + sub edi, ebx + dec dword[ebp-linesleft] + jz .fin + add ebx, [ebp+offset]; + cmp dword[ebp-linesleft],1 + je .lastline + mov dword[ebp-nextline],ebx + neg ebx + mov dword[ebp-prevline],ebx + jmp .loopy +.lastline + mov dword[ebp-nextline],0 + neg ebx + mov dword[ebp-prevline],ebx + jmp .loopy +.fin + emms + popad + mov esp,ebp + pop ebp + ret + +SECTION .data +FuncTable + dd ..@flag0, ..@flag1, ..@flag2, ..@flag3, ..@flag4, ..@flag5, ..@flag6, ..@flag7 + dd ..@flag8, ..@flag9, ..@flag10, ..@flag11, ..@flag12, ..@flag13, ..@flag14, ..@flag15 + dd ..@flag16, ..@flag17, ..@flag18, ..@flag19, ..@flag20, ..@flag21, ..@flag22, ..@flag23 + dd ..@flag24, ..@flag25, ..@flag26, ..@flag27, ..@flag28, ..@flag29, ..@flag30, ..@flag31 + dd ..@flag32, ..@flag33, ..@flag34, ..@flag35, ..@flag36, ..@flag37, ..@flag38, ..@flag39 + dd ..@flag40, ..@flag41, ..@flag42, ..@flag43, ..@flag44, ..@flag45, ..@flag46, ..@flag47 + dd ..@flag48, ..@flag49, ..@flag50, ..@flag51, ..@flag52, ..@flag53, ..@flag54, ..@flag55 + dd ..@flag56, ..@flag57, ..@flag58, ..@flag59, ..@flag60, ..@flag61, ..@flag62, ..@flag63 + dd ..@flag64, ..@flag65, ..@flag66, ..@flag67, ..@flag68, ..@flag69, ..@flag70, ..@flag71 + dd ..@flag72, ..@flag73, ..@flag74, ..@flag75, ..@flag76, ..@flag77, ..@flag78, ..@flag79 + dd ..@flag80, ..@flag81, ..@flag82, ..@flag83, ..@flag84, ..@flag85, ..@flag86, ..@flag87 + dd ..@flag88, ..@flag89, ..@flag90, ..@flag91, ..@flag92, ..@flag93, ..@flag94, ..@flag95 + dd ..@flag96, ..@flag97, ..@flag98, ..@flag99, ..@flag100, ..@flag101, ..@flag102, ..@flag103 + dd ..@flag104, ..@flag105, ..@flag106, ..@flag107, ..@flag108, ..@flag109, ..@flag110, ..@flag111 + dd ..@flag112, ..@flag113, ..@flag114, ..@flag115, ..@flag116, ..@flag117, ..@flag118, ..@flag119 + dd ..@flag120, ..@flag121, ..@flag122, ..@flag123, ..@flag124, ..@flag125, ..@flag126, ..@flag127 + dd ..@flag128, ..@flag129, ..@flag130, ..@flag131, ..@flag132, ..@flag133, ..@flag134, ..@flag135 + dd ..@flag136, ..@flag137, ..@flag138, ..@flag139, ..@flag140, ..@flag141, ..@flag142, ..@flag143 + dd ..@flag144, ..@flag145, ..@flag146, ..@flag147, ..@flag148, ..@flag149, ..@flag150, ..@flag151 + dd ..@flag152, ..@flag153, ..@flag154, ..@flag155, ..@flag156, ..@flag157, ..@flag158, ..@flag159 + dd ..@flag160, ..@flag161, ..@flag162, ..@flag163, ..@flag164, ..@flag165, ..@flag166, ..@flag167 + dd ..@flag168, ..@flag169, ..@flag170, ..@flag171, ..@flag172, ..@flag173, ..@flag174, ..@flag175 + dd ..@flag176, ..@flag177, ..@flag178, ..@flag179, ..@flag180, ..@flag181, ..@flag182, ..@flag183 + dd ..@flag184, ..@flag185, ..@flag186, ..@flag187, ..@flag188, ..@flag189, ..@flag190, ..@flag191 + dd ..@flag192, ..@flag193, ..@flag194, ..@flag195, ..@flag196, ..@flag197, ..@flag198, ..@flag199 + dd ..@flag200, ..@flag201, ..@flag202, ..@flag203, ..@flag204, ..@flag205, ..@flag206, ..@flag207 + dd ..@flag208, ..@flag209, ..@flag210, ..@flag211, ..@flag212, ..@flag213, ..@flag214, ..@flag215 + dd ..@flag216, ..@flag217, ..@flag218, ..@flag219, ..@flag220, ..@flag221, ..@flag222, ..@flag223 + dd ..@flag224, ..@flag225, ..@flag226, ..@flag227, ..@flag228, ..@flag229, ..@flag230, ..@flag231 + dd ..@flag232, ..@flag233, ..@flag234, ..@flag235, ..@flag236, ..@flag237, ..@flag238, ..@flag239 + dd ..@flag240, ..@flag241, ..@flag242, ..@flag243, ..@flag244, ..@flag245, ..@flag246, ..@flag247 + dd ..@flag248, ..@flag249, ..@flag250, ..@flag251, ..@flag252, ..@flag253, ..@flag254, ..@flag255 + +FuncTable2 + dd ..@cross0, ..@cross1, ..@cross2, ..@flag0, + dd ..@cross4, ..@flag0, ..@flag0, ..@flag0, + dd ..@cross8, ..@flag0, ..@flag0, ..@flag0, + dd ..@flag0, ..@flag0, ..@flag0, ..@flag0 + diff --git a/src/filters/hq/asm/hq3x_32.asm b/src/filters/hq/asm/hq3x_32.asm new file mode 100644 index 0000000..ff52eb2 --- /dev/null +++ b/src/filters/hq/asm/hq3x_32.asm @@ -0,0 +1,2575 @@ +;hq3x filter (thread-safe version) +;32bpp output +;---------------------------------------------------------- +;Copyright (C) 2003 MaxSt ( maxst@hiend3d.com ) +; +;This program is free software; you can redistribute it and/or +;modify it under the terms of the GNU General Public License +;as published by the Free Software Foundation; either +;version 2 of the License, or (at your option) any later +;version. +; +;This program is distributed in the hope that it will be useful, +;but WITHOUT ANY WARRANTY; without even the implied warranty of +;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;GNU General Public License for more details. +; +;You should have received a copy of the GNU General Public License +;along with this program; if not, write to the Free Software +;Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +%include "macros.mac" + +EXTSYM LUT16to32,RGBtoYUV + +SECTION .bss + +SECTION .data + +reg_blank dd 0,0 +const7 dd 0x00070007,0x00000007 +threshold dd 0x00300706,0x00000000 + +SECTION .text + +%macro TestDiff 2 + xor ecx,ecx + mov edx,[%1] + cmp edx,[%2] + je %%fin + mov ecx,RGBtoYUV + movd mm1,[ecx+edx*4] + movq mm5,mm1 + mov edx,[%2] + movd mm2,[ecx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd ecx,mm1 +%%fin: +%endmacro + +%macro DiffOrNot 4 + TestDiff %1,%2 + test ecx,ecx + jz %%same + %3 + jmp %%fin +%%same: + %4 +%%fin +%endmacro + +%macro DiffOrNot 6 + TestDiff %1,%2 + test ecx,ecx + jz %%same + %3 + %4 + jmp %%fin +%%same: + %5 + %6 +%%fin +%endmacro + +%macro DiffOrNot 8 + TestDiff %1,%2 + test ecx,ecx + jz %%same + %3 + %4 + %5 + jmp %%fin +%%same: + %6 + %7 + %8 +%%fin +%endmacro + +%macro DiffOrNot 10 + TestDiff %1,%2 + test ecx,ecx + jz %%same + %3 + %4 + %5 + %6 + jmp %%fin +%%same: + %7 + %8 + %9 + %10 +%%fin +%endmacro + +%macro Interp1 3 + mov edx,%2 + shl edx,2 + add edx,%3 + sub edx,%2 + shr edx,2 + mov %1,edx +%endmacro + +%macro Interp2 4 + mov edx,%2 + shl edx,1 + add edx,%3 + add edx,%4 + shr edx,2 + mov %1,edx +%endmacro + +%macro Interp3 2 + movd mm1, eax + movd mm2, %2 + punpcklbw mm1, [reg_blank] + punpcklbw mm2, [reg_blank] + pmullw mm1, [const7] + paddw mm1, mm2 + psrlw mm1, 3 + packuswb mm1, [reg_blank] + movd %1, mm1 +%endmacro + +%macro Interp4 3 + movd mm1, eax + movd mm2, %2 + movd mm3, %3 + punpcklbw mm1, [reg_blank] + punpcklbw mm2, [reg_blank] + punpcklbw mm3, [reg_blank] + psllw mm1, 1 + paddw mm2, mm3 + pmullw mm2, [const7] + paddw mm1, mm2 + psrlw mm1, 4 + packuswb mm1, [reg_blank] + movd %1, mm1 +%endmacro + +%macro Interp5 3 + mov edx,%2 + add edx,%3 + shr edx,1 + mov %1,edx +%endmacro + +%macro PIXEL00_1M 0 + Interp1 [edi],eax,dword[ebp-c1] +%endmacro + +%macro PIXEL00_1U 0 + Interp1 [edi],eax,dword[ebp-c2] +%endmacro + +%macro PIXEL00_1L 0 + Interp1 [edi],eax,dword[ebp-c4] +%endmacro + +%macro PIXEL00_2 0 + Interp2 [edi],eax,dword[ebp-c4],dword[ebp-c2] +%endmacro + +%macro PIXEL00_4 0 + Interp4 [edi],dword[ebp-c4],dword[ebp-c2] +%endmacro + +%macro PIXEL00_5 0 + Interp5 [edi],dword[ebp-c4],dword[ebp-c2] +%endmacro + +%macro PIXEL00_C 0 + mov [edi],eax +%endmacro + +%macro PIXEL01_1 0 + Interp1 [edi+4],eax,dword[ebp-c2] +%endmacro + +%macro PIXEL01_3 0 + Interp3 [edi+4],dword[ebp-c2] +%endmacro + +%macro PIXEL01_6 0 + Interp1 [edi+4],dword[ebp-c2],eax +%endmacro + +%macro PIXEL01_C 0 + mov [edi+4],eax +%endmacro + +%macro PIXEL02_1M 0 + Interp1 [edi+8],eax,dword[ebp-c3] +%endmacro + +%macro PIXEL02_1U 0 + Interp1 [edi+8],eax,dword[ebp-c2] +%endmacro + +%macro PIXEL02_1R 0 + Interp1 [edi+8],eax,dword[ebp-c6] +%endmacro + +%macro PIXEL02_2 0 + Interp2 [edi+8],eax,dword[ebp-c2],dword[ebp-c6] +%endmacro + +%macro PIXEL02_4 0 + Interp4 [edi+8],dword[ebp-c2],dword[ebp-c6] +%endmacro + +%macro PIXEL02_5 0 + Interp5 [edi+8],dword[ebp-c2],dword[ebp-c6] +%endmacro + +%macro PIXEL02_C 0 + mov [edi+8],eax +%endmacro + +%macro PIXEL10_1 0 + Interp1 [edi+ebx],eax,dword[ebp-c4] +%endmacro + +%macro PIXEL10_3 0 + Interp3 [edi+ebx],dword[ebp-c4] +%endmacro + +%macro PIXEL10_6 0 + Interp1 [edi+ebx],dword[ebp-c4],eax +%endmacro + +%macro PIXEL10_C 0 + mov [edi+ebx],eax +%endmacro + +%macro PIXEL11 0 + mov [edi+ebx+4],eax +%endmacro + +%macro PIXEL12_1 0 + Interp1 [edi+ebx+8],eax,dword[ebp-c6] +%endmacro + +%macro PIXEL12_3 0 + Interp3 [edi+ebx+8],dword[ebp-c6] +%endmacro + +%macro PIXEL12_6 0 + Interp1 [edi+ebx+8],dword[ebp-c6],eax +%endmacro + +%macro PIXEL12_C 0 + mov [edi+ebx+8],eax +%endmacro + +%macro PIXEL20_1M 0 + Interp1 [edi+ebx*2],eax,dword[ebp-c7] +%endmacro + +%macro PIXEL20_1D 0 + Interp1 [edi+ebx*2],eax,dword[ebp-c8] +%endmacro + +%macro PIXEL20_1L 0 + Interp1 [edi+ebx*2],eax,dword[ebp-c4] +%endmacro + +%macro PIXEL20_2 0 + Interp2 [edi+ebx*2],eax,dword[ebp-c8],dword[ebp-c4] +%endmacro + +%macro PIXEL20_4 0 + Interp4 [edi+ebx*2],dword[ebp-c8],dword[ebp-c4] +%endmacro + +%macro PIXEL20_5 0 + Interp5 [edi+ebx*2],dword[ebp-c8],dword[ebp-c4] +%endmacro + +%macro PIXEL20_C 0 + mov [edi+ebx*2],eax +%endmacro + +%macro PIXEL21_1 0 + Interp1 [edi+ebx*2+4],eax,dword[ebp-c8] +%endmacro + +%macro PIXEL21_3 0 + Interp3 [edi+ebx*2+4],dword[ebp-c8] +%endmacro + +%macro PIXEL21_6 0 + Interp1 [edi+ebx*2+4],dword[ebp-c8],eax +%endmacro + +%macro PIXEL21_C 0 + mov [edi+ebx*2+4],eax +%endmacro + +%macro PIXEL22_1M 0 + Interp1 [edi+ebx*2+8],eax,dword[ebp-c9] +%endmacro + +%macro PIXEL22_1D 0 + Interp1 [edi+ebx*2+8],eax,dword[ebp-c8] +%endmacro + +%macro PIXEL22_1R 0 + Interp1 [edi+ebx*2+8],eax,dword[ebp-c6] +%endmacro + +%macro PIXEL22_2 0 + Interp2 [edi+ebx*2+8],eax,dword[ebp-c6],dword[ebp-c8] +%endmacro + +%macro PIXEL22_4 0 + Interp4 [edi+ebx*2+8],dword[ebp-c6],dword[ebp-c8] +%endmacro + +%macro PIXEL22_5 0 + Interp5 [edi+ebx*2+8],dword[ebp-c6],dword[ebp-c8] +%endmacro + +%macro PIXEL22_C 0 + mov [edi+ebx*2+8],eax +%endmacro + +inbuffer equ 8 +outbuffer equ 12 +Xres equ 16 +Yres equ 20 +pitch equ 24 +offset equ 28 + +linesleft equ 4 +xcounter equ 8 +cross equ 12 +nextline equ 16 +prevline equ 20 +w1 equ 24 +w2 equ 28 +w3 equ 32 +w4 equ 36 +w5 equ 40 +w6 equ 44 +w7 equ 48 +w8 equ 52 +w9 equ 56 +c1 equ 60 +c2 equ 64 +c3 equ 68 +c4 equ 72 +c5 equ 76 +c6 equ 80 +c7 equ 84 +c8 equ 88 +c9 equ 92 +localsize equ 92 + +NEWSYM hq3x_32 + push ebp + mov ebp,esp + sub esp, localsize + pushad + + mov esi,[ebp+inbuffer] + mov edi,[ebp+outbuffer] + mov edx,[ebp+Yres] + mov [ebp-linesleft],edx + mov ebx,[ebp+Xres] + shl ebx,1 + mov dword[ebp-prevline],0 + mov eax, ebx + add eax, [ebp+offset] + mov dword[ebp-nextline],eax +.loopy + mov ecx,[ebp+Xres] + sub ecx,2 ; x={Xres-2, Xres-1} are special cases. + mov dword[ebp-xcounter],ecx + ; x=0 - special case + mov ebx,[ebp-prevline] + movq mm5,[esi+ebx] + movq mm6,[esi] + mov ebx,[ebp-nextline] + movq mm7,[esi+ebx] + movd eax,mm5 + movzx edx,ax + mov [ebp-w1],edx + mov [ebp-w2],edx + shr eax,16 + mov [ebp-w3],eax + movd eax,mm6 + movzx edx,ax + mov [ebp-w4],edx + mov [ebp-w5],edx + shr eax,16 + mov [ebp-w6],eax + movd eax,mm7 + movzx edx,ax + mov [ebp-w7],edx + mov [ebp-w8],edx + shr eax,16 + mov [ebp-w9],eax + jmp .flags +.loopx + mov ebx,[ebp-prevline] + movq mm5,[esi+ebx-2] + movq mm6,[esi-2] + mov ebx,[ebp-nextline] + movq mm7,[esi+ebx-2] + movd eax,mm5 + movzx edx,ax + mov [ebp-w1],edx + shr eax,16 + mov [ebp-w2],eax + psrlq mm5,32 + movd eax,mm5 + movzx edx,ax + mov [ebp-w3],edx + movd eax,mm6 + movzx edx,ax + mov [ebp-w4],edx + shr eax,16 + mov [ebp-w5],eax + psrlq mm6,32 + movd eax,mm6 + movzx edx,ax + mov [ebp-w6],edx + movd eax,mm7 + movzx edx,ax + mov [ebp-w7],edx + shr eax,16 + mov [ebp-w8],eax + psrlq mm7,32 + movd eax,mm7 + movzx edx,ax + mov [ebp-w9],edx +.flags + mov ebx,RGBtoYUV + mov eax,[ebp-w5] + xor ecx,ecx + movd mm5,[ebx+eax*4] + mov dword[ebp-cross],0 + + mov edx,[ebp-w2] + cmp eax,edx + je .noflag2 + or dword[ebp-cross],1 + movq mm1,mm5 + movd mm2,[ebx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 + test edx,edx + jz .noflag2 + or ecx,2 +.noflag2 + mov edx,[ebp-w4] + cmp eax,edx + je .noflag4 + or dword[ebp-cross],2 + movq mm1,mm5 + movd mm2,[ebx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 + test edx,edx + jz .noflag4 + or ecx,8 +.noflag4 + mov edx,[ebp-w6] + cmp eax,edx + je .noflag6 + or dword[ebp-cross],4 + movq mm1,mm5 + movd mm2,[ebx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 + test edx,edx + jz .noflag6 + or ecx,16 +.noflag6 + mov edx,[ebp-w8] + cmp eax,edx + je .noflag8 + or dword[ebp-cross],8 + movq mm1,mm5 + movd mm2,[ebx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 + test edx,edx + jz .noflag8 + or ecx,64 +.noflag8 + test ecx,ecx + jnz .testflag1 + mov ecx,[ebp-cross] + mov ebx,LUT16to32 + mov eax,[ebx+eax*4] + jmp [FuncTable2+ecx*4] +.testflag1 + mov edx,[ebp-w1] + cmp eax,edx + je .noflag1 + movq mm1,mm5 + movd mm2,[ebx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 + test edx,edx + jz .noflag1 + or ecx,1 +.noflag1 + mov edx,[ebp-w3] + cmp eax,edx + je .noflag3 + movq mm1,mm5 + movd mm2,[ebx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 + test edx,edx + jz .noflag3 + or ecx,4 +.noflag3 + mov edx,[ebp-w7] + cmp eax,edx + je .noflag7 + movq mm1,mm5 + movd mm2,[ebx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 + test edx,edx + jz .noflag7 + or ecx,32 +.noflag7 + mov edx,[ebp-w9] + cmp eax,edx + je .noflag9 + movq mm1,mm5 + movd mm2,[ebx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 + test edx,edx + jz .noflag9 + or ecx,128 +.noflag9 + mov ebx,LUT16to32 + mov eax,[ebx+eax*4] + mov edx,[ebp-w2] + mov edx,[ebx+edx*4] + mov [ebp-c2],edx + mov edx,[ebp-w4] + mov edx,[ebx+edx*4] + mov [ebp-c4],edx + mov edx,[ebp-w6] + mov edx,[ebx+edx*4] + mov [ebp-c6],edx + mov edx,[ebp-w8] + mov edx,[ebx+edx*4] + mov [ebp-c8],edx + test ecx,0x005A + jz .switch + mov edx,[ebp-w1] + mov edx,[ebx+edx*4] + mov [ebp-c1],edx + mov edx,[ebp-w3] + mov edx,[ebx+edx*4] + mov [ebp-c3],edx + mov edx,[ebp-w7] + mov edx,[ebx+edx*4] + mov [ebp-c7],edx + mov edx,[ebp-w9] + mov edx,[ebx+edx*4] + mov [ebp-c9],edx +.switch + mov ebx,[ebp+pitch] + jmp [FuncTable+ecx*4] + +..@flag0 +..@flag1 +..@flag4 +..@flag32 +..@flag128 +..@flag5 +..@flag132 +..@flag160 +..@flag33 +..@flag129 +..@flag36 +..@flag133 +..@flag164 +..@flag161 +..@flag37 +..@flag165 +; PIXEL00_2 +; PIXEL01_1 +; PIXEL02_2 +; PIXEL10_1 +; PIXEL11 +; PIXEL12_1 +; PIXEL20_2 +; PIXEL21_1 +; PIXEL22_2 + +; the same, only optimized + mov ecx,eax + shl ecx,1 + add ecx,[ebp-c2] + mov edx,ecx + add edx,[ebp-c4] + shr edx,2 + mov [edi],edx + mov edx,ecx + add edx,eax + shr edx,2 + mov [edi+4],edx + add ecx,[ebp-c6] + shr ecx,2 + mov [edi+8],ecx + mov ecx,eax + shl ecx,2 + sub ecx,eax + mov edx,ecx + add edx,[ebp-c4] + shr edx,2 + mov [edi+ebx],edx + mov [edi+ebx+4],eax + add ecx,[ebp-c6] + shr ecx,2 + mov [edi+ebx+8],ecx + mov ecx,eax + shl ecx,1 + add ecx,[ebp-c8] + mov edx,ecx + add edx,[ebp-c4] + shr edx,2 + mov [edi+ebx*2],edx + mov edx,ecx + add edx,eax + shr edx,2 + mov [edi+ebx*2+4],edx + add ecx,[ebp-c6] + shr ecx,2 + mov [edi+ebx*2+8],ecx + jmp .loopx_end +..@flag2 +..@flag34 +..@flag130 +..@flag162 + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag16 +..@flag17 +..@flag48 +..@flag49 + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag64 +..@flag65 +..@flag68 +..@flag69 + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag8 +..@flag12 +..@flag136 +..@flag140 + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag3 +..@flag35 +..@flag131 +..@flag163 + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag6 +..@flag38 +..@flag134 +..@flag166 + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag20 +..@flag21 +..@flag52 +..@flag53 + PIXEL00_2 + PIXEL01_1 + PIXEL02_1U + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag144 +..@flag145 +..@flag176 +..@flag177 + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag192 +..@flag193 +..@flag196 +..@flag197 + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag96 +..@flag97 +..@flag100 +..@flag101 + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag40 +..@flag44 +..@flag168 +..@flag172 + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1D + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag9 +..@flag13 +..@flag137 +..@flag141 + PIXEL00_1U + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag18 +..@flag50 + PIXEL00_1M + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_1M,PIXEL12_C,PIXEL01_3,PIXEL02_4,PIXEL12_3 + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag80 +..@flag81 + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL20_1M + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL21_C,PIXEL22_1M,PIXEL12_3,PIXEL21_3,PIXEL22_4 + jmp .loopx_end +..@flag72 +..@flag76 + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_1M,PIXEL21_C,PIXEL10_3,PIXEL20_4,PIXEL21_3 + PIXEL22_1M + jmp .loopx_end +..@flag10 +..@flag138 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_1M,PIXEL01_C,PIXEL10_C,PIXEL00_4,PIXEL01_3,PIXEL10_3 + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag66 + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag24 + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag7 +..@flag39 +..@flag135 + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag148 +..@flag149 +..@flag180 + PIXEL00_2 + PIXEL01_1 + PIXEL02_1U + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag224 +..@flag228 +..@flag225 + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag41 +..@flag169 +..@flag45 + PIXEL00_1U + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1D + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag22 +..@flag54 + PIXEL00_1M + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_C,PIXEL12_C,PIXEL01_3,PIXEL02_4,PIXEL12_3 + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag208 +..@flag209 + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL20_1M + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL21_C,PIXEL22_C,PIXEL12_3,PIXEL21_3,PIXEL22_4 + jmp .loopx_end +..@flag104 +..@flag108 + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_C,PIXEL21_C,PIXEL10_3,PIXEL20_4,PIXEL21_3 + PIXEL22_1M + jmp .loopx_end +..@flag11 +..@flag139 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL10_C,PIXEL00_4,PIXEL01_3,PIXEL10_3 + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag19 +..@flag51 + DiffOrNot ebp-w2,ebp-w6,PIXEL00_1L,PIXEL01_C,PIXEL02_1M,PIXEL12_C,PIXEL00_2,PIXEL01_6,PIXEL02_5,PIXEL12_1 + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag146 +..@flag178 + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_1M,PIXEL12_C,PIXEL22_1D,PIXEL01_1,PIXEL02_5,PIXEL12_6,PIXEL22_2 + PIXEL00_1M + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + jmp .loopx_end +..@flag84 +..@flag85 + DiffOrNot ebp-w6,ebp-w8,PIXEL02_1U,PIXEL12_C,PIXEL21_C,PIXEL22_1M,PIXEL02_2,PIXEL12_6,PIXEL21_1,PIXEL22_5 + PIXEL00_2 + PIXEL01_1 + PIXEL10_1 + PIXEL11 + PIXEL20_1M + jmp .loopx_end +..@flag112 +..@flag113 + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL20_1L,PIXEL21_C,PIXEL22_1M,PIXEL12_1,PIXEL20_2,PIXEL21_6,PIXEL22_5 + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + jmp .loopx_end +..@flag200 +..@flag204 + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_1M,PIXEL21_C,PIXEL22_1R,PIXEL10_1,PIXEL20_5,PIXEL21_6,PIXEL22_2 + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + jmp .loopx_end +..@flag73 +..@flag77 + DiffOrNot ebp-w8,ebp-w4,PIXEL00_1U,PIXEL10_C,PIXEL20_1M,PIXEL21_C,PIXEL00_2,PIXEL10_6,PIXEL20_5,PIXEL21_1 + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + PIXEL22_1M + jmp .loopx_end +..@flag42 +..@flag170 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_1M,PIXEL01_C,PIXEL10_C,PIXEL20_1D,PIXEL00_5,PIXEL01_1,PIXEL10_6,PIXEL20_2 + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag14 +..@flag142 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_1M,PIXEL01_C,PIXEL02_1R,PIXEL10_C,PIXEL00_5,PIXEL01_6,PIXEL02_2,PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag67 + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag70 + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag28 + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag152 + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag194 + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag98 + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag56 + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag25 + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag26 +..@flag31 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL10_C,PIXEL00_4,PIXEL10_3 + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_C,PIXEL12_C,PIXEL02_4,PIXEL12_3 + PIXEL11 + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag82 +..@flag214 + PIXEL00_1M + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_C,PIXEL01_3,PIXEL02_4 + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1M + DiffOrNot ebp-w6,ebp-w8,PIXEL21_C,PIXEL22_C,PIXEL21_3,PIXEL22_4 + jmp .loopx_end +..@flag88 +..@flag248 + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL11 + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_C,PIXEL10_3,PIXEL20_4 + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL22_C,PIXEL12_3,PIXEL22_4 + jmp .loopx_end +..@flag74 +..@flag107 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL00_4,PIXEL01_3 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_1 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_C,PIXEL21_C,PIXEL20_4,PIXEL21_3 + PIXEL22_1M + jmp .loopx_end +..@flag27 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL10_C,PIXEL00_4,PIXEL01_3,PIXEL10_3 + PIXEL02_1M + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag86 + PIXEL00_1M + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_C,PIXEL12_C,PIXEL01_3,PIXEL02_4,PIXEL12_3 + PIXEL10_1 + PIXEL11 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag216 + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL20_1M + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL21_C,PIXEL22_C,PIXEL12_3,PIXEL21_3,PIXEL22_4 + jmp .loopx_end +..@flag106 + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL11 + PIXEL12_1 + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_C,PIXEL21_C,PIXEL10_3,PIXEL20_4,PIXEL21_3 + PIXEL22_1M + jmp .loopx_end +..@flag30 + PIXEL00_1M + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_C,PIXEL12_C,PIXEL01_3,PIXEL02_4,PIXEL12_3 + PIXEL10_C + PIXEL11 + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag210 + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL20_1M + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL21_C,PIXEL22_C,PIXEL12_3,PIXEL21_3,PIXEL22_4 + jmp .loopx_end +..@flag120 + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL11 + PIXEL12_C + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_C,PIXEL21_C,PIXEL10_3,PIXEL20_4,PIXEL21_3 + PIXEL22_1M + jmp .loopx_end +..@flag75 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL10_C,PIXEL00_4,PIXEL01_3,PIXEL10_3 + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag29 + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag198 + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag184 + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag99 + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag57 + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag71 + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag156 + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag226 + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag60 + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag195 + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag102 + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag153 + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag58 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_1M,PIXEL00_2 + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_1M,PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag83 + PIXEL00_1L + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_1M,PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_1M,PIXEL22_2 + jmp .loopx_end +..@flag92 + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + DiffOrNot ebp-w8,ebp-w4,PIXEL20_1M,PIXEL20_2 + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_1M,PIXEL22_2 + jmp .loopx_end +..@flag202 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_1M,PIXEL00_2 + PIXEL01_C + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_1 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_1M,PIXEL20_2 + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag78 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_1M,PIXEL00_2 + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_1M,PIXEL20_2 + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag154 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_1M,PIXEL00_2 + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_1M,PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag114 + PIXEL00_1M + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_1M,PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_1M,PIXEL22_2 + jmp .loopx_end +..@flag89 + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + DiffOrNot ebp-w8,ebp-w4,PIXEL20_1M,PIXEL20_2 + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_1M,PIXEL22_2 + jmp .loopx_end +..@flag90 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_1M,PIXEL00_2 + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_1M,PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_C + DiffOrNot ebp-w8,ebp-w4,PIXEL20_1M,PIXEL20_2 + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_1M,PIXEL22_2 + jmp .loopx_end +..@flag55 +..@flag23 + DiffOrNot ebp-w2,ebp-w6,PIXEL00_1L,PIXEL01_C,PIXEL02_C,PIXEL12_C,PIXEL00_2,PIXEL01_6,PIXEL02_5,PIXEL12_1 + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag182 +..@flag150 + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_C,PIXEL12_C,PIXEL22_1D,PIXEL01_1,PIXEL02_5,PIXEL12_6,PIXEL22_2 + PIXEL00_1M + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + jmp .loopx_end +..@flag213 +..@flag212 + DiffOrNot ebp-w6,ebp-w8,PIXEL02_1U,PIXEL12_C,PIXEL21_C,PIXEL22_C,PIXEL02_2,PIXEL12_6,PIXEL21_1,PIXEL22_5 + PIXEL00_2 + PIXEL01_1 + PIXEL10_1 + PIXEL11 + PIXEL20_1M + jmp .loopx_end +..@flag241 +..@flag240 + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL20_1L,PIXEL21_C,PIXEL22_C,PIXEL12_1,PIXEL20_2,PIXEL21_6,PIXEL22_5 + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + jmp .loopx_end +..@flag236 +..@flag232 + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_C,PIXEL21_C,PIXEL22_1R,PIXEL10_1,PIXEL20_5,PIXEL21_6,PIXEL22_2 + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + jmp .loopx_end +..@flag109 +..@flag105 + DiffOrNot ebp-w8,ebp-w4,PIXEL00_1U,PIXEL10_C,PIXEL20_C,PIXEL21_C,PIXEL00_2,PIXEL10_6,PIXEL20_5,PIXEL21_1 + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + PIXEL22_1M + jmp .loopx_end +..@flag171 +..@flag43 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL10_C,PIXEL20_1D,PIXEL00_5,PIXEL01_1,PIXEL10_6,PIXEL20_2 + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag143 +..@flag15 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL02_1R,PIXEL10_C,PIXEL00_5,PIXEL01_6,PIXEL02_2,PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag124 + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL11 + PIXEL12_C + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_C,PIXEL21_C,PIXEL10_3,PIXEL20_4,PIXEL21_3 + PIXEL22_1M + jmp .loopx_end +..@flag203 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL10_C,PIXEL00_4,PIXEL01_3,PIXEL10_3 + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag62 + PIXEL00_1M + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_C,PIXEL12_C,PIXEL01_3,PIXEL02_4,PIXEL12_3 + PIXEL10_C + PIXEL11 + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag211 + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL20_1M + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL21_C,PIXEL22_C,PIXEL12_3,PIXEL21_3,PIXEL22_4 + jmp .loopx_end +..@flag118 + PIXEL00_1M + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_C,PIXEL12_C,PIXEL01_3,PIXEL02_4,PIXEL12_3 + PIXEL10_1 + PIXEL11 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag217 + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL20_1M + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL21_C,PIXEL22_C,PIXEL12_3,PIXEL21_3,PIXEL22_4 + jmp .loopx_end +..@flag110 + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL11 + PIXEL12_1 + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_C,PIXEL21_C,PIXEL10_3,PIXEL20_4,PIXEL21_3 + PIXEL22_1M + jmp .loopx_end +..@flag155 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL10_C,PIXEL00_4,PIXEL01_3,PIXEL10_3 + PIXEL02_1M + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag188 + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag185 + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag61 + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag157 + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag103 + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag227 + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag230 + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag199 + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag220 + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_1M,PIXEL20_2 + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL21_C,PIXEL22_C,PIXEL12_3,PIXEL21_3,PIXEL22_4 + jmp .loopx_end +..@flag158 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_1M,PIXEL00_2 + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_C,PIXEL12_C,PIXEL01_3,PIXEL02_4,PIXEL12_3 + PIXEL10_C + PIXEL11 + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag234 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_1M,PIXEL00_2 + PIXEL01_C + PIXEL02_1M + PIXEL11 + PIXEL12_1 + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_C,PIXEL21_C,PIXEL10_3,PIXEL20_4,PIXEL21_3 + PIXEL22_1R + jmp .loopx_end +..@flag242 + PIXEL00_1M + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_1M,PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL20_1L + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL21_C,PIXEL22_C,PIXEL12_3,PIXEL21_3,PIXEL22_4 + jmp .loopx_end +..@flag59 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL10_C,PIXEL00_4,PIXEL01_3,PIXEL10_3 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_1M,PIXEL02_2 + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag121 + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL11 + PIXEL12_C + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_C,PIXEL21_C,PIXEL10_3,PIXEL20_4,PIXEL21_3 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_1M,PIXEL22_2 + jmp .loopx_end +..@flag87 + PIXEL00_1L + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_C,PIXEL12_C,PIXEL01_3,PIXEL02_4,PIXEL12_3 + PIXEL10_1 + PIXEL11 + PIXEL20_1M + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_1M,PIXEL22_2 + jmp .loopx_end +..@flag79 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL10_C,PIXEL00_4,PIXEL01_3,PIXEL10_3 + PIXEL02_1R + PIXEL11 + PIXEL12_1 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_1M,PIXEL20_2 + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag122 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_1M,PIXEL00_2 + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_1M,PIXEL02_2 + PIXEL11 + PIXEL12_C + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_C,PIXEL21_C,PIXEL10_3,PIXEL20_4,PIXEL21_3 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_1M,PIXEL22_2 + jmp .loopx_end +..@flag94 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_1M,PIXEL00_2 + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_C,PIXEL12_C,PIXEL01_3,PIXEL02_4,PIXEL12_3 + PIXEL10_C + PIXEL11 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_1M,PIXEL20_2 + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_1M,PIXEL22_2 + jmp .loopx_end +..@flag218 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_1M,PIXEL00_2 + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_1M,PIXEL02_2 + PIXEL10_C + PIXEL11 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_1M,PIXEL20_2 + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL21_C,PIXEL22_C,PIXEL12_3,PIXEL21_3,PIXEL22_4 + jmp .loopx_end +..@flag91 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL10_C,PIXEL00_4,PIXEL01_3,PIXEL10_3 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_1M,PIXEL02_2 + PIXEL11 + PIXEL12_C + DiffOrNot ebp-w8,ebp-w4,PIXEL20_1M,PIXEL20_2 + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_1M,PIXEL22_2 + jmp .loopx_end +..@flag229 + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag167 + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag173 + PIXEL00_1U + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1D + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag181 + PIXEL00_2 + PIXEL01_1 + PIXEL02_1U + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag186 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_1M,PIXEL00_2 + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_1M,PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag115 + PIXEL00_1L + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_1M,PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_1M,PIXEL22_2 + jmp .loopx_end +..@flag93 + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + DiffOrNot ebp-w8,ebp-w4,PIXEL20_1M,PIXEL20_2 + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_1M,PIXEL22_2 + jmp .loopx_end +..@flag206 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_1M,PIXEL00_2 + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_1M,PIXEL20_2 + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag205 +..@flag201 + PIXEL00_1U + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_1M,PIXEL20_2 + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag174 +..@flag46 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_1M,PIXEL00_2 + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1D + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag179 +..@flag147 + PIXEL00_1L + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_1M,PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag117 +..@flag116 + PIXEL00_2 + PIXEL01_1 + PIXEL02_1U + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_1M,PIXEL22_2 + jmp .loopx_end +..@flag189 + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag231 + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag126 + PIXEL00_1M + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_C,PIXEL12_C,PIXEL01_3,PIXEL02_4,PIXEL12_3 + PIXEL11 + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_C,PIXEL21_C,PIXEL10_3,PIXEL20_4,PIXEL21_3 + PIXEL22_1M + jmp .loopx_end +..@flag219 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL10_C,PIXEL00_4,PIXEL01_3,PIXEL10_3 + PIXEL02_1M + PIXEL11 + PIXEL20_1M + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL21_C,PIXEL22_C,PIXEL12_3,PIXEL21_3,PIXEL22_4 + jmp .loopx_end +..@flag125 + DiffOrNot ebp-w8,ebp-w4,PIXEL00_1U,PIXEL10_C,PIXEL20_C,PIXEL21_C,PIXEL00_2,PIXEL10_6,PIXEL20_5,PIXEL21_1 + PIXEL01_1 + PIXEL02_1U + PIXEL11 + PIXEL12_C + PIXEL22_1M + jmp .loopx_end +..@flag221 + DiffOrNot ebp-w6,ebp-w8,PIXEL02_1U,PIXEL12_C,PIXEL21_C,PIXEL22_C,PIXEL02_2,PIXEL12_6,PIXEL21_1,PIXEL22_5 + PIXEL00_1U + PIXEL01_1 + PIXEL10_C + PIXEL11 + PIXEL20_1M + jmp .loopx_end +..@flag207 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL02_1R,PIXEL10_C,PIXEL00_5,PIXEL01_6,PIXEL02_2,PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag238 + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_C,PIXEL21_C,PIXEL22_1R,PIXEL10_1,PIXEL20_5,PIXEL21_6,PIXEL22_2 + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL11 + PIXEL12_1 + jmp .loopx_end +..@flag190 + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_C,PIXEL12_C,PIXEL22_1D,PIXEL01_1,PIXEL02_5,PIXEL12_6,PIXEL22_2 + PIXEL00_1M + PIXEL10_C + PIXEL11 + PIXEL20_1D + PIXEL21_1 + jmp .loopx_end +..@flag187 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL10_C,PIXEL20_1D,PIXEL00_5,PIXEL01_1,PIXEL10_6,PIXEL20_2 + PIXEL02_1M + PIXEL11 + PIXEL12_C + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag243 + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL20_1L,PIXEL21_C,PIXEL22_C,PIXEL12_1,PIXEL20_2,PIXEL21_6,PIXEL22_5 + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + jmp .loopx_end +..@flag119 + DiffOrNot ebp-w2,ebp-w6,PIXEL00_1L,PIXEL01_C,PIXEL02_C,PIXEL12_C,PIXEL00_2,PIXEL01_6,PIXEL02_5,PIXEL12_1 + PIXEL10_1 + PIXEL11 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag237 +..@flag233 + PIXEL00_1U + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_C,PIXEL20_2 + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag175 +..@flag47 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL00_2 + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1D + PIXEL21_1 + PIXEL22_2 + jmp .loopx_end +..@flag183 +..@flag151 + PIXEL00_1L + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_C,PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag245 +..@flag244 + PIXEL00_2 + PIXEL01_1 + PIXEL02_1U + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_C,PIXEL22_2 + jmp .loopx_end +..@flag250 + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL11 + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_C,PIXEL10_3,PIXEL20_4 + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL22_C,PIXEL12_3,PIXEL22_4 + jmp .loopx_end +..@flag123 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL00_4,PIXEL01_3 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + DiffOrNot ebp-w8,ebp-w4,PIXEL20_C,PIXEL21_C,PIXEL20_4,PIXEL21_3 + PIXEL22_1M + jmp .loopx_end +..@flag95 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL10_C,PIXEL00_4,PIXEL10_3 + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_C,PIXEL12_C,PIXEL02_4,PIXEL12_3 + PIXEL11 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + jmp .loopx_end +..@flag222 + PIXEL00_1M + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_C,PIXEL01_3,PIXEL02_4 + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + DiffOrNot ebp-w6,ebp-w8,PIXEL21_C,PIXEL22_C,PIXEL21_3,PIXEL22_4 + jmp .loopx_end +..@flag252 + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL11 + PIXEL12_C + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_C,PIXEL10_3,PIXEL20_4 + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_C,PIXEL22_2 + jmp .loopx_end +..@flag249 + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_C,PIXEL20_2 + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL22_C,PIXEL12_3,PIXEL22_4 + jmp .loopx_end +..@flag235 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL00_4,PIXEL01_3 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_1 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_C,PIXEL20_2 + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag111 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL00_2 + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_C,PIXEL21_C,PIXEL20_4,PIXEL21_3 + PIXEL22_1M + jmp .loopx_end +..@flag63 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL00_2 + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_C,PIXEL12_C,PIXEL02_4,PIXEL12_3 + PIXEL10_C + PIXEL11 + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + jmp .loopx_end +..@flag159 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL10_C,PIXEL00_4,PIXEL10_3 + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_C,PIXEL02_2 + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag215 + PIXEL00_1L + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_C,PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1M + DiffOrNot ebp-w6,ebp-w8,PIXEL21_C,PIXEL22_C,PIXEL21_3,PIXEL22_4 + jmp .loopx_end +..@flag246 + PIXEL00_1M + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_C,PIXEL01_3,PIXEL02_4 + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_C,PIXEL22_2 + jmp .loopx_end +..@flag254 + PIXEL00_1M + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_C,PIXEL01_3,PIXEL02_4 + PIXEL11 + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_C,PIXEL10_3,PIXEL20_4 + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL21_C,PIXEL22_C,PIXEL12_3,PIXEL21_3,PIXEL22_2 + jmp .loopx_end +..@flag253 + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + DiffOrNot ebp-w8,ebp-w4,PIXEL20_C,PIXEL20_2 + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_C,PIXEL22_2 + jmp .loopx_end +..@flag251 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL00_4,PIXEL01_3 + PIXEL02_1M + PIXEL11 + DiffOrNot ebp-w8,ebp-w4,PIXEL10_C,PIXEL20_C,PIXEL21_C,PIXEL10_3,PIXEL20_2,PIXEL21_3 + DiffOrNot ebp-w6,ebp-w8,PIXEL12_C,PIXEL22_C,PIXEL12_3,PIXEL22_4 + jmp .loopx_end +..@flag239 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL00_2 + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_C,PIXEL20_2 + PIXEL21_C + PIXEL22_1R + jmp .loopx_end +..@flag127 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL01_C,PIXEL10_C,PIXEL00_2,PIXEL01_3,PIXEL10_3 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_C,PIXEL12_C,PIXEL02_4,PIXEL12_3 + PIXEL11 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_C,PIXEL21_C,PIXEL20_4,PIXEL21_3 + PIXEL22_1M + jmp .loopx_end +..@flag191 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL00_2 + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_C,PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + jmp .loopx_end +..@flag223 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL10_C,PIXEL00_4,PIXEL10_3 + DiffOrNot ebp-w2,ebp-w6,PIXEL01_C,PIXEL02_C,PIXEL12_C,PIXEL01_3,PIXEL02_2,PIXEL12_3 + PIXEL11 + PIXEL20_1M + DiffOrNot ebp-w6,ebp-w8,PIXEL21_C,PIXEL22_C,PIXEL21_3,PIXEL22_4 + jmp .loopx_end +..@flag247 + PIXEL00_1L + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_C,PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_C,PIXEL22_2 + jmp .loopx_end +..@flag255 + DiffOrNot ebp-w4,ebp-w2,PIXEL00_C,PIXEL00_2 + PIXEL01_C + DiffOrNot ebp-w2,ebp-w6,PIXEL02_C,PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_C + DiffOrNot ebp-w8,ebp-w4,PIXEL20_C,PIXEL20_2 + PIXEL21_C + DiffOrNot ebp-w6,ebp-w8,PIXEL22_C,PIXEL22_2 + jmp .loopx_end + +..@cross0 + mov ebx,[ebp+pitch] + mov [edi],eax + mov [edi+4],eax + mov [edi+8],eax + mov [edi+ebx],eax + mov [edi+ebx+4],eax + mov [edi+ebx+8],eax + mov [edi+ebx*2],eax + mov [edi+ebx*2+4],eax + mov [edi+ebx*2+8],eax + jmp .loopx_end +..@cross1 + mov ecx,[ebp-w2] + mov edx,eax + shl edx,2 + add edx,[ebx+ecx*4] + sub edx,eax + shr edx,2 + mov ebx,[ebp+pitch] + mov [edi],edx + mov [edi+4],edx + mov [edi+8],edx + mov [edi+ebx],eax + mov [edi+ebx+4],eax + mov [edi+ebx+8],eax + mov [edi+ebx*2],eax + mov [edi+ebx*2+4],eax + mov [edi+ebx*2+8],eax + jmp .loopx_end +..@cross2 + mov ecx,[ebp-w4] + mov edx,eax + shl edx,2 + add edx,[ebx+ecx*4] + sub edx,eax + shr edx,2 + mov ebx,[ebp+pitch] + mov [edi],edx + mov [edi+4],eax + mov [edi+8],eax + mov [edi+ebx],edx + mov [edi+ebx+4],eax + mov [edi+ebx+8],eax + mov [edi+ebx*2],edx + mov [edi+ebx*2+4],eax + mov [edi+ebx*2+8],eax + jmp .loopx_end +..@cross4 + mov ecx,[ebp-w6] + mov edx,eax + shl edx,2 + add edx,[ebx+ecx*4] + sub edx,eax + shr edx,2 + mov ebx,[ebp+pitch] + mov [edi],eax + mov [edi+4],eax + mov [edi+8],edx + mov [edi+ebx],eax + mov [edi+ebx+4],eax + mov [edi+ebx+8],edx + mov [edi+ebx*2],eax + mov [edi+ebx*2+4],eax + mov [edi+ebx*2+8],edx + jmp .loopx_end +..@cross8 + mov ecx,[ebp-w8] + mov edx,eax + shl edx,2 + add edx,[ebx+ecx*4] + sub edx,eax + shr edx,2 + mov ebx,[ebp+pitch] + mov [edi],eax + mov [edi+4],eax + mov [edi+8],eax + mov [edi+ebx],eax + mov [edi+ebx+4],eax + mov [edi+ebx+8],eax + mov [edi+ebx*2],edx + mov [edi+ebx*2+4],edx + mov [edi+ebx*2+8],edx + jmp .loopx_end +..@crossN + mov edx,[ebp-w2] + mov ecx,[ebx+edx*4] + mov [ebp-c2],ecx + mov edx,[ebp-w4] + mov ecx,[ebx+edx*4] + mov [ebp-c4],ecx + mov edx,[ebp-w6] + mov ecx,[ebx+edx*4] + mov [ebp-c6],ecx + mov edx,[ebp-w8] + mov ecx,[ebx+edx*4] + mov [ebp-c8],ecx + mov ebx,[ebp+pitch] + jmp ..@flag0 + +.loopx_end + add esi,2 + add edi,12 + dec dword[ebp-xcounter] + jle .xres_2 + jmp .loopx +.xres_2 + ; x=Xres-2 - special case + jl .xres_1 + mov ebx,[ebp-prevline] + movq mm5,[esi+ebx-4] + movq mm6,[esi-4] + mov ebx,[ebp-nextline] + movq mm7,[esi+ebx-4] + psrlq mm5,16 + psrlq mm6,16 + psrlq mm7,16 + movd eax,mm5 + movzx edx,ax + mov [ebp-w1],edx + shr eax,16 + mov [ebp-w2],eax + psrlq mm5,32 + movd eax,mm5 + mov [ebp-w3],eax + movd eax,mm6 + movzx edx,ax + mov [ebp-w4],edx + shr eax,16 + mov [ebp-w5],eax + psrlq mm6,32 + movd eax,mm6 + mov [ebp-w6],eax + movd eax,mm7 + movzx edx,ax + mov [ebp-w7],edx + shr eax,16 + mov [ebp-w8],eax + psrlq mm7,32 + movd eax,mm7 + mov [ebp-w9],eax + jmp .flags +.xres_1 + cmp dword[ebp-xcounter],-1 + jl .nexty + ; x=Xres-1 - special case + mov ebx,[ebp-prevline] + movq mm5,[esi+ebx-6] + movq mm6,[esi-6] + mov ebx,[ebp-nextline] + movq mm7,[esi+ebx-6] + psrlq mm5,32 + psrlq mm6,32 + psrlq mm7,32 + movd eax,mm5 + movzx edx,ax + mov [ebp-w1],edx + shr eax,16 + mov [ebp-w2],eax + mov [ebp-w3],eax + movd eax,mm6 + movzx edx,ax + mov [ebp-w4],edx + shr eax,16 + mov [ebp-w5],eax + mov [ebp-w6],eax + movd eax,mm7 + movzx edx,ax + mov [ebp-w7],edx + shr eax,16 + mov [ebp-w8],eax + mov [ebp-w9],eax + jmp .flags +.nexty + add esi,[ebp+offset] ; added - move source pointer past end-of-line blanks + add edi,ebx + add edi,ebx + add edi,ebx + mov ebx, [ebp+Xres] ; added, bug - need to add to destination offset + shl ebx, 2 + sub edi, ebx + sub edi, ebx + sub edi, ebx + shr ebx, 1 + dec dword[ebp-linesleft] + jz .fin + add ebx, [ebp+offset]; + cmp dword[ebp-linesleft],1 + je .lastline + mov dword[ebp-nextline],ebx + neg ebx + mov dword[ebp-prevline],ebx + jmp .loopy +.lastline + mov dword[ebp-nextline],0 + neg ebx + mov dword[ebp-prevline],ebx + jmp .loopy +.fin + emms + popad + mov esp,ebp + pop ebp + ret + +SECTION .data +FuncTable + dd ..@flag0, ..@flag1, ..@flag2, ..@flag3, ..@flag4, ..@flag5, ..@flag6, ..@flag7 + dd ..@flag8, ..@flag9, ..@flag10, ..@flag11, ..@flag12, ..@flag13, ..@flag14, ..@flag15 + dd ..@flag16, ..@flag17, ..@flag18, ..@flag19, ..@flag20, ..@flag21, ..@flag22, ..@flag23 + dd ..@flag24, ..@flag25, ..@flag26, ..@flag27, ..@flag28, ..@flag29, ..@flag30, ..@flag31 + dd ..@flag32, ..@flag33, ..@flag34, ..@flag35, ..@flag36, ..@flag37, ..@flag38, ..@flag39 + dd ..@flag40, ..@flag41, ..@flag42, ..@flag43, ..@flag44, ..@flag45, ..@flag46, ..@flag47 + dd ..@flag48, ..@flag49, ..@flag50, ..@flag51, ..@flag52, ..@flag53, ..@flag54, ..@flag55 + dd ..@flag56, ..@flag57, ..@flag58, ..@flag59, ..@flag60, ..@flag61, ..@flag62, ..@flag63 + dd ..@flag64, ..@flag65, ..@flag66, ..@flag67, ..@flag68, ..@flag69, ..@flag70, ..@flag71 + dd ..@flag72, ..@flag73, ..@flag74, ..@flag75, ..@flag76, ..@flag77, ..@flag78, ..@flag79 + dd ..@flag80, ..@flag81, ..@flag82, ..@flag83, ..@flag84, ..@flag85, ..@flag86, ..@flag87 + dd ..@flag88, ..@flag89, ..@flag90, ..@flag91, ..@flag92, ..@flag93, ..@flag94, ..@flag95 + dd ..@flag96, ..@flag97, ..@flag98, ..@flag99, ..@flag100, ..@flag101, ..@flag102, ..@flag103 + dd ..@flag104, ..@flag105, ..@flag106, ..@flag107, ..@flag108, ..@flag109, ..@flag110, ..@flag111 + dd ..@flag112, ..@flag113, ..@flag114, ..@flag115, ..@flag116, ..@flag117, ..@flag118, ..@flag119 + dd ..@flag120, ..@flag121, ..@flag122, ..@flag123, ..@flag124, ..@flag125, ..@flag126, ..@flag127 + dd ..@flag128, ..@flag129, ..@flag130, ..@flag131, ..@flag132, ..@flag133, ..@flag134, ..@flag135 + dd ..@flag136, ..@flag137, ..@flag138, ..@flag139, ..@flag140, ..@flag141, ..@flag142, ..@flag143 + dd ..@flag144, ..@flag145, ..@flag146, ..@flag147, ..@flag148, ..@flag149, ..@flag150, ..@flag151 + dd ..@flag152, ..@flag153, ..@flag154, ..@flag155, ..@flag156, ..@flag157, ..@flag158, ..@flag159 + dd ..@flag160, ..@flag161, ..@flag162, ..@flag163, ..@flag164, ..@flag165, ..@flag166, ..@flag167 + dd ..@flag168, ..@flag169, ..@flag170, ..@flag171, ..@flag172, ..@flag173, ..@flag174, ..@flag175 + dd ..@flag176, ..@flag177, ..@flag178, ..@flag179, ..@flag180, ..@flag181, ..@flag182, ..@flag183 + dd ..@flag184, ..@flag185, ..@flag186, ..@flag187, ..@flag188, ..@flag189, ..@flag190, ..@flag191 + dd ..@flag192, ..@flag193, ..@flag194, ..@flag195, ..@flag196, ..@flag197, ..@flag198, ..@flag199 + dd ..@flag200, ..@flag201, ..@flag202, ..@flag203, ..@flag204, ..@flag205, ..@flag206, ..@flag207 + dd ..@flag208, ..@flag209, ..@flag210, ..@flag211, ..@flag212, ..@flag213, ..@flag214, ..@flag215 + dd ..@flag216, ..@flag217, ..@flag218, ..@flag219, ..@flag220, ..@flag221, ..@flag222, ..@flag223 + dd ..@flag224, ..@flag225, ..@flag226, ..@flag227, ..@flag228, ..@flag229, ..@flag230, ..@flag231 + dd ..@flag232, ..@flag233, ..@flag234, ..@flag235, ..@flag236, ..@flag237, ..@flag238, ..@flag239 + dd ..@flag240, ..@flag241, ..@flag242, ..@flag243, ..@flag244, ..@flag245, ..@flag246, ..@flag247 + dd ..@flag248, ..@flag249, ..@flag250, ..@flag251, ..@flag252, ..@flag253, ..@flag254, ..@flag255 + +FuncTable2 + dd ..@cross0, ..@cross1, ..@cross2, ..@crossN, + dd ..@cross4, ..@crossN, ..@crossN, ..@crossN, + dd ..@cross8, ..@crossN, ..@crossN, ..@crossN, + dd ..@crossN, ..@crossN, ..@crossN, ..@crossN + diff --git a/src/filters/hq/asm/hq4x_16.asm b/src/filters/hq/asm/hq4x_16.asm new file mode 100644 index 0000000..b7b26a5 --- /dev/null +++ b/src/filters/hq/asm/hq4x_16.asm @@ -0,0 +1,3962 @@ +;hq4x filter (thread-safe version) +;16bpp output +;---------------------------------------------------------- +;Copyright (C) 2003 MaxSt ( maxst@hiend3d.com ) +; +;This program is free software; you can redistribute it and/or +;modify it under the terms of the GNU General Public License +;as published by the Free Software Foundation; either +;version 2 of the License, or (at your option) any later +;version. +; +;This program is distributed in the hope that it will be useful, +;but WITHOUT ANY WARRANTY; without even the implied warranty of +;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;GNU General Public License for more details. +; +;You should have received a copy of the GNU General Public License +;along with this program; if not, write to the Free Software +;Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +%include "macros.mac" + +EXTSYM LUT16to32,RGBtoYUV + +SECTION .bss + +SECTION .data + +reg_blank dd 0,0 +const3 dd 0x00030003,0x00000003 +const5 dd 0x00050005,0x00000005 +const6 dd 0x00060006,0x00000006 +const7 dd 0x00070007,0x00000007 +threshold dd 0x00300706,0x00000000 +zerolowbits dd 0xF7DEF7DE + +SECTION .text + +%macro AUXADDRESS 0 + mov ecx, edi + add ecx, ebx + add ecx, ebx +%endmacro + +%macro TestDiff 2 + mov edx,[%1] + sub edx,[%2] + jz %%fin + mov edx,[%1] + shl edx,2 + add edx,RGBtoYUV + movd mm1,[edx] + movq mm5,mm1 + mov edx,[%2] + shl edx,2 + add edx,RGBtoYUV + movd mm2,[edx] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 +%%fin: +%endmacro + +%macro DiffOrNot 4 + TestDiff %1,%2 + test edx,edx + jz %%same + %3 + jmp %%fin +%%same: + %4 +%%fin +%endmacro + +%macro DiffOrNot 8 + TestDiff %1,%2 + test edx,edx + jz %%same + %3 + %4 + %5 + jmp %%fin +%%same: + %6 + %7 + %8 +%%fin +%endmacro + +%macro DiffOrNot 10 + TestDiff %1,%2 + test edx,edx + jz %%same + %3 + %4 + %5 + %6 + jmp %%fin +%%same: + %7 + %8 + %9 + %10 +%%fin +%endmacro + +%macro DiffOrNot 14 + TestDiff %1,%2 + test edx,edx + jz %%same + %3 + %4 + %5 + %6 + %7 + %8 + jmp %%fin +%%same: + %9 + %10 + %11 + %12 + %13 + %14 +%%fin +%endmacro + +%macro Interp1 3 + mov edx,%2 + mov eax,%3 + cmp edx,eax + je %%fin + and edx,[zerolowbits] + and eax,[zerolowbits] + add eax,edx + shr eax,1 + add eax,0x0821 + and eax,[zerolowbits] + add edx,eax + shr edx,1 +%%fin + mov %1,dx +%endmacro + +%macro Interp2 4 + mov edx,%3 + mov eax,%4 + cmp edx,eax + je %%fin1 + and edx,[zerolowbits] + and eax,[zerolowbits] + add eax,edx + shr eax,1 + add eax,0x0821 +%%fin1 + mov edx,%2 + cmp edx,eax + je %%fin2 + and eax,[zerolowbits] + and edx,[zerolowbits] + add edx,eax + shr edx,1 +%%fin2 + mov %1,dx +%endmacro + +%macro Interp3 3 + mov eax, LUT16to32 + mov edx, %2 + movd mm1, [eax+edx*4] + mov edx, %3 + movd mm2, [eax+edx*4] + punpcklbw mm1, [reg_blank] + punpcklbw mm2, [reg_blank] + pmullw mm1, [const7] + paddw mm1, mm2 + psrlw mm1, 5 + packuswb mm1, [reg_blank] + movd edx, mm1 + shl dl, 2 + shr edx, 1 + shl dx, 3 + shr edx, 5 + mov %1, dx +%endmacro + +%macro Interp5 3 + mov edx,%2 + mov eax,%3 + cmp edx,eax + je %%fin + and edx,[zerolowbits] + and eax,[zerolowbits] + add edx,eax + shr edx,1 +%%fin + mov %1,dx +%endmacro + +%macro Interp6 4 + mov eax, LUT16to32 + mov edx, %2 + movd mm1, [eax+edx*4] + mov edx, %3 + movd mm2, [eax+edx*4] + mov edx, %4 + movd mm3, [eax+edx*4] + punpcklbw mm1, [reg_blank] + punpcklbw mm2, [reg_blank] + punpcklbw mm3, [reg_blank] + pmullw mm1, [const5] + psllw mm2, 1 + paddw mm1, mm3 + paddw mm1, mm2 + psrlw mm1, 5 + packuswb mm1, [reg_blank] + movd edx, mm1 + shl dl, 2 + shr edx, 1 + shl dx, 3 + shr edx, 5 + mov %1, dx +%endmacro + +%macro Interp7 4 + mov eax, LUT16to32 + mov edx, %2 + movd mm1, [eax+edx*4] + mov edx, %3 + movd mm2, [eax+edx*4] + mov edx, %4 + movd mm3, [eax+edx*4] + punpcklbw mm1, [reg_blank] + punpcklbw mm2, [reg_blank] + punpcklbw mm3, [reg_blank] + pmullw mm1, [const6] + paddw mm2, mm3 + paddw mm1, mm2 + psrlw mm1, 5 + packuswb mm1, [reg_blank] + movd edx, mm1 + shl dl, 2 + shr edx, 1 + shl dx, 3 + shr edx, 5 + mov %1, dx +%endmacro + +%macro Interp8 3 + mov eax, LUT16to32 + mov edx, %2 + movd mm1, [eax+edx*4] + mov edx, %3 + movd mm2, [eax+edx*4] + punpcklbw mm1, [reg_blank] + punpcklbw mm2, [reg_blank] + pmullw mm1, [const5] + pmullw mm2, [const3] + paddw mm1, mm2 + psrlw mm1, 5 + packuswb mm1, [reg_blank] + movd edx, mm1 + shl dl, 2 + shr edx, 1 + shl dx, 3 + shr edx, 5 + mov %1, dx +%endmacro + +%macro PIXEL00_0 0 + mov eax,dword[ebp-w5] + mov [edi],ax +%endmacro + +%macro PIXEL00_11 0 + Interp1 [edi],dword[ebp-w5],dword[ebp-w4] +%endmacro + +%macro PIXEL00_12 0 + Interp1 [edi],dword[ebp-w5],dword[ebp-w2] +%endmacro + +%macro PIXEL00_20 0 + Interp2 [edi],dword[ebp-w5],dword[ebp-w2],dword[ebp-w4] +%endmacro + +%macro PIXEL00_50 0 + Interp5 [edi],dword[ebp-w2],dword[ebp-w4] +%endmacro + +%macro PIXEL00_80 0 + Interp8 [edi],dword[ebp-w5],dword[ebp-w1] +%endmacro + +%macro PIXEL00_81 0 + Interp8 [edi],dword[ebp-w5],dword[ebp-w4] +%endmacro + +%macro PIXEL00_82 0 + Interp8 [edi],dword[ebp-w5],dword[ebp-w2] +%endmacro + +%macro PIXEL01_0 0 + mov eax,dword[ebp-w5] + mov [edi+2],ax +%endmacro + +%macro PIXEL01_10 0 + Interp1 [edi+2],dword[ebp-w5],dword[ebp-w1] +%endmacro + +%macro PIXEL01_12 0 + Interp1 [edi+2],dword[ebp-w5],dword[ebp-w2] +%endmacro + +%macro PIXEL01_14 0 + Interp1 [edi+2],dword[ebp-w2],dword[ebp-w5] +%endmacro + +%macro PIXEL01_21 0 + Interp2 [edi+2],dword[ebp-w2],dword[ebp-w5],dword[ebp-w4] +%endmacro + +%macro PIXEL01_31 0 + Interp3 [edi+2],dword[ebp-w5],dword[ebp-w4] +%endmacro + +%macro PIXEL01_50 0 + Interp5 [edi+2],dword[ebp-w2],dword[ebp-w5] +%endmacro + +%macro PIXEL01_60 0 + Interp6 [edi+2],dword[ebp-w5],dword[ebp-w2],dword[ebp-w4] +%endmacro + +%macro PIXEL01_61 0 + Interp6 [edi+2],dword[ebp-w5],dword[ebp-w2],dword[ebp-w1] +%endmacro + +%macro PIXEL01_82 0 + Interp8 [edi+2],dword[ebp-w5],dword[ebp-w2] +%endmacro + +%macro PIXEL01_83 0 + Interp8 [edi+2],dword[ebp-w2],dword[ebp-w4] +%endmacro + +%macro PIXEL02_0 0 + mov eax,dword[ebp-w5] + mov [edi+4],ax +%endmacro + +%macro PIXEL02_10 0 + Interp1 [edi+4],dword[ebp-w5],dword[ebp-w3] +%endmacro + +%macro PIXEL02_11 0 + Interp1 [edi+4],dword[ebp-w5],dword[ebp-w2] +%endmacro + +%macro PIXEL02_13 0 + Interp1 [edi+4],dword[ebp-w2],dword[ebp-w5] +%endmacro + +%macro PIXEL02_21 0 + Interp2 [edi+4],dword[ebp-w2],dword[ebp-w5],dword[ebp-w6] +%endmacro + +%macro PIXEL02_32 0 + Interp3 [edi+4],dword[ebp-w5],dword[ebp-w6] +%endmacro + +%macro PIXEL02_50 0 + Interp5 [edi+4],dword[ebp-w2],dword[ebp-w5] +%endmacro + +%macro PIXEL02_60 0 + Interp6 [edi+4],dword[ebp-w5],dword[ebp-w2],dword[ebp-w6] +%endmacro + +%macro PIXEL02_61 0 + Interp6 [edi+4],dword[ebp-w5],dword[ebp-w2],dword[ebp-w3] +%endmacro + +%macro PIXEL02_81 0 + Interp8 [edi+4],dword[ebp-w5],dword[ebp-w2] +%endmacro + +%macro PIXEL02_83 0 + Interp8 [edi+4],dword[ebp-w2],dword[ebp-w6] +%endmacro + +%macro PIXEL03_0 0 + mov eax,dword[ebp-w5] + mov [edi+6],ax +%endmacro + +%macro PIXEL03_11 0 + Interp1 [edi+6],dword[ebp-w5],dword[ebp-w2] +%endmacro + +%macro PIXEL03_12 0 + Interp1 [edi+6],dword[ebp-w5],dword[ebp-w6] +%endmacro + +%macro PIXEL03_20 0 + Interp2 [edi+6],dword[ebp-w5],dword[ebp-w2],dword[ebp-w6] +%endmacro + +%macro PIXEL03_50 0 + Interp5 [edi+6],dword[ebp-w2],dword[ebp-w6] +%endmacro + +%macro PIXEL03_80 0 + Interp8 [edi+6],dword[ebp-w5],dword[ebp-w3] +%endmacro + +%macro PIXEL03_81 0 + Interp8 [edi+6],dword[ebp-w5],dword[ebp-w2] +%endmacro + +%macro PIXEL03_82 0 + Interp8 [edi+6],dword[ebp-w5],dword[ebp-w6] +%endmacro + +%macro PIXEL10_0 0 + mov eax,dword[ebp-w5] + mov [edi+ebx],ax +%endmacro + +%macro PIXEL10_10 0 + Interp1 [edi+ebx],dword[ebp-w5],dword[ebp-w1] +%endmacro + +%macro PIXEL10_11 0 + Interp1 [edi+ebx],dword[ebp-w5],dword[ebp-w4] +%endmacro + +%macro PIXEL10_13 0 + Interp1 [edi+ebx],dword[ebp-w4],dword[ebp-w5] +%endmacro + +%macro PIXEL10_21 0 + Interp2 [edi+ebx],dword[ebp-w4],dword[ebp-w5],dword[ebp-w2] +%endmacro + +%macro PIXEL10_32 0 + Interp3 [edi+ebx],dword[ebp-w5],dword[ebp-w2] +%endmacro + +%macro PIXEL10_50 0 + Interp5 [edi+ebx],dword[ebp-w4],dword[ebp-w5] +%endmacro + +%macro PIXEL10_60 0 + Interp6 [edi+ebx],dword[ebp-w5],dword[ebp-w4],dword[ebp-w2] +%endmacro + +%macro PIXEL10_61 0 + Interp6 [edi+ebx],dword[ebp-w5],dword[ebp-w4],dword[ebp-w1] +%endmacro + +%macro PIXEL10_81 0 + Interp8 [edi+ebx],dword[ebp-w5],dword[ebp-w4] +%endmacro + +%macro PIXEL10_83 0 + Interp8 [edi+ebx],dword[ebp-w4],dword[ebp-w2] +%endmacro + +%macro PIXEL11_0 0 + mov eax,dword[ebp-w5] + mov [edi+ebx+2],ax +%endmacro + +%macro PIXEL11_30 0 + Interp3 [edi+ebx+2],dword[ebp-w5],dword[ebp-w1] +%endmacro + +%macro PIXEL11_31 0 + Interp3 [edi+ebx+2],dword[ebp-w5],dword[ebp-w4] +%endmacro + +%macro PIXEL11_32 0 + Interp3 [edi+ebx+2],dword[ebp-w5],dword[ebp-w2] +%endmacro + +%macro PIXEL11_70 0 + Interp7 [edi+ebx+2],dword[ebp-w5],dword[ebp-w4],dword[ebp-w2] +%endmacro + +%macro PIXEL12_0 0 + mov eax,dword[ebp-w5] + mov [edi+ebx+4],ax +%endmacro + +%macro PIXEL12_30 0 + Interp3 [edi+ebx+4],dword[ebp-w5],dword[ebp-w3] +%endmacro + +%macro PIXEL12_31 0 + Interp3 [edi+ebx+4],dword[ebp-w5],dword[ebp-w2] +%endmacro + +%macro PIXEL12_32 0 + Interp3 [edi+ebx+4],dword[ebp-w5],dword[ebp-w6] +%endmacro + +%macro PIXEL12_70 0 + Interp7 [edi+ebx+4],dword[ebp-w5],dword[ebp-w6],dword[ebp-w2] +%endmacro + +%macro PIXEL13_0 0 + mov eax,dword[ebp-w5] + mov [edi+ebx+6],ax +%endmacro + +%macro PIXEL13_10 0 + Interp1 [edi+ebx+6],dword[ebp-w5],dword[ebp-w3] +%endmacro + +%macro PIXEL13_12 0 + Interp1 [edi+ebx+6],dword[ebp-w5],dword[ebp-w6] +%endmacro + +%macro PIXEL13_14 0 + Interp1 [edi+ebx+6],dword[ebp-w6],dword[ebp-w5] +%endmacro + +%macro PIXEL13_21 0 + Interp2 [edi+ebx+6],dword[ebp-w6],dword[ebp-w5],dword[ebp-w2] +%endmacro + +%macro PIXEL13_31 0 + Interp3 [edi+ebx+6],dword[ebp-w5],dword[ebp-w2] +%endmacro + +%macro PIXEL13_50 0 + Interp5 [edi+ebx+6],dword[ebp-w6],dword[ebp-w5] +%endmacro + +%macro PIXEL13_60 0 + Interp6 [edi+ebx+6],dword[ebp-w5],dword[ebp-w6],dword[ebp-w2] +%endmacro + +%macro PIXEL13_61 0 + Interp6 [edi+ebx+6],dword[ebp-w5],dword[ebp-w6],dword[ebp-w3] +%endmacro + +%macro PIXEL13_82 0 + Interp8 [edi+ebx+6],dword[ebp-w5],dword[ebp-w6] +%endmacro + +%macro PIXEL13_83 0 + Interp8 [edi+ebx+6],dword[ebp-w6],dword[ebp-w2] +%endmacro + +%macro PIXEL20_0 0 + mov eax,dword[ebp-w5] + mov [ecx],ax +%endmacro + +%macro PIXEL20_10 0 + Interp1 [ecx],dword[ebp-w5],dword[ebp-w7] +%endmacro + +%macro PIXEL20_12 0 + Interp1 [ecx],dword[ebp-w5],dword[ebp-w4] +%endmacro + +%macro PIXEL20_14 0 + Interp1 [ecx],dword[ebp-w4],dword[ebp-w5] +%endmacro + +%macro PIXEL20_21 0 + Interp2 [ecx],dword[ebp-w4],dword[ebp-w5],dword[ebp-w8] +%endmacro + +%macro PIXEL20_31 0 + Interp3 [ecx],dword[ebp-w5],dword[ebp-w8] +%endmacro + +%macro PIXEL20_50 0 + Interp5 [ecx],dword[ebp-w4],dword[ebp-w5] +%endmacro + +%macro PIXEL20_60 0 + Interp6 [ecx],dword[ebp-w5],dword[ebp-w4],dword[ebp-w8] +%endmacro + +%macro PIXEL20_61 0 + Interp6 [ecx],dword[ebp-w5],dword[ebp-w4],dword[ebp-w7] +%endmacro + +%macro PIXEL20_82 0 + Interp8 [ecx],dword[ebp-w5],dword[ebp-w4] +%endmacro + +%macro PIXEL20_83 0 + Interp8 [ecx],dword[ebp-w4],dword[ebp-w8] +%endmacro + +%macro PIXEL21_0 0 + mov eax,dword[ebp-w5] + mov [ecx+2],ax +%endmacro + +%macro PIXEL21_30 0 + Interp3 [ecx+2],dword[ebp-w5],dword[ebp-w7] +%endmacro + +%macro PIXEL21_31 0 + Interp3 [ecx+2],dword[ebp-w5],dword[ebp-w8] +%endmacro + +%macro PIXEL21_32 0 + Interp3 [ecx+2],dword[ebp-w5],dword[ebp-w4] +%endmacro + +%macro PIXEL21_70 0 + Interp7 [ecx+2],dword[ebp-w5],dword[ebp-w4],dword[ebp-w8] +%endmacro + +%macro PIXEL22_0 0 + mov eax,dword[ebp-w5] + mov [ecx+4],ax +%endmacro + +%macro PIXEL22_30 0 + Interp3 [ecx+4],dword[ebp-w5],dword[ebp-w9] +%endmacro + +%macro PIXEL22_31 0 + Interp3 [ecx+4],dword[ebp-w5],dword[ebp-w6] +%endmacro + +%macro PIXEL22_32 0 + Interp3 [ecx+4],dword[ebp-w5],dword[ebp-w8] +%endmacro + +%macro PIXEL22_70 0 + Interp7 [ecx+4],dword[ebp-w5],dword[ebp-w6],dword[ebp-w8] +%endmacro + +%macro PIXEL23_0 0 + mov eax,dword[ebp-w5] + mov [ecx+6],ax +%endmacro + +%macro PIXEL23_10 0 + Interp1 [ecx+6],dword[ebp-w5],dword[ebp-w9] +%endmacro + +%macro PIXEL23_11 0 + Interp1 [ecx+6],dword[ebp-w5],dword[ebp-w6] +%endmacro + +%macro PIXEL23_13 0 + Interp1 [ecx+6],dword[ebp-w6],dword[ebp-w5] +%endmacro + +%macro PIXEL23_21 0 + Interp2 [ecx+6],dword[ebp-w6],dword[ebp-w5],dword[ebp-w8] +%endmacro + +%macro PIXEL23_32 0 + Interp3 [ecx+6],dword[ebp-w5],dword[ebp-w8] +%endmacro + +%macro PIXEL23_50 0 + Interp5 [ecx+6],dword[ebp-w6],dword[ebp-w5] +%endmacro + +%macro PIXEL23_60 0 + Interp6 [ecx+6],dword[ebp-w5],dword[ebp-w6],dword[ebp-w8] +%endmacro + +%macro PIXEL23_61 0 + Interp6 [ecx+6],dword[ebp-w5],dword[ebp-w6],dword[ebp-w9] +%endmacro + +%macro PIXEL23_81 0 + Interp8 [ecx+6],dword[ebp-w5],dword[ebp-w6] +%endmacro + +%macro PIXEL23_83 0 + Interp8 [ecx+6],dword[ebp-w6],dword[ebp-w8] +%endmacro + +%macro PIXEL30_0 0 + mov eax,dword[ebp-w5] + mov [ecx+ebx],ax +%endmacro + +%macro PIXEL30_11 0 + Interp1 [ecx+ebx],dword[ebp-w5],dword[ebp-w8] +%endmacro + +%macro PIXEL30_12 0 + Interp1 [ecx+ebx],dword[ebp-w5],dword[ebp-w4] +%endmacro + +%macro PIXEL30_20 0 + Interp2 [ecx+ebx],dword[ebp-w5],dword[ebp-w8],dword[ebp-w4] +%endmacro + +%macro PIXEL30_50 0 + Interp5 [ecx+ebx],dword[ebp-w8],dword[ebp-w4] +%endmacro + +%macro PIXEL30_80 0 + Interp8 [ecx+ebx],dword[ebp-w5],dword[ebp-w7] +%endmacro + +%macro PIXEL30_81 0 + Interp8 [ecx+ebx],dword[ebp-w5],dword[ebp-w8] +%endmacro + +%macro PIXEL30_82 0 + Interp8 [ecx+ebx],dword[ebp-w5],dword[ebp-w4] +%endmacro + +%macro PIXEL31_0 0 + mov eax,dword[ebp-w5] + mov [ecx+ebx+2],ax +%endmacro + +%macro PIXEL31_10 0 + Interp1 [ecx+ebx+2],dword[ebp-w5],dword[ebp-w7] +%endmacro + +%macro PIXEL31_11 0 + Interp1 [ecx+ebx+2],dword[ebp-w5],dword[ebp-w8] +%endmacro + +%macro PIXEL31_13 0 + Interp1 [ecx+ebx+2],dword[ebp-w8],dword[ebp-w5] +%endmacro + +%macro PIXEL31_21 0 + Interp2 [ecx+ebx+2],dword[ebp-w8],dword[ebp-w5],dword[ebp-w4] +%endmacro + +%macro PIXEL31_32 0 + Interp3 [ecx+ebx+2],dword[ebp-w5],dword[ebp-w4] +%endmacro + +%macro PIXEL31_50 0 + Interp5 [ecx+ebx+2],dword[ebp-w8],dword[ebp-w5] +%endmacro + +%macro PIXEL31_60 0 + Interp6 [ecx+ebx+2],dword[ebp-w5],dword[ebp-w8],dword[ebp-w4] +%endmacro + +%macro PIXEL31_61 0 + Interp6 [ecx+ebx+2],dword[ebp-w5],dword[ebp-w8],dword[ebp-w7] +%endmacro + +%macro PIXEL31_81 0 + Interp8 [ecx+ebx+2],dword[ebp-w5],dword[ebp-w8] +%endmacro + +%macro PIXEL31_83 0 + Interp8 [ecx+ebx+2],dword[ebp-w8],dword[ebp-w4] +%endmacro + +%macro PIXEL32_0 0 + mov eax,dword[ebp-w5] + mov [ecx+ebx+4],ax +%endmacro + +%macro PIXEL32_10 0 + Interp1 [ecx+ebx+4],dword[ebp-w5],dword[ebp-w9] +%endmacro + +%macro PIXEL32_12 0 + Interp1 [ecx+ebx+4],dword[ebp-w5],dword[ebp-w8] +%endmacro + +%macro PIXEL32_14 0 + Interp1 [ecx+ebx+4],dword[ebp-w8],dword[ebp-w5] +%endmacro + +%macro PIXEL32_21 0 + Interp2 [ecx+ebx+4],dword[ebp-w8],dword[ebp-w5],dword[ebp-w6] +%endmacro + +%macro PIXEL32_31 0 + Interp3 [ecx+ebx+4],dword[ebp-w5],dword[ebp-w6] +%endmacro + +%macro PIXEL32_50 0 + Interp5 [ecx+ebx+4],dword[ebp-w8],dword[ebp-w5] +%endmacro + +%macro PIXEL32_60 0 + Interp6 [ecx+ebx+4],dword[ebp-w5],dword[ebp-w8],dword[ebp-w6] +%endmacro + +%macro PIXEL32_61 0 + Interp6 [ecx+ebx+4],dword[ebp-w5],dword[ebp-w8],dword[ebp-w9] +%endmacro + +%macro PIXEL32_82 0 + Interp8 [ecx+ebx+4],dword[ebp-w5],dword[ebp-w8] +%endmacro + +%macro PIXEL32_83 0 + Interp8 [ecx+ebx+4],dword[ebp-w8],dword[ebp-w6] +%endmacro + +%macro PIXEL33_0 0 + mov eax,dword[ebp-w5] + mov [ecx+ebx+6],ax +%endmacro + +%macro PIXEL33_11 0 + Interp1 [ecx+ebx+6],dword[ebp-w5],dword[ebp-w6] +%endmacro + +%macro PIXEL33_12 0 + Interp1 [ecx+ebx+6],dword[ebp-w5],dword[ebp-w8] +%endmacro + +%macro PIXEL33_20 0 + Interp2 [ecx+ebx+6],dword[ebp-w5],dword[ebp-w8],dword[ebp-w6] +%endmacro + +%macro PIXEL33_50 0 + Interp5 [ecx+ebx+6],dword[ebp-w8],dword[ebp-w6] +%endmacro + +%macro PIXEL33_80 0 + Interp8 [ecx+ebx+6],dword[ebp-w5],dword[ebp-w9] +%endmacro + +%macro PIXEL33_81 0 + Interp8 [ecx+ebx+6],dword[ebp-w5],dword[ebp-w6] +%endmacro + +%macro PIXEL33_82 0 + Interp8 [ecx+ebx+6],dword[ebp-w5],dword[ebp-w8] +%endmacro + +inbuffer equ 8 +outbuffer equ 12 +Xres equ 16 +Yres equ 20 +pitch equ 24 +offset equ 28 + +linesleft equ 4 +xcounter equ 8 +cross equ 12 +nextline equ 16 +prevline equ 20 +w1 equ 24 +w2 equ 28 +w3 equ 32 +w4 equ 36 +w5 equ 40 +w6 equ 44 +w7 equ 48 +w8 equ 52 +w9 equ 56 +localsize equ 56 + +NEWSYM hq4x_16 + push ebp + mov ebp,esp + sub esp, localsize + pushad + + mov esi,[ebp+inbuffer] + mov edi,[ebp+outbuffer] + mov edx,[ebp+Yres] + mov [ebp-linesleft],edx + mov ebx,[ebp+Xres] + shl ebx,1 + mov dword[ebp-prevline],0 + mov eax, [ebp+offset] + add eax, ebx + mov dword[ebp-nextline],eax +.loopy + mov ecx,[ebp+Xres] + sub ecx,2 ; x={Xres-2, Xres-1} are special cases. + mov dword[ebp-xcounter],ecx + ; x=0 - special case + mov ebx,[ebp-prevline] + movq mm5,[esi+ebx] + movq mm6,[esi] + mov ebx,[ebp-nextline] + movq mm7,[esi+ebx] + movd eax,mm5 + movzx edx,ax + mov [ebp-w1],edx + mov [ebp-w2],edx + shr eax,16 + mov [ebp-w3],eax + movd eax,mm6 + movzx edx,ax + mov [ebp-w4],edx + mov [ebp-w5],edx + shr eax,16 + mov [ebp-w6],eax + movd eax,mm7 + movzx edx,ax + mov [ebp-w7],edx + mov [ebp-w8],edx + shr eax,16 + mov [ebp-w9],eax + jmp .flags +.loopx + mov ebx,[ebp-prevline] + movq mm5,[esi+ebx-2] + movq mm6,[esi-2] + mov ebx,[ebp-nextline] + movq mm7,[esi+ebx-2] + movd eax,mm5 + movzx edx,ax + mov [ebp-w1],edx + shr eax,16 + mov [ebp-w2],eax + psrlq mm5,32 + movd eax,mm5 + movzx edx,ax + mov [ebp-w3],edx + movd eax,mm6 + movzx edx,ax + mov [ebp-w4],edx + shr eax,16 + mov [ebp-w5],eax + psrlq mm6,32 + movd eax,mm6 + movzx edx,ax + mov [ebp-w6],edx + movd eax,mm7 + movzx edx,ax + mov [ebp-w7],edx + shr eax,16 + mov [ebp-w8],eax + psrlq mm7,32 + movd eax,mm7 + movzx edx,ax + mov [ebp-w9],edx +.flags + mov ebx,RGBtoYUV + mov eax,[ebp-w5] + xor ecx,ecx + movd mm5,[ebx+eax*4] + mov dword[ebp-cross],0 + + mov edx,[ebp-w2] + cmp eax,edx + je .noflag2 + or dword[ebp-cross],1 + movq mm1,mm5 + movd mm2,[ebx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 + test edx,edx + jz .noflag2 + or ecx,2 +.noflag2 + mov edx,[ebp-w4] + cmp eax,edx + je .noflag4 + or dword[ebp-cross],2 + movq mm1,mm5 + movd mm2,[ebx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 + test edx,edx + jz .noflag4 + or ecx,8 +.noflag4 + mov edx,[ebp-w6] + cmp eax,edx + je .noflag6 + or dword[ebp-cross],4 + movq mm1,mm5 + movd mm2,[ebx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 + test edx,edx + jz .noflag6 + or ecx,16 +.noflag6 + mov edx,[ebp-w8] + cmp eax,edx + je .noflag8 + or dword[ebp-cross],8 + movq mm1,mm5 + movd mm2,[ebx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 + test edx,edx + jz .noflag8 + or ecx,64 +.noflag8 + cmp dword[ebp-cross],0 + jnz .testflag1 + mov ebx,[ebp+pitch] + mov edx,eax + shl eax,16 + or eax,edx + AUXADDRESS + mov [edi],eax + mov [edi+4],eax + mov [edi+ebx],eax + mov [edi+ebx+4],eax + mov [ecx],eax + mov [ecx+4],eax + mov [ecx+ebx],eax + mov [ecx+ebx+4],eax + jmp .loopx_end +.testflag1 + mov edx,[ebp-w1] + cmp eax,edx + je .noflag1 + movq mm1,mm5 + movd mm2,[ebx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 + test edx,edx + jz .noflag1 + or ecx,1 +.noflag1 + mov edx,[ebp-w3] + cmp eax,edx + je .noflag3 + movq mm1,mm5 + movd mm2,[ebx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 + test edx,edx + jz .noflag3 + or ecx,4 +.noflag3 + mov edx,[ebp-w7] + cmp eax,edx + je .noflag7 + movq mm1,mm5 + movd mm2,[ebx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 + test edx,edx + jz .noflag7 + or ecx,32 +.noflag7 + mov edx,[ebp-w9] + cmp eax,edx + je .noflag9 + movq mm1,mm5 + movd mm2,[ebx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 + test edx,edx + jz .noflag9 + or ecx,128 +.noflag9 + mov ebx,[ebp+pitch] + jmp [FuncTable+ecx*4] + +..@flag0 +..@flag1 +..@flag4 +..@flag32 +..@flag128 +..@flag5 +..@flag132 +..@flag160 +..@flag33 +..@flag129 +..@flag36 +..@flag133 +..@flag164 +..@flag161 +..@flag37 +..@flag165 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_60 + PIXEL03_20 + PIXEL10_60 + PIXEL11_70 + PIXEL12_70 + PIXEL13_60 + PIXEL20_60 + PIXEL21_70 + PIXEL22_70 + PIXEL23_60 + PIXEL30_20 + PIXEL31_60 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag2 +..@flag34 +..@flag130 +..@flag162 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + PIXEL02_10 + PIXEL03_80 + PIXEL10_61 + PIXEL11_30 + PIXEL12_30 + PIXEL13_61 + PIXEL20_60 + PIXEL21_70 + PIXEL22_70 + PIXEL23_60 + PIXEL30_20 + PIXEL31_60 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag16 +..@flag17 +..@flag48 +..@flag49 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_61 + PIXEL03_80 + PIXEL10_60 + PIXEL11_70 + PIXEL12_30 + PIXEL13_10 + PIXEL20_60 + PIXEL21_70 + PIXEL22_30 + PIXEL23_10 + PIXEL30_20 + PIXEL31_60 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag64 +..@flag65 +..@flag68 +..@flag69 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_60 + PIXEL03_20 + PIXEL10_60 + PIXEL11_70 + PIXEL12_70 + PIXEL13_60 + PIXEL20_61 + PIXEL21_30 + PIXEL22_30 + PIXEL23_61 + PIXEL30_80 + PIXEL31_10 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag8 +..@flag12 +..@flag136 +..@flag140 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_60 + PIXEL03_20 + PIXEL10_10 + PIXEL11_30 + PIXEL12_70 + PIXEL13_60 + PIXEL20_10 + PIXEL21_30 + PIXEL22_70 + PIXEL23_60 + PIXEL30_80 + PIXEL31_61 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag3 +..@flag35 +..@flag131 +..@flag163 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + PIXEL02_10 + PIXEL03_80 + PIXEL10_81 + PIXEL11_31 + PIXEL12_30 + PIXEL13_61 + PIXEL20_60 + PIXEL21_70 + PIXEL22_70 + PIXEL23_60 + PIXEL30_20 + PIXEL31_60 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag6 +..@flag38 +..@flag134 +..@flag166 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + PIXEL02_32 + PIXEL03_82 + PIXEL10_61 + PIXEL11_30 + PIXEL12_32 + PIXEL13_82 + PIXEL20_60 + PIXEL21_70 + PIXEL22_70 + PIXEL23_60 + PIXEL30_20 + PIXEL31_60 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag20 +..@flag21 +..@flag52 +..@flag53 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_81 + PIXEL03_81 + PIXEL10_60 + PIXEL11_70 + PIXEL12_31 + PIXEL13_31 + PIXEL20_60 + PIXEL21_70 + PIXEL22_30 + PIXEL23_10 + PIXEL30_20 + PIXEL31_60 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag144 +..@flag145 +..@flag176 +..@flag177 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_61 + PIXEL03_80 + PIXEL10_60 + PIXEL11_70 + PIXEL12_30 + PIXEL13_10 + PIXEL20_60 + PIXEL21_70 + PIXEL22_32 + PIXEL23_32 + PIXEL30_20 + PIXEL31_60 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag192 +..@flag193 +..@flag196 +..@flag197 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_60 + PIXEL03_20 + PIXEL10_60 + PIXEL11_70 + PIXEL12_70 + PIXEL13_60 + PIXEL20_61 + PIXEL21_30 + PIXEL22_31 + PIXEL23_81 + PIXEL30_80 + PIXEL31_10 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag96 +..@flag97 +..@flag100 +..@flag101 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_60 + PIXEL03_20 + PIXEL10_60 + PIXEL11_70 + PIXEL12_70 + PIXEL13_60 + PIXEL20_82 + PIXEL21_32 + PIXEL22_30 + PIXEL23_61 + PIXEL30_82 + PIXEL31_32 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag40 +..@flag44 +..@flag168 +..@flag172 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_60 + PIXEL03_20 + PIXEL10_10 + PIXEL11_30 + PIXEL12_70 + PIXEL13_60 + PIXEL20_31 + PIXEL21_31 + PIXEL22_70 + PIXEL23_60 + PIXEL30_81 + PIXEL31_81 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag9 +..@flag13 +..@flag137 +..@flag141 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_60 + PIXEL03_20 + PIXEL10_32 + PIXEL11_32 + PIXEL12_70 + PIXEL13_60 + PIXEL20_10 + PIXEL21_30 + PIXEL22_70 + PIXEL23_60 + PIXEL30_80 + PIXEL31_61 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag18 +..@flag50 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_10,PIXEL03_80,PIXEL12_30,PIXEL13_10,PIXEL02_50,PIXEL03_50,PIXEL12_0,PIXEL13_50 + PIXEL10_61 + PIXEL11_30 + PIXEL20_60 + PIXEL21_70 + PIXEL22_30 + PIXEL23_10 + PIXEL30_20 + PIXEL31_60 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag80 +..@flag81 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_61 + PIXEL03_80 + PIXEL10_60 + PIXEL11_70 + PIXEL12_30 + PIXEL13_10 + PIXEL20_61 + PIXEL21_30 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_30,PIXEL23_10,PIXEL32_10,PIXEL33_80,PIXEL22_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + PIXEL30_80 + PIXEL31_10 + jmp .loopx_end +..@flag72 +..@flag76 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_60 + PIXEL03_20 + PIXEL10_10 + PIXEL11_30 + PIXEL12_70 + PIXEL13_60 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_10,PIXEL21_30,PIXEL30_80,PIXEL31_10,PIXEL20_50,PIXEL21_0,PIXEL30_50,PIXEL31_50 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag10 +..@flag138 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_80,PIXEL01_10,PIXEL10_10,PIXEL11_30,PIXEL00_50,PIXEL01_50,PIXEL10_50,PIXEL11_0 + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_61 + PIXEL20_10 + PIXEL21_30 + PIXEL22_70 + PIXEL23_60 + PIXEL30_80 + PIXEL31_61 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag66 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + PIXEL02_10 + PIXEL03_80 + PIXEL10_61 + PIXEL11_30 + PIXEL12_30 + PIXEL13_61 + PIXEL20_61 + PIXEL21_30 + PIXEL22_30 + PIXEL23_61 + PIXEL30_80 + PIXEL31_10 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag24 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_61 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_61 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag7 +..@flag39 +..@flag135 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + PIXEL02_32 + PIXEL03_82 + PIXEL10_81 + PIXEL11_31 + PIXEL12_32 + PIXEL13_82 + PIXEL20_60 + PIXEL21_70 + PIXEL22_70 + PIXEL23_60 + PIXEL30_20 + PIXEL31_60 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag148 +..@flag149 +..@flag180 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_81 + PIXEL03_81 + PIXEL10_60 + PIXEL11_70 + PIXEL12_31 + PIXEL13_31 + PIXEL20_60 + PIXEL21_70 + PIXEL22_32 + PIXEL23_32 + PIXEL30_20 + PIXEL31_60 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag224 +..@flag228 +..@flag225 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_60 + PIXEL03_20 + PIXEL10_60 + PIXEL11_70 + PIXEL12_70 + PIXEL13_60 + PIXEL20_82 + PIXEL21_32 + PIXEL22_31 + PIXEL23_81 + PIXEL30_82 + PIXEL31_32 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag41 +..@flag169 +..@flag45 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_60 + PIXEL03_20 + PIXEL10_32 + PIXEL11_32 + PIXEL12_70 + PIXEL13_60 + PIXEL20_31 + PIXEL21_31 + PIXEL22_70 + PIXEL23_60 + PIXEL30_81 + PIXEL31_81 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag22 +..@flag54 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL10_61 + PIXEL11_30 + PIXEL12_0 + PIXEL20_60 + PIXEL21_70 + PIXEL22_30 + PIXEL23_10 + PIXEL30_20 + PIXEL31_60 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag208 +..@flag209 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_61 + PIXEL03_80 + PIXEL10_60 + PIXEL11_70 + PIXEL12_30 + PIXEL13_10 + PIXEL20_61 + PIXEL21_30 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + PIXEL30_80 + PIXEL31_10 + jmp .loopx_end +..@flag104 +..@flag108 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_60 + PIXEL03_20 + PIXEL10_10 + PIXEL11_30 + PIXEL12_70 + PIXEL13_60 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag11 +..@flag139 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_61 + PIXEL20_10 + PIXEL21_30 + PIXEL22_70 + PIXEL23_60 + PIXEL30_80 + PIXEL31_61 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag19 +..@flag51 + AUXADDRESS + DiffOrNot ebp-w2,ebp-w6,PIXEL00_81,PIXEL01_31,PIXEL02_10,PIXEL03_80,PIXEL12_30,PIXEL13_10,PIXEL00_12,PIXEL01_14,PIXEL02_83,PIXEL03_50,PIXEL12_70,PIXEL13_21 + PIXEL10_81 + PIXEL11_31 + PIXEL20_60 + PIXEL21_70 + PIXEL22_30 + PIXEL23_10 + PIXEL30_20 + PIXEL31_60 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag146 +..@flag178 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_10,PIXEL03_80,PIXEL12_30,PIXEL13_10,PIXEL23_32,PIXEL33_82,PIXEL02_21,PIXEL03_50,PIXEL12_70,PIXEL13_83,PIXEL23_13,PIXEL33_11 + PIXEL10_61 + PIXEL11_30 + PIXEL20_60 + PIXEL21_70 + PIXEL22_32 + PIXEL30_20 + PIXEL31_60 + PIXEL32_82 + jmp .loopx_end +..@flag84 +..@flag85 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_81 + DiffOrNot ebp-w6,ebp-w8,PIXEL03_81,PIXEL13_31,PIXEL22_30,PIXEL23_10,PIXEL32_10,PIXEL33_80,PIXEL03_12,PIXEL13_14,PIXEL22_70,PIXEL23_83,PIXEL32_21,PIXEL33_50 + PIXEL10_60 + PIXEL11_70 + PIXEL12_31 + PIXEL20_61 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + jmp .loopx_end +..@flag112 +..@flag113 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_61 + PIXEL03_80 + PIXEL10_60 + PIXEL11_70 + PIXEL12_30 + PIXEL13_10 + PIXEL20_82 + PIXEL21_32 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_30,PIXEL23_10,PIXEL30_82,PIXEL31_32,PIXEL32_10,PIXEL33_80,PIXEL22_70,PIXEL23_21,PIXEL30_11,PIXEL31_13,PIXEL32_83,PIXEL33_50 + jmp .loopx_end +..@flag200 +..@flag204 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_60 + PIXEL03_20 + PIXEL10_10 + PIXEL11_30 + PIXEL12_70 + PIXEL13_60 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_10,PIXEL21_30,PIXEL30_80,PIXEL31_10,PIXEL32_31,PIXEL33_81,PIXEL20_21,PIXEL21_70,PIXEL30_50,PIXEL31_83,PIXEL32_14,PIXEL33_12 + PIXEL22_31 + PIXEL23_81 + jmp .loopx_end +..@flag73 +..@flag77 + AUXADDRESS + DiffOrNot ebp-w8,ebp-w4,PIXEL00_82,PIXEL10_32,PIXEL20_10,PIXEL21_30,PIXEL30_80,PIXEL31_10,PIXEL00_11,PIXEL10_13,PIXEL20_83,PIXEL21_70,PIXEL30_50,PIXEL31_21 + PIXEL01_82 + PIXEL02_60 + PIXEL03_20 + PIXEL11_32 + PIXEL12_70 + PIXEL13_60 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag42 +..@flag170 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_80,PIXEL01_10,PIXEL10_10,PIXEL11_30,PIXEL20_31,PIXEL30_81,PIXEL00_50,PIXEL01_21,PIXEL10_83,PIXEL11_70,PIXEL20_14,PIXEL30_12 + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_61 + PIXEL21_31 + PIXEL22_70 + PIXEL23_60 + PIXEL31_81 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag14 +..@flag142 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_80,PIXEL01_10,PIXEL02_32,PIXEL03_82,PIXEL10_10,PIXEL11_30,PIXEL00_50,PIXEL01_83,PIXEL02_13,PIXEL03_11,PIXEL10_21,PIXEL11_70 + PIXEL12_32 + PIXEL13_82 + PIXEL20_10 + PIXEL21_30 + PIXEL22_70 + PIXEL23_60 + PIXEL30_80 + PIXEL31_61 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag67 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + PIXEL02_10 + PIXEL03_80 + PIXEL10_81 + PIXEL11_31 + PIXEL12_30 + PIXEL13_61 + PIXEL20_61 + PIXEL21_30 + PIXEL22_30 + PIXEL23_61 + PIXEL30_80 + PIXEL31_10 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag70 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + PIXEL02_32 + PIXEL03_82 + PIXEL10_61 + PIXEL11_30 + PIXEL12_32 + PIXEL13_82 + PIXEL20_61 + PIXEL21_30 + PIXEL22_30 + PIXEL23_61 + PIXEL30_80 + PIXEL31_10 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag28 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_81 + PIXEL03_81 + PIXEL10_10 + PIXEL11_30 + PIXEL12_31 + PIXEL13_31 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_61 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag152 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_61 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_32 + PIXEL23_32 + PIXEL30_80 + PIXEL31_61 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag194 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + PIXEL02_10 + PIXEL03_80 + PIXEL10_61 + PIXEL11_30 + PIXEL12_30 + PIXEL13_61 + PIXEL20_61 + PIXEL21_30 + PIXEL22_31 + PIXEL23_81 + PIXEL30_80 + PIXEL31_10 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag98 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + PIXEL02_10 + PIXEL03_80 + PIXEL10_61 + PIXEL11_30 + PIXEL12_30 + PIXEL13_61 + PIXEL20_82 + PIXEL21_32 + PIXEL22_30 + PIXEL23_61 + PIXEL30_82 + PIXEL31_32 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag56 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_61 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + PIXEL20_31 + PIXEL21_31 + PIXEL22_30 + PIXEL23_10 + PIXEL30_81 + PIXEL31_81 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag25 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_61 + PIXEL03_80 + PIXEL10_32 + PIXEL11_32 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_61 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag26 +..@flag31 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL11_0 + PIXEL12_0 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_61 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag82 +..@flag214 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL10_61 + PIXEL11_30 + PIXEL12_0 + PIXEL20_61 + PIXEL21_30 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + PIXEL30_80 + PIXEL31_10 + jmp .loopx_end +..@flag88 +..@flag248 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_61 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + jmp .loopx_end +..@flag74 +..@flag107 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_61 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag27 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_61 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag86 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL10_61 + PIXEL11_30 + PIXEL12_0 + PIXEL20_61 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_10 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag216 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_61 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + PIXEL30_80 + PIXEL31_10 + jmp .loopx_end +..@flag106 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + PIXEL02_10 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_61 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag30 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL10_10 + PIXEL11_30 + PIXEL12_0 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_61 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag210 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + PIXEL02_10 + PIXEL03_80 + PIXEL10_61 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + PIXEL20_61 + PIXEL21_30 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + PIXEL30_80 + PIXEL31_10 + jmp .loopx_end +..@flag120 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_61 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag75 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_61 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_61 + PIXEL30_80 + PIXEL31_10 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag29 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_81 + PIXEL03_81 + PIXEL10_32 + PIXEL11_32 + PIXEL12_31 + PIXEL13_31 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_61 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag198 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + PIXEL02_32 + PIXEL03_82 + PIXEL10_61 + PIXEL11_30 + PIXEL12_32 + PIXEL13_82 + PIXEL20_61 + PIXEL21_30 + PIXEL22_31 + PIXEL23_81 + PIXEL30_80 + PIXEL31_10 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag184 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_61 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + PIXEL20_31 + PIXEL21_31 + PIXEL22_32 + PIXEL23_32 + PIXEL30_81 + PIXEL31_81 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag99 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + PIXEL02_10 + PIXEL03_80 + PIXEL10_81 + PIXEL11_31 + PIXEL12_30 + PIXEL13_61 + PIXEL20_82 + PIXEL21_32 + PIXEL22_30 + PIXEL23_61 + PIXEL30_82 + PIXEL31_32 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag57 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_61 + PIXEL03_80 + PIXEL10_32 + PIXEL11_32 + PIXEL12_30 + PIXEL13_10 + PIXEL20_31 + PIXEL21_31 + PIXEL22_30 + PIXEL23_10 + PIXEL30_81 + PIXEL31_81 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag71 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + PIXEL02_32 + PIXEL03_82 + PIXEL10_81 + PIXEL11_31 + PIXEL12_32 + PIXEL13_82 + PIXEL20_61 + PIXEL21_30 + PIXEL22_30 + PIXEL23_61 + PIXEL30_80 + PIXEL31_10 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag156 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_81 + PIXEL03_81 + PIXEL10_10 + PIXEL11_30 + PIXEL12_31 + PIXEL13_31 + PIXEL20_10 + PIXEL21_30 + PIXEL22_32 + PIXEL23_32 + PIXEL30_80 + PIXEL31_61 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag226 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + PIXEL02_10 + PIXEL03_80 + PIXEL10_61 + PIXEL11_30 + PIXEL12_30 + PIXEL13_61 + PIXEL20_82 + PIXEL21_32 + PIXEL22_31 + PIXEL23_81 + PIXEL30_82 + PIXEL31_32 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag60 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_81 + PIXEL03_81 + PIXEL10_10 + PIXEL11_30 + PIXEL12_31 + PIXEL13_31 + PIXEL20_31 + PIXEL21_31 + PIXEL22_30 + PIXEL23_10 + PIXEL30_81 + PIXEL31_81 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag195 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + PIXEL02_10 + PIXEL03_80 + PIXEL10_81 + PIXEL11_31 + PIXEL12_30 + PIXEL13_61 + PIXEL20_61 + PIXEL21_30 + PIXEL22_31 + PIXEL23_81 + PIXEL30_80 + PIXEL31_10 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag102 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + PIXEL02_32 + PIXEL03_82 + PIXEL10_61 + PIXEL11_30 + PIXEL12_32 + PIXEL13_82 + PIXEL20_82 + PIXEL21_32 + PIXEL22_30 + PIXEL23_61 + PIXEL30_82 + PIXEL31_32 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag153 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_61 + PIXEL03_80 + PIXEL10_32 + PIXEL11_32 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_32 + PIXEL23_32 + PIXEL30_80 + PIXEL31_61 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag58 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_80,PIXEL01_10,PIXEL10_10,PIXEL11_30,PIXEL00_20,PIXEL01_12,PIXEL10_11,PIXEL11_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_10,PIXEL03_80,PIXEL12_30,PIXEL13_10,PIXEL02_11,PIXEL03_20,PIXEL12_0,PIXEL13_12 + PIXEL20_31 + PIXEL21_31 + PIXEL22_30 + PIXEL23_10 + PIXEL30_81 + PIXEL31_81 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag83 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_10,PIXEL03_80,PIXEL12_30,PIXEL13_10,PIXEL02_11,PIXEL03_20,PIXEL12_0,PIXEL13_12 + PIXEL10_81 + PIXEL11_31 + PIXEL20_61 + PIXEL21_30 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_30,PIXEL23_10,PIXEL32_10,PIXEL33_80,PIXEL22_0,PIXEL23_11,PIXEL32_12,PIXEL33_20 + PIXEL30_80 + PIXEL31_10 + jmp .loopx_end +..@flag92 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_81 + PIXEL03_81 + PIXEL10_10 + PIXEL11_30 + PIXEL12_31 + PIXEL13_31 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_10,PIXEL21_30,PIXEL30_80,PIXEL31_10,PIXEL20_12,PIXEL21_0,PIXEL30_20,PIXEL31_11 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_30,PIXEL23_10,PIXEL32_10,PIXEL33_80,PIXEL22_0,PIXEL23_11,PIXEL32_12,PIXEL33_20 + jmp .loopx_end +..@flag202 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_80,PIXEL01_10,PIXEL10_10,PIXEL11_30,PIXEL00_20,PIXEL01_12,PIXEL10_11,PIXEL11_0 + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_61 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_10,PIXEL21_30,PIXEL30_80,PIXEL31_10,PIXEL20_12,PIXEL21_0,PIXEL30_20,PIXEL31_11 + PIXEL22_31 + PIXEL23_81 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag78 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_80,PIXEL01_10,PIXEL10_10,PIXEL11_30,PIXEL00_20,PIXEL01_12,PIXEL10_11,PIXEL11_0 + PIXEL02_32 + PIXEL03_82 + PIXEL12_32 + PIXEL13_82 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_10,PIXEL21_30,PIXEL30_80,PIXEL31_10,PIXEL20_12,PIXEL21_0,PIXEL30_20,PIXEL31_11 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag154 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_80,PIXEL01_10,PIXEL10_10,PIXEL11_30,PIXEL00_20,PIXEL01_12,PIXEL10_11,PIXEL11_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_10,PIXEL03_80,PIXEL12_30,PIXEL13_10,PIXEL02_11,PIXEL03_20,PIXEL12_0,PIXEL13_12 + PIXEL20_10 + PIXEL21_30 + PIXEL22_32 + PIXEL23_32 + PIXEL30_80 + PIXEL31_61 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag114 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_10,PIXEL03_80,PIXEL12_30,PIXEL13_10,PIXEL02_11,PIXEL03_20,PIXEL12_0,PIXEL13_12 + PIXEL10_61 + PIXEL11_30 + PIXEL20_82 + PIXEL21_32 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_30,PIXEL23_10,PIXEL32_10,PIXEL33_80,PIXEL22_0,PIXEL23_11,PIXEL32_12,PIXEL33_20 + PIXEL30_82 + PIXEL31_32 + jmp .loopx_end +..@flag89 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_61 + PIXEL03_80 + PIXEL10_32 + PIXEL11_32 + PIXEL12_30 + PIXEL13_10 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_10,PIXEL21_30,PIXEL30_80,PIXEL31_10,PIXEL20_12,PIXEL21_0,PIXEL30_20,PIXEL31_11 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_30,PIXEL23_10,PIXEL32_10,PIXEL33_80,PIXEL22_0,PIXEL23_11,PIXEL32_12,PIXEL33_20 + jmp .loopx_end +..@flag90 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_80,PIXEL01_10,PIXEL10_10,PIXEL11_30,PIXEL00_20,PIXEL01_12,PIXEL10_11,PIXEL11_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_10,PIXEL03_80,PIXEL12_30,PIXEL13_10,PIXEL02_11,PIXEL03_20,PIXEL12_0,PIXEL13_12 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_10,PIXEL21_30,PIXEL30_80,PIXEL31_10,PIXEL20_12,PIXEL21_0,PIXEL30_20,PIXEL31_11 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_30,PIXEL23_10,PIXEL32_10,PIXEL33_80,PIXEL22_0,PIXEL23_11,PIXEL32_12,PIXEL33_20 + jmp .loopx_end +..@flag55 +..@flag23 + AUXADDRESS + DiffOrNot ebp-w2,ebp-w6,PIXEL00_81,PIXEL01_31,PIXEL02_0,PIXEL03_0,PIXEL12_0,PIXEL13_0,PIXEL00_12,PIXEL01_14,PIXEL02_83,PIXEL03_50,PIXEL12_70,PIXEL13_21 + PIXEL10_81 + PIXEL11_31 + PIXEL20_60 + PIXEL21_70 + PIXEL22_30 + PIXEL23_10 + PIXEL30_20 + PIXEL31_60 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag182 +..@flag150 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL12_0,PIXEL13_0,PIXEL23_32,PIXEL33_82,PIXEL02_21,PIXEL03_50,PIXEL12_70,PIXEL13_83,PIXEL23_13,PIXEL33_11 + PIXEL10_61 + PIXEL11_30 + PIXEL20_60 + PIXEL21_70 + PIXEL22_32 + PIXEL30_20 + PIXEL31_60 + PIXEL32_82 + jmp .loopx_end +..@flag213 +..@flag212 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_81 + DiffOrNot ebp-w6,ebp-w8,PIXEL03_81,PIXEL13_31,PIXEL22_0,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL03_12,PIXEL13_14,PIXEL22_70,PIXEL23_83,PIXEL32_21,PIXEL33_50 + PIXEL10_60 + PIXEL11_70 + PIXEL12_31 + PIXEL20_61 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + jmp .loopx_end +..@flag241 +..@flag240 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_61 + PIXEL03_80 + PIXEL10_60 + PIXEL11_70 + PIXEL12_30 + PIXEL13_10 + PIXEL20_82 + PIXEL21_32 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_0,PIXEL23_0,PIXEL30_82,PIXEL31_32,PIXEL32_0,PIXEL33_0,PIXEL22_70,PIXEL23_21,PIXEL30_11,PIXEL31_13,PIXEL32_83,PIXEL33_50 + jmp .loopx_end +..@flag236 +..@flag232 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_60 + PIXEL03_20 + PIXEL10_10 + PIXEL11_30 + PIXEL12_70 + PIXEL13_60 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL21_0,PIXEL30_0,PIXEL31_0,PIXEL32_31,PIXEL33_81,PIXEL20_21,PIXEL21_70,PIXEL30_50,PIXEL31_83,PIXEL32_14,PIXEL33_12 + PIXEL22_31 + PIXEL23_81 + jmp .loopx_end +..@flag109 +..@flag105 + AUXADDRESS + DiffOrNot ebp-w8,ebp-w4,PIXEL00_82,PIXEL10_32,PIXEL20_0,PIXEL21_0,PIXEL30_0,PIXEL31_0,PIXEL00_11,PIXEL10_13,PIXEL20_83,PIXEL21_70,PIXEL30_50,PIXEL31_21 + PIXEL01_82 + PIXEL02_60 + PIXEL03_20 + PIXEL11_32 + PIXEL12_70 + PIXEL13_60 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag171 +..@flag43 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL11_0,PIXEL20_31,PIXEL30_81,PIXEL00_50,PIXEL01_21,PIXEL10_83,PIXEL11_70,PIXEL20_14,PIXEL30_12 + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_61 + PIXEL21_31 + PIXEL22_70 + PIXEL23_60 + PIXEL31_81 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag143 +..@flag15 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL02_32,PIXEL03_82,PIXEL10_0,PIXEL11_0,PIXEL00_50,PIXEL01_83,PIXEL02_13,PIXEL03_11,PIXEL10_21,PIXEL11_70 + PIXEL12_32 + PIXEL13_82 + PIXEL20_10 + PIXEL21_30 + PIXEL22_70 + PIXEL23_60 + PIXEL30_80 + PIXEL31_61 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag124 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_81 + PIXEL03_81 + PIXEL10_10 + PIXEL11_30 + PIXEL12_31 + PIXEL13_31 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag203 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_61 + PIXEL20_10 + PIXEL21_30 + PIXEL22_31 + PIXEL23_81 + PIXEL30_80 + PIXEL31_10 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag62 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL10_10 + PIXEL11_30 + PIXEL12_0 + PIXEL20_31 + PIXEL21_31 + PIXEL22_30 + PIXEL23_10 + PIXEL30_81 + PIXEL31_81 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag211 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + PIXEL02_10 + PIXEL03_80 + PIXEL10_81 + PIXEL11_31 + PIXEL12_30 + PIXEL13_10 + PIXEL20_61 + PIXEL21_30 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + PIXEL30_80 + PIXEL31_10 + jmp .loopx_end +..@flag118 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL10_61 + PIXEL11_30 + PIXEL12_0 + PIXEL20_82 + PIXEL21_32 + PIXEL22_30 + PIXEL23_10 + PIXEL30_82 + PIXEL31_32 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag217 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_61 + PIXEL03_80 + PIXEL10_32 + PIXEL11_32 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + PIXEL30_80 + PIXEL31_10 + jmp .loopx_end +..@flag110 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + PIXEL02_32 + PIXEL03_82 + PIXEL10_10 + PIXEL11_30 + PIXEL12_32 + PIXEL13_82 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag155 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_32 + PIXEL23_32 + PIXEL30_80 + PIXEL31_61 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag188 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_81 + PIXEL03_81 + PIXEL10_10 + PIXEL11_30 + PIXEL12_31 + PIXEL13_31 + PIXEL20_31 + PIXEL21_31 + PIXEL22_32 + PIXEL23_32 + PIXEL30_81 + PIXEL31_81 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag185 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_61 + PIXEL03_80 + PIXEL10_32 + PIXEL11_32 + PIXEL12_30 + PIXEL13_10 + PIXEL20_31 + PIXEL21_31 + PIXEL22_32 + PIXEL23_32 + PIXEL30_81 + PIXEL31_81 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag61 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_81 + PIXEL03_81 + PIXEL10_32 + PIXEL11_32 + PIXEL12_31 + PIXEL13_31 + PIXEL20_31 + PIXEL21_31 + PIXEL22_30 + PIXEL23_10 + PIXEL30_81 + PIXEL31_81 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag157 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_81 + PIXEL03_81 + PIXEL10_32 + PIXEL11_32 + PIXEL12_31 + PIXEL13_31 + PIXEL20_10 + PIXEL21_30 + PIXEL22_32 + PIXEL23_32 + PIXEL30_80 + PIXEL31_61 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag103 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + PIXEL02_32 + PIXEL03_82 + PIXEL10_81 + PIXEL11_31 + PIXEL12_32 + PIXEL13_82 + PIXEL20_82 + PIXEL21_32 + PIXEL22_30 + PIXEL23_61 + PIXEL30_82 + PIXEL31_32 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag227 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + PIXEL02_10 + PIXEL03_80 + PIXEL10_81 + PIXEL11_31 + PIXEL12_30 + PIXEL13_61 + PIXEL20_82 + PIXEL21_32 + PIXEL22_31 + PIXEL23_81 + PIXEL30_82 + PIXEL31_32 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag230 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + PIXEL02_32 + PIXEL03_82 + PIXEL10_61 + PIXEL11_30 + PIXEL12_32 + PIXEL13_82 + PIXEL20_82 + PIXEL21_32 + PIXEL22_31 + PIXEL23_81 + PIXEL30_82 + PIXEL31_32 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag199 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + PIXEL02_32 + PIXEL03_82 + PIXEL10_81 + PIXEL11_31 + PIXEL12_32 + PIXEL13_82 + PIXEL20_61 + PIXEL21_30 + PIXEL22_31 + PIXEL23_81 + PIXEL30_80 + PIXEL31_10 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag220 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_81 + PIXEL03_81 + PIXEL10_10 + PIXEL11_30 + PIXEL12_31 + PIXEL13_31 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_10,PIXEL21_30,PIXEL30_80,PIXEL31_10,PIXEL20_12,PIXEL21_0,PIXEL30_20,PIXEL31_11 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + jmp .loopx_end +..@flag158 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_80,PIXEL01_10,PIXEL10_10,PIXEL11_30,PIXEL00_20,PIXEL01_12,PIXEL10_11,PIXEL11_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL12_0 + PIXEL20_10 + PIXEL21_30 + PIXEL22_32 + PIXEL23_32 + PIXEL30_80 + PIXEL31_61 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag234 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_80,PIXEL01_10,PIXEL10_10,PIXEL11_30,PIXEL00_20,PIXEL01_12,PIXEL10_11,PIXEL11_0 + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_61 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + PIXEL22_31 + PIXEL23_81 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag242 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_10,PIXEL03_80,PIXEL12_30,PIXEL13_10,PIXEL02_11,PIXEL03_20,PIXEL12_0,PIXEL13_12 + PIXEL10_61 + PIXEL11_30 + PIXEL20_82 + PIXEL21_32 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + PIXEL30_82 + PIXEL31_32 + jmp .loopx_end +..@flag59 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_10,PIXEL03_80,PIXEL12_30,PIXEL13_10,PIXEL02_11,PIXEL03_20,PIXEL12_0,PIXEL13_12 + PIXEL11_0 + PIXEL20_31 + PIXEL21_31 + PIXEL22_30 + PIXEL23_10 + PIXEL30_81 + PIXEL31_81 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag121 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_61 + PIXEL03_80 + PIXEL10_32 + PIXEL11_32 + PIXEL12_30 + PIXEL13_10 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_30,PIXEL23_10,PIXEL32_10,PIXEL33_80,PIXEL22_0,PIXEL23_11,PIXEL32_12,PIXEL33_20 + jmp .loopx_end +..@flag87 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL10_81 + PIXEL11_31 + PIXEL12_0 + PIXEL20_61 + PIXEL21_30 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_30,PIXEL23_10,PIXEL32_10,PIXEL33_80,PIXEL22_0,PIXEL23_11,PIXEL32_12,PIXEL33_20 + PIXEL30_80 + PIXEL31_10 + jmp .loopx_end +..@flag79 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + PIXEL02_32 + PIXEL03_82 + PIXEL11_0 + PIXEL12_32 + PIXEL13_82 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_10,PIXEL21_30,PIXEL30_80,PIXEL31_10,PIXEL20_12,PIXEL21_0,PIXEL30_20,PIXEL31_11 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag122 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_80,PIXEL01_10,PIXEL10_10,PIXEL11_30,PIXEL00_20,PIXEL01_12,PIXEL10_11,PIXEL11_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_10,PIXEL03_80,PIXEL12_30,PIXEL13_10,PIXEL02_11,PIXEL03_20,PIXEL12_0,PIXEL13_12 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_30,PIXEL23_10,PIXEL32_10,PIXEL33_80,PIXEL22_0,PIXEL23_11,PIXEL32_12,PIXEL33_20 + jmp .loopx_end +..@flag94 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_80,PIXEL01_10,PIXEL10_10,PIXEL11_30,PIXEL00_20,PIXEL01_12,PIXEL10_11,PIXEL11_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL12_0 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_10,PIXEL21_30,PIXEL30_80,PIXEL31_10,PIXEL20_12,PIXEL21_0,PIXEL30_20,PIXEL31_11 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_30,PIXEL23_10,PIXEL32_10,PIXEL33_80,PIXEL22_0,PIXEL23_11,PIXEL32_12,PIXEL33_20 + jmp .loopx_end +..@flag218 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_80,PIXEL01_10,PIXEL10_10,PIXEL11_30,PIXEL00_20,PIXEL01_12,PIXEL10_11,PIXEL11_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_10,PIXEL03_80,PIXEL12_30,PIXEL13_10,PIXEL02_11,PIXEL03_20,PIXEL12_0,PIXEL13_12 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_10,PIXEL21_30,PIXEL30_80,PIXEL31_10,PIXEL20_12,PIXEL21_0,PIXEL30_20,PIXEL31_11 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + jmp .loopx_end +..@flag91 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_10,PIXEL03_80,PIXEL12_30,PIXEL13_10,PIXEL02_11,PIXEL03_20,PIXEL12_0,PIXEL13_12 + PIXEL11_0 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_10,PIXEL21_30,PIXEL30_80,PIXEL31_10,PIXEL20_12,PIXEL21_0,PIXEL30_20,PIXEL31_11 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_30,PIXEL23_10,PIXEL32_10,PIXEL33_80,PIXEL22_0,PIXEL23_11,PIXEL32_12,PIXEL33_20 + jmp .loopx_end +..@flag229 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_60 + PIXEL03_20 + PIXEL10_60 + PIXEL11_70 + PIXEL12_70 + PIXEL13_60 + PIXEL20_82 + PIXEL21_32 + PIXEL22_31 + PIXEL23_81 + PIXEL30_82 + PIXEL31_32 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag167 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + PIXEL02_32 + PIXEL03_82 + PIXEL10_81 + PIXEL11_31 + PIXEL12_32 + PIXEL13_82 + PIXEL20_60 + PIXEL21_70 + PIXEL22_70 + PIXEL23_60 + PIXEL30_20 + PIXEL31_60 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag173 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_60 + PIXEL03_20 + PIXEL10_32 + PIXEL11_32 + PIXEL12_70 + PIXEL13_60 + PIXEL20_31 + PIXEL21_31 + PIXEL22_70 + PIXEL23_60 + PIXEL30_81 + PIXEL31_81 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag181 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_81 + PIXEL03_81 + PIXEL10_60 + PIXEL11_70 + PIXEL12_31 + PIXEL13_31 + PIXEL20_60 + PIXEL21_70 + PIXEL22_32 + PIXEL23_32 + PIXEL30_20 + PIXEL31_60 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag186 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_80,PIXEL01_10,PIXEL10_10,PIXEL11_30,PIXEL00_20,PIXEL01_12,PIXEL10_11,PIXEL11_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_10,PIXEL03_80,PIXEL12_30,PIXEL13_10,PIXEL02_11,PIXEL03_20,PIXEL12_0,PIXEL13_12 + PIXEL20_31 + PIXEL21_31 + PIXEL22_32 + PIXEL23_32 + PIXEL30_81 + PIXEL31_81 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag115 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_10,PIXEL03_80,PIXEL12_30,PIXEL13_10,PIXEL02_11,PIXEL03_20,PIXEL12_0,PIXEL13_12 + PIXEL10_81 + PIXEL11_31 + PIXEL20_82 + PIXEL21_32 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_30,PIXEL23_10,PIXEL32_10,PIXEL33_80,PIXEL22_0,PIXEL23_11,PIXEL32_12,PIXEL33_20 + PIXEL30_82 + PIXEL31_32 + jmp .loopx_end +..@flag93 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_81 + PIXEL03_81 + PIXEL10_32 + PIXEL11_32 + PIXEL12_31 + PIXEL13_31 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_10,PIXEL21_30,PIXEL30_80,PIXEL31_10,PIXEL20_12,PIXEL21_0,PIXEL30_20,PIXEL31_11 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_30,PIXEL23_10,PIXEL32_10,PIXEL33_80,PIXEL22_0,PIXEL23_11,PIXEL32_12,PIXEL33_20 + jmp .loopx_end +..@flag206 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_80,PIXEL01_10,PIXEL10_10,PIXEL11_30,PIXEL00_20,PIXEL01_12,PIXEL10_11,PIXEL11_0 + PIXEL02_32 + PIXEL03_82 + PIXEL12_32 + PIXEL13_82 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_10,PIXEL21_30,PIXEL30_80,PIXEL31_10,PIXEL20_12,PIXEL21_0,PIXEL30_20,PIXEL31_11 + PIXEL22_31 + PIXEL23_81 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag205 +..@flag201 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_60 + PIXEL03_20 + PIXEL10_32 + PIXEL11_32 + PIXEL12_70 + PIXEL13_60 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_10,PIXEL21_30,PIXEL30_80,PIXEL31_10,PIXEL20_12,PIXEL21_0,PIXEL30_20,PIXEL31_11 + PIXEL22_31 + PIXEL23_81 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag174 +..@flag46 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_80,PIXEL01_10,PIXEL10_10,PIXEL11_30,PIXEL00_20,PIXEL01_12,PIXEL10_11,PIXEL11_0 + PIXEL02_32 + PIXEL03_82 + PIXEL12_32 + PIXEL13_82 + PIXEL20_31 + PIXEL21_31 + PIXEL22_70 + PIXEL23_60 + PIXEL30_81 + PIXEL31_81 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag179 +..@flag147 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_10,PIXEL03_80,PIXEL12_30,PIXEL13_10,PIXEL02_11,PIXEL03_20,PIXEL12_0,PIXEL13_12 + PIXEL10_81 + PIXEL11_31 + PIXEL20_60 + PIXEL21_70 + PIXEL22_32 + PIXEL23_32 + PIXEL30_20 + PIXEL31_60 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag117 +..@flag116 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_81 + PIXEL03_81 + PIXEL10_60 + PIXEL11_70 + PIXEL12_31 + PIXEL13_31 + PIXEL20_82 + PIXEL21_32 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_30,PIXEL23_10,PIXEL32_10,PIXEL33_80,PIXEL22_0,PIXEL23_11,PIXEL32_12,PIXEL33_20 + PIXEL30_82 + PIXEL31_32 + jmp .loopx_end +..@flag189 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_81 + PIXEL03_81 + PIXEL10_32 + PIXEL11_32 + PIXEL12_31 + PIXEL13_31 + PIXEL20_31 + PIXEL21_31 + PIXEL22_32 + PIXEL23_32 + PIXEL30_81 + PIXEL31_81 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag231 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + PIXEL02_32 + PIXEL03_82 + PIXEL10_81 + PIXEL11_31 + PIXEL12_32 + PIXEL13_82 + PIXEL20_82 + PIXEL21_32 + PIXEL22_31 + PIXEL23_81 + PIXEL30_82 + PIXEL31_32 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag126 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL10_10 + PIXEL11_30 + PIXEL12_0 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag219 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + PIXEL30_80 + PIXEL31_10 + jmp .loopx_end +..@flag125 + AUXADDRESS + DiffOrNot ebp-w8,ebp-w4,PIXEL00_82,PIXEL10_32,PIXEL20_0,PIXEL21_0,PIXEL30_0,PIXEL31_0,PIXEL00_11,PIXEL10_13,PIXEL20_83,PIXEL21_70,PIXEL30_50,PIXEL31_21 + PIXEL01_82 + PIXEL02_81 + PIXEL03_81 + PIXEL11_32 + PIXEL12_31 + PIXEL13_31 + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag221 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_81 + DiffOrNot ebp-w6,ebp-w8,PIXEL03_81,PIXEL13_31,PIXEL22_0,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL03_12,PIXEL13_14,PIXEL22_70,PIXEL23_83,PIXEL32_21,PIXEL33_50 + PIXEL10_32 + PIXEL11_32 + PIXEL12_31 + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + jmp .loopx_end +..@flag207 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL02_32,PIXEL03_82,PIXEL10_0,PIXEL11_0,PIXEL00_50,PIXEL01_83,PIXEL02_13,PIXEL03_11,PIXEL10_21,PIXEL11_70 + PIXEL12_32 + PIXEL13_82 + PIXEL20_10 + PIXEL21_30 + PIXEL22_31 + PIXEL23_81 + PIXEL30_80 + PIXEL31_10 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag238 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + PIXEL02_32 + PIXEL03_82 + PIXEL10_10 + PIXEL11_30 + PIXEL12_32 + PIXEL13_82 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL21_0,PIXEL30_0,PIXEL31_0,PIXEL32_31,PIXEL33_81,PIXEL20_21,PIXEL21_70,PIXEL30_50,PIXEL31_83,PIXEL32_14,PIXEL33_12 + PIXEL22_31 + PIXEL23_81 + jmp .loopx_end +..@flag190 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL12_0,PIXEL13_0,PIXEL23_32,PIXEL33_82,PIXEL02_21,PIXEL03_50,PIXEL12_70,PIXEL13_83,PIXEL23_13,PIXEL33_11 + PIXEL10_10 + PIXEL11_30 + PIXEL20_31 + PIXEL21_31 + PIXEL22_32 + PIXEL30_81 + PIXEL31_81 + PIXEL32_82 + jmp .loopx_end +..@flag187 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL11_0,PIXEL20_31,PIXEL30_81,PIXEL00_50,PIXEL01_21,PIXEL10_83,PIXEL11_70,PIXEL20_14,PIXEL30_12 + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + PIXEL21_31 + PIXEL22_32 + PIXEL23_32 + PIXEL31_81 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag243 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + PIXEL02_10 + PIXEL03_80 + PIXEL10_81 + PIXEL11_31 + PIXEL12_30 + PIXEL13_10 + PIXEL20_82 + PIXEL21_32 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_0,PIXEL23_0,PIXEL30_82,PIXEL31_32,PIXEL32_0,PIXEL33_0,PIXEL22_70,PIXEL23_21,PIXEL30_11,PIXEL31_13,PIXEL32_83,PIXEL33_50 + jmp .loopx_end +..@flag119 + AUXADDRESS + DiffOrNot ebp-w2,ebp-w6,PIXEL00_81,PIXEL01_31,PIXEL02_0,PIXEL03_0,PIXEL12_0,PIXEL13_0,PIXEL00_12,PIXEL01_14,PIXEL02_83,PIXEL03_50,PIXEL12_70,PIXEL13_21 + PIXEL10_81 + PIXEL11_31 + PIXEL20_82 + PIXEL21_32 + PIXEL22_30 + PIXEL23_10 + PIXEL30_82 + PIXEL31_32 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag237 +..@flag233 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_60 + PIXEL03_20 + PIXEL10_32 + PIXEL11_32 + PIXEL12_70 + PIXEL13_60 + PIXEL20_0 + PIXEL21_0 + PIXEL22_31 + PIXEL23_81 + DiffOrNot ebp-w8,ebp-w4,PIXEL30_0,PIXEL30_20 + PIXEL31_0 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag175 +..@flag47 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL00_20 + PIXEL01_0 + PIXEL02_32 + PIXEL03_82 + PIXEL10_0 + PIXEL11_0 + PIXEL12_32 + PIXEL13_82 + PIXEL20_31 + PIXEL21_31 + PIXEL22_70 + PIXEL23_60 + PIXEL30_81 + PIXEL31_81 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag183 +..@flag151 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + PIXEL02_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL03_0,PIXEL03_20 + PIXEL10_81 + PIXEL11_31 + PIXEL12_0 + PIXEL13_0 + PIXEL20_60 + PIXEL21_70 + PIXEL22_32 + PIXEL23_32 + PIXEL30_20 + PIXEL31_60 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag245 +..@flag244 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_81 + PIXEL03_81 + PIXEL10_60 + PIXEL11_70 + PIXEL12_31 + PIXEL13_31 + PIXEL20_82 + PIXEL21_32 + PIXEL22_0 + PIXEL23_0 + PIXEL30_82 + PIXEL31_32 + PIXEL32_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL33_0,PIXEL33_20 + jmp .loopx_end +..@flag250 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + PIXEL02_10 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + jmp .loopx_end +..@flag123 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_10 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag95 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL11_0 + PIXEL12_0 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_10 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag222 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL10_10 + PIXEL11_30 + PIXEL12_0 + PIXEL20_10 + PIXEL21_30 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + PIXEL30_80 + PIXEL31_10 + jmp .loopx_end +..@flag252 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_81 + PIXEL03_81 + PIXEL10_10 + PIXEL11_30 + PIXEL12_31 + PIXEL13_31 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + PIXEL22_0 + PIXEL23_0 + PIXEL32_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL33_0,PIXEL33_20 + jmp .loopx_end +..@flag249 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_61 + PIXEL03_80 + PIXEL10_32 + PIXEL11_32 + PIXEL12_30 + PIXEL13_10 + PIXEL20_0 + PIXEL21_0 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + DiffOrNot ebp-w8,ebp-w4,PIXEL30_0,PIXEL30_20 + PIXEL31_0 + jmp .loopx_end +..@flag235 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_61 + PIXEL20_0 + PIXEL21_0 + PIXEL22_31 + PIXEL23_81 + DiffOrNot ebp-w8,ebp-w4,PIXEL30_0,PIXEL30_20 + PIXEL31_0 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag111 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL00_20 + PIXEL01_0 + PIXEL02_32 + PIXEL03_82 + PIXEL10_0 + PIXEL11_0 + PIXEL12_32 + PIXEL13_82 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag63 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL00_20 + PIXEL01_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL10_0 + PIXEL11_0 + PIXEL12_0 + PIXEL20_31 + PIXEL21_31 + PIXEL22_30 + PIXEL23_10 + PIXEL30_81 + PIXEL31_81 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag159 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + PIXEL02_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL03_0,PIXEL03_20 + PIXEL11_0 + PIXEL12_0 + PIXEL13_0 + PIXEL20_10 + PIXEL21_30 + PIXEL22_32 + PIXEL23_32 + PIXEL30_80 + PIXEL31_61 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag215 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + PIXEL02_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL03_0,PIXEL03_20 + PIXEL10_81 + PIXEL11_31 + PIXEL12_0 + PIXEL13_0 + PIXEL20_61 + PIXEL21_30 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + PIXEL30_80 + PIXEL31_10 + jmp .loopx_end +..@flag246 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL10_61 + PIXEL11_30 + PIXEL12_0 + PIXEL20_82 + PIXEL21_32 + PIXEL22_0 + PIXEL23_0 + PIXEL30_82 + PIXEL31_32 + PIXEL32_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL33_0,PIXEL33_20 + jmp .loopx_end +..@flag254 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL10_10 + PIXEL11_30 + PIXEL12_0 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + PIXEL22_0 + PIXEL23_0 + PIXEL32_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL33_0,PIXEL33_20 + jmp .loopx_end +..@flag253 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_81 + PIXEL03_81 + PIXEL10_32 + PIXEL11_32 + PIXEL12_31 + PIXEL13_31 + PIXEL20_0 + PIXEL21_0 + PIXEL22_0 + PIXEL23_0 + DiffOrNot ebp-w8,ebp-w4,PIXEL30_0,PIXEL30_20 + PIXEL31_0 + PIXEL32_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL33_0,PIXEL33_20 + jmp .loopx_end +..@flag251 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_10 + PIXEL20_0 + PIXEL21_0 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + DiffOrNot ebp-w8,ebp-w4,PIXEL30_0,PIXEL30_20 + PIXEL31_0 + jmp .loopx_end +..@flag239 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL00_20 + PIXEL01_0 + PIXEL02_32 + PIXEL03_82 + PIXEL10_0 + PIXEL11_0 + PIXEL12_32 + PIXEL13_82 + PIXEL20_0 + PIXEL21_0 + PIXEL22_31 + PIXEL23_81 + DiffOrNot ebp-w8,ebp-w4,PIXEL30_0,PIXEL30_20 + PIXEL31_0 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag127 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL00_20 + PIXEL01_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL10_0 + PIXEL11_0 + PIXEL12_0 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag191 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL00_20 + PIXEL01_0 + PIXEL02_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL03_0,PIXEL03_20 + PIXEL10_0 + PIXEL11_0 + PIXEL12_0 + PIXEL13_0 + PIXEL20_31 + PIXEL21_31 + PIXEL22_32 + PIXEL23_32 + PIXEL30_81 + PIXEL31_81 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag223 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + PIXEL02_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL03_0,PIXEL03_20 + PIXEL11_0 + PIXEL12_0 + PIXEL13_0 + PIXEL20_10 + PIXEL21_30 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + PIXEL30_80 + PIXEL31_10 + jmp .loopx_end +..@flag247 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + PIXEL02_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL03_0,PIXEL03_20 + PIXEL10_81 + PIXEL11_31 + PIXEL12_0 + PIXEL13_0 + PIXEL20_82 + PIXEL21_32 + PIXEL22_0 + PIXEL23_0 + PIXEL30_82 + PIXEL31_32 + PIXEL32_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL33_0,PIXEL33_20 + jmp .loopx_end +..@flag255 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL00_20 + PIXEL01_0 + PIXEL02_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL03_0,PIXEL03_20 + PIXEL10_0 + PIXEL11_0 + PIXEL12_0 + PIXEL13_0 + PIXEL20_0 + PIXEL21_0 + PIXEL22_0 + PIXEL23_0 + DiffOrNot ebp-w8,ebp-w4,PIXEL30_0,PIXEL30_20 + PIXEL31_0 + PIXEL32_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL33_0,PIXEL33_20 + jmp .loopx_end + +.loopx_end + add esi,2 + add edi,8 + dec dword[ebp-xcounter] + jle .xres_2 + jmp .loopx +.xres_2 + ; x=Xres-2 - special case + jl .xres_1 + mov ebx,[ebp-prevline] + movq mm5,[esi+ebx-4] + movq mm6,[esi-4] + mov ebx,[ebp-nextline] + movq mm7,[esi+ebx-4] + psrlq mm5,16 + psrlq mm6,16 + psrlq mm7,16 + movd eax,mm5 + movzx edx,ax + mov [ebp-w1],edx + shr eax,16 + mov [ebp-w2],eax + psrlq mm5,32 + movd eax,mm5 + mov [ebp-w3],eax + movd eax,mm6 + movzx edx,ax + mov [ebp-w4],edx + shr eax,16 + mov [ebp-w5],eax + psrlq mm6,32 + movd eax,mm6 + mov [ebp-w6],eax + movd eax,mm7 + movzx edx,ax + mov [ebp-w7],edx + shr eax,16 + mov [ebp-w8],eax + psrlq mm7,32 + movd eax,mm7 + mov [ebp-w9],eax + jmp .flags +.xres_1 + cmp dword[ebp-xcounter],-1 + jl .nexty + ; x=Xres-1 - special case + mov ebx,[ebp-prevline] + movq mm5,[esi+ebx-6] + movq mm6,[esi-6] + mov ebx,[ebp-nextline] + movq mm7,[esi+ebx-6] + psrlq mm5,32 + psrlq mm6,32 + psrlq mm7,32 + movd eax,mm5 + movzx edx,ax + mov [ebp-w1],edx + shr eax,16 + mov [ebp-w2],eax + mov [ebp-w3],eax + movd eax,mm6 + movzx edx,ax + mov [ebp-w4],edx + shr eax,16 + mov [ebp-w5],eax + mov [ebp-w6],eax + movd eax,mm7 + movzx edx,ax + mov [ebp-w7],edx + shr eax,16 + mov [ebp-w8],eax + mov [ebp-w9],eax + jmp .flags +.nexty + add esi,[ebp+offset] ; added - move source pointer past end-of-line blanks + add edi,ebx + add edi,ebx + add edi,ebx + add edi,ebx + mov ebx, [ebp+Xres] ; added, bug - need to add to destination offset + shl ebx, 3 + sub edi, ebx + shr ebx, 2 + dec dword[ebp-linesleft] + jz .fin + add ebx, [ebp+offset]; + cmp dword[ebp-linesleft],1 + je .lastline + mov dword[ebp-nextline],ebx + neg ebx + mov dword[ebp-prevline],ebx + jmp .loopy +.lastline + mov dword[ebp-nextline],0 + neg ebx + mov dword[ebp-prevline],ebx + jmp .loopy +.fin + emms + popad + mov esp,ebp + pop ebp + ret + +SECTION .data +FuncTable + dd ..@flag0, ..@flag1, ..@flag2, ..@flag3, ..@flag4, ..@flag5, ..@flag6, ..@flag7 + dd ..@flag8, ..@flag9, ..@flag10, ..@flag11, ..@flag12, ..@flag13, ..@flag14, ..@flag15 + dd ..@flag16, ..@flag17, ..@flag18, ..@flag19, ..@flag20, ..@flag21, ..@flag22, ..@flag23 + dd ..@flag24, ..@flag25, ..@flag26, ..@flag27, ..@flag28, ..@flag29, ..@flag30, ..@flag31 + dd ..@flag32, ..@flag33, ..@flag34, ..@flag35, ..@flag36, ..@flag37, ..@flag38, ..@flag39 + dd ..@flag40, ..@flag41, ..@flag42, ..@flag43, ..@flag44, ..@flag45, ..@flag46, ..@flag47 + dd ..@flag48, ..@flag49, ..@flag50, ..@flag51, ..@flag52, ..@flag53, ..@flag54, ..@flag55 + dd ..@flag56, ..@flag57, ..@flag58, ..@flag59, ..@flag60, ..@flag61, ..@flag62, ..@flag63 + dd ..@flag64, ..@flag65, ..@flag66, ..@flag67, ..@flag68, ..@flag69, ..@flag70, ..@flag71 + dd ..@flag72, ..@flag73, ..@flag74, ..@flag75, ..@flag76, ..@flag77, ..@flag78, ..@flag79 + dd ..@flag80, ..@flag81, ..@flag82, ..@flag83, ..@flag84, ..@flag85, ..@flag86, ..@flag87 + dd ..@flag88, ..@flag89, ..@flag90, ..@flag91, ..@flag92, ..@flag93, ..@flag94, ..@flag95 + dd ..@flag96, ..@flag97, ..@flag98, ..@flag99, ..@flag100, ..@flag101, ..@flag102, ..@flag103 + dd ..@flag104, ..@flag105, ..@flag106, ..@flag107, ..@flag108, ..@flag109, ..@flag110, ..@flag111 + dd ..@flag112, ..@flag113, ..@flag114, ..@flag115, ..@flag116, ..@flag117, ..@flag118, ..@flag119 + dd ..@flag120, ..@flag121, ..@flag122, ..@flag123, ..@flag124, ..@flag125, ..@flag126, ..@flag127 + dd ..@flag128, ..@flag129, ..@flag130, ..@flag131, ..@flag132, ..@flag133, ..@flag134, ..@flag135 + dd ..@flag136, ..@flag137, ..@flag138, ..@flag139, ..@flag140, ..@flag141, ..@flag142, ..@flag143 + dd ..@flag144, ..@flag145, ..@flag146, ..@flag147, ..@flag148, ..@flag149, ..@flag150, ..@flag151 + dd ..@flag152, ..@flag153, ..@flag154, ..@flag155, ..@flag156, ..@flag157, ..@flag158, ..@flag159 + dd ..@flag160, ..@flag161, ..@flag162, ..@flag163, ..@flag164, ..@flag165, ..@flag166, ..@flag167 + dd ..@flag168, ..@flag169, ..@flag170, ..@flag171, ..@flag172, ..@flag173, ..@flag174, ..@flag175 + dd ..@flag176, ..@flag177, ..@flag178, ..@flag179, ..@flag180, ..@flag181, ..@flag182, ..@flag183 + dd ..@flag184, ..@flag185, ..@flag186, ..@flag187, ..@flag188, ..@flag189, ..@flag190, ..@flag191 + dd ..@flag192, ..@flag193, ..@flag194, ..@flag195, ..@flag196, ..@flag197, ..@flag198, ..@flag199 + dd ..@flag200, ..@flag201, ..@flag202, ..@flag203, ..@flag204, ..@flag205, ..@flag206, ..@flag207 + dd ..@flag208, ..@flag209, ..@flag210, ..@flag211, ..@flag212, ..@flag213, ..@flag214, ..@flag215 + dd ..@flag216, ..@flag217, ..@flag218, ..@flag219, ..@flag220, ..@flag221, ..@flag222, ..@flag223 + dd ..@flag224, ..@flag225, ..@flag226, ..@flag227, ..@flag228, ..@flag229, ..@flag230, ..@flag231 + dd ..@flag232, ..@flag233, ..@flag234, ..@flag235, ..@flag236, ..@flag237, ..@flag238, ..@flag239 + dd ..@flag240, ..@flag241, ..@flag242, ..@flag243, ..@flag244, ..@flag245, ..@flag246, ..@flag247 + dd ..@flag248, ..@flag249, ..@flag250, ..@flag251, ..@flag252, ..@flag253, ..@flag254, ..@flag255 + + diff --git a/src/filters/hq/asm/hq4x_32.asm b/src/filters/hq/asm/hq4x_32.asm new file mode 100644 index 0000000..ba9b3b3 --- /dev/null +++ b/src/filters/hq/asm/hq4x_32.asm @@ -0,0 +1,3933 @@ +;hq4x filter (thread-safe version) +;32bpp output +;---------------------------------------------------------- +;Copyright (C) 2003 MaxSt ( maxst@hiend3d.com ) +; +;This program is free software; you can redistribute it and/or +;modify it under the terms of the GNU General Public License +;as published by the Free Software Foundation; either +;version 2 of the License, or (at your option) any later +;version. +; +;This program is distributed in the hope that it will be useful, +;but WITHOUT ANY WARRANTY; without even the implied warranty of +;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;GNU General Public License for more details. +; +;You should have received a copy of the GNU General Public License +;along with this program; if not, write to the Free Software +;Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +%include "macros.mac" + +EXTSYM LUT16to32,RGBtoYUV + +SECTION .bss + +SECTION .data + +reg_blank dd 0,0 +const3 dd 0x00030003,0x00000003 +const5 dd 0x00050005,0x00000005 +const6 dd 0x00060006,0x00000006 +const7 dd 0x00070007,0x00000007 +threshold dd 0x00300706,0x00000000 + +SECTION .text + +%macro AUXADDRESS 0 + mov ecx, edi + add ecx, ebx + add ecx, ebx +%endmacro + +%macro TestDiff 2 + mov edx,[%1] + sub edx,[%2] + jz %%fin + mov edx,[%1] + shl edx,2 + add edx,RGBtoYUV + movd mm1,[edx] + movq mm5,mm1 + mov edx,[%2] + shl edx,2 + add edx,RGBtoYUV + movd mm2,[edx] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 +%%fin: +%endmacro + +%macro DiffOrNot 4 + TestDiff %1,%2 + test edx,edx + jz %%same + %3 + jmp %%fin +%%same: + %4 +%%fin +%endmacro + +%macro DiffOrNot 8 + TestDiff %1,%2 + test edx,edx + jz %%same + %3 + %4 + %5 + jmp %%fin +%%same: + %6 + %7 + %8 +%%fin +%endmacro + +%macro DiffOrNot 10 + TestDiff %1,%2 + test edx,edx + jz %%same + %3 + %4 + %5 + %6 + jmp %%fin +%%same: + %7 + %8 + %9 + %10 +%%fin +%endmacro + +%macro DiffOrNot 14 + TestDiff %1,%2 + test edx,edx + jz %%same + %3 + %4 + %5 + %6 + %7 + %8 + jmp %%fin +%%same: + %9 + %10 + %11 + %12 + %13 + %14 +%%fin +%endmacro + +%macro Interp1 3 + mov edx,%2 + shl edx,2 + add edx,%3 + sub edx,%2 + shr edx,2 + mov %1,edx +%endmacro + +%macro Interp2 4 + mov edx,%2 + shl edx,1 + add edx,%3 + add edx,%4 + shr edx,2 + mov %1,edx +%endmacro + +%macro Interp3 2 + movd mm1, eax + movd mm2, %2 + punpcklbw mm1, [reg_blank] + punpcklbw mm2, [reg_blank] + pmullw mm1, [const7] + paddw mm1, mm2 + psrlw mm1, 3 + packuswb mm1, [reg_blank] + movd %1, mm1 +%endmacro + +%macro Interp5 3 + mov edx,%2 + add edx,%3 + shr edx,1 + mov %1,edx +%endmacro + +%macro Interp6 3 + movd mm1, eax + movd mm2, %2 + movd mm3, %3 + punpcklbw mm1, [reg_blank] + punpcklbw mm2, [reg_blank] + punpcklbw mm3, [reg_blank] + pmullw mm1, [const5] + psllw mm2, 1 + paddw mm1, mm3 + paddw mm1, mm2 + psrlw mm1, 3 + packuswb mm1, [reg_blank] + movd %1, mm1 +%endmacro + +%macro Interp7 3 + movd mm1, eax + movd mm2, %2 + movd mm3, %3 + punpcklbw mm1, [reg_blank] + punpcklbw mm2, [reg_blank] + punpcklbw mm3, [reg_blank] + pmullw mm1, [const6] + paddw mm2, mm3 + paddw mm1, mm2 + psrlw mm1, 3 + packuswb mm1, [reg_blank] + movd %1, mm1 +%endmacro + +%macro Interp8 3 + movd mm1, %2 + movd mm2, %3 + punpcklbw mm1, [reg_blank] + punpcklbw mm2, [reg_blank] + pmullw mm1, [const5] + pmullw mm2, [const3] + paddw mm1, mm2 + psrlw mm1, 3 + packuswb mm1, [reg_blank] + movd %1, mm1 +%endmacro + +%macro PIXEL00_0 0 + mov [edi],eax +%endmacro + +%macro PIXEL00_11 0 + Interp1 [edi],eax,dword[ebp-c4] +%endmacro + +%macro PIXEL00_12 0 + Interp1 [edi],eax,dword[ebp-c2] +%endmacro + +%macro PIXEL00_20 0 + Interp2 [edi],eax,dword[ebp-c2],dword[ebp-c4] +%endmacro + +%macro PIXEL00_50 0 + Interp5 [edi],dword[ebp-c2],dword[ebp-c4] +%endmacro + +%macro PIXEL00_80 0 + Interp8 [edi],eax,dword[ebp-c1] +%endmacro + +%macro PIXEL00_81 0 + Interp8 [edi],eax,dword[ebp-c4] +%endmacro + +%macro PIXEL00_82 0 + Interp8 [edi],eax,dword[ebp-c2] +%endmacro + +%macro PIXEL01_0 0 + mov [edi+4],eax +%endmacro + +%macro PIXEL01_10 0 + Interp1 [edi+4],eax,dword[ebp-c1] +%endmacro + +%macro PIXEL01_12 0 + Interp1 [edi+4],eax,dword[ebp-c2] +%endmacro + +%macro PIXEL01_14 0 + Interp1 [edi+4],dword[ebp-c2],eax +%endmacro + +%macro PIXEL01_21 0 + Interp2 [edi+4],dword[ebp-c2],eax,dword[ebp-c4] +%endmacro + +%macro PIXEL01_31 0 + Interp3 [edi+4],dword[ebp-c4] +%endmacro + +%macro PIXEL01_50 0 + Interp5 [edi+4],dword[ebp-c2],eax +%endmacro + +%macro PIXEL01_60 0 + Interp6 [edi+4],dword[ebp-c2],dword[ebp-c4] +%endmacro + +%macro PIXEL01_61 0 + Interp6 [edi+4],dword[ebp-c2],dword[ebp-c1] +%endmacro + +%macro PIXEL01_82 0 + Interp8 [edi+4],eax,dword[ebp-c2] +%endmacro + +%macro PIXEL01_83 0 + Interp8 [edi+4],dword[ebp-c2],dword[ebp-c4] +%endmacro + +%macro PIXEL02_0 0 + mov [edi+8],eax +%endmacro + +%macro PIXEL02_10 0 + Interp1 [edi+8],eax,dword[ebp-c3] +%endmacro + +%macro PIXEL02_11 0 + Interp1 [edi+8],eax,dword[ebp-c2] +%endmacro + +%macro PIXEL02_13 0 + Interp1 [edi+8],dword[ebp-c2],eax +%endmacro + +%macro PIXEL02_21 0 + Interp2 [edi+8],dword[ebp-c2],eax,dword[ebp-c6] +%endmacro + +%macro PIXEL02_32 0 + Interp3 [edi+8],dword[ebp-c6] +%endmacro + +%macro PIXEL02_50 0 + Interp5 [edi+8],dword[ebp-c2],eax +%endmacro + +%macro PIXEL02_60 0 + Interp6 [edi+8],dword[ebp-c2],dword[ebp-c6] +%endmacro + +%macro PIXEL02_61 0 + Interp6 [edi+8],dword[ebp-c2],dword[ebp-c3] +%endmacro + +%macro PIXEL02_81 0 + Interp8 [edi+8],eax,dword[ebp-c2] +%endmacro + +%macro PIXEL02_83 0 + Interp8 [edi+8],dword[ebp-c2],dword[ebp-c6] +%endmacro + +%macro PIXEL03_0 0 + mov [edi+12],eax +%endmacro + +%macro PIXEL03_11 0 + Interp1 [edi+12],eax,dword[ebp-c2] +%endmacro + +%macro PIXEL03_12 0 + Interp1 [edi+12],eax,dword[ebp-c6] +%endmacro + +%macro PIXEL03_20 0 + Interp2 [edi+12],eax,dword[ebp-c2],dword[ebp-c6] +%endmacro + +%macro PIXEL03_50 0 + Interp5 [edi+12],dword[ebp-c2],dword[ebp-c6] +%endmacro + +%macro PIXEL03_80 0 + Interp8 [edi+12],eax,dword[ebp-c3] +%endmacro + +%macro PIXEL03_81 0 + Interp8 [edi+12],eax,dword[ebp-c2] +%endmacro + +%macro PIXEL03_82 0 + Interp8 [edi+12],eax,dword[ebp-c6] +%endmacro + +%macro PIXEL10_0 0 + mov [edi+ebx],eax +%endmacro + +%macro PIXEL10_10 0 + Interp1 [edi+ebx],eax,dword[ebp-c1] +%endmacro + +%macro PIXEL10_11 0 + Interp1 [edi+ebx],eax,dword[ebp-c4] +%endmacro + +%macro PIXEL10_13 0 + Interp1 [edi+ebx],dword[ebp-c4],eax +%endmacro + +%macro PIXEL10_21 0 + Interp2 [edi+ebx],dword[ebp-c4],eax,dword[ebp-c2] +%endmacro + +%macro PIXEL10_32 0 + Interp3 [edi+ebx],dword[ebp-c2] +%endmacro + +%macro PIXEL10_50 0 + Interp5 [edi+ebx],dword[ebp-c4],eax +%endmacro + +%macro PIXEL10_60 0 + Interp6 [edi+ebx],dword[ebp-c4],dword[ebp-c2] +%endmacro + +%macro PIXEL10_61 0 + Interp6 [edi+ebx],dword[ebp-c4],dword[ebp-c1] +%endmacro + +%macro PIXEL10_81 0 + Interp8 [edi+ebx],eax,dword[ebp-c4] +%endmacro + +%macro PIXEL10_83 0 + Interp8 [edi+ebx],dword[ebp-c4],dword[ebp-c2] +%endmacro + +%macro PIXEL11_0 0 + mov [edi+ebx+4],eax +%endmacro + +%macro PIXEL11_30 0 + Interp3 [edi+ebx+4],dword[ebp-c1] +%endmacro + +%macro PIXEL11_31 0 + Interp3 [edi+ebx+4],dword[ebp-c4] +%endmacro + +%macro PIXEL11_32 0 + Interp3 [edi+ebx+4],dword[ebp-c2] +%endmacro + +%macro PIXEL11_70 0 + Interp7 [edi+ebx+4],dword[ebp-c4],dword[ebp-c2] +%endmacro + +%macro PIXEL12_0 0 + mov [edi+ebx+8],eax +%endmacro + +%macro PIXEL12_30 0 + Interp3 [edi+ebx+8],dword[ebp-c3] +%endmacro + +%macro PIXEL12_31 0 + Interp3 [edi+ebx+8],dword[ebp-c2] +%endmacro + +%macro PIXEL12_32 0 + Interp3 [edi+ebx+8],dword[ebp-c6] +%endmacro + +%macro PIXEL12_70 0 + Interp7 [edi+ebx+8],dword[ebp-c6],dword[ebp-c2] +%endmacro + +%macro PIXEL13_0 0 + mov [edi+ebx+12],eax +%endmacro + +%macro PIXEL13_10 0 + Interp1 [edi+ebx+12],eax,dword[ebp-c3] +%endmacro + +%macro PIXEL13_12 0 + Interp1 [edi+ebx+12],eax,dword[ebp-c6] +%endmacro + +%macro PIXEL13_14 0 + Interp1 [edi+ebx+12],dword[ebp-c6],eax +%endmacro + +%macro PIXEL13_21 0 + Interp2 [edi+ebx+12],dword[ebp-c6],eax,dword[ebp-c2] +%endmacro + +%macro PIXEL13_31 0 + Interp3 [edi+ebx+12],dword[ebp-c2] +%endmacro + +%macro PIXEL13_50 0 + Interp5 [edi+ebx+12],dword[ebp-c6],eax +%endmacro + +%macro PIXEL13_60 0 + Interp6 [edi+ebx+12],dword[ebp-c6],dword[ebp-c2] +%endmacro + +%macro PIXEL13_61 0 + Interp6 [edi+ebx+12],dword[ebp-c6],dword[ebp-c3] +%endmacro + +%macro PIXEL13_82 0 + Interp8 [edi+ebx+12],eax,dword[ebp-c6] +%endmacro + +%macro PIXEL13_83 0 + Interp8 [edi+ebx+12],dword[ebp-c6],dword[ebp-c2] +%endmacro + +%macro PIXEL20_0 0 + mov [ecx],eax +%endmacro + +%macro PIXEL20_10 0 + Interp1 [ecx],eax,dword[ebp-c7] +%endmacro + +%macro PIXEL20_12 0 + Interp1 [ecx],eax,dword[ebp-c4] +%endmacro + +%macro PIXEL20_14 0 + Interp1 [ecx],dword[ebp-c4],eax +%endmacro + +%macro PIXEL20_21 0 + Interp2 [ecx],dword[ebp-c4],eax,dword[ebp-c8] +%endmacro + +%macro PIXEL20_31 0 + Interp3 [ecx],dword[ebp-c8] +%endmacro + +%macro PIXEL20_50 0 + Interp5 [ecx],dword[ebp-c4],eax +%endmacro + +%macro PIXEL20_60 0 + Interp6 [ecx],dword[ebp-c4],dword[ebp-c8] +%endmacro + +%macro PIXEL20_61 0 + Interp6 [ecx],dword[ebp-c4],dword[ebp-c7] +%endmacro + +%macro PIXEL20_82 0 + Interp8 [ecx],eax,dword[ebp-c4] +%endmacro + +%macro PIXEL20_83 0 + Interp8 [ecx],dword[ebp-c4],dword[ebp-c8] +%endmacro + +%macro PIXEL21_0 0 + mov [ecx+4],eax +%endmacro + +%macro PIXEL21_30 0 + Interp3 [ecx+4],dword[ebp-c7] +%endmacro + +%macro PIXEL21_31 0 + Interp3 [ecx+4],dword[ebp-c8] +%endmacro + +%macro PIXEL21_32 0 + Interp3 [ecx+4],dword[ebp-c4] +%endmacro + +%macro PIXEL21_70 0 + Interp7 [ecx+4],dword[ebp-c4],dword[ebp-c8] +%endmacro + +%macro PIXEL22_0 0 + mov [ecx+8],eax +%endmacro + +%macro PIXEL22_30 0 + Interp3 [ecx+8],dword[ebp-c9] +%endmacro + +%macro PIXEL22_31 0 + Interp3 [ecx+8],dword[ebp-c6] +%endmacro + +%macro PIXEL22_32 0 + Interp3 [ecx+8],dword[ebp-c8] +%endmacro + +%macro PIXEL22_70 0 + Interp7 [ecx+8],dword[ebp-c6],dword[ebp-c8] +%endmacro + +%macro PIXEL23_0 0 + mov [ecx+12],eax +%endmacro + +%macro PIXEL23_10 0 + Interp1 [ecx+12],eax,dword[ebp-c9] +%endmacro + +%macro PIXEL23_11 0 + Interp1 [ecx+12],eax,dword[ebp-c6] +%endmacro + +%macro PIXEL23_13 0 + Interp1 [ecx+12],dword[ebp-c6],eax +%endmacro + +%macro PIXEL23_21 0 + Interp2 [ecx+12],dword[ebp-c6],eax,dword[ebp-c8] +%endmacro + +%macro PIXEL23_32 0 + Interp3 [ecx+12],dword[ebp-c8] +%endmacro + +%macro PIXEL23_50 0 + Interp5 [ecx+12],dword[ebp-c6],eax +%endmacro + +%macro PIXEL23_60 0 + Interp6 [ecx+12],dword[ebp-c6],dword[ebp-c8] +%endmacro + +%macro PIXEL23_61 0 + Interp6 [ecx+12],dword[ebp-c6],dword[ebp-c9] +%endmacro + +%macro PIXEL23_81 0 + Interp8 [ecx+12],eax,dword[ebp-c6] +%endmacro + +%macro PIXEL23_83 0 + Interp8 [ecx+12],dword[ebp-c6],dword[ebp-c8] +%endmacro + +%macro PIXEL30_0 0 + mov [ecx+ebx],eax +%endmacro + +%macro PIXEL30_11 0 + Interp1 [ecx+ebx],eax,dword[ebp-c8] +%endmacro + +%macro PIXEL30_12 0 + Interp1 [ecx+ebx],eax,dword[ebp-c4] +%endmacro + +%macro PIXEL30_20 0 + Interp2 [ecx+ebx],eax,dword[ebp-c8],dword[ebp-c4] +%endmacro + +%macro PIXEL30_50 0 + Interp5 [ecx+ebx],dword[ebp-c8],dword[ebp-c4] +%endmacro + +%macro PIXEL30_80 0 + Interp8 [ecx+ebx],eax,dword[ebp-c7] +%endmacro + +%macro PIXEL30_81 0 + Interp8 [ecx+ebx],eax,dword[ebp-c8] +%endmacro + +%macro PIXEL30_82 0 + Interp8 [ecx+ebx],eax,dword[ebp-c4] +%endmacro + +%macro PIXEL31_0 0 + mov [ecx+ebx+4],eax +%endmacro + +%macro PIXEL31_10 0 + Interp1 [ecx+ebx+4],eax,dword[ebp-c7] +%endmacro + +%macro PIXEL31_11 0 + Interp1 [ecx+ebx+4],eax,dword[ebp-c8] +%endmacro + +%macro PIXEL31_13 0 + Interp1 [ecx+ebx+4],dword[ebp-c8],eax +%endmacro + +%macro PIXEL31_21 0 + Interp2 [ecx+ebx+4],dword[ebp-c8],eax,dword[ebp-c4] +%endmacro + +%macro PIXEL31_32 0 + Interp3 [ecx+ebx+4],dword[ebp-c4] +%endmacro + +%macro PIXEL31_50 0 + Interp5 [ecx+ebx+4],dword[ebp-c8],eax +%endmacro + +%macro PIXEL31_60 0 + Interp6 [ecx+ebx+4],dword[ebp-c8],dword[ebp-c4] +%endmacro + +%macro PIXEL31_61 0 + Interp6 [ecx+ebx+4],dword[ebp-c8],dword[ebp-c7] +%endmacro + +%macro PIXEL31_81 0 + Interp8 [ecx+ebx+4],eax,dword[ebp-c8] +%endmacro + +%macro PIXEL31_83 0 + Interp8 [ecx+ebx+4],dword[ebp-c8],dword[ebp-c4] +%endmacro + +%macro PIXEL32_0 0 + mov [ecx+ebx+8],eax +%endmacro + +%macro PIXEL32_10 0 + Interp1 [ecx+ebx+8],eax,dword[ebp-c9] +%endmacro + +%macro PIXEL32_12 0 + Interp1 [ecx+ebx+8],eax,dword[ebp-c8] +%endmacro + +%macro PIXEL32_14 0 + Interp1 [ecx+ebx+8],dword[ebp-c8],eax +%endmacro + +%macro PIXEL32_21 0 + Interp2 [ecx+ebx+8],dword[ebp-c8],eax,dword[ebp-c6] +%endmacro + +%macro PIXEL32_31 0 + Interp3 [ecx+ebx+8],dword[ebp-c6] +%endmacro + +%macro PIXEL32_50 0 + Interp5 [ecx+ebx+8],dword[ebp-c8],eax +%endmacro + +%macro PIXEL32_60 0 + Interp6 [ecx+ebx+8],dword[ebp-c8],dword[ebp-c6] +%endmacro + +%macro PIXEL32_61 0 + Interp6 [ecx+ebx+8],dword[ebp-c8],dword[ebp-c9] +%endmacro + +%macro PIXEL32_82 0 + Interp8 [ecx+ebx+8],eax,dword[ebp-c8] +%endmacro + +%macro PIXEL32_83 0 + Interp8 [ecx+ebx+8],dword[ebp-c8],dword[ebp-c6] +%endmacro + +%macro PIXEL33_0 0 + mov [ecx+ebx+12],eax +%endmacro + +%macro PIXEL33_11 0 + Interp1 [ecx+ebx+12],eax,dword[ebp-c6] +%endmacro + +%macro PIXEL33_12 0 + Interp1 [ecx+ebx+12],eax,dword[ebp-c8] +%endmacro + +%macro PIXEL33_20 0 + Interp2 [ecx+ebx+12],eax,dword[ebp-c8],dword[ebp-c6] +%endmacro + +%macro PIXEL33_50 0 + Interp5 [ecx+ebx+12],dword[ebp-c8],dword[ebp-c6] +%endmacro + +%macro PIXEL33_80 0 + Interp8 [ecx+ebx+12],eax,dword[ebp-c9] +%endmacro + +%macro PIXEL33_81 0 + Interp8 [ecx+ebx+12],eax,dword[ebp-c6] +%endmacro + +%macro PIXEL33_82 0 + Interp8 [ecx+ebx+12],eax,dword[ebp-c8] +%endmacro + +inbuffer equ 8 +outbuffer equ 12 +Xres equ 16 +Yres equ 20 +pitch equ 24 +offset equ 28 + +linesleft equ 4 +xcounter equ 8 +cross equ 12 +nextline equ 16 +prevline equ 20 +w1 equ 24 +w2 equ 28 +w3 equ 32 +w4 equ 36 +w5 equ 40 +w6 equ 44 +w7 equ 48 +w8 equ 52 +w9 equ 56 +c1 equ 60 +c2 equ 64 +c3 equ 68 +c4 equ 72 +c5 equ 76 +c6 equ 80 +c7 equ 84 +c8 equ 88 +c9 equ 92 +localsize equ 92 + +NEWSYM hq4x_32 + push ebp + mov ebp,esp + sub esp, localsize + pushad + + mov esi,[ebp+inbuffer] + mov edi,[ebp+outbuffer] + mov edx,[ebp+Yres] + mov [ebp-linesleft],edx + mov ebx,[ebp+Xres] + shl ebx,1 + mov dword[ebp-prevline],0 + mov eax, ebx + add eax, [ebp+offset] + mov dword[ebp-nextline],eax +.loopy + mov ecx,[ebp+Xres] + sub ecx,2 ; x={Xres-2, Xres-1} are special cases. + mov dword[ebp-xcounter],ecx + ; x=0 - special case + mov ebx,[ebp-prevline] + movq mm5,[esi+ebx] + movq mm6,[esi] + mov ebx,[ebp-nextline] + movq mm7,[esi+ebx] + movd eax,mm5 + movzx edx,ax + mov [ebp-w1],edx + mov [ebp-w2],edx + shr eax,16 + mov [ebp-w3],eax + movd eax,mm6 + movzx edx,ax + mov [ebp-w4],edx + mov [ebp-w5],edx + shr eax,16 + mov [ebp-w6],eax + movd eax,mm7 + movzx edx,ax + mov [ebp-w7],edx + mov [ebp-w8],edx + shr eax,16 + mov [ebp-w9],eax + jmp .flags +.loopx + mov ebx,[ebp-prevline] + movq mm5,[esi+ebx-2] + movq mm6,[esi-2] + mov ebx,[ebp-nextline] + movq mm7,[esi+ebx-2] + movd eax,mm5 + movzx edx,ax + mov [ebp-w1],edx + shr eax,16 + mov [ebp-w2],eax + psrlq mm5,32 + movd eax,mm5 + movzx edx,ax + mov [ebp-w3],edx + movd eax,mm6 + movzx edx,ax + mov [ebp-w4],edx + shr eax,16 + mov [ebp-w5],eax + psrlq mm6,32 + movd eax,mm6 + movzx edx,ax + mov [ebp-w6],edx + movd eax,mm7 + movzx edx,ax + mov [ebp-w7],edx + shr eax,16 + mov [ebp-w8],eax + psrlq mm7,32 + movd eax,mm7 + movzx edx,ax + mov [ebp-w9],edx +.flags + mov ebx,RGBtoYUV + mov eax,[ebp-w5] + xor ecx,ecx + movd mm5,[ebx+eax*4] + mov dword[ebp-cross],0 + + mov edx,[ebp-w2] + cmp eax,edx + je .noflag2 + or dword[ebp-cross],1 + movq mm1,mm5 + movd mm2,[ebx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 + test edx,edx + jz .noflag2 + or ecx,2 +.noflag2 + mov edx,[ebp-w4] + cmp eax,edx + je .noflag4 + or dword[ebp-cross],2 + movq mm1,mm5 + movd mm2,[ebx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 + test edx,edx + jz .noflag4 + or ecx,8 +.noflag4 + mov edx,[ebp-w6] + cmp eax,edx + je .noflag6 + or dword[ebp-cross],4 + movq mm1,mm5 + movd mm2,[ebx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 + test edx,edx + jz .noflag6 + or ecx,16 +.noflag6 + mov edx,[ebp-w8] + cmp eax,edx + je .noflag8 + or dword[ebp-cross],8 + movq mm1,mm5 + movd mm2,[ebx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 + test edx,edx + jz .noflag8 + or ecx,64 +.noflag8 + cmp dword[ebp-cross],0 + jnz .testflag1 + mov ebx,LUT16to32 + mov eax,[ebx+eax*4] + mov ebx,[ebp+pitch] + AUXADDRESS + mov [edi],eax + mov [edi+4],eax + mov [edi+8],eax + mov [edi+12],eax + mov [edi+ebx],eax + mov [edi+ebx+4],eax + mov [edi+ebx+8],eax + mov [edi+ebx+12],eax + mov [ecx],eax + mov [ecx+4],eax + mov [ecx+8],eax + mov [ecx+12],eax + mov [ecx+ebx],eax + mov [ecx+ebx+4],eax + mov [ecx+ebx+8],eax + mov [ecx+ebx+12],eax + jmp .loopx_end +.testflag1 + mov edx,[ebp-w1] + cmp eax,edx + je .noflag1 + movq mm1,mm5 + movd mm2,[ebx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 + test edx,edx + jz .noflag1 + or ecx,1 +.noflag1 + mov edx,[ebp-w3] + cmp eax,edx + je .noflag3 + movq mm1,mm5 + movd mm2,[ebx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 + test edx,edx + jz .noflag3 + or ecx,4 +.noflag3 + mov edx,[ebp-w7] + cmp eax,edx + je .noflag7 + movq mm1,mm5 + movd mm2,[ebx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 + test edx,edx + jz .noflag7 + or ecx,32 +.noflag7 + mov edx,[ebp-w9] + cmp eax,edx + je .noflag9 + movq mm1,mm5 + movd mm2,[ebx+edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,[threshold] + movd edx,mm1 + test edx,edx + jz .noflag9 + or ecx,128 +.noflag9 + mov ebx,LUT16to32 + mov eax,[ebx+eax*4] + mov edx,[ebp-w2] + mov edx,[ebx+edx*4] + mov [ebp-c2],edx + mov edx,[ebp-w4] + mov edx,[ebx+edx*4] + mov [ebp-c4],edx + mov edx,[ebp-w6] + mov edx,[ebx+edx*4] + mov [ebp-c6],edx + mov edx,[ebp-w8] + mov edx,[ebx+edx*4] + mov [ebp-c8],edx + test ecx,0x005A + jz .switch + mov edx,[ebp-w1] + mov edx,[ebx+edx*4] + mov [ebp-c1],edx + mov edx,[ebp-w3] + mov edx,[ebx+edx*4] + mov [ebp-c3],edx + mov edx,[ebp-w7] + mov edx,[ebx+edx*4] + mov [ebp-c7],edx + mov edx,[ebp-w9] + mov edx,[ebx+edx*4] + mov [ebp-c9],edx +.switch + mov ebx,[ebp+pitch] + jmp [FuncTable+ecx*4] + +..@flag0 +..@flag1 +..@flag4 +..@flag32 +..@flag128 +..@flag5 +..@flag132 +..@flag160 +..@flag33 +..@flag129 +..@flag36 +..@flag133 +..@flag164 +..@flag161 +..@flag37 +..@flag165 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_60 + PIXEL03_20 + PIXEL10_60 + PIXEL11_70 + PIXEL12_70 + PIXEL13_60 + PIXEL20_60 + PIXEL21_70 + PIXEL22_70 + PIXEL23_60 + PIXEL30_20 + PIXEL31_60 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag2 +..@flag34 +..@flag130 +..@flag162 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + PIXEL02_10 + PIXEL03_80 + PIXEL10_61 + PIXEL11_30 + PIXEL12_30 + PIXEL13_61 + PIXEL20_60 + PIXEL21_70 + PIXEL22_70 + PIXEL23_60 + PIXEL30_20 + PIXEL31_60 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag16 +..@flag17 +..@flag48 +..@flag49 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_61 + PIXEL03_80 + PIXEL10_60 + PIXEL11_70 + PIXEL12_30 + PIXEL13_10 + PIXEL20_60 + PIXEL21_70 + PIXEL22_30 + PIXEL23_10 + PIXEL30_20 + PIXEL31_60 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag64 +..@flag65 +..@flag68 +..@flag69 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_60 + PIXEL03_20 + PIXEL10_60 + PIXEL11_70 + PIXEL12_70 + PIXEL13_60 + PIXEL20_61 + PIXEL21_30 + PIXEL22_30 + PIXEL23_61 + PIXEL30_80 + PIXEL31_10 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag8 +..@flag12 +..@flag136 +..@flag140 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_60 + PIXEL03_20 + PIXEL10_10 + PIXEL11_30 + PIXEL12_70 + PIXEL13_60 + PIXEL20_10 + PIXEL21_30 + PIXEL22_70 + PIXEL23_60 + PIXEL30_80 + PIXEL31_61 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag3 +..@flag35 +..@flag131 +..@flag163 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + PIXEL02_10 + PIXEL03_80 + PIXEL10_81 + PIXEL11_31 + PIXEL12_30 + PIXEL13_61 + PIXEL20_60 + PIXEL21_70 + PIXEL22_70 + PIXEL23_60 + PIXEL30_20 + PIXEL31_60 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag6 +..@flag38 +..@flag134 +..@flag166 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + PIXEL02_32 + PIXEL03_82 + PIXEL10_61 + PIXEL11_30 + PIXEL12_32 + PIXEL13_82 + PIXEL20_60 + PIXEL21_70 + PIXEL22_70 + PIXEL23_60 + PIXEL30_20 + PIXEL31_60 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag20 +..@flag21 +..@flag52 +..@flag53 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_81 + PIXEL03_81 + PIXEL10_60 + PIXEL11_70 + PIXEL12_31 + PIXEL13_31 + PIXEL20_60 + PIXEL21_70 + PIXEL22_30 + PIXEL23_10 + PIXEL30_20 + PIXEL31_60 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag144 +..@flag145 +..@flag176 +..@flag177 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_61 + PIXEL03_80 + PIXEL10_60 + PIXEL11_70 + PIXEL12_30 + PIXEL13_10 + PIXEL20_60 + PIXEL21_70 + PIXEL22_32 + PIXEL23_32 + PIXEL30_20 + PIXEL31_60 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag192 +..@flag193 +..@flag196 +..@flag197 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_60 + PIXEL03_20 + PIXEL10_60 + PIXEL11_70 + PIXEL12_70 + PIXEL13_60 + PIXEL20_61 + PIXEL21_30 + PIXEL22_31 + PIXEL23_81 + PIXEL30_80 + PIXEL31_10 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag96 +..@flag97 +..@flag100 +..@flag101 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_60 + PIXEL03_20 + PIXEL10_60 + PIXEL11_70 + PIXEL12_70 + PIXEL13_60 + PIXEL20_82 + PIXEL21_32 + PIXEL22_30 + PIXEL23_61 + PIXEL30_82 + PIXEL31_32 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag40 +..@flag44 +..@flag168 +..@flag172 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_60 + PIXEL03_20 + PIXEL10_10 + PIXEL11_30 + PIXEL12_70 + PIXEL13_60 + PIXEL20_31 + PIXEL21_31 + PIXEL22_70 + PIXEL23_60 + PIXEL30_81 + PIXEL31_81 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag9 +..@flag13 +..@flag137 +..@flag141 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_60 + PIXEL03_20 + PIXEL10_32 + PIXEL11_32 + PIXEL12_70 + PIXEL13_60 + PIXEL20_10 + PIXEL21_30 + PIXEL22_70 + PIXEL23_60 + PIXEL30_80 + PIXEL31_61 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag18 +..@flag50 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_10,PIXEL03_80,PIXEL12_30,PIXEL13_10,PIXEL02_50,PIXEL03_50,PIXEL12_0,PIXEL13_50 + PIXEL10_61 + PIXEL11_30 + PIXEL20_60 + PIXEL21_70 + PIXEL22_30 + PIXEL23_10 + PIXEL30_20 + PIXEL31_60 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag80 +..@flag81 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_61 + PIXEL03_80 + PIXEL10_60 + PIXEL11_70 + PIXEL12_30 + PIXEL13_10 + PIXEL20_61 + PIXEL21_30 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_30,PIXEL23_10,PIXEL32_10,PIXEL33_80,PIXEL22_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + PIXEL30_80 + PIXEL31_10 + jmp .loopx_end +..@flag72 +..@flag76 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_60 + PIXEL03_20 + PIXEL10_10 + PIXEL11_30 + PIXEL12_70 + PIXEL13_60 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_10,PIXEL21_30,PIXEL30_80,PIXEL31_10,PIXEL20_50,PIXEL21_0,PIXEL30_50,PIXEL31_50 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag10 +..@flag138 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_80,PIXEL01_10,PIXEL10_10,PIXEL11_30,PIXEL00_50,PIXEL01_50,PIXEL10_50,PIXEL11_0 + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_61 + PIXEL20_10 + PIXEL21_30 + PIXEL22_70 + PIXEL23_60 + PIXEL30_80 + PIXEL31_61 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag66 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + PIXEL02_10 + PIXEL03_80 + PIXEL10_61 + PIXEL11_30 + PIXEL12_30 + PIXEL13_61 + PIXEL20_61 + PIXEL21_30 + PIXEL22_30 + PIXEL23_61 + PIXEL30_80 + PIXEL31_10 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag24 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_61 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_61 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag7 +..@flag39 +..@flag135 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + PIXEL02_32 + PIXEL03_82 + PIXEL10_81 + PIXEL11_31 + PIXEL12_32 + PIXEL13_82 + PIXEL20_60 + PIXEL21_70 + PIXEL22_70 + PIXEL23_60 + PIXEL30_20 + PIXEL31_60 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag148 +..@flag149 +..@flag180 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_81 + PIXEL03_81 + PIXEL10_60 + PIXEL11_70 + PIXEL12_31 + PIXEL13_31 + PIXEL20_60 + PIXEL21_70 + PIXEL22_32 + PIXEL23_32 + PIXEL30_20 + PIXEL31_60 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag224 +..@flag228 +..@flag225 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_60 + PIXEL03_20 + PIXEL10_60 + PIXEL11_70 + PIXEL12_70 + PIXEL13_60 + PIXEL20_82 + PIXEL21_32 + PIXEL22_31 + PIXEL23_81 + PIXEL30_82 + PIXEL31_32 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag41 +..@flag169 +..@flag45 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_60 + PIXEL03_20 + PIXEL10_32 + PIXEL11_32 + PIXEL12_70 + PIXEL13_60 + PIXEL20_31 + PIXEL21_31 + PIXEL22_70 + PIXEL23_60 + PIXEL30_81 + PIXEL31_81 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag22 +..@flag54 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL10_61 + PIXEL11_30 + PIXEL12_0 + PIXEL20_60 + PIXEL21_70 + PIXEL22_30 + PIXEL23_10 + PIXEL30_20 + PIXEL31_60 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag208 +..@flag209 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_61 + PIXEL03_80 + PIXEL10_60 + PIXEL11_70 + PIXEL12_30 + PIXEL13_10 + PIXEL20_61 + PIXEL21_30 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + PIXEL30_80 + PIXEL31_10 + jmp .loopx_end +..@flag104 +..@flag108 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_60 + PIXEL03_20 + PIXEL10_10 + PIXEL11_30 + PIXEL12_70 + PIXEL13_60 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag11 +..@flag139 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_61 + PIXEL20_10 + PIXEL21_30 + PIXEL22_70 + PIXEL23_60 + PIXEL30_80 + PIXEL31_61 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag19 +..@flag51 + AUXADDRESS + DiffOrNot ebp-w2,ebp-w6,PIXEL00_81,PIXEL01_31,PIXEL02_10,PIXEL03_80,PIXEL12_30,PIXEL13_10,PIXEL00_12,PIXEL01_14,PIXEL02_83,PIXEL03_50,PIXEL12_70,PIXEL13_21 + PIXEL10_81 + PIXEL11_31 + PIXEL20_60 + PIXEL21_70 + PIXEL22_30 + PIXEL23_10 + PIXEL30_20 + PIXEL31_60 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag146 +..@flag178 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_10,PIXEL03_80,PIXEL12_30,PIXEL13_10,PIXEL23_32,PIXEL33_82,PIXEL02_21,PIXEL03_50,PIXEL12_70,PIXEL13_83,PIXEL23_13,PIXEL33_11 + PIXEL10_61 + PIXEL11_30 + PIXEL20_60 + PIXEL21_70 + PIXEL22_32 + PIXEL30_20 + PIXEL31_60 + PIXEL32_82 + jmp .loopx_end +..@flag84 +..@flag85 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_81 + DiffOrNot ebp-w6,ebp-w8,PIXEL03_81,PIXEL13_31,PIXEL22_30,PIXEL23_10,PIXEL32_10,PIXEL33_80,PIXEL03_12,PIXEL13_14,PIXEL22_70,PIXEL23_83,PIXEL32_21,PIXEL33_50 + PIXEL10_60 + PIXEL11_70 + PIXEL12_31 + PIXEL20_61 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + jmp .loopx_end +..@flag112 +..@flag113 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_61 + PIXEL03_80 + PIXEL10_60 + PIXEL11_70 + PIXEL12_30 + PIXEL13_10 + PIXEL20_82 + PIXEL21_32 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_30,PIXEL23_10,PIXEL30_82,PIXEL31_32,PIXEL32_10,PIXEL33_80,PIXEL22_70,PIXEL23_21,PIXEL30_11,PIXEL31_13,PIXEL32_83,PIXEL33_50 + jmp .loopx_end +..@flag200 +..@flag204 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_60 + PIXEL03_20 + PIXEL10_10 + PIXEL11_30 + PIXEL12_70 + PIXEL13_60 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_10,PIXEL21_30,PIXEL30_80,PIXEL31_10,PIXEL32_31,PIXEL33_81,PIXEL20_21,PIXEL21_70,PIXEL30_50,PIXEL31_83,PIXEL32_14,PIXEL33_12 + PIXEL22_31 + PIXEL23_81 + jmp .loopx_end +..@flag73 +..@flag77 + AUXADDRESS + DiffOrNot ebp-w8,ebp-w4,PIXEL00_82,PIXEL10_32,PIXEL20_10,PIXEL21_30,PIXEL30_80,PIXEL31_10,PIXEL00_11,PIXEL10_13,PIXEL20_83,PIXEL21_70,PIXEL30_50,PIXEL31_21 + PIXEL01_82 + PIXEL02_60 + PIXEL03_20 + PIXEL11_32 + PIXEL12_70 + PIXEL13_60 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag42 +..@flag170 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_80,PIXEL01_10,PIXEL10_10,PIXEL11_30,PIXEL20_31,PIXEL30_81,PIXEL00_50,PIXEL01_21,PIXEL10_83,PIXEL11_70,PIXEL20_14,PIXEL30_12 + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_61 + PIXEL21_31 + PIXEL22_70 + PIXEL23_60 + PIXEL31_81 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag14 +..@flag142 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_80,PIXEL01_10,PIXEL02_32,PIXEL03_82,PIXEL10_10,PIXEL11_30,PIXEL00_50,PIXEL01_83,PIXEL02_13,PIXEL03_11,PIXEL10_21,PIXEL11_70 + PIXEL12_32 + PIXEL13_82 + PIXEL20_10 + PIXEL21_30 + PIXEL22_70 + PIXEL23_60 + PIXEL30_80 + PIXEL31_61 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag67 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + PIXEL02_10 + PIXEL03_80 + PIXEL10_81 + PIXEL11_31 + PIXEL12_30 + PIXEL13_61 + PIXEL20_61 + PIXEL21_30 + PIXEL22_30 + PIXEL23_61 + PIXEL30_80 + PIXEL31_10 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag70 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + PIXEL02_32 + PIXEL03_82 + PIXEL10_61 + PIXEL11_30 + PIXEL12_32 + PIXEL13_82 + PIXEL20_61 + PIXEL21_30 + PIXEL22_30 + PIXEL23_61 + PIXEL30_80 + PIXEL31_10 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag28 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_81 + PIXEL03_81 + PIXEL10_10 + PIXEL11_30 + PIXEL12_31 + PIXEL13_31 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_61 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag152 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_61 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_32 + PIXEL23_32 + PIXEL30_80 + PIXEL31_61 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag194 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + PIXEL02_10 + PIXEL03_80 + PIXEL10_61 + PIXEL11_30 + PIXEL12_30 + PIXEL13_61 + PIXEL20_61 + PIXEL21_30 + PIXEL22_31 + PIXEL23_81 + PIXEL30_80 + PIXEL31_10 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag98 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + PIXEL02_10 + PIXEL03_80 + PIXEL10_61 + PIXEL11_30 + PIXEL12_30 + PIXEL13_61 + PIXEL20_82 + PIXEL21_32 + PIXEL22_30 + PIXEL23_61 + PIXEL30_82 + PIXEL31_32 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag56 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_61 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + PIXEL20_31 + PIXEL21_31 + PIXEL22_30 + PIXEL23_10 + PIXEL30_81 + PIXEL31_81 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag25 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_61 + PIXEL03_80 + PIXEL10_32 + PIXEL11_32 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_61 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag26 +..@flag31 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL11_0 + PIXEL12_0 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_61 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag82 +..@flag214 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL10_61 + PIXEL11_30 + PIXEL12_0 + PIXEL20_61 + PIXEL21_30 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + PIXEL30_80 + PIXEL31_10 + jmp .loopx_end +..@flag88 +..@flag248 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_61 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + jmp .loopx_end +..@flag74 +..@flag107 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_61 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag27 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_61 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag86 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL10_61 + PIXEL11_30 + PIXEL12_0 + PIXEL20_61 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_10 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag216 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_61 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + PIXEL30_80 + PIXEL31_10 + jmp .loopx_end +..@flag106 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + PIXEL02_10 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_61 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag30 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL10_10 + PIXEL11_30 + PIXEL12_0 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_61 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag210 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + PIXEL02_10 + PIXEL03_80 + PIXEL10_61 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + PIXEL20_61 + PIXEL21_30 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + PIXEL30_80 + PIXEL31_10 + jmp .loopx_end +..@flag120 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_61 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag75 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_61 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_61 + PIXEL30_80 + PIXEL31_10 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag29 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_81 + PIXEL03_81 + PIXEL10_32 + PIXEL11_32 + PIXEL12_31 + PIXEL13_31 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_61 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag198 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + PIXEL02_32 + PIXEL03_82 + PIXEL10_61 + PIXEL11_30 + PIXEL12_32 + PIXEL13_82 + PIXEL20_61 + PIXEL21_30 + PIXEL22_31 + PIXEL23_81 + PIXEL30_80 + PIXEL31_10 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag184 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_61 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + PIXEL20_31 + PIXEL21_31 + PIXEL22_32 + PIXEL23_32 + PIXEL30_81 + PIXEL31_81 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag99 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + PIXEL02_10 + PIXEL03_80 + PIXEL10_81 + PIXEL11_31 + PIXEL12_30 + PIXEL13_61 + PIXEL20_82 + PIXEL21_32 + PIXEL22_30 + PIXEL23_61 + PIXEL30_82 + PIXEL31_32 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag57 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_61 + PIXEL03_80 + PIXEL10_32 + PIXEL11_32 + PIXEL12_30 + PIXEL13_10 + PIXEL20_31 + PIXEL21_31 + PIXEL22_30 + PIXEL23_10 + PIXEL30_81 + PIXEL31_81 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag71 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + PIXEL02_32 + PIXEL03_82 + PIXEL10_81 + PIXEL11_31 + PIXEL12_32 + PIXEL13_82 + PIXEL20_61 + PIXEL21_30 + PIXEL22_30 + PIXEL23_61 + PIXEL30_80 + PIXEL31_10 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag156 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_81 + PIXEL03_81 + PIXEL10_10 + PIXEL11_30 + PIXEL12_31 + PIXEL13_31 + PIXEL20_10 + PIXEL21_30 + PIXEL22_32 + PIXEL23_32 + PIXEL30_80 + PIXEL31_61 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag226 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + PIXEL02_10 + PIXEL03_80 + PIXEL10_61 + PIXEL11_30 + PIXEL12_30 + PIXEL13_61 + PIXEL20_82 + PIXEL21_32 + PIXEL22_31 + PIXEL23_81 + PIXEL30_82 + PIXEL31_32 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag60 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_81 + PIXEL03_81 + PIXEL10_10 + PIXEL11_30 + PIXEL12_31 + PIXEL13_31 + PIXEL20_31 + PIXEL21_31 + PIXEL22_30 + PIXEL23_10 + PIXEL30_81 + PIXEL31_81 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag195 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + PIXEL02_10 + PIXEL03_80 + PIXEL10_81 + PIXEL11_31 + PIXEL12_30 + PIXEL13_61 + PIXEL20_61 + PIXEL21_30 + PIXEL22_31 + PIXEL23_81 + PIXEL30_80 + PIXEL31_10 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag102 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + PIXEL02_32 + PIXEL03_82 + PIXEL10_61 + PIXEL11_30 + PIXEL12_32 + PIXEL13_82 + PIXEL20_82 + PIXEL21_32 + PIXEL22_30 + PIXEL23_61 + PIXEL30_82 + PIXEL31_32 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag153 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_61 + PIXEL03_80 + PIXEL10_32 + PIXEL11_32 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_32 + PIXEL23_32 + PIXEL30_80 + PIXEL31_61 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag58 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_80,PIXEL01_10,PIXEL10_10,PIXEL11_30,PIXEL00_20,PIXEL01_12,PIXEL10_11,PIXEL11_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_10,PIXEL03_80,PIXEL12_30,PIXEL13_10,PIXEL02_11,PIXEL03_20,PIXEL12_0,PIXEL13_12 + PIXEL20_31 + PIXEL21_31 + PIXEL22_30 + PIXEL23_10 + PIXEL30_81 + PIXEL31_81 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag83 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_10,PIXEL03_80,PIXEL12_30,PIXEL13_10,PIXEL02_11,PIXEL03_20,PIXEL12_0,PIXEL13_12 + PIXEL10_81 + PIXEL11_31 + PIXEL20_61 + PIXEL21_30 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_30,PIXEL23_10,PIXEL32_10,PIXEL33_80,PIXEL22_0,PIXEL23_11,PIXEL32_12,PIXEL33_20 + PIXEL30_80 + PIXEL31_10 + jmp .loopx_end +..@flag92 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_81 + PIXEL03_81 + PIXEL10_10 + PIXEL11_30 + PIXEL12_31 + PIXEL13_31 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_10,PIXEL21_30,PIXEL30_80,PIXEL31_10,PIXEL20_12,PIXEL21_0,PIXEL30_20,PIXEL31_11 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_30,PIXEL23_10,PIXEL32_10,PIXEL33_80,PIXEL22_0,PIXEL23_11,PIXEL32_12,PIXEL33_20 + jmp .loopx_end +..@flag202 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_80,PIXEL01_10,PIXEL10_10,PIXEL11_30,PIXEL00_20,PIXEL01_12,PIXEL10_11,PIXEL11_0 + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_61 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_10,PIXEL21_30,PIXEL30_80,PIXEL31_10,PIXEL20_12,PIXEL21_0,PIXEL30_20,PIXEL31_11 + PIXEL22_31 + PIXEL23_81 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag78 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_80,PIXEL01_10,PIXEL10_10,PIXEL11_30,PIXEL00_20,PIXEL01_12,PIXEL10_11,PIXEL11_0 + PIXEL02_32 + PIXEL03_82 + PIXEL12_32 + PIXEL13_82 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_10,PIXEL21_30,PIXEL30_80,PIXEL31_10,PIXEL20_12,PIXEL21_0,PIXEL30_20,PIXEL31_11 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag154 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_80,PIXEL01_10,PIXEL10_10,PIXEL11_30,PIXEL00_20,PIXEL01_12,PIXEL10_11,PIXEL11_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_10,PIXEL03_80,PIXEL12_30,PIXEL13_10,PIXEL02_11,PIXEL03_20,PIXEL12_0,PIXEL13_12 + PIXEL20_10 + PIXEL21_30 + PIXEL22_32 + PIXEL23_32 + PIXEL30_80 + PIXEL31_61 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag114 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_10,PIXEL03_80,PIXEL12_30,PIXEL13_10,PIXEL02_11,PIXEL03_20,PIXEL12_0,PIXEL13_12 + PIXEL10_61 + PIXEL11_30 + PIXEL20_82 + PIXEL21_32 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_30,PIXEL23_10,PIXEL32_10,PIXEL33_80,PIXEL22_0,PIXEL23_11,PIXEL32_12,PIXEL33_20 + PIXEL30_82 + PIXEL31_32 + jmp .loopx_end +..@flag89 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_61 + PIXEL03_80 + PIXEL10_32 + PIXEL11_32 + PIXEL12_30 + PIXEL13_10 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_10,PIXEL21_30,PIXEL30_80,PIXEL31_10,PIXEL20_12,PIXEL21_0,PIXEL30_20,PIXEL31_11 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_30,PIXEL23_10,PIXEL32_10,PIXEL33_80,PIXEL22_0,PIXEL23_11,PIXEL32_12,PIXEL33_20 + jmp .loopx_end +..@flag90 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_80,PIXEL01_10,PIXEL10_10,PIXEL11_30,PIXEL00_20,PIXEL01_12,PIXEL10_11,PIXEL11_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_10,PIXEL03_80,PIXEL12_30,PIXEL13_10,PIXEL02_11,PIXEL03_20,PIXEL12_0,PIXEL13_12 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_10,PIXEL21_30,PIXEL30_80,PIXEL31_10,PIXEL20_12,PIXEL21_0,PIXEL30_20,PIXEL31_11 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_30,PIXEL23_10,PIXEL32_10,PIXEL33_80,PIXEL22_0,PIXEL23_11,PIXEL32_12,PIXEL33_20 + jmp .loopx_end +..@flag55 +..@flag23 + AUXADDRESS + DiffOrNot ebp-w2,ebp-w6,PIXEL00_81,PIXEL01_31,PIXEL02_0,PIXEL03_0,PIXEL12_0,PIXEL13_0,PIXEL00_12,PIXEL01_14,PIXEL02_83,PIXEL03_50,PIXEL12_70,PIXEL13_21 + PIXEL10_81 + PIXEL11_31 + PIXEL20_60 + PIXEL21_70 + PIXEL22_30 + PIXEL23_10 + PIXEL30_20 + PIXEL31_60 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag182 +..@flag150 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL12_0,PIXEL13_0,PIXEL23_32,PIXEL33_82,PIXEL02_21,PIXEL03_50,PIXEL12_70,PIXEL13_83,PIXEL23_13,PIXEL33_11 + PIXEL10_61 + PIXEL11_30 + PIXEL20_60 + PIXEL21_70 + PIXEL22_32 + PIXEL30_20 + PIXEL31_60 + PIXEL32_82 + jmp .loopx_end +..@flag213 +..@flag212 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_81 + DiffOrNot ebp-w6,ebp-w8,PIXEL03_81,PIXEL13_31,PIXEL22_0,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL03_12,PIXEL13_14,PIXEL22_70,PIXEL23_83,PIXEL32_21,PIXEL33_50 + PIXEL10_60 + PIXEL11_70 + PIXEL12_31 + PIXEL20_61 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + jmp .loopx_end +..@flag241 +..@flag240 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_61 + PIXEL03_80 + PIXEL10_60 + PIXEL11_70 + PIXEL12_30 + PIXEL13_10 + PIXEL20_82 + PIXEL21_32 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_0,PIXEL23_0,PIXEL30_82,PIXEL31_32,PIXEL32_0,PIXEL33_0,PIXEL22_70,PIXEL23_21,PIXEL30_11,PIXEL31_13,PIXEL32_83,PIXEL33_50 + jmp .loopx_end +..@flag236 +..@flag232 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_60 + PIXEL03_20 + PIXEL10_10 + PIXEL11_30 + PIXEL12_70 + PIXEL13_60 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL21_0,PIXEL30_0,PIXEL31_0,PIXEL32_31,PIXEL33_81,PIXEL20_21,PIXEL21_70,PIXEL30_50,PIXEL31_83,PIXEL32_14,PIXEL33_12 + PIXEL22_31 + PIXEL23_81 + jmp .loopx_end +..@flag109 +..@flag105 + AUXADDRESS + DiffOrNot ebp-w8,ebp-w4,PIXEL00_82,PIXEL10_32,PIXEL20_0,PIXEL21_0,PIXEL30_0,PIXEL31_0,PIXEL00_11,PIXEL10_13,PIXEL20_83,PIXEL21_70,PIXEL30_50,PIXEL31_21 + PIXEL01_82 + PIXEL02_60 + PIXEL03_20 + PIXEL11_32 + PIXEL12_70 + PIXEL13_60 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag171 +..@flag43 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL11_0,PIXEL20_31,PIXEL30_81,PIXEL00_50,PIXEL01_21,PIXEL10_83,PIXEL11_70,PIXEL20_14,PIXEL30_12 + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_61 + PIXEL21_31 + PIXEL22_70 + PIXEL23_60 + PIXEL31_81 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag143 +..@flag15 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL02_32,PIXEL03_82,PIXEL10_0,PIXEL11_0,PIXEL00_50,PIXEL01_83,PIXEL02_13,PIXEL03_11,PIXEL10_21,PIXEL11_70 + PIXEL12_32 + PIXEL13_82 + PIXEL20_10 + PIXEL21_30 + PIXEL22_70 + PIXEL23_60 + PIXEL30_80 + PIXEL31_61 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag124 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_81 + PIXEL03_81 + PIXEL10_10 + PIXEL11_30 + PIXEL12_31 + PIXEL13_31 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag203 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_61 + PIXEL20_10 + PIXEL21_30 + PIXEL22_31 + PIXEL23_81 + PIXEL30_80 + PIXEL31_10 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag62 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL10_10 + PIXEL11_30 + PIXEL12_0 + PIXEL20_31 + PIXEL21_31 + PIXEL22_30 + PIXEL23_10 + PIXEL30_81 + PIXEL31_81 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag211 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + PIXEL02_10 + PIXEL03_80 + PIXEL10_81 + PIXEL11_31 + PIXEL12_30 + PIXEL13_10 + PIXEL20_61 + PIXEL21_30 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + PIXEL30_80 + PIXEL31_10 + jmp .loopx_end +..@flag118 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL10_61 + PIXEL11_30 + PIXEL12_0 + PIXEL20_82 + PIXEL21_32 + PIXEL22_30 + PIXEL23_10 + PIXEL30_82 + PIXEL31_32 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag217 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_61 + PIXEL03_80 + PIXEL10_32 + PIXEL11_32 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + PIXEL30_80 + PIXEL31_10 + jmp .loopx_end +..@flag110 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + PIXEL02_32 + PIXEL03_82 + PIXEL10_10 + PIXEL11_30 + PIXEL12_32 + PIXEL13_82 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag155 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_32 + PIXEL23_32 + PIXEL30_80 + PIXEL31_61 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag188 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_81 + PIXEL03_81 + PIXEL10_10 + PIXEL11_30 + PIXEL12_31 + PIXEL13_31 + PIXEL20_31 + PIXEL21_31 + PIXEL22_32 + PIXEL23_32 + PIXEL30_81 + PIXEL31_81 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag185 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_61 + PIXEL03_80 + PIXEL10_32 + PIXEL11_32 + PIXEL12_30 + PIXEL13_10 + PIXEL20_31 + PIXEL21_31 + PIXEL22_32 + PIXEL23_32 + PIXEL30_81 + PIXEL31_81 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag61 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_81 + PIXEL03_81 + PIXEL10_32 + PIXEL11_32 + PIXEL12_31 + PIXEL13_31 + PIXEL20_31 + PIXEL21_31 + PIXEL22_30 + PIXEL23_10 + PIXEL30_81 + PIXEL31_81 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag157 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_81 + PIXEL03_81 + PIXEL10_32 + PIXEL11_32 + PIXEL12_31 + PIXEL13_31 + PIXEL20_10 + PIXEL21_30 + PIXEL22_32 + PIXEL23_32 + PIXEL30_80 + PIXEL31_61 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag103 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + PIXEL02_32 + PIXEL03_82 + PIXEL10_81 + PIXEL11_31 + PIXEL12_32 + PIXEL13_82 + PIXEL20_82 + PIXEL21_32 + PIXEL22_30 + PIXEL23_61 + PIXEL30_82 + PIXEL31_32 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag227 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + PIXEL02_10 + PIXEL03_80 + PIXEL10_81 + PIXEL11_31 + PIXEL12_30 + PIXEL13_61 + PIXEL20_82 + PIXEL21_32 + PIXEL22_31 + PIXEL23_81 + PIXEL30_82 + PIXEL31_32 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag230 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + PIXEL02_32 + PIXEL03_82 + PIXEL10_61 + PIXEL11_30 + PIXEL12_32 + PIXEL13_82 + PIXEL20_82 + PIXEL21_32 + PIXEL22_31 + PIXEL23_81 + PIXEL30_82 + PIXEL31_32 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag199 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + PIXEL02_32 + PIXEL03_82 + PIXEL10_81 + PIXEL11_31 + PIXEL12_32 + PIXEL13_82 + PIXEL20_61 + PIXEL21_30 + PIXEL22_31 + PIXEL23_81 + PIXEL30_80 + PIXEL31_10 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag220 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_81 + PIXEL03_81 + PIXEL10_10 + PIXEL11_30 + PIXEL12_31 + PIXEL13_31 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_10,PIXEL21_30,PIXEL30_80,PIXEL31_10,PIXEL20_12,PIXEL21_0,PIXEL30_20,PIXEL31_11 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + jmp .loopx_end +..@flag158 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_80,PIXEL01_10,PIXEL10_10,PIXEL11_30,PIXEL00_20,PIXEL01_12,PIXEL10_11,PIXEL11_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL12_0 + PIXEL20_10 + PIXEL21_30 + PIXEL22_32 + PIXEL23_32 + PIXEL30_80 + PIXEL31_61 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag234 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_80,PIXEL01_10,PIXEL10_10,PIXEL11_30,PIXEL00_20,PIXEL01_12,PIXEL10_11,PIXEL11_0 + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_61 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + PIXEL22_31 + PIXEL23_81 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag242 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_10,PIXEL03_80,PIXEL12_30,PIXEL13_10,PIXEL02_11,PIXEL03_20,PIXEL12_0,PIXEL13_12 + PIXEL10_61 + PIXEL11_30 + PIXEL20_82 + PIXEL21_32 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + PIXEL30_82 + PIXEL31_32 + jmp .loopx_end +..@flag59 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_10,PIXEL03_80,PIXEL12_30,PIXEL13_10,PIXEL02_11,PIXEL03_20,PIXEL12_0,PIXEL13_12 + PIXEL11_0 + PIXEL20_31 + PIXEL21_31 + PIXEL22_30 + PIXEL23_10 + PIXEL30_81 + PIXEL31_81 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag121 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_61 + PIXEL03_80 + PIXEL10_32 + PIXEL11_32 + PIXEL12_30 + PIXEL13_10 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_30,PIXEL23_10,PIXEL32_10,PIXEL33_80,PIXEL22_0,PIXEL23_11,PIXEL32_12,PIXEL33_20 + jmp .loopx_end +..@flag87 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL10_81 + PIXEL11_31 + PIXEL12_0 + PIXEL20_61 + PIXEL21_30 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_30,PIXEL23_10,PIXEL32_10,PIXEL33_80,PIXEL22_0,PIXEL23_11,PIXEL32_12,PIXEL33_20 + PIXEL30_80 + PIXEL31_10 + jmp .loopx_end +..@flag79 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + PIXEL02_32 + PIXEL03_82 + PIXEL11_0 + PIXEL12_32 + PIXEL13_82 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_10,PIXEL21_30,PIXEL30_80,PIXEL31_10,PIXEL20_12,PIXEL21_0,PIXEL30_20,PIXEL31_11 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag122 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_80,PIXEL01_10,PIXEL10_10,PIXEL11_30,PIXEL00_20,PIXEL01_12,PIXEL10_11,PIXEL11_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_10,PIXEL03_80,PIXEL12_30,PIXEL13_10,PIXEL02_11,PIXEL03_20,PIXEL12_0,PIXEL13_12 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_30,PIXEL23_10,PIXEL32_10,PIXEL33_80,PIXEL22_0,PIXEL23_11,PIXEL32_12,PIXEL33_20 + jmp .loopx_end +..@flag94 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_80,PIXEL01_10,PIXEL10_10,PIXEL11_30,PIXEL00_20,PIXEL01_12,PIXEL10_11,PIXEL11_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL12_0 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_10,PIXEL21_30,PIXEL30_80,PIXEL31_10,PIXEL20_12,PIXEL21_0,PIXEL30_20,PIXEL31_11 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_30,PIXEL23_10,PIXEL32_10,PIXEL33_80,PIXEL22_0,PIXEL23_11,PIXEL32_12,PIXEL33_20 + jmp .loopx_end +..@flag218 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_80,PIXEL01_10,PIXEL10_10,PIXEL11_30,PIXEL00_20,PIXEL01_12,PIXEL10_11,PIXEL11_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_10,PIXEL03_80,PIXEL12_30,PIXEL13_10,PIXEL02_11,PIXEL03_20,PIXEL12_0,PIXEL13_12 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_10,PIXEL21_30,PIXEL30_80,PIXEL31_10,PIXEL20_12,PIXEL21_0,PIXEL30_20,PIXEL31_11 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + jmp .loopx_end +..@flag91 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_10,PIXEL03_80,PIXEL12_30,PIXEL13_10,PIXEL02_11,PIXEL03_20,PIXEL12_0,PIXEL13_12 + PIXEL11_0 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_10,PIXEL21_30,PIXEL30_80,PIXEL31_10,PIXEL20_12,PIXEL21_0,PIXEL30_20,PIXEL31_11 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_30,PIXEL23_10,PIXEL32_10,PIXEL33_80,PIXEL22_0,PIXEL23_11,PIXEL32_12,PIXEL33_20 + jmp .loopx_end +..@flag229 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_60 + PIXEL03_20 + PIXEL10_60 + PIXEL11_70 + PIXEL12_70 + PIXEL13_60 + PIXEL20_82 + PIXEL21_32 + PIXEL22_31 + PIXEL23_81 + PIXEL30_82 + PIXEL31_32 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag167 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + PIXEL02_32 + PIXEL03_82 + PIXEL10_81 + PIXEL11_31 + PIXEL12_32 + PIXEL13_82 + PIXEL20_60 + PIXEL21_70 + PIXEL22_70 + PIXEL23_60 + PIXEL30_20 + PIXEL31_60 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag173 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_60 + PIXEL03_20 + PIXEL10_32 + PIXEL11_32 + PIXEL12_70 + PIXEL13_60 + PIXEL20_31 + PIXEL21_31 + PIXEL22_70 + PIXEL23_60 + PIXEL30_81 + PIXEL31_81 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag181 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_81 + PIXEL03_81 + PIXEL10_60 + PIXEL11_70 + PIXEL12_31 + PIXEL13_31 + PIXEL20_60 + PIXEL21_70 + PIXEL22_32 + PIXEL23_32 + PIXEL30_20 + PIXEL31_60 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag186 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_80,PIXEL01_10,PIXEL10_10,PIXEL11_30,PIXEL00_20,PIXEL01_12,PIXEL10_11,PIXEL11_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_10,PIXEL03_80,PIXEL12_30,PIXEL13_10,PIXEL02_11,PIXEL03_20,PIXEL12_0,PIXEL13_12 + PIXEL20_31 + PIXEL21_31 + PIXEL22_32 + PIXEL23_32 + PIXEL30_81 + PIXEL31_81 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag115 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_10,PIXEL03_80,PIXEL12_30,PIXEL13_10,PIXEL02_11,PIXEL03_20,PIXEL12_0,PIXEL13_12 + PIXEL10_81 + PIXEL11_31 + PIXEL20_82 + PIXEL21_32 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_30,PIXEL23_10,PIXEL32_10,PIXEL33_80,PIXEL22_0,PIXEL23_11,PIXEL32_12,PIXEL33_20 + PIXEL30_82 + PIXEL31_32 + jmp .loopx_end +..@flag93 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_81 + PIXEL03_81 + PIXEL10_32 + PIXEL11_32 + PIXEL12_31 + PIXEL13_31 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_10,PIXEL21_30,PIXEL30_80,PIXEL31_10,PIXEL20_12,PIXEL21_0,PIXEL30_20,PIXEL31_11 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_30,PIXEL23_10,PIXEL32_10,PIXEL33_80,PIXEL22_0,PIXEL23_11,PIXEL32_12,PIXEL33_20 + jmp .loopx_end +..@flag206 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_80,PIXEL01_10,PIXEL10_10,PIXEL11_30,PIXEL00_20,PIXEL01_12,PIXEL10_11,PIXEL11_0 + PIXEL02_32 + PIXEL03_82 + PIXEL12_32 + PIXEL13_82 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_10,PIXEL21_30,PIXEL30_80,PIXEL31_10,PIXEL20_12,PIXEL21_0,PIXEL30_20,PIXEL31_11 + PIXEL22_31 + PIXEL23_81 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag205 +..@flag201 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_60 + PIXEL03_20 + PIXEL10_32 + PIXEL11_32 + PIXEL12_70 + PIXEL13_60 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_10,PIXEL21_30,PIXEL30_80,PIXEL31_10,PIXEL20_12,PIXEL21_0,PIXEL30_20,PIXEL31_11 + PIXEL22_31 + PIXEL23_81 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag174 +..@flag46 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_80,PIXEL01_10,PIXEL10_10,PIXEL11_30,PIXEL00_20,PIXEL01_12,PIXEL10_11,PIXEL11_0 + PIXEL02_32 + PIXEL03_82 + PIXEL12_32 + PIXEL13_82 + PIXEL20_31 + PIXEL21_31 + PIXEL22_70 + PIXEL23_60 + PIXEL30_81 + PIXEL31_81 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag179 +..@flag147 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_10,PIXEL03_80,PIXEL12_30,PIXEL13_10,PIXEL02_11,PIXEL03_20,PIXEL12_0,PIXEL13_12 + PIXEL10_81 + PIXEL11_31 + PIXEL20_60 + PIXEL21_70 + PIXEL22_32 + PIXEL23_32 + PIXEL30_20 + PIXEL31_60 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag117 +..@flag116 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_81 + PIXEL03_81 + PIXEL10_60 + PIXEL11_70 + PIXEL12_31 + PIXEL13_31 + PIXEL20_82 + PIXEL21_32 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_30,PIXEL23_10,PIXEL32_10,PIXEL33_80,PIXEL22_0,PIXEL23_11,PIXEL32_12,PIXEL33_20 + PIXEL30_82 + PIXEL31_32 + jmp .loopx_end +..@flag189 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_81 + PIXEL03_81 + PIXEL10_32 + PIXEL11_32 + PIXEL12_31 + PIXEL13_31 + PIXEL20_31 + PIXEL21_31 + PIXEL22_32 + PIXEL23_32 + PIXEL30_81 + PIXEL31_81 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag231 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + PIXEL02_32 + PIXEL03_82 + PIXEL10_81 + PIXEL11_31 + PIXEL12_32 + PIXEL13_82 + PIXEL20_82 + PIXEL21_32 + PIXEL22_31 + PIXEL23_81 + PIXEL30_82 + PIXEL31_32 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag126 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL10_10 + PIXEL11_30 + PIXEL12_0 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag219 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + PIXEL30_80 + PIXEL31_10 + jmp .loopx_end +..@flag125 + AUXADDRESS + DiffOrNot ebp-w8,ebp-w4,PIXEL00_82,PIXEL10_32,PIXEL20_0,PIXEL21_0,PIXEL30_0,PIXEL31_0,PIXEL00_11,PIXEL10_13,PIXEL20_83,PIXEL21_70,PIXEL30_50,PIXEL31_21 + PIXEL01_82 + PIXEL02_81 + PIXEL03_81 + PIXEL11_32 + PIXEL12_31 + PIXEL13_31 + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag221 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_81 + DiffOrNot ebp-w6,ebp-w8,PIXEL03_81,PIXEL13_31,PIXEL22_0,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL03_12,PIXEL13_14,PIXEL22_70,PIXEL23_83,PIXEL32_21,PIXEL33_50 + PIXEL10_32 + PIXEL11_32 + PIXEL12_31 + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + jmp .loopx_end +..@flag207 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL02_32,PIXEL03_82,PIXEL10_0,PIXEL11_0,PIXEL00_50,PIXEL01_83,PIXEL02_13,PIXEL03_11,PIXEL10_21,PIXEL11_70 + PIXEL12_32 + PIXEL13_82 + PIXEL20_10 + PIXEL21_30 + PIXEL22_31 + PIXEL23_81 + PIXEL30_80 + PIXEL31_10 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag238 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + PIXEL02_32 + PIXEL03_82 + PIXEL10_10 + PIXEL11_30 + PIXEL12_32 + PIXEL13_82 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL21_0,PIXEL30_0,PIXEL31_0,PIXEL32_31,PIXEL33_81,PIXEL20_21,PIXEL21_70,PIXEL30_50,PIXEL31_83,PIXEL32_14,PIXEL33_12 + PIXEL22_31 + PIXEL23_81 + jmp .loopx_end +..@flag190 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL12_0,PIXEL13_0,PIXEL23_32,PIXEL33_82,PIXEL02_21,PIXEL03_50,PIXEL12_70,PIXEL13_83,PIXEL23_13,PIXEL33_11 + PIXEL10_10 + PIXEL11_30 + PIXEL20_31 + PIXEL21_31 + PIXEL22_32 + PIXEL30_81 + PIXEL31_81 + PIXEL32_82 + jmp .loopx_end +..@flag187 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL11_0,PIXEL20_31,PIXEL30_81,PIXEL00_50,PIXEL01_21,PIXEL10_83,PIXEL11_70,PIXEL20_14,PIXEL30_12 + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + PIXEL21_31 + PIXEL22_32 + PIXEL23_32 + PIXEL31_81 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag243 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + PIXEL02_10 + PIXEL03_80 + PIXEL10_81 + PIXEL11_31 + PIXEL12_30 + PIXEL13_10 + PIXEL20_82 + PIXEL21_32 + DiffOrNot ebp-w6,ebp-w8,PIXEL22_0,PIXEL23_0,PIXEL30_82,PIXEL31_32,PIXEL32_0,PIXEL33_0,PIXEL22_70,PIXEL23_21,PIXEL30_11,PIXEL31_13,PIXEL32_83,PIXEL33_50 + jmp .loopx_end +..@flag119 + AUXADDRESS + DiffOrNot ebp-w2,ebp-w6,PIXEL00_81,PIXEL01_31,PIXEL02_0,PIXEL03_0,PIXEL12_0,PIXEL13_0,PIXEL00_12,PIXEL01_14,PIXEL02_83,PIXEL03_50,PIXEL12_70,PIXEL13_21 + PIXEL10_81 + PIXEL11_31 + PIXEL20_82 + PIXEL21_32 + PIXEL22_30 + PIXEL23_10 + PIXEL30_82 + PIXEL31_32 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag237 +..@flag233 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_60 + PIXEL03_20 + PIXEL10_32 + PIXEL11_32 + PIXEL12_70 + PIXEL13_60 + PIXEL20_0 + PIXEL21_0 + PIXEL22_31 + PIXEL23_81 + DiffOrNot ebp-w8,ebp-w4,PIXEL30_0,PIXEL30_20 + PIXEL31_0 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag175 +..@flag47 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL00_20 + PIXEL01_0 + PIXEL02_32 + PIXEL03_82 + PIXEL10_0 + PIXEL11_0 + PIXEL12_32 + PIXEL13_82 + PIXEL20_31 + PIXEL21_31 + PIXEL22_70 + PIXEL23_60 + PIXEL30_81 + PIXEL31_81 + PIXEL32_60 + PIXEL33_20 + jmp .loopx_end +..@flag183 +..@flag151 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + PIXEL02_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL03_0,PIXEL03_20 + PIXEL10_81 + PIXEL11_31 + PIXEL12_0 + PIXEL13_0 + PIXEL20_60 + PIXEL21_70 + PIXEL22_32 + PIXEL23_32 + PIXEL30_20 + PIXEL31_60 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag245 +..@flag244 + AUXADDRESS + PIXEL00_20 + PIXEL01_60 + PIXEL02_81 + PIXEL03_81 + PIXEL10_60 + PIXEL11_70 + PIXEL12_31 + PIXEL13_31 + PIXEL20_82 + PIXEL21_32 + PIXEL22_0 + PIXEL23_0 + PIXEL30_82 + PIXEL31_32 + PIXEL32_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL33_0,PIXEL33_20 + jmp .loopx_end +..@flag250 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + PIXEL02_10 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + jmp .loopx_end +..@flag123 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_10 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag95 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL11_0 + PIXEL12_0 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_10 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag222 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL10_10 + PIXEL11_30 + PIXEL12_0 + PIXEL20_10 + PIXEL21_30 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + PIXEL30_80 + PIXEL31_10 + jmp .loopx_end +..@flag252 + AUXADDRESS + PIXEL00_80 + PIXEL01_61 + PIXEL02_81 + PIXEL03_81 + PIXEL10_10 + PIXEL11_30 + PIXEL12_31 + PIXEL13_31 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + PIXEL22_0 + PIXEL23_0 + PIXEL32_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL33_0,PIXEL33_20 + jmp .loopx_end +..@flag249 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_61 + PIXEL03_80 + PIXEL10_32 + PIXEL11_32 + PIXEL12_30 + PIXEL13_10 + PIXEL20_0 + PIXEL21_0 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + DiffOrNot ebp-w8,ebp-w4,PIXEL30_0,PIXEL30_20 + PIXEL31_0 + jmp .loopx_end +..@flag235 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_61 + PIXEL20_0 + PIXEL21_0 + PIXEL22_31 + PIXEL23_81 + DiffOrNot ebp-w8,ebp-w4,PIXEL30_0,PIXEL30_20 + PIXEL31_0 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag111 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL00_20 + PIXEL01_0 + PIXEL02_32 + PIXEL03_82 + PIXEL10_0 + PIXEL11_0 + PIXEL12_32 + PIXEL13_82 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag63 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL00_20 + PIXEL01_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL10_0 + PIXEL11_0 + PIXEL12_0 + PIXEL20_31 + PIXEL21_31 + PIXEL22_30 + PIXEL23_10 + PIXEL30_81 + PIXEL31_81 + PIXEL32_61 + PIXEL33_80 + jmp .loopx_end +..@flag159 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + PIXEL02_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL03_0,PIXEL03_20 + PIXEL11_0 + PIXEL12_0 + PIXEL13_0 + PIXEL20_10 + PIXEL21_30 + PIXEL22_32 + PIXEL23_32 + PIXEL30_80 + PIXEL31_61 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag215 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + PIXEL02_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL03_0,PIXEL03_20 + PIXEL10_81 + PIXEL11_31 + PIXEL12_0 + PIXEL13_0 + PIXEL20_61 + PIXEL21_30 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + PIXEL30_80 + PIXEL31_10 + jmp .loopx_end +..@flag246 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL10_61 + PIXEL11_30 + PIXEL12_0 + PIXEL20_82 + PIXEL21_32 + PIXEL22_0 + PIXEL23_0 + PIXEL30_82 + PIXEL31_32 + PIXEL32_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL33_0,PIXEL33_20 + jmp .loopx_end +..@flag254 + AUXADDRESS + PIXEL00_80 + PIXEL01_10 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL10_10 + PIXEL11_30 + PIXEL12_0 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + PIXEL22_0 + PIXEL23_0 + PIXEL32_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL33_0,PIXEL33_20 + jmp .loopx_end +..@flag253 + AUXADDRESS + PIXEL00_82 + PIXEL01_82 + PIXEL02_81 + PIXEL03_81 + PIXEL10_32 + PIXEL11_32 + PIXEL12_31 + PIXEL13_31 + PIXEL20_0 + PIXEL21_0 + PIXEL22_0 + PIXEL23_0 + DiffOrNot ebp-w8,ebp-w4,PIXEL30_0,PIXEL30_20 + PIXEL31_0 + PIXEL32_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL33_0,PIXEL33_20 + jmp .loopx_end +..@flag251 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_10 + PIXEL20_0 + PIXEL21_0 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + DiffOrNot ebp-w8,ebp-w4,PIXEL30_0,PIXEL30_20 + PIXEL31_0 + jmp .loopx_end +..@flag239 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL00_20 + PIXEL01_0 + PIXEL02_32 + PIXEL03_82 + PIXEL10_0 + PIXEL11_0 + PIXEL12_32 + PIXEL13_82 + PIXEL20_0 + PIXEL21_0 + PIXEL22_31 + PIXEL23_81 + DiffOrNot ebp-w8,ebp-w4,PIXEL30_0,PIXEL30_20 + PIXEL31_0 + PIXEL32_31 + PIXEL33_81 + jmp .loopx_end +..@flag127 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL00_20 + PIXEL01_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL02_0,PIXEL03_0,PIXEL13_0,PIXEL02_50,PIXEL03_50,PIXEL13_50 + PIXEL10_0 + PIXEL11_0 + PIXEL12_0 + DiffOrNot ebp-w8,ebp-w4,PIXEL20_0,PIXEL30_0,PIXEL31_0,PIXEL20_50,PIXEL30_50,PIXEL31_50 + PIXEL21_0 + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + jmp .loopx_end +..@flag191 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL00_20 + PIXEL01_0 + PIXEL02_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL03_0,PIXEL03_20 + PIXEL10_0 + PIXEL11_0 + PIXEL12_0 + PIXEL13_0 + PIXEL20_31 + PIXEL21_31 + PIXEL22_32 + PIXEL23_32 + PIXEL30_81 + PIXEL31_81 + PIXEL32_82 + PIXEL33_82 + jmp .loopx_end +..@flag223 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL01_0,PIXEL10_0,PIXEL00_50,PIXEL01_50,PIXEL10_50 + PIXEL02_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL03_0,PIXEL03_20 + PIXEL11_0 + PIXEL12_0 + PIXEL13_0 + PIXEL20_10 + PIXEL21_30 + PIXEL22_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL23_0,PIXEL32_0,PIXEL33_0,PIXEL23_50,PIXEL32_50,PIXEL33_50 + PIXEL30_80 + PIXEL31_10 + jmp .loopx_end +..@flag247 + AUXADDRESS + PIXEL00_81 + PIXEL01_31 + PIXEL02_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL03_0,PIXEL03_20 + PIXEL10_81 + PIXEL11_31 + PIXEL12_0 + PIXEL13_0 + PIXEL20_82 + PIXEL21_32 + PIXEL22_0 + PIXEL23_0 + PIXEL30_82 + PIXEL31_32 + PIXEL32_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL33_0,PIXEL33_20 + jmp .loopx_end +..@flag255 + AUXADDRESS + DiffOrNot ebp-w4,ebp-w2,PIXEL00_0,PIXEL00_20 + PIXEL01_0 + PIXEL02_0 + DiffOrNot ebp-w2,ebp-w6,PIXEL03_0,PIXEL03_20 + PIXEL10_0 + PIXEL11_0 + PIXEL12_0 + PIXEL13_0 + PIXEL20_0 + PIXEL21_0 + PIXEL22_0 + PIXEL23_0 + DiffOrNot ebp-w8,ebp-w4,PIXEL30_0,PIXEL30_20 + PIXEL31_0 + PIXEL32_0 + DiffOrNot ebp-w6,ebp-w8,PIXEL33_0,PIXEL33_20 + jmp .loopx_end + +.loopx_end + add esi,2 + add edi,16 + dec dword[ebp-xcounter] + jle .xres_2 + jmp .loopx +.xres_2 + ; x=Xres-2 - special case + jl .xres_1 + mov ebx,[ebp-prevline] + movq mm5,[esi+ebx-4] + movq mm6,[esi-4] + mov ebx,[ebp-nextline] + movq mm7,[esi+ebx-4] + psrlq mm5,16 + psrlq mm6,16 + psrlq mm7,16 + movd eax,mm5 + movzx edx,ax + mov [ebp-w1],edx + shr eax,16 + mov [ebp-w2],eax + psrlq mm5,32 + movd eax,mm5 + mov [ebp-w3],eax + movd eax,mm6 + movzx edx,ax + mov [ebp-w4],edx + shr eax,16 + mov [ebp-w5],eax + psrlq mm6,32 + movd eax,mm6 + mov [ebp-w6],eax + movd eax,mm7 + movzx edx,ax + mov [ebp-w7],edx + shr eax,16 + mov [ebp-w8],eax + psrlq mm7,32 + movd eax,mm7 + mov [ebp-w9],eax + jmp .flags +.xres_1 + cmp dword[ebp-xcounter],-1 + jl .nexty + ; x=Xres-1 - special case + mov ebx,[ebp-prevline] + movq mm5,[esi+ebx-6] + movq mm6,[esi-6] + mov ebx,[ebp-nextline] + movq mm7,[esi+ebx-6] + psrlq mm5,32 + psrlq mm6,32 + psrlq mm7,32 + movd eax,mm5 + movzx edx,ax + mov [ebp-w1],edx + shr eax,16 + mov [ebp-w2],eax + mov [ebp-w3],eax + movd eax,mm6 + movzx edx,ax + mov [ebp-w4],edx + shr eax,16 + mov [ebp-w5],eax + mov [ebp-w6],eax + movd eax,mm7 + movzx edx,ax + mov [ebp-w7],edx + shr eax,16 + mov [ebp-w8],eax + mov [ebp-w9],eax + jmp .flags +.nexty + add esi,[ebp+offset] ; added - move source pointer past end-of-line blanks + add edi,ebx + add edi,ebx + add edi,ebx + add edi,ebx + mov ebx, [ebp+Xres] ; added, bug - need to add to destination offset + shl ebx, 2 + ; can optimize this by increasing shl above + sub edi, ebx + sub edi, ebx + sub edi, ebx + sub edi, ebx + shr ebx, 1 + dec dword[ebp-linesleft] + jz .fin + add ebx, [ebp+offset]; + cmp dword[ebp-linesleft],1 + je .lastline + mov dword[ebp-nextline],ebx + neg ebx + mov dword[ebp-prevline],ebx + jmp .loopy +.lastline + mov dword[ebp-nextline],0 + neg ebx + mov dword[ebp-prevline],ebx + jmp .loopy +.fin + emms + popad + mov esp,ebp + pop ebp + ret + +SECTION .data +FuncTable + dd ..@flag0, ..@flag1, ..@flag2, ..@flag3, ..@flag4, ..@flag5, ..@flag6, ..@flag7 + dd ..@flag8, ..@flag9, ..@flag10, ..@flag11, ..@flag12, ..@flag13, ..@flag14, ..@flag15 + dd ..@flag16, ..@flag17, ..@flag18, ..@flag19, ..@flag20, ..@flag21, ..@flag22, ..@flag23 + dd ..@flag24, ..@flag25, ..@flag26, ..@flag27, ..@flag28, ..@flag29, ..@flag30, ..@flag31 + dd ..@flag32, ..@flag33, ..@flag34, ..@flag35, ..@flag36, ..@flag37, ..@flag38, ..@flag39 + dd ..@flag40, ..@flag41, ..@flag42, ..@flag43, ..@flag44, ..@flag45, ..@flag46, ..@flag47 + dd ..@flag48, ..@flag49, ..@flag50, ..@flag51, ..@flag52, ..@flag53, ..@flag54, ..@flag55 + dd ..@flag56, ..@flag57, ..@flag58, ..@flag59, ..@flag60, ..@flag61, ..@flag62, ..@flag63 + dd ..@flag64, ..@flag65, ..@flag66, ..@flag67, ..@flag68, ..@flag69, ..@flag70, ..@flag71 + dd ..@flag72, ..@flag73, ..@flag74, ..@flag75, ..@flag76, ..@flag77, ..@flag78, ..@flag79 + dd ..@flag80, ..@flag81, ..@flag82, ..@flag83, ..@flag84, ..@flag85, ..@flag86, ..@flag87 + dd ..@flag88, ..@flag89, ..@flag90, ..@flag91, ..@flag92, ..@flag93, ..@flag94, ..@flag95 + dd ..@flag96, ..@flag97, ..@flag98, ..@flag99, ..@flag100, ..@flag101, ..@flag102, ..@flag103 + dd ..@flag104, ..@flag105, ..@flag106, ..@flag107, ..@flag108, ..@flag109, ..@flag110, ..@flag111 + dd ..@flag112, ..@flag113, ..@flag114, ..@flag115, ..@flag116, ..@flag117, ..@flag118, ..@flag119 + dd ..@flag120, ..@flag121, ..@flag122, ..@flag123, ..@flag124, ..@flag125, ..@flag126, ..@flag127 + dd ..@flag128, ..@flag129, ..@flag130, ..@flag131, ..@flag132, ..@flag133, ..@flag134, ..@flag135 + dd ..@flag136, ..@flag137, ..@flag138, ..@flag139, ..@flag140, ..@flag141, ..@flag142, ..@flag143 + dd ..@flag144, ..@flag145, ..@flag146, ..@flag147, ..@flag148, ..@flag149, ..@flag150, ..@flag151 + dd ..@flag152, ..@flag153, ..@flag154, ..@flag155, ..@flag156, ..@flag157, ..@flag158, ..@flag159 + dd ..@flag160, ..@flag161, ..@flag162, ..@flag163, ..@flag164, ..@flag165, ..@flag166, ..@flag167 + dd ..@flag168, ..@flag169, ..@flag170, ..@flag171, ..@flag172, ..@flag173, ..@flag174, ..@flag175 + dd ..@flag176, ..@flag177, ..@flag178, ..@flag179, ..@flag180, ..@flag181, ..@flag182, ..@flag183 + dd ..@flag184, ..@flag185, ..@flag186, ..@flag187, ..@flag188, ..@flag189, ..@flag190, ..@flag191 + dd ..@flag192, ..@flag193, ..@flag194, ..@flag195, ..@flag196, ..@flag197, ..@flag198, ..@flag199 + dd ..@flag200, ..@flag201, ..@flag202, ..@flag203, ..@flag204, ..@flag205, ..@flag206, ..@flag207 + dd ..@flag208, ..@flag209, ..@flag210, ..@flag211, ..@flag212, ..@flag213, ..@flag214, ..@flag215 + dd ..@flag216, ..@flag217, ..@flag218, ..@flag219, ..@flag220, ..@flag221, ..@flag222, ..@flag223 + dd ..@flag224, ..@flag225, ..@flag226, ..@flag227, ..@flag228, ..@flag229, ..@flag230, ..@flag231 + dd ..@flag232, ..@flag233, ..@flag234, ..@flag235, ..@flag236, ..@flag237, ..@flag238, ..@flag239 + dd ..@flag240, ..@flag241, ..@flag242, ..@flag243, ..@flag244, ..@flag245, ..@flag246, ..@flag247 + dd ..@flag248, ..@flag249, ..@flag250, ..@flag251, ..@flag252, ..@flag253, ..@flag254, ..@flag255 + + diff --git a/src/filters/hq/asm/macros.mac b/src/filters/hq/asm/macros.mac new file mode 100644 index 0000000..7c6e45b --- /dev/null +++ b/src/filters/hq/asm/macros.mac @@ -0,0 +1,73 @@ +;Copyright (C) 1997-2007 ZSNES Team ( zsKnight, _Demo_, pagefault, Nach ) +; +;http://www.zsnes.com +;http://sourceforge.net/projects/zsnes +;https://zsnes.bountysource.com +; +;This program is free software; you can redistribute it and/or +;modify it under the terms of the GNU General Public License +;version 2 as published by the Free Software Foundation. +; +;This program is distributed in the hope that it will be useful, +;but WITHOUT ANY WARRANTY; without even the implied warranty of +;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;GNU General Public License for more details. +; +;You should have received a copy of the GNU General Public License +;along with this program; if not, write to the Free Software +;Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + +%ifdef __AMD64__ +bits 64 +%else +bits 32 +%endif + +%ifdef MACHO +section .text align=16 +section .data align=4 +section .bss align=4 +%endif + +%ifdef ELF + +%imacro newsym 1 + GLOBAL %1 + %1: +%endmacro +%imacro newsym 2+ + GLOBAL %1 + %1: %2 +%endmacro +%define EXTSYM EXTERN + +section .note.GNU-stack noalloc noexec nowrite progbits + +%else + +%imacro newsym 1 + GLOBAL _%1 + _%1: + %1: +%endmacro +%imacro newsym 2+ + GLOBAL _%1 + _%1: + %1: %2 +%endmacro +%imacro EXTSYM 1-* +%rep %0 + EXTERN _%1 + %define %1 _%1 +%rotate 1 +%endrep +%endmacro +%endif + +%macro ALIGN32 0 + times ($$-$) & 1Fh nop ; Long word alignment +%endmacro +%macro ALIGN16 0 + times ($$-$) & 1Fh nop ; Long word alignment +%endmacro diff --git a/src/filters/hq/c/hq3x_pattern.h b/src/filters/hq/c/hq3x_pattern.h new file mode 100644 index 0000000..d118039 --- /dev/null +++ b/src/filters/hq/c/hq3x_pattern.h @@ -0,0 +1,3639 @@ +/* + VisualBoyAdvance - a Game Boy & Game Boy Advance emulator + + Copyright (C) 1999 - 2003 Forgotten + Copyright (C) 2003 - 2004 Forgotten and the VBA development team + Copyright (C) 2005 - 2006 VBA development team + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +switch(pattern) +{ +case 0: +case 1: +case 4: +case 32: +case 128: +case 5: +case 132: +case 160: +case 33: +case 129: +case 36: +case 133: +case 164: +case 161: +case 37: +case 165: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + break; + } +case 2: +case 34: +case 130: +case 162: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + break; + } +case 16: +case 17: +case 48: +case 49: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + break; + } +case 64: +case 65: +case 68: +case 69: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + break; + } +case 8: +case 12: +case 136: +case 140: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + break; + } +case 3: +case 35: +case 131: +case 163: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + break; + } +case 6: +case 38: +case 134: +case 166: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + break; + } +case 20: +case 21: +case 52: +case 53: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1U + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + break; + } +case 144: +case 145: +case 176: +case 177: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1D + break; + } +case 192: +case 193: +case 196: +case 197: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + break; + } +case 96: +case 97: +case 100: +case 101: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + break; + } +case 40: +case 44: +case 168: +case 172: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1D + PIXEL21_1 + PIXEL22_2 + break; + } +case 9: +case 13: +case 137: +case 141: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + break; + } +case 18: +case 50: + { + PIXEL00_1M + + if (Diff(yuv[2], yuv[6])) + { + PIXEL01_C + PIXEL02_1M + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + break; + } +case 80: +case 81: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL20_1M + if (Diff(yuv[6], yuv[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_1M + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } +case 72: +case 76: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + if (Diff(yuv[8], yuv[4])) + { + PIXEL10_C + PIXEL20_1M + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } +case 10: +case 138: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_1M + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + break; + } +case 66: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + break; + } +case 24: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + break; + } +case 7: +case 39: +case 135: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + break; + } +case 148: +case 149: +case 180: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1U + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1D + break; + } +case 224: +case 228: +case 225: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + break; + } +case 41: +case 169: +case 45: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1D + PIXEL21_1 + PIXEL22_2 + break; + } +case 22: +case 54: + { + PIXEL00_1M + if (Diff(yuv[2], yuv[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + break; + } +case 208: +case 209: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL20_1M + if (Diff(yuv[6], yuv[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } +case 104: +case 108: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + if (Diff(yuv[8], yuv[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } +case 11: +case 139: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + break; + } +case 19: +case 51: + { + if (Diff(yuv[2], yuv[6])) + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL12_C + } + else + { + PIXEL00_2 + PIXEL01_6 + PIXEL02_5 + PIXEL12_1 + } + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + break; + } +case 146: +case 178: + { + if (Diff(yuv[2], yuv[6])) + { + PIXEL01_C + PIXEL02_1M + PIXEL12_C + PIXEL22_1D + } + else + { + PIXEL01_1 + PIXEL02_5 + PIXEL12_6 + PIXEL22_2 + } + PIXEL00_1M + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + break; + } +case 84: +case 85: + { + if (Diff(yuv[6], yuv[8])) + { + PIXEL02_1U + PIXEL12_C + PIXEL21_C + PIXEL22_1M + } + else + { + PIXEL02_2 + PIXEL12_6 + PIXEL21_1 + PIXEL22_5 + } + PIXEL00_2 + PIXEL01_1 + PIXEL10_1 + PIXEL11 + PIXEL20_1M + break; + } +case 112: +case 113: + { + if (Diff(yuv[6], yuv[8])) + { + PIXEL12_C + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + } + else + { + PIXEL12_1 + PIXEL20_2 + PIXEL21_6 + PIXEL22_5 + } + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + break; + } +case 200: +case 204: + { + if (Diff(yuv[8], yuv[4])) + { + PIXEL10_C + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + } + else + { + PIXEL10_1 + PIXEL20_5 + PIXEL21_6 + PIXEL22_2 + } + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + break; + } +case 73: +case 77: + { + if (Diff(yuv[8], yuv[4])) + { + PIXEL00_1U + PIXEL10_C + PIXEL20_1M + PIXEL21_C + } + else + { + PIXEL00_2 + PIXEL10_6 + PIXEL20_5 + PIXEL21_1 + } + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + PIXEL22_1M + break; + } +case 42: +case 170: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_1M + PIXEL01_C + PIXEL10_C + PIXEL20_1D + } + else + { + PIXEL00_5 + PIXEL01_1 + PIXEL10_6 + PIXEL20_2 + } + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL21_1 + PIXEL22_2 + break; + } +case 14: +case 142: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_C + } + else + { + PIXEL00_5 + PIXEL01_6 + PIXEL02_2 + PIXEL10_1 + } + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + break; + } +case 67: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + break; + } +case 70: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + break; + } +case 28: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + break; + } +case 152: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + break; + } +case 194: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + break; + } +case 98: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + break; + } +case 56: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + break; + } +case 25: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + break; + } +case 26: +case 31: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL10_3 + } + PIXEL01_C + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL02_4 + PIXEL12_3 + } + PIXEL11 + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + break; + } +case 82: +case 214: + { + PIXEL00_1M + if (Diff(yuv[2], yuv[6])) + { + PIXEL01_C + PIXEL02_C + } + else + { + PIXEL01_3 + PIXEL02_4 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1M + if (Diff(yuv[6], yuv[8])) + { + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL21_3 + PIXEL22_4 + } + break; + } +case 88: +case 248: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL11 + if (Diff(yuv[8], yuv[4])) + { + PIXEL10_C + PIXEL20_C + } + else + { + PIXEL10_3 + PIXEL20_4 + } + PIXEL21_C + if (Diff(yuv[6], yuv[8])) + { + PIXEL12_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL22_4 + } + break; + } +case 74: +case 107: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_C + PIXEL01_C + } + else + { + PIXEL00_4 + PIXEL01_3 + } + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } +case 27: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + PIXEL02_1M + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + break; + } +case 86: + { + PIXEL00_1M + if (Diff(yuv[2], yuv[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_1 + PIXEL11 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + break; + } +case 216: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL20_1M + if (Diff(yuv[6], yuv[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } +case 106: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL11 + PIXEL12_1 + if (Diff(yuv[8], yuv[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } +case 30: + { + PIXEL00_1M + if (Diff(yuv[2], yuv[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_C + PIXEL11 + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + break; + } +case 210: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL20_1M + if (Diff(yuv[6], yuv[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } +case 120: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL11 + PIXEL12_C + if (Diff(yuv[8], yuv[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } +case 75: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + break; + } +case 29: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + break; + } +case 198: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + break; + } +case 184: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + break; + } +case 99: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + break; + } +case 57: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + break; + } +case 71: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + break; + } +case 156: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + break; + } +case 226: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + break; + } +case 60: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + break; + } +case 195: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + break; + } +case 102: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + break; + } +case 153: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + break; + } +case 58: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + break; + } +case 83: + { + PIXEL00_1L + PIXEL01_C + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_C + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } +case 92: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } +case 202: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + PIXEL22_1R + break; + } +case 78: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + PIXEL22_1M + break; + } +case 154: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + break; + } +case 114: + { + PIXEL00_1M + PIXEL01_C + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } +case 89: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } +case 90: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_C + PIXEL11 + PIXEL12_C + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } +case 55: +case 23: + { + if (Diff(yuv[2], yuv[6])) + { + PIXEL00_1L + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL00_2 + PIXEL01_6 + PIXEL02_5 + PIXEL12_1 + } + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + break; + } +case 182: +case 150: + { + if (Diff(yuv[2], yuv[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + PIXEL22_1D + } + else + { + PIXEL01_1 + PIXEL02_5 + PIXEL12_6 + PIXEL22_2 + } + PIXEL00_1M + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + break; + } +case 213: +case 212: + { + if (Diff(yuv[6], yuv[8])) + { + PIXEL02_1U + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL02_2 + PIXEL12_6 + PIXEL21_1 + PIXEL22_5 + } + PIXEL00_2 + PIXEL01_1 + PIXEL10_1 + PIXEL11 + PIXEL20_1M + break; + } +case 241: +case 240: + { + if (Diff(yuv[6], yuv[8])) + { + PIXEL12_C + PIXEL20_1L + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_1 + PIXEL20_2 + PIXEL21_6 + PIXEL22_5 + } + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + break; + } +case 236: +case 232: + { + if (Diff(yuv[8], yuv[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + PIXEL22_1R + } + else + { + PIXEL10_1 + PIXEL20_5 + PIXEL21_6 + PIXEL22_2 + } + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + break; + } +case 109: +case 105: + { + if (Diff(yuv[8], yuv[4])) + { + PIXEL00_1U + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL00_2 + PIXEL10_6 + PIXEL20_5 + PIXEL21_1 + } + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + PIXEL22_1M + break; + } +case 171: +case 43: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + PIXEL20_1D + } + else + { + PIXEL00_5 + PIXEL01_1 + PIXEL10_6 + PIXEL20_2 + } + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL21_1 + PIXEL22_2 + break; + } +case 143: +case 15: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL02_1R + PIXEL10_C + } + else + { + PIXEL00_5 + PIXEL01_6 + PIXEL02_2 + PIXEL10_1 + } + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + break; + } +case 124: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL11 + PIXEL12_C + if (Diff(yuv[8], yuv[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } +case 203: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + break; + } +case 62: + { + PIXEL00_1M + if (Diff(yuv[2], yuv[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_C + PIXEL11 + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + break; + } +case 211: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL20_1M + if (Diff(yuv[6], yuv[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } +case 118: + { + PIXEL00_1M + if (Diff(yuv[2], yuv[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_1 + PIXEL11 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + break; + } +case 217: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL20_1M + if (Diff(yuv[6], yuv[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } +case 110: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL11 + PIXEL12_1 + if (Diff(yuv[8], yuv[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } +case 155: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + PIXEL02_1M + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + break; + } +case 188: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + break; + } +case 185: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + break; + } +case 61: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + break; + } +case 157: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + break; + } +case 103: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + break; + } +case 227: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + break; + } +case 230: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + break; + } +case 199: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + break; + } +case 220: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + if (Diff(yuv[6], yuv[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } +case 158: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + if (Diff(yuv[2], yuv[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_C + PIXEL11 + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + break; + } +case 234: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + PIXEL02_1M + PIXEL11 + PIXEL12_1 + if (Diff(yuv[8], yuv[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1R + break; + } +case 242: + { + PIXEL00_1M + PIXEL01_C + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_1 + PIXEL11 + PIXEL20_1L + if (Diff(yuv[6], yuv[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } +case 59: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + break; + } +case 121: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL11 + PIXEL12_C + if (Diff(yuv[8], yuv[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } +case 87: + { + PIXEL00_1L + if (Diff(yuv[2], yuv[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_1 + PIXEL11 + PIXEL20_1M + PIXEL21_C + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } +case 79: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + PIXEL02_1R + PIXEL11 + PIXEL12_1 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + PIXEL22_1M + break; + } +case 122: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL11 + PIXEL12_C + if (Diff(yuv[8], yuv[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } +case 94: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + if (Diff(yuv[2], yuv[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_C + PIXEL11 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } +case 218: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_C + PIXEL11 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + if (Diff(yuv[6], yuv[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } +case 91: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL11 + PIXEL12_C + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } +case 229: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + break; + } +case 167: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + break; + } +case 173: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1D + PIXEL21_1 + PIXEL22_2 + break; + } +case 181: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1U + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1D + break; + } +case 186: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + break; + } +case 115: + { + PIXEL00_1L + PIXEL01_C + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } +case 93: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } +case 206: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + PIXEL22_1R + break; + } +case 205: +case 201: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + PIXEL22_1R + break; + } +case 174: +case 46: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1D + PIXEL21_1 + PIXEL22_2 + break; + } +case 179: +case 147: + { + PIXEL00_1L + PIXEL01_C + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1D + break; + } +case 117: +case 116: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1U + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } +case 189: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + break; + } +case 231: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + break; + } +case 126: + { + PIXEL00_1M + if (Diff(yuv[2], yuv[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL11 + if (Diff(yuv[8], yuv[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } +case 219: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + PIXEL02_1M + PIXEL11 + PIXEL20_1M + if (Diff(yuv[6], yuv[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } +case 125: + { + if (Diff(yuv[8], yuv[4])) + { + PIXEL00_1U + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL00_2 + PIXEL10_6 + PIXEL20_5 + PIXEL21_1 + } + PIXEL01_1 + PIXEL02_1U + PIXEL11 + PIXEL12_C + PIXEL22_1M + break; + } +case 221: + { + if (Diff(yuv[6], yuv[8])) + { + PIXEL02_1U + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL02_2 + PIXEL12_6 + PIXEL21_1 + PIXEL22_5 + } + PIXEL00_1U + PIXEL01_1 + PIXEL10_C + PIXEL11 + PIXEL20_1M + break; + } +case 207: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL02_1R + PIXEL10_C + } + else + { + PIXEL00_5 + PIXEL01_6 + PIXEL02_2 + PIXEL10_1 + } + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + break; + } +case 238: + { + if (Diff(yuv[8], yuv[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + PIXEL22_1R + } + else + { + PIXEL10_1 + PIXEL20_5 + PIXEL21_6 + PIXEL22_2 + } + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL11 + PIXEL12_1 + break; + } +case 190: + { + if (Diff(yuv[2], yuv[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + PIXEL22_1D + } + else + { + PIXEL01_1 + PIXEL02_5 + PIXEL12_6 + PIXEL22_2 + } + PIXEL00_1M + PIXEL10_C + PIXEL11 + PIXEL20_1D + PIXEL21_1 + break; + } +case 187: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + PIXEL20_1D + } + else + { + PIXEL00_5 + PIXEL01_1 + PIXEL10_6 + PIXEL20_2 + } + PIXEL02_1M + PIXEL11 + PIXEL12_C + PIXEL21_1 + PIXEL22_1D + break; + } +case 243: + { + if (Diff(yuv[6], yuv[8])) + { + PIXEL12_C + PIXEL20_1L + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_1 + PIXEL20_2 + PIXEL21_6 + PIXEL22_5 + } + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + break; + } +case 119: + { + if (Diff(yuv[2], yuv[6])) + { + PIXEL00_1L + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL00_2 + PIXEL01_6 + PIXEL02_5 + PIXEL12_1 + } + PIXEL10_1 + PIXEL11 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + break; + } +case 237: +case 233: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_C + } + else + { + PIXEL20_2 + } + PIXEL21_C + PIXEL22_1R + break; + } +case 175: +case 47: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_C + } + else + { + PIXEL00_2 + } + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1D + PIXEL21_1 + PIXEL22_2 + break; + } +case 183: +case 151: + { + PIXEL00_1L + PIXEL01_C + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_C + } + else + { + PIXEL02_2 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1D + break; + } +case 245: +case 244: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1U + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_C + } + else + { + PIXEL22_2 + } + break; + } +case 250: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL11 + if (Diff(yuv[8], yuv[4])) + { + PIXEL10_C + PIXEL20_C + } + else + { + PIXEL10_3 + PIXEL20_4 + } + PIXEL21_C + if (Diff(yuv[6], yuv[8])) + { + PIXEL12_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL22_4 + } + break; + } +case 123: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_C + PIXEL01_C + } + else + { + PIXEL00_4 + PIXEL01_3 + } + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } +case 95: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL10_3 + } + PIXEL01_C + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL02_4 + PIXEL12_3 + } + PIXEL11 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + break; + } +case 222: + { + PIXEL00_1M + if (Diff(yuv[2], yuv[6])) + { + PIXEL01_C + PIXEL02_C + } + else + { + PIXEL01_3 + PIXEL02_4 + } + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + if (Diff(yuv[6], yuv[8])) + { + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL21_3 + PIXEL22_4 + } + break; + } +case 252: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL11 + PIXEL12_C + if (Diff(yuv[8], yuv[4])) + { + PIXEL10_C + PIXEL20_C + } + else + { + PIXEL10_3 + PIXEL20_4 + } + PIXEL21_C + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_C + } + else + { + PIXEL22_2 + } + break; + } +case 249: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_C + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (Diff(yuv[6], yuv[8])) + { + PIXEL12_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL22_4 + } + break; + } +case 235: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_C + PIXEL01_C + } + else + { + PIXEL00_4 + PIXEL01_3 + } + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_C + } + else + { + PIXEL20_2 + } + PIXEL21_C + PIXEL22_1R + break; + } +case 111: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_C + } + else + { + PIXEL00_2 + } + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } +case 63: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_C + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_C + PIXEL11 + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + break; + } +case 159: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL10_3 + } + PIXEL01_C + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_C + } + else + { + PIXEL02_2 + } + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + break; + } +case 215: + { + PIXEL00_1L + PIXEL01_C + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_C + } + else + { + PIXEL02_2 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1M + if (Diff(yuv[6], yuv[8])) + { + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL21_3 + PIXEL22_4 + } + break; + } +case 246: + { + PIXEL00_1M + if (Diff(yuv[2], yuv[6])) + { + PIXEL01_C + PIXEL02_C + } + else + { + PIXEL01_3 + PIXEL02_4 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_C + } + else + { + PIXEL22_2 + } + break; + } +case 254: + { + PIXEL00_1M + if (Diff(yuv[2], yuv[6])) + { + PIXEL01_C + PIXEL02_C + } + else + { + PIXEL01_3 + PIXEL02_4 + } + PIXEL11 + if (Diff(yuv[8], yuv[4])) + { + PIXEL10_C + PIXEL20_C + } + else + { + PIXEL10_3 + PIXEL20_4 + } + if (Diff(yuv[6], yuv[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_2 + } + break; + } +case 253: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_C + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_C + } + else + { + PIXEL22_2 + } + break; + } +case 251: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_C + PIXEL01_C + } + else + { + PIXEL00_4 + PIXEL01_3 + } + PIXEL02_1M + PIXEL11 + if (Diff(yuv[8], yuv[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_2 + PIXEL21_3 + } + if (Diff(yuv[6], yuv[8])) + { + PIXEL12_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL22_4 + } + break; + } +case 239: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_C + } + else + { + PIXEL00_2 + } + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_C + } + else + { + PIXEL20_2 + } + PIXEL21_C + PIXEL22_1R + break; + } +case 127: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_2 + PIXEL01_3 + PIXEL10_3 + } + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL02_4 + PIXEL12_3 + } + PIXEL11 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } +case 191: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_C + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_C + } + else + { + PIXEL02_2 + } + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + break; + } +case 223: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL10_3 + } + if (Diff(yuv[2], yuv[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_2 + PIXEL12_3 + } + PIXEL11 + PIXEL20_1M + if (Diff(yuv[6], yuv[8])) + { + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL21_3 + PIXEL22_4 + } + break; + } +case 247: + { + PIXEL00_1L + PIXEL01_C + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_C + } + else + { + PIXEL02_2 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_C + } + else + { + PIXEL22_2 + } + break; + } +case 255: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_C + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_C + } + else + { + PIXEL02_2 + } + PIXEL10_C + PIXEL11 + PIXEL12_C + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_C + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_C + } + else + { + PIXEL22_2 + } + break; + } +} diff --git a/src/filters/hq/c/hq4x_pattern.h b/src/filters/hq/c/hq4x_pattern.h new file mode 100644 index 0000000..6bef3c7 --- /dev/null +++ b/src/filters/hq/c/hq4x_pattern.h @@ -0,0 +1,4997 @@ +/* + VisualBoyAdvance - a Game Boy & Game Boy Advance emulator + + Copyright (C) 1999 - 2003 Forgotten + Copyright (C) 2003 - 2004 Forgotten and the VBA development team + Copyright (C) 2005 - 2006 VBA development team + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +switch(pattern) +{ +case 0: +case 1: +case 4: +case 32: +case 128: +case 5: +case 132: +case 160: +case 33: +case 129: +case 36: +case 133: +case 164: +case 161: +case 37: +case 165: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_60 + PIXEL03_20 + PIXEL10_60 + PIXEL11_70 + PIXEL12_70 + PIXEL13_60 + PIXEL20_60 + PIXEL21_70 + PIXEL22_70 + PIXEL23_60 + PIXEL30_20 + PIXEL31_60 + PIXEL32_60 + PIXEL33_20 + break; + } +case 2: +case 34: +case 130: +case 162: + { + PIXEL00_80 + PIXEL01_10 + PIXEL02_10 + PIXEL03_80 + PIXEL10_61 + PIXEL11_30 + PIXEL12_30 + PIXEL13_61 + PIXEL20_60 + PIXEL21_70 + PIXEL22_70 + PIXEL23_60 + PIXEL30_20 + PIXEL31_60 + PIXEL32_60 + PIXEL33_20 + break; + } +case 16: +case 17: +case 48: +case 49: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_61 + PIXEL03_80 + PIXEL10_60 + PIXEL11_70 + PIXEL12_30 + PIXEL13_10 + PIXEL20_60 + PIXEL21_70 + PIXEL22_30 + PIXEL23_10 + PIXEL30_20 + PIXEL31_60 + PIXEL32_61 + PIXEL33_80 + break; + } +case 64: +case 65: +case 68: +case 69: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_60 + PIXEL03_20 + PIXEL10_60 + PIXEL11_70 + PIXEL12_70 + PIXEL13_60 + PIXEL20_61 + PIXEL21_30 + PIXEL22_30 + PIXEL23_61 + PIXEL30_80 + PIXEL31_10 + PIXEL32_10 + PIXEL33_80 + break; + } +case 8: +case 12: +case 136: +case 140: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_60 + PIXEL03_20 + PIXEL10_10 + PIXEL11_30 + PIXEL12_70 + PIXEL13_60 + PIXEL20_10 + PIXEL21_30 + PIXEL22_70 + PIXEL23_60 + PIXEL30_80 + PIXEL31_61 + PIXEL32_60 + PIXEL33_20 + break; + } +case 3: +case 35: +case 131: +case 163: + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_10 + PIXEL03_80 + PIXEL10_81 + PIXEL11_31 + PIXEL12_30 + PIXEL13_61 + PIXEL20_60 + PIXEL21_70 + PIXEL22_70 + PIXEL23_60 + PIXEL30_20 + PIXEL31_60 + PIXEL32_60 + PIXEL33_20 + break; + } +case 6: +case 38: +case 134: +case 166: + { + PIXEL00_80 + PIXEL01_10 + PIXEL02_32 + PIXEL03_82 + PIXEL10_61 + PIXEL11_30 + PIXEL12_32 + PIXEL13_82 + PIXEL20_60 + PIXEL21_70 + PIXEL22_70 + PIXEL23_60 + PIXEL30_20 + PIXEL31_60 + PIXEL32_60 + PIXEL33_20 + break; + } +case 20: +case 21: +case 52: +case 53: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_81 + PIXEL03_81 + PIXEL10_60 + PIXEL11_70 + PIXEL12_31 + PIXEL13_31 + PIXEL20_60 + PIXEL21_70 + PIXEL22_30 + PIXEL23_10 + PIXEL30_20 + PIXEL31_60 + PIXEL32_61 + PIXEL33_80 + break; + } +case 144: +case 145: +case 176: +case 177: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_61 + PIXEL03_80 + PIXEL10_60 + PIXEL11_70 + PIXEL12_30 + PIXEL13_10 + PIXEL20_60 + PIXEL21_70 + PIXEL22_32 + PIXEL23_32 + PIXEL30_20 + PIXEL31_60 + PIXEL32_82 + PIXEL33_82 + break; + } +case 192: +case 193: +case 196: +case 197: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_60 + PIXEL03_20 + PIXEL10_60 + PIXEL11_70 + PIXEL12_70 + PIXEL13_60 + PIXEL20_61 + PIXEL21_30 + PIXEL22_31 + PIXEL23_81 + PIXEL30_80 + PIXEL31_10 + PIXEL32_31 + PIXEL33_81 + break; + } +case 96: +case 97: +case 100: +case 101: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_60 + PIXEL03_20 + PIXEL10_60 + PIXEL11_70 + PIXEL12_70 + PIXEL13_60 + PIXEL20_82 + PIXEL21_32 + PIXEL22_30 + PIXEL23_61 + PIXEL30_82 + PIXEL31_32 + PIXEL32_10 + PIXEL33_80 + break; + } +case 40: +case 44: +case 168: +case 172: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_60 + PIXEL03_20 + PIXEL10_10 + PIXEL11_30 + PIXEL12_70 + PIXEL13_60 + PIXEL20_31 + PIXEL21_31 + PIXEL22_70 + PIXEL23_60 + PIXEL30_81 + PIXEL31_81 + PIXEL32_60 + PIXEL33_20 + break; + } +case 9: +case 13: +case 137: +case 141: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_60 + PIXEL03_20 + PIXEL10_32 + PIXEL11_32 + PIXEL12_70 + PIXEL13_60 + PIXEL20_10 + PIXEL21_30 + PIXEL22_70 + PIXEL23_60 + PIXEL30_80 + PIXEL31_61 + PIXEL32_60 + PIXEL33_20 + break; + } +case 18: +case 50: + { + PIXEL00_80 + PIXEL01_10 + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL12_0 + PIXEL13_50 + } + PIXEL10_61 + PIXEL11_30 + PIXEL20_60 + PIXEL21_70 + PIXEL22_30 + PIXEL23_10 + PIXEL30_20 + PIXEL31_60 + PIXEL32_61 + PIXEL33_80 + break; + } +case 80: +case 81: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_61 + PIXEL03_80 + PIXEL10_60 + PIXEL11_70 + PIXEL12_30 + PIXEL13_10 + PIXEL20_61 + PIXEL21_30 + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + } + else + { + PIXEL22_0 + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + PIXEL30_80 + PIXEL31_10 + break; + } +case 72: +case 76: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_60 + PIXEL03_20 + PIXEL10_10 + PIXEL11_30 + PIXEL12_70 + PIXEL13_60 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + } + else + { + PIXEL20_50 + PIXEL21_0 + PIXEL30_50 + PIXEL31_50 + } + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + break; + } +case 10: +case 138: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_80 + PIXEL01_10 + PIXEL10_10 + PIXEL11_30 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + PIXEL11_0 + } + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_61 + PIXEL20_10 + PIXEL21_30 + PIXEL22_70 + PIXEL23_60 + PIXEL30_80 + PIXEL31_61 + PIXEL32_60 + PIXEL33_20 + break; + } +case 66: + { + PIXEL00_80 + PIXEL01_10 + PIXEL02_10 + PIXEL03_80 + PIXEL10_61 + PIXEL11_30 + PIXEL12_30 + PIXEL13_61 + PIXEL20_61 + PIXEL21_30 + PIXEL22_30 + PIXEL23_61 + PIXEL30_80 + PIXEL31_10 + PIXEL32_10 + PIXEL33_80 + break; + } +case 24: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_61 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_61 + PIXEL32_61 + PIXEL33_80 + break; + } +case 7: +case 39: +case 135: + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_32 + PIXEL03_82 + PIXEL10_81 + PIXEL11_31 + PIXEL12_32 + PIXEL13_82 + PIXEL20_60 + PIXEL21_70 + PIXEL22_70 + PIXEL23_60 + PIXEL30_20 + PIXEL31_60 + PIXEL32_60 + PIXEL33_20 + break; + } +case 148: +case 149: +case 180: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_81 + PIXEL03_81 + PIXEL10_60 + PIXEL11_70 + PIXEL12_31 + PIXEL13_31 + PIXEL20_60 + PIXEL21_70 + PIXEL22_32 + PIXEL23_32 + PIXEL30_20 + PIXEL31_60 + PIXEL32_82 + PIXEL33_82 + break; + } +case 224: +case 228: +case 225: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_60 + PIXEL03_20 + PIXEL10_60 + PIXEL11_70 + PIXEL12_70 + PIXEL13_60 + PIXEL20_82 + PIXEL21_32 + PIXEL22_31 + PIXEL23_81 + PIXEL30_82 + PIXEL31_32 + PIXEL32_31 + PIXEL33_81 + break; + } +case 41: +case 169: +case 45: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_60 + PIXEL03_20 + PIXEL10_32 + PIXEL11_32 + PIXEL12_70 + PIXEL13_60 + PIXEL20_31 + PIXEL21_31 + PIXEL22_70 + PIXEL23_60 + PIXEL30_81 + PIXEL31_81 + PIXEL32_60 + PIXEL33_20 + break; + } +case 22: +case 54: + { + PIXEL00_80 + PIXEL01_10 + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL10_61 + PIXEL11_30 + PIXEL12_0 + PIXEL20_60 + PIXEL21_70 + PIXEL22_30 + PIXEL23_10 + PIXEL30_20 + PIXEL31_60 + PIXEL32_61 + PIXEL33_80 + break; + } +case 208: +case 209: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_61 + PIXEL03_80 + PIXEL10_60 + PIXEL11_70 + PIXEL12_30 + PIXEL13_10 + PIXEL20_61 + PIXEL21_30 + PIXEL22_0 + if (Diff(yuv[6], yuv[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + PIXEL30_80 + PIXEL31_10 + break; + } +case 104: +case 108: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_60 + PIXEL03_20 + PIXEL10_10 + PIXEL11_30 + PIXEL12_70 + PIXEL13_60 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + break; + } +case 11: +case 139: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_61 + PIXEL20_10 + PIXEL21_30 + PIXEL22_70 + PIXEL23_60 + PIXEL30_80 + PIXEL31_61 + PIXEL32_60 + PIXEL33_20 + break; + } +case 19: +case 51: + { + if (Diff(yuv[2], yuv[6])) + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + } + else + { + PIXEL00_12 + PIXEL01_14 + PIXEL02_83 + PIXEL03_50 + PIXEL12_70 + PIXEL13_21 + } + PIXEL10_81 + PIXEL11_31 + PIXEL20_60 + PIXEL21_70 + PIXEL22_30 + PIXEL23_10 + PIXEL30_20 + PIXEL31_60 + PIXEL32_61 + PIXEL33_80 + break; + } +case 146: +case 178: + { + PIXEL00_80 + PIXEL01_10 + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + PIXEL23_32 + PIXEL33_82 + } + else + { + PIXEL02_21 + PIXEL03_50 + PIXEL12_70 + PIXEL13_83 + PIXEL23_13 + PIXEL33_11 + } + PIXEL10_61 + PIXEL11_30 + PIXEL20_60 + PIXEL21_70 + PIXEL22_32 + PIXEL30_20 + PIXEL31_60 + PIXEL32_82 + break; + } +case 84: +case 85: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_81 + if (Diff(yuv[6], yuv[8])) + { + PIXEL03_81 + PIXEL13_31 + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + } + else + { + PIXEL03_12 + PIXEL13_14 + PIXEL22_70 + PIXEL23_83 + PIXEL32_21 + PIXEL33_50 + } + PIXEL10_60 + PIXEL11_70 + PIXEL12_31 + PIXEL20_61 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + break; + } +case 112: +case 113: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_61 + PIXEL03_80 + PIXEL10_60 + PIXEL11_70 + PIXEL12_30 + PIXEL13_10 + PIXEL20_82 + PIXEL21_32 + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_30 + PIXEL23_10 + PIXEL30_82 + PIXEL31_32 + PIXEL32_10 + PIXEL33_80 + } + else + { + PIXEL22_70 + PIXEL23_21 + PIXEL30_11 + PIXEL31_13 + PIXEL32_83 + PIXEL33_50 + } + break; + } +case 200: +case 204: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_60 + PIXEL03_20 + PIXEL10_10 + PIXEL11_30 + PIXEL12_70 + PIXEL13_60 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + PIXEL32_31 + PIXEL33_81 + } + else + { + PIXEL20_21 + PIXEL21_70 + PIXEL30_50 + PIXEL31_83 + PIXEL32_14 + PIXEL33_12 + } + PIXEL22_31 + PIXEL23_81 + break; + } +case 73: +case 77: + { + if (Diff(yuv[8], yuv[4])) + { + PIXEL00_82 + PIXEL10_32 + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + } + else + { + PIXEL00_11 + PIXEL10_13 + PIXEL20_83 + PIXEL21_70 + PIXEL30_50 + PIXEL31_21 + } + PIXEL01_82 + PIXEL02_60 + PIXEL03_20 + PIXEL11_32 + PIXEL12_70 + PIXEL13_60 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + break; + } +case 42: +case 170: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_80 + PIXEL01_10 + PIXEL10_10 + PIXEL11_30 + PIXEL20_31 + PIXEL30_81 + } + else + { + PIXEL00_50 + PIXEL01_21 + PIXEL10_83 + PIXEL11_70 + PIXEL20_14 + PIXEL30_12 + } + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_61 + PIXEL21_31 + PIXEL22_70 + PIXEL23_60 + PIXEL31_81 + PIXEL32_60 + PIXEL33_20 + break; + } +case 14: +case 142: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_80 + PIXEL01_10 + PIXEL02_32 + PIXEL03_82 + PIXEL10_10 + PIXEL11_30 + } + else + { + PIXEL00_50 + PIXEL01_83 + PIXEL02_13 + PIXEL03_11 + PIXEL10_21 + PIXEL11_70 + } + PIXEL12_32 + PIXEL13_82 + PIXEL20_10 + PIXEL21_30 + PIXEL22_70 + PIXEL23_60 + PIXEL30_80 + PIXEL31_61 + PIXEL32_60 + PIXEL33_20 + break; + } +case 67: + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_10 + PIXEL03_80 + PIXEL10_81 + PIXEL11_31 + PIXEL12_30 + PIXEL13_61 + PIXEL20_61 + PIXEL21_30 + PIXEL22_30 + PIXEL23_61 + PIXEL30_80 + PIXEL31_10 + PIXEL32_10 + PIXEL33_80 + break; + } +case 70: + { + PIXEL00_80 + PIXEL01_10 + PIXEL02_32 + PIXEL03_82 + PIXEL10_61 + PIXEL11_30 + PIXEL12_32 + PIXEL13_82 + PIXEL20_61 + PIXEL21_30 + PIXEL22_30 + PIXEL23_61 + PIXEL30_80 + PIXEL31_10 + PIXEL32_10 + PIXEL33_80 + break; + } +case 28: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_81 + PIXEL03_81 + PIXEL10_10 + PIXEL11_30 + PIXEL12_31 + PIXEL13_31 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_61 + PIXEL32_61 + PIXEL33_80 + break; + } +case 152: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_61 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_32 + PIXEL23_32 + PIXEL30_80 + PIXEL31_61 + PIXEL32_82 + PIXEL33_82 + break; + } +case 194: + { + PIXEL00_80 + PIXEL01_10 + PIXEL02_10 + PIXEL03_80 + PIXEL10_61 + PIXEL11_30 + PIXEL12_30 + PIXEL13_61 + PIXEL20_61 + PIXEL21_30 + PIXEL22_31 + PIXEL23_81 + PIXEL30_80 + PIXEL31_10 + PIXEL32_31 + PIXEL33_81 + break; + } +case 98: + { + PIXEL00_80 + PIXEL01_10 + PIXEL02_10 + PIXEL03_80 + PIXEL10_61 + PIXEL11_30 + PIXEL12_30 + PIXEL13_61 + PIXEL20_82 + PIXEL21_32 + PIXEL22_30 + PIXEL23_61 + PIXEL30_82 + PIXEL31_32 + PIXEL32_10 + PIXEL33_80 + break; + } +case 56: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_61 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + PIXEL20_31 + PIXEL21_31 + PIXEL22_30 + PIXEL23_10 + PIXEL30_81 + PIXEL31_81 + PIXEL32_61 + PIXEL33_80 + break; + } +case 25: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_61 + PIXEL03_80 + PIXEL10_32 + PIXEL11_32 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_61 + PIXEL32_61 + PIXEL33_80 + break; + } +case 26: +case 31: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL11_0 + PIXEL12_0 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_61 + PIXEL32_61 + PIXEL33_80 + break; + } +case 82: +case 214: + { + PIXEL00_80 + PIXEL01_10 + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL10_61 + PIXEL11_30 + PIXEL12_0 + PIXEL20_61 + PIXEL21_30 + PIXEL22_0 + if (Diff(yuv[6], yuv[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + PIXEL30_80 + PIXEL31_10 + break; + } +case 88: +case 248: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_61 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + PIXEL22_0 + if (Diff(yuv[6], yuv[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + break; + } +case 74: +case 107: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_61 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + break; + } +case 27: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_61 + PIXEL32_61 + PIXEL33_80 + break; + } +case 86: + { + PIXEL00_80 + PIXEL01_10 + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL10_61 + PIXEL11_30 + PIXEL12_0 + PIXEL20_61 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_10 + PIXEL32_10 + PIXEL33_80 + break; + } +case 216: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_61 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_0 + if (Diff(yuv[6], yuv[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + PIXEL30_80 + PIXEL31_10 + break; + } +case 106: + { + PIXEL00_80 + PIXEL01_10 + PIXEL02_10 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_61 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + break; + } +case 30: + { + PIXEL00_80 + PIXEL01_10 + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL10_10 + PIXEL11_30 + PIXEL12_0 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_61 + PIXEL32_61 + PIXEL33_80 + break; + } +case 210: + { + PIXEL00_80 + PIXEL01_10 + PIXEL02_10 + PIXEL03_80 + PIXEL10_61 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + PIXEL20_61 + PIXEL21_30 + PIXEL22_0 + if (Diff(yuv[6], yuv[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + PIXEL30_80 + PIXEL31_10 + break; + } +case 120: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_61 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + break; + } +case 75: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_61 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_61 + PIXEL30_80 + PIXEL31_10 + PIXEL32_10 + PIXEL33_80 + break; + } +case 29: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_81 + PIXEL03_81 + PIXEL10_32 + PIXEL11_32 + PIXEL12_31 + PIXEL13_31 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_61 + PIXEL32_61 + PIXEL33_80 + break; + } +case 198: + { + PIXEL00_80 + PIXEL01_10 + PIXEL02_32 + PIXEL03_82 + PIXEL10_61 + PIXEL11_30 + PIXEL12_32 + PIXEL13_82 + PIXEL20_61 + PIXEL21_30 + PIXEL22_31 + PIXEL23_81 + PIXEL30_80 + PIXEL31_10 + PIXEL32_31 + PIXEL33_81 + break; + } +case 184: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_61 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + PIXEL20_31 + PIXEL21_31 + PIXEL22_32 + PIXEL23_32 + PIXEL30_81 + PIXEL31_81 + PIXEL32_82 + PIXEL33_82 + break; + } +case 99: + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_10 + PIXEL03_80 + PIXEL10_81 + PIXEL11_31 + PIXEL12_30 + PIXEL13_61 + PIXEL20_82 + PIXEL21_32 + PIXEL22_30 + PIXEL23_61 + PIXEL30_82 + PIXEL31_32 + PIXEL32_10 + PIXEL33_80 + break; + } +case 57: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_61 + PIXEL03_80 + PIXEL10_32 + PIXEL11_32 + PIXEL12_30 + PIXEL13_10 + PIXEL20_31 + PIXEL21_31 + PIXEL22_30 + PIXEL23_10 + PIXEL30_81 + PIXEL31_81 + PIXEL32_61 + PIXEL33_80 + break; + } +case 71: + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_32 + PIXEL03_82 + PIXEL10_81 + PIXEL11_31 + PIXEL12_32 + PIXEL13_82 + PIXEL20_61 + PIXEL21_30 + PIXEL22_30 + PIXEL23_61 + PIXEL30_80 + PIXEL31_10 + PIXEL32_10 + PIXEL33_80 + break; + } +case 156: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_81 + PIXEL03_81 + PIXEL10_10 + PIXEL11_30 + PIXEL12_31 + PIXEL13_31 + PIXEL20_10 + PIXEL21_30 + PIXEL22_32 + PIXEL23_32 + PIXEL30_80 + PIXEL31_61 + PIXEL32_82 + PIXEL33_82 + break; + } +case 226: + { + PIXEL00_80 + PIXEL01_10 + PIXEL02_10 + PIXEL03_80 + PIXEL10_61 + PIXEL11_30 + PIXEL12_30 + PIXEL13_61 + PIXEL20_82 + PIXEL21_32 + PIXEL22_31 + PIXEL23_81 + PIXEL30_82 + PIXEL31_32 + PIXEL32_31 + PIXEL33_81 + break; + } +case 60: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_81 + PIXEL03_81 + PIXEL10_10 + PIXEL11_30 + PIXEL12_31 + PIXEL13_31 + PIXEL20_31 + PIXEL21_31 + PIXEL22_30 + PIXEL23_10 + PIXEL30_81 + PIXEL31_81 + PIXEL32_61 + PIXEL33_80 + break; + } +case 195: + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_10 + PIXEL03_80 + PIXEL10_81 + PIXEL11_31 + PIXEL12_30 + PIXEL13_61 + PIXEL20_61 + PIXEL21_30 + PIXEL22_31 + PIXEL23_81 + PIXEL30_80 + PIXEL31_10 + PIXEL32_31 + PIXEL33_81 + break; + } +case 102: + { + PIXEL00_80 + PIXEL01_10 + PIXEL02_32 + PIXEL03_82 + PIXEL10_61 + PIXEL11_30 + PIXEL12_32 + PIXEL13_82 + PIXEL20_82 + PIXEL21_32 + PIXEL22_30 + PIXEL23_61 + PIXEL30_82 + PIXEL31_32 + PIXEL32_10 + PIXEL33_80 + break; + } +case 153: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_61 + PIXEL03_80 + PIXEL10_32 + PIXEL11_32 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_32 + PIXEL23_32 + PIXEL30_80 + PIXEL31_61 + PIXEL32_82 + PIXEL33_82 + break; + } +case 58: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_80 + PIXEL01_10 + PIXEL10_10 + PIXEL11_30 + } + else + { + PIXEL00_20 + PIXEL01_12 + PIXEL10_11 + PIXEL11_0 + } + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + } + else + { + PIXEL02_11 + PIXEL03_20 + PIXEL12_0 + PIXEL13_12 + } + PIXEL20_31 + PIXEL21_31 + PIXEL22_30 + PIXEL23_10 + PIXEL30_81 + PIXEL31_81 + PIXEL32_61 + PIXEL33_80 + break; + } +case 83: + { + PIXEL00_81 + PIXEL01_31 + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + } + else + { + PIXEL02_11 + PIXEL03_20 + PIXEL12_0 + PIXEL13_12 + } + PIXEL10_81 + PIXEL11_31 + PIXEL20_61 + PIXEL21_30 + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + } + else + { + PIXEL22_0 + PIXEL23_11 + PIXEL32_12 + PIXEL33_20 + } + PIXEL30_80 + PIXEL31_10 + break; + } +case 92: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_81 + PIXEL03_81 + PIXEL10_10 + PIXEL11_30 + PIXEL12_31 + PIXEL13_31 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + } + else + { + PIXEL20_12 + PIXEL21_0 + PIXEL30_20 + PIXEL31_11 + } + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + } + else + { + PIXEL22_0 + PIXEL23_11 + PIXEL32_12 + PIXEL33_20 + } + break; + } +case 202: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_80 + PIXEL01_10 + PIXEL10_10 + PIXEL11_30 + } + else + { + PIXEL00_20 + PIXEL01_12 + PIXEL10_11 + PIXEL11_0 + } + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_61 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + } + else + { + PIXEL20_12 + PIXEL21_0 + PIXEL30_20 + PIXEL31_11 + } + PIXEL22_31 + PIXEL23_81 + PIXEL32_31 + PIXEL33_81 + break; + } +case 78: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_80 + PIXEL01_10 + PIXEL10_10 + PIXEL11_30 + } + else + { + PIXEL00_20 + PIXEL01_12 + PIXEL10_11 + PIXEL11_0 + } + PIXEL02_32 + PIXEL03_82 + PIXEL12_32 + PIXEL13_82 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + } + else + { + PIXEL20_12 + PIXEL21_0 + PIXEL30_20 + PIXEL31_11 + } + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + break; + } +case 154: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_80 + PIXEL01_10 + PIXEL10_10 + PIXEL11_30 + } + else + { + PIXEL00_20 + PIXEL01_12 + PIXEL10_11 + PIXEL11_0 + } + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + } + else + { + PIXEL02_11 + PIXEL03_20 + PIXEL12_0 + PIXEL13_12 + } + PIXEL20_10 + PIXEL21_30 + PIXEL22_32 + PIXEL23_32 + PIXEL30_80 + PIXEL31_61 + PIXEL32_82 + PIXEL33_82 + break; + } +case 114: + { + PIXEL00_80 + PIXEL01_10 + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + } + else + { + PIXEL02_11 + PIXEL03_20 + PIXEL12_0 + PIXEL13_12 + } + PIXEL10_61 + PIXEL11_30 + PIXEL20_82 + PIXEL21_32 + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + } + else + { + PIXEL22_0 + PIXEL23_11 + PIXEL32_12 + PIXEL33_20 + } + PIXEL30_82 + PIXEL31_32 + break; + } +case 89: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_61 + PIXEL03_80 + PIXEL10_32 + PIXEL11_32 + PIXEL12_30 + PIXEL13_10 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + } + else + { + PIXEL20_12 + PIXEL21_0 + PIXEL30_20 + PIXEL31_11 + } + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + } + else + { + PIXEL22_0 + PIXEL23_11 + PIXEL32_12 + PIXEL33_20 + } + break; + } +case 90: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_80 + PIXEL01_10 + PIXEL10_10 + PIXEL11_30 + } + else + { + PIXEL00_20 + PIXEL01_12 + PIXEL10_11 + PIXEL11_0 + } + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + } + else + { + PIXEL02_11 + PIXEL03_20 + PIXEL12_0 + PIXEL13_12 + } + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + } + else + { + PIXEL20_12 + PIXEL21_0 + PIXEL30_20 + PIXEL31_11 + } + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + } + else + { + PIXEL22_0 + PIXEL23_11 + PIXEL32_12 + PIXEL33_20 + } + break; + } +case 55: +case 23: + { + if (Diff(yuv[2], yuv[6])) + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_0 + PIXEL03_0 + PIXEL12_0 + PIXEL13_0 + } + else + { + PIXEL00_12 + PIXEL01_14 + PIXEL02_83 + PIXEL03_50 + PIXEL12_70 + PIXEL13_21 + } + PIXEL10_81 + PIXEL11_31 + PIXEL20_60 + PIXEL21_70 + PIXEL22_30 + PIXEL23_10 + PIXEL30_20 + PIXEL31_60 + PIXEL32_61 + PIXEL33_80 + break; + } +case 182: +case 150: + { + PIXEL00_80 + PIXEL01_10 + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL12_0 + PIXEL13_0 + PIXEL23_32 + PIXEL33_82 + } + else + { + PIXEL02_21 + PIXEL03_50 + PIXEL12_70 + PIXEL13_83 + PIXEL23_13 + PIXEL33_11 + } + PIXEL10_61 + PIXEL11_30 + PIXEL20_60 + PIXEL21_70 + PIXEL22_32 + PIXEL30_20 + PIXEL31_60 + PIXEL32_82 + break; + } +case 213: +case 212: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_81 + if (Diff(yuv[6], yuv[8])) + { + PIXEL03_81 + PIXEL13_31 + PIXEL22_0 + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL03_12 + PIXEL13_14 + PIXEL22_70 + PIXEL23_83 + PIXEL32_21 + PIXEL33_50 + } + PIXEL10_60 + PIXEL11_70 + PIXEL12_31 + PIXEL20_61 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + break; + } +case 241: +case 240: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_61 + PIXEL03_80 + PIXEL10_60 + PIXEL11_70 + PIXEL12_30 + PIXEL13_10 + PIXEL20_82 + PIXEL21_32 + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_0 + PIXEL23_0 + PIXEL30_82 + PIXEL31_32 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL22_70 + PIXEL23_21 + PIXEL30_11 + PIXEL31_13 + PIXEL32_83 + PIXEL33_50 + } + break; + } +case 236: +case 232: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_60 + PIXEL03_20 + PIXEL10_10 + PIXEL11_30 + PIXEL12_70 + PIXEL13_60 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_0 + PIXEL21_0 + PIXEL30_0 + PIXEL31_0 + PIXEL32_31 + PIXEL33_81 + } + else + { + PIXEL20_21 + PIXEL21_70 + PIXEL30_50 + PIXEL31_83 + PIXEL32_14 + PIXEL33_12 + } + PIXEL22_31 + PIXEL23_81 + break; + } +case 109: +case 105: + { + if (Diff(yuv[8], yuv[4])) + { + PIXEL00_82 + PIXEL10_32 + PIXEL20_0 + PIXEL21_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL00_11 + PIXEL10_13 + PIXEL20_83 + PIXEL21_70 + PIXEL30_50 + PIXEL31_21 + } + PIXEL01_82 + PIXEL02_60 + PIXEL03_20 + PIXEL11_32 + PIXEL12_70 + PIXEL13_60 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + break; + } +case 171: +case 43: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + PIXEL11_0 + PIXEL20_31 + PIXEL30_81 + } + else + { + PIXEL00_50 + PIXEL01_21 + PIXEL10_83 + PIXEL11_70 + PIXEL20_14 + PIXEL30_12 + } + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_61 + PIXEL21_31 + PIXEL22_70 + PIXEL23_60 + PIXEL31_81 + PIXEL32_60 + PIXEL33_20 + break; + } +case 143: +case 15: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL02_32 + PIXEL03_82 + PIXEL10_0 + PIXEL11_0 + } + else + { + PIXEL00_50 + PIXEL01_83 + PIXEL02_13 + PIXEL03_11 + PIXEL10_21 + PIXEL11_70 + } + PIXEL12_32 + PIXEL13_82 + PIXEL20_10 + PIXEL21_30 + PIXEL22_70 + PIXEL23_60 + PIXEL30_80 + PIXEL31_61 + PIXEL32_60 + PIXEL33_20 + break; + } +case 124: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_81 + PIXEL03_81 + PIXEL10_10 + PIXEL11_30 + PIXEL12_31 + PIXEL13_31 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + break; + } +case 203: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_61 + PIXEL20_10 + PIXEL21_30 + PIXEL22_31 + PIXEL23_81 + PIXEL30_80 + PIXEL31_10 + PIXEL32_31 + PIXEL33_81 + break; + } +case 62: + { + PIXEL00_80 + PIXEL01_10 + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL10_10 + PIXEL11_30 + PIXEL12_0 + PIXEL20_31 + PIXEL21_31 + PIXEL22_30 + PIXEL23_10 + PIXEL30_81 + PIXEL31_81 + PIXEL32_61 + PIXEL33_80 + break; + } +case 211: + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_10 + PIXEL03_80 + PIXEL10_81 + PIXEL11_31 + PIXEL12_30 + PIXEL13_10 + PIXEL20_61 + PIXEL21_30 + PIXEL22_0 + if (Diff(yuv[6], yuv[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + PIXEL30_80 + PIXEL31_10 + break; + } +case 118: + { + PIXEL00_80 + PIXEL01_10 + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL10_61 + PIXEL11_30 + PIXEL12_0 + PIXEL20_82 + PIXEL21_32 + PIXEL22_30 + PIXEL23_10 + PIXEL30_82 + PIXEL31_32 + PIXEL32_10 + PIXEL33_80 + break; + } +case 217: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_61 + PIXEL03_80 + PIXEL10_32 + PIXEL11_32 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_0 + if (Diff(yuv[6], yuv[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + PIXEL30_80 + PIXEL31_10 + break; + } +case 110: + { + PIXEL00_80 + PIXEL01_10 + PIXEL02_32 + PIXEL03_82 + PIXEL10_10 + PIXEL11_30 + PIXEL12_32 + PIXEL13_82 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + break; + } +case 155: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_32 + PIXEL23_32 + PIXEL30_80 + PIXEL31_61 + PIXEL32_82 + PIXEL33_82 + break; + } +case 188: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_81 + PIXEL03_81 + PIXEL10_10 + PIXEL11_30 + PIXEL12_31 + PIXEL13_31 + PIXEL20_31 + PIXEL21_31 + PIXEL22_32 + PIXEL23_32 + PIXEL30_81 + PIXEL31_81 + PIXEL32_82 + PIXEL33_82 + break; + } +case 185: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_61 + PIXEL03_80 + PIXEL10_32 + PIXEL11_32 + PIXEL12_30 + PIXEL13_10 + PIXEL20_31 + PIXEL21_31 + PIXEL22_32 + PIXEL23_32 + PIXEL30_81 + PIXEL31_81 + PIXEL32_82 + PIXEL33_82 + break; + } +case 61: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_81 + PIXEL03_81 + PIXEL10_32 + PIXEL11_32 + PIXEL12_31 + PIXEL13_31 + PIXEL20_31 + PIXEL21_31 + PIXEL22_30 + PIXEL23_10 + PIXEL30_81 + PIXEL31_81 + PIXEL32_61 + PIXEL33_80 + break; + } +case 157: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_81 + PIXEL03_81 + PIXEL10_32 + PIXEL11_32 + PIXEL12_31 + PIXEL13_31 + PIXEL20_10 + PIXEL21_30 + PIXEL22_32 + PIXEL23_32 + PIXEL30_80 + PIXEL31_61 + PIXEL32_82 + PIXEL33_82 + break; + } +case 103: + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_32 + PIXEL03_82 + PIXEL10_81 + PIXEL11_31 + PIXEL12_32 + PIXEL13_82 + PIXEL20_82 + PIXEL21_32 + PIXEL22_30 + PIXEL23_61 + PIXEL30_82 + PIXEL31_32 + PIXEL32_10 + PIXEL33_80 + break; + } +case 227: + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_10 + PIXEL03_80 + PIXEL10_81 + PIXEL11_31 + PIXEL12_30 + PIXEL13_61 + PIXEL20_82 + PIXEL21_32 + PIXEL22_31 + PIXEL23_81 + PIXEL30_82 + PIXEL31_32 + PIXEL32_31 + PIXEL33_81 + break; + } +case 230: + { + PIXEL00_80 + PIXEL01_10 + PIXEL02_32 + PIXEL03_82 + PIXEL10_61 + PIXEL11_30 + PIXEL12_32 + PIXEL13_82 + PIXEL20_82 + PIXEL21_32 + PIXEL22_31 + PIXEL23_81 + PIXEL30_82 + PIXEL31_32 + PIXEL32_31 + PIXEL33_81 + break; + } +case 199: + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_32 + PIXEL03_82 + PIXEL10_81 + PIXEL11_31 + PIXEL12_32 + PIXEL13_82 + PIXEL20_61 + PIXEL21_30 + PIXEL22_31 + PIXEL23_81 + PIXEL30_80 + PIXEL31_10 + PIXEL32_31 + PIXEL33_81 + break; + } +case 220: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_81 + PIXEL03_81 + PIXEL10_10 + PIXEL11_30 + PIXEL12_31 + PIXEL13_31 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + } + else + { + PIXEL20_12 + PIXEL21_0 + PIXEL30_20 + PIXEL31_11 + } + PIXEL22_0 + if (Diff(yuv[6], yuv[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + break; + } +case 158: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_80 + PIXEL01_10 + PIXEL10_10 + PIXEL11_30 + } + else + { + PIXEL00_20 + PIXEL01_12 + PIXEL10_11 + PIXEL11_0 + } + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL12_0 + PIXEL20_10 + PIXEL21_30 + PIXEL22_32 + PIXEL23_32 + PIXEL30_80 + PIXEL31_61 + PIXEL32_82 + PIXEL33_82 + break; + } +case 234: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_80 + PIXEL01_10 + PIXEL10_10 + PIXEL11_30 + } + else + { + PIXEL00_20 + PIXEL01_12 + PIXEL10_11 + PIXEL11_0 + } + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_61 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + PIXEL22_31 + PIXEL23_81 + PIXEL32_31 + PIXEL33_81 + break; + } +case 242: + { + PIXEL00_80 + PIXEL01_10 + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + } + else + { + PIXEL02_11 + PIXEL03_20 + PIXEL12_0 + PIXEL13_12 + } + PIXEL10_61 + PIXEL11_30 + PIXEL20_82 + PIXEL21_32 + PIXEL22_0 + if (Diff(yuv[6], yuv[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + PIXEL30_82 + PIXEL31_32 + break; + } +case 59: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + } + else + { + PIXEL02_11 + PIXEL03_20 + PIXEL12_0 + PIXEL13_12 + } + PIXEL11_0 + PIXEL20_31 + PIXEL21_31 + PIXEL22_30 + PIXEL23_10 + PIXEL30_81 + PIXEL31_81 + PIXEL32_61 + PIXEL33_80 + break; + } +case 121: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_61 + PIXEL03_80 + PIXEL10_32 + PIXEL11_32 + PIXEL12_30 + PIXEL13_10 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + } + else + { + PIXEL22_0 + PIXEL23_11 + PIXEL32_12 + PIXEL33_20 + } + break; + } +case 87: + { + PIXEL00_81 + PIXEL01_31 + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL10_81 + PIXEL11_31 + PIXEL12_0 + PIXEL20_61 + PIXEL21_30 + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + } + else + { + PIXEL22_0 + PIXEL23_11 + PIXEL32_12 + PIXEL33_20 + } + PIXEL30_80 + PIXEL31_10 + break; + } +case 79: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + PIXEL02_32 + PIXEL03_82 + PIXEL11_0 + PIXEL12_32 + PIXEL13_82 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + } + else + { + PIXEL20_12 + PIXEL21_0 + PIXEL30_20 + PIXEL31_11 + } + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + break; + } +case 122: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_80 + PIXEL01_10 + PIXEL10_10 + PIXEL11_30 + } + else + { + PIXEL00_20 + PIXEL01_12 + PIXEL10_11 + PIXEL11_0 + } + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + } + else + { + PIXEL02_11 + PIXEL03_20 + PIXEL12_0 + PIXEL13_12 + } + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + } + else + { + PIXEL22_0 + PIXEL23_11 + PIXEL32_12 + PIXEL33_20 + } + break; + } +case 94: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_80 + PIXEL01_10 + PIXEL10_10 + PIXEL11_30 + } + else + { + PIXEL00_20 + PIXEL01_12 + PIXEL10_11 + PIXEL11_0 + } + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL12_0 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + } + else + { + PIXEL20_12 + PIXEL21_0 + PIXEL30_20 + PIXEL31_11 + } + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + } + else + { + PIXEL22_0 + PIXEL23_11 + PIXEL32_12 + PIXEL33_20 + } + break; + } +case 218: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_80 + PIXEL01_10 + PIXEL10_10 + PIXEL11_30 + } + else + { + PIXEL00_20 + PIXEL01_12 + PIXEL10_11 + PIXEL11_0 + } + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + } + else + { + PIXEL02_11 + PIXEL03_20 + PIXEL12_0 + PIXEL13_12 + } + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + } + else + { + PIXEL20_12 + PIXEL21_0 + PIXEL30_20 + PIXEL31_11 + } + PIXEL22_0 + if (Diff(yuv[6], yuv[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + break; + } +case 91: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + } + else + { + PIXEL02_11 + PIXEL03_20 + PIXEL12_0 + PIXEL13_12 + } + PIXEL11_0 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + } + else + { + PIXEL20_12 + PIXEL21_0 + PIXEL30_20 + PIXEL31_11 + } + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + } + else + { + PIXEL22_0 + PIXEL23_11 + PIXEL32_12 + PIXEL33_20 + } + break; + } +case 229: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_60 + PIXEL03_20 + PIXEL10_60 + PIXEL11_70 + PIXEL12_70 + PIXEL13_60 + PIXEL20_82 + PIXEL21_32 + PIXEL22_31 + PIXEL23_81 + PIXEL30_82 + PIXEL31_32 + PIXEL32_31 + PIXEL33_81 + break; + } +case 167: + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_32 + PIXEL03_82 + PIXEL10_81 + PIXEL11_31 + PIXEL12_32 + PIXEL13_82 + PIXEL20_60 + PIXEL21_70 + PIXEL22_70 + PIXEL23_60 + PIXEL30_20 + PIXEL31_60 + PIXEL32_60 + PIXEL33_20 + break; + } +case 173: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_60 + PIXEL03_20 + PIXEL10_32 + PIXEL11_32 + PIXEL12_70 + PIXEL13_60 + PIXEL20_31 + PIXEL21_31 + PIXEL22_70 + PIXEL23_60 + PIXEL30_81 + PIXEL31_81 + PIXEL32_60 + PIXEL33_20 + break; + } +case 181: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_81 + PIXEL03_81 + PIXEL10_60 + PIXEL11_70 + PIXEL12_31 + PIXEL13_31 + PIXEL20_60 + PIXEL21_70 + PIXEL22_32 + PIXEL23_32 + PIXEL30_20 + PIXEL31_60 + PIXEL32_82 + PIXEL33_82 + break; + } +case 186: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_80 + PIXEL01_10 + PIXEL10_10 + PIXEL11_30 + } + else + { + PIXEL00_20 + PIXEL01_12 + PIXEL10_11 + PIXEL11_0 + } + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + } + else + { + PIXEL02_11 + PIXEL03_20 + PIXEL12_0 + PIXEL13_12 + } + PIXEL20_31 + PIXEL21_31 + PIXEL22_32 + PIXEL23_32 + PIXEL30_81 + PIXEL31_81 + PIXEL32_82 + PIXEL33_82 + break; + } +case 115: + { + PIXEL00_81 + PIXEL01_31 + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + } + else + { + PIXEL02_11 + PIXEL03_20 + PIXEL12_0 + PIXEL13_12 + } + PIXEL10_81 + PIXEL11_31 + PIXEL20_82 + PIXEL21_32 + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + } + else + { + PIXEL22_0 + PIXEL23_11 + PIXEL32_12 + PIXEL33_20 + } + PIXEL30_82 + PIXEL31_32 + break; + } +case 93: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_81 + PIXEL03_81 + PIXEL10_32 + PIXEL11_32 + PIXEL12_31 + PIXEL13_31 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + } + else + { + PIXEL20_12 + PIXEL21_0 + PIXEL30_20 + PIXEL31_11 + } + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + } + else + { + PIXEL22_0 + PIXEL23_11 + PIXEL32_12 + PIXEL33_20 + } + break; + } +case 206: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_80 + PIXEL01_10 + PIXEL10_10 + PIXEL11_30 + } + else + { + PIXEL00_20 + PIXEL01_12 + PIXEL10_11 + PIXEL11_0 + } + PIXEL02_32 + PIXEL03_82 + PIXEL12_32 + PIXEL13_82 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + } + else + { + PIXEL20_12 + PIXEL21_0 + PIXEL30_20 + PIXEL31_11 + } + PIXEL22_31 + PIXEL23_81 + PIXEL32_31 + PIXEL33_81 + break; + } +case 205: +case 201: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_60 + PIXEL03_20 + PIXEL10_32 + PIXEL11_32 + PIXEL12_70 + PIXEL13_60 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + } + else + { + PIXEL20_12 + PIXEL21_0 + PIXEL30_20 + PIXEL31_11 + } + PIXEL22_31 + PIXEL23_81 + PIXEL32_31 + PIXEL33_81 + break; + } +case 174: +case 46: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_80 + PIXEL01_10 + PIXEL10_10 + PIXEL11_30 + } + else + { + PIXEL00_20 + PIXEL01_12 + PIXEL10_11 + PIXEL11_0 + } + PIXEL02_32 + PIXEL03_82 + PIXEL12_32 + PIXEL13_82 + PIXEL20_31 + PIXEL21_31 + PIXEL22_70 + PIXEL23_60 + PIXEL30_81 + PIXEL31_81 + PIXEL32_60 + PIXEL33_20 + break; + } +case 179: +case 147: + { + PIXEL00_81 + PIXEL01_31 + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + } + else + { + PIXEL02_11 + PIXEL03_20 + PIXEL12_0 + PIXEL13_12 + } + PIXEL10_81 + PIXEL11_31 + PIXEL20_60 + PIXEL21_70 + PIXEL22_32 + PIXEL23_32 + PIXEL30_20 + PIXEL31_60 + PIXEL32_82 + PIXEL33_82 + break; + } +case 117: +case 116: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_81 + PIXEL03_81 + PIXEL10_60 + PIXEL11_70 + PIXEL12_31 + PIXEL13_31 + PIXEL20_82 + PIXEL21_32 + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + } + else + { + PIXEL22_0 + PIXEL23_11 + PIXEL32_12 + PIXEL33_20 + } + PIXEL30_82 + PIXEL31_32 + break; + } +case 189: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_81 + PIXEL03_81 + PIXEL10_32 + PIXEL11_32 + PIXEL12_31 + PIXEL13_31 + PIXEL20_31 + PIXEL21_31 + PIXEL22_32 + PIXEL23_32 + PIXEL30_81 + PIXEL31_81 + PIXEL32_82 + PIXEL33_82 + break; + } +case 231: + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_32 + PIXEL03_82 + PIXEL10_81 + PIXEL11_31 + PIXEL12_32 + PIXEL13_82 + PIXEL20_82 + PIXEL21_32 + PIXEL22_31 + PIXEL23_81 + PIXEL30_82 + PIXEL31_32 + PIXEL32_31 + PIXEL33_81 + break; + } +case 126: + { + PIXEL00_80 + PIXEL01_10 + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL10_10 + PIXEL11_30 + PIXEL12_0 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + break; + } +case 219: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_0 + if (Diff(yuv[6], yuv[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + PIXEL30_80 + PIXEL31_10 + break; + } +case 125: + { + if (Diff(yuv[8], yuv[4])) + { + PIXEL00_82 + PIXEL10_32 + PIXEL20_0 + PIXEL21_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL00_11 + PIXEL10_13 + PIXEL20_83 + PIXEL21_70 + PIXEL30_50 + PIXEL31_21 + } + PIXEL01_82 + PIXEL02_81 + PIXEL03_81 + PIXEL11_32 + PIXEL12_31 + PIXEL13_31 + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + break; + } +case 221: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_81 + if (Diff(yuv[6], yuv[8])) + { + PIXEL03_81 + PIXEL13_31 + PIXEL22_0 + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL03_12 + PIXEL13_14 + PIXEL22_70 + PIXEL23_83 + PIXEL32_21 + PIXEL33_50 + } + PIXEL10_32 + PIXEL11_32 + PIXEL12_31 + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + break; + } +case 207: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL02_32 + PIXEL03_82 + PIXEL10_0 + PIXEL11_0 + } + else + { + PIXEL00_50 + PIXEL01_83 + PIXEL02_13 + PIXEL03_11 + PIXEL10_21 + PIXEL11_70 + } + PIXEL12_32 + PIXEL13_82 + PIXEL20_10 + PIXEL21_30 + PIXEL22_31 + PIXEL23_81 + PIXEL30_80 + PIXEL31_10 + PIXEL32_31 + PIXEL33_81 + break; + } +case 238: + { + PIXEL00_80 + PIXEL01_10 + PIXEL02_32 + PIXEL03_82 + PIXEL10_10 + PIXEL11_30 + PIXEL12_32 + PIXEL13_82 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_0 + PIXEL21_0 + PIXEL30_0 + PIXEL31_0 + PIXEL32_31 + PIXEL33_81 + } + else + { + PIXEL20_21 + PIXEL21_70 + PIXEL30_50 + PIXEL31_83 + PIXEL32_14 + PIXEL33_12 + } + PIXEL22_31 + PIXEL23_81 + break; + } +case 190: + { + PIXEL00_80 + PIXEL01_10 + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL12_0 + PIXEL13_0 + PIXEL23_32 + PIXEL33_82 + } + else + { + PIXEL02_21 + PIXEL03_50 + PIXEL12_70 + PIXEL13_83 + PIXEL23_13 + PIXEL33_11 + } + PIXEL10_10 + PIXEL11_30 + PIXEL20_31 + PIXEL21_31 + PIXEL22_32 + PIXEL30_81 + PIXEL31_81 + PIXEL32_82 + break; + } +case 187: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + PIXEL11_0 + PIXEL20_31 + PIXEL30_81 + } + else + { + PIXEL00_50 + PIXEL01_21 + PIXEL10_83 + PIXEL11_70 + PIXEL20_14 + PIXEL30_12 + } + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + PIXEL21_31 + PIXEL22_32 + PIXEL23_32 + PIXEL31_81 + PIXEL32_82 + PIXEL33_82 + break; + } +case 243: + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_10 + PIXEL03_80 + PIXEL10_81 + PIXEL11_31 + PIXEL12_30 + PIXEL13_10 + PIXEL20_82 + PIXEL21_32 + if (Diff(yuv[6], yuv[8])) + { + PIXEL22_0 + PIXEL23_0 + PIXEL30_82 + PIXEL31_32 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL22_70 + PIXEL23_21 + PIXEL30_11 + PIXEL31_13 + PIXEL32_83 + PIXEL33_50 + } + break; + } +case 119: + { + if (Diff(yuv[2], yuv[6])) + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_0 + PIXEL03_0 + PIXEL12_0 + PIXEL13_0 + } + else + { + PIXEL00_12 + PIXEL01_14 + PIXEL02_83 + PIXEL03_50 + PIXEL12_70 + PIXEL13_21 + } + PIXEL10_81 + PIXEL11_31 + PIXEL20_82 + PIXEL21_32 + PIXEL22_30 + PIXEL23_10 + PIXEL30_82 + PIXEL31_32 + PIXEL32_10 + PIXEL33_80 + break; + } +case 237: +case 233: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_60 + PIXEL03_20 + PIXEL10_32 + PIXEL11_32 + PIXEL12_70 + PIXEL13_60 + PIXEL20_0 + PIXEL21_0 + PIXEL22_31 + PIXEL23_81 + if (Diff(yuv[8], yuv[4])) + { + PIXEL30_0 + } + else + { + PIXEL30_20 + } + PIXEL31_0 + PIXEL32_31 + PIXEL33_81 + break; + } +case 175: +case 47: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_0 + PIXEL02_32 + PIXEL03_82 + PIXEL10_0 + PIXEL11_0 + PIXEL12_32 + PIXEL13_82 + PIXEL20_31 + PIXEL21_31 + PIXEL22_70 + PIXEL23_60 + PIXEL30_81 + PIXEL31_81 + PIXEL32_60 + PIXEL33_20 + break; + } +case 183: +case 151: + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_0 + if (Diff(yuv[2], yuv[6])) + { + PIXEL03_0 + } + else + { + PIXEL03_20 + } + PIXEL10_81 + PIXEL11_31 + PIXEL12_0 + PIXEL13_0 + PIXEL20_60 + PIXEL21_70 + PIXEL22_32 + PIXEL23_32 + PIXEL30_20 + PIXEL31_60 + PIXEL32_82 + PIXEL33_82 + break; + } +case 245: +case 244: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_81 + PIXEL03_81 + PIXEL10_60 + PIXEL11_70 + PIXEL12_31 + PIXEL13_31 + PIXEL20_82 + PIXEL21_32 + PIXEL22_0 + PIXEL23_0 + PIXEL30_82 + PIXEL31_32 + PIXEL32_0 + if (Diff(yuv[6], yuv[8])) + { + PIXEL33_0 + } + else + { + PIXEL33_20 + } + break; + } +case 250: + { + PIXEL00_80 + PIXEL01_10 + PIXEL02_10 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + PIXEL22_0 + if (Diff(yuv[6], yuv[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + break; + } +case 123: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_10 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + break; + } +case 95: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL11_0 + PIXEL12_0 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_10 + PIXEL32_10 + PIXEL33_80 + break; + } +case 222: + { + PIXEL00_80 + PIXEL01_10 + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL10_10 + PIXEL11_30 + PIXEL12_0 + PIXEL20_10 + PIXEL21_30 + PIXEL22_0 + if (Diff(yuv[6], yuv[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + PIXEL30_80 + PIXEL31_10 + break; + } +case 252: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_81 + PIXEL03_81 + PIXEL10_10 + PIXEL11_30 + PIXEL12_31 + PIXEL13_31 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + PIXEL22_0 + PIXEL23_0 + PIXEL32_0 + if (Diff(yuv[6], yuv[8])) + { + PIXEL33_0 + } + else + { + PIXEL33_20 + } + break; + } +case 249: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_61 + PIXEL03_80 + PIXEL10_32 + PIXEL11_32 + PIXEL12_30 + PIXEL13_10 + PIXEL20_0 + PIXEL21_0 + PIXEL22_0 + if (Diff(yuv[6], yuv[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + if (Diff(yuv[8], yuv[4])) + { + PIXEL30_0 + } + else + { + PIXEL30_20 + } + PIXEL31_0 + break; + } +case 235: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_61 + PIXEL20_0 + PIXEL21_0 + PIXEL22_31 + PIXEL23_81 + if (Diff(yuv[8], yuv[4])) + { + PIXEL30_0 + } + else + { + PIXEL30_20 + } + PIXEL31_0 + PIXEL32_31 + PIXEL33_81 + break; + } +case 111: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_0 + PIXEL02_32 + PIXEL03_82 + PIXEL10_0 + PIXEL11_0 + PIXEL12_32 + PIXEL13_82 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + break; + } +case 63: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_0 + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL10_0 + PIXEL11_0 + PIXEL12_0 + PIXEL20_31 + PIXEL21_31 + PIXEL22_30 + PIXEL23_10 + PIXEL30_81 + PIXEL31_81 + PIXEL32_61 + PIXEL33_80 + break; + } +case 159: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + PIXEL02_0 + if (Diff(yuv[2], yuv[6])) + { + PIXEL03_0 + } + else + { + PIXEL03_20 + } + PIXEL11_0 + PIXEL12_0 + PIXEL13_0 + PIXEL20_10 + PIXEL21_30 + PIXEL22_32 + PIXEL23_32 + PIXEL30_80 + PIXEL31_61 + PIXEL32_82 + PIXEL33_82 + break; + } +case 215: + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_0 + if (Diff(yuv[2], yuv[6])) + { + PIXEL03_0 + } + else + { + PIXEL03_20 + } + PIXEL10_81 + PIXEL11_31 + PIXEL12_0 + PIXEL13_0 + PIXEL20_61 + PIXEL21_30 + PIXEL22_0 + if (Diff(yuv[6], yuv[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + PIXEL30_80 + PIXEL31_10 + break; + } +case 246: + { + PIXEL00_80 + PIXEL01_10 + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL10_61 + PIXEL11_30 + PIXEL12_0 + PIXEL20_82 + PIXEL21_32 + PIXEL22_0 + PIXEL23_0 + PIXEL30_82 + PIXEL31_32 + PIXEL32_0 + if (Diff(yuv[6], yuv[8])) + { + PIXEL33_0 + } + else + { + PIXEL33_20 + } + break; + } +case 254: + { + PIXEL00_80 + PIXEL01_10 + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL10_10 + PIXEL11_30 + PIXEL12_0 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + PIXEL22_0 + PIXEL23_0 + PIXEL32_0 + if (Diff(yuv[6], yuv[8])) + { + PIXEL33_0 + } + else + { + PIXEL33_20 + } + break; + } +case 253: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_81 + PIXEL03_81 + PIXEL10_32 + PIXEL11_32 + PIXEL12_31 + PIXEL13_31 + PIXEL20_0 + PIXEL21_0 + PIXEL22_0 + PIXEL23_0 + if (Diff(yuv[8], yuv[4])) + { + PIXEL30_0 + } + else + { + PIXEL30_20 + } + PIXEL31_0 + PIXEL32_0 + if (Diff(yuv[6], yuv[8])) + { + PIXEL33_0 + } + else + { + PIXEL33_20 + } + break; + } +case 251: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_10 + PIXEL20_0 + PIXEL21_0 + PIXEL22_0 + if (Diff(yuv[6], yuv[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + if (Diff(yuv[8], yuv[4])) + { + PIXEL30_0 + } + else + { + PIXEL30_20 + } + PIXEL31_0 + break; + } +case 239: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_0 + PIXEL02_32 + PIXEL03_82 + PIXEL10_0 + PIXEL11_0 + PIXEL12_32 + PIXEL13_82 + PIXEL20_0 + PIXEL21_0 + PIXEL22_31 + PIXEL23_81 + if (Diff(yuv[8], yuv[4])) + { + PIXEL30_0 + } + else + { + PIXEL30_20 + } + PIXEL31_0 + PIXEL32_31 + PIXEL33_81 + break; + } +case 127: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_0 + if (Diff(yuv[2], yuv[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL10_0 + PIXEL11_0 + PIXEL12_0 + if (Diff(yuv[8], yuv[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + break; + } +case 191: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_0 + PIXEL02_0 + if (Diff(yuv[2], yuv[6])) + { + PIXEL03_0 + } + else + { + PIXEL03_20 + } + PIXEL10_0 + PIXEL11_0 + PIXEL12_0 + PIXEL13_0 + PIXEL20_31 + PIXEL21_31 + PIXEL22_32 + PIXEL23_32 + PIXEL30_81 + PIXEL31_81 + PIXEL32_82 + PIXEL33_82 + break; + } +case 223: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + PIXEL02_0 + if (Diff(yuv[2], yuv[6])) + { + PIXEL03_0 + } + else + { + PIXEL03_20 + } + PIXEL11_0 + PIXEL12_0 + PIXEL13_0 + PIXEL20_10 + PIXEL21_30 + PIXEL22_0 + if (Diff(yuv[6], yuv[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + PIXEL30_80 + PIXEL31_10 + break; + } +case 247: + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_0 + if (Diff(yuv[2], yuv[6])) + { + PIXEL03_0 + } + else + { + PIXEL03_20 + } + PIXEL10_81 + PIXEL11_31 + PIXEL12_0 + PIXEL13_0 + PIXEL20_82 + PIXEL21_32 + PIXEL22_0 + PIXEL23_0 + PIXEL30_82 + PIXEL31_32 + PIXEL32_0 + if (Diff(yuv[6], yuv[8])) + { + PIXEL33_0 + } + else + { + PIXEL33_20 + } + break; + } +case 255: + { + if (Diff(yuv[4], yuv[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_0 + PIXEL02_0 + if (Diff(yuv[2], yuv[6])) + { + PIXEL03_0 + } + else + { + PIXEL03_20 + } + PIXEL10_0 + PIXEL11_0 + PIXEL12_0 + PIXEL13_0 + PIXEL20_0 + PIXEL21_0 + PIXEL22_0 + PIXEL23_0 + if (Diff(yuv[8], yuv[4])) + { + PIXEL30_0 + } + else + { + PIXEL30_20 + } + PIXEL31_0 + PIXEL32_0 + if (Diff(yuv[6], yuv[8])) + { + PIXEL33_0 + } + else + { + PIXEL33_20 + } + break; + } +} diff --git a/src/filters/hq/c/hq_base.h b/src/filters/hq/c/hq_base.h new file mode 100644 index 0000000..b63c614 --- /dev/null +++ b/src/filters/hq/c/hq_base.h @@ -0,0 +1,449 @@ +/* + VisualBoyAdvance - a Game Boy & Game Boy Advance emulator + + Copyright (C) 2008 VBA-M development team + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + hq filter by Maxim Stepin ( http://hiend3d.com ) +*/ + + +#ifdef _16BIT +#ifdef _32BIT +#error _16BIT and _32BIT defined at the same time! +#endif +#endif + + +#ifdef _16BIT +#define SIZE_PIXEL 2 // 16bit = 2 bytes +#define COLORTYPE unsigned short +#define RGBtoYUV RGBtoYUV_16 +#define Interp1 Interp1_16 +#define Interp2 Interp2_16 +#define Interp3 Interp3_16 +#define Interp4 Interp4_16 +#define Interp5 Interp5_16 +#define Interp6 Interp6_16 +#define Interp7 Interp7_16 +#define Interp8 Interp8_16 +#endif + + +#ifdef _32BIT +#define SIZE_PIXEL 4 // 32bit = 4 bytes +#define COLORTYPE unsigned int +#define RGBtoYUV RGBtoYUV_32 +#define Interp1 Interp1_32 +#define Interp2 Interp2_32 +#define Interp3 Interp3_32 +#define Interp4 Interp4_32 +#define Interp5 Interp5_32 +#define Interp6 Interp6_32 +#define Interp7 Interp7_32 +#define Interp8 Interp8_32 +#endif + + +#ifdef _HQ3X + +#define _MAGNIFICATION 3 + +#define PIXEL00_1M Interp1( pOut, c[5], c[1] ); +#define PIXEL00_1U Interp1( pOut, c[5], c[2] ); +#define PIXEL00_1L Interp1( pOut, c[5], c[4] ); +#define PIXEL00_2 Interp2( pOut, c[5], c[4], c[2] ); +#define PIXEL00_4 Interp4( pOut, c[5], c[4], c[2] ); +#define PIXEL00_5 Interp5( pOut, c[4], c[2] ); +#define PIXEL00_C *((COLORTYPE*)(pOut)) = c[5]; + +#define PIXEL01_1 Interp1( pOut+SIZE_PIXEL, c[5], c[2] ); +#define PIXEL01_3 Interp3( pOut+SIZE_PIXEL, c[5], c[2] ); +#define PIXEL01_6 Interp1( pOut+SIZE_PIXEL, c[2], c[5] ); +#define PIXEL01_C *((COLORTYPE*)(pOut+SIZE_PIXEL)) = c[5]; + +#define PIXEL02_1M Interp1( pOut+SIZE_PIXEL+SIZE_PIXEL, c[5], c[3] ); +#define PIXEL02_1U Interp1( pOut+SIZE_PIXEL+SIZE_PIXEL, c[5], c[2] ); +#define PIXEL02_1R Interp1( pOut+SIZE_PIXEL+SIZE_PIXEL, c[5], c[6] ); +#define PIXEL02_2 Interp2( pOut+SIZE_PIXEL+SIZE_PIXEL, c[5], c[2], c[6] ); +#define PIXEL02_4 Interp4( pOut+SIZE_PIXEL+SIZE_PIXEL, c[5], c[2], c[6] ); +#define PIXEL02_5 Interp5( pOut+SIZE_PIXEL+SIZE_PIXEL, c[2], c[6] ); +#define PIXEL02_C *((COLORTYPE*)(pOut+SIZE_PIXEL+SIZE_PIXEL)) = c[5]; + +#define PIXEL10_1 Interp1( pOut+dstPitch, c[5], c[4] ); +#define PIXEL10_3 Interp3( pOut+dstPitch, c[5], c[4] ); +#define PIXEL10_6 Interp1( pOut+dstPitch, c[4], c[5] ); +#define PIXEL10_C *((COLORTYPE*)(pOut+dstPitch)) = c[5]; + +#define PIXEL11 *((COLORTYPE*)(pOut+dstPitch+SIZE_PIXEL)) = c[5]; + +#define PIXEL12_1 Interp1( pOut+dstPitch+SIZE_PIXEL+SIZE_PIXEL, c[5], c[6] ); +#define PIXEL12_3 Interp3( pOut+dstPitch+SIZE_PIXEL+SIZE_PIXEL, c[5], c[6] ); +#define PIXEL12_6 Interp1( pOut+dstPitch+SIZE_PIXEL+SIZE_PIXEL, c[6], c[5] ); +#define PIXEL12_C *((COLORTYPE*)(pOut+dstPitch+SIZE_PIXEL+SIZE_PIXEL)) = c[5]; + +#define PIXEL20_1M Interp1( pOut+dstPitch+dstPitch, c[5], c[7] ); +#define PIXEL20_1D Interp1( pOut+dstPitch+dstPitch, c[5], c[8] ); +#define PIXEL20_1L Interp1( pOut+dstPitch+dstPitch, c[5], c[4] ); +#define PIXEL20_2 Interp2( pOut+dstPitch+dstPitch, c[5], c[8], c[4] ); +#define PIXEL20_4 Interp4( pOut+dstPitch+dstPitch, c[5], c[8], c[4] ); +#define PIXEL20_5 Interp5( pOut+dstPitch+dstPitch, c[8], c[4] ); +#define PIXEL20_C *((COLORTYPE*)(pOut+dstPitch+dstPitch)) = c[5]; + +#define PIXEL21_1 Interp1( pOut+dstPitch+dstPitch+SIZE_PIXEL, c[5], c[8] ); +#define PIXEL21_3 Interp3( pOut+dstPitch+dstPitch+SIZE_PIXEL, c[5], c[8] ); +#define PIXEL21_6 Interp1( pOut+dstPitch+dstPitch+SIZE_PIXEL, c[8], c[5] ); +#define PIXEL21_C *((COLORTYPE*)(pOut+dstPitch+dstPitch+SIZE_PIXEL)) = c[5]; + +#define PIXEL22_1M Interp1( pOut+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL, c[5], c[9] ); +#define PIXEL22_1D Interp1( pOut+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL, c[5], c[8] ); +#define PIXEL22_1R Interp1( pOut+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL, c[5], c[6] ); +#define PIXEL22_2 Interp2( pOut+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL, c[5], c[6], c[8] ); +#define PIXEL22_4 Interp4( pOut+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL, c[5], c[6], c[8] ); +#define PIXEL22_5 Interp5( pOut+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL, c[6], c[8] ); +#define PIXEL22_C *((COLORTYPE*)(pOut+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL)) = c[5]; + +#endif // #ifdef _HQ3X + + + +#ifdef _HQ4X + +#define _MAGNIFICATION 4 + +#define PIXEL00_0 *((COLORTYPE*)(pOut)) = c[5]; +#define PIXEL00_11 Interp1(pOut, c[5], c[4]); +#define PIXEL00_12 Interp1(pOut, c[5], c[2]); +#define PIXEL00_20 Interp2(pOut, c[5], c[2], c[4]); +#define PIXEL00_50 Interp5(pOut, c[2], c[4]); +#define PIXEL00_80 Interp8(pOut, c[5], c[1]); +#define PIXEL00_81 Interp8(pOut, c[5], c[4]); +#define PIXEL00_82 Interp8(pOut, c[5], c[2]); + +#define PIXEL01_0 *((COLORTYPE*)(pOut+SIZE_PIXEL)) = c[5]; +#define PIXEL01_10 Interp1(pOut+SIZE_PIXEL, c[5], c[1]); +#define PIXEL01_12 Interp1(pOut+SIZE_PIXEL, c[5], c[2]); +#define PIXEL01_14 Interp1(pOut+SIZE_PIXEL, c[2], c[5]); +#define PIXEL01_21 Interp2(pOut+SIZE_PIXEL, c[2], c[5], c[4]); +#define PIXEL01_31 Interp3(pOut+SIZE_PIXEL, c[5], c[4]); +#define PIXEL01_50 Interp5(pOut+SIZE_PIXEL, c[2], c[5]); +#define PIXEL01_60 Interp6(pOut+SIZE_PIXEL, c[5], c[2], c[4]); +#define PIXEL01_61 Interp6(pOut+SIZE_PIXEL, c[5], c[2], c[1]); +#define PIXEL01_82 Interp8(pOut+SIZE_PIXEL, c[5], c[2]); +#define PIXEL01_83 Interp8(pOut+SIZE_PIXEL, c[2], c[4]); + +#define PIXEL02_0 *((COLORTYPE*)(pOut+SIZE_PIXEL+SIZE_PIXEL)) = c[5]; +#define PIXEL02_10 Interp1(pOut+SIZE_PIXEL+SIZE_PIXEL, c[5], c[3]); +#define PIXEL02_11 Interp1(pOut+SIZE_PIXEL+SIZE_PIXEL, c[5], c[2]); +#define PIXEL02_13 Interp1(pOut+SIZE_PIXEL+SIZE_PIXEL, c[2], c[5]); +#define PIXEL02_21 Interp2(pOut+SIZE_PIXEL+SIZE_PIXEL, c[2], c[5], c[6]); +#define PIXEL02_32 Interp3(pOut+SIZE_PIXEL+SIZE_PIXEL, c[5], c[6]); +#define PIXEL02_50 Interp5(pOut+SIZE_PIXEL+SIZE_PIXEL, c[2], c[5]); +#define PIXEL02_60 Interp6(pOut+SIZE_PIXEL+SIZE_PIXEL, c[5], c[2], c[6]); +#define PIXEL02_61 Interp6(pOut+SIZE_PIXEL+SIZE_PIXEL, c[5], c[2], c[3]); +#define PIXEL02_81 Interp8(pOut+SIZE_PIXEL+SIZE_PIXEL, c[5], c[2]); +#define PIXEL02_83 Interp8(pOut+SIZE_PIXEL+SIZE_PIXEL, c[2], c[6]); + +#define PIXEL03_0 *((COLORTYPE*)(pOut+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL)) = c[5]; +#define PIXEL03_11 Interp1(pOut+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[5], c[2]); +#define PIXEL03_12 Interp1(pOut+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[5], c[6]); +#define PIXEL03_20 Interp2(pOut+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[5], c[2], c[6]); +#define PIXEL03_50 Interp5(pOut+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[2], c[6]); +#define PIXEL03_80 Interp8(pOut+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[5], c[3]); +#define PIXEL03_81 Interp8(pOut+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[5], c[2]); +#define PIXEL03_82 Interp8(pOut+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[5], c[6]); + +#define PIXEL10_0 *((COLORTYPE*)(pOut+dstPitch)) = c[5]; +#define PIXEL10_10 Interp1(pOut+dstPitch, c[5], c[1]); +#define PIXEL10_11 Interp1(pOut+dstPitch, c[5], c[4]); +#define PIXEL10_13 Interp1(pOut+dstPitch, c[4], c[5]); +#define PIXEL10_21 Interp2(pOut+dstPitch, c[4], c[5], c[2]); +#define PIXEL10_32 Interp3(pOut+dstPitch, c[5], c[2]); +#define PIXEL10_50 Interp5(pOut+dstPitch, c[4], c[5]); +#define PIXEL10_60 Interp6(pOut+dstPitch, c[5], c[4], c[2]); +#define PIXEL10_61 Interp6(pOut+dstPitch, c[5], c[4], c[1]); +#define PIXEL10_81 Interp8(pOut+dstPitch, c[5], c[4]); +#define PIXEL10_83 Interp8(pOut+dstPitch, c[4], c[2]); + +#define PIXEL11_0 *((COLORTYPE*)(pOut+dstPitch+SIZE_PIXEL)) = c[5]; +#define PIXEL11_30 Interp3(pOut+dstPitch+SIZE_PIXEL, c[5], c[1]); +#define PIXEL11_31 Interp3(pOut+dstPitch+SIZE_PIXEL, c[5], c[4]); +#define PIXEL11_32 Interp3(pOut+dstPitch+SIZE_PIXEL, c[5], c[2]); +#define PIXEL11_70 Interp7(pOut+dstPitch+SIZE_PIXEL, c[5], c[4], c[2]); + +#define PIXEL12_0 *((COLORTYPE*)(pOut+dstPitch+SIZE_PIXEL+SIZE_PIXEL)) = c[5]; +#define PIXEL12_30 Interp3(pOut+dstPitch+SIZE_PIXEL+SIZE_PIXEL, c[5], c[3]); +#define PIXEL12_31 Interp3(pOut+dstPitch+SIZE_PIXEL+SIZE_PIXEL, c[5], c[2]); +#define PIXEL12_32 Interp3(pOut+dstPitch+SIZE_PIXEL+SIZE_PIXEL, c[5], c[6]); +#define PIXEL12_70 Interp7(pOut+dstPitch+SIZE_PIXEL+SIZE_PIXEL, c[5], c[6], c[2]); + +#define PIXEL13_0 *((COLORTYPE*)(pOut+dstPitch+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL)) = c[5]; +#define PIXEL13_10 Interp1(pOut+dstPitch+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[5], c[3]); +#define PIXEL13_12 Interp1(pOut+dstPitch+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[5], c[6]); +#define PIXEL13_14 Interp1(pOut+dstPitch+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[6], c[5]); +#define PIXEL13_21 Interp2(pOut+dstPitch+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[6], c[5], c[2]); +#define PIXEL13_31 Interp3(pOut+dstPitch+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[5], c[2]); +#define PIXEL13_50 Interp5(pOut+dstPitch+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[6], c[5]); +#define PIXEL13_60 Interp6(pOut+dstPitch+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[5], c[6], c[2]); +#define PIXEL13_61 Interp6(pOut+dstPitch+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[5], c[6], c[3]); +#define PIXEL13_82 Interp8(pOut+dstPitch+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[5], c[6]); +#define PIXEL13_83 Interp8(pOut+dstPitch+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[6], c[2]); + +#define PIXEL20_0 *((COLORTYPE*)(pOut+dstPitch+dstPitch)) = c[5]; +#define PIXEL20_10 Interp1(pOut+dstPitch+dstPitch, c[5], c[7]); +#define PIXEL20_12 Interp1(pOut+dstPitch+dstPitch, c[5], c[4]); +#define PIXEL20_14 Interp1(pOut+dstPitch+dstPitch, c[4], c[5]); +#define PIXEL20_21 Interp2(pOut+dstPitch+dstPitch, c[4], c[5], c[8]); +#define PIXEL20_31 Interp3(pOut+dstPitch+dstPitch, c[5], c[8]); +#define PIXEL20_50 Interp5(pOut+dstPitch+dstPitch, c[4], c[5]); +#define PIXEL20_60 Interp6(pOut+dstPitch+dstPitch, c[5], c[4], c[8]); +#define PIXEL20_61 Interp6(pOut+dstPitch+dstPitch, c[5], c[4], c[7]); +#define PIXEL20_82 Interp8(pOut+dstPitch+dstPitch, c[5], c[4]); +#define PIXEL20_83 Interp8(pOut+dstPitch+dstPitch, c[4], c[8]); + +#define PIXEL21_0 *((COLORTYPE*)(pOut+dstPitch+dstPitch+SIZE_PIXEL)) = c[5]; +#define PIXEL21_30 Interp3(pOut+dstPitch+dstPitch+SIZE_PIXEL, c[5], c[7]); +#define PIXEL21_31 Interp3(pOut+dstPitch+dstPitch+SIZE_PIXEL, c[5], c[8]); +#define PIXEL21_32 Interp3(pOut+dstPitch+dstPitch+SIZE_PIXEL, c[5], c[4]); +#define PIXEL21_70 Interp7(pOut+dstPitch+dstPitch+SIZE_PIXEL, c[5], c[4], c[8]); +#define PIXEL22_0 *((COLORTYPE*)(pOut+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL)) = c[5]; +#define PIXEL22_30 Interp3(pOut+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL, c[5], c[9]); +#define PIXEL22_31 Interp3(pOut+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL, c[5], c[6]); +#define PIXEL22_32 Interp3(pOut+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL, c[5], c[8]); +#define PIXEL22_70 Interp7(pOut+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL, c[5], c[6], c[8]); + +#define PIXEL23_0 *((COLORTYPE*)(pOut+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL)) = c[5]; +#define PIXEL23_10 Interp1(pOut+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[5], c[9]); +#define PIXEL23_11 Interp1(pOut+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[5], c[6]); +#define PIXEL23_13 Interp1(pOut+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[6], c[5]); +#define PIXEL23_21 Interp2(pOut+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[6], c[5], c[8]); +#define PIXEL23_32 Interp3(pOut+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[5], c[8]); +#define PIXEL23_50 Interp5(pOut+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[6], c[5]); +#define PIXEL23_60 Interp6(pOut+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[5], c[6], c[8]); +#define PIXEL23_61 Interp6(pOut+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[5], c[6], c[9]); +#define PIXEL23_81 Interp8(pOut+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[5], c[6]); +#define PIXEL23_83 Interp8(pOut+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[6], c[8]); + +#define PIXEL30_0 *((COLORTYPE*)(pOut+dstPitch+dstPitch+dstPitch)) = c[5]; +#define PIXEL30_11 Interp1(pOut+dstPitch+dstPitch+dstPitch, c[5], c[8]); +#define PIXEL30_12 Interp1(pOut+dstPitch+dstPitch+dstPitch, c[5], c[4]); +#define PIXEL30_20 Interp2(pOut+dstPitch+dstPitch+dstPitch, c[5], c[8], c[4]); +#define PIXEL30_50 Interp5(pOut+dstPitch+dstPitch+dstPitch, c[8], c[4]); +#define PIXEL30_80 Interp8(pOut+dstPitch+dstPitch+dstPitch, c[5], c[7]); +#define PIXEL30_81 Interp8(pOut+dstPitch+dstPitch+dstPitch, c[5], c[8]); +#define PIXEL30_82 Interp8(pOut+dstPitch+dstPitch+dstPitch, c[5], c[4]); + +#define PIXEL31_0 *((COLORTYPE*)(pOut+dstPitch+dstPitch+dstPitch+SIZE_PIXEL)) = c[5]; +#define PIXEL31_10 Interp1(pOut+dstPitch+dstPitch+dstPitch+SIZE_PIXEL, c[5], c[7]); +#define PIXEL31_11 Interp1(pOut+dstPitch+dstPitch+dstPitch+SIZE_PIXEL, c[5], c[8]); +#define PIXEL31_13 Interp1(pOut+dstPitch+dstPitch+dstPitch+SIZE_PIXEL, c[8], c[5]); +#define PIXEL31_21 Interp2(pOut+dstPitch+dstPitch+dstPitch+SIZE_PIXEL, c[8], c[5], c[4]); +#define PIXEL31_32 Interp3(pOut+dstPitch+dstPitch+dstPitch+SIZE_PIXEL, c[5], c[4]); +#define PIXEL31_50 Interp5(pOut+dstPitch+dstPitch+dstPitch+SIZE_PIXEL, c[8], c[5]); +#define PIXEL31_60 Interp6(pOut+dstPitch+dstPitch+dstPitch+SIZE_PIXEL, c[5], c[8], c[4]); +#define PIXEL31_61 Interp6(pOut+dstPitch+dstPitch+dstPitch+SIZE_PIXEL, c[5], c[8], c[7]); +#define PIXEL31_81 Interp8(pOut+dstPitch+dstPitch+dstPitch+SIZE_PIXEL, c[5], c[8]); +#define PIXEL31_83 Interp8(pOut+dstPitch+dstPitch+dstPitch+SIZE_PIXEL, c[8], c[4]); + +#define PIXEL32_0 *((COLORTYPE*)(pOut+dstPitch+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL)) = c[5]; +#define PIXEL32_10 Interp1(pOut+dstPitch+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL, c[5], c[9]); +#define PIXEL32_12 Interp1(pOut+dstPitch+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL, c[5], c[8]); +#define PIXEL32_14 Interp1(pOut+dstPitch+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL, c[8], c[5]); +#define PIXEL32_21 Interp2(pOut+dstPitch+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL, c[8], c[5], c[6]); +#define PIXEL32_31 Interp3(pOut+dstPitch+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL, c[5], c[6]); +#define PIXEL32_50 Interp5(pOut+dstPitch+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL, c[8], c[5]); +#define PIXEL32_60 Interp6(pOut+dstPitch+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL, c[5], c[8], c[6]); +#define PIXEL32_61 Interp6(pOut+dstPitch+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL, c[5], c[8], c[9]); +#define PIXEL32_82 Interp8(pOut+dstPitch+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL, c[5], c[8]); +#define PIXEL32_83 Interp8(pOut+dstPitch+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL, c[8], c[6]); + +#define PIXEL33_0 *((COLORTYPE*)(pOut+dstPitch+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL)) = c[5]; +#define PIXEL33_11 Interp1(pOut+dstPitch+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[5], c[6]); +#define PIXEL33_12 Interp1(pOut+dstPitch+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[5], c[8]); +#define PIXEL33_20 Interp2(pOut+dstPitch+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[5], c[8], c[6]); +#define PIXEL33_50 Interp5(pOut+dstPitch+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[8], c[6]); +#define PIXEL33_80 Interp8(pOut+dstPitch+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[5], c[9]); +#define PIXEL33_81 Interp8(pOut+dstPitch+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[5], c[6]); +#define PIXEL33_82 Interp8(pOut+dstPitch+dstPitch+dstPitch+SIZE_PIXEL+SIZE_PIXEL+SIZE_PIXEL, c[5], c[8]); + +#endif // #ifdef _HQ4X + + + +// function header +#ifdef _16BIT + #ifdef _HQ3X + void hq3x16( + #endif + #ifdef _HQ4X + void hq4x16( + #endif +#endif + +#ifdef _32BIT + #ifdef _HQ3X + void hq3x32( + #endif + #ifdef _HQ4X + void hq4x32( + #endif +#endif + +unsigned char *pIn, unsigned int srcPitch, +unsigned char *, +unsigned char *pOut, unsigned int dstPitch, +int Xres, int Yres ) +{ + unsigned int yuv[10] = {0}; // yuv[0] not used + // yuv[1-9] allows reusage of calculated YUV values + int x, y; + unsigned int linePlus, lineMinus; + + COLORTYPE c[10]; // c[0] not used + // +----+----+----+ + // | | | | + // | c1 | c2 | c3 | + // +----+----+----+ + // | | | | + // | c4 | c5 | c6 | + // +----+----+----+ + // | | | | + // | c7 | c8 | c9 | + // +----+----+----+ + + for (y=0; y0) + { + // upper border possible: + c[1] = *((COLORTYPE*)(pIn - lineMinus - SIZE_PIXEL)); + + c[4] = *((COLORTYPE*)(pIn - SIZE_PIXEL)); + + // lower border possible: + c[7] = *((COLORTYPE*)(pIn + linePlus - SIZE_PIXEL)); + } + else + { // left border + c[1] = c[2]; + c[4] = c[5]; + c[7] = c[8]; + } + + if (x 0x00300000 ) || + ( abs_32((yuv[5] & 0x0000FF00) - (yuv[k] & 0x0000FF00)) > 0x00000700 ) || + ( abs_32((yuv[5] & 0x000000FF) - (yuv[k] & 0x000000FF)) > 0x00000006 ) + ) { + pattern |= flag; + } + } + flag <<= 1; + } + +#ifdef _HQ3X +#include "hq3x_pattern.h" +#endif + +#ifdef _HQ4X +#include "hq4x_pattern.h" +#endif + + pIn += SIZE_PIXEL; + pOut += _MAGNIFICATION * SIZE_PIXEL; + } + pIn += srcPitch - ( Xres * SIZE_PIXEL ); + pOut += dstPitch - ( _MAGNIFICATION * Xres * SIZE_PIXEL ); + pOut += ( _MAGNIFICATION - 1 ) * dstPitch; + } +} + +#ifdef _32BIT + #ifdef _HQ3X +void hq3x32_32(unsigned char *pIn, unsigned int srcPitch, unsigned char *, unsigned char *pOut, unsigned int dstPitch, int Xres, int Yres) +{ + hq3x32(pIn, srcPitch, 0, pOut, dstPitch, Xres, Yres); +} + #endif + #ifdef _HQ4X +void hq4x32_32(unsigned char *pIn, unsigned int srcPitch, unsigned char *, unsigned char *pOut, unsigned int dstPitch, int Xres, int Yres) +{ + hq4x32(pIn, srcPitch, 0, pOut, dstPitch, Xres, Yres); +} + #endif +#endif + +#undef SIZE_PIXEL +#undef COLORTYPE +#undef _MAGNIFICATION +#undef RGBtoYUV +#undef Interp1 +#undef Interp2 +#undef Interp3 +#undef Interp4 +#undef Interp5 +#undef Interp6 +#undef Interp7 +#undef Interp8 diff --git a/src/filters/hq/c/hq_implementation.cpp b/src/filters/hq/c/hq_implementation.cpp new file mode 100644 index 0000000..0b72064 --- /dev/null +++ b/src/filters/hq/c/hq_implementation.cpp @@ -0,0 +1,64 @@ +/* + VisualBoyAdvance - a Game Boy & Game Boy Advance emulator + + Copyright (C) 2008 VBA-M development team + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + hq filter by Maxim Stepin ( http://hiend3d.com ) +*/ + + +#include "hq_shared.h" + + +#define _16BIT + #define _HQ3X + // hq3x, 16bit + #include "hq_base.h" + #undef _HQ3X + + #define _HQ4X + // hq4x, 16bit + #include "hq_base.h" + #undef _HQ4X +#undef _16BIT + + +#define _32BIT + #define _HQ3X + // hq3x, 32bit + #include "hq_base.h" + #undef _HQ3X + + #define _HQ4X + // hq4x, 32bit + #include "hq_base.h" + #undef _HQ4X +#undef _32BIT + + + +#undef GMASK +#undef RBMASK +#undef GSHIFT1MASK +#undef RBSHIFT1MASK +#undef GSHIFT2MASK +#undef RBSHIFT2MASK +#undef GSHIFT3MASK +#undef RBSHIFT3MASK +#undef GSHIFT4MASK +#undef RBSHIFT4MASK diff --git a/src/filters/hq/c/hq_shared.h b/src/filters/hq/c/hq_shared.h new file mode 100644 index 0000000..850a56c --- /dev/null +++ b/src/filters/hq/c/hq_shared.h @@ -0,0 +1,445 @@ +/* + VisualBoyAdvance - a Game Boy & Game Boy Advance emulator + + Copyright (C) 2008 VBA-M development team + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + hq filter by Maxim Stepin ( http://hiend3d.com ) +*/ + +#ifdef RGB555 +// 5 bits for green +#define GMASK 0x03E0 +#define RBMASK 0x7C1F +// MASK << 1 +#define GSHIFT1MASK 0x000007C0 +#define RBSHIFT1MASK 0x0000F83E +// MASK << 2 +#define GSHIFT2MASK 0x00000F80 +#define RBSHIFT2MASK 0x0001F07C +// MASK << 3 +#define GSHIFT3MASK 0x00001F00 +#define RBSHIFT3MASK 0x0003E0F8 +// MASK << 4 +#define GSHIFT4MASK 0x00003E00 +#define RBSHIFT4MASK 0x0007C1F0 +#else +// RGB565 +// 6 bits for green +#define GMASK 0x07E0 +#define RBMASK 0xF81F +#define GSHIFT1MASK 0x00000FC0 +#define RBSHIFT1MASK 0x0001F03E +#define GSHIFT2MASK 0x00001F80 +#define RBSHIFT2MASK 0x0003E07C +#define GSHIFT3MASK 0x00003F00 +#define RBSHIFT3MASK 0x0007C0F8 +#define GSHIFT4MASK 0x00007E00 +#define RBSHIFT4MASK 0x000F81F0 +#endif + + +// we only need the 32bit version because our YUV format has 32bits +#define abs_32( value ) ( ( value ) & 0x7FFFFFFF ) + + +inline bool Diff( unsigned int YUV1, unsigned int YUV2 ) +{ + if( YUV1 == YUV2 ) return false; // Save some processing power + + return + ( abs_32((YUV1 & 0x00FF0000) - (YUV2 & 0x00FF0000)) > 0x00300000 ) || + ( abs_32((YUV1 & 0x0000FF00) - (YUV2 & 0x0000FF00)) > 0x00000700 ) || + ( abs_32((YUV1 & 0x000000FF) - (YUV2 & 0x000000FF)) > 0x00000006 ); +} + + + +// =============== +// 32bit routines: +// =============== + +// ( c1*3 + c2 ) / 4 +// hq3x, hq4x +#define Interp1_32( pc, c1, c2 ) \ +( \ + *( (unsigned int *)(pc) ) = \ + ( (c1) == (c2) ) ? c1 : \ + ( \ + ( ( \ + ( ( (c1) & 0x00FF00 ) * 3 ) + \ + ( (c2) & 0x00FF00 ) \ + ) & 0x0003FC00 ) \ + + \ + ( ( \ + ( ( (c1) & 0xFF00FF ) * 3 ) + \ + ( (c2) & 0xFF00FF ) \ + ) & 0x03FC03FC ) \ + ) >> 2 \ +) + + +// ( c1*2 + c2 + c3 ) / 4 +// hq3x, hq4x +#define Interp2_32( pc, c1, c2, c3 ) \ +( \ + *( (unsigned int *)(pc) ) = \ + ( ( (c1) == (c2) ) == (c3) ) ? c1 : \ + ( \ + ( ( \ + ( ( (c1) & 0x00FF00 ) * 2 ) + \ + ( (c2) & 0x00FF00 ) + \ + ( (c3) & 0x00FF00 ) \ + ) & 0x0003FC00 ) \ + + \ + ( ( \ + ( ( (c1) & 0xFF00FF ) * 2 ) + \ + ( (c2) & 0xFF00FF ) + \ + ( (c3) & 0xFF00FF ) \ + ) & 0x03FC03FC ) \ + ) >> 2 \ +) + + +// ( c1*7 + c2 ) / 8 +// hq3x, hq4x +#define Interp3_32( pc, c1, c2 ) \ +( \ + *( (unsigned int *)(pc) ) = \ + ( (c1) == (c2) ) ? c1 : \ + ( \ + ( ( \ + ( ( (c1) & 0x00FF00 ) * 7 ) + \ + ( (c2) & 0x00FF00 ) \ + ) & 0x0007F800 ) \ + + \ + ( ( \ + ( ( (c1) & 0xFF00FF ) * 7 ) + \ + ( (c2) & 0xFF00FF ) \ + ) & 0x07F807F8 ) \ + ) >> 3 \ +) + + +// ( c1*2 + (c2+c3)*7 ) / 16 +// hq3x, not used by hq4x +#define Interp4_32( pc, c1, c2, c3 ) \ +( \ + *( (unsigned int *)(pc) ) = \ + ( ( (c1) == (c2) ) == (c3) ) ? c1 : \ + ( \ + ( ( ( ( (c1) & 0x00FF00 ) * 2 ) + ( ( ( (c2) & 0x00FF00 ) + ( (c3) & 0x00FF00 ) ) * 7 ) ) & 0x000FF000 ) + \ + ( ( ( ( (c1) & 0xFF00FF ) * 2 ) + ( ( ( (c2) & 0xFF00FF ) + ( (c3) & 0xFF00FF ) ) * 7 ) ) & 0x0FF00FF0 ) \ + ) >> 4 \ +) + + +// ( c1 + c2 ) / 2 +// hq3x, hq4x +#define Interp5_32( pc, c1, c2 ) \ +( \ + *( (unsigned int *)(pc) ) = \ + ( (c1) == (c2) ) ? c1 : \ + ( \ + ( ( \ + ( (c1) & 0x00FF00 ) + \ + ( (c2) & 0x00FF00 ) \ + ) & 0x0001FE00 ) \ + + \ + ( ( \ + ( (c1) & 0xFF00FF ) + \ + ( (c2) & 0xFF00FF ) \ + ) & 0x01FE01FE ) \ + ) >> 1 \ +) + + +// ( c1*5 + c2*2 + c3 ) / 8 +// hq4x +#define Interp6_32( pc, c1, c2, c3 ) \ +( \ + *( (unsigned int *)(pc) ) = \ + ( ( (c1) == (c2) ) == (c3) ) ? c1 : \ + ( \ + ( ( \ + ( ( (c1) & 0x00FF00 ) * 5 ) + \ + ( ( (c2) & 0x00FF00 ) * 2 ) + \ + ( (c3) & 0x00FF00 ) \ + ) & 0x0007F800 ) \ + + \ + ( ( \ + ( ( (c1) & 0xFF00FF ) * 5 ) + \ + ( ( (c2) & 0xFF00FF ) * 2 ) + \ + ( (c3) & 0xFF00FF ) \ + ) & 0x07F807F8 ) \ + ) >> 3 \ +) + + +// ( c1*6 + c2 + c3 ) / 8 +// hq4x +#define Interp7_32( pc, c1, c2, c3 ) \ +( \ + *( (unsigned int *)(pc) ) = \ + ( ( (c1) == (c2) ) == (c3) ) ? c1 : \ + ( \ + ( ( \ + ( ( (c1) & 0x00FF00 ) * 6 ) + \ + ( (c2) & 0x00FF00 ) + \ + ( (c3) & 0x00FF00 ) \ + ) & 0x0007F800 ) \ + + \ + ( ( \ + ( ( (c1) & 0xFF00FF ) * 6 ) + \ + ( (c2) & 0xFF00FF ) + \ + ( (c3) & 0xFF00FF ) \ + ) & 0x07F807F8 ) \ + ) >> 3 \ +) + + +// ( c1*5 + c2*3 ) / 8 +// hq4x +#define Interp8_32( pc, c1, c2 ) \ +( \ + *( (unsigned int *)(pc) ) = \ + ( (c1) == (c2) ) ? c1 : \ + ( \ + ( ( \ + ( ( (c1) & 0x00FF00 ) * 5 ) + \ + ( ( (c2) & 0x00FF00 ) * 3 ) \ + ) & 0x0007F800 ) \ + + \ + ( ( \ + ( ( (c1) & 0xFF00FF ) * 5 ) + \ + ( ( (c2) & 0xFF00FF ) * 3 ) \ + ) & 0x07F807F8 ) \ + ) >> 3 \ +) + + +// 32 bit input color +// 0x00YYUUVV return value +inline unsigned int RGBtoYUV_32( unsigned int c ) +{ + // Division through 3 slows down the emulation about 10% !!! + + register unsigned char r, g, b; + b = c & 0x0000FF; + g = ( c & 0x00FF00 ) >> 8; + r = c >> 16; + return ( (r + g + b) << 14 ) + + ( ( r - b + 512 ) << 4 ) + + ( ( 2*g - r - b ) >> 3 ) + 128; + + // unoptimized: + //unsigned char r, g, b, Y, u, v; + //b = (c & 0x000000FF); + //g = (c & 0x0000FF00) >> 8; + //r = (c & 0x00FF0000) >> 16; + //Y = (r + g + b) >> 2; + //u = 128 + ((r - b) >> 2); + //v = 128 + ((-r + 2*g -b)>>3); + //return (Y<<16) + (u<<8) + v; +} + + + +// =============== +// 16bit routines: +// =============== + +// ( c1*3 + c2 ) / 4 +// hq3x, hq4x +#define Interp1_16( pc, c1, c2 ) \ +( \ + *( (unsigned short *)(pc) ) = \ + ( (c1) == (c2) ) ? c1 : \ + ( \ + ( ( \ + ( ( (c1) & GMASK ) * 3 ) + \ + ( (c2) & GMASK ) \ + ) & GSHIFT2MASK ) \ + + \ + ( ( \ + ( ( (c1) & RBMASK ) * 3 ) + \ + ( (c2) & RBMASK ) \ + ) & RBSHIFT2MASK ) \ + ) >> 2 \ +) + + +// ( c1*2 + c2 + c3 ) / 4 +// hq3x, hq4x +#define Interp2_16( pc, c1, c2, c3 ) \ +( \ + *( (unsigned short *)(pc) ) = \ + ( ( (c1) == (c2) ) == (c3) ) ? c1 : \ + ( \ + ( ( \ + ( ( (c1) & GMASK ) * 2 ) + \ + ( (c2) & GMASK ) + \ + ( (c3) & GMASK ) \ + ) & GSHIFT2MASK ) \ + + \ + ( ( \ + ( ( (c1) & RBMASK ) * 2 ) + \ + ( (c2) & RBMASK ) + \ + ( (c3) & RBMASK ) \ + ) & RBSHIFT2MASK ) \ + ) >> 2 \ +) + + +// ( c1*7 + c2 ) / 8 +// hq3x, hq4x +#define Interp3_16( pc, c1, c2 ) \ +( \ + *( (unsigned short *)(pc) ) = \ + ( (c1) == (c2) ) ? c1 : \ + ( \ + ( ( \ + ( ( (c1) & GMASK ) * 7 ) + \ + ( (c2) & GMASK ) \ + ) & GSHIFT3MASK ) \ + + \ + ( ( \ + ( ( (c1) & RBMASK ) * 7 ) + \ + ( (c2) & RBMASK ) \ + ) & RBSHIFT3MASK ) \ + ) >> 3 \ +) + + +// ( c1*2 + (c2+c3)*7 ) / 16 +// hq3x, not used by hq4x +#define Interp4_16( pc, c1, c2, c3 ) \ +( \ + *( (unsigned short *)(pc) ) = \ + ( ( (c1) == (c2) ) == (c3) ) ? c1 : \ + ( \ + ( ( ( ( (c1) & GMASK ) * 2 ) + ( ( ( (c2) & GMASK ) + ( (c3) & GMASK ) ) * 7 ) ) & GSHIFT4MASK ) + \ + ( ( ( ( (c1) & RBMASK ) * 2 ) + ( ( ( (c2) & RBMASK ) + ( (c3) & RBMASK ) ) * 7 ) ) & RBSHIFT4MASK ) \ + ) >> 4 \ +) + + +// ( c1 + c2 ) / 2 +// hq3x, hq4x +#define Interp5_16( pc, c1, c2 ) \ +( \ + *( (unsigned short *)(pc) ) = \ + ( (c1) == (c2) ) ? c1 : \ + ( \ + ( ( \ + ( (c1) & GMASK ) + \ + ( (c2) & GMASK ) \ + ) & GSHIFT1MASK ) \ + + \ + ( ( \ + ( (c1) & RBMASK ) + \ + ( (c2) & RBMASK ) \ + ) & RBSHIFT1MASK ) \ + ) >> 1 \ +) + + +// ( c1*5 + c2*2 + c3 ) / 8 +// hq4x +#define Interp6_16( pc, c1, c2, c3 ) \ +( \ + *( (unsigned short *)(pc) ) = \ + ( ( (c1) == (c2) ) == (c3) ) ? c1 : \ + ( \ + ( ( \ + ( ( (c1) & GMASK ) * 5 ) + \ + ( ( (c2) & GMASK ) * 2 ) + \ + ( (c3) & GMASK ) \ + ) & GSHIFT3MASK ) \ + + \ + ( ( \ + ( ( (c1) & RBMASK ) * 5 ) + \ + ( ( (c2) & RBMASK ) * 2 ) + \ + ( (c3) & RBMASK ) \ + ) & RBSHIFT3MASK ) \ + ) >> 3 \ +) + + +// ( c1*6 + c2 + c3 ) / 8 +// hq4x +#define Interp7_16( pc, c1, c2, c3 ) \ +( \ + *( (unsigned short *)(pc) ) = \ + ( ( (c1) == (c2) ) == (c3) ) ? c1 : \ + ( \ + ( ( \ + ( ( (c1) & GMASK ) * 6 ) + \ + ( (c2) & GMASK ) + \ + ( (c3) & GMASK ) \ + ) & GSHIFT3MASK ) \ + + \ + ( ( \ + ( ( (c1) & RBMASK ) * 6 ) + \ + ( (c2) & RBMASK ) + \ + ( (c3) & RBMASK ) \ + ) & RBSHIFT3MASK ) \ + ) >> 3 \ +) + + +// ( c1*5 + c2*3 ) / 8 +// hq4x +#define Interp8_16( pc, c1, c2 ) \ +( \ + *( (unsigned short *)(pc) ) = \ + ( (c1) == (c2) ) ? c1 : \ + ( \ + ( ( \ + ( ( (c1) & GMASK ) * 5 ) + \ + ( ( (c2) & GMASK ) * 3 ) \ + ) & GSHIFT3MASK ) \ + + \ + ( ( \ + ( ( (c1) & RBMASK ) * 5 ) + \ + ( ( (c2) & RBMASK ) * 3 ) \ + ) & RBSHIFT3MASK ) \ + ) >> 3 \ +) + + +// 16 bit input color +// 0x00YYUUVV return value +inline unsigned int RGBtoYUV_16( unsigned short c ) +{ + // Division through 3 slows down the emulation about 10% !!! + + register unsigned char r, g, b; +#ifdef RGB555 + r = ( c & 0x7C00 ) >> 7; + g = ( c & 0x03E0 ) >> 2; + b = ( c & 0x001F ) << 3; +#else + r = ( c & 0xF800 ) >> 8; + g = ( c & 0x07E0 ) >> 3; + b = ( c & 0x001F ) << 3; +#endif + + return ( (r + g + b) << 14 ) + + ( ( r - b + 512 ) << 4 ) + + ( ( 2*g - r - b ) >> 3 ) + 128; +} diff --git a/src/filters/hq2x.cpp b/src/filters/hq2x.cpp new file mode 100644 index 0000000..33dd3bc --- /dev/null +++ b/src/filters/hq2x.cpp @@ -0,0 +1,604 @@ +/* + * This file is part of the Advance project. + * + * Copyright (C) 2003 Andrea Mazzoleni + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * In addition, as a special exception, Andrea Mazzoleni + * gives permission to link the code of this program with + * the MAME library (or with modified versions of MAME that use the + * same license as MAME), and distribute linked combinations including + * the two. You must obey the GNU General Public License in all + * respects for all of the code used other than MAME. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ +#include "../System.h" +#include "interp.h" + +/***************************************************************************/ +/* HQ2x C implementation */ + +/* + * This effect is a rewritten implementation of the hq2x effect made by Maxim Stepin + */ + +static void hq2x_16_def(u16* dst0, u16* dst1, const u16* src0, const u16* src1, const u16* src2, unsigned count) +{ + unsigned i; + + for(i=0;i0) { + c[0] = src0[-1]; + c[3] = src1[-1]; + c[6] = src2[-1]; + } else { + c[0] = c[1]; + c[3] = c[4]; + c[6] = c[7]; + } + + if (i0) { + c[0] = src0[-1]; + c[3] = src1[-1]; + c[6] = src2[-1]; + } else { + c[0] = c[1]; + c[3] = c[4]; + c[6] = c[7]; + } + + if (i0) { + c[0] = src0[-1]; + c[3] = src1[-1]; + c[6] = src2[-1]; + } else { + c[0] = c[1]; + c[3] = c[4]; + c[6] = c[7]; + } + + if (i0) { + c[0] = src0[-1]; + c[3] = src1[-1]; + c[6] = src2[-1]; + } else { + c[0] = c[1]; + c[3] = c[4]; + c[6] = c[7]; + } + + if (i> 1); + + u16 *src0 = (u16 *)srcPtr; + u16 *src1 = src0 + (srcPitch >> 1); + u16 *src2 = src1 + (srcPitch >> 1); + + hq2x_16_def(dst0, dst1, src0, src0, src1, width); + + int count = height; + + count -= 2; + while(count) { + dst0 += dstPitch; + dst1 += dstPitch; + hq2x_16_def(dst0, dst1, src0, src1, src2, width); + src0 = src1; + src1 = src2; + src2 += srcPitch >> 1; + --count; + } + dst0 += dstPitch; + dst1 += dstPitch; + hq2x_16_def(dst0, dst1, src0, src1, src1, width); +} + +void hq2x32(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u32 *dst0 = (u32 *)dstPtr; + u32 *dst1 = dst0 + (dstPitch >> 2); + + u32 *src0 = (u32 *)srcPtr; + u32 *src1 = src0 + (srcPitch >> 2); + u32 *src2 = src1 + (srcPitch >> 2); + hq2x_32_def(dst0, dst1, src0, src0, src1, width); + + int count = height; + + count -= 2; + while(count) { + dst0 += dstPitch >> 1; + dst1 += dstPitch >> 1; + hq2x_32_def(dst0, dst1, src0, src1, src2, width); + src0 = src1; + src1 = src2; + src2 += srcPitch >> 2; + --count; + } + dst0 += dstPitch >> 1; + dst1 += dstPitch >> 1; + hq2x_32_def(dst0, dst1, src0, src1, src1, width); +} + +void lq2x(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u16 *dst0 = (u16 *)dstPtr; + u16 *dst1 = dst0 + (dstPitch >> 1); + + u16 *src0 = (u16 *)srcPtr; + u16 *src1 = src0 + (srcPitch >> 1); + u16 *src2 = src1 + (srcPitch >> 1); + + lq2x_16_def(dst0, dst1, src0, src0, src1, width); + + int count = height; + + count -= 2; + while(count) { + dst0 += dstPitch; + dst1 += dstPitch; + lq2x_16_def(dst0, dst1, src0, src1, src2, width); + src0 = src1; + src1 = src2; + src2 += srcPitch >> 1; + --count; + } + dst0 += dstPitch; + dst1 += dstPitch; + lq2x_16_def(dst0, dst1, src0, src1, src1, width); +} + +void lq2x32(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u32 *dst0 = (u32 *)dstPtr; + u32 *dst1 = dst0 + (dstPitch >> 2); + + u32 *src0 = (u32 *)srcPtr; + u32 *src1 = src0 + (srcPitch >> 2); + u32 *src2 = src1 + (srcPitch >> 2); + lq2x_32_def(dst0, dst1, src0, src0, src1, width); + + int count = height; + + count -= 2; + while(count) { + dst0 += dstPitch >> 1; + dst1 += dstPitch >> 1; + lq2x_32_def(dst0, dst1, src0, src1, src2, width); + src0 = src1; + src1 = src2; + src2 += srcPitch >> 2; + --count; + } + dst0 += dstPitch >> 1; + dst1 += dstPitch >> 1; + lq2x_32_def(dst0, dst1, src0, src1, src1, width); +} + +void hq2x_init(unsigned bits_per_pixel) +{ + interp_set(bits_per_pixel); +} diff --git a/src/filters/hq2x.h b/src/filters/hq2x.h new file mode 100644 index 0000000..49c0b26 --- /dev/null +++ b/src/filters/hq2x.h @@ -0,0 +1,1824 @@ +case 0 : +case 1 : +case 4 : +case 5 : +case 32 : +case 33 : +case 36 : +case 37 : +case 128 : +case 129 : +case 132 : +case 133 : +case 160 : +case 161 : +case 164 : +case 165 : +{ + P0 = I211(4, 1, 3); + P1 = I211(4, 1, 5); + P2 = I211(4, 3, 7); + P3 = I211(4, 5, 7); +} break; +case 2 : +case 34 : +case 130 : +case 162 : +{ + P0 = I31(4, 0); + P1 = I31(4, 2); + P2 = I211(4, 3, 7); + P3 = I211(4, 5, 7); +} break; +case 3 : +case 35 : +case 131 : +case 163 : +{ + P0 = I31(4, 3); + P1 = I31(4, 2); + P2 = I211(4, 3, 7); + P3 = I211(4, 5, 7); +} break; +case 6 : +case 38 : +case 134 : +case 166 : +{ + P0 = I31(4, 0); + P1 = I31(4, 5); + P2 = I211(4, 3, 7); + P3 = I211(4, 5, 7); +} break; +case 7 : +case 39 : +case 135 : +case 167 : +{ + P0 = I31(4, 3); + P1 = I31(4, 5); + P2 = I211(4, 3, 7); + P3 = I211(4, 5, 7); +} break; +case 8 : +case 12 : +case 136 : +case 140 : +{ + P0 = I31(4, 0); + P1 = I211(4, 1, 5); + P2 = I31(4, 6); + P3 = I211(4, 5, 7); +} break; +case 9 : +case 13 : +case 137 : +case 141 : +{ + P0 = I31(4, 1); + P1 = I211(4, 1, 5); + P2 = I31(4, 6); + P3 = I211(4, 5, 7); +} break; +case 10 : +case 138 : +{ + P1 = I31(4, 2); + P2 = I31(4, 6); + P3 = I211(4, 5, 7); + if (MUL) { + P0 = I31(4, 0); + } else { + P0 = I211(4, 1, 3); + } +} break; +case 11 : +case 139 : +{ + P1 = I31(4, 2); + P2 = I31(4, 6); + P3 = I211(4, 5, 7); + if (MUL) { + P0 = IC(4); + } else { + P0 = I211(4, 1, 3); + } +} break; +case 14 : +case 142 : +{ + P2 = I31(4, 6); + P3 = I211(4, 5, 7); + if (MUL) { + P0 = I31(4, 0); + P1 = I31(4, 5); + } else { + P0 = I332(1, 3, 4); + P1 = I521(4, 1, 5); + } +} break; +case 15 : +case 143 : +{ + P2 = I31(4, 6); + P3 = I211(4, 5, 7); + if (MUL) { + P0 = IC(4); + P1 = I31(4, 5); + } else { + P0 = I332(1, 3, 4); + P1 = I521(4, 1, 5); + } +} break; +case 16 : +case 17 : +case 48 : +case 49 : +{ + P0 = I211(4, 1, 3); + P1 = I31(4, 2); + P2 = I211(4, 3, 7); + P3 = I31(4, 8); +} break; +case 18 : +case 50 : +{ + P0 = I31(4, 0); + P2 = I211(4, 3, 7); + P3 = I31(4, 8); + if (MUR) { + P1 = I31(4, 2); + } else { + P1 = I211(4, 1, 5); + } +} break; +case 19 : +case 51 : +{ + P2 = I211(4, 3, 7); + P3 = I31(4, 8); + if (MUR) { + P0 = I31(4, 3); + P1 = I31(4, 2); + } else { + P0 = I521(4, 1, 3); + P1 = I332(1, 5, 4); + } +} break; +case 20 : +case 21 : +case 52 : +case 53 : +{ + P0 = I211(4, 1, 3); + P1 = I31(4, 1); + P2 = I211(4, 3, 7); + P3 = I31(4, 8); +} break; +case 22 : +case 54 : +{ + P0 = I31(4, 0); + P2 = I211(4, 3, 7); + P3 = I31(4, 8); + if (MUR) { + P1 = IC(4); + } else { + P1 = I211(4, 1, 5); + } +} break; +case 23 : +case 55 : +{ + P2 = I211(4, 3, 7); + P3 = I31(4, 8); + if (MUR) { + P0 = I31(4, 3); + P1 = IC(4); + } else { + P0 = I521(4, 1, 3); + P1 = I332(1, 5, 4); + } +} break; +case 24 : +case 66 : +{ + P0 = I31(4, 0); + P1 = I31(4, 2); + P2 = I31(4, 6); + P3 = I31(4, 8); +} break; +case 25 : +{ + P0 = I31(4, 1); + P1 = I31(4, 2); + P2 = I31(4, 6); + P3 = I31(4, 8); +} break; +case 26 : +case 31 : +case 95 : +{ + P2 = I31(4, 6); + P3 = I31(4, 8); + if (MUL) { + P0 = IC(4); + } else { + P0 = I211(4, 1, 3); + } + if (MUR) { + P1 = IC(4); + } else { + P1 = I211(4, 1, 5); + } +} break; +case 27 : +case 75 : +{ + P1 = I31(4, 2); + P2 = I31(4, 6); + P3 = I31(4, 8); + if (MUL) { + P0 = IC(4); + } else { + P0 = I211(4, 1, 3); + } +} break; +case 28 : +{ + P0 = I31(4, 0); + P1 = I31(4, 1); + P2 = I31(4, 6); + P3 = I31(4, 8); +} break; +case 29 : +{ + P0 = I31(4, 1); + P1 = I31(4, 1); + P2 = I31(4, 6); + P3 = I31(4, 8); +} break; +case 30 : +case 86 : +{ + P0 = I31(4, 0); + P2 = I31(4, 6); + P3 = I31(4, 8); + if (MUR) { + P1 = IC(4); + } else { + P1 = I211(4, 1, 5); + } +} break; +case 40 : +case 44 : +case 168 : +case 172 : +{ + P0 = I31(4, 0); + P1 = I211(4, 1, 5); + P2 = I31(4, 7); + P3 = I211(4, 5, 7); +} break; +case 41 : +case 45 : +case 169 : +case 173 : +{ + P0 = I31(4, 1); + P1 = I211(4, 1, 5); + P2 = I31(4, 7); + P3 = I211(4, 5, 7); +} break; +case 42 : +case 170 : +{ + P1 = I31(4, 2); + P3 = I211(4, 5, 7); + if (MUL) { + P0 = I31(4, 0); + P2 = I31(4, 7); + } else { + P0 = I332(1, 3, 4); + P2 = I521(4, 3, 7); + } +} break; +case 43 : +case 171 : +{ + P1 = I31(4, 2); + P3 = I211(4, 5, 7); + if (MUL) { + P0 = IC(4); + P2 = I31(4, 7); + } else { + P0 = I332(1, 3, 4); + P2 = I521(4, 3, 7); + } +} break; +case 46 : +case 174 : +{ + P1 = I31(4, 5); + P2 = I31(4, 7); + P3 = I211(4, 5, 7); + if (MUL) { + P0 = I31(4, 0); + } else { + P0 = I611(4, 1, 3); + } +} break; +case 47 : +case 175 : +{ + P1 = I31(4, 5); + P2 = I31(4, 7); + P3 = I211(4, 5, 7); + if (MUL) { + P0 = IC(4); + } else { + P0 = I1411(4, 1, 3); + } +} break; +case 56 : +{ + P0 = I31(4, 0); + P1 = I31(4, 2); + P2 = I31(4, 7); + P3 = I31(4, 8); +} break; +case 57 : +{ + P0 = I31(4, 1); + P1 = I31(4, 2); + P2 = I31(4, 7); + P3 = I31(4, 8); +} break; +case 58 : +{ + P2 = I31(4, 7); + P3 = I31(4, 8); + if (MUL) { + P0 = I31(4, 0); + } else { + P0 = I611(4, 1, 3); + } + if (MUR) { + P1 = I31(4, 2); + } else { + P1 = I611(4, 1, 5); + } +} break; +case 59 : +{ + P2 = I31(4, 7); + P3 = I31(4, 8); + if (MUL) { + P0 = IC(4); + } else { + P0 = I211(4, 1, 3); + } + if (MUR) { + P1 = I31(4, 2); + } else { + P1 = I611(4, 1, 5); + } +} break; +case 60 : +{ + P0 = I31(4, 0); + P1 = I31(4, 1); + P2 = I31(4, 7); + P3 = I31(4, 8); +} break; +case 61 : +{ + P0 = I31(4, 1); + P1 = I31(4, 1); + P2 = I31(4, 7); + P3 = I31(4, 8); +} break; +case 62 : +{ + P0 = I31(4, 0); + P2 = I31(4, 7); + P3 = I31(4, 8); + if (MUR) { + P1 = IC(4); + } else { + P1 = I211(4, 1, 5); + } +} break; +case 63 : +{ + P2 = I31(4, 7); + P3 = I31(4, 8); + if (MUL) { + P0 = IC(4); + } else { + P0 = I1411(4, 1, 3); + } + if (MUR) { + P1 = IC(4); + } else { + P1 = I211(4, 1, 5); + } +} break; +case 64 : +case 65 : +case 68 : +case 69 : +{ + P0 = I211(4, 1, 3); + P1 = I211(4, 1, 5); + P2 = I31(4, 6); + P3 = I31(4, 8); +} break; +case 67 : +{ + P0 = I31(4, 3); + P1 = I31(4, 2); + P2 = I31(4, 6); + P3 = I31(4, 8); +} break; +case 70 : +{ + P0 = I31(4, 0); + P1 = I31(4, 5); + P2 = I31(4, 6); + P3 = I31(4, 8); +} break; +case 71 : +{ + P0 = I31(4, 3); + P1 = I31(4, 5); + P2 = I31(4, 6); + P3 = I31(4, 8); +} break; +case 72 : +case 76 : +{ + P0 = I31(4, 0); + P1 = I211(4, 1, 5); + P3 = I31(4, 8); + if (MDL) { + P2 = I31(4, 6); + } else { + P2 = I211(4, 3, 7); + } +} break; +case 73 : +case 77 : +{ + P1 = I211(4, 1, 5); + P3 = I31(4, 8); + if (MDL) { + P0 = I31(4, 1); + P2 = I31(4, 6); + } else { + P0 = I521(4, 3, 1); + P2 = I332(3, 7, 4); + } +} break; +case 74 : +case 107 : +case 123 : +{ + P1 = I31(4, 2); + P3 = I31(4, 8); + if (MDL) { + P2 = IC(4); + } else { + P2 = I211(4, 3, 7); + } + if (MUL) { + P0 = IC(4); + } else { + P0 = I211(4, 1, 3); + } +} break; +case 78 : +{ + P1 = I31(4, 5); + P3 = I31(4, 8); + if (MDL) { + P2 = I31(4, 6); + } else { + P2 = I611(4, 3, 7); + } + if (MUL) { + P0 = I31(4, 0); + } else { + P0 = I611(4, 1, 3); + } +} break; +case 79 : +{ + P1 = I31(4, 5); + P3 = I31(4, 8); + if (MDL) { + P2 = I31(4, 6); + } else { + P2 = I611(4, 3, 7); + } + if (MUL) { + P0 = IC(4); + } else { + P0 = I211(4, 1, 3); + } +} break; +case 80 : +case 81 : +{ + P0 = I211(4, 1, 3); + P1 = I31(4, 2); + P2 = I31(4, 6); + if (MDR) { + P3 = I31(4, 8); + } else { + P3 = I211(4, 5, 7); + } +} break; +case 82 : +case 214 : +case 222 : +{ + P0 = I31(4, 0); + P2 = I31(4, 6); + if (MDR) { + P3 = IC(4); + } else { + P3 = I211(4, 5, 7); + } + if (MUR) { + P1 = IC(4); + } else { + P1 = I211(4, 1, 5); + } +} break; +case 83 : +{ + P0 = I31(4, 3); + P2 = I31(4, 6); + if (MDR) { + P3 = I31(4, 8); + } else { + P3 = I611(4, 5, 7); + } + if (MUR) { + P1 = I31(4, 2); + } else { + P1 = I611(4, 1, 5); + } +} break; +case 84 : +case 85 : +{ + P0 = I211(4, 1, 3); + P2 = I31(4, 6); + if (MDR) { + P1 = I31(4, 1); + P3 = I31(4, 8); + } else { + P1 = I521(4, 5, 1); + P3 = I332(5, 7, 4); + } +} break; +case 87 : +{ + P0 = I31(4, 3); + P2 = I31(4, 6); + if (MDR) { + P3 = I31(4, 8); + } else { + P3 = I611(4, 5, 7); + } + if (MUR) { + P1 = IC(4); + } else { + P1 = I211(4, 1, 5); + } +} break; +case 88 : +case 248 : +case 250 : +{ + P0 = I31(4, 0); + P1 = I31(4, 2); + if (MDL) { + P2 = IC(4); + } else { + P2 = I211(4, 3, 7); + } + if (MDR) { + P3 = IC(4); + } else { + P3 = I211(4, 5, 7); + } +} break; +case 89 : +{ + P0 = I31(4, 1); + P1 = I31(4, 2); + if (MDL) { + P2 = I31(4, 6); + } else { + P2 = I611(4, 3, 7); + } + if (MDR) { + P3 = I31(4, 8); + } else { + P3 = I611(4, 5, 7); + } +} break; +case 90 : +{ + if (MDL) { + P2 = I31(4, 6); + } else { + P2 = I611(4, 3, 7); + } + if (MDR) { + P3 = I31(4, 8); + } else { + P3 = I611(4, 5, 7); + } + if (MUL) { + P0 = I31(4, 0); + } else { + P0 = I611(4, 1, 3); + } + if (MUR) { + P1 = I31(4, 2); + } else { + P1 = I611(4, 1, 5); + } +} break; +case 91 : +{ + if (MDL) { + P2 = I31(4, 6); + } else { + P2 = I611(4, 3, 7); + } + if (MDR) { + P3 = I31(4, 8); + } else { + P3 = I611(4, 5, 7); + } + if (MUL) { + P0 = IC(4); + } else { + P0 = I211(4, 1, 3); + } + if (MUR) { + P1 = I31(4, 2); + } else { + P1 = I611(4, 1, 5); + } +} break; +case 92 : +{ + P0 = I31(4, 0); + P1 = I31(4, 1); + if (MDL) { + P2 = I31(4, 6); + } else { + P2 = I611(4, 3, 7); + } + if (MDR) { + P3 = I31(4, 8); + } else { + P3 = I611(4, 5, 7); + } +} break; +case 93 : +{ + P0 = I31(4, 1); + P1 = I31(4, 1); + if (MDL) { + P2 = I31(4, 6); + } else { + P2 = I611(4, 3, 7); + } + if (MDR) { + P3 = I31(4, 8); + } else { + P3 = I611(4, 5, 7); + } +} break; +case 94 : +{ + if (MDL) { + P2 = I31(4, 6); + } else { + P2 = I611(4, 3, 7); + } + if (MDR) { + P3 = I31(4, 8); + } else { + P3 = I611(4, 5, 7); + } + if (MUL) { + P0 = I31(4, 0); + } else { + P0 = I611(4, 1, 3); + } + if (MUR) { + P1 = IC(4); + } else { + P1 = I211(4, 1, 5); + } +} break; +case 96 : +case 97 : +case 100 : +case 101 : +{ + P0 = I211(4, 1, 3); + P1 = I211(4, 1, 5); + P2 = I31(4, 3); + P3 = I31(4, 8); +} break; +case 98 : +{ + P0 = I31(4, 0); + P1 = I31(4, 2); + P2 = I31(4, 3); + P3 = I31(4, 8); +} break; +case 99 : +{ + P0 = I31(4, 3); + P1 = I31(4, 2); + P2 = I31(4, 3); + P3 = I31(4, 8); +} break; +case 102 : +{ + P0 = I31(4, 0); + P1 = I31(4, 5); + P2 = I31(4, 3); + P3 = I31(4, 8); +} break; +case 103 : +{ + P0 = I31(4, 3); + P1 = I31(4, 5); + P2 = I31(4, 3); + P3 = I31(4, 8); +} break; +case 104 : +case 108 : +{ + P0 = I31(4, 0); + P1 = I211(4, 1, 5); + P3 = I31(4, 8); + if (MDL) { + P2 = IC(4); + } else { + P2 = I211(4, 3, 7); + } +} break; +case 105 : +case 109 : +{ + P1 = I211(4, 1, 5); + P3 = I31(4, 8); + if (MDL) { + P0 = I31(4, 1); + P2 = IC(4); + } else { + P0 = I521(4, 3, 1); + P2 = I332(3, 7, 4); + } +} break; +case 106 : +case 120 : +{ + P0 = I31(4, 0); + P1 = I31(4, 2); + P3 = I31(4, 8); + if (MDL) { + P2 = IC(4); + } else { + P2 = I211(4, 3, 7); + } +} break; +case 110 : +{ + P0 = I31(4, 0); + P1 = I31(4, 5); + P3 = I31(4, 8); + if (MDL) { + P2 = IC(4); + } else { + P2 = I211(4, 3, 7); + } +} break; +case 111 : +{ + P1 = I31(4, 5); + P3 = I31(4, 8); + if (MDL) { + P2 = IC(4); + } else { + P2 = I211(4, 3, 7); + } + if (MUL) { + P0 = IC(4); + } else { + P0 = I1411(4, 1, 3); + } +} break; +case 112 : +case 113 : +{ + P0 = I211(4, 1, 3); + P1 = I31(4, 2); + if (MDR) { + P2 = I31(4, 3); + P3 = I31(4, 8); + } else { + P2 = I521(4, 7, 3); + P3 = I332(5, 7, 4); + } +} break; +case 114 : +{ + P0 = I31(4, 0); + P2 = I31(4, 3); + if (MDR) { + P3 = I31(4, 8); + } else { + P3 = I611(4, 5, 7); + } + if (MUR) { + P1 = I31(4, 2); + } else { + P1 = I611(4, 1, 5); + } +} break; +case 115 : +{ + P0 = I31(4, 3); + P2 = I31(4, 3); + if (MDR) { + P3 = I31(4, 8); + } else { + P3 = I611(4, 5, 7); + } + if (MUR) { + P1 = I31(4, 2); + } else { + P1 = I611(4, 1, 5); + } +} break; +case 116 : +case 117 : +{ + P0 = I211(4, 1, 3); + P1 = I31(4, 1); + P2 = I31(4, 3); + if (MDR) { + P3 = I31(4, 8); + } else { + P3 = I611(4, 5, 7); + } +} break; +case 118 : +{ + P0 = I31(4, 0); + P2 = I31(4, 3); + P3 = I31(4, 8); + if (MUR) { + P1 = IC(4); + } else { + P1 = I211(4, 1, 5); + } +} break; +case 119 : +{ + P2 = I31(4, 3); + P3 = I31(4, 8); + if (MUR) { + P0 = I31(4, 3); + P1 = IC(4); + } else { + P0 = I521(4, 1, 3); + P1 = I332(1, 5, 4); + } +} break; +case 121 : +{ + P0 = I31(4, 1); + P1 = I31(4, 2); + if (MDL) { + P2 = IC(4); + } else { + P2 = I211(4, 3, 7); + } + if (MDR) { + P3 = I31(4, 8); + } else { + P3 = I611(4, 5, 7); + } +} break; +case 122 : +{ + if (MDL) { + P2 = IC(4); + } else { + P2 = I211(4, 3, 7); + } + if (MDR) { + P3 = I31(4, 8); + } else { + P3 = I611(4, 5, 7); + } + if (MUL) { + P0 = I31(4, 0); + } else { + P0 = I611(4, 1, 3); + } + if (MUR) { + P1 = I31(4, 2); + } else { + P1 = I611(4, 1, 5); + } +} break; +case 124 : +{ + P0 = I31(4, 0); + P1 = I31(4, 1); + P3 = I31(4, 8); + if (MDL) { + P2 = IC(4); + } else { + P2 = I211(4, 3, 7); + } +} break; +case 125 : +{ + P1 = I31(4, 1); + P3 = I31(4, 8); + if (MDL) { + P0 = I31(4, 1); + P2 = IC(4); + } else { + P0 = I521(4, 3, 1); + P2 = I332(3, 7, 4); + } +} break; +case 126 : +{ + P0 = I31(4, 0); + P3 = I31(4, 8); + if (MDL) { + P2 = IC(4); + } else { + P2 = I211(4, 3, 7); + } + if (MUR) { + P1 = IC(4); + } else { + P1 = I211(4, 1, 5); + } +} break; +case 127 : +{ + P3 = I31(4, 8); + if (MDL) { + P2 = IC(4); + } else { + P2 = I211(4, 3, 7); + } + if (MUL) { + P0 = IC(4); + } else { + P0 = I1411(4, 1, 3); + } + if (MUR) { + P1 = IC(4); + } else { + P1 = I211(4, 1, 5); + } +} break; +case 144 : +case 145 : +case 176 : +case 177 : +{ + P0 = I211(4, 1, 3); + P1 = I31(4, 2); + P2 = I211(4, 3, 7); + P3 = I31(4, 7); +} break; +case 146 : +case 178 : +{ + P0 = I31(4, 0); + P2 = I211(4, 3, 7); + if (MUR) { + P1 = I31(4, 2); + P3 = I31(4, 7); + } else { + P1 = I332(1, 5, 4); + P3 = I521(4, 5, 7); + } +} break; +case 147 : +case 179 : +{ + P0 = I31(4, 3); + P2 = I211(4, 3, 7); + P3 = I31(4, 7); + if (MUR) { + P1 = I31(4, 2); + } else { + P1 = I611(4, 1, 5); + } +} break; +case 148 : +case 149 : +case 180 : +case 181 : +{ + P0 = I211(4, 1, 3); + P1 = I31(4, 1); + P2 = I211(4, 3, 7); + P3 = I31(4, 7); +} break; +case 150 : +case 182 : +{ + P0 = I31(4, 0); + P2 = I211(4, 3, 7); + if (MUR) { + P1 = IC(4); + P3 = I31(4, 7); + } else { + P1 = I332(1, 5, 4); + P3 = I521(4, 5, 7); + } +} break; +case 151 : +case 183 : +{ + P0 = I31(4, 3); + P2 = I211(4, 3, 7); + P3 = I31(4, 7); + if (MUR) { + P1 = IC(4); + } else { + P1 = I1411(4, 1, 5); + } +} break; +case 152 : +{ + P0 = I31(4, 0); + P1 = I31(4, 2); + P2 = I31(4, 6); + P3 = I31(4, 7); +} break; +case 153 : +{ + P0 = I31(4, 1); + P1 = I31(4, 2); + P2 = I31(4, 6); + P3 = I31(4, 7); +} break; +case 154 : +{ + P2 = I31(4, 6); + P3 = I31(4, 7); + if (MUL) { + P0 = I31(4, 0); + } else { + P0 = I611(4, 1, 3); + } + if (MUR) { + P1 = I31(4, 2); + } else { + P1 = I611(4, 1, 5); + } +} break; +case 155 : +{ + P1 = I31(4, 2); + P2 = I31(4, 6); + P3 = I31(4, 7); + if (MUL) { + P0 = IC(4); + } else { + P0 = I211(4, 1, 3); + } +} break; +case 156 : +{ + P0 = I31(4, 0); + P1 = I31(4, 1); + P2 = I31(4, 6); + P3 = I31(4, 7); +} break; +case 157 : +{ + P0 = I31(4, 1); + P1 = I31(4, 1); + P2 = I31(4, 6); + P3 = I31(4, 7); +} break; +case 158 : +{ + P2 = I31(4, 6); + P3 = I31(4, 7); + if (MUL) { + P0 = I31(4, 0); + } else { + P0 = I611(4, 1, 3); + } + if (MUR) { + P1 = IC(4); + } else { + P1 = I211(4, 1, 5); + } +} break; +case 159 : +{ + P2 = I31(4, 6); + P3 = I31(4, 7); + if (MUL) { + P0 = IC(4); + } else { + P0 = I211(4, 1, 3); + } + if (MUR) { + P1 = IC(4); + } else { + P1 = I1411(4, 1, 5); + } +} break; +case 184 : +{ + P0 = I31(4, 0); + P1 = I31(4, 2); + P2 = I31(4, 7); + P3 = I31(4, 7); +} break; +case 185 : +{ + P0 = I31(4, 1); + P1 = I31(4, 2); + P2 = I31(4, 7); + P3 = I31(4, 7); +} break; +case 186 : +{ + P2 = I31(4, 7); + P3 = I31(4, 7); + if (MUL) { + P0 = I31(4, 0); + } else { + P0 = I611(4, 1, 3); + } + if (MUR) { + P1 = I31(4, 2); + } else { + P1 = I611(4, 1, 5); + } +} break; +case 187 : +{ + P1 = I31(4, 2); + P3 = I31(4, 7); + if (MUL) { + P0 = IC(4); + P2 = I31(4, 7); + } else { + P0 = I332(1, 3, 4); + P2 = I521(4, 3, 7); + } +} break; +case 188 : +{ + P0 = I31(4, 0); + P1 = I31(4, 1); + P2 = I31(4, 7); + P3 = I31(4, 7); +} break; +case 189 : +{ + P0 = I31(4, 1); + P1 = I31(4, 1); + P2 = I31(4, 7); + P3 = I31(4, 7); +} break; +case 190 : +{ + P0 = I31(4, 0); + P2 = I31(4, 7); + if (MUR) { + P1 = IC(4); + P3 = I31(4, 7); + } else { + P1 = I332(1, 5, 4); + P3 = I521(4, 5, 7); + } +} break; +case 191 : +{ + P2 = I31(4, 7); + P3 = I31(4, 7); + if (MUL) { + P0 = IC(4); + } else { + P0 = I1411(4, 1, 3); + } + if (MUR) { + P1 = IC(4); + } else { + P1 = I1411(4, 1, 5); + } +} break; +case 192 : +case 193 : +case 196 : +case 197 : +{ + P0 = I211(4, 1, 3); + P1 = I211(4, 1, 5); + P2 = I31(4, 6); + P3 = I31(4, 5); +} break; +case 194 : +{ + P0 = I31(4, 0); + P1 = I31(4, 2); + P2 = I31(4, 6); + P3 = I31(4, 5); +} break; +case 195 : +{ + P0 = I31(4, 3); + P1 = I31(4, 2); + P2 = I31(4, 6); + P3 = I31(4, 5); +} break; +case 198 : +{ + P0 = I31(4, 0); + P1 = I31(4, 5); + P2 = I31(4, 6); + P3 = I31(4, 5); +} break; +case 199 : +{ + P0 = I31(4, 3); + P1 = I31(4, 5); + P2 = I31(4, 6); + P3 = I31(4, 5); +} break; +case 200 : +case 204 : +{ + P0 = I31(4, 0); + P1 = I211(4, 1, 5); + if (MDL) { + P2 = I31(4, 6); + P3 = I31(4, 5); + } else { + P2 = I332(3, 7, 4); + P3 = I521(4, 7, 5); + } +} break; +case 201 : +case 205 : +{ + P0 = I31(4, 1); + P1 = I211(4, 1, 5); + P3 = I31(4, 5); + if (MDL) { + P2 = I31(4, 6); + } else { + P2 = I611(4, 3, 7); + } +} break; +case 202 : +{ + P1 = I31(4, 2); + P3 = I31(4, 5); + if (MDL) { + P2 = I31(4, 6); + } else { + P2 = I611(4, 3, 7); + } + if (MUL) { + P0 = I31(4, 0); + } else { + P0 = I611(4, 1, 3); + } +} break; +case 203 : +{ + P1 = I31(4, 2); + P2 = I31(4, 6); + P3 = I31(4, 5); + if (MUL) { + P0 = IC(4); + } else { + P0 = I211(4, 1, 3); + } +} break; +case 206 : +{ + P1 = I31(4, 5); + P3 = I31(4, 5); + if (MDL) { + P2 = I31(4, 6); + } else { + P2 = I611(4, 3, 7); + } + if (MUL) { + P0 = I31(4, 0); + } else { + P0 = I611(4, 1, 3); + } +} break; +case 207 : +{ + P2 = I31(4, 6); + P3 = I31(4, 5); + if (MUL) { + P0 = IC(4); + P1 = I31(4, 5); + } else { + P0 = I332(1, 3, 4); + P1 = I521(4, 1, 5); + } +} break; +case 208 : +case 209 : +{ + P0 = I211(4, 1, 3); + P1 = I31(4, 2); + P2 = I31(4, 6); + if (MDR) { + P3 = IC(4); + } else { + P3 = I211(4, 5, 7); + } +} break; +case 210 : +case 216 : +{ + P0 = I31(4, 0); + P1 = I31(4, 2); + P2 = I31(4, 6); + if (MDR) { + P3 = IC(4); + } else { + P3 = I211(4, 5, 7); + } +} break; +case 211 : +{ + P0 = I31(4, 3); + P1 = I31(4, 2); + P2 = I31(4, 6); + if (MDR) { + P3 = IC(4); + } else { + P3 = I211(4, 5, 7); + } +} break; +case 212 : +case 213 : +{ + P0 = I211(4, 1, 3); + P2 = I31(4, 6); + if (MDR) { + P1 = I31(4, 1); + P3 = IC(4); + } else { + P1 = I521(4, 5, 1); + P3 = I332(5, 7, 4); + } +} break; +case 215 : +{ + P0 = I31(4, 3); + P2 = I31(4, 6); + if (MDR) { + P3 = IC(4); + } else { + P3 = I211(4, 5, 7); + } + if (MUR) { + P1 = IC(4); + } else { + P1 = I1411(4, 1, 5); + } +} break; +case 217 : +{ + P0 = I31(4, 1); + P1 = I31(4, 2); + P2 = I31(4, 6); + if (MDR) { + P3 = IC(4); + } else { + P3 = I211(4, 5, 7); + } +} break; +case 218 : +{ + if (MDL) { + P2 = I31(4, 6); + } else { + P2 = I611(4, 3, 7); + } + if (MDR) { + P3 = IC(4); + } else { + P3 = I211(4, 5, 7); + } + if (MUL) { + P0 = I31(4, 0); + } else { + P0 = I611(4, 1, 3); + } + if (MUR) { + P1 = I31(4, 2); + } else { + P1 = I611(4, 1, 5); + } +} break; +case 219 : +{ + P1 = I31(4, 2); + P2 = I31(4, 6); + if (MDR) { + P3 = IC(4); + } else { + P3 = I211(4, 5, 7); + } + if (MUL) { + P0 = IC(4); + } else { + P0 = I211(4, 1, 3); + } +} break; +case 220 : +{ + P0 = I31(4, 0); + P1 = I31(4, 1); + if (MDL) { + P2 = I31(4, 6); + } else { + P2 = I611(4, 3, 7); + } + if (MDR) { + P3 = IC(4); + } else { + P3 = I211(4, 5, 7); + } +} break; +case 221 : +{ + P0 = I31(4, 1); + P2 = I31(4, 6); + if (MDR) { + P1 = I31(4, 1); + P3 = IC(4); + } else { + P1 = I521(4, 5, 1); + P3 = I332(5, 7, 4); + } +} break; +case 223 : +{ + P2 = I31(4, 6); + if (MDR) { + P3 = IC(4); + } else { + P3 = I211(4, 5, 7); + } + if (MUL) { + P0 = IC(4); + } else { + P0 = I211(4, 1, 3); + } + if (MUR) { + P1 = IC(4); + } else { + P1 = I1411(4, 1, 5); + } +} break; +case 224 : +case 225 : +case 228 : +case 229 : +{ + P0 = I211(4, 1, 3); + P1 = I211(4, 1, 5); + P2 = I31(4, 3); + P3 = I31(4, 5); +} break; +case 226 : +{ + P0 = I31(4, 0); + P1 = I31(4, 2); + P2 = I31(4, 3); + P3 = I31(4, 5); +} break; +case 227 : +{ + P0 = I31(4, 3); + P1 = I31(4, 2); + P2 = I31(4, 3); + P3 = I31(4, 5); +} break; +case 230 : +{ + P0 = I31(4, 0); + P1 = I31(4, 5); + P2 = I31(4, 3); + P3 = I31(4, 5); +} break; +case 231 : +{ + P0 = I31(4, 3); + P1 = I31(4, 5); + P2 = I31(4, 3); + P3 = I31(4, 5); +} break; +case 232 : +case 236 : +{ + P0 = I31(4, 0); + P1 = I211(4, 1, 5); + if (MDL) { + P2 = IC(4); + P3 = I31(4, 5); + } else { + P2 = I332(3, 7, 4); + P3 = I521(4, 7, 5); + } +} break; +case 233 : +case 237 : +{ + P0 = I31(4, 1); + P1 = I211(4, 1, 5); + P3 = I31(4, 5); + if (MDL) { + P2 = IC(4); + } else { + P2 = I1411(4, 3, 7); + } +} break; +case 234 : +{ + P1 = I31(4, 2); + P3 = I31(4, 5); + if (MDL) { + P2 = IC(4); + } else { + P2 = I211(4, 3, 7); + } + if (MUL) { + P0 = I31(4, 0); + } else { + P0 = I611(4, 1, 3); + } +} break; +case 235 : +{ + P1 = I31(4, 2); + P3 = I31(4, 5); + if (MDL) { + P2 = IC(4); + } else { + P2 = I1411(4, 3, 7); + } + if (MUL) { + P0 = IC(4); + } else { + P0 = I211(4, 1, 3); + } +} break; +case 238 : +{ + P0 = I31(4, 0); + P1 = I31(4, 5); + if (MDL) { + P2 = IC(4); + P3 = I31(4, 5); + } else { + P2 = I332(3, 7, 4); + P3 = I521(4, 7, 5); + } +} break; +case 239 : +{ + P1 = I31(4, 5); + P3 = I31(4, 5); + if (MDL) { + P2 = IC(4); + } else { + P2 = I1411(4, 3, 7); + } + if (MUL) { + P0 = IC(4); + } else { + P0 = I1411(4, 1, 3); + } +} break; +case 240 : +case 241 : +{ + P0 = I211(4, 1, 3); + P1 = I31(4, 2); + if (MDR) { + P2 = I31(4, 3); + P3 = IC(4); + } else { + P2 = I521(4, 7, 3); + P3 = I332(5, 7, 4); + } +} break; +case 242 : +{ + P0 = I31(4, 0); + P2 = I31(4, 3); + if (MDR) { + P3 = IC(4); + } else { + P3 = I211(4, 5, 7); + } + if (MUR) { + P1 = I31(4, 2); + } else { + P1 = I611(4, 1, 5); + } +} break; +case 243 : +{ + P0 = I31(4, 3); + P1 = I31(4, 2); + if (MDR) { + P2 = I31(4, 3); + P3 = IC(4); + } else { + P2 = I521(4, 7, 3); + P3 = I332(5, 7, 4); + } +} break; +case 244 : +case 245 : +{ + P0 = I211(4, 1, 3); + P1 = I31(4, 1); + P2 = I31(4, 3); + if (MDR) { + P3 = IC(4); + } else { + P3 = I1411(4, 5, 7); + } +} break; +case 246 : +{ + P0 = I31(4, 0); + P2 = I31(4, 3); + if (MDR) { + P3 = IC(4); + } else { + P3 = I1411(4, 5, 7); + } + if (MUR) { + P1 = IC(4); + } else { + P1 = I211(4, 1, 5); + } +} break; +case 247 : +{ + P0 = I31(4, 3); + P2 = I31(4, 3); + if (MDR) { + P3 = IC(4); + } else { + P3 = I1411(4, 5, 7); + } + if (MUR) { + P1 = IC(4); + } else { + P1 = I1411(4, 1, 5); + } +} break; +case 249 : +{ + P0 = I31(4, 1); + P1 = I31(4, 2); + if (MDL) { + P2 = IC(4); + } else { + P2 = I1411(4, 3, 7); + } + if (MDR) { + P3 = IC(4); + } else { + P3 = I211(4, 5, 7); + } +} break; +case 251 : +{ + P1 = I31(4, 2); + if (MDL) { + P2 = IC(4); + } else { + P2 = I1411(4, 3, 7); + } + if (MDR) { + P3 = IC(4); + } else { + P3 = I211(4, 5, 7); + } + if (MUL) { + P0 = IC(4); + } else { + P0 = I211(4, 1, 3); + } +} break; +case 252 : +{ + P0 = I31(4, 0); + P1 = I31(4, 1); + if (MDL) { + P2 = IC(4); + } else { + P2 = I211(4, 3, 7); + } + if (MDR) { + P3 = IC(4); + } else { + P3 = I1411(4, 5, 7); + } +} break; +case 253 : +{ + P0 = I31(4, 1); + P1 = I31(4, 1); + if (MDL) { + P2 = IC(4); + } else { + P2 = I1411(4, 3, 7); + } + if (MDR) { + P3 = IC(4); + } else { + P3 = I1411(4, 5, 7); + } +} break; +case 254 : +{ + P0 = I31(4, 0); + if (MDL) { + P2 = IC(4); + } else { + P2 = I211(4, 3, 7); + } + if (MDR) { + P3 = IC(4); + } else { + P3 = I1411(4, 5, 7); + } + if (MUR) { + P1 = IC(4); + } else { + P1 = I211(4, 1, 5); + } +} break; +case 255 : +{ + if (MDL) { + P2 = IC(4); + } else { + P2 = I1411(4, 3, 7); + } + if (MDR) { + P3 = IC(4); + } else { + P3 = I1411(4, 5, 7); + } + if (MUL) { + P0 = IC(4); + } else { + P0 = I1411(4, 1, 3); + } + if (MUR) { + P1 = IC(4); + } else { + P1 = I1411(4, 1, 5); + } +} break; diff --git a/src/filters/interframe.cpp b/src/filters/interframe.cpp new file mode 100644 index 0000000..b72b78d --- /dev/null +++ b/src/filters/interframe.cpp @@ -0,0 +1,583 @@ +#include "../System.h" +#include +#include + +#ifdef MMX +extern "C" bool cpu_mmx; +#endif + +/* + * Thanks to Kawaks' Mr. K for the code + + Incorporated into vba by Anthony Di Franco +*/ + +static u8 *frm1 = NULL; +static u8 *frm2 = NULL; +static u8 *frm3 = NULL; + +extern int RGB_LOW_BITS_MASK; +extern u32 qRGB_COLOR_MASK[2]; + +static void Init() +{ + frm1 = (u8 *)calloc(322*242,4); + // 1 frame ago + frm2 = (u8 *)calloc(322*242,4); + // 2 frames ago + frm3 = (u8 *)calloc(322*242,4); + // 3 frames ago +} + +void InterframeCleanup() +{ + if(frm1) + free(frm1); + if(frm2) + free(frm2); + if(frm3) + free(frm3); + frm1 = frm2 = frm3 = NULL; +} + +#ifdef MMX +static void SmartIB_MMX(u8 *srcPtr, u32 srcPitch, int width, int starty, int height) +{ + 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; + + for(int i = 0; i < height; i++) { +#ifdef __GNUC__ + asm volatile ( + "push %4\n" + "movq 0(%5), %%mm7\n" // colorMask + "0:\n" + "movq 0(%0), %%mm0\n" // src0 + "movq 0(%1), %%mm1\n" // src1 + "movq 0(%2), %%mm2\n" // src2 + "movq 0(%3), %%mm3\n" // src3 + "movq %%mm0, 0(%3)\n" // src3 = src0 + "movq %%mm0, %%mm4\n" + "movq %%mm1, %%mm5\n" + "pcmpeqw %%mm2, %%mm5\n" // src1 == src2 (A) + "pcmpeqw %%mm3, %%mm4\n" // src3 == src0 (B) + "por %%mm5, %%mm4\n" // A | B + "movq %%mm2, %%mm5\n" + "pcmpeqw %%mm0, %%mm5\n" // src0 == src2 (C) + "pcmpeqw %%mm1, %%mm3\n" // src1 == src3 (D) + "por %%mm3, %%mm5\n" // C|D + "pandn %%mm5, %%mm4\n" // (!(A|B))&(C|D) + "movq %%mm0, %%mm2\n" + "pand %%mm7, %%mm2\n" // color & colorMask + "pand %%mm7, %%mm1\n" // src1 & colorMask + "psrlw $1, %%mm2\n" // (color & colorMask) >> 1 (E) + "psrlw $1, %%mm1\n" // (src & colorMask) >> 1 (F) + "paddw %%mm2, %%mm1\n" // E+F + "pand %%mm4, %%mm1\n" // (E+F) & res + "pandn %%mm0, %%mm4\n" // color& !res + + "por %%mm1, %%mm4\n" + "movq %%mm4, 0(%0)\n" // src0 = res + + "addl $8, %0\n" + "addl $8, %1\n" + "addl $8, %2\n" + "addl $8, %3\n" + + "decl %4\n" + "jnz 0b\n" + "pop %4\n" + "emms\n" + : "+r" (src0), "+r" (src1), "+r" (src2), "+r" (src3) + : "r" (count), "r" (qRGB_COLOR_MASK) + ); +#else + __asm { + movq mm7, qword ptr [qRGB_COLOR_MASK]; + mov eax, src0; + mov ebx, src1; + mov ecx, src2; + mov edx, src3; + mov edi, count; + label0: + movq mm0, qword ptr [eax]; // src0 + movq mm1, qword ptr [ebx]; // src1 + movq mm2, qword ptr [ecx]; // src2 + movq mm3, qword ptr [edx]; // src3 + movq qword ptr [edx], mm0; // src3 = src0 + movq mm4, mm0; + movq mm5, mm1; + pcmpeqw mm5, mm2; // src1 == src2 (A) + pcmpeqw mm4, mm3; // src3 == src0 (B) + por mm4, mm5; // A | B + movq mm5, mm2; + pcmpeqw mm5, mm0; // src0 == src2 (C) + pcmpeqw mm3, mm1; // src1 == src3 (D) + por mm5, mm3; // C|D + pandn mm4, mm5; // (!(A|B))&(C|D) + movq mm2, mm0; + pand mm2, mm7; // color & colorMask + pand mm1, mm7; // src1 & colorMask + psrlw mm2, 1; // (color & colorMask) >> 1 (E) + psrlw mm1, 1; // (src & colorMask) >> 1 (F) + paddw mm1, mm2; // E+F + pand mm1, mm4; // (E+F) & res + pandn mm4, mm0; // color & !res + + por mm4, mm1; + movq qword ptr [eax], mm4; // src0 = res + + add eax, 8; + add ebx, 8; + add ecx, 8; + add edx, 8; + + dec edi; + jnz label0; + mov src0, eax; + mov src1, ebx; + mov src2, ecx; + mov src3, edx; + emms; + } +#endif + src0+=2; + src1+=2; + src2+=2; + src3+=2; + } + + /* Swap buffers around */ + u8 *temp = frm1; + frm1 = frm3; + frm3 = frm2; + frm2 = temp; +} +#endif + +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, starty, height); + return; + } +#endif + + u16 colorMask = ~RGB_LOW_BITS_MASK; + + 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; + + int pos = 0; + for (int j = 0; j < height; j++) + for (int i = 0; i < sPitch; i++) { + u16 color = src0[pos]; + src0[pos] = + (src1[pos] != src2[pos]) && + (src3[pos] != color) && + ((color == src2[pos]) || (src1[pos] == src3[pos])) + ? (((color & colorMask) >> 1) + ((src1[pos] & colorMask) >> 1)) : + color; + src3[pos] = color; /* oldest buffer now holds newest frame */ + pos++; + } + + /* Swap buffers around */ + u8 *temp = frm1; + frm1 = frm3; + frm3 = frm2; + frm2 = temp; +} + +void SmartIB(u8 *srcPtr, u32 srcPitch, int width, int height) +{ + 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; + + for(int i = 0; i < height; i++) { +#ifdef __GNUC__ + asm volatile ( + "push %4\n" + "movq 0(%5), %%mm7\n" // colorMask + "0:\n" + "movq 0(%0), %%mm0\n" // src0 + "movq 0(%1), %%mm1\n" // src1 + "movq 0(%2), %%mm2\n" // src2 + "movq 0(%3), %%mm3\n" // src3 + "movq %%mm0, 0(%3)\n" // src3 = src0 + "movq %%mm0, %%mm4\n" + "movq %%mm1, %%mm5\n" + "pcmpeqd %%mm2, %%mm5\n" // src1 == src2 (A) + "pcmpeqd %%mm3, %%mm4\n" // src3 == src0 (B) + "por %%mm5, %%mm4\n" // A | B + "movq %%mm2, %%mm5\n" + "pcmpeqd %%mm0, %%mm5\n" // src0 == src2 (C) + "pcmpeqd %%mm1, %%mm3\n" // src1 == src3 (D) + "por %%mm3, %%mm5\n" // C|D + "pandn %%mm5, %%mm4\n" // (!(A|B))&(C|D) + "movq %%mm0, %%mm2\n" + "pand %%mm7, %%mm2\n" // color & colorMask + "pand %%mm7, %%mm1\n" // src1 & colorMask + "psrld $1, %%mm2\n" // (color & colorMask) >> 1 (E) + "psrld $1, %%mm1\n" // (src & colorMask) >> 1 (F) + "paddd %%mm2, %%mm1\n" // E+F + "pand %%mm4, %%mm1\n" // (E+F) & res + "pandn %%mm0, %%mm4\n" // color& !res + + "por %%mm1, %%mm4\n" + "movq %%mm4, 0(%0)\n" // src0 = res + + "addl $8, %0\n" + "addl $8, %1\n" + "addl $8, %2\n" + "addl $8, %3\n" + + "decl %4\n" + "jnz 0b\n" + "pop %4\n" + "emms\n" + : "+r" (src0), "+r" (src1), "+r" (src2), "+r" (src3) + : "r" (count), "r" (qRGB_COLOR_MASK) + ); +#else + __asm { + movq mm7, qword ptr [qRGB_COLOR_MASK]; + mov eax, src0; + mov ebx, src1; + mov ecx, src2; + mov edx, src3; + mov edi, count; + label0: + movq mm0, qword ptr [eax]; // src0 + movq mm1, qword ptr [ebx]; // src1 + movq mm2, qword ptr [ecx]; // src2 + movq mm3, qword ptr [edx]; // src3 + movq qword ptr [edx], mm0; // src3 = src0 + movq mm4, mm0; + movq mm5, mm1; + pcmpeqd mm5, mm2; // src1 == src2 (A) + pcmpeqd mm4, mm3; // src3 == src0 (B) + por mm4, mm5; // A | B + movq mm5, mm2; + pcmpeqd mm5, mm0; // src0 == src2 (C) + pcmpeqd mm3, mm1; // src1 == src3 (D) + por mm5, mm3; // C|D + pandn mm4, mm5; // (!(A|B))&(C|D) + movq mm2, mm0; + pand mm2, mm7; // color & colorMask + pand mm1, mm7; // src1 & colorMask + psrld mm2, 1; // (color & colorMask) >> 1 (E) + psrld mm1, 1; // (src & colorMask) >> 1 (F) + paddd mm1, mm2; // E+F + pand mm1, mm4; // (E+F) & res + pandn mm4, mm0; // color & !res + + por mm4, mm1; + movq qword ptr [eax], mm4; // src0 = res + + add eax, 8; + add ebx, 8; + add ecx, 8; + add edx, 8; + + dec edi; + jnz label0; + mov src0, eax; + mov src1, ebx; + mov src2, ecx; + mov src3, edx; + emms; + } +#endif + + src0++; + src1++; + src2++; + src3++; + } + /* Swap buffers around */ + u8 *temp = frm1; + frm1 = frm3; + frm3 = frm2; + frm2 = temp; +} +#endif + +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, starty, height); + return; + } +#endif + + 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; + + int sPitch = srcPitch >> 2; + int pos = 0; + + for (int j = 0; j < height; j++) + for (int i = 0; i < sPitch; i++) { + u32 color = src0[pos]; + src0[pos] = + (src1[pos] != src2[pos]) && + (src3[pos] != color) && + ((color == src2[pos]) || (src1[pos] == src3[pos])) + ? (((color & colorMask) >> 1) + ((src1[pos] & colorMask) >> 1)) : + color; + src3[pos] = color; /* oldest buffer now holds newest frame */ + pos++; + } + + /* Swap buffers around */ + u8 *temp = frm1; + frm1 = frm3; + frm3 = frm2; + frm2 = temp; +} + +void SmartIB32(u8 *srcPtr, u32 srcPitch, int width, int height) +{ + 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; + + for(int i = 0; i < height; i++) { +#ifdef __GNUC__ + asm volatile ( + "push %2\n" + "movq 0(%3), %%mm7\n" // colorMask + "0:\n" + "movq 0(%0), %%mm0\n" // src0 + "movq 0(%1), %%mm1\n" // src1 + "movq %%mm0, 0(%1)\n" // src1 = src0 + "pand %%mm7, %%mm0\n" // color & colorMask + "pand %%mm7, %%mm1\n" // src1 & colorMask + "psrlw $1, %%mm0\n" // (color & colorMask) >> 1 (E) + "psrlw $1, %%mm1\n" // (src & colorMask) >> 1 (F) + "paddw %%mm1, %%mm0\n" // E+F + + "movq %%mm0, 0(%0)\n" // src0 = res + + "addl $8, %0\n" + "addl $8, %1\n" + + "decl %2\n" + "jnz 0b\n" + "pop %2\n" + "emms\n" + : "+r" (src0), "+r" (src1) + : "r" (count), "r" (qRGB_COLOR_MASK) + ); +#else + __asm { + movq mm7, qword ptr [qRGB_COLOR_MASK]; + mov eax, src0; + mov ebx, src1; + mov edi, count; + label0: + movq mm0, qword ptr [eax]; // src0 + movq mm1, qword ptr [ebx]; // src1 + movq qword ptr [ebx], mm0; // src1 = src0 + pand mm0, mm7; // color & colorMask + pand mm1, mm7; // src1 & colorMask + psrlw mm0, 1; // (color & colorMask) >> 1 (E) + psrlw mm1, 1; // (src & colorMask) >> 1 (F) + paddw mm0, mm1; // E+F + + movq qword ptr [eax], mm0; // src0 = res + + add eax, 8; + add ebx, 8; + + dec edi; + jnz label0; + mov src0, eax; + mov src1, ebx; + emms; + } +#endif + src0+=2; + src1+=2; + } +} +#endif + +void MotionBlurIB(u8 *srcPtr, u32 srcPitch, int width, int starty, int height) +{ + if(frm1 == NULL) { + Init(); + } + +#ifdef MMX + if(cpu_mmx) { + MotionBlurIB_MMX(srcPtr, srcPitch, width, starty, height); + return; + } +#endif + + u16 colorMask = ~RGB_LOW_BITS_MASK; + + u16 *src0 = (u16 *)srcPtr + starty * srcPitch / 2; + u16 *src1 = (u16 *)frm1 + starty * srcPitch / 2; + + int sPitch = srcPitch >> 1; + + int pos = 0; + for (int j = 0; j < height; j++) + for (int i = 0; i < sPitch; i++) { + u16 color = src0[pos]; + src0[pos] = + (((color & colorMask) >> 1) + ((src1[pos] & colorMask) >> 1)); + src1[pos] = color; + pos++; + } +} + +void MotionBlurIB(u8 *srcPtr, u32 srcPitch, int width, int height) +{ + 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; + + for(int i = 0; i < height; i++) { +#ifdef __GNUC__ + asm volatile ( + "push %2\n" + "movq 0(%3), %%mm7\n" // colorMask + "0:\n" + "movq 0(%0), %%mm0\n" // src0 + "movq 0(%1), %%mm1\n" // src1 + "movq %%mm0, 0(%1)\n" // src1 = src0 + "pand %%mm7, %%mm0\n" // color & colorMask + "pand %%mm7, %%mm1\n" // src1 & colorMask + "psrld $1, %%mm0\n" // (color & colorMask) >> 1 (E) + "psrld $1, %%mm1\n" // (src & colorMask) >> 1 (F) + "paddd %%mm1, %%mm0\n" // E+F + + "movq %%mm0, 0(%0)\n" // src0 = res + + "addl $8, %0\n" + "addl $8, %1\n" + + "decl %2\n" + "jnz 0b\n" + "pop %2\n" + "emms\n" + : "+r" (src0), "+r" (src1) + : "r" (count), "r" (qRGB_COLOR_MASK) + ); +#else + __asm { + movq mm7, qword ptr [qRGB_COLOR_MASK]; + mov eax, src0; + mov ebx, src1; + mov edi, count; + label0: + movq mm0, qword ptr [eax]; // src0 + movq mm1, qword ptr [ebx]; // src1 + movq qword ptr [ebx], mm0; // src1 = src0 + pand mm0, mm7; // color & colorMask + pand mm1, mm7; // src1 & colorMask + psrld mm0, 1; // (color & colorMask) >> 1 (E) + psrld mm1, 1; // (src & colorMask) >> 1 (F) + paddd mm0, mm1; // E+F + + movq qword ptr [eax], mm0; // src0 = res + + add eax, 8; + add ebx, 8; + + dec edi; + jnz label0; + mov src0, eax; + mov src1, ebx; + emms; + } +#endif + src0++; + src1++; + } +} +#endif + +void MotionBlurIB32(u8 *srcPtr, u32 srcPitch, int width, int starty, int height) +{ + if(frm1 == NULL) { + Init(); + } + +#ifdef MMX + if(cpu_mmx) { + MotionBlurIB32_MMX(srcPtr, srcPitch, width, starty, height); + return; + } +#endif + + u32 *src0 = (u32 *)srcPtr + starty * srcPitch / 4; + u32 *src1 = (u32 *)frm1 + starty * srcPitch / 4; + + u32 colorMask = 0xfefefe; + + int sPitch = srcPitch >> 2; + int pos = 0; + + for (int j = 0; j < height; j++) + for (int i = 0; i < sPitch; i++) { + u32 color = src0[pos]; + src0[pos] = (((color & colorMask) >> 1) + + ((src1[pos] & colorMask) >> 1)); + src1[pos] = color; + pos++; + } +} + +void MotionBlurIB32(u8 *srcPtr, u32 srcPitch, int width, int height) +{ + MotionBlurIB32(srcPtr, srcPitch, width, 0, height); +} diff --git a/src/filters/interp.h b/src/filters/interp.h new file mode 100644 index 0000000..57a329c --- /dev/null +++ b/src/filters/interp.h @@ -0,0 +1,302 @@ +/* + * This file is part of the Advance project. + * + * Copyright (C) 2003 Andrea Mazzoleni + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * In addition, as a special exception, Andrea Mazzoleni + * gives permission to link the code of this program with + * the MAME library (or with modified versions of MAME that use the + * same license as MAME), and distribute linked combinations including + * the two. You must obey the GNU General Public License in all + * respects for all of the code used other than MAME. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +#ifndef __INTERP_H +#define __INTERP_H + +#define __STDC_CONSTANT_MACROS + +#include + + +typedef uint16_t interp_uint16; +typedef uint32_t interp_uint32; + +/***************************************************************************/ +/* Basic types */ + +/***************************************************************************/ +/* interpolation */ + +static unsigned interp_mask[2]; +static unsigned interp_bits_per_pixel; + +#define INTERP_16_MASK_1(v) (v & interp_mask[0]) +#define INTERP_16_MASK_2(v) (v & interp_mask[1]) + +static inline u16 interp_16_521(u16 p1, u16 p2, u16 p3) +{ + return INTERP_16_MASK_1((INTERP_16_MASK_1(p1)*5 + INTERP_16_MASK_1(p2)*2 + INTERP_16_MASK_1(p3)*1) / 8) + | INTERP_16_MASK_2((INTERP_16_MASK_2(p1)*5 + INTERP_16_MASK_2(p2)*2 + INTERP_16_MASK_2(p3)*1) / 8); +} + +static inline u16 interp_16_332(u16 p1, u16 p2, u16 p3) +{ + return INTERP_16_MASK_1((INTERP_16_MASK_1(p1)*3 + INTERP_16_MASK_1(p2)*3 + INTERP_16_MASK_1(p3)*2) / 8) + | INTERP_16_MASK_2((INTERP_16_MASK_2(p1)*3 + INTERP_16_MASK_2(p2)*3 + INTERP_16_MASK_2(p3)*2) / 8); +} + +static inline u16 interp_16_611(u16 p1, u16 p2, u16 p3) +{ + return INTERP_16_MASK_1((INTERP_16_MASK_1(p1)*6 + INTERP_16_MASK_1(p2) + INTERP_16_MASK_1(p3)) / 8) + | INTERP_16_MASK_2((INTERP_16_MASK_2(p1)*6 + INTERP_16_MASK_2(p2) + INTERP_16_MASK_2(p3)) / 8); +} + +static inline u16 interp_16_71(u16 p1, u16 p2) +{ + return INTERP_16_MASK_1((INTERP_16_MASK_1(p1)*7 + INTERP_16_MASK_1(p2)) / 8) + | INTERP_16_MASK_2((INTERP_16_MASK_2(p1)*7 + INTERP_16_MASK_2(p2)) / 8); +} + +static inline u16 interp_16_211(u16 p1, u16 p2, u16 p3) +{ + return INTERP_16_MASK_1((INTERP_16_MASK_1(p1)*2 + INTERP_16_MASK_1(p2) + INTERP_16_MASK_1(p3)) / 4) + | INTERP_16_MASK_2((INTERP_16_MASK_2(p1)*2 + INTERP_16_MASK_2(p2) + INTERP_16_MASK_2(p3)) / 4); +} + +static inline u16 interp_16_772(u16 p1, u16 p2, u16 p3) +{ + return INTERP_16_MASK_1(((INTERP_16_MASK_1(p1) + INTERP_16_MASK_1(p2))*7 + INTERP_16_MASK_1(p3)*2) / 16) + | INTERP_16_MASK_2(((INTERP_16_MASK_2(p1) + INTERP_16_MASK_2(p2))*7 + INTERP_16_MASK_2(p3)*2) / 16); +} + +static inline u16 interp_16_11(u16 p1, u16 p2) +{ + return INTERP_16_MASK_1((INTERP_16_MASK_1(p1) + INTERP_16_MASK_1(p2)) / 2) + | INTERP_16_MASK_2((INTERP_16_MASK_2(p1) + INTERP_16_MASK_2(p2)) / 2); +} + +static inline u16 interp_16_31(u16 p1, u16 p2) +{ + return INTERP_16_MASK_1((INTERP_16_MASK_1(p1)*3 + INTERP_16_MASK_1(p2)) / 4) + | INTERP_16_MASK_2((INTERP_16_MASK_2(p1)*3 + INTERP_16_MASK_2(p2)) / 4); +} + +static inline u16 interp_16_1411(u16 p1, u16 p2, u16 p3) +{ + return INTERP_16_MASK_1((INTERP_16_MASK_1(p1)*14 + INTERP_16_MASK_1(p2) + INTERP_16_MASK_1(p3)) / 16) + | INTERP_16_MASK_2((INTERP_16_MASK_2(p1)*14 + INTERP_16_MASK_2(p2) + INTERP_16_MASK_2(p3)) / 16); +} + +static inline u16 interp_16_431(u16 p1, u16 p2, u16 p3) +{ + return INTERP_16_MASK_1((INTERP_16_MASK_1(p1)*4 + INTERP_16_MASK_1(p2)*3 + INTERP_16_MASK_1(p3)) / 8) + | INTERP_16_MASK_2((INTERP_16_MASK_2(p1)*4 + INTERP_16_MASK_2(p2)*3 + INTERP_16_MASK_2(p3)) / 8); +} + +static inline u16 interp_16_53(u16 p1, u16 p2) +{ + return INTERP_16_MASK_1((INTERP_16_MASK_1(p1)*5 + INTERP_16_MASK_1(p2)*3) / 8) + | INTERP_16_MASK_2((INTERP_16_MASK_2(p1)*5 + INTERP_16_MASK_2(p2)*3) / 8); +} + +static inline u16 interp_16_151(u16 p1, u16 p2) +{ + return INTERP_16_MASK_1((INTERP_16_MASK_1(p1)*15 + INTERP_16_MASK_1(p2)) / 16) + | INTERP_16_MASK_2((INTERP_16_MASK_2(p1)*15 + INTERP_16_MASK_2(p2)) / 16); +} + +static inline u16 interp_16_97(u16 p1, u16 p2) +{ + return INTERP_16_MASK_1((INTERP_16_MASK_1(p1)*9 + INTERP_16_MASK_1(p2)*7) / 16) + | INTERP_16_MASK_2((INTERP_16_MASK_2(p1)*9 + INTERP_16_MASK_2(p2)*7) / 16); +} + +#define INTERP_32_MASK_1(v) (v & 0xFF00FF) +#define INTERP_32_MASK_2(v) (v & 0x00FF00) + +static inline u32 interp_32_521(u32 p1, u32 p2, u32 p3) +{ + return INTERP_32_MASK_1((INTERP_32_MASK_1(p1)*5 + INTERP_32_MASK_1(p2)*2 + INTERP_32_MASK_1(p3)*1) / 8) + | INTERP_32_MASK_2((INTERP_32_MASK_2(p1)*5 + INTERP_32_MASK_2(p2)*2 + INTERP_32_MASK_2(p3)*1) / 8); +} + +static inline u32 interp_32_332(u32 p1, u32 p2, u32 p3) +{ + return INTERP_32_MASK_1((INTERP_32_MASK_1(p1)*3 + INTERP_32_MASK_1(p2)*3 + INTERP_32_MASK_1(p3)*2) / 8) + | INTERP_32_MASK_2((INTERP_32_MASK_2(p1)*3 + INTERP_32_MASK_2(p2)*3 + INTERP_32_MASK_2(p3)*2) / 8); +} + +static inline u32 interp_32_211(u32 p1, u32 p2, u32 p3) +{ + return INTERP_32_MASK_1((INTERP_32_MASK_1(p1)*2 + INTERP_32_MASK_1(p2) + INTERP_32_MASK_1(p3)) / 4) + | INTERP_32_MASK_2((INTERP_32_MASK_2(p1)*2 + INTERP_32_MASK_2(p2) + INTERP_32_MASK_2(p3)) / 4); +} + +static inline u32 interp_32_611(u32 p1, u32 p2, u32 p3) +{ + return INTERP_32_MASK_1((INTERP_32_MASK_1(p1)*6 + INTERP_32_MASK_1(p2) + INTERP_32_MASK_1(p3)) / 8) + | INTERP_32_MASK_2((INTERP_32_MASK_2(p1)*6 + INTERP_32_MASK_2(p2) + INTERP_32_MASK_2(p3)) / 8); +} + +static inline u32 interp_32_71(u32 p1, u32 p2) +{ + return INTERP_32_MASK_1((INTERP_32_MASK_1(p1)*7 + INTERP_32_MASK_1(p2)) / 8) + | INTERP_32_MASK_2((INTERP_32_MASK_2(p1)*7 + INTERP_32_MASK_2(p2)) / 8); +} + +static inline u32 interp_32_772(u32 p1, u32 p2, u32 p3) +{ + return INTERP_32_MASK_1(((INTERP_32_MASK_1(p1) + INTERP_32_MASK_1(p2))*7 + INTERP_32_MASK_1(p3)*2) / 16) + | INTERP_32_MASK_2(((INTERP_32_MASK_2(p1) + INTERP_32_MASK_2(p2))*7 + INTERP_32_MASK_2(p3)*2) / 16); +} + +static inline u32 interp_32_11(u32 p1, u32 p2) +{ + return INTERP_32_MASK_1((INTERP_32_MASK_1(p1) + INTERP_32_MASK_1(p2)) / 2) + | INTERP_32_MASK_2((INTERP_32_MASK_2(p1) + INTERP_32_MASK_2(p2)) / 2); +} + +static inline u32 interp_32_31(u32 p1, u32 p2) +{ + return INTERP_32_MASK_1((INTERP_32_MASK_1(p1)*3 + INTERP_32_MASK_1(p2)) / 4) + | INTERP_32_MASK_2((INTERP_32_MASK_2(p1)*3 + INTERP_32_MASK_2(p2)) / 4); +} + +static inline u32 interp_32_1411(u32 p1, u32 p2, u32 p3) +{ + return INTERP_32_MASK_1((INTERP_32_MASK_1(p1)*14 + INTERP_32_MASK_1(p2) + INTERP_32_MASK_1(p3)) / 16) + | INTERP_32_MASK_2((INTERP_32_MASK_2(p1)*14 + INTERP_32_MASK_2(p2) + INTERP_32_MASK_2(p3)) / 16); +} + +static inline u32 interp_32_431(u32 p1, u32 p2, u32 p3) +{ + return INTERP_32_MASK_1((INTERP_32_MASK_1(p1)*4 + INTERP_32_MASK_1(p2)*3 + INTERP_32_MASK_1(p3)) / 8) + | INTERP_32_MASK_2((INTERP_32_MASK_2(p1)*4 + INTERP_32_MASK_2(p2)*3 + INTERP_32_MASK_2(p3)) / 8); +} + +static inline u32 interp_32_53(u32 p1, u32 p2) +{ + return INTERP_32_MASK_1((INTERP_32_MASK_1(p1)*5 + INTERP_32_MASK_1(p2)*3) / 8) + | INTERP_32_MASK_2((INTERP_32_MASK_2(p1)*5 + INTERP_32_MASK_2(p2)*3) / 8); +} + +static inline u32 interp_32_151(u32 p1, u32 p2) +{ + return INTERP_32_MASK_1((INTERP_32_MASK_1(p1)*15 + INTERP_32_MASK_1(p2)) / 16) + | INTERP_32_MASK_2((INTERP_32_MASK_2(p1)*15 + INTERP_32_MASK_2(p2)) / 16); +} + +static inline u32 interp_32_97(u32 p1, u32 p2) +{ + return INTERP_32_MASK_1((INTERP_32_MASK_1(p1)*9 + INTERP_32_MASK_1(p2)*7) / 16) + | INTERP_32_MASK_2((INTERP_32_MASK_2(p1)*9 + INTERP_32_MASK_2(p2)*7) / 16); +} + +/***************************************************************************/ +/* diff */ + +#define INTERP_Y_LIMIT (0x30*4) +#define INTERP_U_LIMIT (0x07*4) +#define INTERP_V_LIMIT (0x06*8) + +static int interp_16_diff(u16 p1, u16 p2) +{ + int r, g, b; + int y, u, v; + + if (p1 == p2) + return 0; + + if (interp_bits_per_pixel == 16) { + b = (int)((p1 & 0x1F) - (p2 & 0x1F)) << 3; + g = (int)((p1 & 0x7E0) - (p2 & 0x7E0)) >> 3; + r = (int)((p1 & 0xF800) - (p2 & 0xF800)) >> 8; + } else { + b = (int)((p1 & 0x1F) - (p2 & 0x1F)) << 3; + g = (int)((p1 & 0x3E0) - (p2 & 0x3E0)) >> 2; + r = (int)((p1 & 0x7C00) - (p2 & 0x7C00)) >> 7; + } + + y = r + g + b; + u = r - b; + v = -r + 2*g - b; + + if (y < -INTERP_Y_LIMIT || y > INTERP_Y_LIMIT) + return 1; + + if (u < -INTERP_U_LIMIT || u > INTERP_U_LIMIT) + return 1; + + if (v < -INTERP_V_LIMIT || v > INTERP_V_LIMIT) + return 1; + + return 0; +} + +static int interp_32_diff(u32 p1, u32 p2) +{ + int r, g, b; + int y, u, v; + + if ((p1 & 0xF8F8F8) == (p2 & 0xF8F8F8)) + return 0; + + b = (int)((p1 & 0xFF) - (p2 & 0xFF)); + g = (int)((p1 & 0xFF00) - (p2 & 0xFF00)) >> 8; + r = (int)((p1 & 0xFF0000) - (p2 & 0xFF0000)) >> 16; + + y = r + g + b; + u = r - b; + v = -r + 2*g - b; + + if (y < -INTERP_Y_LIMIT || y > INTERP_Y_LIMIT) + return 1; + + if (u < -INTERP_U_LIMIT || u > INTERP_U_LIMIT) + return 1; + + if (v < -INTERP_V_LIMIT || v > INTERP_V_LIMIT) + return 1; + + return 0; +} + +static void interp_set(unsigned bits_per_pixel) +{ + interp_bits_per_pixel = bits_per_pixel; + + switch (bits_per_pixel) { + case 15 : + interp_mask[0] = 0x7C1F; + interp_mask[1] = 0x03E0; + break; + case 16 : + interp_mask[0] = 0xF81F; + interp_mask[1] = 0x07E0; + break; + case 32 : + interp_mask[0] = 0xFF00FF; + interp_mask[1] = 0x00FF00; + break; + } +} + +#endif diff --git a/src/filters/lq2x.h b/src/filters/lq2x.h new file mode 100644 index 0000000..094c2b5 --- /dev/null +++ b/src/filters/lq2x.h @@ -0,0 +1,1284 @@ +case 0 : +case 2 : +case 4 : +case 6 : +case 8 : +case 12 : +case 16 : +case 20 : +case 24 : +case 28 : +case 32 : +case 34 : +case 36 : +case 38 : +case 40 : +case 44 : +case 48 : +case 52 : +case 56 : +case 60 : +case 64 : +case 66 : +case 68 : +case 70 : +case 96 : +case 98 : +case 100 : +case 102 : +case 128 : +case 130 : +case 132 : +case 134 : +case 136 : +case 140 : +case 144 : +case 148 : +case 152 : +case 156 : +case 160 : +case 162 : +case 164 : +case 166 : +case 168 : +case 172 : +case 176 : +case 180 : +case 184 : +case 188 : +case 192 : +case 194 : +case 196 : +case 198 : +case 224 : +case 226 : +case 228 : +case 230 : +{ + P0 = IC(0); + P1 = IC(0); + P2 = IC(0); + P3 = IC(0); +} break; +case 1 : +case 5 : +case 9 : +case 13 : +case 17 : +case 21 : +case 25 : +case 29 : +case 33 : +case 37 : +case 41 : +case 45 : +case 49 : +case 53 : +case 57 : +case 61 : +case 65 : +case 69 : +case 97 : +case 101 : +case 129 : +case 133 : +case 137 : +case 141 : +case 145 : +case 149 : +case 153 : +case 157 : +case 161 : +case 165 : +case 169 : +case 173 : +case 177 : +case 181 : +case 185 : +case 189 : +case 193 : +case 197 : +case 225 : +case 229 : +{ + P0 = IC(1); + P1 = IC(1); + P2 = IC(1); + P3 = IC(1); +} break; +case 3 : +case 35 : +case 67 : +case 99 : +case 131 : +case 163 : +case 195 : +case 227 : +{ + P0 = IC(2); + P1 = IC(2); + P2 = IC(2); + P3 = IC(2); +} break; +case 7 : +case 39 : +case 71 : +case 103 : +case 135 : +case 167 : +case 199 : +case 231 : +{ + P0 = IC(3); + P1 = IC(3); + P2 = IC(3); + P3 = IC(3); +} break; +case 10 : +case 138 : +{ + P1 = IC(0); + P2 = IC(0); + P3 = IC(0); + if (MUL) { + P0 = IC(0); + } else { + P0 = I211(0, 1, 3); + } +} break; +case 11 : +case 27 : +case 75 : +case 139 : +case 155 : +case 203 : +{ + P1 = IC(2); + P2 = IC(2); + P3 = IC(2); + if (MUL) { + P0 = IC(2); + } else { + P0 = I211(2, 1, 3); + } +} break; +case 14 : +case 142 : +{ + P2 = IC(0); + P3 = IC(0); + if (MUL) { + P0 = IC(0); + P1 = IC(0); + } else { + P0 = I332(1, 3, 0); + P1 = I31(0, 1); + } +} break; +case 15 : +case 143 : +case 207 : +{ + P2 = IC(4); + P3 = IC(4); + if (MUL) { + P0 = IC(4); + P1 = IC(4); + } else { + P0 = I332(1, 3, 4); + P1 = I31(4, 1); + } +} break; +case 18 : +case 22 : +case 30 : +case 50 : +case 54 : +case 62 : +case 86 : +case 118 : +{ + P0 = IC(0); + P2 = IC(0); + P3 = IC(0); + if (MUR) { + P1 = IC(0); + } else { + P1 = I211(0, 1, 5); + } +} break; +case 19 : +case 51 : +{ + P2 = IC(2); + P3 = IC(2); + if (MUR) { + P0 = IC(2); + P1 = IC(2); + } else { + P0 = I31(2, 1); + P1 = I332(1, 5, 2); + } +} break; +case 23 : +case 55 : +case 119 : +{ + P2 = IC(3); + P3 = IC(3); + if (MUR) { + P0 = IC(3); + P1 = IC(3); + } else { + P0 = I31(3, 1); + P1 = I332(1, 5, 3); + } +} break; +case 26 : +{ + P2 = IC(0); + P3 = IC(0); + if (MUL) { + P0 = IC(0); + } else { + P0 = I211(0, 1, 3); + } + if (MUR) { + P1 = IC(0); + } else { + P1 = I211(0, 1, 5); + } +} break; +case 31 : +case 95 : +{ + P2 = IC(4); + P3 = IC(4); + if (MUL) { + P0 = IC(4); + } else { + P0 = I211(4, 1, 3); + } + if (MUR) { + P1 = IC(4); + } else { + P1 = I211(4, 1, 5); + } +} break; +case 42 : +case 170 : +{ + P1 = IC(0); + P3 = IC(0); + if (MUL) { + P0 = IC(0); + P2 = IC(0); + } else { + P0 = I332(1, 3, 0); + P2 = I31(0, 3); + } +} break; +case 43 : +case 171 : +case 187 : +{ + P1 = IC(2); + P3 = IC(2); + if (MUL) { + P0 = IC(2); + P2 = IC(2); + } else { + P0 = I332(1, 3, 2); + P2 = I31(2, 3); + } +} break; +case 46 : +case 174 : +{ + P1 = IC(0); + P2 = IC(0); + P3 = IC(0); + if (MUL) { + P0 = IC(0); + } else { + P0 = I611(0, 1, 3); + } +} break; +case 47 : +case 175 : +{ + P1 = IC(4); + P2 = IC(4); + P3 = IC(4); + if (MUL) { + P0 = IC(4); + } else { + P0 = I1411(4, 1, 3); + } +} break; +case 58 : +case 154 : +case 186 : +{ + P2 = IC(0); + P3 = IC(0); + if (MUL) { + P0 = IC(0); + } else { + P0 = I611(0, 1, 3); + } + if (MUR) { + P1 = IC(0); + } else { + P1 = I611(0, 1, 5); + } +} break; +case 59 : +{ + P2 = IC(2); + P3 = IC(2); + if (MUL) { + P0 = IC(2); + } else { + P0 = I211(2, 1, 3); + } + if (MUR) { + P1 = IC(2); + } else { + P1 = I611(2, 1, 5); + } +} break; +case 63 : +{ + P2 = IC(4); + P3 = IC(4); + if (MUL) { + P0 = IC(4); + } else { + P0 = I1411(4, 1, 3); + } + if (MUR) { + P1 = IC(4); + } else { + P1 = I211(4, 1, 5); + } +} break; +case 72 : +case 76 : +case 104 : +case 106 : +case 108 : +case 110 : +case 120 : +case 124 : +{ + P0 = IC(0); + P1 = IC(0); + P3 = IC(0); + if (MDL) { + P2 = IC(0); + } else { + P2 = I211(0, 3, 7); + } +} break; +case 73 : +case 77 : +case 105 : +case 109 : +case 125 : +{ + P1 = IC(1); + P3 = IC(1); + if (MDL) { + P0 = IC(1); + P2 = IC(1); + } else { + P0 = I31(1, 3); + P2 = I332(3, 7, 1); + } +} break; +case 74 : +{ + P1 = IC(0); + P3 = IC(0); + if (MDL) { + P2 = IC(0); + } else { + P2 = I211(0, 3, 7); + } + if (MUL) { + P0 = IC(0); + } else { + P0 = I211(0, 1, 3); + } +} break; +case 78 : +case 202 : +case 206 : +{ + P1 = IC(0); + P3 = IC(0); + if (MDL) { + P2 = IC(0); + } else { + P2 = I611(0, 3, 7); + } + if (MUL) { + P0 = IC(0); + } else { + P0 = I611(0, 1, 3); + } +} break; +case 79 : +{ + P1 = IC(4); + P3 = IC(4); + if (MDL) { + P2 = IC(4); + } else { + P2 = I611(4, 3, 7); + } + if (MUL) { + P0 = IC(4); + } else { + P0 = I211(4, 1, 3); + } +} break; +case 80 : +case 208 : +case 210 : +case 216 : +{ + P0 = IC(0); + P1 = IC(0); + P2 = IC(0); + if (MDR) { + P3 = IC(0); + } else { + P3 = I211(0, 5, 7); + } +} break; +case 81 : +case 209 : +case 217 : +{ + P0 = IC(1); + P1 = IC(1); + P2 = IC(1); + if (MDR) { + P3 = IC(1); + } else { + P3 = I211(1, 5, 7); + } +} break; +case 82 : +case 214 : +case 222 : +{ + P0 = IC(0); + P2 = IC(0); + if (MDR) { + P3 = IC(0); + } else { + P3 = I211(0, 5, 7); + } + if (MUR) { + P1 = IC(0); + } else { + P1 = I211(0, 1, 5); + } +} break; +case 83 : +case 115 : +{ + P0 = IC(2); + P2 = IC(2); + if (MDR) { + P3 = IC(2); + } else { + P3 = I611(2, 5, 7); + } + if (MUR) { + P1 = IC(2); + } else { + P1 = I611(2, 1, 5); + } +} break; +case 84 : +case 212 : +{ + P0 = IC(0); + P2 = IC(0); + if (MDR) { + P1 = IC(0); + P3 = IC(0); + } else { + P1 = I31(0, 5); + P3 = I332(5, 7, 0); + } +} break; +case 85 : +case 213 : +case 221 : +{ + P0 = IC(1); + P2 = IC(1); + if (MDR) { + P1 = IC(1); + P3 = IC(1); + } else { + P1 = I31(1, 5); + P3 = I332(5, 7, 1); + } +} break; +case 87 : +{ + P0 = IC(3); + P2 = IC(3); + if (MDR) { + P3 = IC(3); + } else { + P3 = I611(3, 5, 7); + } + if (MUR) { + P1 = IC(3); + } else { + P1 = I211(3, 1, 5); + } +} break; +case 88 : +case 248 : +case 250 : +{ + P0 = IC(0); + P1 = IC(0); + if (MDL) { + P2 = IC(0); + } else { + P2 = I211(0, 3, 7); + } + if (MDR) { + P3 = IC(0); + } else { + P3 = I211(0, 5, 7); + } +} break; +case 89 : +case 93 : +{ + P0 = IC(1); + P1 = IC(1); + if (MDL) { + P2 = IC(1); + } else { + P2 = I611(1, 3, 7); + } + if (MDR) { + P3 = IC(1); + } else { + P3 = I611(1, 5, 7); + } +} break; +case 90 : +{ + if (MDL) { + P2 = IC(0); + } else { + P2 = I611(0, 3, 7); + } + if (MDR) { + P3 = IC(0); + } else { + P3 = I611(0, 5, 7); + } + if (MUL) { + P0 = IC(0); + } else { + P0 = I611(0, 1, 3); + } + if (MUR) { + P1 = IC(0); + } else { + P1 = I611(0, 1, 5); + } +} break; +case 91 : +{ + if (MDL) { + P2 = IC(2); + } else { + P2 = I611(2, 3, 7); + } + if (MDR) { + P3 = IC(2); + } else { + P3 = I611(2, 5, 7); + } + if (MUL) { + P0 = IC(2); + } else { + P0 = I211(2, 1, 3); + } + if (MUR) { + P1 = IC(2); + } else { + P1 = I611(2, 1, 5); + } +} break; +case 92 : +{ + P0 = IC(0); + P1 = IC(0); + if (MDL) { + P2 = IC(0); + } else { + P2 = I611(0, 3, 7); + } + if (MDR) { + P3 = IC(0); + } else { + P3 = I611(0, 5, 7); + } +} break; +case 94 : +{ + if (MDL) { + P2 = IC(0); + } else { + P2 = I611(0, 3, 7); + } + if (MDR) { + P3 = IC(0); + } else { + P3 = I611(0, 5, 7); + } + if (MUL) { + P0 = IC(0); + } else { + P0 = I611(0, 1, 3); + } + if (MUR) { + P1 = IC(0); + } else { + P1 = I211(0, 1, 5); + } +} break; +case 107 : +case 123 : +{ + P1 = IC(2); + P3 = IC(2); + if (MDL) { + P2 = IC(2); + } else { + P2 = I211(2, 3, 7); + } + if (MUL) { + P0 = IC(2); + } else { + P0 = I211(2, 1, 3); + } +} break; +case 111 : +{ + P1 = IC(4); + P3 = IC(4); + if (MDL) { + P2 = IC(4); + } else { + P2 = I211(4, 3, 7); + } + if (MUL) { + P0 = IC(4); + } else { + P0 = I1411(4, 1, 3); + } +} break; +case 112 : +case 240 : +{ + P0 = IC(0); + P1 = IC(0); + if (MDR) { + P2 = IC(0); + P3 = IC(0); + } else { + P2 = I31(0, 7); + P3 = I332(5, 7, 0); + } +} break; +case 113 : +case 241 : +{ + P0 = IC(1); + P1 = IC(1); + if (MDR) { + P2 = IC(1); + P3 = IC(1); + } else { + P2 = I31(1, 7); + P3 = I332(5, 7, 1); + } +} break; +case 114 : +{ + P0 = IC(0); + P2 = IC(0); + if (MDR) { + P3 = IC(0); + } else { + P3 = I611(0, 5, 7); + } + if (MUR) { + P1 = IC(0); + } else { + P1 = I611(0, 1, 5); + } +} break; +case 116 : +{ + P0 = IC(0); + P1 = IC(0); + P2 = IC(0); + if (MDR) { + P3 = IC(0); + } else { + P3 = I611(0, 5, 7); + } +} break; +case 117 : +{ + P0 = IC(1); + P1 = IC(1); + P2 = IC(1); + if (MDR) { + P3 = IC(1); + } else { + P3 = I611(1, 5, 7); + } +} break; +case 121 : +{ + P0 = IC(1); + P1 = IC(1); + if (MDL) { + P2 = IC(1); + } else { + P2 = I211(1, 3, 7); + } + if (MDR) { + P3 = IC(1); + } else { + P3 = I611(1, 5, 7); + } +} break; +case 122 : +{ + if (MDL) { + P2 = IC(0); + } else { + P2 = I211(0, 3, 7); + } + if (MDR) { + P3 = IC(0); + } else { + P3 = I611(0, 5, 7); + } + if (MUL) { + P0 = IC(0); + } else { + P0 = I611(0, 1, 3); + } + if (MUR) { + P1 = IC(0); + } else { + P1 = I611(0, 1, 5); + } +} break; +case 126 : +{ + P0 = IC(0); + P3 = IC(0); + if (MDL) { + P2 = IC(0); + } else { + P2 = I211(0, 3, 7); + } + if (MUR) { + P1 = IC(0); + } else { + P1 = I211(0, 1, 5); + } +} break; +case 127 : +{ + P3 = IC(4); + if (MDL) { + P2 = IC(4); + } else { + P2 = I211(4, 3, 7); + } + if (MUL) { + P0 = IC(4); + } else { + P0 = I1411(4, 1, 3); + } + if (MUR) { + P1 = IC(4); + } else { + P1 = I211(4, 1, 5); + } +} break; +case 146 : +case 150 : +case 178 : +case 182 : +case 190 : +{ + P0 = IC(0); + P2 = IC(0); + if (MUR) { + P1 = IC(0); + P3 = IC(0); + } else { + P1 = I332(1, 5, 0); + P3 = I31(0, 5); + } +} break; +case 147 : +case 179 : +{ + P0 = IC(2); + P2 = IC(2); + P3 = IC(2); + if (MUR) { + P1 = IC(2); + } else { + P1 = I611(2, 1, 5); + } +} break; +case 151 : +case 183 : +{ + P0 = IC(3); + P2 = IC(3); + P3 = IC(3); + if (MUR) { + P1 = IC(3); + } else { + P1 = I1411(3, 1, 5); + } +} break; +case 158 : +{ + P2 = IC(0); + P3 = IC(0); + if (MUL) { + P0 = IC(0); + } else { + P0 = I611(0, 1, 3); + } + if (MUR) { + P1 = IC(0); + } else { + P1 = I211(0, 1, 5); + } +} break; +case 159 : +{ + P2 = IC(4); + P3 = IC(4); + if (MUL) { + P0 = IC(4); + } else { + P0 = I211(4, 1, 3); + } + if (MUR) { + P1 = IC(4); + } else { + P1 = I1411(4, 1, 5); + } +} break; +case 191 : +{ + P2 = IC(4); + P3 = IC(4); + if (MUL) { + P0 = IC(4); + } else { + P0 = I1411(4, 1, 3); + } + if (MUR) { + P1 = IC(4); + } else { + P1 = I1411(4, 1, 5); + } +} break; +case 200 : +case 204 : +case 232 : +case 236 : +case 238 : +{ + P0 = IC(0); + P1 = IC(0); + if (MDL) { + P2 = IC(0); + P3 = IC(0); + } else { + P2 = I332(3, 7, 0); + P3 = I31(0, 7); + } +} break; +case 201 : +case 205 : +{ + P0 = IC(1); + P1 = IC(1); + P3 = IC(1); + if (MDL) { + P2 = IC(1); + } else { + P2 = I611(1, 3, 7); + } +} break; +case 211 : +{ + P0 = IC(2); + P1 = IC(2); + P2 = IC(2); + if (MDR) { + P3 = IC(2); + } else { + P3 = I211(2, 5, 7); + } +} break; +case 215 : +{ + P0 = IC(3); + P2 = IC(3); + if (MDR) { + P3 = IC(3); + } else { + P3 = I211(3, 5, 7); + } + if (MUR) { + P1 = IC(3); + } else { + P1 = I1411(3, 1, 5); + } +} break; +case 218 : +{ + if (MDL) { + P2 = IC(0); + } else { + P2 = I611(0, 3, 7); + } + if (MDR) { + P3 = IC(0); + } else { + P3 = I211(0, 5, 7); + } + if (MUL) { + P0 = IC(0); + } else { + P0 = I611(0, 1, 3); + } + if (MUR) { + P1 = IC(0); + } else { + P1 = I611(0, 1, 5); + } +} break; +case 219 : +{ + P1 = IC(2); + P2 = IC(2); + if (MDR) { + P3 = IC(2); + } else { + P3 = I211(2, 5, 7); + } + if (MUL) { + P0 = IC(2); + } else { + P0 = I211(2, 1, 3); + } +} break; +case 220 : +{ + P0 = IC(0); + P1 = IC(0); + if (MDL) { + P2 = IC(0); + } else { + P2 = I611(0, 3, 7); + } + if (MDR) { + P3 = IC(0); + } else { + P3 = I211(0, 5, 7); + } +} break; +case 223 : +{ + P2 = IC(4); + if (MDR) { + P3 = IC(4); + } else { + P3 = I211(4, 5, 7); + } + if (MUL) { + P0 = IC(4); + } else { + P0 = I211(4, 1, 3); + } + if (MUR) { + P1 = IC(4); + } else { + P1 = I1411(4, 1, 5); + } +} break; +case 233 : +case 237 : +{ + P0 = IC(1); + P1 = IC(1); + P3 = IC(1); + if (MDL) { + P2 = IC(1); + } else { + P2 = I1411(1, 3, 7); + } +} break; +case 234 : +{ + P1 = IC(0); + P3 = IC(0); + if (MDL) { + P2 = IC(0); + } else { + P2 = I211(0, 3, 7); + } + if (MUL) { + P0 = IC(0); + } else { + P0 = I611(0, 1, 3); + } +} break; +case 235 : +{ + P1 = IC(2); + P3 = IC(2); + if (MDL) { + P2 = IC(2); + } else { + P2 = I1411(2, 3, 7); + } + if (MUL) { + P0 = IC(2); + } else { + P0 = I211(2, 1, 3); + } +} break; +case 239 : +{ + P1 = IC(4); + P3 = IC(4); + if (MDL) { + P2 = IC(4); + } else { + P2 = I1411(4, 3, 7); + } + if (MUL) { + P0 = IC(4); + } else { + P0 = I1411(4, 1, 3); + } +} break; +case 242 : +{ + P0 = IC(0); + P2 = IC(0); + if (MDR) { + P3 = IC(0); + } else { + P3 = I211(0, 5, 7); + } + if (MUR) { + P1 = IC(0); + } else { + P1 = I611(0, 1, 5); + } +} break; +case 243 : +{ + P0 = IC(2); + P1 = IC(2); + if (MDR) { + P2 = IC(2); + P3 = IC(2); + } else { + P2 = I31(2, 7); + P3 = I332(5, 7, 2); + } +} break; +case 244 : +{ + P0 = IC(0); + P1 = IC(0); + P2 = IC(0); + if (MDR) { + P3 = IC(0); + } else { + P3 = I1411(0, 5, 7); + } +} break; +case 245 : +{ + P0 = IC(1); + P1 = IC(1); + P2 = IC(1); + if (MDR) { + P3 = IC(1); + } else { + P3 = I1411(1, 5, 7); + } +} break; +case 246 : +{ + P0 = IC(0); + P2 = IC(0); + if (MDR) { + P3 = IC(0); + } else { + P3 = I1411(0, 5, 7); + } + if (MUR) { + P1 = IC(0); + } else { + P1 = I211(0, 1, 5); + } +} break; +case 247 : +{ + P0 = IC(3); + P2 = IC(3); + if (MDR) { + P3 = IC(3); + } else { + P3 = I1411(3, 5, 7); + } + if (MUR) { + P1 = IC(3); + } else { + P1 = I1411(3, 1, 5); + } +} break; +case 249 : +{ + P0 = IC(1); + P1 = IC(1); + if (MDL) { + P2 = IC(1); + } else { + P2 = I1411(1, 3, 7); + } + if (MDR) { + P3 = IC(1); + } else { + P3 = I211(1, 5, 7); + } +} break; +case 251 : +{ + P1 = IC(2); + if (MDL) { + P2 = IC(2); + } else { + P2 = I1411(2, 3, 7); + } + if (MDR) { + P3 = IC(2); + } else { + P3 = I211(2, 5, 7); + } + if (MUL) { + P0 = IC(2); + } else { + P0 = I211(2, 1, 3); + } +} break; +case 252 : +{ + P0 = IC(0); + P1 = IC(0); + if (MDL) { + P2 = IC(0); + } else { + P2 = I211(0, 3, 7); + } + if (MDR) { + P3 = IC(0); + } else { + P3 = I1411(0, 5, 7); + } +} break; +case 253 : +{ + P0 = IC(1); + P1 = IC(1); + if (MDL) { + P2 = IC(1); + } else { + P2 = I1411(1, 3, 7); + } + if (MDR) { + P3 = IC(1); + } else { + P3 = I1411(1, 5, 7); + } +} break; +case 254 : +{ + P0 = IC(0); + if (MDL) { + P2 = IC(0); + } else { + P2 = I211(0, 3, 7); + } + if (MDR) { + P3 = IC(0); + } else { + P3 = I1411(0, 5, 7); + } + if (MUR) { + P1 = IC(0); + } else { + P1 = I211(0, 1, 5); + } +} break; +case 255 : +{ + if (MDL) { + P2 = IC(4); + } else { + P2 = I1411(4, 3, 7); + } + if (MDR) { + P3 = IC(4); + } else { + P3 = I1411(4, 5, 7); + } + if (MUL) { + P0 = IC(4); + } else { + P0 = I1411(4, 1, 3); + } + if (MUR) { + P1 = IC(4); + } else { + P1 = I1411(4, 1, 5); + } +} break; diff --git a/src/filters/pixel.cpp b/src/filters/pixel.cpp new file mode 100644 index 0000000..079d66c --- /dev/null +++ b/src/filters/pixel.cpp @@ -0,0 +1,132 @@ +#include "../System.h" + +extern int RGB_LOW_BITS_MASK; + +void Pixelate(u8 *srcPtr, u32 srcPitch, u8 *deltaPtr, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u8 *nextLine, *finish; + u32 colorMask = ~(RGB_LOW_BITS_MASK | (RGB_LOW_BITS_MASK << 16)); + + nextLine = dstPtr + dstPitch; + + do { + u32 *bP = (u32 *) srcPtr; + u32 *xP = (u32 *) deltaPtr; + u32 *dP = (u32 *) dstPtr; + u32 *nL = (u32 *) nextLine; + u32 currentPixel; + u32 nextPixel; + u32 currentDelta; + u32 nextDelta; + + finish = (u8 *) bP + ((width+2) << 1); + nextPixel = *bP++; + nextDelta = *xP++; + + do { + currentPixel = nextPixel; + currentDelta = nextDelta; + nextPixel = *bP++; + nextDelta = *xP++; + + if ((nextPixel != nextDelta) || (currentPixel != currentDelta)) { + u32 colorA, colorB, product; + + *(xP - 2) = currentPixel; +#ifdef WORDS_BIGENDIAN + colorA = currentPixel >> 16; + colorB = currentPixel & 0xffff; +#else + colorA = currentPixel & 0xffff; + colorB = currentPixel >> 16; +#endif + product = (((colorA & colorMask) >> 1) & colorMask) >> 1; + +#ifdef WORDS_BIGENDIAN + *(nL) = (product << 16) | (product); + *(dP) = (colorA << 16) | product; +#else + *(nL) = product | (product << 16); + *(dP) = colorA | (product << 16); +#endif + +#ifdef WORDS_BIGENDIAN + colorA = nextPixel >> 16; +#else + colorA = nextPixel & 0xffff; +#endif + product = (((colorB & colorMask) >> 1) & colorMask) >> 1; +#ifdef WORDS_BIGENDIAN + *(nL + 1) = (product << 16) | (product); + *(dP + 1) = (colorB << 16) | (product); +#else + *(nL + 1) = (product) | (product << 16); + *(dP + 1) = (colorB) | (product << 16); +#endif + } + + dP += 2; + nL += 2; + } while ((u8 *) bP < finish); + + deltaPtr += srcPitch; + srcPtr += srcPitch; + dstPtr += dstPitch << 1; + nextLine += dstPitch << 1; + } + while (--height); +} + +void Pixelate32(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u8 *nextLine, *finish; + u32 colorMask = ~RGB_LOW_BITS_MASK; + + nextLine = dstPtr + dstPitch; + + do { + u32 *bP = (u32 *) srcPtr; + // u32 *xP = (u32 *) deltaPtr; + u32 *dP = (u32 *) dstPtr; + u32 *nL = (u32 *) nextLine; + u32 currentPixel; + u32 nextPixel; + + finish = (u8 *) bP + ((width+1) << 2); + nextPixel = *bP++; + + do { + currentPixel = nextPixel; + nextPixel = *bP++; + + u32 colorA, colorB, product; + + colorA = currentPixel; + colorB = nextPixel; + + product = (((colorA & colorMask) >> 1) & colorMask) >> 1; + *(nL) = product; + *(nL+1) = product; + *(dP) = colorA; + *(dP+1) = product; + + nextPixel = *bP++; + colorA = nextPixel; + product = (((colorB & colorMask) >> 1) & colorMask) >> 1; + *(nL + 2) = product; + *(nL + 3) = product; + *(dP + 2) = colorB; + *(dP + 3) = product; + + dP += 4; + nL += 4; + } while ((u8 *) bP < finish); + + srcPtr += srcPitch; + dstPtr += dstPitch << 1; + nextLine += dstPitch << 1; + } + while (--height); +} diff --git a/src/filters/scanline.cpp b/src/filters/scanline.cpp new file mode 100644 index 0000000..8341157 --- /dev/null +++ b/src/filters/scanline.cpp @@ -0,0 +1,212 @@ +#include "../System.h" + +extern int RGB_LOW_BITS_MASK; + +void Scanlines (u8 *srcPtr, u32 srcPitch, u8 *, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u8 *nextLine, *finish; + + nextLine = dstPtr + dstPitch; + + do { + u32 *bP = (u32 *) srcPtr; + u32 *dP = (u32 *) dstPtr; + u32 *nL = (u32 *) nextLine; + u32 currentPixel; + u32 nextPixel; + + finish = (u8 *) bP + ((width+2) << 1); + nextPixel = *bP++; + + do { + currentPixel = nextPixel; + nextPixel = *bP++; + u32 colorA, colorB; + +#ifdef WORDS_BIGENDIAN + colorA = currentPixel >> 16; + colorB = currentPixel & 0xffff; +#else + colorA = currentPixel & 0xffff; + colorB = currentPixel >> 16; +#endif + + *(dP) = colorA | colorA<<16; + *(nL) = 0; + +#ifdef WORDS_BIGENDIAN + colorA = nextPixel >> 16; +#else + colorA = nextPixel & 0xffff; +#endif + + *(dP + 1) = colorB | (colorB << 16); + *(nL + 1) = 0; + + dP += 2; + nL += 2; + } while ((u8 *) bP < finish); + + srcPtr += srcPitch; + dstPtr += dstPitch << 1; + nextLine += dstPitch << 1; + } + while (--height); +} + +void Scanlines32(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u8 *nextLine, *finish; + + nextLine = dstPtr + dstPitch; + + do { + u32 *bP = (u32 *) srcPtr; + u32 *dP = (u32 *) dstPtr; + u32 *nL = (u32 *) nextLine; + u32 currentPixel; + u32 nextPixel; + + finish = (u8 *) bP + ((width+1) << 2); + nextPixel = *bP++; + + do { + currentPixel = nextPixel; + nextPixel = *bP++; + + u32 colorA, colorB; + + colorA = currentPixel; + colorB = nextPixel; + + *(dP) = colorA; + *(dP+1) = colorA; + *(nL) = 0; + *(nL+1) = 0; + + *(dP + 2) = colorB; + *(dP + 3) = colorB; + *(nL+2) = 0; + *(nL+3) = 0; + + nextPixel = *bP++; + + dP += 4; + nL += 4; + } while ((u8 *) bP < finish); + + srcPtr += srcPitch; + dstPtr += dstPitch << 1; + nextLine += dstPitch << 1; + } + while (--height); +} + +void ScanlinesTV(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u8 *nextLine, *finish; + u32 colorMask = ~(RGB_LOW_BITS_MASK | (RGB_LOW_BITS_MASK << 16)); + + nextLine = dstPtr + dstPitch; + + do { + u32 *bP = (u32 *) srcPtr; + u32 *dP = (u32 *) dstPtr; + u32 *nL = (u32 *) nextLine; + u32 currentPixel; + u32 nextPixel; + + finish = (u8 *) bP + ((width+2) << 1); + nextPixel = *bP++; + + do { + currentPixel = nextPixel; + nextPixel = *bP++; + + u32 colorA, colorB; + +#ifdef WORDS_BIGENDIAN + colorA = currentPixel >> 16; + colorB = currentPixel & 0xFFFF; +#else + colorA = currentPixel & 0xFFFF; + colorB = currentPixel >> 16; +#endif + + *(dP) = colorA = colorA | ((((colorA & colorMask) >> 1) + + ((colorB & colorMask) >> 1))) << 16; + colorA = ((colorA & colorMask) >> 1); + colorA += ((colorA & colorMask) >> 1); + *(nL) = colorA; + + colorA = nextPixel & 0xFFFF; + + *(dP + 1) = colorB = colorB | ((((colorA & colorMask) >> 1) + + ((colorB & colorMask) >> 1))) << 16; + colorB = ((colorB & colorMask) >> 1); + colorB += ((colorB & colorMask) >> 1); + + *(nL + 1) = colorB; + + dP += 2; + nL += 2; + } while ((u8 *) bP < finish); + + srcPtr += srcPitch; + dstPtr += dstPitch << 1; + nextLine += dstPitch << 1; + } + while (--height); +} + +void ScanlinesTV32(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u8 *nextLine, *finish; + u32 colorMask = ~RGB_LOW_BITS_MASK; + + nextLine = dstPtr + dstPitch; + + do { + u32 *bP = (u32 *) srcPtr; + u32 *dP = (u32 *) dstPtr; + u32 *nL = (u32 *) nextLine; + u32 currentPixel; + u32 nextPixel; + + finish = (u8 *) bP + ((width+1) << 2); + nextPixel = *bP++; + + do { + currentPixel = nextPixel; + nextPixel = *bP++; + + u32 colorA, colorB, temp; + + colorA = currentPixel; + colorB = nextPixel; + + *(dP) = colorA; + *(dP+1) = temp = ((colorA & colorMask) >> 1) + + ((colorB & colorMask) >> 1); + temp = ((temp & colorMask) >> 1); + temp += ((temp & colorMask) >> 1); + colorA = ((colorA & colorMask) >> 1); + colorA += ((colorA & colorMask) >> 1); + + *(nL) = colorA; + *(nL+1) = temp; + + dP += 2; + nL += 2; + } while ((u8 *) bP < finish); + + srcPtr += srcPitch; + dstPtr += dstPitch << 1; + nextLine += dstPitch << 1; + } + while (--height); +} diff --git a/src/filters/simpleFilter.cpp b/src/filters/simpleFilter.cpp new file mode 100644 index 0000000..05260a0 --- /dev/null +++ b/src/filters/simpleFilter.cpp @@ -0,0 +1,285 @@ +#include "../System.h" + +void Simple2x16(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u8 *nextLine, *finish; + + nextLine = dstPtr + dstPitch; + + do { + u32 *bP = (u32 *) srcPtr; + u32 *dP = (u32 *) dstPtr; + u32 *nL = (u32 *) nextLine; + u32 currentPixel; + + finish = (u8 *) bP + ((width+2) << 1); + currentPixel = *bP++; + + do { +#ifdef WORDS_BIGENDIAN + u32 color = currentPixel >> 16; +#else + u32 color = currentPixel & 0xffff; +#endif + + color = color | (color << 16); + + *(dP) = color; + *(nL) = color; + +#ifdef WORDS_BIGENDIAN + color = currentPixel & 0xffff; +#else + color = currentPixel >> 16; +#endif + color = color| (color << 16); + *(dP + 1) = color; + *(nL + 1) = color; + + currentPixel = *bP++; + + dP += 2; + nL += 2; + } while ((u8 *) bP < finish); + + srcPtr += srcPitch; + dstPtr += dstPitch << 1; + nextLine += dstPitch << 1; + } + while (--height); +} + +void Simple2x32(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u8 *nextLine, *finish; + + nextLine = dstPtr + dstPitch; + + do { + u32 *bP = (u32 *) srcPtr; + u32 *dP = (u32 *) dstPtr; + u32 *nL = (u32 *) nextLine; + u32 currentPixel; + + finish = (u8 *) bP + ((width+1) << 2); + currentPixel = *bP++; + + do { + u32 color = currentPixel; + + *(dP) = color; + *(dP+1) = color; + *(nL) = color; + *(nL + 1) = color; + + currentPixel = *bP++; + + dP += 2; + nL += 2; + } while ((u8 *) bP < finish); + + srcPtr += srcPitch; + dstPtr += dstPitch << 1; + nextLine += dstPitch << 1; + } + while (--height); +} + + +void Simple3x16(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ +#define magnification 3 +#define colorBytes 2 // 16 bit colors = 2 byte colors + + // Generic Simple magnification filter + int x, y; // Source Position Counter + unsigned int dx, dy; // Destination pixel's pixels + unsigned short col; // Source color + + srcPitch = (srcPitch / colorBytes) - width; // This is the part of the source pitch in pixels that is more than the source image + dstPitch = dstPitch / colorBytes; + + unsigned short *src, *dst, *dst2; + src = (unsigned short *)srcPtr; // Since everything is time-critical this should be better than converting the pointers x*y times + dst = (unsigned short *)dstPtr; + + for (y = 0; y < height; y++) // Line + { + for (x = 0; x < width; x++) // Pixel in Line + { + col = *src; + + dst2 = dst; + *dst2 = col; + for (dy = 0; dy < magnification; dy++) + { + for (dx = 0; dx < magnification; dx++) + { + *dst2 = col; + dst2++; + } + dst2+=dstPitch; + dst2-=magnification; + } + + src++; + dst+=magnification; + } + src+=srcPitch; + dst+=dstPitch * magnification; + dst-=width * magnification; + } +#undef magnification +#undef colorBytes +} + + + +void Simple3x32(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ +#define magnification 3 +#define colorBytes 4 // 32 bit colors = 4 byte colors + + // Generic Simple magnification filter + int x, y; // Source Position Counter + unsigned int dx, dy; // Destination pixel's pixels + unsigned int col; // Source color + + srcPitch = (srcPitch / colorBytes) - width; // This is the part of the source pitch in pixels that is more than the source image + dstPitch = dstPitch / colorBytes; + + unsigned int *src, *dst, *dst2; + src = (unsigned int *)srcPtr; // Since everything is time-critical this should be better than converting the pointers x*y times + dst = (unsigned int *)dstPtr; + + for (y = 0; y < height; y++) // Line + { + for (x = 0; x < width; x++) // Pixel in Line + { + col = *src; + + dst2 = dst; + *dst2 = col; + for (dy = 0; dy < magnification; dy++) + { + for (dx = 0; dx < magnification; dx++) + { + *dst2 = col; + dst2++; + } + dst2+=dstPitch; + dst2-=magnification; + } + + src++; + dst+=magnification; + } + src+=srcPitch; + dst+=dstPitch * magnification; + dst-=width * magnification; + } +#undef magnification +#undef colorBytes +} + +void Simple4x16(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ +#define magnification 4 +#define colorBytes 2 // 16 bit colors = 2 byte colors + + // Generic Simple magnification filter + int x, y; // Source Position Counter + unsigned int dx, dy; // Destination pixel's pixels + unsigned short col; // Source color + + srcPitch = (srcPitch / colorBytes) - width; // This is the part of the source pitch in pixels that is more than the source image + dstPitch = dstPitch / colorBytes; + + unsigned short *src, *dst, *dst2; + src = (unsigned short *)srcPtr; // Since everything is time-critical this should be better than converting the pointers x*y times + dst = (unsigned short *)dstPtr; + + for (y = 0; y < height; y++) // Line + { + for (x = 0; x < width; x++) // Pixel in Line + { + col = *src; + + dst2 = dst; + *dst2 = col; + for (dy = 0; dy < magnification; dy++) + { + for (dx = 0; dx < magnification; dx++) + { + *dst2 = col; + dst2++; + } + dst2+=dstPitch; + dst2-=magnification; + } + + src++; + dst+=magnification; + } + src+=srcPitch; + dst+=dstPitch * magnification; + dst-=width * magnification; + } +#undef magnification +#undef colorBytes +} + + + +void Simple4x32(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ +#define magnification 4 +#define colorBytes 4 // 32 bit colors = 4 byte colors + + // Generic Simple magnification filter + int x, y; // Source Position Counter + unsigned int dx, dy; // Destination pixel's pixels + unsigned int col; // Source color + + srcPitch = (srcPitch / colorBytes) - width; // This is the part of the source pitch in pixels that is more than the source image + dstPitch = dstPitch / colorBytes; + + unsigned int *src, *dst, *dst2; + src = (unsigned int *)srcPtr; // Since everything is time-critical this should be better than converting the pointers x*y times + dst = (unsigned int *)dstPtr; + + for (y = 0; y < height; y++) // Line + { + for (x = 0; x < width; x++) // Pixel in Line + { + col = *src; + + dst2 = dst; + *dst2 = col; + for (dy = 0; dy < magnification; dy++) + { + for (dx = 0; dx < magnification; dx++) + { + *dst2 = col; + dst2++; + } + dst2+=dstPitch; + dst2-=magnification; + } + + src++; + dst+=magnification; + } + src+=srcPitch; + dst+=dstPitch * magnification; + dst-=width * magnification; + } +#undef magnification +#undef colorBytes +} diff --git a/src/gb/GB.cpp b/src/gb/GB.cpp new file mode 100644 index 0000000..c717686 --- /dev/null +++ b/src/gb/GB.cpp @@ -0,0 +1,5471 @@ +//#include "../win32/stdafx.h" // would fix LNK2005 linker errors for MSVC +#include +#include +#include +#include +#include + +#include "../System.h" +#include "../NLS.h" +#include "gb.h" +#include "gbCheats.h" +#include "gbGlobals.h" +#include "gbMemory.h" +#include "gbSGB.h" +#include "gbSound.h" +#include "../Util.h" + +#ifdef __GNUC__ +#define _stricmp strcasecmp +#endif + +extern u8 *pix; +extern bool speedup; +bool gbUpdateSizes(); +bool inBios = false; + +// debugging +bool memorydebug = false; +char gbBuffer[2048]; + +extern u16 gbLineMix[160]; + +// mappers +void (*mapper)(u16,u8) = NULL; +void (*mapperRAM)(u16,u8) = NULL; +u8 (*mapperReadRAM)(u16) = NULL; +void (*mapperUpdateClock)() = NULL; + +// registers +gbRegister PC; +gbRegister SP; +gbRegister AF; +gbRegister BC; +gbRegister DE; +gbRegister HL; +u16 IFF = 0; +// 0xff04 +u8 register_DIV = 0; +// 0xff05 +u8 register_TIMA = 0; +// 0xff06 +u8 register_TMA = 0; +// 0xff07 +u8 register_TAC = 0; +// 0xff0f +u8 register_IF = 0; +// 0xff40 +u8 register_LCDC = 0; +// 0xff41 +u8 register_STAT = 0; +// 0xff42 +u8 register_SCY = 0; +// 0xff43 +u8 register_SCX = 0; +// 0xff44 +u8 register_LY = 0; +// 0xff45 +u8 register_LYC = 0; +// 0xff46 +u8 register_DMA = 0; +// 0xff4a +u8 register_WY = 0; +// 0xff4b +u8 register_WX = 0; +// 0xff4f +u8 register_VBK = 0; +// 0xff51 +u8 register_HDMA1 = 0; +// 0xff52 +u8 register_HDMA2 = 0; +// 0xff53 +u8 register_HDMA3 = 0; +// 0xff54 +u8 register_HDMA4 = 0; +// 0xff55 +u8 register_HDMA5 = 0; +// 0xff70 +u8 register_SVBK = 0; +// 0xffff +u8 register_IE = 0; + +// ticks definition +int GBDIV_CLOCK_TICKS = 64; +int GBLCD_MODE_0_CLOCK_TICKS = 51; +int GBLCD_MODE_1_CLOCK_TICKS = 1140; +int GBLCD_MODE_2_CLOCK_TICKS = 20; +int GBLCD_MODE_3_CLOCK_TICKS = 43; +int GBLY_INCREMENT_CLOCK_TICKS = 114; +int GBTIMER_MODE_0_CLOCK_TICKS = 256; +int GBTIMER_MODE_1_CLOCK_TICKS = 4; +int GBTIMER_MODE_2_CLOCK_TICKS = 16; +int GBTIMER_MODE_3_CLOCK_TICKS = 64; +int GBSERIAL_CLOCK_TICKS = 128; +int GBSYNCHRONIZE_CLOCK_TICKS = 52920; + +// state variables + +// general +int clockTicks = 0; +bool gbSystemMessage = false; +int gbGBCColorType = 0; +int gbHardware = 0; +int gbRomType = 0; +int gbRemainingClockTicks = 0; +int gbOldClockTicks = 0; +int gbIntBreak = 0; +int gbInterruptLaunched = 0; +u8 gbCheatingDevice = 0; // 1 = GS, 2 = GG +// breakpoint +bool breakpoint = false; +// interrupt +int gbInt48Signal = 0; +int gbInterruptWait = 0; +// serial +int gbSerialOn = 0; +int gbSerialTicks = 0; +int gbSerialBits = 0; +// timer +bool gbTimerOn = false; +int gbTimerTicks = GBTIMER_MODE_0_CLOCK_TICKS; +int gbTimerClockTicks = GBTIMER_MODE_0_CLOCK_TICKS; +int gbTimerMode = 0; +bool gbIncreased = false; +// The internal timer is always active, and it is +// not reset by writing to register_TIMA/TMA, but by +// writing to register_DIV... +int gbInternalTimer = 0x55; +const u8 gbTimerMask [4] = {0xff, 0x3, 0xf, 0x3f}; +const u8 gbTimerBug [8] = {0x80, 0x80, 0x02, 0x02, 0x0, 0xff, 0x0, 0xff}; +bool gbTimerModeChange = false; +bool gbTimerOnChange = false; +// lcd +bool gbScreenOn = true; +int gbLcdMode = 2; +int gbLcdModeDelayed = 2; +int gbLcdTicks = GBLCD_MODE_2_CLOCK_TICKS-1; +int gbLcdTicksDelayed = GBLCD_MODE_2_CLOCK_TICKS; +int gbLcdLYIncrementTicks = 114; +int gbLcdLYIncrementTicksDelayed = 115; +int gbScreenTicks = 0; +u8 gbSCYLine[300]; +u8 gbSCXLine[300]; +u8 gbBgpLine[300]; +u8 gbObp0Line [300]; +u8 gbObp1Line [300]; +u8 gbSpritesTicks [300]; +u8 oldRegister_WY; +bool gbLYChangeHappened = false; +bool gbLCDChangeHappened = false; +int gbLine99Ticks = 1; +int gbRegisterLYLCDCOffOn = 0; +int inUseRegister_WY = 0; + +// Used to keep track of the line that ellapse +// when screen is off +int gbWhiteScreen = 0; +bool gbBlackScreen = false; +int register_LCDCBusy = 0; + +// div +int gbDivTicks = GBDIV_CLOCK_TICKS; +// cgb +int gbVramBank = 0; +int gbWramBank = 1; +//sgb +bool gbSgbResetFlag = false; +// gbHdmaDestination is 0x99d0 on startup (tested on HW) +// but I'm not sure what gbHdmaSource is... +int gbHdmaSource = 0x99d0; +int gbHdmaDestination = 0x99d0; +int gbHdmaBytes = 0x0000; +int gbHdmaOn = 0; +int gbSpeed = 0; +// frame counting +int gbFrameCount = 0; +int gbFrameSkip = 0; +int gbFrameSkipCount = 0; +// timing +u32 gbLastTime = 0; +u32 gbElapsedTime = 0; +u32 gbTimeNow = 0; +int gbSynchronizeTicks = GBSYNCHRONIZE_CLOCK_TICKS; +// emulator features +int gbBattery = 0; +bool gbBatteryError = false; +int gbCaptureNumber = 0; +bool gbCapture = false; +bool gbCapturePrevious = false; +int gbJoymask[4] = { 0, 0, 0, 0 }; + +u8 gbRamFill = 0xff; + +int gbRomSizes[] = { 0x00008000, // 32K + 0x00010000, // 64K + 0x00020000, // 128K + 0x00040000, // 256K + 0x00080000, // 512K + 0x00100000, // 1024K + 0x00200000, // 2048K + 0x00400000, // 4096K + 0x00800000 // 8192K +}; +int gbRomSizesMasks[] = { 0x00007fff, + 0x0000ffff, + 0x0001ffff, + 0x0003ffff, + 0x0007ffff, + 0x000fffff, + 0x001fffff, + 0x003fffff, + 0x007fffff +}; + +int gbRamSizes[6] = { 0x00000000, // 0K + 0x00002000, // 2K // Changed to 2000 to avoid problems with gbMemoryMap... + 0x00002000, // 8K + 0x00008000, // 32K + 0x00020000, // 128K + 0x00010000 // 64K +}; + +int gbRamSizesMasks[6] = { 0x00000000, + 0x000007ff, + 0x00001fff, + 0x00007fff, + 0x0001ffff, + 0x0000ffff +}; + +int gbCycles[] = { +// 0 1 2 3 4 5 6 7 8 9 a b c d e f + 1, 3, 2, 2, 1, 1, 2, 1, 5, 2, 2, 2, 1, 1, 2, 1, // 0 + 1, 3, 2, 2, 1, 1, 2, 1, 3, 2, 2, 2, 1, 1, 2, 1, // 1 + 2, 3, 2, 2, 1, 1, 2, 1, 2, 2, 2, 2, 1, 1, 2, 1, // 2 + 2, 3, 2, 2, 3, 3, 3, 1, 2, 2, 2, 2, 1, 1, 2, 1, // 3 + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 4 + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 5 + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 6 + 2, 2, 2, 2, 2, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, // 7 + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 8 + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 9 + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // a + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // b + 2, 3, 3, 4, 3, 4, 2, 4, 2, 4, 3, 2, 3, 6, 2, 4, // c + 2, 3, 3, 1, 3, 4, 2, 4, 2, 4, 3, 1, 3, 1, 2, 4, // d + 3, 3, 2, 1, 1, 4, 2, 4, 4, 1, 4, 1, 1, 1, 2, 4, // e + 3, 3, 2, 1, 1, 4, 2, 4, 3, 2, 4, 1, 0, 1, 2, 4 // f +}; + +int gbCyclesCB[] = { +// 0 1 2 3 4 5 6 7 8 9 a b c d e f + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 0 + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 1 + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 2 + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 3 + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 4 + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 5 + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 6 + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 7 + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 8 + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 9 + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // a + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // b + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // c + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // d + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // e + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2 // f +}; + +u16 DAATable[] = { + 0x0080,0x0100,0x0200,0x0300,0x0400,0x0500,0x0600,0x0700, + 0x0800,0x0900,0x1000,0x1100,0x1200,0x1300,0x1400,0x1500, + 0x1000,0x1100,0x1200,0x1300,0x1400,0x1500,0x1600,0x1700, + 0x1800,0x1900,0x2000,0x2100,0x2200,0x2300,0x2400,0x2500, + 0x2000,0x2100,0x2200,0x2300,0x2400,0x2500,0x2600,0x2700, + 0x2800,0x2900,0x3000,0x3100,0x3200,0x3300,0x3400,0x3500, + 0x3000,0x3100,0x3200,0x3300,0x3400,0x3500,0x3600,0x3700, + 0x3800,0x3900,0x4000,0x4100,0x4200,0x4300,0x4400,0x4500, + 0x4000,0x4100,0x4200,0x4300,0x4400,0x4500,0x4600,0x4700, + 0x4800,0x4900,0x5000,0x5100,0x5200,0x5300,0x5400,0x5500, + 0x5000,0x5100,0x5200,0x5300,0x5400,0x5500,0x5600,0x5700, + 0x5800,0x5900,0x6000,0x6100,0x6200,0x6300,0x6400,0x6500, + 0x6000,0x6100,0x6200,0x6300,0x6400,0x6500,0x6600,0x6700, + 0x6800,0x6900,0x7000,0x7100,0x7200,0x7300,0x7400,0x7500, + 0x7000,0x7100,0x7200,0x7300,0x7400,0x7500,0x7600,0x7700, + 0x7800,0x7900,0x8000,0x8100,0x8200,0x8300,0x8400,0x8500, + 0x8000,0x8100,0x8200,0x8300,0x8400,0x8500,0x8600,0x8700, + 0x8800,0x8900,0x9000,0x9100,0x9200,0x9300,0x9400,0x9500, + 0x9000,0x9100,0x9200,0x9300,0x9400,0x9500,0x9600,0x9700, + 0x9800,0x9900,0x0090,0x0110,0x0210,0x0310,0x0410,0x0510, + 0x0090,0x0110,0x0210,0x0310,0x0410,0x0510,0x0610,0x0710, + 0x0810,0x0910,0x1010,0x1110,0x1210,0x1310,0x1410,0x1510, + 0x1010,0x1110,0x1210,0x1310,0x1410,0x1510,0x1610,0x1710, + 0x1810,0x1910,0x2010,0x2110,0x2210,0x2310,0x2410,0x2510, + 0x2010,0x2110,0x2210,0x2310,0x2410,0x2510,0x2610,0x2710, + 0x2810,0x2910,0x3010,0x3110,0x3210,0x3310,0x3410,0x3510, + 0x3010,0x3110,0x3210,0x3310,0x3410,0x3510,0x3610,0x3710, + 0x3810,0x3910,0x4010,0x4110,0x4210,0x4310,0x4410,0x4510, + 0x4010,0x4110,0x4210,0x4310,0x4410,0x4510,0x4610,0x4710, + 0x4810,0x4910,0x5010,0x5110,0x5210,0x5310,0x5410,0x5510, + 0x5010,0x5110,0x5210,0x5310,0x5410,0x5510,0x5610,0x5710, + 0x5810,0x5910,0x6010,0x6110,0x6210,0x6310,0x6410,0x6510, + 0x6010,0x6110,0x6210,0x6310,0x6410,0x6510,0x6610,0x6710, + 0x6810,0x6910,0x7010,0x7110,0x7210,0x7310,0x7410,0x7510, + 0x7010,0x7110,0x7210,0x7310,0x7410,0x7510,0x7610,0x7710, + 0x7810,0x7910,0x8010,0x8110,0x8210,0x8310,0x8410,0x8510, + 0x8010,0x8110,0x8210,0x8310,0x8410,0x8510,0x8610,0x8710, + 0x8810,0x8910,0x9010,0x9110,0x9210,0x9310,0x9410,0x9510, + 0x9010,0x9110,0x9210,0x9310,0x9410,0x9510,0x9610,0x9710, + 0x9810,0x9910,0xA010,0xA110,0xA210,0xA310,0xA410,0xA510, + 0xA010,0xA110,0xA210,0xA310,0xA410,0xA510,0xA610,0xA710, + 0xA810,0xA910,0xB010,0xB110,0xB210,0xB310,0xB410,0xB510, + 0xB010,0xB110,0xB210,0xB310,0xB410,0xB510,0xB610,0xB710, + 0xB810,0xB910,0xC010,0xC110,0xC210,0xC310,0xC410,0xC510, + 0xC010,0xC110,0xC210,0xC310,0xC410,0xC510,0xC610,0xC710, + 0xC810,0xC910,0xD010,0xD110,0xD210,0xD310,0xD410,0xD510, + 0xD010,0xD110,0xD210,0xD310,0xD410,0xD510,0xD610,0xD710, + 0xD810,0xD910,0xE010,0xE110,0xE210,0xE310,0xE410,0xE510, + 0xE010,0xE110,0xE210,0xE310,0xE410,0xE510,0xE610,0xE710, + 0xE810,0xE910,0xF010,0xF110,0xF210,0xF310,0xF410,0xF510, + 0xF010,0xF110,0xF210,0xF310,0xF410,0xF510,0xF610,0xF710, + 0xF810,0xF910,0x0090,0x0110,0x0210,0x0310,0x0410,0x0510, + 0x0090,0x0110,0x0210,0x0310,0x0410,0x0510,0x0610,0x0710, + 0x0810,0x0910,0x1010,0x1110,0x1210,0x1310,0x1410,0x1510, + 0x1010,0x1110,0x1210,0x1310,0x1410,0x1510,0x1610,0x1710, + 0x1810,0x1910,0x2010,0x2110,0x2210,0x2310,0x2410,0x2510, + 0x2010,0x2110,0x2210,0x2310,0x2410,0x2510,0x2610,0x2710, + 0x2810,0x2910,0x3010,0x3110,0x3210,0x3310,0x3410,0x3510, + 0x3010,0x3110,0x3210,0x3310,0x3410,0x3510,0x3610,0x3710, + 0x3810,0x3910,0x4010,0x4110,0x4210,0x4310,0x4410,0x4510, + 0x4010,0x4110,0x4210,0x4310,0x4410,0x4510,0x4610,0x4710, + 0x4810,0x4910,0x5010,0x5110,0x5210,0x5310,0x5410,0x5510, + 0x5010,0x5110,0x5210,0x5310,0x5410,0x5510,0x5610,0x5710, + 0x5810,0x5910,0x6010,0x6110,0x6210,0x6310,0x6410,0x6510, + 0x0600,0x0700,0x0800,0x0900,0x0A00,0x0B00,0x0C00,0x0D00, + 0x0E00,0x0F00,0x1000,0x1100,0x1200,0x1300,0x1400,0x1500, + 0x1600,0x1700,0x1800,0x1900,0x1A00,0x1B00,0x1C00,0x1D00, + 0x1E00,0x1F00,0x2000,0x2100,0x2200,0x2300,0x2400,0x2500, + 0x2600,0x2700,0x2800,0x2900,0x2A00,0x2B00,0x2C00,0x2D00, + 0x2E00,0x2F00,0x3000,0x3100,0x3200,0x3300,0x3400,0x3500, + 0x3600,0x3700,0x3800,0x3900,0x3A00,0x3B00,0x3C00,0x3D00, + 0x3E00,0x3F00,0x4000,0x4100,0x4200,0x4300,0x4400,0x4500, + 0x4600,0x4700,0x4800,0x4900,0x4A00,0x4B00,0x4C00,0x4D00, + 0x4E00,0x4F00,0x5000,0x5100,0x5200,0x5300,0x5400,0x5500, + 0x5600,0x5700,0x5800,0x5900,0x5A00,0x5B00,0x5C00,0x5D00, + 0x5E00,0x5F00,0x6000,0x6100,0x6200,0x6300,0x6400,0x6500, + 0x6600,0x6700,0x6800,0x6900,0x6A00,0x6B00,0x6C00,0x6D00, + 0x6E00,0x6F00,0x7000,0x7100,0x7200,0x7300,0x7400,0x7500, + 0x7600,0x7700,0x7800,0x7900,0x7A00,0x7B00,0x7C00,0x7D00, + 0x7E00,0x7F00,0x8000,0x8100,0x8200,0x8300,0x8400,0x8500, + 0x8600,0x8700,0x8800,0x8900,0x8A00,0x8B00,0x8C00,0x8D00, + 0x8E00,0x8F00,0x9000,0x9100,0x9200,0x9300,0x9400,0x9500, + 0x9600,0x9700,0x9800,0x9900,0x9A00,0x9B00,0x9C00,0x9D00, + 0x9E00,0x9F00,0x0090,0x0110,0x0210,0x0310,0x0410,0x0510, + 0x0610,0x0710,0x0810,0x0910,0x0A10,0x0B10,0x0C10,0x0D10, + 0x0E10,0x0F10,0x1010,0x1110,0x1210,0x1310,0x1410,0x1510, + 0x1610,0x1710,0x1810,0x1910,0x1A10,0x1B10,0x1C10,0x1D10, + 0x1E10,0x1F10,0x2010,0x2110,0x2210,0x2310,0x2410,0x2510, + 0x2610,0x2710,0x2810,0x2910,0x2A10,0x2B10,0x2C10,0x2D10, + 0x2E10,0x2F10,0x3010,0x3110,0x3210,0x3310,0x3410,0x3510, + 0x3610,0x3710,0x3810,0x3910,0x3A10,0x3B10,0x3C10,0x3D10, + 0x3E10,0x3F10,0x4010,0x4110,0x4210,0x4310,0x4410,0x4510, + 0x4610,0x4710,0x4810,0x4910,0x4A10,0x4B10,0x4C10,0x4D10, + 0x4E10,0x4F10,0x5010,0x5110,0x5210,0x5310,0x5410,0x5510, + 0x5610,0x5710,0x5810,0x5910,0x5A10,0x5B10,0x5C10,0x5D10, + 0x5E10,0x5F10,0x6010,0x6110,0x6210,0x6310,0x6410,0x6510, + 0x6610,0x6710,0x6810,0x6910,0x6A10,0x6B10,0x6C10,0x6D10, + 0x6E10,0x6F10,0x7010,0x7110,0x7210,0x7310,0x7410,0x7510, + 0x7610,0x7710,0x7810,0x7910,0x7A10,0x7B10,0x7C10,0x7D10, + 0x7E10,0x7F10,0x8010,0x8110,0x8210,0x8310,0x8410,0x8510, + 0x8610,0x8710,0x8810,0x8910,0x8A10,0x8B10,0x8C10,0x8D10, + 0x8E10,0x8F10,0x9010,0x9110,0x9210,0x9310,0x9410,0x9510, + 0x9610,0x9710,0x9810,0x9910,0x9A10,0x9B10,0x9C10,0x9D10, + 0x9E10,0x9F10,0xA010,0xA110,0xA210,0xA310,0xA410,0xA510, + 0xA610,0xA710,0xA810,0xA910,0xAA10,0xAB10,0xAC10,0xAD10, + 0xAE10,0xAF10,0xB010,0xB110,0xB210,0xB310,0xB410,0xB510, + 0xB610,0xB710,0xB810,0xB910,0xBA10,0xBB10,0xBC10,0xBD10, + 0xBE10,0xBF10,0xC010,0xC110,0xC210,0xC310,0xC410,0xC510, + 0xC610,0xC710,0xC810,0xC910,0xCA10,0xCB10,0xCC10,0xCD10, + 0xCE10,0xCF10,0xD010,0xD110,0xD210,0xD310,0xD410,0xD510, + 0xD610,0xD710,0xD810,0xD910,0xDA10,0xDB10,0xDC10,0xDD10, + 0xDE10,0xDF10,0xE010,0xE110,0xE210,0xE310,0xE410,0xE510, + 0xE610,0xE710,0xE810,0xE910,0xEA10,0xEB10,0xEC10,0xED10, + 0xEE10,0xEF10,0xF010,0xF110,0xF210,0xF310,0xF410,0xF510, + 0xF610,0xF710,0xF810,0xF910,0xFA10,0xFB10,0xFC10,0xFD10, + 0xFE10,0xFF10,0x0090,0x0110,0x0210,0x0310,0x0410,0x0510, + 0x0610,0x0710,0x0810,0x0910,0x0A10,0x0B10,0x0C10,0x0D10, + 0x0E10,0x0F10,0x1010,0x1110,0x1210,0x1310,0x1410,0x1510, + 0x1610,0x1710,0x1810,0x1910,0x1A10,0x1B10,0x1C10,0x1D10, + 0x1E10,0x1F10,0x2010,0x2110,0x2210,0x2310,0x2410,0x2510, + 0x2610,0x2710,0x2810,0x2910,0x2A10,0x2B10,0x2C10,0x2D10, + 0x2E10,0x2F10,0x3010,0x3110,0x3210,0x3310,0x3410,0x3510, + 0x3610,0x3710,0x3810,0x3910,0x3A10,0x3B10,0x3C10,0x3D10, + 0x3E10,0x3F10,0x4010,0x4110,0x4210,0x4310,0x4410,0x4510, + 0x4610,0x4710,0x4810,0x4910,0x4A10,0x4B10,0x4C10,0x4D10, + 0x4E10,0x4F10,0x5010,0x5110,0x5210,0x5310,0x5410,0x5510, + 0x5610,0x5710,0x5810,0x5910,0x5A10,0x5B10,0x5C10,0x5D10, + 0x5E10,0x5F10,0x6010,0x6110,0x6210,0x6310,0x6410,0x6510, + 0x00C0,0x0140,0x0240,0x0340,0x0440,0x0540,0x0640,0x0740, + 0x0840,0x0940,0x0A40,0x0B40,0x0C40,0x0D40,0x0E40,0x0F40, + 0x1040,0x1140,0x1240,0x1340,0x1440,0x1540,0x1640,0x1740, + 0x1840,0x1940,0x1A40,0x1B40,0x1C40,0x1D40,0x1E40,0x1F40, + 0x2040,0x2140,0x2240,0x2340,0x2440,0x2540,0x2640,0x2740, + 0x2840,0x2940,0x2A40,0x2B40,0x2C40,0x2D40,0x2E40,0x2F40, + 0x3040,0x3140,0x3240,0x3340,0x3440,0x3540,0x3640,0x3740, + 0x3840,0x3940,0x3A40,0x3B40,0x3C40,0x3D40,0x3E40,0x3F40, + 0x4040,0x4140,0x4240,0x4340,0x4440,0x4540,0x4640,0x4740, + 0x4840,0x4940,0x4A40,0x4B40,0x4C40,0x4D40,0x4E40,0x4F40, + 0x5040,0x5140,0x5240,0x5340,0x5440,0x5540,0x5640,0x5740, + 0x5840,0x5940,0x5A40,0x5B40,0x5C40,0x5D40,0x5E40,0x5F40, + 0x6040,0x6140,0x6240,0x6340,0x6440,0x6540,0x6640,0x6740, + 0x6840,0x6940,0x6A40,0x6B40,0x6C40,0x6D40,0x6E40,0x6F40, + 0x7040,0x7140,0x7240,0x7340,0x7440,0x7540,0x7640,0x7740, + 0x7840,0x7940,0x7A40,0x7B40,0x7C40,0x7D40,0x7E40,0x7F40, + 0x8040,0x8140,0x8240,0x8340,0x8440,0x8540,0x8640,0x8740, + 0x8840,0x8940,0x8A40,0x8B40,0x8C40,0x8D40,0x8E40,0x8F40, + 0x9040,0x9140,0x9240,0x9340,0x9440,0x9540,0x9640,0x9740, + 0x9840,0x9940,0x9A40,0x9B40,0x9C40,0x9D40,0x9E40,0x9F40, + 0xA040,0xA140,0xA240,0xA340,0xA440,0xA540,0xA640,0xA740, + 0xA840,0xA940,0xAA40,0xAB40,0xAC40,0xAD40,0xAE40,0xAF40, + 0xB040,0xB140,0xB240,0xB340,0xB440,0xB540,0xB640,0xB740, + 0xB840,0xB940,0xBA40,0xBB40,0xBC40,0xBD40,0xBE40,0xBF40, + 0xC040,0xC140,0xC240,0xC340,0xC440,0xC540,0xC640,0xC740, + 0xC840,0xC940,0xCA40,0xCB40,0xCC40,0xCD40,0xCE40,0xCF40, + 0xD040,0xD140,0xD240,0xD340,0xD440,0xD540,0xD640,0xD740, + 0xD840,0xD940,0xDA40,0xDB40,0xDC40,0xDD40,0xDE40,0xDF40, + 0xE040,0xE140,0xE240,0xE340,0xE440,0xE540,0xE640,0xE740, + 0xE840,0xE940,0xEA40,0xEB40,0xEC40,0xED40,0xEE40,0xEF40, + 0xF040,0xF140,0xF240,0xF340,0xF440,0xF540,0xF640,0xF740, + 0xF840,0xF940,0xFA40,0xFB40,0xFC40,0xFD40,0xFE40,0xFF40, + 0xA050,0xA150,0xA250,0xA350,0xA450,0xA550,0xA650,0xA750, + 0xA850,0xA950,0xAA50,0xAB50,0xAC50,0xAD50,0xAE50,0xAF50, + 0xB050,0xB150,0xB250,0xB350,0xB450,0xB550,0xB650,0xB750, + 0xB850,0xB950,0xBA50,0xBB50,0xBC50,0xBD50,0xBE50,0xBF50, + 0xC050,0xC150,0xC250,0xC350,0xC450,0xC550,0xC650,0xC750, + 0xC850,0xC950,0xCA50,0xCB50,0xCC50,0xCD50,0xCE50,0xCF50, + 0xD050,0xD150,0xD250,0xD350,0xD450,0xD550,0xD650,0xD750, + 0xD850,0xD950,0xDA50,0xDB50,0xDC50,0xDD50,0xDE50,0xDF50, + 0xE050,0xE150,0xE250,0xE350,0xE450,0xE550,0xE650,0xE750, + 0xE850,0xE950,0xEA50,0xEB50,0xEC50,0xED50,0xEE50,0xEF50, + 0xF050,0xF150,0xF250,0xF350,0xF450,0xF550,0xF650,0xF750, + 0xF850,0xF950,0xFA50,0xFB50,0xFC50,0xFD50,0xFE50,0xFF50, + 0x00D0,0x0150,0x0250,0x0350,0x0450,0x0550,0x0650,0x0750, + 0x0850,0x0950,0x0A50,0x0B50,0x0C50,0x0D50,0x0E50,0x0F50, + 0x1050,0x1150,0x1250,0x1350,0x1450,0x1550,0x1650,0x1750, + 0x1850,0x1950,0x1A50,0x1B50,0x1C50,0x1D50,0x1E50,0x1F50, + 0x2050,0x2150,0x2250,0x2350,0x2450,0x2550,0x2650,0x2750, + 0x2850,0x2950,0x2A50,0x2B50,0x2C50,0x2D50,0x2E50,0x2F50, + 0x3050,0x3150,0x3250,0x3350,0x3450,0x3550,0x3650,0x3750, + 0x3850,0x3950,0x3A50,0x3B50,0x3C50,0x3D50,0x3E50,0x3F50, + 0x4050,0x4150,0x4250,0x4350,0x4450,0x4550,0x4650,0x4750, + 0x4850,0x4950,0x4A50,0x4B50,0x4C50,0x4D50,0x4E50,0x4F50, + 0x5050,0x5150,0x5250,0x5350,0x5450,0x5550,0x5650,0x5750, + 0x5850,0x5950,0x5A50,0x5B50,0x5C50,0x5D50,0x5E50,0x5F50, + 0x6050,0x6150,0x6250,0x6350,0x6450,0x6550,0x6650,0x6750, + 0x6850,0x6950,0x6A50,0x6B50,0x6C50,0x6D50,0x6E50,0x6F50, + 0x7050,0x7150,0x7250,0x7350,0x7450,0x7550,0x7650,0x7750, + 0x7850,0x7950,0x7A50,0x7B50,0x7C50,0x7D50,0x7E50,0x7F50, + 0x8050,0x8150,0x8250,0x8350,0x8450,0x8550,0x8650,0x8750, + 0x8850,0x8950,0x8A50,0x8B50,0x8C50,0x8D50,0x8E50,0x8F50, + 0x9050,0x9150,0x9250,0x9350,0x9450,0x9550,0x9650,0x9750, + 0x9850,0x9950,0x9A50,0x9B50,0x9C50,0x9D50,0x9E50,0x9F50, + 0xFA40,0xFB40,0xFC40,0xFD40,0xFE40,0xFF40,0x00C0,0x0140, + 0x0240,0x0340,0x0440,0x0540,0x0640,0x0740,0x0840,0x0940, + 0x0A40,0x0B40,0x0C40,0x0D40,0x0E40,0x0F40,0x1040,0x1140, + 0x1240,0x1340,0x1440,0x1540,0x1640,0x1740,0x1840,0x1940, + 0x1A40,0x1B40,0x1C40,0x1D40,0x1E40,0x1F40,0x2040,0x2140, + 0x2240,0x2340,0x2440,0x2540,0x2640,0x2740,0x2840,0x2940, + 0x2A40,0x2B40,0x2C40,0x2D40,0x2E40,0x2F40,0x3040,0x3140, + 0x3240,0x3340,0x3440,0x3540,0x3640,0x3740,0x3840,0x3940, + 0x3A40,0x3B40,0x3C40,0x3D40,0x3E40,0x3F40,0x4040,0x4140, + 0x4240,0x4340,0x4440,0x4540,0x4640,0x4740,0x4840,0x4940, + 0x4A40,0x4B40,0x4C40,0x4D40,0x4E40,0x4F40,0x5040,0x5140, + 0x5240,0x5340,0x5440,0x5540,0x5640,0x5740,0x5840,0x5940, + 0x5A40,0x5B40,0x5C40,0x5D40,0x5E40,0x5F40,0x6040,0x6140, + 0x6240,0x6340,0x6440,0x6540,0x6640,0x6740,0x6840,0x6940, + 0x6A40,0x6B40,0x6C40,0x6D40,0x6E40,0x6F40,0x7040,0x7140, + 0x7240,0x7340,0x7440,0x7540,0x7640,0x7740,0x7840,0x7940, + 0x7A40,0x7B40,0x7C40,0x7D40,0x7E40,0x7F40,0x8040,0x8140, + 0x8240,0x8340,0x8440,0x8540,0x8640,0x8740,0x8840,0x8940, + 0x8A40,0x8B40,0x8C40,0x8D40,0x8E40,0x8F40,0x9040,0x9140, + 0x9240,0x9340,0x9440,0x9540,0x9640,0x9740,0x9840,0x9940, + 0x9A40,0x9B40,0x9C40,0x9D40,0x9E40,0x9F40,0xA040,0xA140, + 0xA240,0xA340,0xA440,0xA540,0xA640,0xA740,0xA840,0xA940, + 0xAA40,0xAB40,0xAC40,0xAD40,0xAE40,0xAF40,0xB040,0xB140, + 0xB240,0xB340,0xB440,0xB540,0xB640,0xB740,0xB840,0xB940, + 0xBA40,0xBB40,0xBC40,0xBD40,0xBE40,0xBF40,0xC040,0xC140, + 0xC240,0xC340,0xC440,0xC540,0xC640,0xC740,0xC840,0xC940, + 0xCA40,0xCB40,0xCC40,0xCD40,0xCE40,0xCF40,0xD040,0xD140, + 0xD240,0xD340,0xD440,0xD540,0xD640,0xD740,0xD840,0xD940, + 0xDA40,0xDB40,0xDC40,0xDD40,0xDE40,0xDF40,0xE040,0xE140, + 0xE240,0xE340,0xE440,0xE540,0xE640,0xE740,0xE840,0xE940, + 0xEA40,0xEB40,0xEC40,0xED40,0xEE40,0xEF40,0xF040,0xF140, + 0xF240,0xF340,0xF440,0xF540,0xF640,0xF740,0xF840,0xF940, + 0x9A50,0x9B50,0x9C50,0x9D50,0x9E50,0x9F50,0xA050,0xA150, + 0xA250,0xA350,0xA450,0xA550,0xA650,0xA750,0xA850,0xA950, + 0xAA50,0xAB50,0xAC50,0xAD50,0xAE50,0xAF50,0xB050,0xB150, + 0xB250,0xB350,0xB450,0xB550,0xB650,0xB750,0xB850,0xB950, + 0xBA50,0xBB50,0xBC50,0xBD50,0xBE50,0xBF50,0xC050,0xC150, + 0xC250,0xC350,0xC450,0xC550,0xC650,0xC750,0xC850,0xC950, + 0xCA50,0xCB50,0xCC50,0xCD50,0xCE50,0xCF50,0xD050,0xD150, + 0xD250,0xD350,0xD450,0xD550,0xD650,0xD750,0xD850,0xD950, + 0xDA50,0xDB50,0xDC50,0xDD50,0xDE50,0xDF50,0xE050,0xE150, + 0xE250,0xE350,0xE450,0xE550,0xE650,0xE750,0xE850,0xE950, + 0xEA50,0xEB50,0xEC50,0xED50,0xEE50,0xEF50,0xF050,0xF150, + 0xF250,0xF350,0xF450,0xF550,0xF650,0xF750,0xF850,0xF950, + 0xFA50,0xFB50,0xFC50,0xFD50,0xFE50,0xFF50,0x00D0,0x0150, + 0x0250,0x0350,0x0450,0x0550,0x0650,0x0750,0x0850,0x0950, + 0x0A50,0x0B50,0x0C50,0x0D50,0x0E50,0x0F50,0x1050,0x1150, + 0x1250,0x1350,0x1450,0x1550,0x1650,0x1750,0x1850,0x1950, + 0x1A50,0x1B50,0x1C50,0x1D50,0x1E50,0x1F50,0x2050,0x2150, + 0x2250,0x2350,0x2450,0x2550,0x2650,0x2750,0x2850,0x2950, + 0x2A50,0x2B50,0x2C50,0x2D50,0x2E50,0x2F50,0x3050,0x3150, + 0x3250,0x3350,0x3450,0x3550,0x3650,0x3750,0x3850,0x3950, + 0x3A50,0x3B50,0x3C50,0x3D50,0x3E50,0x3F50,0x4050,0x4150, + 0x4250,0x4350,0x4450,0x4550,0x4650,0x4750,0x4850,0x4950, + 0x4A50,0x4B50,0x4C50,0x4D50,0x4E50,0x4F50,0x5050,0x5150, + 0x5250,0x5350,0x5450,0x5550,0x5650,0x5750,0x5850,0x5950, + 0x5A50,0x5B50,0x5C50,0x5D50,0x5E50,0x5F50,0x6050,0x6150, + 0x6250,0x6350,0x6450,0x6550,0x6650,0x6750,0x6850,0x6950, + 0x6A50,0x6B50,0x6C50,0x6D50,0x6E50,0x6F50,0x7050,0x7150, + 0x7250,0x7350,0x7450,0x7550,0x7650,0x7750,0x7850,0x7950, + 0x7A50,0x7B50,0x7C50,0x7D50,0x7E50,0x7F50,0x8050,0x8150, + 0x8250,0x8350,0x8450,0x8550,0x8650,0x8750,0x8850,0x8950, + 0x8A50,0x8B50,0x8C50,0x8D50,0x8E50,0x8F50,0x9050,0x9150, + 0x9250,0x9350,0x9450,0x9550,0x9650,0x9750,0x9850,0x9950, +}; + +u8 ZeroTable[256] = { + 0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0 +}; + +#define GBSAVE_GAME_VERSION_1 1 +#define GBSAVE_GAME_VERSION_2 2 +#define GBSAVE_GAME_VERSION_3 3 +#define GBSAVE_GAME_VERSION_4 4 +#define GBSAVE_GAME_VERSION_5 5 +#define GBSAVE_GAME_VERSION_6 6 +#define GBSAVE_GAME_VERSION_7 7 +#define GBSAVE_GAME_VERSION_8 8 +#define GBSAVE_GAME_VERSION_9 9 +#define GBSAVE_GAME_VERSION_10 10 +#define GBSAVE_GAME_VERSION_11 11 +#define GBSAVE_GAME_VERSION_12 12 +#define GBSAVE_GAME_VERSION GBSAVE_GAME_VERSION_12 + +int inline gbGetValue(int min,int max,int v) +{ + return (int)(min+(float)(max-min)*(2.0*(v/31.0)-(v/31.0)*(v/31.0))); +} + +void gbGenFilter() +{ + for (int r=0;r<32;r++) { + for (int g=0;g<32;g++) { + for (int b=0;b<32;b++) { + int nr=gbGetValue(gbGetValue(4,14,g), + gbGetValue(24,29,g),r)-4; + int ng=gbGetValue(gbGetValue(4+gbGetValue(0,5,r), + 14+gbGetValue(0,3,r),b), + gbGetValue(24+gbGetValue(0,3,r), + 29+gbGetValue(0,1,r),b),g)-4; + int nb=gbGetValue(gbGetValue(4+gbGetValue(0,5,r), + 14+gbGetValue(0,3,r),g), + gbGetValue(24+gbGetValue(0,3,r), + 29+gbGetValue(0,1,r),g),b)-4; + gbColorFilter[(b<<10)|(g<<5)|r]=(nb<<10)|(ng<<5)|nr; + } + } + } +} + +bool gbIsGameboyRom(char * file) +{ + if(strlen(file) > 4) { + char * p = strrchr(file,'.'); + + if(p != NULL) { + if(_stricmp(p, ".gb") == 0) + return true; + if(_stricmp(p, ".dmg") == 0) + return true; + if(_stricmp(p, ".gbc") == 0) + return true; + if(_stricmp(p, ".cgb") == 0) + return true; + if(_stricmp(p, ".sgb") == 0) + return true; + } + } + + return false; +} + +void gbCopyMemory(u16 d, u16 s, int count) +{ + while(count) { + gbMemoryMap[d>>12][d & 0x0fff] = gbMemoryMap[s>>12][s & 0x0fff]; + s++; + d++; + count--; + } +} + +void gbDoHdma() +{ + + gbCopyMemory((gbHdmaDestination & 0x1ff0) | 0x8000, + gbHdmaSource & 0xfff0, + 0x10); + + gbHdmaDestination += 0x10; + if (gbHdmaDestination == 0xa000) + gbHdmaDestination = 0x8000; + + gbHdmaSource += 0x10; + if (gbHdmaSource == 0x8000) + gbHdmaSource = 0xa000; + + register_HDMA2 = gbHdmaSource & 0xff; + register_HDMA1 = gbHdmaSource>>8; + + register_HDMA4 = gbHdmaDestination & 0xff; + register_HDMA3 = gbHdmaDestination>>8; + + gbHdmaBytes -= 0x10; + gbMemory[0xff55] = --register_HDMA5; + if(register_HDMA5 == 0xff) + gbHdmaOn = 0; + +// We need to add the dmaClockticks for HDMA ! + if(gbSpeed) + gbDmaTicks = 17; + else + gbDmaTicks = 9; + + if (IFF & 0x80) + gbDmaTicks++; + +} + +// fix for Harley and Lego Racers +void gbCompareLYToLYC() +{ + if(register_LCDC & 0x80) { + if(register_LY == register_LYC) { + + // mark that we have a match + register_STAT |= 4; + + // check if we need an interrupt + if (register_STAT & 0x40) + { + // send LCD interrupt only if no interrupt 48h signal... + if (!gbInt48Signal) + { + register_IF |=2; + } + gbInt48Signal |= 8; + } + } + else // no match + { + register_STAT &= 0xfb; + gbInt48Signal &=~8; + } + } +} + +void gbWriteMemory(register u16 address, register u8 value) +{ + + if(address < 0x8000) { +#ifndef FINAL_VERSION + if(memorydebug && (address>0x3fff || address < 0x2000)) { + log("Memory register write %04x=%02x PC=%04x\n", + address, + value, + PC.W); + } + +#endif + if(mapper) + (*mapper)(address, value); + return; + + } + + if(address < 0xa000) { + // No access to Vram during mode 3 + // (used to emulate the gfx differences between GB & GBC-GBA/SP in Stunt Racer) + if ((gbLcdModeDelayed !=3) || + // This part is used to emulate a small difference between hardwares + // (check 8-in-1's arrow on GBA/GBC to verify it) + ((register_LY == 0) && ((gbHardware & 0xa) && (gbScreenOn==false) && + (register_LCDC & 0x80)) && + (gbLcdLYIncrementTicksDelayed ==(GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS)))) + gbMemoryMap[address>>12][address&0x0fff] = value; + return; + } + + // Used for the mirroring of 0xC000 in 0xE000 + if ((address >= 0xe000) && (address < 0xfe00)) + address &= ~0x2000; + + if(address < 0xc000) { +#ifndef FINAL_VERSION + if(memorydebug) { + log("Memory register write %04x=%02x PC=%04x\n", + address, + value, + PC.W); + } +#endif + + // Is that a correct fix ??? (it used to be 'if (mapper)')... + if(mapperRAM) + (*mapperRAM)(address, value); + return; + } + + + if(address < 0xfe00) { + gbMemoryMap[address>>12][address & 0x0fff] = value; + return; + } + + // OAM not accessible during mode 2 & 3. + if(address < 0xfea0) + { + if (((gbHardware & 0xa) && ((gbLcdMode | gbLcdModeDelayed) &2)) || + ((gbHardware & 5) && (((gbLcdModeDelayed == 2) && + (gbLcdTicksDelayed<=GBLCD_MODE_2_CLOCK_TICKS)) || + (gbLcdModeDelayed == 3)))) + return; + else + { + gbMemory[address] = value; + return; + } + } + + + + if((address > 0xfea0) && (address < 0xff00)){ // GBC allows reading/writing to that area + gbMemory[address] = value; + return; + } + + switch(address & 0x00ff) { + + case 0x00: { + gbMemory[0xff00] = ((gbMemory[0xff00] & 0xcf) | + (value & 0x30) | 0xc0); + if(gbSgbMode) { + gbSgbDoBitTransfer(value); + } + return; + } + + case 0x01: { + gbMemory[0xff01] = value; + return; + } + + // serial control + case 0x02: { + gbSerialOn = (value & 0x80); + gbMemory[0xff02] = value; + if(gbSerialOn) { + gbSerialTicks = GBSERIAL_CLOCK_TICKS; +#ifdef OLD_GB_LINK + if(linkConnected) { + if(value & 1) { + linkSendByte(0x100|gbMemory[0xFF01]); + Sleep(5); + } + } +#endif + } + + gbSerialBits = 0; + return; + } + + case 0x04: { + // DIV register resets on any write + // (not totally perfect, but better than nothing) + gbMemory[0xff04] = register_DIV = 0; + gbDivTicks = GBDIV_CLOCK_TICKS; + // Another weird timer 'bug' : + // Writing to DIV register resets the internal timer, + // and can also increase TIMA/trigger an interrupt + // in some cases... + if (gbTimerOn && !(gbInternalTimer & (gbTimerClockTicks>>1))) + { + gbMemory[0xff05] = ++register_TIMA; + if(register_TIMA == 0) { + // timer overflow! + + // reload timer modulo + gbMemory[0xff05] = register_TIMA = register_TMA; + + // flag interrupt + gbMemory[0xff0f] = register_IF |= 4; + } + } + gbInternalTimer = 0xff; + return; + } + case 0x05: + gbMemory[0xff05] = register_TIMA = value; + return; + + case 0x06: + gbMemory[0xff06] = register_TMA = value; + return; + + // TIMER control + case 0x07: { + + gbTimerModeChange = (((value & 3) != (register_TAC&3)) && (value & register_TAC & 4)) ? true : false; + gbTimerOnChange = (((value ^ register_TAC) & 4) == 4) ? true : false; + + gbTimerOn = (value & 4) ? true : false; + + if (gbTimerOnChange || gbTimerModeChange) + { + gbTimerMode = value & 3; + + switch(gbTimerMode) { + case 0: + gbTimerClockTicks = GBTIMER_MODE_0_CLOCK_TICKS; + break; + case 1: + gbTimerClockTicks = GBTIMER_MODE_1_CLOCK_TICKS; + break; + case 2: + gbTimerClockTicks = GBTIMER_MODE_2_CLOCK_TICKS; + break; + case 3: + gbTimerClockTicks = GBTIMER_MODE_3_CLOCK_TICKS; + break; + } + } + + + // This looks weird, but this emulates a bug in which register_TIMA + // is increased when writing to register_TAC + // (This fixes Korodice's long-delay between menus bug). + + if (gbTimerOnChange || gbTimerModeChange) + { + bool temp = false; + + if ((gbTimerOn && !gbTimerModeChange) && (gbTimerMode & 2) && + !(gbInternalTimer & 0x80) && (gbInternalTimer & (gbTimerClockTicks>>1)) && + !(gbInternalTimer & (gbTimerClockTicks>>5))) + temp = true; + else if ((!gbTimerOn && !gbTimerModeChange && gbTimerOnChange ) && ((gbTimerTicks-1) < (gbTimerClockTicks>>1))) + temp = true; + else if (gbTimerOn && gbTimerModeChange && !gbTimerOnChange) + { + switch(gbTimerMode & 3) + { + case 0x00: + temp = false; + break; + case 0x01: + if (((gbInternalTimer & 0x82) == 2) && (gbTimerTicks>(clockTicks+1))) + temp = true; + break; + case 0x02: + if (((gbInternalTimer & 0x88) == 0x8) && (gbTimerTicks>(clockTicks+1))) + temp = true; + break; + case 0x03: + if (((gbInternalTimer & 0xA0) == 0x20) && (gbTimerTicks>(clockTicks+1))) + temp = true; + break; + } + } + + if (temp) + { + gbMemory[0xff05] = ++register_TIMA; + if((register_TIMA & 0xff) == 0) { + // timer overflow! + + // reload timer modulo + gbMemory[0xff05] = register_TIMA = register_TMA; + + // flag interrupt + gbMemory[0xff0f] = register_IF |= 4; + } + } + } + gbMemory[0xff07] = register_TAC = value; + return; + } + + case 0x0f: { + gbMemory[0xff0f] = register_IF = value; + //gbMemory[0xff0f] = register_IE |= value; + return; + } + + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1a: + case 0x1b: + case 0x1c: + case 0x1d: + case 0x1e: + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: { + if (gbMemory[NR52] & 0x80) { + SOUND_EVENT(address,value); + return; + } + } + + case 0x26: { + SOUND_EVENT(address,value); + return; + } + + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3a: + case 0x3b: + case 0x3c: + case 0x3d: + case 0x3e: + case 0x3f: { + SOUND_EVENT(address,value); + //gbMemory[address] = value; + return; + } + + case 0x40: { + int lcdChange = (register_LCDC & 0x80) ^ (value & 0x80); + + // don't draw the window if it was not enabled and not being drawn before + if(!(register_LCDC & 0x20) && (value & 0x20) && gbWindowLine == -1 && + register_LY > register_WY) + gbWindowLine = 146; + // 007 fix : don't draw the first window's 1st line if it's enable 'too late' + // (ie. if register_LY == register_WY when enabling it) + // and move it to the next line + else if (!(register_LCDC & 0x20) && (value & 0x20) && (register_LY == register_WY)) + gbWindowLine = -2; + + + gbMemory[0xff40] = register_LCDC = value; + + + if(lcdChange) { + if((value & 0x80) && (!register_LCDCBusy)) { + + // if (!gbWhiteScreen && !gbSgbMask) + + // systemDrawScreen(); + + + + gbRegisterLYLCDCOffOn = (register_LY + 144) % 154; + + gbLcdTicks = GBLCD_MODE_2_CLOCK_TICKS - (gbSpeed ? 2 : 1); + gbLcdTicksDelayed = GBLCD_MODE_2_CLOCK_TICKS - (gbSpeed ? 1 : 0); + gbLcdLYIncrementTicks = GBLY_INCREMENT_CLOCK_TICKS - (gbSpeed ? 2 : 1); + gbLcdLYIncrementTicksDelayed = GBLY_INCREMENT_CLOCK_TICKS - (gbSpeed ? 1 : 0); + gbLcdMode = 2; + gbLcdModeDelayed = 2; + gbMemory[0xff41] = register_STAT = (register_STAT & 0xfc) | 2; + gbMemory[0xff44] = register_LY = 0x00; + gbInt48Signal = 0; + gbLYChangeHappened = false; + gbLCDChangeHappened = false; + gbWindowLine = 146; + oldRegister_WY = 146; + + // Fix for Namco Gallery Vol.2 + // (along with updating register_LCDC at the start of 'case 0x40') : + if(register_STAT & 0x20) + { + // send LCD interrupt only if no interrupt 48h signal... + if (!gbInt48Signal) + { + gbMemory[0xff0f] = register_IF |= 2; + } + gbInt48Signal |= 4; + } + gbCompareLYToLYC(); + + } else { + + register_LCDCBusy = clockTicks+6; + + //used to update the screen with white lines when it's off. + //(it looks strange, but it's pretty accurate) + + gbWhiteScreen = 0; + + gbScreenTicks = ((150-register_LY)*GBLY_INCREMENT_CLOCK_TICKS + + (49<<(gbSpeed ? 1 : 0))); + + // disable the screen rendering + gbScreenOn = false; + gbLcdTicks = 0; + gbLcdMode = 0; + gbLcdModeDelayed = 0; + gbMemory[0xff41] = register_STAT &= 0xfc; + gbInt48Signal = 0; + } + } + return; + } + + // STAT + case 0x41: { + //register_STAT = (register_STAT & 0x87) | + // (value & 0x7c); + gbMemory[0xff41] = register_STAT = (value & 0xf8) | (register_STAT & 0x07); // fix ? + // GB bug from Devrs FAQ + // proper fix + gbInt48Signal &= ((register_STAT>>3) & 0xF); + + if((register_LCDC & 0x80)) { + if ((register_STAT & 0x08) && (gbLcdMode == 0)) + { + if (!gbInt48Signal) + { + gbMemory[0xff0f] = register_IF |=2; + } + gbInt48Signal |= 1; + } + if ((register_STAT & 0x10) && (gbLcdMode == 1)) + { + if (!gbInt48Signal) + { + gbMemory[0xff0f] = register_IF |=2; + } + gbInt48Signal |= 2; + } + if ((register_STAT & 0x20) && (gbLcdMode == 2)) + { + if (!gbInt48Signal) + { + gbMemory[0xff0f] = register_IF |=2; + } + gbInt48Signal |= 4; + } + gbCompareLYToLYC(); + + gbMemory[0xff0f] = register_IF; + gbMemory[0xff41] = register_STAT; + + } + return; + } + + // SCY + case 0x42: { + int temp = -1; + + if ((gbLcdMode == 3) || (gbLcdModeDelayed == 3)) + temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) - + gbLcdLYIncrementTicks); + + if (temp >=0) + { + for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++) + if (temp < 300) + gbSCYLine[i] = value; + } + + else + memset(gbSCYLine, value, sizeof(gbSCYLine)); + + gbMemory[0xff42] = register_SCY = value; + return; + } + + // SCX + case 0x43: { + int temp = -1; + + if (gbLcdModeDelayed == 3) + temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) - + gbLcdLYIncrementTicksDelayed); + + if (temp >=0) + { + for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++) + if (temp < 300) + gbSCXLine[i] = value; + } + + else + memset(gbSCXLine, value, sizeof(gbSCXLine)); + + gbMemory[0xff43] = register_SCX = value; + return; + } + + // LY + case 0x44: { + // read only + return; + } + + // LYC + case 0x45: { + if (register_LYC != value) + { + gbMemory[0xff45] = register_LYC = value; + if(register_LCDC & 0x80) { + gbCompareLYToLYC(); + } + } + return; + } + + // DMA! + case 0x46: { + int source = value * 0x0100; + + gbCopyMemory(0xfe00, + source, + 0xa0); + gbMemory[0xff46] = register_DMA = value; + return; + } + + // BGP + case 0x47: { + + int temp = -1; + + gbMemory[0xff47] = value; + + if (gbLcdModeDelayed == 3) + temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) - + gbLcdLYIncrementTicksDelayed); + + if (temp >=0) + { + for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++) + if (temp < 300) + gbBgpLine[i] = value; + } + else + memset(gbBgpLine,value,sizeof(gbBgpLine)); + + gbBgp[0] = value & 0x03; + gbBgp[1] = (value & 0x0c)>>2; + gbBgp[2] = (value & 0x30)>>4; + gbBgp[3] = (value & 0xc0)>>6; + break; + } + + // OBP0 + case 0x48: { + int temp = -1; + + gbMemory[0xff48] = value; + + if (gbLcdModeDelayed == 3) + temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) - + gbLcdLYIncrementTicksDelayed); + + if (temp >=0) + { + for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++) + if (temp < 300) + gbObp0Line[i] = value; + } + else + memset(gbObp0Line,value,sizeof(gbObp0Line)); + + gbObp0[0] = value & 0x03; + gbObp0[1] = (value & 0x0c)>>2; + gbObp0[2] = (value & 0x30)>>4; + gbObp0[3] = (value & 0xc0)>>6; + break; + } + + // OBP1 + case 0x49: { + int temp = -1; + + gbMemory[0xff49] = value; + + if (gbLcdModeDelayed == 3) + temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) - + gbLcdLYIncrementTicksDelayed); + + if (temp >=0) + { + for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++) + if (temp < 300) + gbObp1Line[i] = value; + } + else + memset(gbObp1Line,value,sizeof(gbObp1Line)); + + gbObp1[0] = value & 0x03; + gbObp1[1] = (value & 0x0c)>>2; + gbObp1[2] = (value & 0x30)>>4; + gbObp1[3] = (value & 0xc0)>>6; + break; + } + + // WY + case 0x4a: + gbMemory[0xff4a] = register_WY = value; + if ((register_LY <= register_WY) && ((gbWindowLine < 0) || (gbWindowLine>=144))) + { + gbWindowLine = -1; + oldRegister_WY = register_WY; + } + return; + + // WX + case 0x4b: + gbMemory[0xff4b] = register_WX = value; + return; + + // KEY1 + case 0x4d: { + if(gbCgbMode) { + gbMemory[0xff4d] = (gbMemory[0xff4d] & 0x80) | (value & 1) | 0x7e; + return; + } + } + break; + + // VBK + case 0x4f: { + if(gbCgbMode) { + value = value & 1; + if(value == gbVramBank) + return; + + int vramAddress = value * 0x2000; + gbMemoryMap[0x08] = &gbVram[vramAddress]; + gbMemoryMap[0x09] = &gbVram[vramAddress + 0x1000]; + + gbVramBank = value; + register_VBK = value; + } + return; + } + break; + + // BOOTROM disable register (also gbCgbMode enabler if value & 0x10 ?) + case 0x50 : + { + if (useBios && inBios && !skipBios && (value & 1)) + { + gbMemoryMap[0x00] = &gbRom[0x0000]; + memcpy ((u8 *)(gbRom+0x100), (u8 *)(gbMemory + 0x100), 0xF00); + inBios = false; + } + } + + // HDMA1 + case 0x51: { + if(gbCgbMode) { + if(value > 0x7f && value < 0xa0) + value = 0; + + gbHdmaSource = (value << 8) | (gbHdmaSource & 0xf0); + + register_HDMA1 = value; + return; + } + } + break; + + // HDMA2 + case 0x52: { + if(gbCgbMode) { + value = value & 0xf0; + + gbHdmaSource = (gbHdmaSource & 0xff00) | (value); + + register_HDMA2 = value; + return; + } + } + break; + + // HDMA3 + case 0x53: { + if(gbCgbMode) { + value = value & 0x1f; + gbHdmaDestination = (value << 8) | (gbHdmaDestination & 0xf0); + gbHdmaDestination |= 0x8000; + register_HDMA3 = value; + return; + } + } + break; + + // HDMA4 + case 0x54: { + if(gbCgbMode) { + value = value & 0xf0; + gbHdmaDestination = (gbHdmaDestination & 0x1f00) | value; + gbHdmaDestination |= 0x8000; + register_HDMA4 = value; + return; + } + } + break; + + // HDMA5 + case 0x55: { + + if(gbCgbMode) { + gbHdmaBytes = 16 + (value & 0x7f) * 16; + if(gbHdmaOn) { + if(value & 0x80) { + gbMemory[0xff55] = register_HDMA5 = (value & 0x7f); + } else { + register_HDMA5 = 0xff; + gbHdmaOn = 0; + } + } else { + if(value & 0x80) { + gbHdmaOn = 1; + gbMemory[0xff55] = register_HDMA5 = value & 0x7f; + if(gbLcdModeDelayed == 0) + gbDoHdma(); + } else { + // we need to take the time it takes to complete the transfer into + // account... according to GB DEV FAQs, the setup time is the same + // for single and double speed, but the actual transfer takes the + // same time + if(gbSpeed) + gbDmaTicks = 2+16 * ((value & 0x7f) +1); + else + gbDmaTicks = 1+8 * ((value & 0x7f)+1); + + gbCopyMemory((gbHdmaDestination & 0x1ff0) | 0x8000, + gbHdmaSource & 0xfff0, + gbHdmaBytes); + gbHdmaDestination += gbHdmaBytes; + gbHdmaSource += gbHdmaBytes; + + gbMemory[0xff51] = register_HDMA1 = 0xff;// = (gbHdmaSource >> 8) & 0xff; + gbMemory[0xff52] = register_HDMA2 = 0xff;// = gbHdmaSource & 0xf0; + gbMemory[0xff53] = register_HDMA3 = 0xff;// = ((gbHdmaDestination - 0x8000) >> 8) & 0x1f; + gbMemory[0xff54] = register_HDMA4 = 0xff;// = gbHdmaDestination & 0xf0; + gbMemory[0xff55] = register_HDMA5 = 0xff; + } + } + return; + } + } + break; + + // BCPS + case 0x68: { + if(gbCgbMode) { + int paletteIndex = (value & 0x3f) >> 1; + int paletteHiLo = (value & 0x01); + + gbMemory[0xff68] = value; + + gbMemory[0xff69] = (paletteHiLo ? + (gbPalette[paletteIndex] >> 8) : + (gbPalette[paletteIndex] & 0x00ff)); + return; + } + } + break; + + // BCPD + case 0x69: { + if(gbCgbMode) { + int v = gbMemory[0xff68]; + int paletteIndex = (v & 0x3f) >> 1; + int paletteHiLo = (v & 0x01); + + // No access to gbPalette during mode 3 (Color Panel Demo) + if (((gbLcdModeDelayed != 3) && (!((gbLcdMode == 0) && (gbLcdTicks>=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-1)))) && (!gbSpeed)) || + (gbSpeed && ((gbLcdMode == 1) || (gbLcdMode == 2) || + ((gbLcdMode == 3) && (gbLcdTicks>(GBLCD_MODE_3_CLOCK_TICKS-2))) || + ((gbLcdMode == 0) && (gbLcdTicks<=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-2)))))) + { + gbMemory[0xff69] = value; + gbPalette[paletteIndex] = (paletteHiLo ? + ((value << 8) | (gbPalette[paletteIndex] & 0xff)) : + ((gbPalette[paletteIndex] & 0xff00) | (value))) & 0x7fff; + } + + + if(gbMemory[0xff68] & 0x80) { + int index = ((gbMemory[0xff68] & 0x3f) + 1) & 0x3f; + + gbMemory[0xff68] = (gbMemory[0xff68] & 0x80) | index; + gbMemory[0xff69] = (index & 1 ? + (gbPalette[index>>1] >> 8) : + (gbPalette[index>>1] & 0x00ff)); + } + return; + } + } + break; + + // OCPS + case 0x6a: { + if(gbCgbMode) { + int paletteIndex = (value & 0x3f) >> 1; + int paletteHiLo = (value & 0x01); + + paletteIndex += 32; + + gbMemory[0xff6a] = value; + + gbMemory[0xff6b] = (paletteHiLo ? + (gbPalette[paletteIndex] >> 8) : + (gbPalette[paletteIndex] & 0x00ff)); + return; + } + } + break; + + // OCPD + case 0x6b: { + + if(gbCgbMode) { + int v = gbMemory[0xff6a]; + int paletteIndex = (v & 0x3f) >> 1; + int paletteHiLo = (v & 0x01); + + paletteIndex += 32; + + // No access to gbPalette during mode 3 (Color Panel Demo) + if (((gbLcdModeDelayed != 3) && (!((gbLcdMode == 0) && (gbLcdTicks>=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-1)))) && (!gbSpeed)) || + (gbSpeed && ((gbLcdMode == 1) || (gbLcdMode == 2) || + ((gbLcdMode == 3) && (gbLcdTicks>(GBLCD_MODE_3_CLOCK_TICKS-2))) || + ((gbLcdMode == 0) && (gbLcdTicks<=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-2)))))) + { + gbMemory[0xff6b] = value; + gbPalette[paletteIndex] = (paletteHiLo ? + ((value << 8) | (gbPalette[paletteIndex] & 0xff)) : + ((gbPalette[paletteIndex] & 0xff00) | (value))) & 0x7fff; + } + + if(gbMemory[0xff6a] & 0x80) { + int index = ((gbMemory[0xff6a] & 0x3f) + 1) & 0x3f; + + gbMemory[0xff6a] = (gbMemory[0xff6a] & 0x80) | index; + + gbMemory[0xff6b] = (index & 1 ? + (gbPalette[(index>>1) + 32] >> 8) : + (gbPalette[(index>>1) + 32] & 0x00ff)); + } + return; + } + } + break; + + case 0x6c: { + gbMemory[0xff6c] = 0xfe | value; + return; + } + + + // SVBK + case 0x70: { + if(gbCgbMode) { + value = value & 7; + + int bank = value; + if(value == 0) + bank = 1; + + if(bank == gbWramBank) + return; + + int wramAddress = bank * 0x1000; + gbMemoryMap[0x0d] = &gbWram[wramAddress]; + + gbWramBank = bank; + gbMemory[0xff70] = register_SVBK = value; + return; + } + } + + case 0x75:{ + gbMemory[0xff75] = 0x8f | value; + return; + } + + case 0xff: { + gbMemory[0xffff] = register_IE = value; + return; + } + } + + if(address < 0xff80) + { + gbMemory[address] = value; + return; + } + + gbMemory[address] = value; +} + +u8 gbReadOpcode(register u16 address) +{ + if(gbCheatMap[address]) + return gbCheatRead(address); + + if(address < 0x8000) + return gbMemoryMap[address>>12][address&0x0fff]; + + if (address < 0xa000) + { + // A lot of 'ugly' checks... But only way to emulate this particular behaviour... + if ( + ( + (gbHardware & 0xa) && + ( + (gbLcdModeDelayed != 3) || + ( + ((register_LY == 0) && (gbScreenOn == false) && (register_LCDC & 0x80)) && + (gbLcdLYIncrementTicksDelayed == (GBLY_INCREMENT_CLOCK_TICKS - GBLCD_MODE_2_CLOCK_TICKS)) + ) + ) + ) + || + ( + (gbHardware & 0x5) && + (gbLcdModeDelayed != 3) && + ( + (gbLcdMode != 3) || + ((register_LY == 0) && ((gbScreenOn == false) && (register_LCDC & 0x80)) && + (gbLcdLYIncrementTicks == (GBLY_INCREMENT_CLOCK_TICKS - GBLCD_MODE_2_CLOCK_TICKS))) + ) + ) + ) + return gbMemoryMap[address >> 12][address & 0x0fff]; + + return 0xff; + } + + // Used for the mirroring of 0xC000 in 0xE000 + if ((address >= 0xe000) && (address < 0xfe00)) + address &= ~0x2000; + + switch(address & 0xf000) { + case 0x0a: + case 0x0b: + if(mapperReadRAM) + return mapperReadRAM(address); + break; + case 0x0f: + if(address > 0xff00) { + switch(address & 0x00ff) { + case 0x02: + return (gbMemory[0xff02]); + case 0x03: + return (0xff); + case 0x04: + return register_DIV; + case 0x05: + return register_TIMA; + case 0x06: + return register_TMA; + case 0x07: + return (0xf8 | register_TAC); + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x0e: + return (0xff); + case 0x0f: + return (0xe0 | gbMemory[0xff0f]); + case 0x40: + return register_LCDC; + case 0x41: + // This is a GB/C only bug (ie. not GBA/SP). + if ((gbHardware & 7) && (gbLcdMode == 2) && (gbLcdModeDelayed == 1) && (!gbSpeed)) + return (0x80 | (gbMemory[0xff41] & 0xFC)); + else + return (0x80 | gbMemory[0xff41]); + case 0x42: + return register_SCY; + case 0x43: + return register_SCX; + case 0x44: + if (((gbHardware & 7) && ((gbLcdMode == 1) && (gbLcdTicks == 0x71))) || + (!(register_LCDC & 0x80))) + return 0; + else + return register_LY; + case 0x45: + return register_LYC; + case 0x46: + return register_DMA; + case 0x4a: + return register_WY; + case 0x4b: + return register_WX; + case 0x4c: + return 0xff; + case 0x4f: + return (0xfe | register_VBK); + case 0x51: + return register_HDMA1; + case 0x52: + return register_HDMA2; + case 0x53: + return register_HDMA3; + case 0x54: + return register_HDMA4; + case 0x55: + return register_HDMA5; + case 0x68: + case 0x6a: + if (gbCgbMode) + return (0x40 | gbMemory[address]); + else + return 0xc0; + case 0x69: + case 0x6b: + if (gbCgbMode) + { + // No access to gbPalette during mode 3 (Color Panel Demo) + if (((gbLcdModeDelayed != 3) && (!((gbLcdMode == 0) && (gbLcdTicks>=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-1)))) && (!gbSpeed)) || + (gbSpeed && ((gbLcdMode == 1) || (gbLcdMode == 2) || + ((gbLcdMode == 3) && (gbLcdTicks>(GBLCD_MODE_3_CLOCK_TICKS-2))) || + ((gbLcdMode == 0) && (gbLcdTicks<=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-2)))))) + return (gbMemory[address]); + else + return 0xff; + } + else + return 0xff; + case 0x70: + if (gbCgbMode) + return (0xf8 | register_SVBK); + else + return 0xff; + case 0xff: + return register_IE; + } + } + // OAM not accessible during mode 2 & 3. + if(((address >= 0xfe00) && (address<0xfea0)) && + ((gbLcdMode | gbLcdModeDelayed) &2)) + return 0xff; + break; + } + + if ((address >= 0xfea0) && (address < 0xff00)) + { + if (gbHardware & 1) + return ((((address + ((address >> 4) - 0xfea)) >> 2) & 1) ? 0x00 : 0xff ); + else if (gbHardware & 2) + return gbMemoryMap[address>>12][address & 0x0fff]; + else if (gbHardware & 4) + return ((((address + ((address >> 4) - 0xfea)) >> 2) & 1) ? 0xff : 0x00 ); + else if (gbHardware & 8) + return ((address & 0xf0) |((address & 0xf0)>>4)); + } + + return gbMemoryMap[address>>12][address & 0x0fff]; +} + +u8 gbReadMemory(register u16 address) +{ + if(gbCheatMap[address]) + return gbCheatRead(address); + + + if(address < 0x8000) + return gbMemoryMap[address>>12][address&0x0fff]; + + + if (address < 0xa000) + { + // A lot of 'ugly' checks... But only way to emulate this particular behaviour... + if ( + ( + (gbHardware & 0xa) && + ( + (gbLcdModeDelayed != 3) || + ( + ((register_LY == 0) && (gbScreenOn == false) && (register_LCDC & 0x80)) && + (gbLcdLYIncrementTicksDelayed == (GBLY_INCREMENT_CLOCK_TICKS - GBLCD_MODE_2_CLOCK_TICKS)) + ) + ) + ) + || + ( + (gbHardware & 0x5) && + (gbLcdModeDelayed != 3) && + ( + (gbLcdMode != 3) || + ((register_LY == 0) && ((gbScreenOn == false) && (register_LCDC & 0x80)) && + (gbLcdLYIncrementTicks == (GBLY_INCREMENT_CLOCK_TICKS - GBLCD_MODE_2_CLOCK_TICKS))) + ) + ) + ) + return gbMemoryMap[address >> 12][address & 0x0fff]; + + return 0xff; + } + + if ((address >= 0xe000) && (address < 0xfe00)) + address &= ~0x2000; + + if(address < 0xc000) { +#ifndef FINAL_VERSION + if(memorydebug) { + log("Memory register read %04x PC=%04x\n", + address, + PC.W); + } +#endif + + // for the 2kb ram limit (fixes crash in shawu's story + // but now its sram test fails, as the it expects 8kb and not 2kb... + // So use the 'genericflashcard' option to fix it). + if (address<=(0xa000+gbRamSizeMask)) + { + if(mapperReadRAM) + return mapperReadRAM(address); + return gbMemoryMap[address>>12][address & 0x0fff]; + } + return 0xff; + } + + if(address >= 0xff00) { + if ( address >= 0xFF10 && address <= 0xFF3F ) + return gbSoundRead( address ); + + switch(address & 0x00ff) { + case 0x00: + { + if(gbSgbMode) { + gbSgbReadingController |= 4; + gbSgbResetPacketState(); + } + + int b = gbMemory[0xff00]; + + if((b & 0x30) == 0x20) { + b &= 0xf0; + + int joy = 0; + if(gbSgbMode && gbSgbMultiplayer) { + switch(gbSgbNextController) { + case 0x0f: + joy = 0; + break; + case 0x0e: + joy = 1; + break; + case 0x0d: + joy = 2; + break; + case 0x0c: + joy = 3; + break; + default: + joy = 0; + } + } + int joystate = gbJoymask[joy]; + if(!(joystate & 128)) + b |= 0x08; + if(!(joystate & 64)) + b |= 0x04; + if(!(joystate & 32)) + b |= 0x02; + if(!(joystate & 16)) + b |= 0x01; + + gbMemory[0xff00] = b; + } else if((b & 0x30) == 0x10) { + b &= 0xf0; + + int joy = 0; + if(gbSgbMode && gbSgbMultiplayer) { + switch(gbSgbNextController) { + case 0x0f: + joy = 0; + break; + case 0x0e: + joy = 1; + break; + case 0x0d: + joy = 2; + break; + case 0x0c: + joy = 3; + break; + default: + joy = 0; + } + } + int joystate = gbJoymask[joy]; + if(!(joystate & 8)) + b |= 0x08; + if(!(joystate & 4)) + b |= 0x04; + if(!(joystate & 2)) + b |= 0x02; + if(!(joystate & 1)) + b |= 0x01; + + gbMemory[0xff00] = b; + } else { + if(gbSgbMode && gbSgbMultiplayer) { + gbMemory[0xff00] = 0xf0 | gbSgbNextController; + } else { + gbMemory[0xff00] = 0xff; + } + } + } + return gbMemory[0xff00]; + break; + case 0x01: + return gbMemory[0xff01]; + case 0x02: + return (gbMemory[0xff02]); + case 0x04: + return register_DIV; + case 0x05: + return register_TIMA; + case 0x06: + return register_TMA; + case 0x07: + return (0xf8 | register_TAC); + case 0x0f: + return (0xe0 | gbMemory[0xff0f]); + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + if ((gbMemory[NR30] & 0x80) && (gbMemory[NR34] & 0x80)) + return 0xFF; + else + return gbMemoryMap[address>>12][address & 0x0fff]; + case 0x40: + return register_LCDC; + case 0x41: + // This is a GB/C only bug (ie. not GBA/SP). + if ((gbHardware & 7) && (gbLcdMode == 2) && (gbLcdModeDelayed == 1) && (!gbSpeed)) + return (0x80 | (gbMemory[0xff41] & 0xFC)); + else + return (0x80 | gbMemory[0xff41]); + case 0x42: + return register_SCY; + case 0x43: + return register_SCX; + case 0x44: + if (((gbHardware & 7) && ((gbLcdMode == 1) && (gbLcdTicks == 0x71))) || + (!(register_LCDC & 0x80))) + return (0); + else + return register_LY; + case 0x45: + return register_LYC; + case 0x46: + return register_DMA; + case 0x4a: + return register_WY; + case 0x4b: + return register_WX; + case 0x4f: + return (0xfe | register_VBK); + case 0x51: + return register_HDMA1; + case 0x52: + return register_HDMA2; + case 0x53: + return register_HDMA3; + case 0x54: + return register_HDMA4; + case 0x55: + return register_HDMA5; + case 0x68: + case 0x6a: + if (gbCgbMode) + return (0x40 | gbMemory[address]); + else + return 0xc0; + case 0x69: + case 0x6b: + if (gbCgbMode) + { + // No access to gbPalette during mode 3 (Color Panel Demo) + if (((gbLcdModeDelayed != 3) && (!((gbLcdMode == 0) && (gbLcdTicks>=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-1)))) && (!gbSpeed)) || + (gbSpeed && ((gbLcdMode == 1) || (gbLcdMode == 2) || + ((gbLcdMode == 3) && (gbLcdTicks>(GBLCD_MODE_3_CLOCK_TICKS-2))) || + ((gbLcdMode == 0) && (gbLcdTicks<=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-2)))))) + return (gbMemory[address]); + else + return 0xff; + } + else + return 0xff; + case 0x70: + if (gbCgbMode) + return (0xf8 | register_SVBK); + else + return 0xff; + case 0xff: + return register_IE; + } + } + // OAM not accessible during mode 2 & 3. + if(((address >= 0xfe00) && (address<0xfea0)) && + ((((gbLcdMode | gbLcdModeDelayed) & 2) && + (!(gbSpeed && (gbHardware & 0x2) && !(gbLcdModeDelayed & 2) && (gbLcdMode == 2)))) || + (gbSpeed && (gbHardware & 0x2) && (gbLcdModeDelayed == 0) && (gbLcdTicksDelayed == (GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]))))) + return 0xff; + + if ((address >= 0xfea0) && (address < 0xff00)) + { + if (gbHardware & 1) + return ((((address + ((address >> 4) - 0xfea)) >> 2) & 1) ? 0x00 : 0xff ); + else if (gbHardware & 2) + return gbMemoryMap[address>>12][address & 0x0fff]; + else if (gbHardware & 4) + return ((((address + ((address >> 4) - 0xfea)) >> 2) & 1) ? 0xff : 0x00 ); + else if (gbHardware & 8) + return ((address & 0xf0) |((address & 0xf0)>>4)); + } + + return gbMemoryMap[address>>12][address & 0x0fff]; +} + +void gbVblank_interrupt() +{ + gbCheatWrite(false); // Emulates GS codes. + gbMemory[0xff0f] = register_IF &= 0xfe; + gbWriteMemory(--SP.W, PC.B.B1); + gbWriteMemory(--SP.W, PC.B.B0); + PC.W = 0x40; +} + +void gbLcd_interrupt() +{ + gbCheatWrite(false); // Emulates GS codes. + gbMemory[0xff0f] = register_IF &= 0xfd; + gbWriteMemory(--SP.W, PC.B.B1); + gbWriteMemory(--SP.W, PC.B.B0); + PC.W = 0x48; +} + +void gbTimer_interrupt() +{ + gbMemory[0xff0f] = register_IF &= 0xfb; + gbWriteMemory(--SP.W, PC.B.B1); + gbWriteMemory(--SP.W, PC.B.B0); + PC.W = 0x50; +} + +void gbSerial_interrupt() +{ + gbMemory[0xff0f] = register_IF &= 0xf7; + gbWriteMemory(--SP.W, PC.B.B1); + gbWriteMemory(--SP.W, PC.B.B0); + PC.W = 0x58; +} + +void gbJoypad_interrupt() +{ + gbMemory[0xff0f] = register_IF &= 0xef; + gbWriteMemory(--SP.W, PC.B.B1); + gbWriteMemory(--SP.W, PC.B.B0); + PC.W = 0x60; +} + +void gbSpeedSwitch() +{ + gbBlackScreen = true; + if(gbSpeed == 0) { + gbSpeed = 1; + GBLCD_MODE_0_CLOCK_TICKS = 51 * 2; + GBLCD_MODE_1_CLOCK_TICKS = 1140 * 2; + GBLCD_MODE_2_CLOCK_TICKS = 20 * 2; + GBLCD_MODE_3_CLOCK_TICKS = 43 * 2; + GBLY_INCREMENT_CLOCK_TICKS = 114 * 2; + GBDIV_CLOCK_TICKS = 64; + GBTIMER_MODE_0_CLOCK_TICKS = 256; + GBTIMER_MODE_1_CLOCK_TICKS = 4; + GBTIMER_MODE_2_CLOCK_TICKS = 16; + GBTIMER_MODE_3_CLOCK_TICKS = 64; + GBSERIAL_CLOCK_TICKS = 128 * 2; + gbLcdTicks *= 2; + gbLcdTicksDelayed *=2; + gbLcdTicksDelayed--; + gbLcdLYIncrementTicks *= 2; + gbLcdLYIncrementTicksDelayed *= 2; + gbLcdLYIncrementTicksDelayed--; + gbSerialTicks *= 2; + //SOUND_CLOCK_TICKS = soundQuality * 24 * 2; + //soundTicks *= 2; + gbLine99Ticks = 3; + } else { + gbSpeed = 0; + GBLCD_MODE_0_CLOCK_TICKS = 51; + GBLCD_MODE_1_CLOCK_TICKS = 1140; + GBLCD_MODE_2_CLOCK_TICKS = 20; + GBLCD_MODE_3_CLOCK_TICKS = 43; + GBLY_INCREMENT_CLOCK_TICKS = 114; + GBDIV_CLOCK_TICKS = 64; + GBTIMER_MODE_0_CLOCK_TICKS = 256; + GBTIMER_MODE_1_CLOCK_TICKS = 4; + GBTIMER_MODE_2_CLOCK_TICKS = 16; + GBTIMER_MODE_3_CLOCK_TICKS = 64; + GBSERIAL_CLOCK_TICKS = 128; + gbLcdTicks >>= 1; + gbLcdTicksDelayed++; + gbLcdTicksDelayed >>=1; + gbLcdLYIncrementTicks >>= 1; + gbLcdLYIncrementTicksDelayed++; + gbLcdLYIncrementTicksDelayed >>= 1; + gbSerialTicks /= 2; + //SOUND_CLOCK_TICKS = soundQuality * 24; + //soundTicks /= 2; + gbLine99Ticks = 1; + if (gbHardware & 8) + gbLine99Ticks++; + } + gbDmaTicks += (134)*GBLY_INCREMENT_CLOCK_TICKS + (37<<(gbSpeed ? 1 : 0)); +} + +bool CPUIsGBBios(const char * file) +{ + if(strlen(file) > 4) { + const char * p = strrchr(file,'.'); + + if(p != NULL) { + if(_stricmp(p, ".gb") == 0) + return true; + if(_stricmp(p, ".bin") == 0) + return true; + if(_stricmp(p, ".bios") == 0) + return true; + if(_stricmp(p, ".rom") == 0) + return true; + } + } + + return false; +} + +void gbCPUInit(const char *biosFileName, bool useBiosFile) +{ + useBios = false; + if (useBiosFile) + { + int size = 0x100; + if(utilLoad(biosFileName, + CPUIsGBBios, + bios, + size)) { + if(size == 0x100) + useBios = true; + else + systemMessage(MSG_INVALID_BIOS_FILE_SIZE, N_("Invalid BOOTROM file size")); + } + } +} + +void gbGetHardwareType() +{ + gbCgbMode = 0; + gbSgbMode = 0; + if(gbRom[0x143] & 0x80) { + if((gbEmulatorType == 0) || + gbEmulatorType == 1 || + gbEmulatorType == 4) { + gbCgbMode = 1; + } + } + + if((gbCgbMode == 0 ) && (gbRom[0x146] == 0x03)) { + if(gbEmulatorType == 0 || + gbEmulatorType == 2 || + gbEmulatorType == 5) + gbSgbMode = 1; + } + + gbHardware = 1; // GB + if (((gbCgbMode == 1) && (gbEmulatorType == 0)) || (gbEmulatorType == 1)) + gbHardware = 2; // GBC + else if (((gbSgbMode == 1) && (gbEmulatorType == 0)) || (gbEmulatorType == 2) || (gbEmulatorType == 5)) + gbHardware = 4; // SGB(2) + else if (gbEmulatorType == 4) + gbHardware = 8; // GBA + + gbGBCColorType = 0; + if (gbHardware & 8) // If GBA is selected, choose the GBA default settings. + gbGBCColorType = 2; // (0 = GBC, 1 = GBA, 2 = GBASP) +} + +void gbReset() +{ + gbGetHardwareType(); + + oldRegister_WY = 146; + gbInterruptLaunched = 0; + + if(gbCgbMode == 1) { + if (gbVram == NULL) + gbVram = (u8 *)malloc(0x4000); + if (gbWram == NULL) + gbWram = (u8 *)malloc(0x8000); + memset(gbVram,0,0x4000); + memset(gbPalette,0, 2*128); + } + else + { + if(gbVram != NULL) { + free(gbVram); + gbVram = NULL; + } + if(gbWram != NULL) { + free(gbWram); + gbWram = NULL; + } + } + + gbLYChangeHappened = false; + gbLCDChangeHappened = false; + gbBlackScreen = false; + gbInterruptWait = 0; + gbDmaTicks = 0; + clockTicks = 0; + + // clean Wram + // This kinda emulates the startup state of Wram on GB/C (not very accurate, + // but way closer to the reality than filling it with 00es or FFes). + // On GBA/GBASP, it's kinda filled with random data. + // In all cases, most of the 2nd bank is filled with 00s. + // The starting data are important for some 'buggy' games, like Buster Brothers or + // Karamuchou ha Oosawagi!. + if (gbMemory != NULL) + { + memset(gbMemory,0xff, 65536); + for (int temp = 0xC000; temp < 0xE000; temp++) + if ((temp & 0x8) ^((temp & 0x800)>>8)) + { + if ((gbHardware & 0x02) && (gbGBCColorType == 0)) + gbMemory[temp] = 0x0; + else + gbMemory[temp] = 0x0f; + } + + else + gbMemory[temp] = 0xff; + } + + if(gbSpeed) { + gbSpeedSwitch(); + gbMemory[0xff4d] = 0; + } + + // GB bios set this memory area to 0 + // Fixes Pitman (J) title screen + if (gbHardware & 0x1) { + memset(&gbMemory[0x8000], 0x0, 0x2000); + } + + // clean LineBuffer + if (gbLineBuffer != NULL) + memset(gbLineBuffer, 0, sizeof(*gbLineBuffer)); + // clean Pix + if (pix != NULL) + memset(pix, 0, sizeof(*pix)); + // clean Vram + if (gbVram != NULL) + memset(gbVram, 0, 0x4000); + // clean Wram 2 + // This kinda emulates the startup state of Wram on GBC (not very accurate, + // but way closer to the reality than filling it with 00es or FFes). + // On GBA/GBASP, it's kinda filled with random data. + // In all cases, most of the 2nd bank is filled with 00s. + // The starting data are important for some 'buggy' games, like Buster Brothers or + // Karamuchou ha Oosawagi! + if (gbWram != NULL) + { + for (int i = 0; i<8; i++) + if (i != 2) + memcpy ((u16 *)(gbWram+i*0x1000), (u16 *)(gbMemory+0xC000), 0x1000); + } + + memset(gbSCYLine,0,sizeof(gbSCYLine)); + memset(gbSCXLine,0,sizeof(gbSCXLine)); + memset(gbBgpLine,0xfc,sizeof(gbBgpLine)); + if (gbHardware & 5) + { + memset(gbObp0Line,0xff,sizeof(gbObp0Line)); + memset(gbObp1Line,0xff,sizeof(gbObp1Line)); + } + else + { + memset(gbObp0Line,0x0,sizeof(gbObp0Line)); + memset(gbObp1Line,0x0,sizeof(gbObp1Line)); + } + memset(gbSpritesTicks,0x0,sizeof(gbSpritesTicks)); + + SP.W = 0xfffe; + AF.W = 0x01b0; + BC.W = 0x0013; + DE.W = 0x00d8; + HL.W = 0x014d; + PC.W = 0x0100; + IFF = 0; + gbInt48Signal = 0; + + register_TIMA = 0; + register_TMA = 0; + register_TAC = 0; + gbMemory[0xff0f] = register_IF = 1; + gbMemory[0xff40] = register_LCDC = 0x91; + gbMemory[0xff47] = 0xfc; + + if (gbCgbMode) + gbMemory[0xff4d] = 0x7e; + else + gbMemory[0xff4d] = 0xff; + + if (!gbCgbMode) + gbMemory[0xff70] = gbMemory[0xff74] = 0xff; + + if (gbCgbMode) + gbMemory[0xff56] = 0x3e; + else + gbMemory[0xff56] = 0xff; + + register_SCY = 0; + register_SCX = 0; + register_LYC = 0; + register_DMA = 0xff; + register_WY = 0; + register_WX = 0; + register_VBK = 0; + register_HDMA1 = 0xff; + register_HDMA2 = 0xff; + register_HDMA3 = 0xff; + register_HDMA4 = 0xff; + register_HDMA5 = 0xff; + register_SVBK = 0; + register_IE = 0; + + if (gbCgbMode) + gbMemory[0xff02] = 0x7c; + else + gbMemory[0xff02] = 0x7e; + + gbMemory[0xff03] = 0xff; + int i; + for (i = 0x8; i<0xf; i++) + gbMemory[0xff00+i] = 0xff; + + gbMemory[0xff13] = 0xff; + gbMemory[0xff15] = 0xff; + gbMemory[0xff18] = 0xff; + gbMemory[0xff1d] = 0xff; + gbMemory[0xff1f] = 0xff; + + for (i = 0x27; i<0x30; i++) + gbMemory[0xff00+i] = 0xff; + + gbMemory[0xff4c] = 0xff; + gbMemory[0xff4e] = 0xff; + gbMemory[0xff50] = 0xff; + + for (i = 0x57; i<0x68; i++) + gbMemory[0xff00+i] = 0xff; + + for (i = 0x5d; i<0x70; i++) + gbMemory[0xff00+i] = 0xff; + + gbMemory[0xff71] = 0xff; + + for (i = 0x78; i<0x80; i++) + gbMemory[0xff00+i] = 0xff; + + if (gbHardware & 0xa) + { + + if (gbHardware & 2) + { + AF.W = 0x1180; + BC.W = 0x0000; + } + else + { + AF.W = 0x1100; + BC.W = 0x0100; // GBA/SP have B = 0x01 (which means GBC & GBA/SP bootrom are different !) + } + + gbMemory[0xff26] = 0xf1; + if (gbCgbMode) + { + + gbMemory[0xff31] = 0xff; + gbMemory[0xff33] = 0xff; + gbMemory[0xff35] = 0xff; + gbMemory[0xff37] = 0xff; + gbMemory[0xff39] = 0xff; + gbMemory[0xff3b] = 0xff; + gbMemory[0xff3d] = 0xff; + + gbMemory[0xff44] = register_LY = 0x90; + gbDivTicks = 0x19 + ((gbHardware & 2) >> 1); + gbInternalTimer = 0x58 + ((gbHardware & 2) >> 1); + gbLcdTicks = GBLCD_MODE_1_CLOCK_TICKS - + (register_LY-0x8F)*GBLY_INCREMENT_CLOCK_TICKS + 72 + ((gbHardware & 2) >> 1); + gbLcdLYIncrementTicks = 72 + ((gbHardware & 2) >> 1); + gbMemory[0xff04] = register_DIV = 0x1E; + } + else + { + gbMemory[0xff44] = register_LY = 0x94; + gbDivTicks = 0x22 + ((gbHardware & 2) >> 1); + gbInternalTimer = 0x61 + ((gbHardware & 2) >> 1); + gbLcdTicks = GBLCD_MODE_1_CLOCK_TICKS - + (register_LY-0x8F)*GBLY_INCREMENT_CLOCK_TICKS + 25 + ((gbHardware & 2) >> 1); + gbLcdLYIncrementTicks = 25 + ((gbHardware & 2) >> 1); + gbMemory[0xff04] = register_DIV = 0x26; + } + + + DE.W = 0xff56; + HL.W = 0x000d; + + register_HDMA5 = 0xff; + gbMemory[0xff68] = 0xc0; + gbMemory[0xff6a] = 0xc0; + + + gbMemory[0xff41] = register_STAT = 0x81; + gbLcdMode = 1; + } + else + { + if (gbHardware & 4) + { + if(gbEmulatorType == 5) + AF.W = 0xffb0; + else + AF.W = 0x01b0; + BC.W = 0x0013; + DE.W = 0x00d8; + HL.W = 0x014d; + } + gbDivTicks = 14; + gbInternalTimer = gbDivTicks--; + gbMemory[0xff04] = register_DIV = 0xAB; + gbMemory[0xff41] = register_STAT = 0x85; + gbMemory[0xff44] = register_LY = 0x00; + gbLcdTicks = 15; + gbLcdLYIncrementTicks = 114+gbLcdTicks; + gbLcdMode = 1; + + // used for the handling of the gb Boot Rom + if ((gbHardware & 5) && (bios != NULL) && useBios && !skipBios) + { + memcpy ((u8 *)(gbMemory), (u8 *)(gbRom), 0x1000); + memcpy ((u8 *)(gbMemory), (u8 *)(bios), 0x100); + gbWhiteScreen = 0; + + gbInternalTimer = 0x3e; + gbDivTicks = 0x3f; + gbMemory[0xff04] = register_DIV = 0x00; + PC.W = 0x0000; + register_LCDC = 0x11; + gbScreenOn = false; + gbLcdTicks = 0; + gbLcdMode = 0; + gbLcdModeDelayed = 0; + gbMemory[0xff41] = register_STAT &= 0xfc; + gbInt48Signal = 0; + gbLcdLYIncrementTicks = GBLY_INCREMENT_CLOCK_TICKS; + } + } + + gbLine99Ticks = 1; + if (gbHardware & 8) + gbLine99Ticks++; + + gbLcdModeDelayed = gbLcdMode; + gbLcdTicksDelayed = gbLcdTicks+1; + gbLcdLYIncrementTicksDelayed = gbLcdLYIncrementTicks+1; + + + gbTimerModeChange = false; + gbTimerOnChange = false; + gbTimerOn = false; + + if(gbCgbMode) { + for (i = 0; i<0x20; i++) + gbPalette[i] = 0x7fff; + + // This is just to show that the starting values of the OBJ palettes are different + // between the 3 consoles, and that they 'kinda' stay the same at each reset + // (they can slightly change, somehow (randomly?)). + // You can check the effects of gbGBCColorType on the "Vila Caldan Color" gbc demo. + // Note that you could also check the Div register to check on which system the game + // is running (GB,GBC and GBA(SP) have different startup values). + // Unfortunatly, I don't have any SGB system, so I can't get their starting values. + + if (gbGBCColorType == 0) // GBC Hardware + { + gbPalette[0x20] = 0x0600; + gbPalette[0x21] = 0xfdf3; + gbPalette[0x22] = 0x041c; + gbPalette[0x23] = 0xf5db; + gbPalette[0x24] = 0x4419; + gbPalette[0x25] = 0x57ea; + gbPalette[0x26] = 0x2808; + gbPalette[0x27] = 0x9b75; + gbPalette[0x28] = 0x129b; + gbPalette[0x29] = 0xfce0; + gbPalette[0x2a] = 0x22da; + gbPalette[0x2b] = 0x4ac5; + gbPalette[0x2c] = 0x2d71; + gbPalette[0x2d] = 0xf0c2; + gbPalette[0x2e] = 0x5137; + gbPalette[0x2f] = 0x2d41; + gbPalette[0x30] = 0x6b2d; + gbPalette[0x31] = 0x2215; + gbPalette[0x32] = 0xbe0a; + gbPalette[0x33] = 0xc053; + gbPalette[0x34] = 0xfe5f; + gbPalette[0x35] = 0xe000; + gbPalette[0x36] = 0xbe10; + gbPalette[0x37] = 0x914d; + gbPalette[0x38] = 0x7f91; + gbPalette[0x39] = 0x02b5; + gbPalette[0x3a] = 0x77ac; + gbPalette[0x3b] = 0x14e5; + gbPalette[0x3c] = 0xcf89; + gbPalette[0x3d] = 0xa03d; + gbPalette[0x3e] = 0xfd50; + gbPalette[0x3f] = 0x91ff; + } + else if (gbGBCColorType == 1) // GBA Hardware + { + gbPalette[0x20] = 0xbe00; + gbPalette[0x21] = 0xfdfd; + gbPalette[0x22] = 0xbd69; + gbPalette[0x23] = 0x7baf; + gbPalette[0x24] = 0xf5ff; + gbPalette[0x25] = 0x3f8f; + gbPalette[0x26] = 0xcee5; + gbPalette[0x27] = 0x5bf7; + gbPalette[0x28] = 0xb35b; + gbPalette[0x29] = 0xef97; + gbPalette[0x2a] = 0xef9f; + gbPalette[0x2b] = 0x97f7; + gbPalette[0x2c] = 0x82bf; + gbPalette[0x2d] = 0x9f3d; + gbPalette[0x2e] = 0xddde; + gbPalette[0x2f] = 0xbad5; + gbPalette[0x30] = 0x3cba; + gbPalette[0x31] = 0xdfd7; + gbPalette[0x32] = 0xedea; + gbPalette[0x33] = 0xfeda; + gbPalette[0x34] = 0xf7f9; + gbPalette[0x35] = 0xfdee; + gbPalette[0x36] = 0x6d2f; + gbPalette[0x37] = 0xf0e6; + gbPalette[0x38] = 0xf7f0; + gbPalette[0x39] = 0xf296; + gbPalette[0x3a] = 0x3bf1; + gbPalette[0x3b] = 0xe211; + gbPalette[0x3c] = 0x69ba; + gbPalette[0x3d] = 0x3d0d; + gbPalette[0x3e] = 0xdfd3; + gbPalette[0x3f] = 0xa6ba; + } + else if (gbGBCColorType == 2) // GBASP Hardware + { + gbPalette[0x20] = 0x9c00; + gbPalette[0x21] = 0x6340; + gbPalette[0x22] = 0x10c6; + gbPalette[0x23] = 0xdb97; + gbPalette[0x24] = 0x7622; + gbPalette[0x25] = 0x3e57; + gbPalette[0x26] = 0x2e12; + gbPalette[0x27] = 0x95c3; + gbPalette[0x28] = 0x1095; + gbPalette[0x29] = 0x488c; + gbPalette[0x2a] = 0x8241; + gbPalette[0x2b] = 0xde8c; + gbPalette[0x2c] = 0xfabc; + gbPalette[0x2d] = 0x0e81; + gbPalette[0x2e] = 0x7675; + gbPalette[0x2f] = 0xfdec; + gbPalette[0x30] = 0xddfd; + gbPalette[0x31] = 0x5995; + gbPalette[0x32] = 0x066a; + gbPalette[0x33] = 0xed1e; + gbPalette[0x34] = 0x1e84; + gbPalette[0x35] = 0x1d14; + gbPalette[0x36] = 0x11c3; + gbPalette[0x37] = 0x2749; + gbPalette[0x38] = 0xa727; + gbPalette[0x39] = 0x6266; + gbPalette[0x3a] = 0xe27b; + gbPalette[0x3b] = 0xe3fc; + gbPalette[0x3c] = 0x1f76; + gbPalette[0x3d] = 0xf158; + gbPalette[0x3e] = 0x468e; + gbPalette[0x3f] = 0xa540; + } + } else { + if(gbSgbMode) { + for(i = 0; i < 8; i++) + gbPalette[i] = systemGbPalette[gbPaletteOption*8+i]; + + } + for(i = 0; i < 8; i++) + gbPalette[i] = systemGbPalette[gbPaletteOption*8+i]; + } + + GBTIMER_MODE_0_CLOCK_TICKS = 256; + GBTIMER_MODE_1_CLOCK_TICKS = 4; + GBTIMER_MODE_2_CLOCK_TICKS = 16; + GBTIMER_MODE_3_CLOCK_TICKS = 64; + + GBLY_INCREMENT_CLOCK_TICKS = 114; + gbTimerTicks = GBTIMER_MODE_0_CLOCK_TICKS; + gbTimerClockTicks = GBTIMER_MODE_0_CLOCK_TICKS; + gbSerialTicks = 0; + gbSerialBits = 0; + gbSerialOn = 0; + gbWindowLine = -1; + gbTimerOn = false; + gbTimerMode = 0; + gbSpeed = 0; + gbJoymask[0] = gbJoymask[1] = gbJoymask[2] = gbJoymask[3] = 0; + + if(gbCgbMode) { + gbSpeed = 0; + gbHdmaOn = 0; + gbHdmaSource = 0x99d0; + gbHdmaDestination = 0x99d0; + gbVramBank = 0; + gbWramBank = 1; + + } + + // used to clean the borders + if (gbSgbMode) + { + gbSgbResetFlag = true; + gbSgbReset(); + if (gbBorderOn) + gbSgbRenderBorder(); + gbSgbResetFlag = false; + } + + for(i = 0; i < 4; i++) + gbBgp[i] = gbObp0[i] = gbObp1[i] = i; + + memset(&gbDataMBC1,0, sizeof(gbDataMBC1)); + gbDataMBC1.mapperROMBank = 1; + + gbDataMBC2.mapperRAMEnable = 0; + gbDataMBC2.mapperROMBank = 1; + + memset(&gbDataMBC3,0, 6 * sizeof(int)); + gbDataMBC3.mapperROMBank = 1; + + memset(&gbDataMBC5, 0, sizeof(gbDataMBC5)); + gbDataMBC5.mapperROMBank = 1; + + memset(&gbDataHuC1, 0, sizeof(gbDataHuC1)); + gbDataHuC1.mapperROMBank = 1; + + memset(&gbDataHuC3, 0, sizeof(gbDataHuC3)); + gbDataHuC3.mapperROMBank = 1; + + memset(&gbDataTAMA5,0, 26*sizeof(int)); + gbDataTAMA5.mapperROMBank = 1; + + memset(&gbDataMMM01,0, sizeof(gbDataMMM01)); + gbDataMMM01.mapperROMBank = 1; + + if (useBios && !skipBios && (gbHardware & 5)) + { + gbMemoryMap[0x00] = &gbMemory[0x0000]; + inBios = true; + } + else + { + gbMemoryMap[0x00] = &gbRom[0x0000]; + inBios = false; + } + + gbMemoryMap[0x01] = &gbRom[0x1000]; + gbMemoryMap[0x02] = &gbRom[0x2000]; + gbMemoryMap[0x03] = &gbRom[0x3000]; + gbMemoryMap[0x04] = &gbRom[0x4000]; + gbMemoryMap[0x05] = &gbRom[0x5000]; + gbMemoryMap[0x06] = &gbRom[0x6000]; + gbMemoryMap[0x07] = &gbRom[0x7000]; + if(gbCgbMode) { + gbMemoryMap[0x08] = &gbVram[0x0000]; + gbMemoryMap[0x09] = &gbVram[0x1000]; + gbMemoryMap[0x0a] = &gbMemory[0xa000]; + gbMemoryMap[0x0b] = &gbMemory[0xb000]; + gbMemoryMap[0x0c] = &gbMemory[0xc000]; + gbMemoryMap[0x0d] = &gbWram[0x1000]; + gbMemoryMap[0x0e] = &gbMemory[0xe000]; + gbMemoryMap[0x0f] = &gbMemory[0xf000]; + } else { + gbMemoryMap[0x08] = &gbMemory[0x8000]; + gbMemoryMap[0x09] = &gbMemory[0x9000]; + gbMemoryMap[0x0a] = &gbMemory[0xa000]; + gbMemoryMap[0x0b] = &gbMemory[0xb000]; + gbMemoryMap[0x0c] = &gbMemory[0xc000]; + gbMemoryMap[0x0d] = &gbMemory[0xd000]; + gbMemoryMap[0x0e] = &gbMemory[0xe000]; + gbMemoryMap[0x0f] = &gbMemory[0xf000]; + } + + if(gbRam) { + gbMemoryMap[0x0a] = &gbRam[0x0000]; + gbMemoryMap[0x0b] = &gbRam[0x1000]; + } + + gbSoundReset(); + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + gbLastTime = systemGetClock(); + gbFrameCount = 0; + + gbScreenOn = true; + gbSystemMessage = false; + + gbCheatWrite(true); // Emulates GS codes. + +} + +void gbWriteSaveMBC1(const char * name) +{ + if (gbRam) + { + FILE *gzFile = fopen(name,"wb"); + + if(gzFile == NULL) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); + return; + } + + fwrite(gbRam, + 1, + (gbRamSizeMask+1), + gzFile); + + fclose(gzFile); + } +} + +void gbWriteSaveMBC2(const char * name) +{ + if (gbRam) + { + FILE *file = fopen(name, "wb"); + + if(file == NULL) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); + return; + } + + fwrite(gbMemoryMap[0x0a], + 1, + 512, + file); + + fclose(file); + } +} + +void gbWriteSaveMBC3(const char * name, bool extendedSave) +{ + if (gbRam || extendedSave) + { + FILE *gzFile = fopen(name,"wb"); + if (gbRam) + { + + if(gzFile == NULL) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); + return; + } + + fwrite(gbRam, + 1, + (gbRamSizeMask+1), + gzFile); + } + + if(extendedSave) + fwrite(&gbDataMBC3.mapperSeconds, + 1, + 10*sizeof(int) + sizeof(time_t), + gzFile); + + fclose(gzFile); + } +} + +void gbWriteSaveMBC5(const char * name) +{ + if (gbRam) + { + FILE *gzFile = fopen(name,"wb"); + + if(gzFile == NULL) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); + return; + } + + fwrite(gbRam, + 1, + (gbRamSizeMask+1), + gzFile); + + fclose(gzFile); + } +} + +void gbWriteSaveMBC7(const char * name) +{ + if (gbRam) + { + FILE *file = fopen(name, "wb"); + + if(file == NULL) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); + return; + } + + fwrite(&gbMemory[0xa000], + 1, + 256, + file); + + fclose(file); + } +} + +void gbWriteSaveTAMA5(const char * name, bool extendedSave) +{ + FILE *gzFile = fopen(name,"wb"); + + if(gzFile == NULL) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); + return; + } + if (gbRam) + fwrite(gbRam, + 1, + (gbRamSizeMask+1), + gzFile); + + fwrite(gbTAMA5ram, + 1, + (gbTAMA5ramSize), + gzFile); + + if(extendedSave) + fwrite(&gbDataTAMA5.mapperSeconds, + 1, + 14*sizeof(int) + sizeof(time_t), + gzFile); + + fclose(gzFile); +} + +void gbWriteSaveMMM01(const char * name) +{ + if (gbRam) + { + FILE *gzFile = fopen(name,"wb"); + + if(gzFile == NULL) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); + return; + } + + fwrite(gbRam, + 1, + (gbRamSizeMask+1), + gzFile); + + fclose(gzFile); + } +} + + +bool gbReadSaveMBC1(const char * name) +{ + if (gbRam) + { + gzFile gzFile = gzopen(name, "rb"); + + if(gzFile == NULL) { + return false; + } + + int read = gzread(gzFile, + gbRam, + (gbRamSizeMask+1)); + + if(read != (gbRamSizeMask+1)) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gzclose(gzFile); + gbBatteryError = true; + return false; + } + + // Also checks if the battery file it bigger than gbRamSizeMask+1 ! + u8 data[1]; + data[0] = 0; + + read = gzread(gzFile, + data, + 1); + if(read >0) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gzclose(gzFile); + gbBatteryError = true; + return false; + } + + gzclose(gzFile); + return true; + } + else + return false; +} + + +bool gbReadSaveMBC2(const char * name) +{ + if (gbRam) + { + FILE *file = fopen(name, "rb"); + + if(file == NULL) { + return false; + } + + size_t read = fread(gbMemoryMap[0x0a], + 1, + 512, + file); + + if(read != 512) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + fclose(file); + gbBatteryError = true; + return false; + } + + // Also checks if the battery file it bigger than gbRamSizeMask+1 ! + u8 data[1]; + data[0] = 0; + + read = fread(&data[0], + 1, + 1, + file); + if(read > 0) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + fclose(file); + gbBatteryError = true; + return false; + } + + fclose(file); + return true; + } + else + return false; +} + +bool gbReadSaveMBC3(const char * name) +{ + gzFile gzFile = gzopen(name, "rb"); + + if(gzFile == NULL) { + return false; + } + + int read = 0; + + if (gbRam) + read = gzread(gzFile, + gbRam, + (gbRamSizeMask+1)); + else + read = (gbRamSizeMask+1); + + + bool res = true; + + if(read != (gbRamSizeMask+1)) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gbBatteryError = true; + res = false; + } else if ((gbRomType == 0xf) || (gbRomType == 0x10)){ + read = gzread(gzFile, + &gbDataMBC3.mapperSeconds, + sizeof(int)*10 + sizeof(time_t)); + + if(read != (sizeof(int)*10 + sizeof(time_t)) && read != 0) { + systemMessage(MSG_FAILED_TO_READ_RTC,N_("Failed to read RTC from save game %s (continuing)"), + name); + res = false; + } + else if (read == 0) + { + systemMessage(MSG_FAILED_TO_READ_RTC,N_("Failed to read RTC from save game %s (continuing)"), + name); + res = false; + } + else + { + // Also checks if the battery file it bigger than gbRamSizeMask+1+RTC ! + u8 data[1]; + data[0] = 0; + + read = gzread(gzFile, + data, + 1); + if(read >0) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gbBatteryError = true; + res = false; + } + } + } + + gzclose(gzFile); + return res; +} + +bool gbReadSaveMBC5(const char * name) +{ + if (gbRam) + { + gzFile gzFile = gzopen(name, "rb"); + + if(gzFile == NULL) { + return false; + } + + int read = gzread(gzFile, + gbRam, + (gbRamSizeMask+1)); + + if(read != (gbRamSizeMask+1)) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gzclose(gzFile); + gbBatteryError = true; + return false; + } + + + // Also checks if the battery file it bigger than gbRamSizeMask+1 ! + u8 data[1]; + data[0] = 0; + + read = gzread(gzFile, + data, + 1); + if(read >0) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gzclose(gzFile); + gbBatteryError = true; + return false; + } + + gzclose(gzFile); + return true; + } + else + return false; +} + +bool gbReadSaveMBC7(const char * name) +{ + if (gbRam) + { + FILE *file = fopen(name, "rb"); + + if(file == NULL) { + return false; + } + + size_t read = fread(&gbMemory[0xa000], + 1, + 256, + file); + + if(read != 256) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + fclose(file); + gbBatteryError = true; + return false; + } + + // Also checks if the battery file it bigger than gbRamSizeMask+1 ! + u8 data[1]; + data[0] = 0; + + read = fread(&data[0], + 1, + 1, + file); + if(read > 0) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + fclose(file); + gbBatteryError = true; + return false; + } + + fclose(file); + return true; + } + else + return false; +} + +bool gbReadSaveTAMA5(const char * name) +{ + gzFile gzFile = gzopen(name, "rb"); + + if(gzFile == NULL) { + return false; + } + + int read = 0; + + if (gbRam) + read = gzread(gzFile, + gbRam, + (gbRamSizeMask+1)); + else + read = gbRamSizeMask; + + read += gzread(gzFile, + gbTAMA5ram, + gbTAMA5ramSize); + + bool res = true; + + if(read != (gbRamSizeMask+gbTAMA5ramSize+1)) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gbBatteryError = true; + res = false; + } else { + read = gzread(gzFile, + &gbDataTAMA5.mapperSeconds, + sizeof(int)*14 + sizeof(time_t)); + + if(read != (sizeof(int)*14 + sizeof(time_t)) && read != 0) { + systemMessage(MSG_FAILED_TO_READ_RTC,N_("Failed to read RTC from save game %s (continuing)"), + name); + res = false; + } + else if (read == 0) + { + systemMessage(MSG_FAILED_TO_READ_RTC,N_("Failed to read RTC from save game %s (continuing)"), + name); + res = false; + } + else + { + // Also checks if the battery file it bigger than gbRamSizeMask+1+RTC ! + u8 data[1]; + data[0] = 0; + + read = gzread(gzFile, + data, + 1); + if(read >0) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gbBatteryError = true; + res = false; + } + } + } + + gzclose(gzFile); + return res; +} + + +bool gbReadSaveMMM01(const char * name) +{ + if (gbRam) + { + gzFile gzFile = gzopen(name, "rb"); + + if(gzFile == NULL) { + return false; + } + + int read = gzread(gzFile, + gbRam, + (gbRamSizeMask+1)); + + if(read != (gbRamSizeMask+1)) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gzclose(gzFile); + gbBatteryError = true; + return false; + } + + // Also checks if the battery file it bigger than gbRamSizeMask+1 ! + u8 data[1]; + data[0] = 0; + + read = gzread(gzFile, + data, + 1); + if(read >0) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gzclose(gzFile); + gbBatteryError = true; + return false; + } + + gzclose(gzFile); + return true; + } + else + return false; +} + +void gbInit() +{ + gbGenFilter(); + gbSgbInit(); + + gbMemory = (u8 *)malloc(65536); + + pix = (u8 *)calloc(1,4*257*226); + + gbLineBuffer = (u16 *)malloc(160 * sizeof(u16)); +} + +bool gbWriteBatteryFile(const char *file, bool extendedSave) +{ + if(gbBattery) { + switch(gbRomType) { + case 0x03: + gbWriteSaveMBC1(file); + break; + case 0x06: + gbWriteSaveMBC2(file); + break; + case 0x0d: + gbWriteSaveMMM01(file); + break; + case 0x0f: + case 0x10: + gbWriteSaveMBC3(file, extendedSave); + break; + case 0x13: + case 0xfc: + gbWriteSaveMBC3(file, false); + case 0x1b: + case 0x1e: + gbWriteSaveMBC5(file); + break; + case 0x22: + gbWriteSaveMBC7(file); + break; + case 0xfd: + gbWriteSaveTAMA5(file, extendedSave); + break; + case 0xff: + gbWriteSaveMBC1(file); + break; + } + } + return true; +} + +bool gbWriteBatteryFile(const char *file) +{ + if (!gbBatteryError) + { + gbWriteBatteryFile(file, true); + return true; + } + else return false; +} + +bool gbReadBatteryFile(const char *file) +{ + bool res = false; + if(gbBattery) { + switch(gbRomType) { + case 0x03: + res = gbReadSaveMBC1(file); + break; + case 0x06: + res = gbReadSaveMBC2(file); + break; + case 0x0d: + res = gbReadSaveMMM01(file); + break; + case 0x0f: + case 0x10: + if(!gbReadSaveMBC3(file)) { + time(&gbDataMBC3.mapperLastTime); + struct tm *lt; + lt = localtime(&gbDataMBC3.mapperLastTime); + gbDataMBC3.mapperSeconds = lt->tm_sec; + gbDataMBC3.mapperMinutes = lt->tm_min; + gbDataMBC3.mapperHours = lt->tm_hour; + gbDataMBC3.mapperDays = lt->tm_yday & 255; + gbDataMBC3.mapperControl = (gbDataMBC3.mapperControl & 0xfe) | + (lt->tm_yday > 255 ? 1: 0); + res = false; + break; + } + res = true; + break; + case 0x13: + case 0xfc: + res = gbReadSaveMBC3(file); + break; + case 0x1b: + case 0x1e: + res = gbReadSaveMBC5(file); + break; + case 0x22: + res = gbReadSaveMBC7(file); + break; + case 0xfd: + if(!gbReadSaveTAMA5(file)) { + u8 gbDaysinMonth [12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + time(&gbDataTAMA5.mapperLastTime); + struct tm *lt; + lt = localtime(&gbDataTAMA5.mapperLastTime); + gbDataTAMA5.mapperSeconds = lt->tm_sec; + gbDataTAMA5.mapperMinutes = lt->tm_min; + gbDataTAMA5.mapperHours = lt->tm_hour; + gbDataTAMA5.mapperDays = 1; + gbDataTAMA5.mapperMonths = 1; + gbDataTAMA5.mapperYears = 1970; + int days = lt->tm_yday+365*3; + while (days) + { + gbDataTAMA5.mapperDays++; + days--; + if (gbDataTAMA5.mapperDays>gbDaysinMonth[gbDataTAMA5.mapperMonths-1]) + { + gbDataTAMA5.mapperDays = 1; + gbDataTAMA5.mapperMonths++; + if (gbDataTAMA5.mapperMonths>12) + { + gbDataTAMA5.mapperMonths = 1; + gbDataTAMA5.mapperYears++; + if ((gbDataTAMA5.mapperYears & 3) == 0) + gbDaysinMonth[1] = 29; + else + gbDaysinMonth[1] = 28; + } + } + } + gbDataTAMA5.mapperControl = (gbDataTAMA5.mapperControl & 0xfe) | + (lt->tm_yday > 255 ? 1: 0); + res = false; + break; + } + res = true; + break; + case 0xff: + res = gbReadSaveMBC1(file); + break; + } + } + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + return res; +} + +bool gbReadGSASnapshot(const char *fileName) +{ + FILE *file = fopen(fileName, "rb"); + + if(!file) { + systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName); + return false; + } + + fseek(file, 0x4, SEEK_SET); + char buffer[16]; + char buffer2[16]; + fread(buffer, 1, 15, file); + buffer[15] = 0; + memcpy(buffer2, &gbRom[0x134], 15); + buffer2[15] = 0; + if(memcmp(buffer, buffer2, 15)) { + systemMessage(MSG_CANNOT_IMPORT_SNAPSHOT_FOR, + N_("Cannot import snapshot for %s. Current game is %s"), + buffer, + buffer2); + fclose(file); + return false; + } + fseek(file, 0x13, SEEK_SET); + size_t read = 0; + int toRead = 0; + switch(gbRomType) { + case 0x03: + case 0x0f: + case 0x10: + case 0x13: + case 0x1b: + case 0x1e: + case 0xff: + read = fread(gbRam, 1, (gbRamSizeMask+1), file); + toRead = (gbRamSizeMask+1); + break; + case 0x06: + case 0x22: + read = fread(&gbMemory[0xa000],1,256,file); + toRead = 256; + break; + default: + systemMessage(MSG_UNSUPPORTED_SNAPSHOT_FILE, + N_("Unsupported snapshot file %s"), + fileName); + fclose(file); + return false; + } + fclose(file); + gbReset(); + return true; +} + +variable_desc gbSaveGameStruct[] = { + { &PC.W, sizeof(u16) }, + { &SP.W, sizeof(u16) }, + { &AF.W, sizeof(u16) }, + { &BC.W, sizeof(u16) }, + { &DE.W, sizeof(u16) }, + { &HL.W, sizeof(u16) }, + { &IFF, sizeof(u8) }, + { &GBLCD_MODE_0_CLOCK_TICKS, sizeof(int) }, + { &GBLCD_MODE_1_CLOCK_TICKS, sizeof(int) }, + { &GBLCD_MODE_2_CLOCK_TICKS, sizeof(int) }, + { &GBLCD_MODE_3_CLOCK_TICKS, sizeof(int) }, + { &GBDIV_CLOCK_TICKS, sizeof(int) }, + { &GBLY_INCREMENT_CLOCK_TICKS, sizeof(int) }, + { &GBTIMER_MODE_0_CLOCK_TICKS, sizeof(int) }, + { &GBTIMER_MODE_1_CLOCK_TICKS, sizeof(int) }, + { &GBTIMER_MODE_2_CLOCK_TICKS, sizeof(int) }, + { &GBTIMER_MODE_3_CLOCK_TICKS, sizeof(int) }, + { &GBSERIAL_CLOCK_TICKS, sizeof(int) }, + { &GBSYNCHRONIZE_CLOCK_TICKS, sizeof(int) }, + { &gbDivTicks, sizeof(int) }, + { &gbLcdMode, sizeof(int) }, + { &gbLcdTicks, sizeof(int) }, + { &gbLcdLYIncrementTicks, sizeof(int) }, + { &gbTimerTicks, sizeof(int) }, + { &gbTimerClockTicks, sizeof(int) }, + { &gbSerialTicks, sizeof(int) }, + { &gbSerialBits, sizeof(int) }, + { &gbInt48Signal, sizeof(int) }, + { &gbInterruptWait, sizeof(int) }, + { &gbSynchronizeTicks, sizeof(int) }, + { &gbTimerOn, sizeof(int) }, + { &gbTimerMode, sizeof(int) }, + { &gbSerialOn, sizeof(int) }, + { &gbWindowLine, sizeof(int) }, + { &gbCgbMode, sizeof(int) }, + { &gbVramBank, sizeof(int) }, + { &gbWramBank, sizeof(int) }, + { &gbHdmaSource, sizeof(int) }, + { &gbHdmaDestination, sizeof(int) }, + { &gbHdmaBytes, sizeof(int) }, + { &gbHdmaOn, sizeof(int) }, + { &gbSpeed, sizeof(int) }, + { &gbSgbMode, sizeof(int) }, + { ®ister_DIV, sizeof(u8) }, + { ®ister_TIMA, sizeof(u8) }, + { ®ister_TMA, sizeof(u8) }, + { ®ister_TAC, sizeof(u8) }, + { ®ister_IF, sizeof(u8) }, + { ®ister_LCDC, sizeof(u8) }, + { ®ister_STAT, sizeof(u8) }, + { ®ister_SCY, sizeof(u8) }, + { ®ister_SCX, sizeof(u8) }, + { ®ister_LY, sizeof(u8) }, + { ®ister_LYC, sizeof(u8) }, + { ®ister_DMA, sizeof(u8) }, + { ®ister_WY, sizeof(u8) }, + { ®ister_WX, sizeof(u8) }, + { ®ister_VBK, sizeof(u8) }, + { ®ister_HDMA1, sizeof(u8) }, + { ®ister_HDMA2, sizeof(u8) }, + { ®ister_HDMA3, sizeof(u8) }, + { ®ister_HDMA4, sizeof(u8) }, + { ®ister_HDMA5, sizeof(u8) }, + { ®ister_SVBK, sizeof(u8) }, + { ®ister_IE , sizeof(u8) }, + { &gbBgp[0], sizeof(u8) }, + { &gbBgp[1], sizeof(u8) }, + { &gbBgp[2], sizeof(u8) }, + { &gbBgp[3], sizeof(u8) }, + { &gbObp0[0], sizeof(u8) }, + { &gbObp0[1], sizeof(u8) }, + { &gbObp0[2], sizeof(u8) }, + { &gbObp0[3], sizeof(u8) }, + { &gbObp1[0], sizeof(u8) }, + { &gbObp1[1], sizeof(u8) }, + { &gbObp1[2], sizeof(u8) }, + { &gbObp1[3], sizeof(u8) }, + { NULL, 0 } +}; + + +static bool gbWriteSaveState(gzFile gzFile) +{ + + utilWriteInt(gzFile, GBSAVE_GAME_VERSION); + + utilGzWrite(gzFile, &gbRom[0x134], 15); + + utilWriteInt(gzFile, useBios); + utilWriteInt(gzFile, inBios); + + utilWriteData(gzFile, gbSaveGameStruct); + + utilGzWrite(gzFile, &IFF, 2); + + if(gbSgbMode) { + gbSgbSaveGame(gzFile); + } + + utilGzWrite(gzFile, &gbDataMBC1, sizeof(gbDataMBC1)); + utilGzWrite(gzFile, &gbDataMBC2, sizeof(gbDataMBC2)); + utilGzWrite(gzFile, &gbDataMBC3, sizeof(gbDataMBC3)); + utilGzWrite(gzFile, &gbDataMBC5, sizeof(gbDataMBC5)); + utilGzWrite(gzFile, &gbDataHuC1, sizeof(gbDataHuC1)); + utilGzWrite(gzFile, &gbDataHuC3, sizeof(gbDataHuC3)); + utilGzWrite(gzFile, &gbDataTAMA5, sizeof(gbDataTAMA5)); + if (gbTAMA5ram != NULL) + utilGzWrite(gzFile, gbTAMA5ram, gbTAMA5ramSize); + utilGzWrite(gzFile, &gbDataMMM01, sizeof(gbDataMMM01)); + + utilGzWrite(gzFile, gbPalette, 128 * sizeof(u16)); + + utilGzWrite(gzFile, &gbMemory[0x8000], 0x8000); + + if(gbRamSize && gbRam) { + utilWriteInt(gzFile, gbRamSize); + utilGzWrite(gzFile, gbRam, gbRamSize); + } + + if(gbCgbMode) { + utilGzWrite(gzFile, gbVram, 0x4000); + utilGzWrite(gzFile, gbWram, 0x8000); + } + + gbSoundSaveGame(gzFile); + + gbCheatsSaveGame(gzFile); + + utilWriteInt(gzFile, gbLcdModeDelayed); + utilWriteInt(gzFile, gbLcdTicksDelayed); + utilWriteInt(gzFile, gbLcdLYIncrementTicksDelayed); + utilWriteInt(gzFile, gbSpritesTicks[299]); + utilWriteInt(gzFile, gbTimerModeChange); + utilWriteInt(gzFile, gbTimerOnChange); + utilWriteInt(gzFile, gbHardware); + utilWriteInt(gzFile, gbBlackScreen); + utilWriteInt(gzFile, oldRegister_WY); + utilWriteInt(gzFile, gbWindowLine); + utilWriteInt(gzFile, inUseRegister_WY); + utilWriteInt(gzFile, gbScreenOn); + utilWriteInt(gzFile, 0x12345678); // end marker + return true; +} + +bool gbWriteMemSaveState(char *memory, int available) +{ + gzFile gzFile = utilMemGzOpen(memory, available, "w"); + + if(gzFile == NULL) { + return false; + } + + bool res = gbWriteSaveState(gzFile); + + long pos = utilGzMemTell(gzFile)+8; + + if(pos >= (available)) + res = false; + + utilGzClose(gzFile); + + return res; +} + +bool gbWriteSaveState(const char *name) +{ + gzFile gzFile = utilGzOpen(name,"wb"); + + if(gzFile == NULL) + return false; + + bool res = gbWriteSaveState(gzFile); + + utilGzClose(gzFile); + return res; +} + +static bool gbReadSaveState(gzFile gzFile) +{ + int version = utilReadInt(gzFile); + + if(version > GBSAVE_GAME_VERSION || version < 0) { + systemMessage(MSG_UNSUPPORTED_VB_SGM, + N_("Unsupported VisualBoy save game version %d"), version); + return false; + } + + u8 romname[20]; + + utilGzRead(gzFile, romname, 15); + + if(memcmp(&gbRom[0x134], romname, 15) != 0) { + systemMessage(MSG_CANNOT_LOAD_SGM_FOR, + N_("Cannot load save game for %s. Playing %s"), + romname, &gbRom[0x134]); + return false; + } + + + bool ub = false; + bool ib = false; + + if (version >= 11) + { + ub = utilReadInt(gzFile) ? true : false; + ib = utilReadInt(gzFile) ? true : false; + + if((ub != useBios) && (ib)) { + if(useBios) + systemMessage(MSG_SAVE_GAME_NOT_USING_BIOS, + N_("Save game is not using the BIOS files")); + else + systemMessage(MSG_SAVE_GAME_USING_BIOS, + N_("Save game is using the BIOS file")); + return false; + } + } + + gbReset(); + + inBios = ib; + + utilReadData(gzFile, gbSaveGameStruct); + + + // Correct crash when loading color gameboy save in regular gameboy type. + if (!gbCgbMode) + { + if(gbVram != NULL) { + free(gbVram); + gbVram = NULL; + } + if(gbWram != NULL) { + free(gbWram); + gbWram = NULL; + } + } + else + { + if(gbVram == NULL) + gbVram = (u8 *)malloc(0x4000); + if(gbWram == NULL) + gbWram = (u8 *)malloc(0x8000); + memset(gbVram,0,0x4000); + memset(gbPalette,0, 2*128); + } + + + + if(version >= GBSAVE_GAME_VERSION_7) { + utilGzRead(gzFile, &IFF, 2); + } + + if(gbSgbMode) { + gbSgbReadGame(gzFile, version); + } else { + gbSgbMask = 0; // loading a game at the wrong time causes no display + } + if (version<11) + utilGzRead(gzFile, &gbDataMBC1, sizeof(gbDataMBC1) - sizeof(int)); + else + utilGzRead(gzFile, &gbDataMBC1, sizeof(gbDataMBC1)); + utilGzRead(gzFile, &gbDataMBC2, sizeof(gbDataMBC2)); + if(version < GBSAVE_GAME_VERSION_4) + // prior to version 4, there was no adjustment for the time the game + // was last played, so we have less to read. This needs update if the + // structure changes again. + utilGzRead(gzFile, &gbDataMBC3, sizeof(gbDataMBC3)-sizeof(time_t)); + else + utilGzRead(gzFile, &gbDataMBC3, sizeof(gbDataMBC3)); + utilGzRead(gzFile, &gbDataMBC5, sizeof(gbDataMBC5)); + utilGzRead(gzFile, &gbDataHuC1, sizeof(gbDataHuC1)); + utilGzRead(gzFile, &gbDataHuC3, sizeof(gbDataHuC3)); + if(version>=11) + { + utilGzRead(gzFile, &gbDataTAMA5, sizeof(gbDataTAMA5)); + if(gbTAMA5ram != NULL) { + if(skipSaveGameBattery) { + utilGzSeek(gzFile, gbTAMA5ramSize, SEEK_CUR); + } else { + utilGzRead(gzFile, gbTAMA5ram, gbTAMA5ramSize); + } + } + utilGzRead(gzFile, &gbDataMMM01, sizeof(gbDataMMM01)); + } + + if(version < GBSAVE_GAME_VERSION_5) { + utilGzRead(gzFile, pix, 256*224*sizeof(u16)); + } + memset(pix, 0, 257*226*sizeof(u32)); + + if(version < GBSAVE_GAME_VERSION_6) { + utilGzRead(gzFile, gbPalette, 64 * sizeof(u16)); + } else + utilGzRead(gzFile, gbPalette, 128 * sizeof(u16)); + + if (version < 11) + utilGzRead(gzFile, gbPalette, 128 * sizeof(u16)); + + if(version < GBSAVE_GAME_VERSION_10) { + if(!gbCgbMode && !gbSgbMode) { + for(int i = 0; i < 8; i++) + gbPalette[i] = systemGbPalette[gbPaletteOption*8+i]; + } + } + + utilGzRead(gzFile, &gbMemory[0x8000], 0x8000); + + if(gbRamSize && gbRam) { + if(version < 11) + if(skipSaveGameBattery) { + utilGzSeek(gzFile, gbRamSize, SEEK_CUR); //skip + } else { + utilGzRead(gzFile, gbRam, gbRamSize); //read + } + else + { + int ramSize = utilReadInt(gzFile); + if(skipSaveGameBattery) { + utilGzSeek(gzFile, (gbRamSize>ramSize) ? ramSize : gbRamSize, SEEK_CUR); //skip + } else { + utilGzRead(gzFile, gbRam, (gbRamSize>ramSize) ? ramSize : gbRamSize); //read + } + if(ramSize>gbRamSize) + utilGzSeek(gzFile,ramSize-gbRamSize,SEEK_CUR); + } + } + + memset(gbSCYLine, register_SCY, sizeof(gbSCYLine)); + memset(gbSCXLine, register_SCX, sizeof(gbSCXLine)); + memset(gbBgpLine, (gbBgp[0] | (gbBgp[1]<<2) | (gbBgp[2]<<4) | + (gbBgp[3]<<6)), sizeof(gbBgpLine)); + memset(gbObp0Line, (gbObp0[0] | (gbObp0[1]<<2) | (gbObp0[2]<<4) | + (gbObp0[3]<<6)), sizeof(gbObp0Line)); + memset(gbObp1Line, (gbObp1[0] | (gbObp1[1]<<2) | (gbObp1[2]<<4) | + (gbObp1[3]<<6)), sizeof(gbObp1Line)); + memset(gbSpritesTicks, 0x0, sizeof(gbSpritesTicks)); + + if (inBios) + { + gbMemoryMap[0x00] = &gbMemory[0x0000]; + memcpy ((u8 *)(gbMemory), (u8 *)(gbRom), 0x1000); + memcpy ((u8 *)(gbMemory), (u8 *)(bios), 0x100); + } + else + gbMemoryMap[0x00] = &gbRom[0x0000]; + gbMemoryMap[0x01] = &gbRom[0x1000]; + gbMemoryMap[0x02] = &gbRom[0x2000]; + gbMemoryMap[0x03] = &gbRom[0x3000]; + gbMemoryMap[0x04] = &gbRom[0x4000]; + gbMemoryMap[0x05] = &gbRom[0x5000]; + gbMemoryMap[0x06] = &gbRom[0x6000]; + gbMemoryMap[0x07] = &gbRom[0x7000]; + gbMemoryMap[0x08] = &gbMemory[0x8000]; + gbMemoryMap[0x09] = &gbMemory[0x9000]; + gbMemoryMap[0x0a] = &gbMemory[0xa000]; + gbMemoryMap[0x0b] = &gbMemory[0xb000]; + gbMemoryMap[0x0c] = &gbMemory[0xc000]; + gbMemoryMap[0x0d] = &gbMemory[0xd000]; + gbMemoryMap[0x0e] = &gbMemory[0xe000]; + gbMemoryMap[0x0f] = &gbMemory[0xf000]; + + switch(gbRomType) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + // MBC 1 + memoryUpdateMapMBC1(); + break; + case 0x05: + case 0x06: + // MBC2 + memoryUpdateMapMBC2(); + break; + case 0x0b: + case 0x0c: + case 0x0d: + // MMM01 + memoryUpdateMapMMM01(); + break; + case 0x0f: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + // MBC 3 + memoryUpdateMapMBC3(); + break; + case 0x19: + case 0x1a: + case 0x1b: + // MBC5 + memoryUpdateMapMBC5(); + break; + case 0x1c: + case 0x1d: + case 0x1e: + // MBC 5 Rumble + memoryUpdateMapMBC5(); + break; + case 0x22: + // MBC 7 + memoryUpdateMapMBC7(); + break; + case 0x56: + // GS3 + memoryUpdateMapGS3(); + break; + case 0xfd: + // TAMA5 + memoryUpdateMapTAMA5(); + break; + case 0xfe: + // HuC3 + memoryUpdateMapHuC3(); + break; + case 0xff: + // HuC1 + memoryUpdateMapHuC1(); + break; + } + + if(gbCgbMode) { + utilGzRead(gzFile, gbVram, 0x4000); + utilGzRead(gzFile, gbWram, 0x8000); + + int value = register_SVBK; + if(value == 0) + value = 1; + + gbMemoryMap[0x08] = &gbVram[register_VBK * 0x2000]; + gbMemoryMap[0x09] = &gbVram[register_VBK * 0x2000 + 0x1000]; + gbMemoryMap[0x0d] = &gbWram[value * 0x1000]; + } + + gbSoundReadGame(version, gzFile); + + if (gbCgbMode && gbSgbMode) { + gbSgbMode = 0; + } + + if(gbBorderOn && !gbSgbMask) { + gbSgbRenderBorder(); + } + + systemDrawScreen(); + + if(version > GBSAVE_GAME_VERSION_1) + { + if( skipSaveGameCheats ) { + gbCheatsReadGameSkip(gzFile, version); + } else { + gbCheatsReadGame(gzFile, version); + } + } + + if (version<11) + { + gbWriteMemory(0xff00, 0); + gbMemory[0xff04] = register_DIV; + gbMemory[0xff05] = register_TIMA; + gbMemory[0xff06] = register_TMA; + gbMemory[0xff07] = register_TAC; + gbMemory[0xff40] = register_LCDC; + gbMemory[0xff42] = register_SCY; + gbMemory[0xff43] = register_SCX; + gbMemory[0xff44] = register_LY; + gbMemory[0xff45] = register_LYC; + gbMemory[0xff46] = register_DMA; + gbMemory[0xff4a] = register_WY; + gbMemory[0xff4b] = register_WX; + gbMemory[0xff4f] = register_VBK; + gbMemory[0xff51] = register_HDMA1; + gbMemory[0xff52] = register_HDMA2; + gbMemory[0xff53] = register_HDMA3; + gbMemory[0xff54] = register_HDMA4; + gbMemory[0xff55] = register_HDMA5; + gbMemory[0xff70] = register_SVBK; + gbMemory[0xffff] = register_IE; + GBDIV_CLOCK_TICKS = 64; + + if (gbSpeed) + gbDivTicks /=2; + + if ((gbLcdMode == 0) && (register_STAT & 8)) + gbInt48Signal |= 1; + if ((gbLcdMode == 1) && (register_STAT & 0x10)) + gbInt48Signal |= 2; + if ((gbLcdMode == 2) && (register_STAT & 0x20)) + gbInt48Signal |= 4; + if ((register_LY==register_LYC) && (register_STAT & 0x40)) + gbInt48Signal |= 8; + + gbLcdLYIncrementTicks = GBLY_INCREMENT_CLOCK_TICKS; + + if (gbLcdMode == 2) + gbLcdLYIncrementTicks-=GBLCD_MODE_2_CLOCK_TICKS-gbLcdTicks; + else if (gbLcdMode == 3) + gbLcdLYIncrementTicks -=GBLCD_MODE_2_CLOCK_TICKS+GBLCD_MODE_3_CLOCK_TICKS-gbLcdTicks; + else if (gbLcdMode == 0) + gbLcdLYIncrementTicks =gbLcdTicks; + else if (gbLcdMode == 1) + { + gbLcdLYIncrementTicks = gbLcdTicks % GBLY_INCREMENT_CLOCK_TICKS; + if (register_LY == 0x99) + gbLcdLYIncrementTicks =gbLine99Ticks; + else if (register_LY == 0) + gbLcdLYIncrementTicks += GBLY_INCREMENT_CLOCK_TICKS; + } + + gbLcdModeDelayed = gbLcdMode; + gbLcdTicksDelayed = gbLcdTicks--; + gbLcdLYIncrementTicksDelayed = gbLcdLYIncrementTicks--; + gbInterruptWait = 0; + memset(gbSpritesTicks,0,sizeof(gbSpritesTicks)); + } + else + { + gbLcdModeDelayed = utilReadInt(gzFile); + gbLcdTicksDelayed = utilReadInt(gzFile); + gbLcdLYIncrementTicksDelayed = utilReadInt(gzFile); + gbSpritesTicks[299] = utilReadInt(gzFile) & 0xff; + gbTimerModeChange = (utilReadInt(gzFile) ? true : false); + gbTimerOnChange = (utilReadInt(gzFile) ? true : false); + gbHardware = utilReadInt(gzFile); + gbBlackScreen = (utilReadInt(gzFile) ? true : false); + oldRegister_WY = utilReadInt(gzFile); + gbWindowLine = utilReadInt(gzFile); + inUseRegister_WY = utilReadInt(gzFile); + gbScreenOn = (utilReadInt(gzFile) ? true : false); + } + + if (gbSpeed) + gbLine99Ticks *= 2; + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + if ( version >= 12 && utilReadInt( gzFile ) != 0x12345678 ) + assert( false ); // fails if something read too much/little from file + + return true; +} + +bool gbReadMemSaveState(char *memory, int available) +{ + gzFile gzFile = utilMemGzOpen(memory, available, "r"); + + bool res = gbReadSaveState(gzFile); + + utilGzClose(gzFile); + + return res; +} + +bool gbReadSaveState(const char *name) +{ + gzFile gzFile = utilGzOpen(name,"rb"); + + if(gzFile == NULL) { + return false; + } + + bool res = gbReadSaveState(gzFile); + + utilGzClose(gzFile); + + return res; +} + +bool gbWritePNGFile(const char *fileName) +{ + if(gbBorderOn) + return utilWritePNGFile(fileName, 256, 224, pix); + return utilWritePNGFile(fileName, 160, 144, pix); +} + +bool gbWriteBMPFile(const char *fileName) +{ + if(gbBorderOn) + return utilWriteBMPFile(fileName, 256, 224, pix); + return utilWriteBMPFile(fileName, 160, 144, pix); +} + +void gbCleanUp() +{ + if(gbRam != NULL) { + free(gbRam); + gbRam = NULL; + } + + if(gbRom != NULL) { + free(gbRom); + gbRom = NULL; + } + + if(bios != NULL) { + free(bios); + bios = NULL; + } + + if(gbMemory != NULL) { + free(gbMemory); + gbMemory = NULL; + } + + if(gbLineBuffer != NULL) { + free(gbLineBuffer); + gbLineBuffer = NULL; + } + + if(pix != NULL) { + free(pix); + pix = NULL; + } + + gbSgbShutdown(); + + if(gbVram != NULL) { + free(gbVram); + gbVram = NULL; + } + + if(gbWram != NULL) { + free(gbWram); + gbWram = NULL; + } + + if(gbTAMA5ram != NULL) { + free(gbTAMA5ram); + gbTAMA5ram = NULL; + } + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; +} + +bool gbLoadRom(const char *szFile) +{ + int size = 0; + + if(gbRom != NULL) { + gbCleanUp(); + } + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + gbRom = utilLoad(szFile, + utilIsGBImage, + NULL, + size); + if(!gbRom) + return false; + + gbRomSize = size; + + gbBatteryError = false; + + if(bios != NULL) { + free(bios); + bios = NULL; + } + bios = (u8 *)calloc(1,0x100); + + return gbUpdateSizes(); +} + +bool gbUpdateSizes() +{ + if(gbRom[0x148] > 8) { + systemMessage(MSG_UNSUPPORTED_ROM_SIZE, + N_("Unsupported rom size %02x"), gbRom[0x148]); + return false; + } + + if(gbRomSize < gbRomSizes[gbRom[0x148]]) { + u8 *gbRomNew = (u8 *)realloc(gbRom, gbRomSizes[gbRom[0x148]]); + if( !gbRomNew ) { assert( false ); return false; }; + gbRom = gbRomNew; + for (int i = gbRomSize; igbRomSizes[gbRom[0x148]]) && (genericflashcardEnable)) + { + gbRomSize = gbRomSize>>16; + gbRom[0x148] = 0; + if (gbRomSize) + { + while (!((gbRomSize & 1) || (gbRom[0x148] == 7))) + { + gbRom[0x148]++; + gbRomSize>>=1; + } + gbRom[0x148]++; + } + u8 *gbRomNew = (u8 *)realloc(gbRom, gbRomSizes[gbRom[0x148]]); + if( !gbRomNew ) { assert( false ); return false; }; + gbRom = gbRomNew; + } + gbRomSize = gbRomSizes[gbRom[0x148]]; + gbRomSizeMask = gbRomSizesMasks[gbRom[0x148]]; + + + // The 'genericflashcard' option allows some PD to work. + // However, the setting is dangerous (if you let in enabled + // and play a normal game, it might just break everything). + // That's why it is not saved in the emulator options. + // Also I added some checks in VBA to make sure your saves will not be + // overwritten if you wrongly enable this option for a game + // you already played (and vice-versa, ie. if you forgot to + // enable the option for a game you played with it enabled, like Shawu Story). + u8 ramsize = genericflashcardEnable ? 5 : gbRom[0x149]; + gbRom[0x149] = ramsize; + + if ((gbRom[2] == 0x6D) && (gbRom[5] == 0x47) && (gbRom[6] == 0x65) && (gbRom[7] == 0x6E) && + (gbRom[8] == 0x69) && (gbRom[9] == 0x65) && (gbRom[0xA] == 0x28) && (gbRom[0xB] == 0x54)) + { + gbCheatingDevice = 1; // GameGenie + for (int i = 0; i < 0x20; i++) // Cleans GG hardware registers + gbRom[0x4000+i] = 0; + } + else if (((gbRom[0x104] == 0x44) && (gbRom[0x156] == 0xEA) && (gbRom[0x158] == 0x7F) && + (gbRom[0x159] == 0xEA) && (gbRom[0x15B] == 0x7F)) || ((gbRom[0x165] == 0x3E) && + (gbRom[0x166] == 0xD9) && (gbRom[0x16D] == 0xE1) && (gbRom[0x16E] == 0x7F))) + gbCheatingDevice = 2; // GameShark + else gbCheatingDevice = 0; + + if(ramsize > 5) { + systemMessage(MSG_UNSUPPORTED_RAM_SIZE, + N_("Unsupported ram size %02x"), gbRom[0x149]); + return false; + } + + gbRamSize = gbRamSizes[ramsize]; + gbRamSizeMask = gbRamSizesMasks[ramsize]; + + gbRomType = gbRom[0x147]; + if (genericflashcardEnable) + { + /*if (gbRomType<2) + gbRomType =3; + else if ((gbRomType == 0xc) || (gbRomType == 0xf) || (gbRomType == 0x12) || + (gbRomType == 0x16) || (gbRomType == 0x1a) || (gbRomType == 0x1d)) + gbRomType++; + else if ((gbRomType == 0xb) || (gbRomType == 0x11) || (gbRomType == 0x15) || + (gbRomType == 0x19) || (gbRomType == 0x1c)) + gbRomType+=2; + else if ((gbRomType == 0x5) || (gbRomType == 0x6)) + gbRomType = 0x1a;*/ + gbRomType = 0x1b; + } + else if (gbCheatingDevice == 1) + gbRomType = 0x55; + else if (gbCheatingDevice == 2) + gbRomType = 0x56; + + gbRom[0x147] = gbRomType; + + mapperReadRAM = NULL; + + switch(gbRomType) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x08: + case 0x09: + // MBC 1 + mapper = mapperMBC1ROM; + mapperRAM = mapperMBC1RAM; + mapperReadRAM = mapperMBC1ReadRAM; + break; + case 0x05: + case 0x06: + // MBC2 + mapper = mapperMBC2ROM; + mapperRAM = mapperMBC2RAM; + gbRamSize = 0x200; + gbRamSizeMask = 0x1ff; + break; + case 0x0b: + case 0x0c: + case 0x0d: + // MMM01 + mapper = mapperMMM01ROM; + mapperRAM = mapperMMM01RAM; + break; + case 0x0f: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0xfc: + // MBC 3 + mapper = mapperMBC3ROM; + mapperRAM = mapperMBC3RAM; + mapperReadRAM = mapperMBC3ReadRAM; + break; + case 0x19: + case 0x1a: + case 0x1b: + // MBC5 + mapper = mapperMBC5ROM; + mapperRAM = mapperMBC5RAM; + mapperReadRAM = mapperMBC5ReadRAM; + break; + case 0x1c: + case 0x1d: + case 0x1e: + // MBC 5 Rumble + mapper = mapperMBC5ROM; + mapperRAM = mapperMBC5RAM; + mapperReadRAM = mapperMBC5ReadRAM; + break; + case 0x22: + // MBC 7 + mapper = mapperMBC7ROM; + mapperRAM = mapperMBC7RAM; + mapperReadRAM = mapperMBC7ReadRAM; + gbRamSize = 0x200; + gbRamSizeMask = 0x1ff; + break; + // GG (GameGenie) + case 0x55: + mapper = mapperGGROM; + break; + case 0x56: + // GS (GameShark) + mapper = mapperGS3ROM; + break; + case 0xfd: + // TAMA5 + if (gbRam!= NULL) + { + free(gbRam); + gbRam = NULL; + } + + ramsize = 3; + gbRamSize = gbRamSizes[3]; + gbRamSizeMask = gbRamSizesMasks[3]; + gbRamFill = 0x0; + + gbTAMA5ramSize = 0x100; + + if (gbTAMA5ram == NULL) + gbTAMA5ram = (u8 *)malloc(gbTAMA5ramSize); + memset(gbTAMA5ram, 0x0, gbTAMA5ramSize); + + mapperRAM = mapperTAMA5RAM; + mapperReadRAM = mapperTAMA5ReadRAM; + mapperUpdateClock = memoryUpdateTAMA5Clock; + break; + case 0xfe: + // HuC3 + mapper = mapperHuC3ROM; + mapperRAM = mapperHuC3RAM; + mapperReadRAM = mapperHuC3ReadRAM; + break; + case 0xff: + // HuC1 + mapper = mapperHuC1ROM; + mapperRAM = mapperHuC1RAM; + break; + default: + systemMessage(MSG_UNKNOWN_CARTRIDGE_TYPE, + N_("Unknown cartridge type %02x"), gbRomType); + return false; + } + + if(gbRamSize) { + gbRam = (u8 *)malloc(gbRamSize); + memset(gbRam, gbRamFill, gbRamSize); + } + + switch(gbRomType) { + case 0x03: + case 0x06: + case 0x0f: + case 0x10: + case 0x13: + case 0x1b: + case 0x1d: + case 0x1e: + case 0x22: + case 0xfd: + case 0xff: + gbBattery = 1; + break; + } + + gbInit(); + + //gbReset(); + + switch(gbRomType) { + case 0x1c: + case 0x1d: + case 0x1e: + gbDataMBC5.isRumbleCartridge = 1; + } + + return true; +} + +int gbGetNextEvent (int _clockTicks) +{ + if (register_LCDC & 0x80) + { + if(gbLcdTicks < _clockTicks) + _clockTicks = gbLcdTicks; + + if(gbLcdTicksDelayed < _clockTicks) + _clockTicks = gbLcdTicksDelayed; + + if(gbLcdLYIncrementTicksDelayed < _clockTicks) + _clockTicks = gbLcdLYIncrementTicksDelayed; + } + + if(gbLcdLYIncrementTicks < _clockTicks) + _clockTicks = gbLcdLYIncrementTicks; + + if(gbSerialOn && (gbSerialTicks < _clockTicks)) + _clockTicks = gbSerialTicks; + + if(gbTimerOn && (((gbInternalTimer) & gbTimerMask[gbTimerMode])+1 < _clockTicks)) + _clockTicks = ((gbInternalTimer) & gbTimerMask[gbTimerMode])+1; + + //if(soundTicks && (soundTicks < _clockTicks)) + // _clockTicks = soundTicks; + + if ((_clockTicks<=0) || (gbInterruptWait)) + _clockTicks = 1; + + return _clockTicks; +} + +void gbDrawLine() +{ + switch(systemColorDepth) { + case 16: + { + u16 * dest = (u16 *)pix + + (gbBorderLineSkip+2) * (register_LY + gbBorderRowSkip+1) + + gbBorderColumnSkip; + for(int x = 0; x < 160; ) { + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + } + if(gbBorderOn) + dest += gbBorderColumnSkip; + *dest++ = 0; // for filters that read one pixel more + } + break; + + case 24: + { + u8 *dest = (u8 *)pix + + 3*(gbBorderLineSkip * (register_LY + gbBorderRowSkip) + + gbBorderColumnSkip); + for(int x = 0; x < 160;) { + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + } + } + break; + + case 32: + { + u32 * dest = (u32 *)pix + + (gbBorderLineSkip+1) * (register_LY + gbBorderRowSkip+1) + + gbBorderColumnSkip; + for(int x = 0; x < 160;) { + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + } + } + break; + } +} + +void gbEmulate(int ticksToStop) +{ + gbRegister tempRegister; + u8 tempValue; + s8 offset; + + clockTicks = 0; + gbDmaTicks = 0; + + register int opcode = 0; + + int opcode1 = 0; + int opcode2 = 0; + bool execute = false; + + while(1) { +#ifndef FINAL_VERSION + if(systemDebug) { + if(!(IFF & 0x80)) { + if(systemDebug > 1) { + sprintf(gbBuffer,"PC=%04x AF=%04x BC=%04x DE=%04x HL=%04x SP=%04x I=%04x\n", + PC.W, AF.W, BC.W, DE.W,HL.W,SP.W,IFF); + } else { + sprintf(gbBuffer,"PC=%04x I=%02x\n", PC.W, IFF); + } + log(gbBuffer); + } + } +#endif + + u16 oldPCW = PC.W; + + if(IFF & 0x80) { + if(register_LCDC & 0x80) { + clockTicks = gbLcdTicks; + } else + clockTicks = 1000; + + clockTicks = gbGetNextEvent(clockTicks); + + /*if(gbLcdTicksDelayed < clockTicks) + clockTicks = gbLcdTicksDelayed; + + if(gbLcdLYIncrementTicksDelayed < clockTicks) + clockTicks = gbLcdLYIncrementTicksDelayed; + + if(gbLcdLYIncrementTicks < clockTicks) + clockTicks = gbLcdLYIncrementTicks; + + if(gbSerialOn && (gbSerialTicks < clockTicks)) + clockTicks = gbSerialTicks; + + if(gbTimerOn && (((gbInternalTimer) & gbTimerMask[gbTimerMode])+1 < clockTicks)) + clockTicks = ((gbInternalTimer) & gbTimerMask[gbTimerMode])+1; + + if(soundTicks && (soundTicks < clockTicks)) + clockTicks = soundTicks; + + if ((clockTicks<=0) || (gbInterruptWait)) + clockTicks = 1;*/ + + } else { + + // First we apply the clockTicks, then we execute the opcodes. + opcode1 = 0; + opcode2 = 0; + execute = true; + + opcode2 = opcode1 = opcode = gbReadOpcode(PC.W++); + + // If HALT state was launched while IME = 0 and (register_IF & register_IE & 0x1F), + // PC.W is not incremented for the first byte of the next instruction. + if (IFF & 2) + { + PC.W--; + IFF &= ~2; + } + + clockTicks = gbCycles[opcode]; + + switch(opcode) { + case 0xCB: + // extended opcode + opcode2 = opcode = gbReadOpcode(PC.W++); + clockTicks = gbCyclesCB[opcode]; + break; + } + gbOldClockTicks = clockTicks-1; + gbIntBreak = 1; + } + + + if(!emulating) + return; + + // For 'breakpoint' support (opcode 0xFC is considered as a breakpoint) + if ((clockTicks==0) && execute) + { + PC.W = oldPCW; + return; + } + + + if (!(IFF & 0x80)) + clockTicks = 1; + + gbRedoLoop: + + + + if (gbInterruptWait) + gbInterruptWait = 0; + else + gbInterruptLaunched = 0; + + + // Used for the EI/DI instruction's delay. + if (IFF & 0x38) + { + int tempIFF = (IFF >> 4) & 3; + + if (tempIFF <=clockTicks) + { + tempIFF = 0; + IFF |=1; + } + else + tempIFF -= clockTicks; + IFF = (IFF & 0xCF) | (tempIFF <<4); + + if (IFF & 0x08) + IFF &= 0x82; + } + + + if (register_LCDCBusy) + { + register_LCDCBusy-=clockTicks; + if (register_LCDCBusy<0) + register_LCDCBusy = 0; + } + + + if(gbSgbMode) { + if(gbSgbPacketTimeout) { + gbSgbPacketTimeout -= clockTicks; + + if(gbSgbPacketTimeout <= 0) + gbSgbResetPacketState(); + } + } + + ticksToStop -= clockTicks; + + // DIV register emulation + gbDivTicks -= clockTicks; + while(gbDivTicks <= 0) { + gbMemory[0xff04] = ++register_DIV; + gbDivTicks += GBDIV_CLOCK_TICKS; + } + + if(register_LCDC & 0x80) { + // LCD stuff + + gbLcdTicks -= clockTicks; + gbLcdTicksDelayed -= clockTicks; + gbLcdLYIncrementTicks -= clockTicks; + gbLcdLYIncrementTicksDelayed -= clockTicks; + + + // our counters are off, see what we need to do + + // This looks (and kinda is) complicated, however this + // is the only way I found to emulate properly the way + // the real hardware operates... + while(((gbLcdTicks <= 0) && (gbLCDChangeHappened == false)) || + ((gbLcdTicksDelayed <= 0) && (gbLCDChangeHappened == true)) || + ((gbLcdLYIncrementTicks <= 0) && (gbLYChangeHappened == false)) || + ((gbLcdLYIncrementTicksDelayed<=0) && (gbLYChangeHappened == true))) + { + + if ((gbLcdLYIncrementTicks <= 0) && (!gbLYChangeHappened)) + { + gbLYChangeHappened = true; + gbMemory[0xff44] = register_LY = (register_LY + 1) % 154; + + if (register_LY == 0x91) + { + /* if (IFF & 0x80) + gbScreenOn = !gbScreenOn; + else*/ if (register_LCDC & 0x80) + gbScreenOn = true; + } + + gbLcdLYIncrementTicks += GBLY_INCREMENT_CLOCK_TICKS; + + if (gbLcdMode == 1) + { + + if(register_LY == 153) + gbLcdLYIncrementTicks -= GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks; + else if(register_LY == 0) + gbLcdLYIncrementTicks += GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks; + } + + // GB only 'bug' : Halt state is broken one tick before LY==LYC interrupt + // is reflected in the registers. + if ((gbHardware & 5) && (IFF & 0x80) && (register_LY == register_LYC) + && (register_STAT & 0x40) && (register_LY != 0)) + { + if (!((gbLcdModeDelayed != 1) && (register_LY==0))) + { + gbInt48Signal &= ~9; + gbCompareLYToLYC(); + gbLYChangeHappened = false; + gbMemory[0xff41] = register_STAT; + gbMemory[0xff0f] = register_IF; + } + + gbLcdLYIncrementTicksDelayed += GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks+1; + } + } + + + if ((gbLcdTicks <= 0) && (!gbLCDChangeHappened)) + { + gbLCDChangeHappened = true; + + switch(gbLcdMode) + { + case 0: + { + // H-Blank + // check if we reached the V-Blank period + if(register_LY == 144) { + // Yes, V-Blank + // set the LY increment counter + if (gbHardware & 0x5) + { + register_IF |= 1; // V-Blank interrupt + } + + gbInt48Signal &= ~6; + if(register_STAT & 0x10) + { + // send LCD interrupt only if no interrupt 48h signal... + if ((!(gbInt48Signal & 1)) && ((!(gbInt48Signal & 8)) || (gbHardware & 0x0a))) + { + register_IF |=2; + gbInterruptLaunched |= 2; + if (gbHardware & 0xa) + gbInterruptWait = 1; + } + gbInt48Signal |= 2; + } + gbInt48Signal &= ~1; + + gbLcdTicks += GBLCD_MODE_1_CLOCK_TICKS; + gbLcdMode = 1; + + } else { + // go the the OAM being accessed mode + gbLcdTicks += GBLCD_MODE_2_CLOCK_TICKS; + gbLcdMode = 2; + + gbInt48Signal &= ~6; + if(register_STAT & 0x20) + { + // send LCD interrupt only if no interrupt 48h signal... + if (!gbInt48Signal) + { + register_IF |= 2; + gbInterruptLaunched |= 2; + } + gbInt48Signal |= 4; + } + gbInt48Signal &= ~1; + } + } + break; + case 1: + { + // V-Blank + // next mode is OAM being accessed mode + gbInt48Signal &= ~5; + if(register_STAT & 0x20) + { + // send LCD interrupt only if no interrupt 48h signal... + if (!gbInt48Signal) + { + register_IF |= 2; + gbInterruptLaunched |= 2; + if ((gbHardware & 0xa) && (IFF & 0x80)) + gbInterruptWait = 1; + } + gbInt48Signal |= 4; + } + gbInt48Signal &= ~2; + + gbLcdTicks += GBLCD_MODE_2_CLOCK_TICKS; + + gbLcdMode = 2; + register_LY = 0x00; + + } + break; + case 2: + { + + // OAM being accessed mode + // next mode is OAM and VRAM in use + if ((gbScreenOn) && (register_LCDC & 0x80)) + { + gbDrawSprites(false); + // Used to add a one tick delay when a window line is drawn. + //(fixes a part of Carmaggedon problem) + if((register_LCDC & 0x01 || gbCgbMode) && (register_LCDC & 0x20) && + (gbWindowLine != -2)) { + + int inUseRegister_WY = 0; + int tempgbWindowLine = gbWindowLine; + + if ((tempgbWindowLine == -1) || (tempgbWindowLine>144)) + { + inUseRegister_WY = oldRegister_WY; + if (register_LY>oldRegister_WY) + tempgbWindowLine = 146; + } + + if(register_LY >= inUseRegister_WY) { + + if (tempgbWindowLine == -1) + tempgbWindowLine = 0; + + int wx = register_WX; + wx -= 7; + if (wx<0) + wx = 0; + + if((wx <= 159) && (tempgbWindowLine <= 143)) + { + for (int i = wx; i<300; i++) + if (gbSpeed) + gbSpritesTicks[i]+=3; + else + gbSpritesTicks[i]+=1; + } + } + } + } + + gbInt48Signal &= ~7; + + gbLcdTicks += GBLCD_MODE_3_CLOCK_TICKS+gbSpritesTicks[299]; + gbLcdMode = 3; + } + break; + case 3: + { + // OAM and VRAM in use + // next mode is H-Blank + + + gbInt48Signal &= ~7; + if(register_STAT & 0x08) + { + // send LCD interrupt only if no interrupt 48h signal... + if (!(gbInt48Signal & 8)) + { + register_IF |= 2; + if ((gbHardware & 0xa) && (IFF & 0x80)) + gbInterruptWait = 1; + } + gbInt48Signal |= 1; + } + + gbLcdTicks += GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]; + + gbLcdMode = 0; + + // No HDMA during HALT ! + if(gbHdmaOn && (!(IFF & 0x80) || (register_IE & register_IF & 0x1f))) { + gbDoHdma(); + } + + } + break; + } + } + + + if ((gbLcdTicksDelayed <= 0) && (gbLCDChangeHappened)) { + int framesToSkip = systemFrameSkip; + if(speedup) + framesToSkip = 9; // try 6 FPS during speedup + //gbLcdTicksDelayed = gbLcdTicks+1; + gbLCDChangeHappened = false; + switch(gbLcdModeDelayed) { + case 0: + { + // H-Blank + + memset(gbSCYLine,gbSCYLine[299],sizeof(gbSCYLine)); + memset(gbSCXLine,gbSCXLine[299],sizeof(gbSCXLine)); + memset(gbBgpLine,gbBgpLine[299],sizeof(gbBgpLine)); + memset(gbObp0Line,gbObp0Line[299],sizeof(gbObp0Line)); + memset(gbObp1Line,gbObp1Line[299],sizeof(gbObp1Line)); + memset(gbSpritesTicks,gbSpritesTicks[299],sizeof(gbSpritesTicks)); + + if (gbWindowLine <0) + oldRegister_WY = register_WY; + // check if we reached the V-Blank period + if(register_LY == 144) { + // Yes, V-Blank + // set the LY increment counter + + if(register_LCDC & 0x80) { + if (gbHardware & 0xa) + { + + register_IF |= 1; // V-Blank interrupt + gbInterruptLaunched |=1; + } + + + } + + gbLcdTicksDelayed += GBLCD_MODE_1_CLOCK_TICKS; + gbLcdModeDelayed = 1; + + gbFrameCount++; + systemFrame(); + + if((gbFrameCount % 10) == 0) + system10Frames(60); + + if(gbFrameCount >= 60) { + u32 currentTime = systemGetClock(); + if(currentTime != gbLastTime) + systemShowSpeed(100000/(currentTime - gbLastTime)); + else + systemShowSpeed(0); + gbLastTime = currentTime; + gbFrameCount = 0; + } + + if(systemReadJoypads()) { + // read joystick + if(gbSgbMode && gbSgbMultiplayer) { + if(gbSgbFourPlayers) { + gbJoymask[0] = systemReadJoypad(0); + gbJoymask[1] = systemReadJoypad(1); + gbJoymask[2] = systemReadJoypad(2); + gbJoymask[3] = systemReadJoypad(3); + } else { + gbJoymask[0] = systemReadJoypad(0); + gbJoymask[1] = systemReadJoypad(1); + } + } else { + gbJoymask[0] = systemReadJoypad(-1); + } + } + int newmask = gbJoymask[0] & 255; + + if(gbRomType == 0x22) { + systemUpdateMotionSensor(); + } + + if(newmask) + { + gbMemory[0xff0f] = register_IF |= 16; + } + + + newmask = (gbJoymask[0] >> 10); + + speedup = (newmask & 1) ? true : false; + gbCapture = (newmask & 2) ? true : false; + + if(gbCapture && !gbCapturePrevious) { + gbCaptureNumber++; + systemScreenCapture(gbCaptureNumber); + } + gbCapturePrevious = gbCapture; + + if(gbFrameSkipCount >= framesToSkip) { + + if(!gbSgbMask) + { + if (gbBorderOn) + gbSgbRenderBorder(); + //if (gbScreenOn) + systemDrawScreen(); + if(systemPauseOnFrame()) + ticksToStop = 0; + } + gbFrameSkipCount = 0; + } else + gbFrameSkipCount++; + + } else { + // go the the OAM being accessed mode + gbLcdTicksDelayed += GBLCD_MODE_2_CLOCK_TICKS; + gbLcdModeDelayed = 2; + gbInt48Signal &= ~3; + } + } + break; + case 1: + { + // V-Blank + // next mode is OAM being accessed mode + + // gbScreenOn = true; + + oldRegister_WY = register_WY; + + gbLcdTicksDelayed += GBLCD_MODE_2_CLOCK_TICKS; + gbLcdModeDelayed = 2; + + // reset the window line + gbWindowLine = -1; + } + break; + case 2: + { + // OAM being accessed mode + // next mode is OAM and VRAM in use + gbLcdTicksDelayed += GBLCD_MODE_3_CLOCK_TICKS+gbSpritesTicks[299]; + gbLcdModeDelayed = 3; + } + break; + case 3: + { + + // OAM and VRAM in use + // next mode is H-Blank + if((register_LY < 144) && (register_LCDC & 0x80) && gbScreenOn) { + if(!gbSgbMask) { + if(gbFrameSkipCount >= framesToSkip) { + if (!gbBlackScreen) + { + gbRenderLine(); + gbDrawSprites(true); + } + else if (gbBlackScreen) + { + u16 color = gbColorOption ? gbColorFilter[0] : 0; + if (!gbCgbMode) + color = gbColorOption ? gbColorFilter[gbPalette[3] & 0x7FFF] : gbPalette[3] & 0x7FFF; + for(int i = 0; i < 160; i++) + { + gbLineMix[i] = color; + gbLineBuffer[i] = 0; + } + } + gbDrawLine(); + } + } + } + gbLcdTicksDelayed += GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]; + gbLcdModeDelayed = 0; + } + break; + } + } + + if ((gbLcdLYIncrementTicksDelayed <= 0) && (gbLYChangeHappened == true)) + { + + gbLYChangeHappened = false; + + if (!((gbLcdMode != 1) && (register_LY==0))) + { + { + gbInt48Signal &= ~8; + gbCompareLYToLYC(); + if ((gbInt48Signal == 8) && (!((register_LY == 0) && (gbHardware & 1)))) + gbInterruptLaunched |= 2; + if ((gbHardware & (gbSpeed ? 8 : 2)) && (register_LY==0) && ((register_STAT & 0x44) == 0x44) && (gbLcdLYIncrementTicksDelayed==0)) + { + gbInterruptWait = 1; + + } + } + } + gbLcdLYIncrementTicksDelayed += GBLY_INCREMENT_CLOCK_TICKS; + + if (gbLcdModeDelayed == 1) + { + + if(register_LY == 153) + gbLcdLYIncrementTicksDelayed -= GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks; + else if(register_LY == 0) + gbLcdLYIncrementTicksDelayed += GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks; + } + gbMemory[0xff0f] = register_IF; + gbMemory[0xff41] = register_STAT; + } + } + gbMemory[0xff0f] = register_IF; + gbMemory[0xff41] = register_STAT = (register_STAT & 0xfc) | gbLcdModeDelayed; + } + else + { + + // Used to update the screen with white lines when it's off. + // (it looks strange, but it's kinda accurate :p) + // You can try the Mario Demo Vx.x for exemple + // (check the bottom 2 lines while moving) + if (!gbWhiteScreen) + { + gbScreenTicks -= clockTicks; + gbLcdLYIncrementTicks -= clockTicks; + while (gbLcdLYIncrementTicks <=0) + { + register_LY = ((register_LY+1)%154); + gbLcdLYIncrementTicks+=GBLY_INCREMENT_CLOCK_TICKS; + } + if (gbScreenTicks <= 0) + { + gbWhiteScreen = 1; + u8 register_LYLcdOff = ((register_LY+154)%154); + for (register_LY=0;register_LY <= 0x90;register_LY++) + { + u16 color = gbColorOption ? gbColorFilter[0x7FFF] : 0x7FFF; + if (!gbCgbMode) + color = gbColorOption ? gbColorFilter[gbPalette[0] & 0x7FFF] : gbPalette[0] & 0x7FFF; + for(int i = 0; i < 160; i++) + { + gbLineMix[i] = color; + gbLineBuffer[i] = 0; + } + gbDrawLine(); + } + register_LY = register_LYLcdOff; + } + } + + if (gbWhiteScreen) + { + gbLcdLYIncrementTicks -= clockTicks; + + while (gbLcdLYIncrementTicks <=0) + { + register_LY = ((register_LY+1)%154); + gbLcdLYIncrementTicks+=GBLY_INCREMENT_CLOCK_TICKS; + if (register_LY<144) + { + + u16 color = gbColorOption ? gbColorFilter[0x7FFF] : 0x7FFF; + if (!gbCgbMode) + color = gbColorOption ? gbColorFilter[gbPalette[0] & 0x7FFF] : gbPalette[0] & 0x7FFF; + for(int i = 0; i < 160; i++) + { + gbLineMix[i] = color; + gbLineBuffer[i] = 0; + } + gbDrawLine(); + } + else if ((register_LY==144) && (!systemFrameSkip)) + { + int framesToSkip = systemFrameSkip; + if(speedup) + framesToSkip = 9; // try 6 FPS during speedup + if((gbFrameSkipCount >= framesToSkip) || (gbWhiteScreen == 1)) { + gbWhiteScreen = 2; + + if(!gbSgbMask) + { + if (gbBorderOn) + gbSgbRenderBorder(); + //if (gbScreenOn) + systemDrawScreen(); + if(systemPauseOnFrame()) + ticksToStop = 0; + } + } + if(systemReadJoypads()) { + // read joystick + if(gbSgbMode && gbSgbMultiplayer) { + if(gbSgbFourPlayers) { + gbJoymask[0] = systemReadJoypad(0); + gbJoymask[1] = systemReadJoypad(1); + gbJoymask[2] = systemReadJoypad(2); + gbJoymask[3] = systemReadJoypad(3); + } else { + gbJoymask[0] = systemReadJoypad(0); + gbJoymask[1] = systemReadJoypad(1); + } + } else { + gbJoymask[0] = systemReadJoypad(-1); + } + } + gbFrameCount++; + + systemFrame(); + + if((gbFrameCount % 10) == 0) + system10Frames(60); + + if(gbFrameCount >= 60) { + u32 currentTime = systemGetClock(); + if(currentTime != gbLastTime) + systemShowSpeed(100000/(currentTime - gbLastTime)); + else + systemShowSpeed(0); + gbLastTime = currentTime; + gbFrameCount = 0; + } + } + } + } + } + + gbMemory[0xff41] = register_STAT; + + // serial emulation + if(gbSerialOn) { +#ifdef OLD_GB_LINK + if(linkConnected) { + gbSerialTicks -= clockTicks; + + while(gbSerialTicks <= 0) { + // increment number of shifted bits + gbSerialBits++; + linkProc(); + if(gbSerialOn && (gbMemory[0xff02] & 1)) { + if(gbSerialBits == 8) { + gbSerialBits = 0; + gbMemory[0xff01] = 0xff; + gbMemory[0xff02] &= 0x7f; + gbSerialOn = 0; + gbMemory[0xff0f] = register_IF |= 8; + gbSerialTicks = 0; + } + } + gbSerialTicks += GBSERIAL_CLOCK_TICKS; + } + } else { +#endif + if(gbMemory[0xff02] & 1) { + gbSerialTicks -= clockTicks; + + // overflow + while(gbSerialTicks <= 0) { + // shift serial byte to right and put a 1 bit in its place + // gbMemory[0xff01] = 0x80 | (gbMemory[0xff01]>>1); + // increment number of shifted bits + gbSerialBits++; + if(gbSerialBits == 8) { + // end of transmission + if(gbSerialFunction) // external device + gbMemory[0xff01] = gbSerialFunction(gbMemory[0xff01]); + else + gbMemory[0xff01] = 0xff; + gbSerialTicks = 0; + gbMemory[0xff02] &= 0x7f; + gbSerialOn = 0; + gbMemory[0xff0f] = register_IF |= 8; + gbSerialBits = 0; + } else + gbSerialTicks += GBSERIAL_CLOCK_TICKS; + } + } +#ifdef OLD_GB_LINK + } +#endif + } + + + soundTicks -= clockTicks; + if ( !gbSpeed ) + soundTicks -= clockTicks; + + while(soundTicks < 0) { + soundTicks += SOUND_CLOCK_TICKS; + + gbSoundTick(); + } + + + // timer emulation + + if(gbTimerOn) { + gbTimerTicks= ((gbInternalTimer) & gbTimerMask[gbTimerMode])+1-clockTicks; + + while(gbTimerTicks <= 0) { + register_TIMA++; + // timer overflow! + if((register_TIMA & 0xff) == 0) { + // reload timer modulo + register_TIMA = register_TMA; + // flag interrupt + gbMemory[0xff0f] = register_IF |= 4; + } + gbTimerTicks += gbTimerClockTicks; + } + gbTimerOnChange = false; + gbTimerModeChange = false; + + gbMemory[0xff05] = register_TIMA; + + } + + gbInternalTimer -= clockTicks; + while (gbInternalTimer<0) + gbInternalTimer+=0x100; + + clockTicks = 0; + + if (gbIntBreak == 1) + { + gbIntBreak = 0; + if ((register_IE & register_IF & gbInterruptLaunched & 0x3) && + ((IFF & 0x81) == 1) && (!gbInterruptWait) && (execute)) + { + gbIntBreak = 2; + PC.W = oldPCW; + execute = false; + gbOldClockTicks = 0; + } + if (gbOldClockTicks) + { + clockTicks = gbOldClockTicks; + gbOldClockTicks = 0; + goto gbRedoLoop; + } + } + + // Executes the opcode(s), and apply the instruction's remaining clockTicks (if any). + if (execute) + { + switch(opcode1) { + case 0xCB: + // extended opcode + switch(opcode2) { +#include "gbCodesCB.h" + } + break; +#include "gbCodes.h" + } + execute = false; + + if (clockTicks) + { + gbDmaTicks += clockTicks; + clockTicks = 0; + } + } + + if (gbDmaTicks) + { + clockTicks = gbGetNextEvent(gbDmaTicks); + + if (clockTicks<=gbDmaTicks) + gbDmaTicks -= clockTicks; + else + { + clockTicks = gbDmaTicks; + gbDmaTicks = 0; + } + + goto gbRedoLoop; + } + + + // Remove the 'if an IE is pending' flag if IE has finished being executed. + if ((IFF & 0x40) && !(IFF & 0x30)) + IFF &= 0x81; + + + + if ((register_IE & register_IF & 0x1f) && (IFF & 0x81) && (!gbInterruptWait)) + { + + if (IFF & 1) + { + // Add 5 ticks for the interrupt execution time + gbDmaTicks += 5; + + if (gbIntBreak == 2) + { + gbDmaTicks--; + gbIntBreak = 0; + } + + + if(register_IF & register_IE & 1) + gbVblank_interrupt(); + else if(register_IF & register_IE & 2) + gbLcd_interrupt(); + else if(register_IF & register_IE & 4) + gbTimer_interrupt(); + else if(register_IF & register_IE & 8) + gbSerial_interrupt(); + else if(register_IF & register_IE & 16) + gbJoypad_interrupt(); + } + + IFF &= ~0x81; + } + + if (IFF & 0x08) + IFF &=~0x79; + + // Used to apply the interrupt's execution time. + if (gbDmaTicks) + { + clockTicks = gbGetNextEvent(gbDmaTicks); + + if (clockTicks<=gbDmaTicks) + gbDmaTicks -= clockTicks; + else + { + clockTicks = gbDmaTicks; + gbDmaTicks = 0; + } + goto gbRedoLoop; + } + + + gbBlackScreen = false; + + if((ticksToStop <= 0)) { + if(!(register_LCDC & 0x80)) { + if(systemReadJoypads()) { + // read joystick + if(gbSgbMode && gbSgbMultiplayer) { + if(gbSgbFourPlayers) { + gbJoymask[0] = systemReadJoypad(0); + gbJoymask[1] = systemReadJoypad(1); + gbJoymask[2] = systemReadJoypad(2); + gbJoymask[3] = systemReadJoypad(3); + } else { + gbJoymask[0] = systemReadJoypad(0); + gbJoymask[1] = systemReadJoypad(1); + } + } else { + gbJoymask[0] = systemReadJoypad(-1); + } + } + } + return; + } + } +} + +struct EmulatedSystem GBSystem = { + // emuMain + gbEmulate, + // emuReset + gbReset, + // emuCleanUp + gbCleanUp, + // emuReadBattery + gbReadBatteryFile, + // emuWriteBattery + gbWriteBatteryFile, + // emuReadState + gbReadSaveState, + // emuWriteState + gbWriteSaveState, + // emuReadMemState + gbReadMemSaveState, + // emuWriteMemState + gbWriteMemSaveState, + // emuWritePNG + gbWritePNGFile, + // emuWriteBMP + gbWriteBMPFile, + // emuUpdateCPSR + NULL, + // emuHasDebugger + false, + // emuCount +#ifdef FINAL_VERSION + 70000/4, +#else + 1000, +#endif +}; diff --git a/src/gb/gb.h b/src/gb/gb.h new file mode 100644 index 0000000..c255614 --- /dev/null +++ b/src/gb/gb.h @@ -0,0 +1,50 @@ +#ifndef GB_H +#define GB_H + +#define C_FLAG 0x10 +#define H_FLAG 0x20 +#define N_FLAG 0x40 +#define Z_FLAG 0x80 + +typedef union { + struct { +#ifdef WORDS_BIGENDIAN + u8 B1, B0; +#else + u8 B0,B1; +#endif + } B; + 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(); +bool gbIsGameboyRom(const char *); +void gbGetHardwareType(); +void gbReset(); +void gbCleanUp(); +void gbCPUInit(const char *,bool); +bool gbWriteBatteryFile(const char *); +bool gbWriteBatteryFile(const char *, bool); +bool gbReadBatteryFile(const char *); +bool gbWriteSaveState(const char *); +bool gbWriteMemSaveState(char *, int); +bool gbReadSaveState(const char *); +bool gbReadMemSaveState(char *, int); +void gbSgbRenderBorder(); +bool gbWritePNGFile(const char *); +bool gbWriteBMPFile(const char *); +bool gbReadGSASnapshot(const char *); + +extern int gbHardware; + +extern struct EmulatedSystem GBSystem; + +#endif // GB_H diff --git a/src/gb/gbCheats.cpp b/src/gb/gbCheats.cpp new file mode 100644 index 0000000..444b052 --- /dev/null +++ b/src/gb/gbCheats.cpp @@ -0,0 +1,533 @@ +#include +#include +#include +#include + +#include "../System.h" +#include "../NLS.h" +#include "../Util.h" + +#include "gbCheats.h" +#include "gbGlobals.h" +#include "gb.h" + +gbCheat gbCheatList[100]; +int gbCheatNumber = 0; +int gbNextCheat = 0; +bool gbCheatMap[0x10000]; + +extern bool cheatsEnabled; + +#define GBCHEAT_IS_HEX(a) ( ((a)>='A' && (a) <='F') || ((a) >='0' && (a) <= '9')) +#define GBCHEAT_HEX_VALUE(a) ( (a) >= 'A' ? (a) - 'A' + 10 : (a) - '0') + +void gbCheatUpdateMap() +{ + memset(gbCheatMap, 0, 0x10000); + + for(int i = 0; i < gbCheatNumber; i++) { + if(gbCheatList[i].enabled) + gbCheatMap[gbCheatList[i].address] = true; + } +} + +void gbCheatsSaveGame(gzFile gzFile) +{ + utilWriteInt(gzFile, gbCheatNumber); + if(gbCheatNumber>0) + utilGzWrite(gzFile, &gbCheatList[0], sizeof(gbCheat)*gbCheatNumber); +} + +void gbCheatsReadGame(gzFile gzFile, int version) +{ + if(version <= 8) { + int gbGgOn = utilReadInt(gzFile); + + if(gbGgOn) { + int n = utilReadInt(gzFile); + gbXxCheat tmpCheat; + for(int i = 0; i < n; i++) { + utilGzRead(gzFile,&tmpCheat, sizeof(gbXxCheat)); + gbAddGgCheat(tmpCheat.cheatCode, tmpCheat.cheatDesc); + } + } + + int gbGsOn = utilReadInt(gzFile); + + if(gbGsOn) { + int n = utilReadInt(gzFile); + gbXxCheat tmpCheat; + for(int i = 0; i < n; i++) { + utilGzRead(gzFile,&tmpCheat, sizeof(gbXxCheat)); + gbAddGsCheat(tmpCheat.cheatCode, tmpCheat.cheatDesc); + } + } + } else { + gbCheatNumber = utilReadInt(gzFile); + + if(gbCheatNumber>0) { + utilGzRead(gzFile, &gbCheatList[0], sizeof(gbCheat)*gbCheatNumber); + } + } + + gbCheatUpdateMap(); +} + + +void gbCheatsReadGameSkip(gzFile gzFile, int version) +{ + if( version <= 8 ) { + int gbGgOn = utilReadInt( gzFile ); + if( gbGgOn ) { + int n = utilReadInt( gzFile ); + if( n > 0 ) { + utilGzSeek( gzFile, n * sizeof(gbXxCheat), SEEK_CUR ); + } + } + + int gbGsOn = utilReadInt( gzFile ); + if( gbGsOn ) { + int n = utilReadInt(gzFile); + if( n > 0 ) { + utilGzSeek( gzFile, n * sizeof(gbXxCheat), SEEK_CUR ); + } + } + } else { + int n = utilReadInt( gzFile ); + + if( n > 0 ) { + utilGzSeek( gzFile, n * sizeof(gbCheat), SEEK_CUR ); + } + } +} + + +void gbCheatsSaveCheatList(const char *file) +{ + if(gbCheatNumber == 0) + return; + FILE *f = fopen(file, "wb"); + if(f == NULL) + return; + int version = 1; + fwrite(&version, 1, sizeof(version), f); + int type = 1; + fwrite(&type, 1, sizeof(type), f); + fwrite(&gbCheatNumber, 1, sizeof(gbCheatNumber), f); + fwrite(gbCheatList, 1, sizeof(gbCheatList), f); + fclose(f); +} + +bool gbCheatsLoadCheatList(const char *file) +{ + gbCheatNumber = 0; + + gbCheatUpdateMap(); + + int count = 0; + + FILE *f = fopen(file, "rb"); + + if(f == NULL) + return false; + + int version = 0; + + if(fread(&version, 1, sizeof(version), f) != sizeof(version)) { + fclose(f); + return false; + } + + if(version != 1) { + systemMessage(MSG_UNSUPPORTED_CHEAT_LIST_VERSION, + N_("Unsupported cheat list version %d"), version); + fclose(f); + return false; + } + + int type = 0; + if(fread(&type, 1, sizeof(type), f) != sizeof(type)) { + fclose(f); + return false; + } + + if(type != 1) { + systemMessage(MSG_UNSUPPORTED_CHEAT_LIST_TYPE, + N_("Unsupported cheat list type %d"), type); + fclose(f); + return false; + } + + if(fread(&count, 1, sizeof(count), f) != sizeof(count)) { + fclose(f); + return false; + } + + if(fread(gbCheatList, 1, sizeof(gbCheatList), f) != sizeof(gbCheatList)) { + fclose(f); + return false; + } + + gbCheatNumber = count; + gbCheatUpdateMap(); + + return true; +} + +bool gbVerifyGsCode(const char *code) +{ + size_t len = strlen(code); + + if(len == 0) + return true; + + if(len != 8) + return false; + + for(int i = 0; i < 8; i++) + if(!GBCHEAT_IS_HEX(code[i])) + return false; + +/* int address = GBCHEAT_HEX_VALUE(code[6]) << 12 | + GBCHEAT_HEX_VALUE(code[7]) << 8 | + GBCHEAT_HEX_VALUE(code[4]) << 4 | + GBCHEAT_HEX_VALUE(code[5]);*/ + + return true; +} + +bool gbAddGsCheat(const char *code, const char *desc) +{ + if(gbCheatNumber > 99) { + systemMessage(MSG_MAXIMUM_NUMBER_OF_CHEATS, + N_("Maximum number of cheats reached.")); + return false; + } + + if(!gbVerifyGsCode(code)) { + systemMessage(MSG_INVALID_GAMESHARK_CODE, + N_("Invalid GameShark code: %s"), code); + return false; + } + + int i = gbCheatNumber; + + strcpy(gbCheatList[i].cheatCode, code); + strcpy(gbCheatList[i].cheatDesc, desc); + + gbCheatList[i].code = GBCHEAT_HEX_VALUE(code[0]) << 4 | + GBCHEAT_HEX_VALUE(code[1]); + + gbCheatList[i].value = GBCHEAT_HEX_VALUE(code[2]) << 4 | + GBCHEAT_HEX_VALUE(code[3]); + + gbCheatList[i].address = GBCHEAT_HEX_VALUE(code[6]) << 12 | + GBCHEAT_HEX_VALUE(code[7]) << 8 | + GBCHEAT_HEX_VALUE(code[4]) << 4 | + GBCHEAT_HEX_VALUE(code[5]); + + gbCheatList[i].compare = 0; + + gbCheatList[i].enabled = true; + + int gsCode = gbCheatList[i].code; + + if ((gsCode !=1) && ((gsCode & 0xF0) !=0x80) && ((gsCode & 0xF0) !=0x90) && + ((gsCode & 0xF0) !=0xA0) && ((gsCode) !=0xF0) && ((gsCode) !=0xF1)) + systemMessage(MSG_WRONG_GAMESHARK_CODE, + N_("Wrong GameShark code type : %s"), code); + else if (((gsCode & 0xF0) ==0xA0) || ((gsCode) ==0xF0) || ((gsCode) ==0xF1)) + systemMessage(MSG_UNSUPPORTED_GAMESHARK_CODE, + N_("Unsupported GameShark code type : %s"), code); + + gbCheatNumber++; + + return true; +} + +bool gbVerifyGgCode(const char *code) +{ + size_t len = strlen(code); + + if(len != 11 && + len != 7 && + len != 6 && + len != 0) + return false; + + if(len == 0) + return true; + + if(!GBCHEAT_IS_HEX(code[0])) + return false; + if(!GBCHEAT_IS_HEX(code[1])) + return false; + if(!GBCHEAT_IS_HEX(code[2])) + return false; + if(code[3] != '-') + return false; + if(!GBCHEAT_IS_HEX(code[4])) + return false; + if(!GBCHEAT_IS_HEX(code[5])) + return false; + if(!GBCHEAT_IS_HEX(code[6])) + return false; + if(code[7] != 0) { + if(code[7] != '-') + return false; + if(code[8] != 0) { + if(!GBCHEAT_IS_HEX(code[8])) + return false; + if(!GBCHEAT_IS_HEX(code[9])) + return false; + if(!GBCHEAT_IS_HEX(code[10])) + return false; + } + } + + // int replace = (GBCHEAT_HEX_VALUE(code[0]) << 4) + + // GBCHEAT_HEX_VALUE(code[1]); + + int address = (GBCHEAT_HEX_VALUE(code[2]) << 8) + + (GBCHEAT_HEX_VALUE(code[4]) << 4) + + (GBCHEAT_HEX_VALUE(code[5])) + + ((GBCHEAT_HEX_VALUE(code[6]) ^ 0x0f) << 12); + + if(address >= 0x8000 && address <= 0x9fff) + return false; + + if(address >= 0xc000) + return false; + + if(code[7] == 0 || code[8] == '0') + return true; + + int compare = (GBCHEAT_HEX_VALUE(code[8]) << 4) + + (GBCHEAT_HEX_VALUE(code[10])); + compare = compare ^ 0xff; + compare = (compare >> 2) | ( (compare << 6) & 0xc0); + compare ^= 0x45; + + int cloak = (GBCHEAT_HEX_VALUE(code[8])) ^ (GBCHEAT_HEX_VALUE(code[9])); + + if(cloak >=1 && cloak <= 7) + return false; + + return true; +} + +bool gbAddGgCheat(const char *code, const char *desc) +{ + if(gbCheatNumber > 99) { + systemMessage(MSG_MAXIMUM_NUMBER_OF_CHEATS, + N_("Maximum number of cheats reached.")); + return false; + } + + if(!gbVerifyGgCode(code)) { + systemMessage(MSG_INVALID_GAMEGENIE_CODE, + N_("Invalid GameGenie code: %s"), code); + return false; + } + + int i = gbCheatNumber; + + size_t len = strlen(code); + + strcpy(gbCheatList[i].cheatCode, code); + strcpy(gbCheatList[i].cheatDesc, desc); + + gbCheatList[i].code = 0x101; + gbCheatList[i].value = (GBCHEAT_HEX_VALUE(code[0]) << 4) + + GBCHEAT_HEX_VALUE(code[1]); + + gbCheatList[i].address = (GBCHEAT_HEX_VALUE(code[2]) << 8) + + (GBCHEAT_HEX_VALUE(code[4]) << 4) + + (GBCHEAT_HEX_VALUE(code[5])) + + ((GBCHEAT_HEX_VALUE(code[6]) ^ 0x0f) << 12); + + gbCheatList[i].compare = 0; + + if(len != 7 && len != 8) { + + int compare = (GBCHEAT_HEX_VALUE(code[8]) << 4) + + (GBCHEAT_HEX_VALUE(code[10])); + compare = compare ^ 0xff; + compare = (compare >> 2) | ( (compare << 6) & 0xc0); + compare ^= 0x45; + + gbCheatList[i].compare = compare; + //gbCheatList[i].code = 0; + gbCheatList[i].code = 0x100; // fix for compare value + + } + + + gbCheatList[i].enabled = true; + + gbCheatMap[gbCheatList[i].address] = true; + + gbCheatNumber++; + + return true; +} + +void gbCheatRemove(int i) +{ + if(i < 0 || i >= gbCheatNumber) { + systemMessage(MSG_INVALID_CHEAT_TO_REMOVE, + N_("Invalid cheat to remove %d"), i); + return; + } + + if((i+1) < gbCheatNumber) { + memcpy(&gbCheatList[i], &gbCheatList[i+1], sizeof(gbCheat)* + (gbCheatNumber-i-1)); + } + + gbCheatNumber--; + + gbCheatUpdateMap(); +} + +void gbCheatRemoveAll() +{ + gbCheatNumber = 0; + gbCheatUpdateMap(); +} + +void gbCheatEnable(int i) +{ + if(i >=0 && i < gbCheatNumber) { + if(!gbCheatList[i].enabled) { + gbCheatList[i].enabled = true; + gbCheatUpdateMap(); + } + } +} + +void gbCheatDisable(int i) +{ + if(i >=0 && i < gbCheatNumber) { + if(gbCheatList[i].enabled) { + gbCheatList[i].enabled = false; + gbCheatUpdateMap(); + } + } +} + +bool gbCheatReadGSCodeFile(const char *fileName) +{ + FILE *file = fopen(fileName, "rb"); + + if(!file) { + systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName); + return false; + } + + fseek(file, 0x18, SEEK_SET); + int count = 0; + fread(&count, 1, 2, file); + int dummy = 0; + gbCheatRemoveAll(); + char desc[13]; + char code[9]; + int i; + for(i = 0; i < count; i++) { + fread(&dummy, 1, 2, file); + fread(desc, 1, 12, file); + desc[12] = 0; + fread(code, 1, 8, file); + code[8] = 0; + gbAddGsCheat(code, desc); + } + + for(i = 0; i < gbCheatNumber; i++) + gbCheatDisable(i); + + fclose(file); + return true; +} + +// Used to emulated GG codes +u8 gbCheatRead(u16 address) +{ + if(!cheatsEnabled) + return gbMemoryMap[address>>12][address & 0xFFF]; + + for(int i = 0; i < gbCheatNumber; i++) { + if(gbCheatList[i].enabled && gbCheatList[i].address == address) { + switch(gbCheatList[i].code) { + case 0x100: // GameGenie support + if(gbMemoryMap[address>>12][address&0xFFF] == gbCheatList[i].compare) + return gbCheatList[i].value; + break; + case 0x101: // GameGenie 6 digits code support + return gbCheatList[i].value; + break; + } + } + } + return gbMemoryMap[address>>12][address&0xFFF]; +} + + +// Used to emulate GS codes. +void gbCheatWrite(bool reboot) +{ + if(cheatsEnabled) + { + u16 address = 0; + + if (gbNextCheat >= gbCheatNumber) + gbNextCheat = 0; + + for(int i = gbNextCheat; i < gbCheatNumber; i++) { + if(gbCheatList[i].enabled) { + address = gbCheatList[i].address; + if ((!reboot) && (address >= 0x8000) && !((address>=0xA000) && (address<0xC000))) + { // These codes are executed one per one, at each Vblank + switch(gbCheatList[i].code) { + case 0x01: + gbWriteMemory(address, gbCheatList[i].value); + gbNextCheat = i+1; + return; + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + case 0x98: + case 0x99: + case 0x9A: + case 0x9B: + case 0x9C: + case 0x9D: + case 0x9E: + case 0x9F: + int oldbank = gbMemory[0xff70]; + gbWriteMemory(0xff70, gbCheatList[i].code & 0xf); + gbWriteMemory(address, gbCheatList[i].value); + gbWriteMemory(0xff70, oldbank); + gbNextCheat = i+1; + return; + } + } + else // These codes are only executed when the game is booted + { + switch(gbCheatList[i].code & 0xF0) { + case 0x80: + gbWriteMemory(0x0000, 0x0A); + gbWriteMemory(0x4000, gbCheatList[i].value & 0xF); + gbWriteMemory(address, gbCheatList[i].value); + gbNextCheat = i+1; + return; + } + } + } + } + } +} diff --git a/src/gb/gbCheats.h b/src/gb/gbCheats.h new file mode 100644 index 0000000..261767e --- /dev/null +++ b/src/gb/gbCheats.h @@ -0,0 +1,44 @@ +#ifndef GBCHEATS_H +#define GBCHEATS_H + +#include "../System.h" + +struct gbXxCheat { + char cheatDesc[100]; + char cheatCode[20]; +}; + +struct gbCheat { + char cheatCode[20]; + char cheatDesc[32]; + u16 address; + int code; + u8 compare; + u8 value; + bool enabled; +}; + +void gbCheatsSaveGame(gzFile); +void gbCheatsReadGame(gzFile, int); +void gbCheatsReadGameSkip(gzFile, int); +void gbCheatsSaveCheatList(const char *); +bool gbCheatsLoadCheatList(const char *); +bool gbCheatReadGSCodeFile(const char *); + +bool gbAddGsCheat(const char *, const char*); +bool gbAddGgCheat(const char *, const char*); +void gbCheatRemove(int); +void gbCheatRemoveAll(); +void gbCheatEnable(int); +void gbCheatDisable(int); +u8 gbCheatRead(u16); +void gbCheatWrite(bool); +bool gbVerifyGsCode(const char *code); +bool gbVerifyGgCode(const char *code); + + +extern int gbCheatNumber; +extern gbCheat gbCheatList[100]; +extern bool gbCheatMap[0x10000]; + +#endif // GBCHEATS_H diff --git a/src/gb/gbCodes.h b/src/gb/gbCodes.h new file mode 100644 index 0000000..9c30d05 --- /dev/null +++ b/src/gb/gbCodes.h @@ -0,0 +1,1424 @@ + case 0x00: + // NOP + break; + case 0x01: + // LD BC, NNNN + BC.B.B0=gbReadOpcode(PC.W++); + BC.B.B1=gbReadOpcode(PC.W++); + break; + case 0x02: + // LD (BC),A + gbWriteMemory(BC.W,AF.B.B1); + break; + case 0x03: + // INC BC + BC.W++; + break; + case 0x04: + // INC B + BC.B.B1++; + AF.B.B0= (AF.B.B0 & C_FLAG)|ZeroTable[BC.B.B1]| (BC.B.B1&0x0F? 0:H_FLAG); + break; + case 0x05: + // DEC B + BC.B.B1--; + AF.B.B0= N_FLAG|(AF.B.B0 & C_FLAG)|ZeroTable[BC.B.B1]| + ((BC.B.B1&0x0F)==0x0F? H_FLAG:0); + break; + case 0x06: + // LD B, NN + BC.B.B1=gbReadOpcode(PC.W++); + break; + case 0x07: + // RLCA + tempValue=AF.B.B1&0x80? C_FLAG:0; + AF.B.B1=(AF.B.B1<<1)|(AF.B.B1>>7); + AF.B.B0=tempValue; + break; + case 0x08: + // LD (NNNN), SP + tempRegister.B.B0=gbReadOpcode(PC.W++); + tempRegister.B.B1=gbReadOpcode(PC.W++); + gbWriteMemory(tempRegister.W++,SP.B.B0); + gbWriteMemory(tempRegister.W,SP.B.B1); + break; + case 0x09: + // ADD HL,BC + tempRegister.W=(HL.W+BC.W)&0xFFFF; + AF.B.B0= (AF.B.B0 & Z_FLAG)| ((HL.W^BC.W^tempRegister.W)&0x1000? H_FLAG:0)| + (((long)HL.W+(long)BC.W)&0x10000? C_FLAG:0); + HL.W=tempRegister.W; + break; + case 0x0a: + // LD A,(BC) + AF.B.B1=gbReadMemory(BC.W); + break; + case 0x0b: + // DEC BC + BC.W--; + break; + case 0x0c: + // INC C + BC.B.B0++; + AF.B.B0= (AF.B.B0 & C_FLAG)|ZeroTable[BC.B.B0]| (BC.B.B0&0x0F? 0:H_FLAG); + break; + case 0x0d: + // DEC C + BC.B.B0--; + AF.B.B0= N_FLAG|(AF.B.B0 & C_FLAG)|ZeroTable[BC.B.B0]| + ((BC.B.B0&0x0F)==0x0F? H_FLAG:0); + break; + case 0x0e: + // LD C, NN + BC.B.B0=gbReadOpcode(PC.W++); + break; + case 0x0f: + // RRCA + tempValue=AF.B.B1&0x01; + AF.B.B1=(AF.B.B1>>1)|(tempValue? 0x80:0); + AF.B.B0=(tempValue<<4); + break; + case 0x10: + // STOP + opcode = gbReadOpcode(PC.W++); + if(gbCgbMode) { + if(gbMemory[0xff4d] & 1) { + + gbSpeedSwitch(); + //clockTicks += 228*144-(gbSpeed ? 62 : 63); + + if(gbSpeed == 0) + gbMemory[0xff4d] = 0x00; + else + gbMemory[0xff4d] = 0x80; + } + } + break; + case 0x11: + // LD DE, NNNN + DE.B.B0=gbReadOpcode(PC.W++); + DE.B.B1=gbReadOpcode(PC.W++); + break; + case 0x12: + // LD (DE),A + gbWriteMemory(DE.W,AF.B.B1); + break; + case 0x13: + // INC DE + DE.W++; + break; + case 0x14: + // INC D + DE.B.B1++; + AF.B.B0= (AF.B.B0 & C_FLAG)|ZeroTable[DE.B.B1]| (DE.B.B1&0x0F? 0:H_FLAG); + break; + case 0x15: + // DEC D + DE.B.B1--; + AF.B.B0= N_FLAG|(AF.B.B0 & C_FLAG)|ZeroTable[DE.B.B1]| + ((DE.B.B1&0x0F)==0x0F? H_FLAG:0); + break; + case 0x16: + // LD D,NN + DE.B.B1=gbReadOpcode(PC.W++); + break; + case 0x17: + // RLA + tempValue=AF.B.B1&0x80? C_FLAG:0; + AF.B.B1=(AF.B.B1<<1)|((AF.B.B0&C_FLAG)>>4); + AF.B.B0=tempValue; + break; + case 0x18: + // JR NN + PC.W+=(s8)gbReadOpcode(PC.W)+1; + break; + case 0x19: + // ADD HL,DE + tempRegister.W=(HL.W+DE.W)&0xFFFF; + AF.B.B0= (AF.B.B0 & Z_FLAG)| ((HL.W^DE.W^tempRegister.W)&0x1000? H_FLAG:0)| + (((long)HL.W+(long)DE.W)&0x10000? C_FLAG:0); + HL.W=tempRegister.W; + break; + case 0x1a: + // LD A,(DE) + AF.B.B1=gbReadMemory(DE.W); + break; + case 0x1b: + // DEC DE + DE.W--; + break; + case 0x1c: + // INC E + DE.B.B0++; + AF.B.B0= (AF.B.B0 & C_FLAG)|ZeroTable[DE.B.B0]| (DE.B.B0&0x0F? 0:H_FLAG); + break; + case 0x1d: + // DEC E + DE.B.B0--; + AF.B.B0= N_FLAG|(AF.B.B0 & C_FLAG)|ZeroTable[DE.B.B0]| + ((DE.B.B0&0x0F)==0x0F? H_FLAG:0); + break; + case 0x1e: + // LD E,NN + DE.B.B0=gbReadOpcode(PC.W++); + break; + case 0x1f: + // RRA + tempValue=AF.B.B1&0x01; + AF.B.B1=(AF.B.B1>>1)|(AF.B.B0&C_FLAG? 0x80:0); + AF.B.B0=(tempValue<<4); + break; + case 0x20: + // JR NZ,NN + if(AF.B.B0&Z_FLAG) + PC.W++; + else { + PC.W+=(s8)gbReadOpcode(PC.W)+1; + clockTicks++; + } + break; + case 0x21: + // LD HL,NNNN + HL.B.B0=gbReadOpcode(PC.W++); + HL.B.B1=gbReadOpcode(PC.W++); + break; + case 0x22: + // LDI (HL),A + gbWriteMemory(HL.W++,AF.B.B1); + break; + case 0x23: + // INC HL + HL.W++; + break; + case 0x24: + // INC H + HL.B.B1++; + AF.B.B0= (AF.B.B0 & C_FLAG)|ZeroTable[HL.B.B1]| (HL.B.B1&0x0F? 0:H_FLAG); + break; + case 0x25: + // DEC H + HL.B.B1--; + AF.B.B0= N_FLAG|(AF.B.B0 & C_FLAG)|ZeroTable[HL.B.B1]| + ((HL.B.B1&0x0F)==0x0F? H_FLAG:0); + break; + case 0x26: + // LD H,NN + HL.B.B1=gbReadOpcode(PC.W++); + break; + case 0x27: + // DAA + tempRegister.W=AF.B.B1; + tempRegister.W|=(AF.B.B0&(C_FLAG|H_FLAG|N_FLAG))<<4; + AF.W=DAATable[tempRegister.W]; + break; + case 0x28: + // JR Z,NN + if(AF.B.B0&Z_FLAG) { + PC.W+=(s8)gbReadOpcode(PC.W)+1; + clockTicks++; + } else + PC.W++; + break; + case 0x29: + // ADD HL,HL + tempRegister.W=(HL.W+HL.W)&0xFFFF; AF.B.B0= (AF.B.B0 & Z_FLAG)| + ((HL.W^HL.W^tempRegister.W)&0x1000? H_FLAG:0)| + (((long)HL.W+(long)HL.W)&0x10000? C_FLAG:0); + HL.W=tempRegister.W; + break; + case 0x2a: + // LDI A,(HL) + AF.B.B1 = gbReadMemory(HL.W++); + break; + case 0x2b: + // DEC HL + HL.W--; + break; + case 0x2c: + // INC L + HL.B.B0++; + AF.B.B0= (AF.B.B0 & C_FLAG)|ZeroTable[HL.B.B0]| (HL.B.B0&0x0F? 0:H_FLAG); + break; + case 0x2d: + // DEC L + HL.B.B0--; + AF.B.B0= N_FLAG|(AF.B.B0 & C_FLAG)|ZeroTable[HL.B.B0]| + ((HL.B.B0&0x0F)==0x0F? H_FLAG:0); + break; + case 0x2e: + // LD L,NN + HL.B.B0=gbReadOpcode(PC.W++); + break; + case 0x2f: + // CPL + AF.B.B1 ^= 255; + AF.B.B0|=N_FLAG|H_FLAG; + break; + case 0x30: + // JR NC,NN + if(AF.B.B0&C_FLAG) + PC.W++; + else { + PC.W+=(s8)gbReadOpcode(PC.W)+1; + clockTicks++; + } + break; + case 0x31: + // LD SP,NNNN + SP.B.B0=gbReadOpcode(PC.W++); + SP.B.B1=gbReadOpcode(PC.W++); + break; + case 0x32: + // LDD (HL),A + gbWriteMemory(HL.W--,AF.B.B1); + break; + case 0x33: + // INC SP + SP.W++; + break; + case 0x34: + // INC (HL) + tempValue=gbReadMemory(HL.W)+1; + AF.B.B0= (AF.B.B0 & C_FLAG)|ZeroTable[tempValue]| (tempValue&0x0F? 0:H_FLAG); + gbWriteMemory(HL.W,tempValue); + break; + case 0x35: + // DEC (HL) + tempValue=gbReadMemory(HL.W)-1; + AF.B.B0= N_FLAG|(AF.B.B0 & C_FLAG)|ZeroTable[tempValue]| + ((tempValue&0x0F)==0x0F? H_FLAG:0);gbWriteMemory(HL.W,tempValue); + break; + case 0x36: + // LD (HL),NN + gbWriteMemory(HL.W,gbReadOpcode(PC.W++)); + break; + case 0x37: + // SCF + AF.B.B0 = (AF.B.B0 & Z_FLAG) | C_FLAG; + break; +case 0x38: + // JR C,NN + if(AF.B.B0&C_FLAG) { + PC.W+=(s8)gbReadOpcode(PC.W)+1; + clockTicks ++; + } else + PC.W++; + break; + case 0x39: + // ADD HL,SP + tempRegister.W=(HL.W+SP.W)&0xFFFF; + AF.B.B0= (AF.B.B0 & Z_FLAG)| ((HL.W^SP.W^tempRegister.W)&0x1000? H_FLAG:0)| + (((long)HL.W+(long)SP.W)&0x10000? C_FLAG:0); + HL.W=tempRegister.W; + break; + case 0x3a: + // LDD A,(HL) + AF.B.B1 = gbReadMemory(HL.W--); + break; + case 0x3b: + // DEC SP + SP.W--; + break; + case 0x3c: + // INC A + AF.B.B1++; + AF.B.B0= (AF.B.B0 & C_FLAG)|ZeroTable[AF.B.B1]| (AF.B.B1&0x0F? 0:H_FLAG); + break; + case 0x3d: + // DEC A + AF.B.B1--; + AF.B.B0= N_FLAG|(AF.B.B0 & C_FLAG)|ZeroTable[AF.B.B1]| + ((AF.B.B1&0x0F)==0x0F? H_FLAG:0); + break; + case 0x3e: + // LD A,NN + AF.B.B1=gbReadOpcode(PC.W++); + break; + case 0x3f: + // CCF + AF.B.B0^=C_FLAG;AF.B.B0&=~(N_FLAG|H_FLAG); + break; + case 0x40: + // LD B,B + BC.B.B1=BC.B.B1; + break; + case 0x41: + // LD B,C + BC.B.B1=BC.B.B0; + break; + case 0x42: + // LD B,D + BC.B.B1=DE.B.B1; + break; + case 0x43: + // LD B,E + BC.B.B1=DE.B.B0; + break; + case 0x44: + // LD B,H + BC.B.B1=HL.B.B1; + break; + case 0x45: + // LD B,L + BC.B.B1=HL.B.B0; + break; + case 0x46: + // LD B,(HL) + BC.B.B1=gbReadMemory(HL.W); + break; + case 0x47: + // LD B,A + BC.B.B1=AF.B.B1; + break; + case 0x48: + // LD C,B + BC.B.B0=BC.B.B1; + break; + case 0x49: + // LD C,C + BC.B.B0=BC.B.B0; + break; + case 0x4a: + // LD C,D + BC.B.B0=DE.B.B1; + break; + case 0x4b: + // LD C,E + BC.B.B0=DE.B.B0; + break; + case 0x4c: + // LD C,H + BC.B.B0=HL.B.B1; + break; + case 0x4d: + // LD C,L + BC.B.B0=HL.B.B0; + break; + case 0x4e: + // LD C,(HL) + BC.B.B0=gbReadMemory(HL.W); + break; + case 0x4f: + // LD C,A + BC.B.B0=AF.B.B1; + break; + case 0x50: + // LD D,B + DE.B.B1=BC.B.B1; + break; + case 0x51: + // LD D,C + DE.B.B1=BC.B.B0; + break; + case 0x52: + // LD D,D + DE.B.B1=DE.B.B1; + break; + case 0x53: + // LD D,E + DE.B.B1=DE.B.B0; + break; + case 0x54: + // LD D,H + DE.B.B1=HL.B.B1; + break; + case 0x55: + // LD D,L + DE.B.B1=HL.B.B0; + break; + case 0x56: + // LD D,(HL) + DE.B.B1=gbReadMemory(HL.W); + break; + case 0x57: + // LD D,A + DE.B.B1=AF.B.B1; + break; + case 0x58: + // LD E,B + DE.B.B0=BC.B.B1; + break; + case 0x59: + // LD E,C + DE.B.B0=BC.B.B0; + break; + case 0x5a: + // LD E,D + DE.B.B0=DE.B.B1; + break; + case 0x5b: + // LD E,E + DE.B.B0=DE.B.B0; + break; + case 0x5c: + // LD E,H + DE.B.B0=HL.B.B1; + break; + case 0x5d: + // LD E,L + DE.B.B0=HL.B.B0; + break; + case 0x5e: + // LD E,(HL) + DE.B.B0=gbReadMemory(HL.W); + break; + case 0x5f: + // LD E,A + DE.B.B0=AF.B.B1; + break; + case 0x60: + // LD H,B + HL.B.B1=BC.B.B1; + break; + case 0x61: + // LD H,C + HL.B.B1=BC.B.B0; + break; + case 0x62: + // LD H,D + HL.B.B1=DE.B.B1; + break; + case 0x63: + // LD H,E + HL.B.B1=DE.B.B0; + break; + case 0x64: + // LD H,H + HL.B.B1=HL.B.B1; + break; + case 0x65: + // LD H,L + HL.B.B1=HL.B.B0; + break; + case 0x66: + // LD H,(HL) + HL.B.B1=gbReadMemory(HL.W); + break; + case 0x67: + // LD H,A + HL.B.B1=AF.B.B1; + break; + case 0x68: + // LD L,B + HL.B.B0=BC.B.B1; + break; + case 0x69: + // LD L,C + HL.B.B0=BC.B.B0; + break; + case 0x6a: + // LD L,D + HL.B.B0=DE.B.B1; + break; + case 0x6b: + // LD L,E + HL.B.B0=DE.B.B0; + break; + case 0x6c: + // LD L,H + HL.B.B0=HL.B.B1; + break; + case 0x6d: + // LD L,L + HL.B.B0=HL.B.B0; + break; + case 0x6e: + // LD L,(HL) + HL.B.B0=gbReadMemory(HL.W); + break; + case 0x6f: + // LD L,A + HL.B.B0=AF.B.B1; + break; + case 0x70: + // LD (HL),B + gbWriteMemory(HL.W,BC.B.B1); + break; + case 0x71: + // LD (HL),C + gbWriteMemory(HL.W,BC.B.B0); + break; + case 0x72: + // LD (HL),D + gbWriteMemory(HL.W,DE.B.B1); + break; + case 0x73: + // LD (HL),E + gbWriteMemory(HL.W,DE.B.B0); + break; + case 0x74: + // LD (HL),H + gbWriteMemory(HL.W,HL.B.B1); + break; + case 0x75: + // LD (HL),L + gbWriteMemory(HL.W,HL.B.B0); + break; + case 0x76: + // HALT + // If an EI is pending, the interrupts are triggered before Halt state !! + // Fix Torpedo Range's intro. + if (IFF & 0x40) + { + IFF &= ~0x70; + IFF |=1; + PC.W--; + } + else + { + // if (IE & IF) and interrupts are disabeld, + // Halt is cancelled. + if ((register_IE & register_IF & 0x1f) && !(IFF & 1)) + { + IFF|=2; + } + else + IFF |= 0x80; + } + break; + case 0x77: + // LD (HL),A + gbWriteMemory(HL.W,AF.B.B1); + break; + case 0x78: + // LD A,B + AF.B.B1=BC.B.B1; + break; + case 0x79: + // LD A,C + AF.B.B1=BC.B.B0; + break; + case 0x7a: + // LD A,D + AF.B.B1=DE.B.B1; + break; + case 0x7b: + // LD A,E + AF.B.B1=DE.B.B0; + break; + case 0x7c: + // LD A,H + AF.B.B1=HL.B.B1; + break; + case 0x7d: + // LD A,L + AF.B.B1=HL.B.B0; + break; + case 0x7e: + // LD A,(HL) + AF.B.B1=gbReadMemory(HL.W); + break; + case 0x7f: + // LD A,A + AF.B.B1=AF.B.B1; + break; + case 0x80: + // ADD B + tempRegister.W=AF.B.B1+BC.B.B1; + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^BC.B.B1^tempRegister.B.B0)&0x10 ? H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x81: + // ADD C + tempRegister.W=AF.B.B1+BC.B.B0; + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^BC.B.B0^tempRegister.B.B0)&0x10 ? H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x82: + // ADD D + tempRegister.W=AF.B.B1+DE.B.B1; + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^DE.B.B1^tempRegister.B.B0)&0x10 ? H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x83: + // ADD E + tempRegister.W=AF.B.B1+DE.B.B0; + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^DE.B.B0^tempRegister.B.B0)&0x10 ? H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x84: + // ADD H + tempRegister.W=AF.B.B1+HL.B.B1; + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^HL.B.B1^tempRegister.B.B0)&0x10 ? H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x85: + // ADD L + tempRegister.W=AF.B.B1+HL.B.B0; + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^HL.B.B0^tempRegister.B.B0)&0x10 ? H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x86: + // ADD (HL) + tempValue=gbReadMemory(HL.W); + tempRegister.W=AF.B.B1+tempValue; + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^tempValue^tempRegister.B.B0)&0x10 ? H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x87: + // ADD A + tempRegister.W=AF.B.B1+AF.B.B1; + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^AF.B.B1^tempRegister.B.B0)&0x10 ? H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x88: + // ADC B: + tempRegister.W=AF.B.B1+BC.B.B1+(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^BC.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x89: + // ADC C + tempRegister.W=AF.B.B1+BC.B.B0+(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^BC.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x8a: + // ADC D + tempRegister.W=AF.B.B1+DE.B.B1+(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^DE.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x8b: + // ADC E + tempRegister.W=AF.B.B1+DE.B.B0+(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^DE.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x8c: + // ADC H + tempRegister.W=AF.B.B1+HL.B.B1+(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^HL.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); AF.B.B1=tempRegister.B.B0; + break; + case 0x8d: + // ADC L + tempRegister.W=AF.B.B1+HL.B.B0+(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^HL.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x8e: + // ADC (HL) + tempValue=gbReadMemory(HL.W); + tempRegister.W=AF.B.B1+tempValue+(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^tempValue^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x8f: + // ADC A + tempRegister.W=AF.B.B1+AF.B.B1+(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^AF.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x90: + // SUB B + tempRegister.W=AF.B.B1-BC.B.B1; + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^BC.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x91: + // SUB C + tempRegister.W=AF.B.B1-BC.B.B0; + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^BC.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x92: + // SUB D + tempRegister.W=AF.B.B1-DE.B.B1; + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^DE.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x93: + // SUB E + tempRegister.W=AF.B.B1-DE.B.B0; + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^DE.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x94: + // SUB H + tempRegister.W=AF.B.B1-HL.B.B1; + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^HL.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x95: + // SUB L + tempRegister.W=AF.B.B1-HL.B.B0; + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^HL.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x96: + // SUB (HL) + tempValue=gbReadMemory(HL.W); + tempRegister.W=AF.B.B1-tempValue; + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^tempValue^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x97: + // SUB A + AF.B.B1=0; + AF.B.B0=N_FLAG|Z_FLAG; + break; + case 0x98: + // SBC B + tempRegister.W=AF.B.B1-BC.B.B1-(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^BC.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x99: + // SBC C + tempRegister.W=AF.B.B1-BC.B.B0-(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^BC.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x9a: + // SBC D + tempRegister.W=AF.B.B1-DE.B.B1-(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^DE.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x9b: + // SBC E + tempRegister.W=AF.B.B1-DE.B.B0-(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^DE.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x9c: + // SBC H + tempRegister.W=AF.B.B1-HL.B.B1-(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^HL.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x9d: + // SBC L + tempRegister.W=AF.B.B1-HL.B.B0-(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^HL.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x9e: + // SBC (HL) + tempValue=gbReadMemory(HL.W); + tempRegister.W=AF.B.B1-tempValue-(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^tempValue^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x9f: + // SBC A + tempRegister.W=AF.B.B1-AF.B.B1-(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^AF.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0xa0: + // AND B + AF.B.B1&=BC.B.B1; + AF.B.B0=H_FLAG|ZeroTable[AF.B.B1]; + break; + case 0xa1: + // AND C + AF.B.B1&=BC.B.B0; + AF.B.B0=H_FLAG|ZeroTable[AF.B.B1]; + break; + case 0xa2: + // AND_D + AF.B.B1&=DE.B.B1; + AF.B.B0=H_FLAG|ZeroTable[AF.B.B1]; + break; + case 0xa3: + // AND E + AF.B.B1&=DE.B.B0; + AF.B.B0=H_FLAG|ZeroTable[AF.B.B1]; + break; + case 0xa4: + // AND H + AF.B.B1&=HL.B.B1; + AF.B.B0=H_FLAG|ZeroTable[AF.B.B1]; + break; + case 0xa5: + // AND L + AF.B.B1&=HL.B.B0; + AF.B.B0=H_FLAG|ZeroTable[AF.B.B1]; + break; + case 0xa6: + // AND (HL) + tempValue=gbReadMemory(HL.W); + AF.B.B1&=tempValue; + AF.B.B0=H_FLAG|ZeroTable[AF.B.B1]; + break; + case 0xa7: + // AND A + AF.B.B1&=AF.B.B1; + AF.B.B0=H_FLAG|ZeroTable[AF.B.B1]; + break; + case 0xa8: + // XOR B + AF.B.B1^=BC.B.B1; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xa9: + // XOR C + AF.B.B1^=BC.B.B0; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xaa: + // XOR D + AF.B.B1^=DE.B.B1; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xab: + // XOR E + AF.B.B1^=DE.B.B0; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xac: + // XOR H + AF.B.B1^=HL.B.B1; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xad: + // XOR L + AF.B.B1^=HL.B.B0; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xae: + // XOR (HL) + tempValue=gbReadMemory(HL.W); + AF.B.B1^=tempValue; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xaf: + // XOR A + AF.B.B1=0; + AF.B.B0=Z_FLAG; + break; + case 0xb0: + // OR B + AF.B.B1|=BC.B.B1; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xb1: + // OR C + AF.B.B1|=BC.B.B0; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xb2: + // OR D + AF.B.B1|=DE.B.B1; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xb3: + // OR E + AF.B.B1|=DE.B.B0; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xb4: + // OR H + AF.B.B1|=HL.B.B1; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xb5: + // OR L + AF.B.B1|=HL.B.B0; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xb6: + // OR (HL) + tempValue=gbReadMemory(HL.W); + AF.B.B1|=tempValue; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xb7: + // OR A + AF.B.B1|=AF.B.B1; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xb8: + // CP B: + tempRegister.W=AF.B.B1-BC.B.B1; + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^BC.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); + break; + case 0xb9: + // CP C + tempRegister.W=AF.B.B1-BC.B.B0; + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^BC.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); + break; + case 0xba: + // CP D + tempRegister.W=AF.B.B1-DE.B.B1; + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^DE.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); + break; + case 0xbb: + // CP E + tempRegister.W=AF.B.B1-DE.B.B0; + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^DE.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); + break; + case 0xbc: + // CP H + tempRegister.W=AF.B.B1-HL.B.B1; + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^HL.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); + break; + case 0xbd: + // CP L + tempRegister.W=AF.B.B1-HL.B.B0; + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^HL.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); + break; + case 0xbe: + // CP (HL) + tempValue=gbReadMemory(HL.W); + tempRegister.W=AF.B.B1-tempValue; + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^tempValue^tempRegister.B.B0)&0x10?H_FLAG:0); + break; + case 0xbf: + // CP A + AF.B.B0=N_FLAG|Z_FLAG; + break; + case 0xc0: + // RET NZ + if(!(AF.B.B0&Z_FLAG)) { + PC.B.B0=gbReadMemory(SP.W++); + PC.B.B1=gbReadMemory(SP.W++); + clockTicks += 3; + } + break; + case 0xc1: + // POP BC + BC.B.B0=gbReadMemory(SP.W++); + BC.B.B1=gbReadMemory(SP.W++); + break; + case 0xc2: + // JP NZ,NNNN + if(AF.B.B0&Z_FLAG) + PC.W+=2; + else { + tempRegister.B.B0=gbReadOpcode(PC.W++); + tempRegister.B.B1=gbReadOpcode(PC.W); + PC.W=tempRegister.W; + clockTicks++; + } + break; + case 0xc3: + // JP NNNN + tempRegister.B.B0=gbReadOpcode(PC.W++); + tempRegister.B.B1=gbReadOpcode(PC.W); + PC.W=tempRegister.W; + break; + case 0xc4: + // CALL NZ,NNNN + if(AF.B.B0&Z_FLAG) + PC.W+=2; + else { + tempRegister.B.B0=gbReadOpcode(PC.W++); + tempRegister.B.B1=gbReadOpcode(PC.W++); + gbWriteMemory(--SP.W,PC.B.B1); + gbWriteMemory(--SP.W,PC.B.B0); + PC.W=tempRegister.W; + clockTicks += 3; + } + break; + case 0xc5: + // PUSH BC + gbWriteMemory(--SP.W,BC.B.B1); + gbWriteMemory(--SP.W,BC.B.B0); + break; + case 0xc6: + // ADD NN + tempValue=gbReadOpcode(PC.W++); + tempRegister.W=AF.B.B1+tempValue; + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^tempValue^tempRegister.B.B0)&0x10 ? H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0xc7: + // RST 00 + gbWriteMemory(--SP.W,PC.B.B1); + gbWriteMemory(--SP.W,PC.B.B0); + PC.W=0x0000; + break; + case 0xc8: + // RET Z + if(AF.B.B0&Z_FLAG) { + PC.B.B0=gbReadMemory(SP.W++); + PC.B.B1=gbReadMemory(SP.W++); + clockTicks += 3; + } + break; + case 0xc9: + // RET + PC.B.B0=gbReadMemory(SP.W++); + PC.B.B1=gbReadMemory(SP.W++); + break; + case 0xca: + // JP Z,NNNN + if(AF.B.B0&Z_FLAG) { + tempRegister.B.B0=gbReadOpcode(PC.W++); + tempRegister.B.B1=gbReadOpcode(PC.W); + PC.W=tempRegister.W; + clockTicks++; + } else + PC.W+=2; + break; + // CB done outside + case 0xcc: + // CALL Z,NNNN + if(AF.B.B0&Z_FLAG) { + tempRegister.B.B0=gbReadOpcode(PC.W++); + tempRegister.B.B1=gbReadOpcode(PC.W++); + gbWriteMemory(--SP.W,PC.B.B1); + gbWriteMemory(--SP.W,PC.B.B0); + PC.W=tempRegister.W; + clockTicks += 3; + } else + PC.W+=2; + break; + case 0xcd: + // CALL NNNN + tempRegister.B.B0=gbReadOpcode(PC.W++); + tempRegister.B.B1=gbReadOpcode(PC.W++); + gbWriteMemory(--SP.W,PC.B.B1); + gbWriteMemory(--SP.W,PC.B.B0); + PC.W=tempRegister.W; + break; + case 0xce: + // ADC NN + tempValue=gbReadOpcode(PC.W++); + tempRegister.W=AF.B.B1+tempValue+(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^tempValue^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0xcf: + // RST 08 + gbWriteMemory(--SP.W,PC.B.B1); + gbWriteMemory(--SP.W,PC.B.B0); + PC.W=0x0008; + break; + case 0xd0: + // RET NC + if(!(AF.B.B0&C_FLAG)) { + PC.B.B0=gbReadMemory(SP.W++); + PC.B.B1=gbReadMemory(SP.W++); + clockTicks += 3; + } + break; + case 0xd1: + // POP DE + DE.B.B0=gbReadMemory(SP.W++); + DE.B.B1=gbReadMemory(SP.W++); + break; + case 0xd2: + // JP NC,NNNN + if(AF.B.B0&C_FLAG) + PC.W+=2; + else { + tempRegister.B.B0=gbReadOpcode(PC.W++); + tempRegister.B.B1=gbReadOpcode(PC.W); + PC.W=tempRegister.W; + clockTicks++; + } + break; + // D3 illegal + case 0xd3: + PC.W--; + IFF = 0; + break; + case 0xd4: + // CALL NC,NNNN + if(AF.B.B0&C_FLAG) + PC.W+=2; + else { + tempRegister.B.B0=gbReadOpcode(PC.W++); + tempRegister.B.B1=gbReadOpcode(PC.W++); + gbWriteMemory(--SP.W,PC.B.B1); + gbWriteMemory(--SP.W,PC.B.B0); + PC.W=tempRegister.W; + clockTicks += 3; + } + break; + case 0xd5: + // PUSH DE + gbWriteMemory(--SP.W,DE.B.B1); + gbWriteMemory(--SP.W,DE.B.B0); + break; + case 0xd6: + // SUB NN + tempValue=gbReadOpcode(PC.W++); + tempRegister.W=AF.B.B1-tempValue; + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^tempValue^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0xd7: + // RST 10 + gbWriteMemory(--SP.W,PC.B.B1); + gbWriteMemory(--SP.W,PC.B.B0); + PC.W=0x0010; + break; + case 0xd8: + // RET C + if(AF.B.B0&C_FLAG) { + PC.B.B0=gbReadMemory(SP.W++); + PC.B.B1=gbReadMemory(SP.W++); + clockTicks += 3; + } + break; + case 0xd9: + // RETI + PC.B.B0=gbReadMemory(SP.W++); + PC.B.B1=gbReadMemory(SP.W++); + IFF |= 0x01; + break; + case 0xda: + // JP C,NNNN + if(AF.B.B0&C_FLAG) { + tempRegister.B.B0=gbReadOpcode(PC.W++); + tempRegister.B.B1=gbReadOpcode(PC.W); + PC.W=tempRegister.W; + clockTicks++; + } else + PC.W+=2; + break; + // DB illegal + case 0xdb: + PC.W--; + IFF = 0; + break; + case 0xdc: + // CALL C,NNNN + if(AF.B.B0&C_FLAG) { + tempRegister.B.B0=gbReadOpcode(PC.W++); + tempRegister.B.B1=gbReadOpcode(PC.W++); + gbWriteMemory(--SP.W,PC.B.B1); + gbWriteMemory(--SP.W,PC.B.B0); + PC.W=tempRegister.W; + clockTicks += 3; + } else + PC.W+=2; + break; + // DD illegal + case 0xdd: + PC.W--; + IFF = 0; + break; + case 0xde: + // SBC NN + tempValue=gbReadOpcode(PC.W++); + tempRegister.W=AF.B.B1-tempValue-(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^tempValue^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0xdf: + // RST 18 + gbWriteMemory(--SP.W,PC.B.B1); + gbWriteMemory(--SP.W,PC.B.B0); + PC.W=0x0018; + break; + case 0xe0: + // LD (FF00+NN),A + gbWriteMemory(0xff00 + gbReadOpcode(PC.W++),AF.B.B1); + break; + case 0xe1: + // POP HL + HL.B.B0=gbReadMemory(SP.W++); + HL.B.B1=gbReadMemory(SP.W++); + break; + case 0xe2: + // LD (FF00+C),A + gbWriteMemory(0xff00 + BC.B.B0,AF.B.B1); + break; + // E3 illegal + // E4 illegal + case 0xe3: + case 0xe4: + PC.W--; + IFF = 0; + break; + case 0xe5: + // PUSH HL + gbWriteMemory(--SP.W,HL.B.B1); + gbWriteMemory(--SP.W,HL.B.B0); + break; + case 0xe6: + // AND NN + tempValue=gbReadOpcode(PC.W++); + AF.B.B1&=tempValue; + AF.B.B0=H_FLAG|ZeroTable[AF.B.B1]; + break; + case 0xe7: + // RST 20 + gbWriteMemory(--SP.W,PC.B.B1); + gbWriteMemory(--SP.W,PC.B.B0); + PC.W=0x0020; + break; + case 0xe8: + // ADD SP,NN + offset = (s8)gbReadOpcode(PC.W++); + tempRegister.W = SP.W + offset; + AF.B.B0 = ((SP.W^offset^tempRegister.W)&0x100? C_FLAG : 0) | + ((SP.W^offset^tempRegister.W)& 0x10? H_FLAG : 0); + SP.W = tempRegister.W; + break; + case 0xe9: + // LD PC,HL + PC.W=HL.W; + break; + case 0xea: + // LD (NNNN),A + tempRegister.B.B0=gbReadOpcode(PC.W++); + tempRegister.B.B1=gbReadOpcode(PC.W++); + gbWriteMemory(tempRegister.W,AF.B.B1); + break; + // EB illegal + // EC illegal + // ED illegal + case 0xeb: + case 0xec: + case 0xed: + PC.W--; + IFF = 0; + break; + case 0xee: + // XOR NN + tempValue=gbReadOpcode(PC.W++); + AF.B.B1^=tempValue; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xef: + // RST 28 + gbWriteMemory(--SP.W,PC.B.B1); + gbWriteMemory(--SP.W,PC.B.B0); + PC.W=0x0028; + break; + case 0xf0: + // LD A,(FF00+NN) + AF.B.B1 = gbReadMemory(0xff00+gbReadOpcode(PC.W++)); + break; + case 0xf1: + // POP AF + AF.B.B0=gbReadMemory(SP.W++)&0xF0; + AF.B.B1=gbReadMemory(SP.W++); + break; + case 0xf2: + // LD A,(FF00+C) + AF.B.B1 = gbReadMemory(0xff00+BC.B.B0); + break; + case 0xf3: + // DI + // IFF&=0xFE; + IFF|=0x08; + break; + // F4 illegal + case 0xf4: + PC.W--; + IFF = 0; + break; + case 0xf5: + // PUSH AF + gbWriteMemory(--SP.W,AF.B.B1); + gbWriteMemory(--SP.W,AF.B.B0); + break; + case 0xf6: + // OR NN + tempValue=gbReadOpcode(PC.W++); + AF.B.B1|=tempValue; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xf7: + // RST 30 + gbWriteMemory(--SP.W,PC.B.B1); + gbWriteMemory(--SP.W,PC.B.B0); + PC.W=0x0030; + break; + case 0xf8: + // LD HL,SP+NN + offset = (s8)gbReadOpcode(PC.W++); + tempRegister.W = SP.W + offset; + AF.B.B0 = ((SP.W^offset^tempRegister.W)&0x100? C_FLAG : 0) | + ((SP.W^offset^tempRegister.W)& 0x10? H_FLAG : 0); + HL.W = tempRegister.W; + break; + case 0xf9: + // LD SP,HL + SP.W=HL.W; + break; + case 0xfa: + // LD A,(NNNN) + tempRegister.B.B0=gbReadOpcode(PC.W++); + tempRegister.B.B1=gbReadOpcode(PC.W++); + AF.B.B1=gbReadMemory(tempRegister.W); + break; + case 0xfb: + // EI + if (!(IFF & 0x30)) + // If an EI is executed right before HALT, + // the interrupts are triggered before the Halt state !! + // Fix Torpedo Range Intro. + // IFF |= 0x10 : 1 ticks before the EI enables the interrupts + // IFF |= 0x40 : marks that an EI is being executed. + IFF|=0x50; + break; + // FC illegal (FC = breakpoint) + case 0xfc: + breakpoint = true; + break; + // FD illegal + case 0xfd: + PC.W--; + IFF = 0; + break; + case 0xfe: + // CP NN + tempValue=gbReadOpcode(PC.W++); + tempRegister.W=AF.B.B1-tempValue; + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^tempValue^tempRegister.B.B0)&0x10?H_FLAG:0); + break; + case 0xff: + // RST 38 + gbWriteMemory(--SP.W,PC.B.B1); + gbWriteMemory(--SP.W,PC.B.B0); + PC.W=0x0038; + break; + default: + if (gbSystemMessage == false) + { + systemMessage(0, N_("Unknown opcode %02x at %04x"), + gbReadOpcode(PC.W-1),PC.W-1); + gbSystemMessage =true; + } + return; diff --git a/src/gb/gbCodesCB.h b/src/gb/gbCodesCB.h new file mode 100644 index 0000000..880fe5e --- /dev/null +++ b/src/gb/gbCodesCB.h @@ -0,0 +1,1272 @@ + case 0x00: + // RLC B + AF.B.B0 = (BC.B.B1 & 0x80)?C_FLAG:0; + BC.B.B1 = (BC.B.B1<<1) | (BC.B.B1>>7); + AF.B.B0 |= ZeroTable[BC.B.B1]; + break; + case 0x01: + // RLC C + AF.B.B0 = (BC.B.B0 & 0x80)?C_FLAG:0; + BC.B.B0 = (BC.B.B0<<1) | (BC.B.B0>>7); + AF.B.B0 |= ZeroTable[BC.B.B0]; + break; + case 0x02: + // RLC D + AF.B.B0 = (DE.B.B1 & 0x80)?C_FLAG:0; + DE.B.B1 = (DE.B.B1<<1) | (DE.B.B1>>7); + AF.B.B0 |= ZeroTable[DE.B.B1]; + break; + case 0x03: + // RLC E + AF.B.B0 = (DE.B.B0 & 0x80)?C_FLAG:0; + DE.B.B0 = (DE.B.B0<<1) | (DE.B.B0>>7); + AF.B.B0 |= ZeroTable[DE.B.B0]; + break; + case 0x04: + // RLC H + AF.B.B0 = (HL.B.B1 & 0x80)?C_FLAG:0; + HL.B.B1 = (HL.B.B1<<1) | (HL.B.B1>>7); + AF.B.B0 |= ZeroTable[HL.B.B1]; + break; + case 0x05: + // RLC L + AF.B.B0 = (HL.B.B0 & 0x80)?C_FLAG:0; + HL.B.B0 = (HL.B.B0<<1) | (HL.B.B0>>7); + AF.B.B0 |= ZeroTable[HL.B.B0]; + break; + case 0x06: + // RLC (HL) + tempValue=gbReadMemory(HL.W); + AF.B.B0 = (tempValue & 0x80)?C_FLAG:0; + tempValue = (tempValue<<1) | (tempValue>>7); + AF.B.B0 |= ZeroTable[tempValue]; + gbWriteMemory(HL.W,tempValue); + break; + case 0x07: + // RLC A + AF.B.B0 = (AF.B.B1 & 0x80)?C_FLAG:0; + AF.B.B1 = (AF.B.B1<<1) | (AF.B.B1>>7); + AF.B.B0 |= ZeroTable[AF.B.B1]; + break; + case 0x08: + // RRC B + AF.B.B0=(BC.B.B1&0x01 ? C_FLAG : 0); + BC.B.B1=(BC.B.B1>>1)|(BC.B.B1<<7); + AF.B.B0|=ZeroTable[BC.B.B1]; + break; + case 0x09: + // RRC C + AF.B.B0=(BC.B.B0&0x01 ? C_FLAG : 0); + BC.B.B0=(BC.B.B0>>1)|(BC.B.B0<<7); + AF.B.B0|=ZeroTable[BC.B.B0]; + break; + case 0x0a: + // RRC D + AF.B.B0=(DE.B.B1&0x01 ? C_FLAG : 0); + DE.B.B1=(DE.B.B1>>1)|(DE.B.B1<<7); + AF.B.B0|=ZeroTable[DE.B.B1]; + break; + case 0x0b: + // RRC E + AF.B.B0=(DE.B.B0&0x01 ? C_FLAG : 0); + DE.B.B0=(DE.B.B0>>1)|(DE.B.B0<<7); + AF.B.B0|=ZeroTable[DE.B.B0]; + break; + case 0x0c: + // RRC H + AF.B.B0=(HL.B.B1&0x01 ? C_FLAG : 0); + HL.B.B1=(HL.B.B1>>1)|(HL.B.B1<<7); + AF.B.B0|=ZeroTable[HL.B.B1]; + break; + case 0x0d: + // RRC L + AF.B.B0=(HL.B.B0&0x01 ? C_FLAG : 0); + HL.B.B0=(HL.B.B0>>1)|(HL.B.B0<<7); + AF.B.B0|=ZeroTable[HL.B.B0]; + break; + case 0x0e: + // RRC (HL) + tempValue=gbReadMemory(HL.W); + AF.B.B0=(tempValue&0x01 ? C_FLAG : 0); + tempValue=(tempValue>>1)|(tempValue<<7); + AF.B.B0|=ZeroTable[tempValue]; + gbWriteMemory(HL.W,tempValue); + break; + case 0x0f: + // RRC A + AF.B.B0=(AF.B.B1&0x01 ? C_FLAG : 0); + AF.B.B1=(AF.B.B1>>1)|(AF.B.B1<<7); + AF.B.B0|=ZeroTable[AF.B.B1]; + break; + case 0x10: + // RL B + if(BC.B.B1&0x80) { + BC.B.B1=(BC.B.B1<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[BC.B.B1]|C_FLAG; + } else { + BC.B.B1=(BC.B.B1<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[BC.B.B1]; + } + break; + case 0x11: + // RL C + if(BC.B.B0&0x80) { + BC.B.B0=(BC.B.B0<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[BC.B.B0]|C_FLAG; + } else { + BC.B.B0=(BC.B.B0<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[BC.B.B0]; + } + break; + case 0x12: + // RL D + if(DE.B.B1&0x80) { + DE.B.B1=(DE.B.B1<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[DE.B.B1]|C_FLAG; + } else { + DE.B.B1=(DE.B.B1<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[DE.B.B1]; + } + break; + case 0x13: + // RL E + if(DE.B.B0&0x80) { + DE.B.B0=(DE.B.B0<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[DE.B.B0]|C_FLAG; + } else { + DE.B.B0=(DE.B.B0<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[DE.B.B0]; + } + break; + case 0x14: + // RL H + if(HL.B.B1&0x80) { + HL.B.B1=(HL.B.B1<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[HL.B.B1]|C_FLAG; + } else { + HL.B.B1=(HL.B.B1<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[HL.B.B1]; + } + break; + case 0x15: + // RL L + if(HL.B.B0&0x80) { + HL.B.B0=(HL.B.B0<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[HL.B.B0]|C_FLAG; + } else { + HL.B.B0=(HL.B.B0<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[HL.B.B0]; + } + break; + case 0x16: + // RL (HL) + tempValue=gbReadMemory(HL.W); + if(tempValue&0x80) { + tempValue=(tempValue<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[tempValue]|C_FLAG; + } else { + tempValue=(tempValue<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[tempValue]; + } + gbWriteMemory(HL.W,tempValue); + break; + case 0x17: + // RL A + if(AF.B.B1&0x80) { + AF.B.B1=(AF.B.B1<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[AF.B.B1]|C_FLAG; + } else { + AF.B.B1=(AF.B.B1<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[AF.B.B1]; + } + break; + case 0x18: + // RR B + if(BC.B.B1&0x01) { + BC.B.B1=(BC.B.B1>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[BC.B.B1]|C_FLAG; + } else { + BC.B.B1=(BC.B.B1>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[BC.B.B1]; + } + break; + case 0x19: + // RR C + if(BC.B.B0&0x01) { + BC.B.B0=(BC.B.B0>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[BC.B.B0]|C_FLAG; + } else { + BC.B.B0=(BC.B.B0>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[BC.B.B0]; + } + break; + case 0x1a: + // RR D + if(DE.B.B1&0x01) { + DE.B.B1=(DE.B.B1>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[DE.B.B1]|C_FLAG; + } else { + DE.B.B1=(DE.B.B1>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[DE.B.B1]; + } + break; + case 0x1b: + // RR E + if(DE.B.B0&0x01) { + DE.B.B0=(DE.B.B0>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[DE.B.B0]|C_FLAG; + } else { + DE.B.B0=(DE.B.B0>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[DE.B.B0]; + } + break; + case 0x1c: + // RR H + if(HL.B.B1&0x01) { + HL.B.B1=(HL.B.B1>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[HL.B.B1]|C_FLAG; + } else { + HL.B.B1=(HL.B.B1>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[HL.B.B1]; + } + break; + case 0x1d: + // RR L + if(HL.B.B0&0x01) { + HL.B.B0=(HL.B.B0>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[HL.B.B0]|C_FLAG; + } else { + HL.B.B0=(HL.B.B0>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[HL.B.B0]; + } + break; + case 0x1e: + // RR (HL) + tempValue=gbReadMemory(HL.W); + if(tempValue&0x01) { + tempValue=(tempValue>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[tempValue]|C_FLAG; + } else { + tempValue=(tempValue>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[tempValue]; + } + gbWriteMemory(HL.W,tempValue); + break; + case 0x1f: + // RR A + if(AF.B.B1&0x01) { + AF.B.B1=(AF.B.B1>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[AF.B.B1]|C_FLAG; + } else { + AF.B.B1=(AF.B.B1>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[AF.B.B1]; + } + break; + case 0x20: + // SLA B + AF.B.B0=(BC.B.B1&0x80?C_FLAG : 0); + BC.B.B1<<=1; + AF.B.B0|=ZeroTable[BC.B.B1]; + break; + case 0x21: + // SLA C + AF.B.B0=(BC.B.B0&0x80?C_FLAG : 0); + BC.B.B0<<=1; + AF.B.B0|=ZeroTable[BC.B.B0]; + break; + case 0x22: + // SLA D + AF.B.B0=(DE.B.B1&0x80?C_FLAG : 0); + DE.B.B1<<=1; + AF.B.B0|=ZeroTable[DE.B.B1]; + break; + case 0x23: + // SLA E + AF.B.B0=(DE.B.B0&0x80?C_FLAG : 0); + DE.B.B0<<=1; + AF.B.B0|=ZeroTable[DE.B.B0]; + break; + case 0x24: + // SLA H + AF.B.B0=(HL.B.B1&0x80?C_FLAG : 0); + HL.B.B1<<=1; + AF.B.B0|=ZeroTable[HL.B.B1]; + break; + case 0x25: + // SLA L + AF.B.B0=(HL.B.B0&0x80?C_FLAG : 0); + HL.B.B0<<=1; + AF.B.B0|=ZeroTable[HL.B.B0]; + break; + case 0x26: + // SLA (HL) + tempValue=gbReadMemory(HL.W); + AF.B.B0=(tempValue&0x80?C_FLAG : 0); + tempValue<<=1; + AF.B.B0|=ZeroTable[tempValue]; + gbWriteMemory(HL.W,tempValue); + break; + case 0x27: + // SLA A + AF.B.B0=(AF.B.B1&0x80?C_FLAG : 0); + AF.B.B1<<=1; + AF.B.B0|=ZeroTable[AF.B.B1]; + break; + case 0x28: + // SRA B + AF.B.B0=(BC.B.B1&0x01 ? C_FLAG: 0); + BC.B.B1=(BC.B.B1>>1)|(BC.B.B1&0x80); + AF.B.B0|=ZeroTable[BC.B.B1]; + break; + case 0x29: + // SRA C + AF.B.B0=(BC.B.B0&0x01 ? C_FLAG: 0); + BC.B.B0=(BC.B.B0>>1)|(BC.B.B0&0x80); + AF.B.B0|=ZeroTable[BC.B.B0]; + break; + case 0x2a: + // SRA D + AF.B.B0=(DE.B.B1&0x01 ? C_FLAG: 0); + DE.B.B1=(DE.B.B1>>1)|(DE.B.B1&0x80); + AF.B.B0|=ZeroTable[DE.B.B1]; + break; + case 0x2b: + // SRA E + AF.B.B0=(DE.B.B0&0x01 ? C_FLAG: 0); + DE.B.B0=(DE.B.B0>>1)|(DE.B.B0&0x80); + AF.B.B0|=ZeroTable[DE.B.B0]; + break; + case 0x2c: + // SRA H + AF.B.B0=(HL.B.B1&0x01 ? C_FLAG: 0); + HL.B.B1=(HL.B.B1>>1)|(HL.B.B1&0x80); + AF.B.B0|=ZeroTable[HL.B.B1]; + break; + case 0x2d: + // SRA L + AF.B.B0=(HL.B.B0&0x01 ? C_FLAG: 0); + HL.B.B0=(HL.B.B0>>1)|(HL.B.B0&0x80); + AF.B.B0|=ZeroTable[HL.B.B0]; + break; + case 0x2e: + // SRA (HL) + tempValue=gbReadMemory(HL.W); + AF.B.B0=(tempValue&0x01 ? C_FLAG: 0); + tempValue=(tempValue>>1)|(tempValue&0x80); + AF.B.B0|=ZeroTable[tempValue]; + gbWriteMemory(HL.W,tempValue); + break; + case 0x2f: + // SRA A + AF.B.B0=(AF.B.B1&0x01 ? C_FLAG: 0); + AF.B.B1=(AF.B.B1>>1)|(AF.B.B1&0x80); + AF.B.B0|=ZeroTable[AF.B.B1]; + break; + case 0x30: + // SWAP B + BC.B.B1 = (BC.B.B1&0xf0)>>4 | (BC.B.B1&0x0f)<<4; + AF.B.B0 = ZeroTable[BC.B.B1]; + break; + case 0x31: + // SWAP C + BC.B.B0 = (BC.B.B0&0xf0)>>4 | (BC.B.B0&0x0f)<<4; + AF.B.B0 = ZeroTable[BC.B.B0]; + break; + case 0x32: + // SWAP D + DE.B.B1 = (DE.B.B1&0xf0)>>4 | (DE.B.B1&0x0f)<<4; + AF.B.B0 = ZeroTable[DE.B.B1]; + break; + case 0x33: + // SWAP E + DE.B.B0 = (DE.B.B0&0xf0)>>4 | (DE.B.B0&0x0f)<<4; + AF.B.B0 = ZeroTable[DE.B.B0]; + break; + case 0x34: + // SWAP H + HL.B.B1 = (HL.B.B1&0xf0)>>4 | (HL.B.B1&0x0f)<<4; + AF.B.B0 = ZeroTable[HL.B.B1]; + break; + case 0x35: + // SWAP L + HL.B.B0 = (HL.B.B0&0xf0)>>4 | (HL.B.B0&0x0f)<<4; + AF.B.B0 = ZeroTable[HL.B.B0]; + break; + case 0x36: + // SWAP (HL) + tempValue=gbReadMemory(HL.W); + tempValue = (tempValue&0xf0)>>4 | (tempValue&0x0f)<<4; + AF.B.B0 = ZeroTable[tempValue]; + gbWriteMemory(HL.W,tempValue); + break; + case 0x37: + // SWAP A + AF.B.B1 = (AF.B.B1&0xf0)>>4 | (AF.B.B1&0x0f)<<4; + AF.B.B0 = ZeroTable[AF.B.B1]; + break; + case 0x38: + // SRL B + AF.B.B0=(BC.B.B1&0x01)?C_FLAG:0; + BC.B.B1>>=1; + AF.B.B0|=ZeroTable[BC.B.B1]; + break; + case 0x39: + // SRL C + AF.B.B0=(BC.B.B0&0x01)?C_FLAG:0; + BC.B.B0>>=1; + AF.B.B0|=ZeroTable[BC.B.B0]; + break; + case 0x3a: + // SRL D + AF.B.B0=(DE.B.B1&0x01)?C_FLAG:0; + DE.B.B1>>=1; + AF.B.B0|=ZeroTable[DE.B.B1]; + break; + case 0x3b: + // SRL E + AF.B.B0=(DE.B.B0&0x01)?C_FLAG:0; + DE.B.B0>>=1; + AF.B.B0|=ZeroTable[DE.B.B0]; + break; + case 0x3c: + // SRL H + AF.B.B0=(HL.B.B1&0x01)?C_FLAG:0; + HL.B.B1>>=1; + AF.B.B0|=ZeroTable[HL.B.B1]; + break; + case 0x3d: + // SRL L + AF.B.B0=(HL.B.B0&0x01)?C_FLAG:0; + HL.B.B0>>=1; + AF.B.B0|=ZeroTable[HL.B.B0]; + break; + case 0x3e: + // SRL (HL) + tempValue=gbReadMemory(HL.W); + AF.B.B0=(tempValue&0x01)?C_FLAG:0; + tempValue>>=1; + AF.B.B0|=ZeroTable[tempValue]; + gbWriteMemory(HL.W,tempValue); + break; + case 0x3f: + // SRL A + AF.B.B0=(AF.B.B1&0x01)?C_FLAG:0; + AF.B.B1>>=1; + AF.B.B0|=ZeroTable[AF.B.B1]; + break; + case 0x40: + // BIT 0,B + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B1&(1<<0)? 0:Z_FLAG); + break; + case 0x41: + // BIT 0,C + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B0&(1<<0)? 0:Z_FLAG); + break; + case 0x42: + // BIT 0,D + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B1&(1<<0)? 0:Z_FLAG); + break; + case 0x43: + // BIT 0,E + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B0&(1<<0)? 0:Z_FLAG); + break; + case 0x44: + // BIT 0,H + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B1&(1<<0)? 0:Z_FLAG); + break; + case 0x45: + // BIT 0,L + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B0&(1<<0)? 0:Z_FLAG); + break; + case 0x46: + // BIT 0,(HL) + tempValue=gbReadMemory(HL.W); + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(tempValue&(1<<0)? 0:Z_FLAG); + break; + case 0x47: + // BIT 0,A + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(AF.B.B1&(1<<0)? 0:Z_FLAG); + break; + case 0x48: + // BIT 1,B + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B1&(1<<1)? 0:Z_FLAG); + break; + case 0x49: + // BIT 1,C + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B0&(1<<1)? 0:Z_FLAG); + break; + case 0x4a: + // BIT 1,D + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B1&(1<<1)? 0:Z_FLAG); + break; + case 0x4b: + // BIT 1,E + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B0&(1<<1)? 0:Z_FLAG); + break; + case 0x4c: + // BIT 1,H + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B1&(1<<1)? 0:Z_FLAG); + break; + case 0x4d: + // BIT 1,L + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B0&(1<<1)? 0:Z_FLAG); + break; + case 0x4e: + // BIT 1,(HL) + tempValue=gbReadMemory(HL.W); + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(tempValue&(1<<1)? 0:Z_FLAG); + break; + case 0x4f: + // BIT 1,A + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(AF.B.B1&(1<<1)? 0:Z_FLAG); + break; + case 0x50: + // BIT 2,B + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B1&(1<<2)? 0:Z_FLAG); + break; + case 0x51: + // BIT 2,C + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B0&(1<<2)? 0:Z_FLAG); + break; + case 0x52: + // BIT 2,D + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B1&(1<<2)? 0:Z_FLAG); + break; + case 0x53: + // BIT 2,E + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B0&(1<<2)? 0:Z_FLAG); + break; + case 0x54: + // BIT 2,H + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B1&(1<<2)? 0:Z_FLAG); + break; + case 0x55: + // BIT 2,L + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B0&(1<<2)? 0:Z_FLAG); + break; + case 0x56: + // BIT 2,(HL) + tempValue=gbReadMemory(HL.W); + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(tempValue&(1<<2)? 0:Z_FLAG); + break; + case 0x57: + // BIT 2,A + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(AF.B.B1&(1<<2)? 0:Z_FLAG); + break; + case 0x58: + // BIT 3,B + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B1&(1<<3)? 0:Z_FLAG); + break; + case 0x59: + // BIT 3,C + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B0&(1<<3)? 0:Z_FLAG); + break; + case 0x5a: + // BIT 3,D + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B1&(1<<3)? 0:Z_FLAG); + break; + case 0x5b: + // BIT 3,E + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B0&(1<<3)? 0:Z_FLAG); + break; + case 0x5c: + // BIT 3,H + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B1&(1<<3)? 0:Z_FLAG); + break; + case 0x5d: + // BIT 3,L + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B0&(1<<3)? 0:Z_FLAG); + break; + case 0x5e: + // BIT 3,(HL) + tempValue=gbReadMemory(HL.W); + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(tempValue&(1<<3)? 0:Z_FLAG); + break; + case 0x5f: + // BIT 3,A + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(AF.B.B1&(1<<3)? 0:Z_FLAG); + break; + case 0x60: + // BIT 4,B + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B1&(1<<4)? 0:Z_FLAG); + break; + case 0x61: + // BIT 4,C + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B0&(1<<4)? 0:Z_FLAG); + break; + case 0x62: + // BIT 4,D + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B1&(1<<4)? 0:Z_FLAG); + break; + case 0x63: + // BIT 4,E + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B0&(1<<4)? 0:Z_FLAG); + break; + case 0x64: + // BIT 4,H + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B1&(1<<4)? 0:Z_FLAG); + break; + case 0x65: + // BIT 4,L + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B0&(1<<4)? 0:Z_FLAG); + break; + case 0x66: + // BIT 4,(HL) + tempValue=gbReadMemory(HL.W); + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(tempValue&(1<<4)? 0:Z_FLAG); + break; + case 0x67: + // BIT 4,A + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(AF.B.B1&(1<<4)? 0:Z_FLAG); + break; + case 0x68: + // BIT 5,B + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B1&(1<<5)? 0:Z_FLAG); + break; + case 0x69: + // BIT 5,C + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B0&(1<<5)? 0:Z_FLAG); + break; + case 0x6a: + // BIT 5,D + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B1&(1<<5)? 0:Z_FLAG); + break; + case 0x6b: + // BIT 5,E + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B0&(1<<5)? 0:Z_FLAG); + break; + case 0x6c: + // BIT 5,H + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B1&(1<<5)? 0:Z_FLAG); + break; + case 0x6d: + // BIT 5,L + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B0&(1<<5)? 0:Z_FLAG); + break; + case 0x6e: + // BIT 5,(HL) + tempValue=gbReadMemory(HL.W); + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(tempValue&(1<<5)? 0:Z_FLAG); + break; + case 0x6f: + // BIT 5,A + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(AF.B.B1&(1<<5)? 0:Z_FLAG); + break; + case 0x70: + // BIT 6,B + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B1&(1<<6)? 0:Z_FLAG); + break; + case 0x71: + // BIT 6,C + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B0&(1<<6)? 0:Z_FLAG); + break; + case 0x72: + // BIT 6,D + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B1&(1<<6)? 0:Z_FLAG); + break; + case 0x73: + // BIT 6,E + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B0&(1<<6)? 0:Z_FLAG); + break; + case 0x74: + // BIT 6,H + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B1&(1<<6)? 0:Z_FLAG); + break; + case 0x75: + // BIT 6,L + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B0&(1<<6)? 0:Z_FLAG); + break; + case 0x76: + // BIT 6,(HL) + tempValue=gbReadMemory(HL.W); + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(tempValue&(1<<6)? 0:Z_FLAG); + break; + case 0x77: + // BIT 6,A + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(AF.B.B1&(1<<6)? 0:Z_FLAG); + break; + case 0x78: + // BIT 7,B + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B1&(1<<7)? 0:Z_FLAG); + break; + case 0x79: + // BIT 7,C + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B0&(1<<7)? 0:Z_FLAG); + break; + case 0x7a: + // BIT 7,D + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B1&(1<<7)? 0:Z_FLAG); + break; + case 0x7b: + // BIT 7,E + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B0&(1<<7)? 0:Z_FLAG); + break; + case 0x7c: + // BIT 7,H + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B1&(1<<7)? 0:Z_FLAG); + break; + case 0x7d: + // BIT 7,L + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B0&(1<<7)? 0:Z_FLAG); + break; + case 0x7e: + // BIT 7,(HL) + tempValue=gbReadMemory(HL.W); + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(tempValue&(1<<7)? 0:Z_FLAG); + break; + case 0x7f: + // BIT 7,A + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(AF.B.B1&(1<<7)? 0:Z_FLAG); + break; + case 0x80: + // RES 0,B + BC.B.B1&=~(1<<0); + break; + case 0x81: + // RES 0,C + BC.B.B0&=~(1<<0); + break; + case 0x82: + // RES 0,D + DE.B.B1&=~(1<<0); + break; + case 0x83: + // RES 0,E + DE.B.B0&=~(1<<0); + break; + case 0x84: + // RES 0,H + HL.B.B1&=~(1<<0); + break; + case 0x85: + // RES 0,L + HL.B.B0&=~(1<<0); + break; + case 0x86: + // RES 0,(HL) + tempValue=gbReadMemory(HL.W); + tempValue&=~(1<<0); + gbWriteMemory(HL.W,tempValue); + break; + case 0x87: + // RES 0,A + AF.B.B1&=~(1<<0); + break; + case 0x88: + // RES 1,B + BC.B.B1&=~(1<<1); + break; + case 0x89: + // RES 1,C + BC.B.B0&=~(1<<1); + break; + case 0x8a: + // RES 1,D + DE.B.B1&=~(1<<1); + break; + case 0x8b: + // RES 1,E + DE.B.B0&=~(1<<1); + break; + case 0x8c: + // RES 1,H + HL.B.B1&=~(1<<1); + break; + case 0x8d: + // RES 1,L + HL.B.B0&=~(1<<1); + break; + case 0x8e: + // RES 1,(HL) + tempValue=gbReadMemory(HL.W); + tempValue&=~(1<<1); + gbWriteMemory(HL.W,tempValue); + break; + case 0x8f: + // RES 1,A + AF.B.B1&=~(1<<1); + break; + case 0x90: + // RES 2,B + BC.B.B1&=~(1<<2); + break; + case 0x91: + // RES 2,C + BC.B.B0&=~(1<<2); + break; + case 0x92: + // RES 2,D + DE.B.B1&=~(1<<2); + break; + case 0x93: + // RES 2,E + DE.B.B0&=~(1<<2); + break; + case 0x94: + // RES 2,H + HL.B.B1&=~(1<<2); + break; + case 0x95: + // RES 2,L + HL.B.B0&=~(1<<2); + break; + case 0x96: + // RES 2,(HL) + tempValue=gbReadMemory(HL.W); + tempValue&=~(1<<2); + gbWriteMemory(HL.W,tempValue); + break; + case 0x97: + // RES 2,A + AF.B.B1&=~(1<<2); + break; + case 0x98: + // RES 3,B + BC.B.B1&=~(1<<3); + break; + case 0x99: + // RES 3,C + BC.B.B0&=~(1<<3); + break; + case 0x9a: + // RES 3,D + DE.B.B1&=~(1<<3); + break; + case 0x9b: + // RES 3,E + DE.B.B0&=~(1<<3); + break; + case 0x9c: + // RES 3,H + HL.B.B1&=~(1<<3); + break; + case 0x9d: + // RES 3,L + HL.B.B0&=~(1<<3); + break; + case 0x9e: + // RES 3,(HL) + tempValue=gbReadMemory(HL.W); + tempValue&=~(1<<3); + gbWriteMemory(HL.W,tempValue); + break; + case 0x9f: + // RES 3,A + AF.B.B1&=~(1<<3); + break; + case 0xa0: + // RES 4,B + BC.B.B1&=~(1<<4); + break; + case 0xa1: + // RES 4,C + BC.B.B0&=~(1<<4); + break; + case 0xa2: + // RES 4,D + DE.B.B1&=~(1<<4); + break; + case 0xa3: + // RES 4,E + DE.B.B0&=~(1<<4); + break; + case 0xa4: + // RES 4,H + HL.B.B1&=~(1<<4); + break; + case 0xa5: + // RES 4,L + HL.B.B0&=~(1<<4); + break; + case 0xa6: + // RES 4,(HL) + tempValue=gbReadMemory(HL.W); + tempValue&=~(1<<4); + gbWriteMemory(HL.W,tempValue); + break; + case 0xa7: + // RES 4,A + AF.B.B1&=~(1<<4); + break; + case 0xa8: + // RES 5,B + BC.B.B1&=~(1<<5); + break; + case 0xa9: + // RES 5,C + BC.B.B0&=~(1<<5); + break; + case 0xaa: + // RES 5,D + DE.B.B1&=~(1<<5); + break; + case 0xab: + // RES 5,E + DE.B.B0&=~(1<<5); + break; + case 0xac: + // RES 5,H + HL.B.B1&=~(1<<5); + break; + case 0xad: + // RES 5,L + HL.B.B0&=~(1<<5); + break; + case 0xae: + // RES 5,(HL) + tempValue=gbReadMemory(HL.W); + tempValue&=~(1<<5); + gbWriteMemory(HL.W,tempValue); + break; + case 0xaf: + // RES 5,A + AF.B.B1&=~(1<<5); + break; + case 0xb0: + // RES 6,B + BC.B.B1&=~(1<<6); + break; + case 0xb1: + // RES 6,C + BC.B.B0&=~(1<<6); + break; + case 0xb2: + // RES 6,D + DE.B.B1&=~(1<<6); + break; + case 0xb3: + // RES 6,E + DE.B.B0&=~(1<<6); + break; + case 0xb4: + // RES 6,H + HL.B.B1&=~(1<<6); + break; + case 0xb5: + // RES 6,L + HL.B.B0&=~(1<<6); + break; + case 0xb6: + // RES 6,(HL) + tempValue=gbReadMemory(HL.W); + tempValue&=~(1<<6); + gbWriteMemory(HL.W,tempValue); + break; + case 0xb7: + // RES 6,A + AF.B.B1&=~(1<<6); + break; + case 0xb8: + // RES 7,B + BC.B.B1&=~(1<<7); + break; + case 0xb9: + // RES 7,C + BC.B.B0&=~(1<<7); + break; + case 0xba: + // RES 7,D + DE.B.B1&=~(1<<7); + break; + case 0xbb: + // RES 7,E + DE.B.B0&=~(1<<7); + break; + case 0xbc: + // RES 7,H + HL.B.B1&=~(1<<7); + break; + case 0xbd: + // RES 7,L + HL.B.B0&=~(1<<7); + break; + case 0xbe: + // RES 7,(HL) + tempValue=gbReadMemory(HL.W); + tempValue&=~(1<<7); + gbWriteMemory(HL.W,tempValue); + break; + case 0xbf: + // RES 7,A + AF.B.B1&=~(1<<7); + break; + case 0xc0: + // SET 0,B + BC.B.B1|=1<<0; + break; + case 0xc1: + // SET 0,C + BC.B.B0|=1<<0; + break; + case 0xc2: + // SET 0,D + DE.B.B1|=1<<0; + break; + case 0xc3: + // SET 0,E + DE.B.B0|=1<<0; + break; + case 0xc4: + // SET 0,H + HL.B.B1|=1<<0; + break; + case 0xc5: + // SET 0,L + HL.B.B0|=1<<0; + break; + case 0xc6: + // SET 0,(HL) + tempValue=gbReadMemory(HL.W); + tempValue|=1<<0; + gbWriteMemory(HL.W,tempValue); + break; + case 0xc7: + // SET 0,A + AF.B.B1|=1<<0; + break; + case 0xc8: + // SET 1,B + BC.B.B1|=1<<1; + break; + case 0xc9: + // SET 1,C + BC.B.B0|=1<<1; + break; + case 0xca: + // SET 1,D + DE.B.B1|=1<<1; + break; + case 0xcb: + // SET 1,E + DE.B.B0|=1<<1; + break; + case 0xcc: + // SET 1,H + HL.B.B1|=1<<1; + break; + case 0xcd: + // SET 1,L + HL.B.B0|=1<<1; + break; + case 0xce: + // SET 1,(HL) + tempValue=gbReadMemory(HL.W); + tempValue|=1<<1; + gbWriteMemory(HL.W,tempValue); + break; + case 0xcf: + // SET 1,A + AF.B.B1|=1<<1; + break; + case 0xd0: + // SET 2,B + BC.B.B1|=1<<2; + break; + case 0xd1: + // SET 2,C + BC.B.B0|=1<<2; + break; + case 0xd2: + // SET 2,D + DE.B.B1|=1<<2; + break; + case 0xd3: + // SET 2,E + DE.B.B0|=1<<2; + break; + case 0xd4: + // SET 2,H + HL.B.B1|=1<<2; + break; + case 0xd5: + // SET 2,L + HL.B.B0|=1<<2; + break; + case 0xd6: + // SET 2,(HL) + tempValue=gbReadMemory(HL.W); + tempValue|=1<<2; + gbWriteMemory(HL.W,tempValue); + break; + case 0xd7: + // SET 2,A + AF.B.B1|=1<<2; + break; + case 0xd8: + // SET 3,B + BC.B.B1|=1<<3; + break; + case 0xd9: + // SET 3,C + BC.B.B0|=1<<3; + break; + case 0xda: + // SET 3,D + DE.B.B1|=1<<3; + break; + case 0xdb: + // SET 3,E + DE.B.B0|=1<<3; + break; + case 0xdc: + // SET 3,H + HL.B.B1|=1<<3; + break; + case 0xdd: + // SET 3,L + HL.B.B0|=1<<3; + break; + case 0xde: + // SET 3,(HL) + tempValue=gbReadMemory(HL.W); + tempValue|=1<<3; + gbWriteMemory(HL.W,tempValue); + break; + case 0xdf: + // SET 3,A + AF.B.B1|=1<<3; + break; + case 0xe0: + // SET 4,B + BC.B.B1|=1<<4; + break; + case 0xe1: + // SET 4,C + BC.B.B0|=1<<4; + break; + case 0xe2: + // SET 4,D + DE.B.B1|=1<<4; + break; + case 0xe3: + // SET 4,E + DE.B.B0|=1<<4; + break; + case 0xe4: + // SET 4,H + HL.B.B1|=1<<4; + break; + case 0xe5: + // SET 4,L + HL.B.B0|=1<<4; + break; + case 0xe6: + // SET 4,(HL) + tempValue=gbReadMemory(HL.W); + tempValue|=1<<4; + gbWriteMemory(HL.W,tempValue); + break; + case 0xe7: + // SET 4,A + AF.B.B1|=1<<4; + break; + case 0xe8: + // SET 5,B + BC.B.B1|=1<<5; + break; + case 0xe9: + // SET 5,C + BC.B.B0|=1<<5; + break; + case 0xea: + // SET 5,D + DE.B.B1|=1<<5; + break; + case 0xeb: + // SET 5,E + DE.B.B0|=1<<5; + break; + case 0xec: + // SET 5,H + HL.B.B1|=1<<5; + break; + case 0xed: + // SET 5,L + HL.B.B0|=1<<5; + break; + case 0xee: + // SET 5,(HL) + tempValue=gbReadMemory(HL.W); + tempValue|=1<<5; + gbWriteMemory(HL.W,tempValue); + break; + case 0xef: + // SET 5,A + AF.B.B1|=1<<5; + break; + case 0xf0: + // SET 6,B + BC.B.B1|=1<<6; + break; + case 0xf1: + // SET 6,C + BC.B.B0|=1<<6; + break; + case 0xf2: + // SET 6,D + DE.B.B1|=1<<6; + break; + case 0xf3: + // SET 6,E + DE.B.B0|=1<<6; + break; + case 0xf4: + // SET 6,H + HL.B.B1|=1<<6; + break; + case 0xf5: + // SET 6,L + HL.B.B0|=1<<6; + break; + case 0xf6: + // SET 6,(HL) + tempValue=gbReadMemory(HL.W); + tempValue|=1<<6; + gbWriteMemory(HL.W,tempValue); + break; + case 0xf7: + // SET 6,A + AF.B.B1|=1<<6; + break; + case 0xf8: + // SET 7,B + BC.B.B1|=1<<7; + break; + case 0xf9: + // SET 7,C + BC.B.B0|=1<<7; + break; + case 0xfa: + // SET 7,D + DE.B.B1|=1<<7; + break; + case 0xfb: + // SET 7,E + DE.B.B0|=1<<7; + break; + case 0xfc: + // SET 7,H + HL.B.B1|=1<<7; + break; + case 0xfd: + // SET 7,L + HL.B.B0|=1<<7; + break; + case 0xfe: + // SET 7,(HL) + tempValue=gbReadMemory(HL.W); + tempValue|=1<<7; + gbWriteMemory(HL.W,tempValue); + break; + case 0xff: + // SET 7,A + AF.B.B1|=1<<7; + break; + default: + if (gbSystemMessage == false) + { + systemMessage(0, N_("Unknown opcode %02x at %04x"), + gbReadOpcode(PC.W-1),PC.W-1); + gbSystemMessage =true; + } + return; diff --git a/src/gb/gbDis.cpp b/src/gb/gbDis.cpp new file mode 100644 index 0000000..15d7d7f --- /dev/null +++ b/src/gb/gbDis.cpp @@ -0,0 +1,231 @@ +#include +#include + +#include "../System.h" +#include "gbGlobals.h" + +typedef struct { + u8 mask; + u8 value; + const char *mnen; +} GBOPCODE; + +#define GB_READ(x) gbMemoryMap[(x)>>12][(x)&0xfff] + +static const char *registers[] = + { "B", "C", "D", "E", "H", "L", "(HL)", "A" }; + +static const char *registers16[] = + { "BC", "DE", "HL", "SP", // for some operations + "BC", "DE", "HL", "AF" }; // for push/pop + +static const char *cond[] = + { "NZ", "Z", "NC", "C" }; + +static char hexDigits[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' +}; + +static GBOPCODE opcodes[] = { + { 0xff, 0x00, "NOP" }, + { 0xcf, 0x01, "LD %R4,%W" }, + { 0xff, 0x02, "LD (BC),A" }, + { 0xcf, 0x03, "INC %R4" }, + { 0xc7, 0x04, "INC %r3" }, + { 0xc7, 0x05, "DEC %r3" }, + { 0xc7, 0x06, "LD %r3,%B" }, + { 0xff, 0x07, "RLCA" }, + { 0xff, 0x08, "LD (%W),SP" }, + { 0xcf, 0x09, "ADD HL,%R4" }, + { 0xff, 0x0a, "LD A,(BC)" }, + { 0xcf, 0x0b, "DEC %R4" }, + { 0xff, 0x0f, "RRCA" }, + { 0xff, 0x10, "STOP" }, + { 0xff, 0x12, "LD (DE),A" }, + { 0xff, 0x17, "RLA" }, + { 0xff, 0x18, "JR %d" }, + { 0xff, 0x1a, "LD A,(DE)" }, + { 0xff, 0x1f, "RRA" }, + { 0xe7, 0x20, "JR %c3,%d" }, + { 0xff, 0x22, "LDI (HL),A" }, + { 0xff, 0x27, "DAA" }, + { 0xff, 0x2a, "LDI A,(HL)" }, + { 0xff, 0x2f, "CPL" }, + { 0xff, 0x32, "LDD (HL),A" }, + { 0xff, 0x37, "SCF" }, + { 0xff, 0x3a, "LDD A,(HL)" }, + { 0xff, 0x3f, "CCF" }, + { 0xff, 0x76, "HALT" }, + { 0xc0, 0x40, "LD %r3,%r0" }, + { 0xf8, 0x80, "ADD A,%r0" }, + { 0xf8, 0x88, "ADC A,%r0" }, + { 0xf8, 0x90, "SUB %r0" }, + { 0xf8, 0x98, "SBC A,%r0" }, + { 0xf8, 0xa0, "AND %r0" }, + { 0xf8, 0xa8, "XOR %r0" }, + { 0xf8, 0xb0, "OR %r0" }, + { 0xf8, 0xb8, "CP %r0" }, + { 0xe7, 0xc0, "RET %c3" }, + { 0xcf, 0xc1, "POP %t4" }, + { 0xe7, 0xc2, "JP %c3,%W" }, + { 0xff, 0xc3, "JP %W" }, + { 0xe7, 0xc4, "CALL %c3,%W" }, + { 0xcf, 0xc5, "PUSH %t4" }, + { 0xff, 0xc6, "ADD A,%B" }, + { 0xc7, 0xc7, "RST %P" }, + { 0xff, 0xc9, "RET" }, + { 0xff, 0xcd, "CALL %W" }, + { 0xff, 0xce, "ADC %B" }, + { 0xff, 0xd6, "SUB %B" }, + { 0xff, 0xd9, "RETI" }, + { 0xff, 0xde, "SBC %B" }, + { 0xff, 0xe0, "LD (FF%B),A" }, + { 0xff, 0xe2, "LD (FF00h+C),A" }, + { 0xff, 0xe6, "AND %B" }, + { 0xff, 0xe8, "ADD SP,%D" }, + { 0xff, 0xe9, "LD PC,HL" }, + { 0xff, 0xea, "LD (%W),A" }, + { 0xff, 0xee, "XOR %B" }, + { 0xff, 0xf0, "LD A,(FF%B)" }, + { 0xff, 0xf2, "LD A,(FF00h+C)" }, + { 0xff, 0xf3, "DI" }, + { 0xff, 0xf6, "OR %B" }, + { 0xff, 0xf8, "LD HL,SP%D" }, + { 0xff, 0xf9, "LD SP,HL" }, + { 0xff, 0xfa, "LD A,(%W)" }, + { 0xff, 0xfb, "EI" }, + { 0xff, 0xfe, "CP %B" }, + { 0x00, 0x00, "DB %B" } +}; + +static GBOPCODE cbOpcodes[] = { + { 0xf8, 0x00, "RLC %r0" }, + { 0xf8, 0x08, "RRC %r0" }, + { 0xf8, 0x10, "RL %r0" }, + { 0xf8, 0x18, "RR %r0" }, + { 0xf8, 0x20, "SLA %r0" }, + { 0xf8, 0x28, "SRA %r0" }, + { 0xf8, 0x30, "SWAP %r0" }, + { 0xf8, 0x38, "SRL %r0" }, + { 0xc0, 0x40, "BIT %b,%r0" }, + { 0xc0, 0x80, "RES %b,%r0" }, + { 0xc0, 0xc0, "SET %b,%r0" }, + { 0x00, 0x00, "DB CBh,%B" } +}; + +static char *addHex(char *p, u8 value) +{ + *p++ = hexDigits[value >> 4]; + *p++ = hexDigits[value & 15]; + return p; +} + +static char *addHex16(char *p, u16 value) +{ + p = addHex(p, value>>8); + return addHex(p, value & 255); +} + +static char *addStr(char *p, const char *s) +{ + while(*s) { + *p++ = *s++; + } + return p; +} + +int gbDis(char *buffer, u16 address) +{ + char *p = buffer; + int instr = 1; + u16 addr = address; + sprintf(p, "%04x ", address); + p += 12; + + u8 opcode = GB_READ(address); + address++; + const char *mnen; + GBOPCODE *op; + if(opcode == 0xcb) { + opcode = GB_READ(address); + address++; + instr++; + op = cbOpcodes; + } else { + op = opcodes; + } + while(op->value != (opcode & op->mask)) op++; + mnen = op->mnen; + + u8 b0, b1; + s8 disp; + int shift; + + while(*mnen) { + if(*mnen == '%') { + mnen++; + switch(*mnen++) { + case 'W': + b0 = GB_READ(address); + address++; + b1 = GB_READ(address); + address++; + p = addHex16(p, b0|b1<<8); + instr += 2; + *p++ = 'h'; + break; + case 'B': + p = addHex(p, GB_READ(address)); + *p++ = 'h'; + address++; + instr++; + break; + case 'D': + disp = GB_READ(address); + if(disp >= 0) + *p++ = '+'; + p += sprintf(p, "%d", disp); + instr++; + break; + case 'd': + disp = GB_READ(address); + address++; + p = addHex16(p, address+disp); + *p++ = 'h'; + instr++; + break; + case 'b': + // kind of a hack, but it works :-) + *p++ = hexDigits[(opcode >> 3) & 7]; + break; + case 'r': + shift = *mnen++ - '0'; + p = addStr(p, registers[(opcode >> shift) & 7]); + break; + case 'R': + shift = *mnen++ - '0'; + p = addStr(p, registers16[(opcode >> shift) & 3]); + break; + case 't': + shift = *mnen++ - '0'; + p = addStr(p, registers16[4+((opcode >> shift) & 3)]); + break; + case 'P': + p = addHex(p, ((opcode >> 3) & 7) * 8); + break; + case 'c': + shift = *mnen++ - '0'; + p = addStr(p, cond[(opcode >> shift) & 3]); + break; + } + } else + *p++ = *mnen++; + } + for(int i = 0; i < instr; i++) { + u16 a = addr + i; + addHex(buffer+5+i*2, GB_READ(a)); + } + *p = 0; + return instr; +} diff --git a/src/gb/gbGfx.cpp b/src/gb/gbGfx.cpp new file mode 100644 index 0000000..67cef3f --- /dev/null +++ b/src/gb/gbGfx.cpp @@ -0,0 +1,575 @@ +#include + +#include "../common/Types.h" +#include "../Util.h" +#include "gbGlobals.h" +#include "gbSGB.h" + +u8 gbInvertTab[256] = { + 0x00,0x80,0x40,0xc0,0x20,0xa0,0x60,0xe0, + 0x10,0x90,0x50,0xd0,0x30,0xb0,0x70,0xf0, + 0x08,0x88,0x48,0xc8,0x28,0xa8,0x68,0xe8, + 0x18,0x98,0x58,0xd8,0x38,0xb8,0x78,0xf8, + 0x04,0x84,0x44,0xc4,0x24,0xa4,0x64,0xe4, + 0x14,0x94,0x54,0xd4,0x34,0xb4,0x74,0xf4, + 0x0c,0x8c,0x4c,0xcc,0x2c,0xac,0x6c,0xec, + 0x1c,0x9c,0x5c,0xdc,0x3c,0xbc,0x7c,0xfc, + 0x02,0x82,0x42,0xc2,0x22,0xa2,0x62,0xe2, + 0x12,0x92,0x52,0xd2,0x32,0xb2,0x72,0xf2, + 0x0a,0x8a,0x4a,0xca,0x2a,0xaa,0x6a,0xea, + 0x1a,0x9a,0x5a,0xda,0x3a,0xba,0x7a,0xfa, + 0x06,0x86,0x46,0xc6,0x26,0xa6,0x66,0xe6, + 0x16,0x96,0x56,0xd6,0x36,0xb6,0x76,0xf6, + 0x0e,0x8e,0x4e,0xce,0x2e,0xae,0x6e,0xee, + 0x1e,0x9e,0x5e,0xde,0x3e,0xbe,0x7e,0xfe, + 0x01,0x81,0x41,0xc1,0x21,0xa1,0x61,0xe1, + 0x11,0x91,0x51,0xd1,0x31,0xb1,0x71,0xf1, + 0x09,0x89,0x49,0xc9,0x29,0xa9,0x69,0xe9, + 0x19,0x99,0x59,0xd9,0x39,0xb9,0x79,0xf9, + 0x05,0x85,0x45,0xc5,0x25,0xa5,0x65,0xe5, + 0x15,0x95,0x55,0xd5,0x35,0xb5,0x75,0xf5, + 0x0d,0x8d,0x4d,0xcd,0x2d,0xad,0x6d,0xed, + 0x1d,0x9d,0x5d,0xdd,0x3d,0xbd,0x7d,0xfd, + 0x03,0x83,0x43,0xc3,0x23,0xa3,0x63,0xe3, + 0x13,0x93,0x53,0xd3,0x33,0xb3,0x73,0xf3, + 0x0b,0x8b,0x4b,0xcb,0x2b,0xab,0x6b,0xeb, + 0x1b,0x9b,0x5b,0xdb,0x3b,0xbb,0x7b,0xfb, + 0x07,0x87,0x47,0xc7,0x27,0xa7,0x67,0xe7, + 0x17,0x97,0x57,0xd7,0x37,0xb7,0x77,0xf7, + 0x0f,0x8f,0x4f,0xcf,0x2f,0xaf,0x6f,0xef, + 0x1f,0x9f,0x5f,0xdf,0x3f,0xbf,0x7f,0xff +}; + +u16 gbLineMix[160]; +u16 gbWindowColor[160]; +extern int inUseRegister_WY; +extern int layerSettings; + +void gbRenderLine() +{ + memset(gbLineMix, 0, sizeof(gbLineMix)); + u8 * bank0; + u8 * bank1; + if(gbCgbMode) { + bank0 = &gbVram[0x0000]; + bank1 = &gbVram[0x2000]; + } else { + bank0 = &gbMemory[0x8000]; + bank1 = NULL; + } + + int tile_map = 0x1800; + if((register_LCDC & 8) != 0) + tile_map = 0x1c00; + + int tile_pattern = 0x0800; + + if((register_LCDC & 16) != 0) + tile_pattern = 0x0000; + + int x = 0; + int y = register_LY; + + if(y >= 144) + return; + + int SpritesTicks = gbSpritesTicks[x]*(gbSpeed ? 2 : 4); + int sx = gbSCXLine[(gbSpeed ? 0 : 4)+SpritesTicks]; + int sy = gbSCYLine[(gbSpeed ? 11 : 5)+SpritesTicks]; + + sy+=y; + + sy &= 255; + + int tx = sx >> 3; + int ty = sy >> 3; + + int bx = 1 << (7 - (sx & 7)); + int by = sy & 7; + + int tile_map_line_y = tile_map + ty * 32; + + int tile_map_address = tile_map_line_y + tx; + + u8 attrs = 0; + if(bank1 != NULL) + attrs = bank1[tile_map_address]; + + u8 tile = bank0[tile_map_address]; + + tile_map_address++; + + if(!(register_LCDC & 0x10)) + tile ^= 0x80; + + int tile_pattern_address = tile_pattern + tile * 16 + by*2; + + if(register_LCDC & 0x80) { + if((register_LCDC & 0x01 || gbCgbMode) && (layerSettings & 0x0100)) { + while(x < 160) { + + + + u8 tile_a = 0; + u8 tile_b = 0; + + if(attrs & 0x40) { + tile_pattern_address = tile_pattern + tile * 16 + (7-by)*2; + } + + if(attrs & 0x08) { + tile_a = bank1[tile_pattern_address++]; + tile_b = bank1[tile_pattern_address]; + } else { + tile_a = bank0[tile_pattern_address++]; + tile_b = bank0[tile_pattern_address]; + } + + if(attrs & 0x20) { + tile_a = gbInvertTab[tile_a]; + tile_b = gbInvertTab[tile_b]; + } + + while(bx > 0) { + u8 c = (tile_a & bx) ? 1 : 0; + c += ((tile_b & bx) ? 2 : 0); + + gbLineBuffer[x] = c; // mark the gbLineBuffer color + + if(attrs & 0x80) + gbLineBuffer[x] |= 0x300; + + if(gbCgbMode) { + c = c + (attrs & 7)*4; + } else { + c = (gbBgpLine[x+(gbSpeed ? 5 : 11)+SpritesTicks]>>(c<<1)) &3; + if(gbSgbMode && !gbCgbMode) { + int dx = x >> 3; + int dy = y >> 3; + + int palette = gbSgbATF[dy * 20 + dx]; + + if(c == 0) + palette = 0; + + c = c + 4*palette; + } + } + gbLineMix[x] = gbColorOption ? gbColorFilter[gbPalette[c] & 0x7FFF] : gbPalette[c] & 0x7FFF; + x++; + if(x >= 160) + break; + bx >>= 1; + } + + bx = 128; + + SpritesTicks = gbSpritesTicks[x]*(gbSpeed ? 2 : 4); + + sx = gbSCXLine[x+(gbSpeed ? 0 : 4)+SpritesTicks]; + + sy = gbSCYLine[x+(gbSpeed ? 11 : 5)+SpritesTicks]; + + + tx = ((sx+x)>>3) & 0x1f; + + sy+=y; + + sy &= 255; + + ty = sy >> 3; + + by = sy & 7; + + tile_pattern_address = tile_pattern + tile * 16 + by * 2; + + tile_map_line_y = tile_map + ty * 32; + + tile_map_address = tile_map_line_y + tx; + + if(bank1) + attrs = bank1[tile_map_line_y + tx]; + + tile = bank0[tile_map_line_y + tx]; + + if(!(register_LCDC & 0x10)) + tile ^= 0x80; + + tile_pattern_address = tile_pattern + tile * 16 + by * 2; + } + } else { + // Use gbBgp[0] instead of 0 (?) + // (this fixes white flashes on Last Bible II) + // Also added the gbColorOption (fixes Dracula Densetsu II color problems) + for(int i = 0; i < 160; i++) + { + u16 color = gbColorOption ? gbColorFilter[0x7FFF] : 0x7FFF; + if (!gbCgbMode) + color = gbColorOption ? + gbColorFilter[gbPalette[gbBgpLine[i+(gbSpeed ? 5 : 11)+gbSpritesTicks[i]*(gbSpeed ? 2 : 4)]&3] & 0x7FFF] : + gbPalette[gbBgpLine[i+(gbSpeed ? 5 : 11)+gbSpritesTicks[i]*(gbSpeed ? 2 : 4)]&3] & 0x7FFF; + gbLineMix[i] = color; + gbLineBuffer[i] = 0; + } + } + + // do the window display + // LCDC.0 also enables/disables the window in !gbCgbMode ?!?! + // (tested on real hardware) + // This fixes Last Bible II & Zankurou Musouken + if((register_LCDC & 0x01 || gbCgbMode) && (register_LCDC & 0x20) && (layerSettings & 0x2000) && (gbWindowLine != -2)) { + int i = 0; + // Fix (accurate emulation) for most of the window display problems + // (ie. Zen - Intergalactic Ninja, Urusei Yatsura...). + if ((gbWindowLine == -1) || (gbWindowLine>144)) + { + inUseRegister_WY = oldRegister_WY; + if (register_LY>oldRegister_WY) + gbWindowLine = 146; + // for (i = 0; i<160; i++) + // gbWindowColor[i] = gbLineMix[i]; + } + + int wy = inUseRegister_WY; + + if(y >= inUseRegister_WY) { + + if ((gbWindowLine == -1) || (gbWindowLine>144)) + gbWindowLine = 0; + + int wx = register_WX; + int swx = 0; + wx -= 7; + + if( wx <= 159 && gbWindowLine <= 143) { + + tile_map = 0x1800; + + if((register_LCDC & 0x40) != 0) + tile_map = 0x1c00; + + + tx = 0; + ty = gbWindowLine >> 3; + + bx = 128; + by = gbWindowLine & 7; + + // Tries to emulate the 'window scrolling bug' when wx == 0 (ie. wx-7 == -7). + // Nothing close to perfect, but good enought for now... + if (wx == -7) + { + swx = 7-((gbSCXLine[0]-1) & 7); + bx >>= ((gbSCXLine[0]+((swx != 1) ? 1 : 0)) & 7); + if (swx == 1) + swx = 2; + + //bx >>= ((gbSCXLine[0]+(((swx>1) && (swx != 7)) ? 1 : 0)) & 7); + + if (swx == 7) + { + //wx = 0; + if ((gbWindowLine>0) || (wy == 0)) + swx = 0; + } + } + else + if(wx < 0) { + bx >>= (-wx); + wx = 0; + } + + tile_map_line_y = tile_map + ty * 32; + + tile_map_address = tile_map_line_y + tx; + + x = wx; + + tile = bank0[tile_map_address]; + u8 attrs = 0; + if(bank1) + attrs = bank1[tile_map_address]; + tile_map_address++; + + if((register_LCDC & 16) == 0) { + if(tile < 128) tile += 128; + else tile -= 128; + } + + tile_pattern_address = tile_pattern + tile * 16 + by*2; + + if (wx) + for (i = 0; i 0) { + u8 c = (tile_a & bx) != 0 ? 1 : 0; + c += ((tile_b & bx) != 0 ? 2 : 0); + + if (x>=0) + { + if(attrs & 0x80) + gbLineBuffer[x] = 0x300 + c; + else + gbLineBuffer[x] = 0x100 + c; + + if(gbCgbMode) { + c = c + (attrs & 7) * 4; + } else { + c = (gbBgpLine[x+(gbSpeed ? 5 : 11)+gbSpritesTicks[x]*(gbSpeed ? 2 : 4)]>>(c<<1)) &3; + if(gbSgbMode && !gbCgbMode) { + int dx = x >> 3; + int dy = y >> 3; + + int palette = gbSgbATF[dy * 20 + dx]; + + if(c == 0) + palette = 0; + + c = c + 4*palette; + } + } + gbLineMix[x] = gbColorOption ? gbColorFilter[gbPalette[c] & 0x7FFF] : gbPalette[c] & 0x7FFF; + } + x++; + if(x >= 160) + break; + bx >>= 1; + } + tx++; + if(tx == 32) + tx = 0; + bx = 128; + tile = bank0[tile_map_line_y + tx]; + if(bank1) + attrs = bank1[tile_map_line_y + tx]; + + if((register_LCDC & 16) == 0) { + if(tile < 128) tile += 128; + else tile -= 128; + } + tile_pattern_address = tile_pattern + tile * 16 + by * 2; + } + + //for (i = swx; i<160; i++) + // gbLineMix[i] = gbWindowColor[i]; + gbWindowLine++; + } + } + } + else if (gbWindowLine == -2) + { + inUseRegister_WY = oldRegister_WY; + if (register_LY>oldRegister_WY) + gbWindowLine = 146; + else + gbWindowLine = 0; + } + } else { + u16 color = gbColorOption ? gbColorFilter[0x7FFF] : 0x7FFF; + if (!gbCgbMode) + color = gbColorOption ? gbColorFilter[gbPalette[0] & 0x7FFF] : gbPalette[0] & 0x7FFF; + for(int i = 0; i < 160; i++) + { + gbLineMix[i] = color; + gbLineBuffer[i] = 0; + } + } +} + +void gbDrawSpriteTile(int tile, int x,int y,int t, int flags, + int size,int spriteNumber) +{ + u8 * bank0; + u8 * bank1; + if(gbCgbMode) { + bank0 = &gbVram[0x0000]; + bank1 = &gbVram[0x2000]; + } else { + bank0 = &gbMemory[0x8000]; + bank1 = NULL; + } + + int init = 0x0000; + + for (int i = 0; i<4; i++) + { + gbObp0[i] = (gbObp0Line[x+11+gbSpritesTicks[x]*(gbSpeed ? 2 : 4)]>>(i<<1)) & 3; + gbObp1[i] = (gbObp1Line[x+11+gbSpritesTicks[x]*(gbSpeed ? 2 : 4)]>>(i<<1)) & 3; + } + u8 *pal = gbObp0; + + int flipx = (flags & 0x20); + int flipy = (flags & 0x40); + + if((flags & 0x10)) + pal = gbObp1; + + if(flipy) { + t = (size ? 15 : 7) - t; + } + + int prio = flags & 0x80; + + int address = init + tile * 16 + 2*t; + int a = 0; + int b = 0; + + if(gbCgbMode && (flags & 0x08)) { + a = bank1[address++]; + b = bank1[address++]; + } else { + a = bank0[address++]; + b = bank0[address++]; + } + + for(int xx = 0; xx < 8; xx++) { + u8 mask = 1 << (7-xx); + u8 c = 0; + if( (a & mask)) + c++; + if( (b & mask)) + c+=2; + + if(c==0) continue; + + int xxx = xx+x; + if(flipx) + xxx = (7-xx+x); + + if(xxx < 0 || xxx > 159) + continue; + + u16 color = gbLineBuffer[xxx]; + + // Fixes OAM-BG priority + if(prio && (register_LCDC & 1)) { + if(color < 0x200 && ((color & 0xFF) != 0)) + continue; + } + // Fixes OAM-BG priority for Moorhuhn 2 + if(color >= 0x300 && color != 0x300 && (register_LCDC & 1)) + continue; + else if(color >= 0x200 && color < 0x300) { + int sprite = color & 0xff; + + int spriteX = gbMemory[0xfe00 + 4 * sprite + 1] - 8; + + if(spriteX == x) { + if(sprite < spriteNumber) + continue; + } else { + if(gbCgbMode) { + if(sprite < spriteNumber) + continue; + } else { + // Fixes GB sprites priorities (was '< x + 8' before) + // ('A boy and his blob...' sprites' emulation is now correct) + if(spriteX < x) + continue; + } + } + } + + + gbLineBuffer[xxx] = 0x200 + spriteNumber; + + // make sure that sprites will work even in CGB mode + if(gbCgbMode) { + c = c + (flags & 0x07)*4 + 32; + } else { + c = pal[c]; + + if(gbSgbMode && !gbCgbMode) { + int dx = xxx >> 3; + int dy = y >> 3; + + int palette = gbSgbATF[dy * 20 + dx]; + + if(c == 0) + palette = 0; + + c = c + 4*palette; + } else { + c += 4; + } + } + + gbLineMix[xxx] = gbColorOption ? gbColorFilter[gbPalette[c] & 0x7FFF] : gbPalette[c] & 0x7FFF; + } +} + +void gbDrawSprites(bool draw) +{ + int x = 0; + int y = 0; + int count = 0; + + int size = (register_LCDC & 4); + + if (!draw) + memset (gbSpritesTicks, 0, sizeof(gbSpritesTicks)); + + if(!(register_LCDC & 0x80)) + return; + + if((register_LCDC & 2) && (layerSettings & 0x1000)) { + int yc = register_LY; + + int address = 0xfe00; + for(int i = 0; i < 40; i++) { + y = gbMemory[address++]; + x = gbMemory[address++]; + int tile = gbMemory[address++]; + if(size) + tile &= 254; + int flags = gbMemory[address++]; + + if(x > 0 && y > 0 && x < 168 && y < 160) { + // check if sprite intersects current line + int t = yc -y + 16; + if((size && t >=0 && t < 16) || (!size && t >= 0 && t < 8)) { + if (draw) + gbDrawSpriteTile(tile,x-8,yc,t,flags,size,i); + else + { + for (int j = x-8; j<300; j++) + if (j>=0) + { + if (gbSpeed) + gbSpritesTicks[j] += 5; + else + gbSpritesTicks[j] += 2+(count&1); + } + } + count++; + } + } + // sprite limit reached! + if(count >= 10) + break; + } + } + return; +} diff --git a/src/gb/gbGlobals.cpp b/src/gb/gbGlobals.cpp new file mode 100644 index 0000000..4977853 --- /dev/null +++ b/src/gb/gbGlobals.cpp @@ -0,0 +1,40 @@ +#include +#include "../common/Types.h" + +u8 *gbMemoryMap[16]; + +int gbRomSizeMask = 0; +int gbRomSize = 0; +int gbRamSizeMask = 0; +int gbRamSize = 0; +int gbTAMA5ramSize = 0; + +u8 *gbMemory = NULL; +u8 *gbVram = NULL; +u8 *gbRom = NULL; +u8 *gbRam = NULL; +u8 *gbWram = NULL; +u16 *gbLineBuffer = NULL; +u8 *gbTAMA5ram = NULL; + +u16 gbPalette[128]; +u8 gbBgp[4] = { 0, 1, 2, 3}; +u8 gbObp0[4] = { 0, 1, 2, 3}; +u8 gbObp1[4] = { 0, 1, 2, 3}; +int gbWindowLine = -1; + +bool genericflashcardEnable = false; +int gbCgbMode = 0; + +u16 gbColorFilter[32768]; +int gbColorOption = 0; +int gbPaletteOption = 0; +int gbEmulatorType = 0; +int gbBorderOn = 0; +int gbBorderAutomatic = 0; +int gbBorderLineSkip = 160; +int gbBorderRowSkip = 0; +int gbBorderColumnSkip = 0; +int gbDmaTicks = 0; + +u8 (*gbSerialFunction)(u8) = NULL; diff --git a/src/gb/gbGlobals.h b/src/gb/gbGlobals.h new file mode 100644 index 0000000..d6c19b6 --- /dev/null +++ b/src/gb/gbGlobals.h @@ -0,0 +1,77 @@ +#ifndef GBGLOBALS_H +#define GBGLOBALS_H + +extern int gbRomSizeMask; +extern int gbRomSize; +extern int gbRamSize; +extern int gbRamSizeMask; +extern int gbTAMA5ramSize; + +extern bool useBios; +extern bool skipBios; +extern u8 *bios; +extern bool skipSaveGameBattery; +extern bool skipSaveGameCheats; + +extern u8 *gbRom; +extern u8 *gbRam; +extern u8 *gbVram; +extern u8 *gbWram; +extern u8 *gbMemory; +extern u16 *gbLineBuffer; +extern u8 *gbTAMA5ram; + +extern u8 *gbMemoryMap[16]; + +extern int gbFrameSkip; +extern u16 gbColorFilter[32768]; +extern int gbColorOption; +extern int gbPaletteOption; +extern int gbEmulatorType; +extern int gbBorderOn; +extern int gbBorderAutomatic; +extern int gbCgbMode; +extern int gbSgbMode; +extern int gbWindowLine; +extern int gbSpeed; +extern u8 gbBgp[4]; +extern u8 gbObp0[4]; +extern u8 gbObp1[4]; +extern u16 gbPalette[128]; +extern bool gbScreenOn; +extern bool gbDrawWindow; +extern u8 gbSCYLine[300]; +// gbSCXLine is used for the emulation (bug) of the SX change +// found in the Artic Zone game. +extern u8 gbSCXLine[300]; +// gbBgpLine is used for the emulation of the +// Prehistorik Man's title screen scroller. +extern u8 gbBgpLine[300]; +extern u8 gbObp0Line [300]; +extern u8 gbObp1Line [300]; +// gbSpritesTicks is used for the emulation of Parodius' Laser Beam. +extern u8 gbSpritesTicks[300]; + +extern u8 register_LCDC; +extern u8 register_LY; +extern u8 register_SCY; +extern u8 register_SCX; +extern u8 register_WY; +extern u8 register_WX; +extern u8 register_VBK; +extern u8 oldRegister_WY; + +extern int emulating; +extern bool genericflashcardEnable; + +extern int gbBorderLineSkip; +extern int gbBorderRowSkip; +extern int gbBorderColumnSkip; +extern int gbDmaTicks; + +extern void gbRenderLine(); +extern void gbDrawSprites(bool); + +extern u8 (*gbSerialFunction)(u8); + +#endif // GBGLOBALS_H diff --git a/src/gb/gbMemory.cpp b/src/gb/gbMemory.cpp new file mode 100644 index 0000000..53a7f69 --- /dev/null +++ b/src/gb/gbMemory.cpp @@ -0,0 +1,1703 @@ +#include "../System.h" +#include "../common/Port.h" +#include "gbGlobals.h" +#include "gbMemory.h" +#include "gb.h" +u8 gbDaysinMonth [12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; +const u8 gbDisabledRam [8] = {0x80, 0xff, 0xf0, 0x00, 0x30, 0xbf, 0xbf, 0xbf}; +extern int gbGBCColorType; +extern gbRegister PC; + +mapperMBC1 gbDataMBC1 = { + 0, // RAM enable + 1, // ROM bank + 0, // RAM bank + 0, // memory model + 0, // ROM high address + 0, // RAM address + 0 // Rom Bank 0 remapping +}; + +// MBC1 ROM write registers +void mapperMBC1ROM(u16 address, u8 value) +{ + int tmpAddress = 0; + + switch(address & 0x6000) { + case 0x0000: // RAM enable register + gbDataMBC1.mapperRAMEnable = ( ( value & 0x0a) == 0x0a ? 1 : 0); + break; + case 0x2000: // ROM bank select + // value = value & 0x1f; + if ((value == 1) && (address == 0x2100)) + gbDataMBC1.mapperRomBank0Remapping = 1; + + if((value & 0x1f) == 0) + value += 1; + if(value == gbDataMBC1.mapperROMBank) + break; + + tmpAddress = value << 14; + + // check current model + if (gbDataMBC1.mapperRomBank0Remapping == 3) { + tmpAddress = (value & 0xf) << 14; + tmpAddress |= (gbDataMBC1.mapperROMHighAddress & 3) << 18; + } + else + if(gbDataMBC1.mapperMemoryModel == 0) { + // model is 16/8, so we have a high address in use + tmpAddress |= (gbDataMBC1.mapperROMHighAddress & 3) << 19; + } + + tmpAddress &= gbRomSizeMask; + gbDataMBC1.mapperROMBank = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + break; + case 0x4000: // RAM bank select + if(gbDataMBC1.mapperMemoryModel == 1) { + if (!gbRamSize) + { + if (gbDataMBC1.mapperRomBank0Remapping == 3) + { + gbDataMBC1.mapperROMHighAddress = value & 0x03; + tmpAddress = (gbDataMBC1.mapperROMHighAddress) << 18; + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x00] = &gbRom[tmpAddress]; + gbMemoryMap[0x01] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x02] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x03] = &gbRom[tmpAddress + 0x3000]; + gbMemoryMap[0x04] = &gbRom[tmpAddress + 0x4000]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x5000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x6000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x7000]; + } + else gbDataMBC1.mapperRomBank0Remapping = 0; + } + // 4/32 model, RAM bank switching provided + value = value & 0x03; + if(value == gbDataMBC1.mapperRAMBank) + break; + tmpAddress = value << 13; + tmpAddress &= gbRamSizeMask; + if(gbRamSize) { + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + } + gbDataMBC1.mapperRAMBank = value; + gbDataMBC1.mapperRAMAddress = tmpAddress; + + if (gbDataMBC1.mapperRomBank0Remapping != 3) + gbDataMBC1.mapperROMHighAddress = 0; + } else { + // 16/8, set the high address + gbDataMBC1.mapperROMHighAddress = value & 0x03; + tmpAddress = gbDataMBC1.mapperROMBank << 14; + tmpAddress |= (gbDataMBC1.mapperROMHighAddress) << 19; + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + if(gbRamSize) { + gbMemoryMap[0x0a] = &gbRam[0]; + gbMemoryMap[0x0b] = &gbRam[0x1000]; + } + + gbDataMBC1.mapperRAMBank = 0; + } + break; + case 0x6000: // memory model select + gbDataMBC1.mapperMemoryModel = value & 1; + + if(gbDataMBC1.mapperMemoryModel == 1) { + // 4/32 model, RAM bank switching provided + + value = gbDataMBC1.mapperRAMBank & 0x03; + tmpAddress = value << 13; + tmpAddress &= gbRamSizeMask; + if(gbRamSize) { + gbMemoryMap[0x0a] = &gbRam[gbDataMBC1.mapperRAMAddress]; + gbMemoryMap[0x0b] = &gbRam[gbDataMBC1.mapperRAMAddress + 0x1000]; + gbDataMBC1.mapperRomBank0Remapping = 0; + } + else gbDataMBC1.mapperRomBank0Remapping |=2; + + gbDataMBC1.mapperRAMBank = value; + gbDataMBC1.mapperRAMAddress = tmpAddress; + + tmpAddress = gbDataMBC1.mapperROMBank << 14; + + + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + } else { + // 16/8, set the high address + + tmpAddress = gbDataMBC1.mapperROMBank << 14; + tmpAddress |= (gbDataMBC1.mapperROMHighAddress) << 19; + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + if(gbRamSize) { + gbMemoryMap[0x0a] = &gbRam[0]; + gbMemoryMap[0x0b] = &gbRam[0x1000]; + } + } + break; + } +} + +// MBC1 RAM write +void mapperMBC1RAM(u16 address, u8 value) +{ + if(gbDataMBC1.mapperRAMEnable) { + if(gbRamSize) { + gbMemoryMap[address >> 12][address & 0x0fff] = value; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } +} + +// MBC1 read RAM +u8 mapperMBC1ReadRAM(u16 address) +{ + + if(gbDataMBC1.mapperRAMEnable) + return gbMemoryMap[address>>12][address & 0x0fff]; + + if (!genericflashcardEnable) + return 0xff; + else + if ((address & 0x1000) >= 0x1000) + { + // The value returned when reading RAM while it's disabled + // is constant, exept for the GBASP hardware. + // (actually, is the address that read is out of the ROM, the returned value if 0xff...) + if (PC.W>=0xff80) + return 0xff; + else + if ((gbHardware & 0x08) && (gbGBCColorType == 2)) + { + if (address & 1) + return 0xfb; + else + return 0x7a; + } + else + return 0x0a; + } + else + return gbDisabledRam[address & 7]; +} + +void memoryUpdateMapMBC1() +{ + int tmpAddress = gbDataMBC1.mapperROMBank << 14; + + // check current model + if (gbDataMBC1.mapperRomBank0Remapping == 3) { + tmpAddress = (gbDataMBC1.mapperROMHighAddress & 3) << 18; + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x00] = &gbRom[tmpAddress]; + gbMemoryMap[0x01] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x02] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x03] = &gbRom[tmpAddress + 0x3000]; + + tmpAddress |= (gbDataMBC1.mapperROMBank & 0xf) << 14; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + } + else + { + if(gbDataMBC1.mapperMemoryModel == 0) { + // model is 16/8, so we have a high address in use + tmpAddress |= (gbDataMBC1.mapperROMHighAddress & 3) << 19; + } + + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + } + + if(gbRamSize) { + if(gbDataMBC1.mapperMemoryModel == 1) { + gbMemoryMap[0x0a] = &gbRam[gbDataMBC1.mapperRAMAddress]; + gbMemoryMap[0x0b] = &gbRam[gbDataMBC1.mapperRAMAddress + 0x1000]; + } else { + gbMemoryMap[0x0a] = &gbRam[0]; + gbMemoryMap[0x0b] = &gbRam[0x1000]; + } + } +} + +mapperMBC2 gbDataMBC2 = { + 0, // RAM enable + 1 // ROM bank +}; + +// MBC2 ROM write registers +void mapperMBC2ROM(u16 address, u8 value) +{ + switch(address & 0x6000) { + case 0x0000: // RAM enable + if(!(address & 0x0100)) { + gbDataMBC2.mapperRAMEnable = (value & 0x0f) == 0x0a; + } + break; + case 0x2000: // ROM bank select + if(address & 0x0100) { + value &= 0x0f; + + if(value == 0) + value = 1; + if(gbDataMBC2.mapperROMBank != value) { + gbDataMBC2.mapperROMBank = value; + + int tmpAddress = value << 14; + + tmpAddress &= gbRomSizeMask; + + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + } + } + break; + } +} + +// MBC2 RAM write +void mapperMBC2RAM(u16 address, u8 value) +{ + if(gbDataMBC2.mapperRAMEnable) { + if(gbRamSize && address < 0xa200) { + gbMemoryMap[address >> 12][address & 0x0fff] = value; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } +} + +void memoryUpdateMapMBC2() +{ + int tmpAddress = gbDataMBC2.mapperROMBank << 14; + + tmpAddress &= gbRomSizeMask; + + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; +} + +mapperMBC3 gbDataMBC3 = { + 0, // RAM enable + 1, // ROM bank + 0, // RAM bank + 0, // RAM address + 0, // timer clock latch + 0, // timer clock register + 0, // timer seconds + 0, // timer minutes + 0, // timer hours + 0, // timer days + 0, // timer control + 0, // timer latched seconds + 0, // timer latched minutes + 0, // timer latched hours + 0, // timer latched days + 0, // timer latched control + (time_t)-1 // last time +}; + +void memoryUpdateMBC3Clock() +{ + time_t now = time(NULL); + time_t diff = now - gbDataMBC3.mapperLastTime; + if(diff > 0) { + // update the clock according to the last update time + gbDataMBC3.mapperSeconds += (int)(diff % 60); + if(gbDataMBC3.mapperSeconds > 59) { + gbDataMBC3.mapperSeconds -= 60; + gbDataMBC3.mapperMinutes++; + } + + diff /= 60; + + gbDataMBC3.mapperMinutes += (int)(diff % 60); + if(gbDataMBC3.mapperMinutes > 59) { + gbDataMBC3.mapperMinutes -= 60; + gbDataMBC3.mapperHours++; + } + + diff /= 60; + + gbDataMBC3.mapperHours += (int)(diff % 24); + if(gbDataMBC3.mapperHours > 23) { + gbDataMBC3.mapperHours -= 24; + gbDataMBC3.mapperDays++; + } + diff /= 24; + + gbDataMBC3.mapperDays += (int)(diff & 0xffffffff); + if(gbDataMBC3.mapperDays > 255) { + if(gbDataMBC3.mapperDays > 511) { + gbDataMBC3.mapperDays %= 512; + gbDataMBC3.mapperControl |= 0x80; + } + gbDataMBC3.mapperControl = (gbDataMBC3.mapperControl & 0xfe) | + (gbDataMBC3.mapperDays>255 ? 1 : 0); + } + } + gbDataMBC3.mapperLastTime = now; +} + +// MBC3 ROM write registers +void mapperMBC3ROM(u16 address, u8 value) +{ + int tmpAddress = 0; + + switch(address & 0x6000) { + case 0x0000: // RAM enable register + gbDataMBC3.mapperRAMEnable = ( ( value & 0x0a) == 0x0a ? 1 : 0); + break; + case 0x2000: // ROM bank select + value = value & 0x7f; + if(value == 0) + value = 1; + if(value == gbDataMBC3.mapperROMBank) + break; + + tmpAddress = value << 14; + + tmpAddress &= gbRomSizeMask; + gbDataMBC3.mapperROMBank = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + break; + case 0x4000: // RAM bank select + if(value < 8) { + if(value == gbDataMBC3.mapperRAMBank) + break; + tmpAddress = value << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + gbDataMBC3.mapperRAMBank = value; + gbDataMBC3.mapperRAMAddress = tmpAddress; + } else { + if(gbDataMBC3.mapperRAMEnable) { + gbDataMBC3.mapperRAMBank = -1; + + gbDataMBC3.mapperClockRegister = value; + } + } + break; + case 0x6000: // clock latch + if(gbDataMBC3.mapperClockLatch == 0 && value == 1) { + memoryUpdateMBC3Clock(); + gbDataMBC3.mapperLSeconds = gbDataMBC3.mapperSeconds; + gbDataMBC3.mapperLMinutes = gbDataMBC3.mapperMinutes; + gbDataMBC3.mapperLHours = gbDataMBC3.mapperHours; + gbDataMBC3.mapperLDays = gbDataMBC3.mapperDays; + gbDataMBC3.mapperLControl = gbDataMBC3.mapperControl; + } + if(value == 0x00 || value == 0x01) + gbDataMBC3.mapperClockLatch = value; + break; + } +} + +// MBC3 RAM write +void mapperMBC3RAM(u16 address, u8 value) +{ + if(gbDataMBC3.mapperRAMEnable) { + if(gbDataMBC3.mapperRAMBank != -1) { + if(gbRamSize) { + gbMemoryMap[address>>12][address & 0x0fff] = value; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } else { + time(&gbDataMBC3.mapperLastTime); + switch(gbDataMBC3.mapperClockRegister) { + case 0x08: + gbDataMBC3.mapperSeconds = value; + break; + case 0x09: + gbDataMBC3.mapperMinutes = value; + break; + case 0x0a: + gbDataMBC3.mapperHours = value; + break; + case 0x0b: + gbDataMBC3.mapperDays = value; + break; + case 0x0c: + if(gbDataMBC3.mapperControl & 0x80) + gbDataMBC3.mapperControl = 0x80 | value; + else + gbDataMBC3.mapperControl = value; + break; + } + } + } +} + +// MBC3 read RAM +u8 mapperMBC3ReadRAM(u16 address) +{ + if(gbDataMBC3.mapperRAMEnable) { + if(gbDataMBC3.mapperRAMBank != -1) { + return gbMemoryMap[address>>12][address & 0x0fff]; + } + + switch(gbDataMBC3.mapperClockRegister) { + case 0x08: + return gbDataMBC3.mapperLSeconds; + break; + case 0x09: + return gbDataMBC3.mapperLMinutes; + break; + case 0x0a: + return gbDataMBC3.mapperLHours; + break; + case 0x0b: + return gbDataMBC3.mapperLDays; + break; + case 0x0c: + return gbDataMBC3.mapperLControl; + } + } + + if (!genericflashcardEnable) + return 0xff; + else + if ((address & 0x1000) >= 0x1000) + { + // The value returned when reading RAM while it's disabled + // is constant, exept for the GBASP hardware. + // (actually, is the address that read is out of the ROM, the returned value if 0xff...) + if (PC.W>=0xff80) + return 0xff; + else + if ((gbHardware & 0x08) && (gbGBCColorType == 2)) + { + if (address & 1) + return 0xfb; + else + return 0x7a; + } + else + return 0x0a; + } + else + return gbDisabledRam[address & 7]; +} + +void memoryUpdateMapMBC3() +{ + int tmpAddress = gbDataMBC3.mapperROMBank << 14; + + tmpAddress &= gbRomSizeMask; + + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + if(gbDataMBC3.mapperRAMBank >= 0 && gbRamSize) { + tmpAddress = gbDataMBC3.mapperRAMBank << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + } +} + +mapperMBC5 gbDataMBC5 = { + 0, // RAM enable + 1, // ROM bank + 0, // RAM bank + 0, // ROM high address + 0, // RAM address + 0 // is rumble cartridge? +}; + +// MBC5 ROM write registers +void mapperMBC5ROM(u16 address, u8 value) +{ + int tmpAddress = 0; + + switch(address & 0x6000) { + case 0x0000: // RAM enable register + gbDataMBC5.mapperRAMEnable = ( ( value & 0x0a) == 0x0a ? 1 : 0); + break; + case 0x2000: // ROM bank select + + if(address < 0x3000) { + value = value & 0xff; + if(value == gbDataMBC5.mapperROMBank) + break; + + tmpAddress = (value << 14) | (gbDataMBC5.mapperROMHighAddress << 22) ; + + tmpAddress &= gbRomSizeMask; + gbDataMBC5.mapperROMBank = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + } else { + value = value & 1; + if(value == gbDataMBC5.mapperROMHighAddress) + break; + + tmpAddress = (gbDataMBC5.mapperROMBank << 14) | (value << 22); + + tmpAddress &= gbRomSizeMask; + gbDataMBC5.mapperROMHighAddress = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + } + break; + case 0x4000: // RAM bank select + if(gbDataMBC5.isRumbleCartridge) + value &= 0x07; + else + value &= 0x0f; + if(value == gbDataMBC5.mapperRAMBank) + break; + tmpAddress = value << 13; + tmpAddress &= gbRamSizeMask; + if(gbRamSize) { + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + + gbDataMBC5.mapperRAMBank = value; + gbDataMBC5.mapperRAMAddress = tmpAddress; + } + break; + } +} + +// MBC5 RAM write +void mapperMBC5RAM(u16 address, u8 value) +{ + if(gbDataMBC5.mapperRAMEnable) { + if(gbRamSize) { + gbMemoryMap[address >> 12][address & 0x0fff] = value; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } +} + +// MBC5 read RAM +u8 mapperMBC5ReadRAM(u16 address) +{ + + if(gbDataMBC5.mapperRAMEnable) + return gbMemoryMap[address>>12][address & 0x0fff]; + + if (!genericflashcardEnable) + return 0xff; + else + if ((address & 0x1000) >= 0x1000) + { + // The value returned when reading RAM while it's disabled + // is constant, exept for the GBASP hardware. + // (actually, is the address that read is out of the ROM, the returned value if 0xff...) + if (PC.W>=0xff80) + return 0xff; + else + if ((gbHardware & 0x08) && (gbGBCColorType == 2)) + { + if (address & 1) + return 0xfb; + else + return 0x7a; + } + else + return 0x0a; + } + else + return gbDisabledRam[address & 7]; +} + +void memoryUpdateMapMBC5() +{ + int tmpAddress = (gbDataMBC5.mapperROMBank << 14) | + (gbDataMBC5.mapperROMHighAddress << 22) ; + + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + if(gbRamSize) { + tmpAddress = gbDataMBC5.mapperRAMBank << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + } +} + +mapperMBC7 gbDataMBC7 = { + 0, // RAM enable + 1, // ROM bank + 0, // RAM bank + 0, // RAM address + 0, // chip select + 0, // ?? + 0, // mapper state + 0, // buffer for receiving serial data + 0, // idle state + 0, // count of bits received + 0, // command received + 0, // address received + 0, // write enable + 0, // value to return on ram +}; + +// MBC7 ROM write registers +void mapperMBC7ROM(u16 address, u8 value) +{ + int tmpAddress = 0; + + switch(address & 0x6000) { + case 0x0000: + break; + case 0x2000: // ROM bank select + value = value & 0x7f; + if(value == 0) + value = 1; + + if(value == gbDataMBC7.mapperROMBank) + break; + + tmpAddress = (value << 14); + + tmpAddress &= gbRomSizeMask; + gbDataMBC7.mapperROMBank = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + break; + case 0x4000: // RAM bank select/enable + if(value < 8) { + tmpAddress = (value&3) << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbMemory[0xa000]; + gbMemoryMap[0x0b] = &gbMemory[0xb000]; + + gbDataMBC7.mapperRAMBank = value; + gbDataMBC7.mapperRAMAddress = tmpAddress; + gbDataMBC7.mapperRAMEnable = 0; + } else { + gbDataMBC7.mapperRAMEnable = 0; + } + break; + } +} + +// MBC7 read RAM +u8 mapperMBC7ReadRAM(u16 address) +{ + switch(address & 0xa0f0) { + case 0xa000: + case 0xa010: + case 0xa060: + case 0xa070: + return 0; + case 0xa020: + // sensor X low byte + return systemGetSensorX() & 255; + case 0xa030: + // sensor X high byte + return systemGetSensorX() >> 8; + case 0xa040: + // sensor Y low byte + return systemGetSensorY() & 255; + case 0xa050: + // sensor Y high byte + return systemGetSensorY() >> 8; + case 0xa080: + return gbDataMBC7.value; + } + + if (!genericflashcardEnable) + return 0xff; + else + if ((address & 0x1000) >= 0x1000) + { + // The value returned when reading RAM while it's disabled + // is constant, exept for the GBASP hardware. + // (actually, is the address that read is out of the ROM, the returned value if 0xff...) + if (PC.W>=0xff80) + return 0xff; + else + if ((gbHardware & 0x08) && (gbGBCColorType == 2)) + { + if (address & 1) + return 0xfb; + else + return 0x7a; + } + else + return 0x0a; + } + else + return gbDisabledRam[address & 7]; +} + +// MBC7 RAM write +void mapperMBC7RAM(u16 address, u8 value) +{ + if(address == 0xa080) { + // special processing needed + int oldCs = gbDataMBC7.cs,oldSk=gbDataMBC7.sk; + + gbDataMBC7.cs=value>>7; + gbDataMBC7.sk=(value>>6)&1; + + if(!oldCs && gbDataMBC7.cs) { + if(gbDataMBC7.state==5) { + if(gbDataMBC7.writeEnable) { + gbMemory[0xa000+gbDataMBC7.address*2]=gbDataMBC7.buffer>>8; + gbMemory[0xa000+gbDataMBC7.address*2+1]=gbDataMBC7.buffer&0xff; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + gbDataMBC7.state=0; + gbDataMBC7.value=1; + } else { + gbDataMBC7.idle=true; + gbDataMBC7.state=0; + } + } + + if(!oldSk && gbDataMBC7.sk) { + if(gbDataMBC7.idle) { + if(value & 0x02) { + gbDataMBC7.idle=false; + gbDataMBC7.count=0; + gbDataMBC7.state=1; + } + } else { + switch(gbDataMBC7.state) { + case 1: + // receiving command + gbDataMBC7.buffer <<= 1; + gbDataMBC7.buffer |= (value & 0x02)?1:0; + gbDataMBC7.count++; + if(gbDataMBC7.count==2) { + // finished receiving command + gbDataMBC7.state=2; + gbDataMBC7.count=0; + gbDataMBC7.code=gbDataMBC7.buffer & 3; + } + break; + case 2: + // receive address + gbDataMBC7.buffer <<= 1; + gbDataMBC7.buffer |= (value&0x02)?1:0; + gbDataMBC7.count++; + if(gbDataMBC7.count==8) { + // finish receiving + gbDataMBC7.state=3; + gbDataMBC7.count=0; + gbDataMBC7.address=gbDataMBC7.buffer&0xff; + if(gbDataMBC7.code==0) { + if((gbDataMBC7.address>>6)==0) { + gbDataMBC7.writeEnable=0; + gbDataMBC7.state=0; + } else if((gbDataMBC7.address>>6) == 3) { + gbDataMBC7.writeEnable=1; + gbDataMBC7.state=0; + } + } + } + break; + case 3: + gbDataMBC7.buffer <<= 1; + gbDataMBC7.buffer |= (value&0x02)?1:0; + gbDataMBC7.count++; + + switch(gbDataMBC7.code) { + case 0: + if(gbDataMBC7.count==16) { + if((gbDataMBC7.address>>6)==0) { + gbDataMBC7.writeEnable = 0; + gbDataMBC7.state=0; + } else if((gbDataMBC7.address>>6)==1) { + if (gbDataMBC7.writeEnable) { + for(int i=0;i<256;i++) { + gbMemory[0xa000+i*2] = gbDataMBC7.buffer >> 8; + gbMemory[0xa000+i*2+1] = gbDataMBC7.buffer & 0xff; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } + gbDataMBC7.state=5; + } else if((gbDataMBC7.address>>6) == 2) { + if (gbDataMBC7.writeEnable) { + for(int i=0;i<256;i++) + WRITE16LE((u16 *)&gbMemory[0xa000+i*2], 0xffff); + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + gbDataMBC7.state=5; + } else if((gbDataMBC7.address>>6)==3) { + gbDataMBC7.writeEnable = 1; + gbDataMBC7.state=0; + } + gbDataMBC7.count=0; + } + break; + case 1: + if(gbDataMBC7.count==16) { + gbDataMBC7.count=0; + gbDataMBC7.state=5; + gbDataMBC7.value=0; + } + break; + case 2: + if(gbDataMBC7.count==1) { + gbDataMBC7.state=4; + gbDataMBC7.count=0; + gbDataMBC7.buffer = (gbMemory[0xa000+gbDataMBC7.address*2]<<8)| + (gbMemory[0xa000+gbDataMBC7.address*2+1]); + } + break; + case 3: + if(gbDataMBC7.count==16) { + gbDataMBC7.count=0; + gbDataMBC7.state=5; + gbDataMBC7.value=0; + gbDataMBC7.buffer=0xffff; + } + break; + } + break; + } + } + } + + if (oldSk && !gbDataMBC7.sk) { + if (gbDataMBC7.state==4) { + gbDataMBC7.value = (gbDataMBC7.buffer & 0x8000)?1:0; + gbDataMBC7.buffer <<= 1; + gbDataMBC7.count++; + if (gbDataMBC7.count==16) { + gbDataMBC7.count=0; + gbDataMBC7.state=0; + } + } + } + } +} + +void memoryUpdateMapMBC7() +{ + int tmpAddress = (gbDataMBC7.mapperROMBank << 14); + + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; +} + +mapperHuC1 gbDataHuC1 = { + 0, // RAM enable + 1, // ROM bank + 0, // RAM bank + 0, // memory model + 0, // ROM high address + 0 // RAM address +}; + +// HuC1 ROM write registers +void mapperHuC1ROM(u16 address, u8 value) +{ + int tmpAddress = 0; + + switch(address & 0x6000) { + case 0x0000: // RAM enable register + gbDataHuC1.mapperRAMEnable = ( ( value & 0x0a) == 0x0a ? 1 : 0); + break; + case 0x2000: // ROM bank select + value = value & 0x3f; + if(value == 0) + value = 1; + if(value == gbDataHuC1.mapperROMBank) + break; + + tmpAddress = value << 14; + + tmpAddress &= gbRomSizeMask; + gbDataHuC1.mapperROMBank = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + break; + case 0x4000: // RAM bank select + if(gbDataHuC1.mapperMemoryModel == 1) { + // 4/32 model, RAM bank switching provided + value = value & 0x03; + if(value == gbDataHuC1.mapperRAMBank) + break; + tmpAddress = value << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + gbDataHuC1.mapperRAMBank = value; + gbDataHuC1.mapperRAMAddress = tmpAddress; + } else { + // 16/8, set the high address + gbDataHuC1.mapperROMHighAddress = value & 0x03; + tmpAddress = gbDataHuC1.mapperROMBank << 14; + tmpAddress |= (gbDataHuC1.mapperROMHighAddress) << 19; + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + } + break; + case 0x6000: // memory model select + gbDataHuC1.mapperMemoryModel = value & 1; + break; + } +} + +// HuC1 RAM write +void mapperHuC1RAM(u16 address, u8 value) +{ + if(gbDataHuC1.mapperRAMEnable) { + if(gbRamSize) { + gbMemoryMap[address >> 12][address & 0x0fff] = value; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } +} + +void memoryUpdateMapHuC1() +{ + int tmpAddress = gbDataHuC1.mapperROMBank << 14; + + tmpAddress &= gbRomSizeMask; + + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + if(gbRamSize) { + tmpAddress = gbDataHuC1.mapperRAMBank << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + } +} + +mapperHuC3 gbDataHuC3 = { + 0, // RAM enable + 1, // ROM bank + 0, // RAM bank + 0, // RAM address + 0, // RAM flag + 0 // RAM read value +}; + + +// HuC3 ROM write registers +void mapperHuC3ROM(u16 address, u8 value) +{ + int tmpAddress = 0; + + switch(address & 0x6000) { + case 0x0000: // RAM enable register + gbDataHuC3.mapperRAMEnable = ( value == 0x0a ? 1 : 0); + gbDataHuC3.mapperRAMFlag = value; + if(gbDataHuC3.mapperRAMFlag != 0x0a) + gbDataHuC3.mapperRAMBank = -1; + break; + case 0x2000: // ROM bank select + value = value & 0x7f; + if(value == 0) + value = 1; + if(value == gbDataHuC3.mapperROMBank) + break; + + tmpAddress = value << 14; + + tmpAddress &= gbRomSizeMask; + gbDataHuC3.mapperROMBank = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + break; + case 0x4000: // RAM bank select + value = value & 0x03; + if(value == gbDataHuC3.mapperRAMBank) + break; + tmpAddress = value << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + gbDataHuC3.mapperRAMBank = value; + gbDataHuC3.mapperRAMAddress = tmpAddress; + break; + case 0x6000: // nothing to do! + break; + } +} + +// HuC3 read RAM +u8 mapperHuC3ReadRAM(u16 address) +{ + if(gbDataHuC3.mapperRAMFlag > 0x0b && + gbDataHuC3.mapperRAMFlag < 0x0e) { + if(gbDataHuC3.mapperRAMFlag != 0x0c) + return 1; + return gbDataHuC3.mapperRAMValue; + } else + return gbMemoryMap[address >> 12][address & 0x0fff]; +} + +// HuC3 RAM write +void mapperHuC3RAM(u16 address, u8 value) +{ + int *p; + + if(gbDataHuC3.mapperRAMFlag < 0x0b || + gbDataHuC3.mapperRAMFlag > 0x0e) { + if(gbDataHuC3.mapperRAMEnable) { + if(gbRamSize) { + gbMemoryMap[address >> 12][address & 0x0fff] = value; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } + } else { + if(gbDataHuC3.mapperRAMFlag == 0x0b) { + if(value == 0x62) { + gbDataHuC3.mapperRAMValue = 1; + } else { + switch(value & 0xf0) { + case 0x10: + p = &gbDataHuC3.mapperRegister2; + gbDataHuC3.mapperRAMValue = *(p+gbDataHuC3.mapperRegister1++); + if(gbDataHuC3.mapperRegister1 > 6) + gbDataHuC3.mapperRegister1 = 0; + break; + case 0x30: + p = &gbDataHuC3.mapperRegister2; + *(p+gbDataHuC3.mapperRegister1++) = value & 0x0f; + if(gbDataHuC3.mapperRegister1 > 6) + gbDataHuC3.mapperRegister1 = 0; + gbDataHuC3.mapperAddress = + (gbDataHuC3.mapperRegister6 << 24) | + (gbDataHuC3.mapperRegister5 << 16) | + (gbDataHuC3.mapperRegister4 << 8) | + (gbDataHuC3.mapperRegister3 << 4) | + (gbDataHuC3.mapperRegister2); + break; + case 0x40: + gbDataHuC3.mapperRegister1 = (gbDataHuC3.mapperRegister1 & 0xf0) | + (value & 0x0f); + gbDataHuC3.mapperRegister2 = (gbDataHuC3.mapperAddress & 0x0f); + gbDataHuC3.mapperRegister3 = ((gbDataHuC3.mapperAddress>>4)&0x0f); + gbDataHuC3.mapperRegister4 = ((gbDataHuC3.mapperAddress>>8)&0x0f); + gbDataHuC3.mapperRegister5 = ((gbDataHuC3.mapperAddress>>16)&0x0f); + gbDataHuC3.mapperRegister6 = ((gbDataHuC3.mapperAddress>>24)&0x0f); + gbDataHuC3.mapperRegister7 = 0; + gbDataHuC3.mapperRegister8 = 0; + gbDataHuC3.mapperRAMValue = 0; + break; + case 0x50: + gbDataHuC3.mapperRegister1 = (gbDataHuC3.mapperRegister1 & 0x0f) | + ((value << 4)&0x0f); + break; + default: + gbDataHuC3.mapperRAMValue = 1; + break; + } + } + } + } +} + +void memoryUpdateMapHuC3() +{ + int tmpAddress = gbDataHuC3.mapperROMBank << 14; + + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + if(gbRamSize) { + tmpAddress = gbDataHuC3.mapperRAMBank << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + } +} + +// TAMA5 (for Tamagotchi 3 (gb)). +// Very basic (and ugly :p) support, only rom bank switching is actually working... +mapperTAMA5 gbDataTAMA5 = { + 1, // RAM enable + 1, // ROM bank + 0, // RAM bank + 0, // RAM address + 0, // RAM Byte select + 0, // mapper command number + 0, // mapper last command; + { + 0, // commands 0x0 + 0, // commands 0x1 + 0, // commands 0x2 + 0, // commands 0x3 + 0, // commands 0x4 + 0, // commands 0x5 + 0, // commands 0x6 + 0, // commands 0x7 + 0, // commands 0x8 + 0, // commands 0x9 + 0, // commands 0xa + 0, // commands 0xb + 0, // commands 0xc + 0, // commands 0xd + 0, // commands 0xe + 0 // commands 0xf + }, + 0, // register + 0, // timer clock latch + 0, // timer clock register + 0, // timer seconds + 0, // timer minutes + 0, // timer hours + 0, // timer days + 0, // timer months + 0, // timer years + 0, // timer control + 0, // timer latched seconds + 0, // timer latched minutes + 0, // timer latched hours + 0, // timer latched days + 0, // timer latched months + 0, // timer latched years + 0, // timer latched control + (time_t)-1 // last time +}; + + +void memoryUpdateTAMA5Clock() +{ + if ((gbDataTAMA5.mapperYears & 3) == 0) + gbDaysinMonth[1] = 29; + else + gbDaysinMonth[1] = 28; + + time_t now = time(NULL); + time_t diff = now - gbDataTAMA5.mapperLastTime; + if(diff > 0) { + // update the clock according to the last update time + gbDataTAMA5.mapperSeconds += (int)(diff % 60); + if(gbDataTAMA5.mapperSeconds > 59) { + gbDataTAMA5.mapperSeconds -= 60; + gbDataTAMA5.mapperMinutes++; + } + + diff /= 60; + + gbDataTAMA5.mapperMinutes += (int)(diff % 60); + if(gbDataTAMA5.mapperMinutes > 59) { + gbDataTAMA5.mapperMinutes -= 60; + gbDataTAMA5.mapperHours++; + } + + diff /= 60; + + gbDataTAMA5.mapperHours += (int)(diff % 24); + diff /= 24; + if(gbDataTAMA5.mapperHours > 23) { + gbDataTAMA5.mapperHours -= 24; + diff++; + + } + + time_t days = diff; + while (days) + { + gbDataTAMA5.mapperDays++; + days--; + if (gbDataTAMA5.mapperDays>gbDaysinMonth[gbDataTAMA5.mapperMonths-1]) + { + gbDataTAMA5.mapperDays = 1; + gbDataTAMA5.mapperMonths++; + if (gbDataTAMA5.mapperMonths>12) + { + gbDataTAMA5.mapperMonths = 1; + gbDataTAMA5.mapperYears++; + if ((gbDataTAMA5.mapperYears & 3) == 0) + gbDaysinMonth[1] = 29; + else + gbDaysinMonth[1] = 28; + } + } + } + } + gbDataTAMA5.mapperLastTime = now; + +} + + + +// TAMA5 RAM write +void mapperTAMA5RAM(u16 address, u8 value) +{ + if ((address & 0xffff) <= 0xa001) + { + switch (address & 1) + { + case 0: // 'Values' Register + { + value &= 0xf; + gbDataTAMA5.mapperCommands[gbDataTAMA5.mapperCommandNumber] = value; + gbMemoryMap[0xa][0] = value; + +/* int test = gbDataTAMA5.mapperCommands[gbDataTAMA5.mapperCommandNumber & 0x0e] | + (gbDataTAMA5.mapperCommands[(gbDataTAMA5.mapperCommandNumber & 0x0e) +1]<<4);*/ + + if ((gbDataTAMA5.mapperCommandNumber & 0xe) == 0) // Read Command !!! + { + gbDataTAMA5.mapperROMBank = gbDataTAMA5.mapperCommands[0] | + (gbDataTAMA5.mapperCommands[1]<<4); + + int tmpAddress = (gbDataTAMA5.mapperROMBank << 14); + + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + gbDataTAMA5.mapperCommands[0x0f] = 0; + } + else if ((gbDataTAMA5.mapperCommandNumber & 0xe) == 4) + { + gbDataTAMA5.mapperCommands[0x0f] = 1; + if (gbDataTAMA5.mapperCommandNumber == 4) + gbDataTAMA5.mapperCommands[5] =0; // correct ? + } + else if ((gbDataTAMA5.mapperCommandNumber & 0xe) == 6) + { + gbDataTAMA5.mapperRamByteSelect = (gbDataTAMA5.mapperCommands[7]<<4) | + (gbDataTAMA5.mapperCommands[6]&0x0f); + + // Write Commands !!! + if (gbDataTAMA5.mapperCommands[0x0f] && (gbDataTAMA5.mapperCommandNumber == 7)) + { + int data = (gbDataTAMA5.mapperCommands[0x04] & 0x0f) | + (gbDataTAMA5.mapperCommands[0x05] <<4); + + // Not sure when the write command should reset... + // but it doesn't seem to matter. + // gbDataTAMA5.mapperCommands[0x0f] = 0; + + if (gbDataTAMA5.mapperRamByteSelect == 0x8) // Timer stuff + { + switch (data & 0xf) + { + case 0x7: + gbDataTAMA5.mapperDays = ((gbDataTAMA5.mapperDays)/10)*10 + (data >> 4); + break; + case 0x8: + gbDataTAMA5.mapperDays = (gbDataTAMA5.mapperDays%10) + (data >>4)*10; + break; + case 0x9: + gbDataTAMA5.mapperMonths = ((gbDataTAMA5.mapperMonths)/10)*10 + (data >> 4); + break; + case 0xa: + gbDataTAMA5.mapperMonths = (gbDataTAMA5.mapperMonths%10) + (data >>4)*10; + break; + case 0xb: + gbDataTAMA5.mapperYears = ((gbDataTAMA5.mapperYears)%1000) + (data >> 4)*1000; + break; + case 0xc: + gbDataTAMA5.mapperYears = (gbDataTAMA5.mapperYears%100) + (gbDataTAMA5.mapperYears/1000)*1000 + + (data >>4)*100; + break; + default : + break; + } + } + else if (gbDataTAMA5.mapperRamByteSelect == 0x18) // Timer stuff again + { + memoryUpdateTAMA5Clock(); + gbDataTAMA5.mapperLSeconds = gbDataTAMA5.mapperSeconds; + gbDataTAMA5.mapperLMinutes = gbDataTAMA5.mapperMinutes; + gbDataTAMA5.mapperLHours = gbDataTAMA5.mapperHours; + gbDataTAMA5.mapperLDays = gbDataTAMA5.mapperDays; + gbDataTAMA5.mapperLMonths = gbDataTAMA5.mapperMonths; + gbDataTAMA5.mapperLYears = gbDataTAMA5.mapperYears; + gbDataTAMA5.mapperLControl = gbDataTAMA5.mapperControl; + + int seconds = (gbDataTAMA5.mapperLSeconds / 10)*16 + gbDataTAMA5.mapperLSeconds %10; + int secondsL = (gbDataTAMA5.mapperLSeconds % 10); + int secondsH = (gbDataTAMA5.mapperLSeconds / 10); + int minutes = (gbDataTAMA5.mapperLMinutes / 10)*16 + gbDataTAMA5.mapperLMinutes %10; + int hours = (gbDataTAMA5.mapperLHours / 10)*16 + gbDataTAMA5.mapperLHours %10; + int DaysL = gbDataTAMA5.mapperLDays % 10; + int DaysH = gbDataTAMA5.mapperLDays /10; + int MonthsL = gbDataTAMA5.mapperLMonths % 10; + int MonthsH = gbDataTAMA5.mapperLMonths / 10; + int Years3 = (gbDataTAMA5.mapperLYears / 100) % 10; + int Years4 = (gbDataTAMA5.mapperLYears / 1000); + + switch (data & 0x0f) + { + // I guess cases 0 and 1 are used for secondsL and secondsH + // so the game would update the timer values on screen when + // the seconds reset to 0... ? + case 0x0: + gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = secondsL; + break; + case 0x1: + gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = secondsH; + break; + case 0x7: + gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = DaysL; // days low + break; + case 0x8: + gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = DaysH; // days high + break; + case 0x9: + gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = MonthsL; // month low + break; + case 0xa: + gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = MonthsH; // month high + break; + case 0xb: + gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = Years4; // years 4th digit + break; + case 0xc: + gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = Years3; // years 3rd digit + break; + default : + break; + } + + gbTAMA5ram[0x54] = seconds; // incorrect ? (not used by the game) ? + gbTAMA5ram[0x64] = minutes; + gbTAMA5ram[0x74] = hours; + gbTAMA5ram[0x84] = DaysH*16+DaysL; // incorrect ? (not used by the game) ? + gbTAMA5ram[0x94] = MonthsH*16+MonthsL; // incorrect ? (not used by the game) ? + + time(&gbDataTAMA5.mapperLastTime); + + gbMemoryMap[0xa][0] = 1; + } + else if (gbDataTAMA5.mapperRamByteSelect == 0x28) // Timer stuff again + { + if ((data & 0xf) == 0xb) + gbDataTAMA5.mapperYears = ((gbDataTAMA5.mapperYears>>2)<<2) + (data & 3); + } + else if (gbDataTAMA5.mapperRamByteSelect == 0x44) + { + gbDataTAMA5.mapperMinutes = (data/16)*10 + data%16; + } + else if (gbDataTAMA5.mapperRamByteSelect == 0x54) + { + gbDataTAMA5.mapperHours = (data/16)*10 + data%16; + } + else + { + gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = data; + } + } + } + } + break; + case 1: // 'Commands' Register + { + gbMemoryMap[0xa][1] = gbDataTAMA5.mapperCommandNumber = value; + + // This should be only a 'is the flashrom ready ?' command. + // However as I couldn't find any 'copy' command + // (that seems to be needed for the saving system to work) + // I put it there... + if (value == 0x0a) + { + for (int i = 0; i<0x10; i++) + for (int j = 0; j<0x10; j++) + if (!(j&2)) + gbTAMA5ram[((i*0x10)+j) | 2] = gbTAMA5ram[(i*0x10)+j]; + // Enable this to see the content of the flashrom in 0xe000 + /*for (int k = 0; k<0x100; k++) + gbMemoryMap[0xe][k] = gbTAMA5ram[k];*/ + + gbMemoryMap[0xa][0] = gbDataTAMA5.mapperRAMEnable = 1; + } + else + { + if ((value & 0x0e) == 0x0c) + { + gbDataTAMA5.mapperRamByteSelect = gbDataTAMA5.mapperCommands[6] | + (gbDataTAMA5.mapperCommands[7]<<4); + + u8 byte = gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect]; + + gbMemoryMap[0xa][0] = (value & 1) ? byte >> 4 : byte & 0x0f; + + gbDataTAMA5.mapperCommands[0x0f] = 0; + } + } + break; + } + } + } + else + { + if(gbDataTAMA5.mapperRAMEnable) { + if(gbDataTAMA5.mapperRAMBank != -1) { + if(gbRamSize) { + gbMemoryMap[address>>12][address & 0x0fff] = value; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } + } + } +} + + +// TAMA5 read RAM +u8 mapperTAMA5ReadRAM(u16 address) +{ + return gbMemoryMap[address>>12][address & 0xfff]; +} + + +void memoryUpdateMapTAMA5() +{ + int tmpAddress = (gbDataTAMA5.mapperROMBank << 14); + + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + if(gbRamSize) { + tmpAddress = 0 << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + } +} + +// MMM01 Used in Momotarou collection (however the rom is corrupted) +mapperMMM01 gbDataMMM01 ={ + 0, // RAM enable + 1, // ROM bank + 0, // RAM bank + 0, // memory model + 0, // ROM high address + 0, // RAM address + 0 // Rom Bank 0 remapping +}; + +// MMM01 ROM write registers +void mapperMMM01ROM(u16 address, u8 value) +{ + int tmpAddress = 0; + + switch(address & 0x6000) { + case 0x0000: // RAM enable register + gbDataMMM01.mapperRAMEnable = ( ( value & 0x0a) == 0x0a ? 1 : 0); + break; + case 0x2000: // ROM bank select + // value = value & 0x1f; + if(value == 0) + value = 1; + if(value == gbDataMMM01.mapperROMBank) + break; + + tmpAddress = value << 14; + + // check current model + if(gbDataMMM01.mapperMemoryModel == 0) { + // model is 16/8, so we have a high address in use + tmpAddress |= (gbDataMMM01.mapperROMHighAddress) << 19; + } + else + tmpAddress |= gbDataMMM01.mapperRomBank0Remapping << 18; + + tmpAddress &= gbRomSizeMask; + gbDataMMM01.mapperROMBank = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + break; + case 0x4000: // RAM bank select + if(gbDataMMM01.mapperMemoryModel == 1) { + // 4/32 model, RAM bank switching provided + value = value & 0x03; + if(value == gbDataMBC1.mapperRAMBank) + break; + tmpAddress = value << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + gbDataMMM01.mapperRAMBank = value; + gbDataMMM01.mapperRAMAddress = tmpAddress; + } else { + // 16/8, set the high address + gbDataMMM01.mapperROMHighAddress = value & 0x03; + tmpAddress = gbDataMMM01.mapperROMBank << 14; + tmpAddress |= (gbDataMMM01.mapperROMHighAddress) << 19; + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + gbDataMMM01.mapperRomBank0Remapping = ((value<<1) | (value & 0x40 ? 1 : 0)) & 0xff; + tmpAddress = gbDataMMM01.mapperRomBank0Remapping << 18; + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x00] = &gbRom[tmpAddress]; + gbMemoryMap[0x01] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x02] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x03] = &gbRom[tmpAddress + 0x3000]; + } + break; + case 0x6000: // memory model select + gbDataMMM01.mapperMemoryModel = value & 1; + break; + } +} + +// MMM01 RAM write +void mapperMMM01RAM(u16 address, u8 value) +{ + if(gbDataMMM01.mapperRAMEnable) { + if(gbRamSize) { + gbMemoryMap[address >> 12][address & 0x0fff] = value; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } +} + +void memoryUpdateMapMMM01() +{ + int tmpAddress = gbDataMMM01.mapperROMBank << 14; + + // check current model + if(gbDataMMM01.mapperMemoryModel == 1) { + // model is 16/8, so we have a high address in use + tmpAddress |= (gbDataMMM01.mapperROMHighAddress) << 19; + } + + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + tmpAddress = gbDataMMM01.mapperRomBank0Remapping << 18; + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x00] = &gbRom[tmpAddress]; + gbMemoryMap[0x01] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x02] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x03] = &gbRom[tmpAddress + 0x3000]; + + if(gbRamSize) { + gbMemoryMap[0x0a] = &gbRam[gbDataMMM01.mapperRAMAddress]; + gbMemoryMap[0x0b] = &gbRam[gbDataMMM01.mapperRAMAddress + 0x1000]; + } +} + +// GameGenie ROM write registers +void mapperGGROM(u16 address, u8 value) +{ + switch(address & 0x6000) { + case 0x0000: // RAM enable register + break; + case 0x2000: // GameGenie has only a half bank + break; + case 0x4000: // GameGenie has no RAM + if ((address >=0x4001) && (address <= 0x4020)) // GG Hardware Registers + gbMemoryMap[address >> 12][address & 0x0fff] = value; + break; + case 0x6000: // GameGenie has only a half bank + break; + } +} + + +// GS3 Used to emulate the GS V3.0 rom bank switching +mapperGS3 gbDataGS3 = { 1 }; // ROM bank + +void mapperGS3ROM(u16 address, u8 value) +{ + int tmpAddress = 0; + + switch(address & 0x6000) { + case 0x0000: // GS has no ram + break; + case 0x2000: // GS has no 'classic' ROM bank select + break; + case 0x4000: // GS has no ram + break; + case 0x6000: // 0x6000 area is RW, and used for GS hardware registers + + if (address == 0x7FE1) // This is the (half) ROM bank select register + { + if(value == gbDataGS3.mapperROMBank) + break; + tmpAddress = value << 13; + + tmpAddress &= gbRomSizeMask; + gbDataGS3.mapperROMBank = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + } + else + gbMemoryMap[address>>12][address & 0x0fff] = value; + break; + } +} + +void memoryUpdateMapGS3() +{ + int tmpAddress = gbDataGS3.mapperROMBank << 13; + + tmpAddress &= gbRomSizeMask; + // GS can only change a half ROM bank + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; +} diff --git a/src/gb/gbMemory.h b/src/gb/gbMemory.h new file mode 100644 index 0000000..7c35c48 --- /dev/null +++ b/src/gb/gbMemory.h @@ -0,0 +1,188 @@ +#ifndef GBMEMORY_H +#define GBMEMORY_H + +#include + +struct mapperMBC1 { + int mapperRAMEnable; + int mapperROMBank; + int mapperRAMBank; + int mapperMemoryModel; + int mapperROMHighAddress; + int mapperRAMAddress; + int mapperRomBank0Remapping; +}; + +struct mapperMBC2 { + int mapperRAMEnable; + int mapperROMBank; +}; + +struct mapperMBC3 { + int mapperRAMEnable; + int mapperROMBank; + int mapperRAMBank; + int mapperRAMAddress; + int mapperClockLatch; + int mapperClockRegister; + int mapperSeconds; + int mapperMinutes; + int mapperHours; + int mapperDays; + int mapperControl; + int mapperLSeconds; + int mapperLMinutes; + int mapperLHours; + int mapperLDays; + int mapperLControl; + time_t mapperLastTime; +}; + +struct mapperMBC5 { + int mapperRAMEnable; + int mapperROMBank; + int mapperRAMBank; + int mapperROMHighAddress; + int mapperRAMAddress; + int isRumbleCartridge; +}; + +struct mapperMBC7 { + int mapperRAMEnable; + int mapperROMBank; + int mapperRAMBank; + int mapperRAMAddress; + int cs; + int sk; + int state; + int buffer; + int idle; + int count; + int code; + int address; + int writeEnable; + int value; +}; + +struct mapperHuC1 { + int mapperRAMEnable; + int mapperROMBank; + int mapperRAMBank; + int mapperMemoryModel; + int mapperROMHighAddress; + int mapperRAMAddress; +}; + +struct mapperHuC3 { + int mapperRAMEnable; + int mapperROMBank; + int mapperRAMBank; + int mapperRAMAddress; + int mapperAddress; + int mapperRAMFlag; + int mapperRAMValue; + int mapperRegister1; + int mapperRegister2; + int mapperRegister3; + int mapperRegister4; + int mapperRegister5; + int mapperRegister6; + int mapperRegister7; + int mapperRegister8; +}; + +struct mapperTAMA5 { + int mapperRAMEnable; + int mapperROMBank; + int mapperRAMBank; + int mapperRAMAddress; + int mapperRamByteSelect; + int mapperCommandNumber; + int mapperLastCommandNumber; + int mapperCommands[0x10]; + int mapperRegister; + int mapperClockLatch; + int mapperClockRegister; + int mapperSeconds; + int mapperMinutes; + int mapperHours; + int mapperDays; + int mapperMonths; + int mapperYears; + int mapperControl; + int mapperLSeconds; + int mapperLMinutes; + int mapperLHours; + int mapperLDays; + int mapperLMonths; + int mapperLYears; + int mapperLControl; + time_t mapperLastTime; +}; + +struct mapperMMM01 { + int mapperRAMEnable; + int mapperROMBank; + int mapperRAMBank; + int mapperMemoryModel; + int mapperROMHighAddress; + int mapperRAMAddress; + int mapperRomBank0Remapping; +}; + +struct mapperGS3 { + int mapperROMBank; +}; + +extern mapperMBC1 gbDataMBC1; +extern mapperMBC2 gbDataMBC2; +extern mapperMBC3 gbDataMBC3; +extern mapperMBC5 gbDataMBC5; +extern mapperHuC1 gbDataHuC1; +extern mapperHuC3 gbDataHuC3; +extern mapperTAMA5 gbDataTAMA5; +extern mapperMMM01 gbDataMMM01; +extern mapperGS3 gbDataGS3; + +void mapperMBC1ROM(u16,u8); +void mapperMBC1RAM(u16,u8); +u8 mapperMBC1ReadRAM(u16); +void mapperMBC2ROM(u16,u8); +void mapperMBC2RAM(u16,u8); +void mapperMBC3ROM(u16,u8); +void mapperMBC3RAM(u16,u8); +u8 mapperMBC3ReadRAM(u16); +void mapperMBC5ROM(u16,u8); +void mapperMBC5RAM(u16,u8); +u8 mapperMBC5ReadRAM(u16); +void mapperMBC7ROM(u16,u8); +void mapperMBC7RAM(u16,u8); +u8 mapperMBC7ReadRAM(u16); +void mapperHuC1ROM(u16,u8); +void mapperHuC1RAM(u16,u8); +void mapperHuC3ROM(u16,u8); +void mapperHuC3RAM(u16,u8); +u8 mapperHuC3ReadRAM(u16); +void mapperTAMA5RAM(u16,u8); +u8 mapperTAMA5ReadRAM(u16); +void memoryUpdateTAMA5Clock(); +void mapperMMM01ROM(u16,u8); +void mapperMMM01RAM(u16,u8); +void mapperGGROM(u16,u8); +void mapperGS3ROM(u16,u8); +//extern void (*mapper)(u16,u8); +//extern void (*mapperRAM)(u16,u8); +//extern u8 (*mapperReadRAM)(u16); + +extern void memoryUpdateMapMBC1(); +extern void memoryUpdateMapMBC2(); +extern void memoryUpdateMapMBC3(); +extern void memoryUpdateMapMBC5(); +extern void memoryUpdateMapMBC7(); +extern void memoryUpdateMapHuC1(); +extern void memoryUpdateMapHuC3(); +extern void memoryUpdateMapTAMA5(); +extern void memoryUpdateMapMMM01(); +extern void memoryUpdateMapGS3(); + +#endif // GBMEMORY_H diff --git a/src/gb/gbPrinter.cpp b/src/gb/gbPrinter.cpp new file mode 100644 index 0000000..82843f2 --- /dev/null +++ b/src/gb/gbPrinter.cpp @@ -0,0 +1,214 @@ +#include +#include +#include "../System.h" + +u8 gbPrinterStatus = 0; +int gbPrinterState = 0; +u8 gbPrinterData[0x280*9]; +u8 gbPrinterPacket[0x400]; +int gbPrinterCount = 0; +int gbPrinterDataCount = 0; +int gbPrinterDataSize = 0; +int gbPrinterResult = 0; + +bool gbPrinterCheckCRC() +{ + u16 crc = 0; + + for(int i = 2; i < (6+gbPrinterDataSize); i++) { + crc += gbPrinterPacket[i]; + } + + int msgCrc = gbPrinterPacket[6+gbPrinterDataSize] + + (gbPrinterPacket[7+gbPrinterDataSize]<<8); + + return msgCrc == crc; +} + +void gbPrinterReset() +{ + gbPrinterState = 0; + gbPrinterDataSize = 0; + gbPrinterDataCount = 0; + gbPrinterCount = 0; + gbPrinterStatus = 0; + gbPrinterResult = 0; +} + +void gbPrinterShowData() +{ + systemGbPrint(gbPrinterData, + gbPrinterDataCount, + gbPrinterPacket[6], + gbPrinterPacket[7], + gbPrinterPacket[8], + gbPrinterPacket[9]); + /* + allegro_init(); + install_keyboard(); + set_gfx_mode(GFX_AUTODETECT, 160, 144, 0, 0); + PALETTE pal; + pal[0].r = 255; + pal[0].g = 255; + pal[0].b = 255; + pal[1].r = 168; + pal[1].g = 168; + pal[1].b = 168; + pal[2].r = 96; + pal[2].g = 96; + pal[2].b = 96; + pal[3].r = 0; + pal[3].g = 0; + pal[3].b = 0; + set_palette(pal); + acquire_screen(); + u8 *data = gbPrinterData; + for(int y = 0; y < 0x12; y++) { + for(int x = 0; x < 0x14; x++) { + for(int k = 0; k < 8; k++) { + int a = *data++; + int b = *data++; + for(int j = 0; j < 8; j++) { + int mask = 1 << (7-j); + int c = 0; + if(a & mask) + c++; + if(b & mask) + c+=2; + putpixel(screen, x*8+j, y*8+k, c); + } + } + } + } + release_screen(); + while(!keypressed()) { + } + */ +} + +void gbPrinterReceiveData() +{ + int i = gbPrinterDataCount; + if(gbPrinterPacket[3]) { // compressed + u8 *data = &gbPrinterPacket[6]; + u8 *dest = &gbPrinterData[gbPrinterDataCount]; + int len = 0; + while(len < gbPrinterDataSize) { + u8 control = *data++; + if(control & 0x80) { // repeated data + control &= 0x7f; + control += 2; + memset(dest, *data++, control); + len += 2; + dest += control; + } else { // raw data + control++; + memcpy(dest, data, control); + dest += control; + data += control; + len += control + 1; + } + } + gbPrinterDataCount = (int)(dest - gbPrinterData); + } else { + memcpy(&gbPrinterData[gbPrinterDataCount], + &gbPrinterPacket[6], + gbPrinterDataSize); + gbPrinterDataCount += gbPrinterDataSize; + } +} + +void gbPrinterCommand() +{ + switch(gbPrinterPacket[2]) { + case 0x01: + // reset/initialize packet + gbPrinterDataCount = 0; + gbPrinterStatus = 0; + break; + case 0x02: + // print packet + gbPrinterShowData(); + break; + case 0x04: + // data packet + gbPrinterReceiveData(); + break; + case 0x0f: + // NUL packet + break; + } +} + +u8 gbPrinterSend(u8 b) +{ + switch(gbPrinterState) { + case 0: + gbPrinterCount = 0; + // receiving preamble + if(b == 0x88) { + gbPrinterPacket[gbPrinterCount++] = b; + gbPrinterState++; + } else { + // todo: handle failure + gbPrinterReset(); + } + break; + case 1: + // receiving preamble + if(b == 0x33) { + gbPrinterPacket[gbPrinterCount++] = b; + gbPrinterState++; + } else { + // todo: handle failure + gbPrinterReset(); + } + break; + case 2: + // receiving header + gbPrinterPacket[gbPrinterCount++] = b; + if(gbPrinterCount == 6) { + gbPrinterState++; + gbPrinterDataSize = gbPrinterPacket[4] + (gbPrinterPacket[5]<<8); + } + break; + case 3: + // receiving data + if(gbPrinterDataSize) { + gbPrinterPacket[gbPrinterCount++] = b; + if(gbPrinterCount == (6+gbPrinterDataSize)) { + gbPrinterState++; + } + break; + } + gbPrinterState++; + // intentionally move to next if no data to receive + case 4: + // receiving CRC + gbPrinterPacket[gbPrinterCount++] = b; + gbPrinterState++; + break; + case 5: + // receiving CRC-2 + gbPrinterPacket[gbPrinterCount++] = b; + if(gbPrinterCheckCRC()) { + gbPrinterCommand(); + } + gbPrinterState++; + break; + case 6: + // receiving dummy 1 + gbPrinterPacket[gbPrinterCount++] = b; + gbPrinterResult = 0x81; + gbPrinterState++; + break; + case 7: + // receiving dummy 2 + gbPrinterPacket[gbPrinterCount++] = b; + gbPrinterResult = gbPrinterStatus; + gbPrinterState = 0; + gbPrinterCount = 0; + break; + } + return gbPrinterResult; +} diff --git a/src/gb/gbPrinter.h b/src/gb/gbPrinter.h new file mode 100644 index 0000000..758a416 --- /dev/null +++ b/src/gb/gbPrinter.h @@ -0,0 +1,8 @@ +#ifndef GBPRINTER_H +#define GBPRINTER_H + +#include "../System.h" + +u8 gbPrinterSend(u8 b); + +#endif // GBPRINTER_H diff --git a/src/gb/gbSGB.cpp b/src/gb/gbSGB.cpp new file mode 100644 index 0000000..2a72bc5 --- /dev/null +++ b/src/gb/gbSGB.cpp @@ -0,0 +1,907 @@ +#include +#include + +#include "../System.h" +#include "../common/Port.h" +#include "../Util.h" +#include "gb.h" +#include "gbGlobals.h" + +extern u8 *pix; +extern bool speedup; +extern bool gbSgbResetFlag; + +#define GBSGB_NONE 0 +#define GBSGB_RESET 1 +#define GBSGB_PACKET_TRANSMIT 2 + +u8 *gbSgbBorderChar = NULL; +u8 *gbSgbBorder = NULL; + +int gbSgbCGBSupport = 0; +int gbSgbMask = 0; +int gbSgbMode = 0; +int gbSgbPacketState = GBSGB_NONE; +int gbSgbBit = 0; +int gbSgbPacketTimeout = 0; +int GBSGB_PACKET_TIMEOUT = 66666; +u8 gbSgbPacket[16*7]; +int gbSgbPacketNBits = 0; +int gbSgbPacketByte = 0; +int gbSgbPacketNumber = 0; +int gbSgbMultiplayer = 0; +int gbSgbFourPlayers = 0; +u8 gbSgbNextController = 0x0f; +u8 gbSgbReadingController = 0; +u16 gbSgbSCPPalette[4*512]; +u8 gbSgbATF[20 * 18]; +u8 gbSgbATFList[45 * 20 * 18]; +u8 gbSgbScreenBuffer[4160]; + +inline void gbSgbDraw24Bit(u8 *p, u16 v) +{ + memcpy(p, &systemColorMap32[v], 3); +} + +inline void gbSgbDraw32Bit(u32 *p, u16 v) +{ + *p = systemColorMap32[v]; +} + +inline void gbSgbDraw16Bit(u16 *p, u16 v) +{ + *p = systemColorMap16[v]; +} + +void gbSgbReset() +{ + gbSgbPacketTimeout = 0; + gbSgbCGBSupport = 0; + gbSgbMask = 0; + gbSgbPacketState = GBSGB_NONE; + gbSgbBit = 0; + gbSgbPacketNBits = 0; + gbSgbPacketNumber = 0; + gbSgbMultiplayer = 0; + gbSgbFourPlayers = 0; + gbSgbNextController = 0x0f; + gbSgbReadingController = 0; + + memset(gbSgbSCPPalette, 0, 512*4); + memset(gbSgbATF, 0, 20*18); + memset(gbSgbATFList, 0, 45 * 20 * 18); + memset(gbSgbPacket, 0, 16 * 7); + memset(gbSgbBorderChar, 0, 32*256); + memset(gbSgbBorder, 0, 2048); + + int i; + for(i = 1; i < 2048; i+=2) { + gbSgbBorder[i] = 1 << 2; + } + + for(i = 0; i < 32; i++) { + gbPalette[i*4] = (0x1f) | (0x1f << 5) | (0x1f << 10); + gbPalette[i*4+1] = (0x15) | (0x15 << 5) | (0x15 << 10); + gbPalette[i*4+2] = (0x0c) | (0x0c << 5) | (0x0c << 10); + gbPalette[i*4+3] = 0; + } +} + +void gbSgbInit() +{ + gbSgbBorderChar = (u8 *)malloc(32 * 256); + gbSgbBorder = (u8 *)malloc(2048); + + gbSgbReset(); +} + +void gbSgbShutdown() +{ + if(gbSgbBorderChar != NULL) { + free(gbSgbBorderChar); + gbSgbBorderChar = NULL; + } + + if(gbSgbBorder != NULL) { + free(gbSgbBorder); + gbSgbBorder = NULL; + } +} + +void gbSgbFillScreen(u16 color) +{ + switch(systemColorDepth) { + case 16: + { + for(int y = 0; y < 144; y++) { + int yLine = (y+gbBorderRowSkip+1)*(gbBorderLineSkip+2) + + gbBorderColumnSkip; + u16 *dest = (u16*)pix + yLine; + for(register int x = 0; x < 160; x++) + gbSgbDraw16Bit(dest++, color); + } + } + break; + case 24: + { + for(int y = 0; y < 144; y++) { + int yLine = (y+gbBorderRowSkip)*gbBorderLineSkip + gbBorderColumnSkip; + u8 *dest = (u8 *)pix + yLine*3; + for(register int x = 0; x < 160; x++) { + gbSgbDraw24Bit(dest, color); + dest += 3; + } + } + } + break; + case 32: + { + for(int y = 0; y < 144; y++) { + int yLine = (y+gbBorderRowSkip+1)*(gbBorderLineSkip+1) + gbBorderColumnSkip; + u32 *dest = (u32 *)pix + yLine; + for(register int x = 0; x < 160; x++) { + gbSgbDraw32Bit(dest++, color); + } + } + } + break; + } +} + +#define getmem(x) gbMemoryMap[(x) >> 12][(x) & 0xfff] + +void gbSgbRenderScreenToBuffer() +{ + u16 mapAddress = 0x9800; + + if(register_LCDC & 0x08) + mapAddress = 0x9c00; + + u16 patternAddress = 0x8800; + + int flag = 1; + + if(register_LCDC & 0x10) { + patternAddress = 0x8000; + flag = 0; + } + + u8 *toAddress = gbSgbScreenBuffer; + + for(int i = 0; i < 13; i++) { + for(int j = 0; j < 20; j++) { + int tile = getmem(mapAddress); + mapAddress++; + + if(flag) { + if(tile > 127) + tile -= 128; + else + tile += 128; + } + for(int k = 0; k < 16; k++) + *toAddress++ = getmem(patternAddress + tile*16 + k); + } + mapAddress += 12; + } +} + +void gbSgbDrawBorderTile(int x, int y, int tile, int attr) +{ + u16 *dest = (u16*)pix + ((y+1) * (256+2)) + x; + u8 *dest8 = (u8*)pix + ((y*256)+x)*3; + u32 *dest32 = (u32*)pix + ((y+1)*257) + x; + + u8 *tileAddress = &gbSgbBorderChar[tile * 32]; + u8 *tileAddress2 = &gbSgbBorderChar[tile * 32 + 16]; + + u8 l = 8; + + u8 palette = ((attr >> 2 ) & 7); + + if(palette < 4) + palette += 4; + + palette *= 16; + + u8 xx = 0; + u8 yy = 0; + + int flipX = attr & 0x40; + int flipY = attr & 0x80; + + while(l > 0) { + u8 mask = 0x80; + u8 a = *tileAddress++; + u8 b = *tileAddress++; + u8 c = *tileAddress2++; + u8 d = *tileAddress2++; + + + + u8 yyy; + if(!flipY) + yyy = yy; + else + yyy = 7 - yy; + + while(mask > 0) { + + u8 color = 0; + if(a & mask) + color++; + if(b & mask) + color+=2; + if(c & mask) + color+=4; + if(d & mask) + color+=8; + + if (color || (y + yy < 40 || y + yy >= 184) || (x + xx < 48 || x + xx >= 208)) { + u8 xxx; + + if(!flipX) + xxx = xx; + else + xxx = 7 - xx; + + u16 cc; + if (color) { + cc = gbPalette[palette + color]; + } else { + cc = gbPalette[0]; + } + + switch(systemColorDepth) { + case 16: + gbSgbDraw16Bit(dest + yyy*(256+2) + xxx, cc); + break; + case 24: + gbSgbDraw24Bit(dest8 + (yyy*256+xxx)*3, cc); + break; + case 32: + gbSgbDraw32Bit(dest32 + yyy*(256+1)+xxx, cc); + break; + } + } + + mask >>= 1; + + xx++; + } + yy++; + xx = 0; + l--; + mask = 0x80; + } +} + +void gbSgbRenderBorder() +{ + if(gbBorderOn) { + u8 *fromAddress = gbSgbBorder; + + for(u8 y = 0; y < 28; y++) { + for(u8 x = 0; x< 32; x++) { + u8 tile = *fromAddress++; + u8 attr = *fromAddress++; + + gbSgbDrawBorderTile(x*8,y*8,tile,attr); + } + } + } +} + +void gbSgbPicture() +{ + gbSgbRenderScreenToBuffer(); + + memcpy(gbSgbBorder, gbSgbScreenBuffer, 2048); + + u16 *paletteAddr = (u16 *)&gbSgbScreenBuffer[2048]; + + for(int i = 64; i < 128; i++) { + gbPalette[i] = READ16LE(paletteAddr++); + } + + gbSgbCGBSupport |= 4; + + if(gbBorderAutomatic && !gbBorderOn && gbSgbCGBSupport > 4) { + gbBorderOn = 1; + systemGbBorderOn(); + } + + if(gbBorderOn && !gbSgbMask) + gbSgbRenderBorder(); + + if(gbSgbMode && gbCgbMode && gbSgbCGBSupport > 4) { + gbSgbCGBSupport = 0; + gbSgbMode = 0; + gbSgbMask = 0; + gbSgbRenderBorder(); + gbReset(); + } + + if(gbSgbCGBSupport > 4) + gbSgbCGBSupport = 0; +} + +void gbSgbSetPalette(int a,int b,u16 *p) +{ + u16 bit00 = READ16LE(p++); + int i; + + for(i = 1; i < 4; i++) { + gbPalette[a*4+i] = READ16LE(p++); + } + + for(i = 1; i < 4; i++) { + gbPalette[b*4+i] = READ16LE(p++); + } + + gbPalette[0] = gbPalette[4] = gbPalette[8] = gbPalette[12] = bit00; + if(gbBorderOn && !gbSgbMask) + gbSgbRenderBorder(); +} + +void gbSgbScpPalette() +{ + gbSgbRenderScreenToBuffer(); + + u16 *fromAddress = (u16 *)gbSgbScreenBuffer; + + for(int i = 0; i < 512*4; i++) { + gbSgbSCPPalette[i] = READ16LE(fromAddress++); + } +} + +void gbSgbSetATF(int n) +{ + if(n < 0) + n = 0; + if(n > 44) + n = 44; + memcpy(gbSgbATF,&gbSgbATFList[n * 20 * 18], 20 * 18); + + if(gbSgbPacket[1] & 0x40) { + gbSgbMask = 0; + if(gbBorderOn) + gbSgbRenderBorder(); + } +} + +void gbSgbSetPalette() +{ + u16 pal = READ16LE((((u16 *)&gbSgbPacket[1])))&511; + memcpy(&gbPalette[0], &gbSgbSCPPalette[pal*4], 4 * sizeof(u16)); + + pal = READ16LE((((u16 *)&gbSgbPacket[3])))&511; + memcpy(&gbPalette[4], &gbSgbSCPPalette[pal*4], 4 * sizeof(u16)); + + pal = READ16LE((((u16 *)&gbSgbPacket[5])))&511; + memcpy(&gbPalette[8], &gbSgbSCPPalette[pal*4], 4 * sizeof(u16)); + + pal = READ16LE((((u16 *)&gbSgbPacket[7])))&511; + memcpy(&gbPalette[12], &gbSgbSCPPalette[pal*4], 4 * sizeof(u16)); + + u8 atf = gbSgbPacket[9]; + + if(atf & 0x80) { + gbSgbSetATF(atf & 0x3f); + } + + if(atf & 0x40) { + gbSgbMask = 0; + if(gbBorderOn) + gbSgbRenderBorder(); + } +} + +void gbSgbAttributeBlock() +{ + u8 *fromAddress = &gbSgbPacket[1]; + + u8 nDataSet = *fromAddress++; + if(nDataSet > 12) + nDataSet = 12; + if(nDataSet == 0) + nDataSet = 1; + + while(nDataSet) { + u8 controlCode = (*fromAddress++) & 7; + u8 paletteDesignation = (*fromAddress++) & 0x3f; + u8 startH = (*fromAddress++) & 0x1f; + u8 startV = (*fromAddress++) & 0x1f; + u8 endH = (*fromAddress++) & 0x1f; + u8 endV = (*fromAddress++) & 0x1f; + + u8 * toAddress = gbSgbATF; + + for(u8 y = 0; y < 18; y++) { + for(u8 x = 0; x < 20; x++) { + if(x < startH || y < startV || + x > endH || y > endV) { + // outside + if(controlCode & 0x04) + *toAddress = (paletteDesignation >> 4) & 0x03; + } else if(x > startH && x < endH && + y > startV && y < endV) { + // inside + if(controlCode & 0x01) + *toAddress = paletteDesignation & 0x03; + } else { + // surrounding line + if(controlCode & 0x02) + *toAddress = (paletteDesignation>>2) & 0x03; + else if(controlCode == 0x01) + *toAddress = paletteDesignation & 0x03; + } + toAddress++; + } + } + nDataSet--; + } +} + +void gbSgbSetColumnPalette(u8 col, u8 p) +{ + // if(col < 0) + // col = 0; + if(col > 19) + col = 19; + + p &= 3; + + u8 *toAddress = &gbSgbATF[col]; + + for(u8 y = 0; y < 18; y++) { + *toAddress = p; + toAddress += 20; + } +} + +void gbSgbSetRowPalette(u8 row, u8 p) +{ + // if(row < 0) + // row = 0; + if(row > 17) + row = 17; + + p &= 3; + + u8 *toAddress = &gbSgbATF[row*20]; + + for(u8 x = 0; x < 20; x++) { + *toAddress++ = p; + } +} + +void gbSgbAttributeDivide() +{ + u8 control = gbSgbPacket[1]; + u8 coord = gbSgbPacket[2]; + u8 colorBR = control & 3; + u8 colorAL = (control >> 2) & 3; + u8 colorOL = (control >> 4) & 3; + + if(control & 0x40) { + if(coord > 17) + coord = 17; + + for(u8 i = 0; i < 18; i++) { + if(i < coord) + gbSgbSetRowPalette(i, colorAL); + else if ( i > coord) + gbSgbSetRowPalette(i, colorBR); + else + gbSgbSetRowPalette(i, colorOL); + } + } else { + if(coord > 19) + coord = 19; + + for(u8 i = 0; i < 20; i++) { + if(i < coord) + gbSgbSetColumnPalette(i, colorAL); + else if ( i > coord) + gbSgbSetColumnPalette(i, colorBR); + else + gbSgbSetColumnPalette(i, colorOL); + } + } +} + +void gbSgbAttributeLine() +{ + u8 *fromAddress = &gbSgbPacket[1]; + + u8 nDataSet = *fromAddress++; + + if(nDataSet > 0x6e) + nDataSet = 0x6e; + + while(nDataSet) { + u8 line = *fromAddress++; + u8 num = line & 0x1f; + u8 pal = (line >> 5) & 0x03; + if(line & 0x80) { + if(num > 17) + num = 17; + gbSgbSetRowPalette(num,pal); + } else { + if(num > 19) + num = 19; + gbSgbSetColumnPalette(num,pal); + } + nDataSet--; + } +} + +void gbSgbAttributeCharacter() +{ + u8 startH = gbSgbPacket[1] & 0x1f; + u8 startV = gbSgbPacket[2] & 0x1f; + int nDataSet = READ16LE(((u16 *)&gbSgbPacket[3])); + int style = gbSgbPacket[5] & 1; + if(startH > 19) + startH = 19; + if(startV > 17) + startV = 17; + + u8 s = 6; + u8 *fromAddress = &gbSgbPacket[6]; + u8 v = *fromAddress++; + + if(style) { + while(nDataSet) { + u8 p = (v >> s) & 3; + gbSgbATF[startV * 20 + startH] = p; + startV++; + if(startV == 18) { + startV = 0; + startH++; + if(startH == 20) + break; + } + + if(s) + s -= 2; + else { + s = 6; + v = *fromAddress++; + nDataSet--; + } + } + } else { + while(nDataSet) { + u8 p = (v >> s) & 3; + gbSgbATF[startV * 20 + startH] = p; + startH++; + if(startH == 20) { + startH = 0; + startV++; + if(startV == 18) + break; + } + + if(s) + s -= 2; + else { + s = 6; + v = *fromAddress++; + nDataSet--; + } + } + } +} + +void gbSgbSetATFList() +{ + gbSgbRenderScreenToBuffer(); + + u8 *fromAddress = gbSgbScreenBuffer; + u8 *toAddress = gbSgbATFList; + + for(int i = 0; i < 45; i++) { + for(int j = 0; j < 90; j++) { + u8 v = *fromAddress++; + u8 s = 6; + if(i == 2) + s = 6; + for(int k = 0; k < 4; k++) { + *toAddress++ = (v >> s) & 0x03; + s -= 2; + } + } + } +} + +void gbSgbMaskEnable() +{ + int gbSgbMaskFlag = gbSgbPacket[1] & 3; + + gbSgbMask = gbSgbMaskFlag; + + switch(gbSgbMaskFlag) { + case 1: + break; + case 2: + gbSgbFillScreen(0x0000); + // memset(&gbPalette[0], 0, 128*sizeof(u16)); + break; + case 3: + gbSgbFillScreen(gbPalette[0]); + break; + } + if(!gbSgbMask) { + if(gbBorderOn) + gbSgbRenderBorder(); + } +} + +void gbSgbChrTransfer() +{ + gbSgbRenderScreenToBuffer(); + + int address = (gbSgbPacket[1] & 1) * (128*32); + + if(gbSgbPacket[1] & 1) + gbSgbCGBSupport |= 2; + else + gbSgbCGBSupport |= 1; + + memcpy(&gbSgbBorderChar[address], gbSgbScreenBuffer, 128 * 32); + + if(gbBorderAutomatic && !gbBorderOn && gbSgbCGBSupport > 4) { + gbBorderOn = 1; + systemGbBorderOn(); + } + + if(gbBorderOn && !gbSgbMask) + gbSgbRenderBorder(); + + if(gbSgbMode && gbCgbMode && gbSgbCGBSupport == 7) { + gbSgbCGBSupport = 0; + gbSgbMode = 0; + gbSgbMask = 0; + gbSgbRenderBorder(); + gbReset(); + } + + if(gbSgbCGBSupport > 4) + gbSgbCGBSupport = 0; +} + +void gbSgbMultiRequest() +{ + if(gbSgbPacket[1] & 1) { + gbSgbMultiplayer = 1; + if(gbSgbPacket[1] & 2) + gbSgbFourPlayers = 1; + else + gbSgbFourPlayers = 0; + gbSgbNextController = 0x0e; + } else { + gbSgbFourPlayers = 0; + gbSgbMultiplayer = 0; + gbSgbNextController = 0x0f; + } +} + +void gbSgbCommand() +{ + int command = gbSgbPacket[0] >> 3; + // int nPacket = gbSgbPacket[0] & 7; + + switch(command) { + case 0x00: + gbSgbSetPalette(0,1,(u16 *)&gbSgbPacket[1]); + break; + case 0x01: + gbSgbSetPalette(2,3,(u16 *)&gbSgbPacket[1]); + break; + case 0x02: + gbSgbSetPalette(0,3,(u16 *)&gbSgbPacket[1]); + break; + case 0x03: + gbSgbSetPalette(1,2,(u16 *)&gbSgbPacket[1]); + break; + case 0x04: + gbSgbAttributeBlock(); + break; + case 0x05: + gbSgbAttributeLine(); + break; + case 0x06: + gbSgbAttributeDivide(); + break; + case 0x07: + gbSgbAttributeCharacter(); + break; + case 0x0a: + gbSgbSetPalette(); + break; + case 0x0b: + gbSgbScpPalette(); + break; + case 0x11: + gbSgbMultiRequest(); + break; + case 0x13: + gbSgbChrTransfer(); + break; + case 0x14: + gbSgbPicture(); + break; + case 0x15: + gbSgbSetATFList(); + break; + case 0x16: + gbSgbSetATF(gbSgbPacket[1] & 0x3f); + break; + case 0x17: + gbSgbMaskEnable(); + break; + } +} + +void gbSgbResetPacketState() +{ + gbSgbPacketState = GBSGB_NONE; + gbSgbPacketTimeout = 0; +} + +void gbSgbDoBitTransfer(u8 value) +{ + value = value & 0x30; + switch(gbSgbPacketState) { + case GBSGB_NONE: + if(value == 0) { + gbSgbPacketState = GBSGB_RESET; + gbSgbPacketTimeout = GBSGB_PACKET_TIMEOUT; + } else if (value == 0x30) { + if(gbSgbMultiplayer) { + if((gbSgbReadingController & 7) == 7) { + gbSgbReadingController = 0; + if(gbSgbMultiplayer) { + gbSgbNextController--; + if(gbSgbFourPlayers) { + if(gbSgbNextController == 0x0b) + gbSgbNextController = 0x0f; + } else { + if(gbSgbNextController == 0x0d) + gbSgbNextController = 0x0f; + } + } + } else { + gbSgbReadingController &= 3; + } + } + gbSgbPacketTimeout = 0; + } else { + if(value == 0x10) + gbSgbReadingController |= 0x2; + else if(value == 0x20) + gbSgbReadingController |= 0x01; + gbSgbPacketTimeout = 0; + } + gbSgbPacketTimeout = 0; + break; + case GBSGB_RESET: + if(value == 0x30) { + gbSgbPacketState = GBSGB_PACKET_TRANSMIT; + gbSgbPacketByte = 0; + gbSgbPacketNBits = 0; + gbSgbPacketTimeout = GBSGB_PACKET_TIMEOUT; + } else if(value == 0x00) { + gbSgbPacketTimeout = GBSGB_PACKET_TIMEOUT; + gbSgbPacketState = GBSGB_RESET; + } else { + gbSgbPacketState = GBSGB_NONE; + gbSgbPacketTimeout = 0; + } + break; + case GBSGB_PACKET_TRANSMIT: + if(value == 0) { + gbSgbPacketState = GBSGB_RESET; + gbSgbPacketTimeout = 0; + } else if (value == 0x30){ + if(gbSgbPacketNBits == 128) { + gbSgbPacketNBits = 0; + gbSgbPacketByte = 0; + gbSgbPacketNumber++; + gbSgbPacketTimeout = 0; + if(gbSgbPacketNumber == (gbSgbPacket[0] & 7)) { + gbSgbCommand(); + gbSgbPacketNumber = 0; + gbSgbPacketState = GBSGB_NONE; + gbSgbPacketTimeout = 0; + } + } else { + if(gbSgbPacketNBits < 128) { + gbSgbPacket[gbSgbPacketNumber * 16 + gbSgbPacketByte] >>= 1; + gbSgbPacket[gbSgbPacketNumber * 16 + gbSgbPacketByte] |= gbSgbBit; + gbSgbPacketNBits++; + if(!(gbSgbPacketNBits & 7)) { + gbSgbPacketByte++; + } + gbSgbPacketTimeout = GBSGB_PACKET_TIMEOUT; + } + } + } else { + if(value == 0x20) + gbSgbBit = 0x00; + else + gbSgbBit = 0x80; + gbSgbPacketTimeout = GBSGB_PACKET_TIMEOUT; + } + gbSgbReadingController = 0; + break; + default: + gbSgbPacketState = GBSGB_NONE; + gbSgbPacketTimeout = 0; + break; + } +} + +variable_desc gbSgbSaveStruct[] = { + { &gbSgbMask, sizeof(int) }, + { &gbSgbPacketState, sizeof(int) }, + { &gbSgbBit, sizeof(int) }, + { &gbSgbPacketNBits, sizeof(int) }, + { &gbSgbPacketByte, sizeof(int) }, + { &gbSgbPacketNumber, sizeof(int) }, + { &gbSgbMultiplayer, sizeof(int) }, + { &gbSgbNextController, sizeof(u8) }, + { &gbSgbReadingController, sizeof(u8) }, + { NULL, 0 } +}; + +variable_desc gbSgbSaveStructV3[] = { + { &gbSgbMask, sizeof(int) }, + { &gbSgbPacketState, sizeof(int) }, + { &gbSgbBit, sizeof(int) }, + { &gbSgbPacketNBits, sizeof(int) }, + { &gbSgbPacketByte, sizeof(int) }, + { &gbSgbPacketNumber, sizeof(int) }, + { &gbSgbMultiplayer, sizeof(int) }, + { &gbSgbNextController, sizeof(u8) }, + { &gbSgbReadingController, sizeof(u8) }, + { &gbSgbFourPlayers, sizeof(int) }, + { NULL, 0 } +}; + +void gbSgbSaveGame(gzFile gzFile) +{ + utilWriteData(gzFile, gbSgbSaveStructV3); + + utilGzWrite(gzFile, gbSgbBorder, 2048); + utilGzWrite(gzFile, gbSgbBorderChar, 32*256); + + utilGzWrite(gzFile, gbSgbPacket, 16*7); + + utilGzWrite(gzFile, gbSgbSCPPalette, 4 * 512 * sizeof(u16)); + utilGzWrite(gzFile, gbSgbATF, 20 * 18); + utilGzWrite(gzFile, gbSgbATFList, 45 * 20 * 18); +} + +void gbSgbReadGame(gzFile gzFile, int version) +{ + if(version >= 3) + utilReadData(gzFile, gbSgbSaveStructV3); + else { + utilReadData(gzFile, gbSgbSaveStruct); + gbSgbFourPlayers = 0; + } + + if(version >= 8) { + utilGzRead(gzFile, gbSgbBorder, 2048); + utilGzRead(gzFile, gbSgbBorderChar, 32*256); + } + + utilGzRead(gzFile, gbSgbPacket, 16*7); + + utilGzRead(gzFile, gbSgbSCPPalette, 4 * 512 * sizeof(u16)); + utilGzRead(gzFile, gbSgbATF, 20 * 18); + utilGzRead(gzFile, gbSgbATFList, 45 * 20 * 18); +} diff --git a/src/gb/gbSGB.h b/src/gb/gbSGB.h new file mode 100644 index 0000000..00ccbdb --- /dev/null +++ b/src/gb/gbSGB.h @@ -0,0 +1,23 @@ +#ifndef GBSGB_H +#define GBSGB_H + +void gbSgbInit(); +void gbSgbShutdown(); +void gbSgbCommand(); +void gbSgbResetPacketState(); +void gbSgbReset(); +void gbSgbDoBitTransfer(u8); +void gbSgbSaveGame(gzFile); +void gbSgbReadGame(gzFile, int version); +void gbSgbRenderBorder(); + +extern u8 gbSgbATF[20*18]; +extern int gbSgbMode; +extern int gbSgbMask; +extern int gbSgbMultiplayer; +extern u8 gbSgbNextController; +extern int gbSgbPacketTimeout; +extern u8 gbSgbReadingController; +extern int gbSgbFourPlayers; + +#endif // GBSGB_H diff --git a/src/gb/gbSound.cpp b/src/gb/gbSound.cpp new file mode 100644 index 0000000..a1209c6 --- /dev/null +++ b/src/gb/gbSound.cpp @@ -0,0 +1,443 @@ +#include + +#include "../gba/Sound.h" +#include "../Util.h" +#include "gbGlobals.h" +#include "gbSound.h" +#include "gb.h" + +#include "../apu/Gb_Apu.h" +#include "../apu/Effects_Buffer.h" + +extern long soundSampleRate; // current sound quality + +gb_effects_config_t gb_effects_config = { false, 0.20f, 0.15f, false }; + +static gb_effects_config_t gb_effects_config_current; +static Simple_Effects_Buffer* stereo_buffer; +static Gb_Apu* gb_apu; + +static float soundVolume_ = -1; +static int prevSoundEnable = -1; +static bool declicking = false; + +int const chan_count = 4; +int const ticks_to_time = 2 * GB_APU_OVERCLOCK; + +static inline blip_time_t blip_time() +{ + return (SOUND_CLOCK_TICKS - soundTicks) * ticks_to_time; +} + +u8 gbSoundRead( u16 address ) +{ + if ( gb_apu && address >= NR10 && address <= 0xFF3F ) + return gb_apu->read_register( blip_time(), address ); + + return gbMemory[address]; +} + +void gbSoundEvent(register u16 address, register int data) +{ + gbMemory[address] = data; + + if ( gb_apu && address >= NR10 && address <= 0xFF3F ) + gb_apu->write_register( blip_time(), address, data ); +} + +static void end_frame( blip_time_t time ) +{ + gb_apu ->end_frame( time ); + stereo_buffer->end_frame( time ); +} + +static void apply_effects() +{ + prevSoundEnable = soundGetEnable(); + gb_effects_config_current = gb_effects_config; + + stereo_buffer->config().enabled = gb_effects_config_current.enabled; + stereo_buffer->config().echo = gb_effects_config_current.echo; + stereo_buffer->config().stereo = gb_effects_config_current.stereo; + stereo_buffer->config().surround = gb_effects_config_current.surround; + stereo_buffer->apply_config(); + + for ( int i = 0; i < chan_count; i++ ) + { + Multi_Buffer::channel_t ch = { 0, 0, 0 }; + if ( prevSoundEnable >> i & 1 ) + ch = stereo_buffer->channel( i ); + gb_apu->set_output( ch.center, ch.left, ch.right, i ); + } +} + +void gbSoundConfigEffects( gb_effects_config_t const& c ) +{ + gb_effects_config = c; +} + +static void apply_volume() +{ + soundVolume_ = soundGetVolume(); + + if ( gb_apu ) + gb_apu->volume( soundVolume_ ); +} + +void gbSoundTick() +{ + if ( gb_apu && stereo_buffer ) + { + // Run sound hardware to present + end_frame( SOUND_CLOCK_TICKS * ticks_to_time ); + + flush_samples(stereo_buffer); + + // Update effects config if it was changed + if ( memcmp( &gb_effects_config_current, &gb_effects_config, + sizeof gb_effects_config ) || soundGetEnable() != prevSoundEnable ) + apply_effects(); + + if ( soundVolume_ != soundGetVolume() ) + apply_volume(); + } +} + +static void reset_apu() +{ + Gb_Apu::mode_t mode = Gb_Apu::mode_dmg; + if ( gbHardware & 2 ) + mode = Gb_Apu::mode_cgb; + if ( gbHardware & 8 || declicking ) + mode = Gb_Apu::mode_agb; + gb_apu->reset( mode ); + gb_apu->reduce_clicks( declicking ); + + if ( stereo_buffer ) + stereo_buffer->clear(); + + soundTicks = SOUND_CLOCK_TICKS; +} + +static void remake_stereo_buffer() +{ + // APU + if ( !gb_apu ) + { + gb_apu = new Gb_Apu; // TODO: handle errors + reset_apu(); + } + + // Stereo_Buffer + delete stereo_buffer; + stereo_buffer = 0; + + stereo_buffer = new Simple_Effects_Buffer; // TODO: handle out of memory + if ( stereo_buffer->set_sample_rate( soundSampleRate ) ) { } // TODO: handle out of memory + stereo_buffer->clock_rate( gb_apu->clock_rate ); + + // Multi_Buffer + static int const chan_types [chan_count] = { + Multi_Buffer::wave_type+1, Multi_Buffer::wave_type+2, + Multi_Buffer::wave_type+3, Multi_Buffer::mixed_type+1 + }; + if ( stereo_buffer->set_channel_count( chan_count, chan_types ) ) { } // TODO: handle errors + + // Volume Level + apply_effects(); + apply_volume(); +} + +void gbSoundSetDeclicking( bool enable ) +{ + if ( declicking != enable ) + { + declicking = enable; + if ( gb_apu ) + { + // Can't change sound hardware mode without resetting APU, so save/load + // state around mode change + gb_apu_state_t state; + gb_apu->save_state( &state ); + reset_apu(); + if ( gb_apu->load_state( state ) ) { } // ignore error + } + } +} + +bool gbSoundGetDeclicking() +{ + return declicking; +} + +void gbSoundReset() +{ + SOUND_CLOCK_TICKS = 20000; // 1/100 second + + remake_stereo_buffer(); + reset_apu(); + + soundPaused = 1; + + gbSoundEvent(0xff10, 0x80); + gbSoundEvent(0xff11, 0xbf); + gbSoundEvent(0xff12, 0xf3); + gbSoundEvent(0xff14, 0xbf); + gbSoundEvent(0xff16, 0x3f); + gbSoundEvent(0xff17, 0x00); + gbSoundEvent(0xff19, 0xbf); + + gbSoundEvent(0xff1a, 0x7f); + gbSoundEvent(0xff1b, 0xff); + gbSoundEvent(0xff1c, 0xbf); + gbSoundEvent(0xff1e, 0xbf); + + gbSoundEvent(0xff20, 0xff); + gbSoundEvent(0xff21, 0x00); + gbSoundEvent(0xff22, 0x00); + gbSoundEvent(0xff23, 0xbf); + gbSoundEvent(0xff24, 0x77); + gbSoundEvent(0xff25, 0xf3); + + if (gbHardware & 0x4) + gbSoundEvent(0xff26, 0xf0); + else + gbSoundEvent(0xff26, 0xf1); + + /* workaround for game Beetlejuice */ + if (gbHardware & 0x1) { + gbSoundEvent(0xff24, 0x77); + gbSoundEvent(0xff25, 0xf3); + } + + int addr = 0xff30; + + while(addr < 0xff40) { + gbMemory[addr++] = 0x00; + gbMemory[addr++] = 0xff; + } +} + +void gbSoundSetSampleRate( long sampleRate ) +{ + if ( soundSampleRate != sampleRate ) + { + if ( systemCanChangeSoundQuality() ) + { + soundShutdown(); + soundSampleRate = sampleRate; + soundInit(); + } + else + { + soundSampleRate = sampleRate; + } + + remake_stereo_buffer(); + } +} + +static struct { + int version; + gb_apu_state_t apu; +} state; + +static char dummy_state [735 * 2]; + +#define SKIP( type, name ) { dummy_state, sizeof (type) } + +#define LOAD( type, name ) { &name, sizeof (type) } + +// Old save state support + +static variable_desc gbsound_format [] = +{ + SKIP( int, soundPaused ), + SKIP( int, soundPlay ), + SKIP( int, soundTicks ), + SKIP( int, SOUND_CLOCK_TICKS ), + SKIP( int, soundLevel1 ), + SKIP( int, soundLevel2 ), + SKIP( int, soundBalance ), + SKIP( int, soundMasterOn ), + SKIP( int, soundIndex ), + SKIP( int, soundVIN ), + SKIP( int, soundOn [0] ), + SKIP( int, soundATL [0] ), + SKIP( int, sound1Skip ), + SKIP( int, soundIndex [0] ), + SKIP( int, sound1Continue ), + SKIP( int, soundEnvelopeVolume [0] ), + SKIP( int, soundEnvelopeATL [0] ), + SKIP( int, sound1EnvelopeATLReload ), + SKIP( int, sound1EnvelopeUpDown ), + SKIP( int, sound1SweepATL ), + SKIP( int, sound1SweepATLReload ), + SKIP( int, sound1SweepSteps ), + SKIP( int, sound1SweepUpDown ), + SKIP( int, sound1SweepStep ), + SKIP( int, soundOn [1] ), + SKIP( int, soundATL [1] ), + SKIP( int, sound2Skip ), + SKIP( int, soundIndex [1] ), + SKIP( int, sound2Continue ), + SKIP( int, soundEnvelopeVolume [1] ), + SKIP( int, soundEnvelopeATL [1] ), + SKIP( int, sound2EnvelopeATLReload ), + SKIP( int, sound2EnvelopeUpDown ), + SKIP( int, soundOn [2] ), + SKIP( int, soundATL [2] ), + SKIP( int, sound3Skip ), + SKIP( int, soundIndex [2] ), + SKIP( int, sound3Continue ), + SKIP( int, sound3OutputLevel ), + SKIP( int, soundOn [3] ), + SKIP( int, soundATL [3] ), + SKIP( int, sound4Skip ), + SKIP( int, soundIndex [3] ), + SKIP( int, sound4Clock ), + SKIP( int, sound4ShiftRight ), + SKIP( int, sound4ShiftSkip ), + SKIP( int, sound4ShiftIndex ), + SKIP( int, sound4NSteps ), + SKIP( int, sound4CountDown ), + SKIP( int, sound4Continue ), + SKIP( int, soundEnvelopeVolume [2] ), + SKIP( int, soundEnvelopeATL [2] ), + SKIP( int, sound4EnvelopeATLReload ), + SKIP( int, sound4EnvelopeUpDown ), + SKIP( int, soundEnableFlag ), + { NULL, 0 } +}; + +static variable_desc gbsound_format2 [] = +{ + SKIP( int, sound1ATLreload ), + SKIP( int, freq1low ), + SKIP( int, freq1high ), + SKIP( int, sound2ATLreload ), + SKIP( int, freq2low ), + SKIP( int, freq2high ), + SKIP( int, sound3ATLreload ), + SKIP( int, freq3low ), + SKIP( int, freq3high ), + SKIP( int, sound4ATLreload ), + SKIP( int, freq4 ), + { NULL, 0 } +}; + +static variable_desc gbsound_format3 [] = +{ + SKIP( u8[2*735], soundBuffer ), + SKIP( u8[2*735], soundBuffer ), + SKIP( u16[735], soundFinalWave ), + { NULL, 0 } +}; + +enum { + nr10 = 0, + nr11, nr12, nr13, nr14, + nr20, nr21, nr22, nr23, nr24, + nr30, nr31, nr32, nr33, nr34, + nr40, nr41, nr42, nr43, nr44, + nr50, nr51, nr52 +}; + +static void gbSoundReadGameOld(int version,gzFile gzFile) +{ + if ( version == 11 ) + { + // Version 11 didn't save any state + // TODO: same for version 10? + state.apu.regs [nr50] = 0x77; // volume at max + state.apu.regs [nr51] = 0xFF; // channels enabled + state.apu.regs [nr52] = 0x80; // power on + return; + } + + // Load state + utilReadData( gzFile, gbsound_format ); + + if ( version >= 11 ) // TODO: never executed; remove? + utilReadData( gzFile, gbsound_format2 ); + + utilReadData( gzFile, gbsound_format3 ); + + int quality = 1; + if ( version >= 7 ) + quality = utilReadInt( gzFile ); + + gbSoundSetSampleRate( 44100 / quality ); + + // Convert to format Gb_Apu uses + gb_apu_state_t& s = state.apu; + + // Only some registers are properly preserved + static int const regs_to_copy [] = { + nr10, nr11, nr12, nr21, nr22, nr30, nr32, nr42, nr43, nr50, nr51, nr52, -1 + }; + for ( int i = 0; regs_to_copy [i] >= 0; i++ ) + s.regs [regs_to_copy [i]] = gbMemory [0xFF10 + regs_to_copy [i]]; + + memcpy( &s.regs [0x20], &gbMemory [0xFF30], 0x10 ); // wave +} + +// New state format + +static variable_desc gb_state [] = +{ + LOAD( int, state.version ), // room_for_expansion will be used by later versions + + // APU + LOAD( u8 [0x40], state.apu.regs ), // last values written to registers and wave RAM (both banks) + LOAD( int, state.apu.frame_time ), // clocks until next frame sequencer action + LOAD( int, state.apu.frame_phase ), // next step frame sequencer will run + + LOAD( int, state.apu.sweep_freq ), // sweep's internal frequency register + LOAD( int, state.apu.sweep_delay ), // clocks until next sweep action + LOAD( int, state.apu.sweep_enabled ), + LOAD( int, state.apu.sweep_neg ), // obscure internal flag + LOAD( int, state.apu.noise_divider ), + LOAD( int, state.apu.wave_buf ), // last read byte of wave RAM + + LOAD( int [4], state.apu.delay ), // clocks until next channel action + LOAD( int [4], state.apu.length_ctr ), + LOAD( int [4], state.apu.phase ), // square/wave phase, noise LFSR + LOAD( int [4], state.apu.enabled ), // internal enabled flag + + LOAD( int [3], state.apu.env_delay ), // clocks until next envelope action + LOAD( int [3], state.apu.env_volume ), + LOAD( int [3], state.apu.env_enabled ), + + SKIP( int [13], room_for_expansion ), + + // Emulator + SKIP( int [16], room_for_expansion ), + + { NULL, 0 } +}; + +void gbSoundSaveGame( gzFile out ) +{ + gb_apu->save_state( &state.apu ); + + // Be sure areas for expansion get written as zero + memset( dummy_state, 0, sizeof dummy_state ); + + state.version = 1; + utilWriteData( out, gb_state ); +} + +void gbSoundReadGame( int version, gzFile in ) +{ + // Prepare APU and default state + reset_apu(); + gb_apu->save_state( &state.apu ); + + if ( version > 11 ) + utilReadData( in, gb_state ); + else + gbSoundReadGameOld( version, in ); + + gb_apu->load_state( state.apu ); +} diff --git a/src/gb/gbSound.h b/src/gb/gbSound.h new file mode 100644 index 0000000..47ecf2e --- /dev/null +++ b/src/gb/gbSound.h @@ -0,0 +1,76 @@ +#ifndef GBSOUND_H +#define GBSOUND_H + +// GB sound emulation + +// See Sound.h for sound setup/options + +//// GB sound options + +void gbSoundSetSampleRate( long sampleRate ); + +// Manages declicking mode. When enabled, clicks are reduced. Note that clicks +// are normal for GB and GBC sound hardware. +void gbSoundSetDeclicking( bool enable ); +bool gbSoundGetDeclicking(); + +// Effects configuration +struct gb_effects_config_t +{ + bool enabled; // false = disable all effects + + float echo; // 0.0 = none, 1.0 = lots + float stereo; // 0.0 = channels in center, 1.0 = channels on left/right + bool surround; // true = put some channels in back +}; + +// Changes effects configuration +void gbSoundConfigEffects( gb_effects_config_t const& ); +extern gb_effects_config_t gb_effects_config; // current configuration + + +//// GB sound emulation + +// GB sound registers +#define NR10 0xff10 +#define NR11 0xff11 +#define NR12 0xff12 +#define NR13 0xff13 +#define NR14 0xff14 +#define NR21 0xff16 +#define NR22 0xff17 +#define NR23 0xff18 +#define NR24 0xff19 +#define NR30 0xff1a +#define NR31 0xff1b +#define NR32 0xff1c +#define NR33 0xff1d +#define NR34 0xff1e +#define NR41 0xff20 +#define NR42 0xff21 +#define NR43 0xff22 +#define NR44 0xff23 +#define NR50 0xff24 +#define NR51 0xff25 +#define NR52 0xff26 + +// Resets emulated sound hardware +void gbSoundReset(); + +// Emulates write to sound hardware +void gbSoundEvent( u16 address, int data ); +#define SOUND_EVENT gbSoundEvent + +// Emulates read from sound hardware +u8 gbSoundRead( u16 address ); + +// Notifies emulator that SOUND_CLOCK_TICKS clocks have passed +void gbSoundTick(); +extern int SOUND_CLOCK_TICKS; // Number of 16.8 MHz clocks between calls to gbSoundTick() +extern int soundTicks; // Number of 16.8 MHz clocks until gbSoundTick() will be called + +// Saves/loads emulator state +void gbSoundSaveGame( gzFile out ); +void gbSoundReadGame( int version, gzFile in ); + +#endif // GBSOUND_H diff --git a/src/gba/CheatSearch.cpp b/src/gba/CheatSearch.cpp new file mode 100644 index 0000000..fd643d2 --- /dev/null +++ b/src/gba/CheatSearch.cpp @@ -0,0 +1,311 @@ +#include +#include + +#include "CheatSearch.h" + +CheatSearchBlock cheatSearchBlocks[4]; + +CheatSearchData cheatSearchData = { + 0, + cheatSearchBlocks +}; + +static bool cheatSearchEQ(u32 a, u32 b) +{ + return a == b; +} + +static bool cheatSearchNE(u32 a, u32 b) +{ + return a != b; +} + +static bool cheatSearchLT(u32 a, u32 b) +{ + return a < b; +} + +static bool cheatSearchLE(u32 a, u32 b) +{ + return a <= b; +} + +static bool cheatSearchGT(u32 a, u32 b) +{ + return a > b; +} + +static bool cheatSearchGE(u32 a, u32 b) +{ + return a >= b; +} + +static bool cheatSearchSignedEQ(s32 a, s32 b) +{ + return a == b; +} + +static bool cheatSearchSignedNE(s32 a, s32 b) +{ + return a != b; +} + +static bool cheatSearchSignedLT(s32 a, s32 b) +{ + return a < b; +} + +static bool cheatSearchSignedLE(s32 a, s32 b) +{ + return a <= b; +} + +static bool cheatSearchSignedGT(s32 a, s32 b) +{ + return a > b; +} + +static bool cheatSearchSignedGE(s32 a, s32 b) +{ + return a >= b; +} + +static bool (*cheatSearchFunc[])(u32,u32) = { + cheatSearchEQ, + cheatSearchNE, + cheatSearchLT, + cheatSearchLE, + cheatSearchGT, + cheatSearchGE +}; + +static bool (*cheatSearchSignedFunc[])(s32,s32) = { + cheatSearchSignedEQ, + cheatSearchSignedNE, + cheatSearchSignedLT, + cheatSearchSignedLE, + cheatSearchSignedGT, + cheatSearchSignedGE +}; + +void cheatSearchCleanup(CheatSearchData *cs) +{ + int count = cs->count; + + for(int i = 0; i < count; i++) { + free(cs->blocks[i].saved); + free(cs->blocks[i].bits); + } + cs->count = 0; +} + +void cheatSearchStart(const CheatSearchData *cs) +{ + int count = cs->count; + + for(int i = 0; i < count; i++) { + CheatSearchBlock *block = &cs->blocks[i]; + + memset(block->bits, 0xff, block->size >> 3); + memcpy(block->saved, block->data, block->size); + } +} + +s32 cheatSearchSignedRead(u8 *data, int off, int size) +{ + u32 res = data[off++]; + + switch(size) { + case BITS_8: + res <<= 24; + return ((s32)res) >> 24; + case BITS_16: + res |= ((u32)data[off++])<<8; + res <<= 16; + return ((s32)res) >> 16; + case BITS_32: + res |= ((u32)data[off++])<<8; + res |= ((u32)data[off++])<<16; + res |= ((u32)data[off++])<<24; + return (s32)res; + } + return (s32)res; +} + +u32 cheatSearchRead(u8 *data, int off, int size) +{ + u32 res = data[off++]; + if(size == BITS_16) + res |= ((u32)data[off++])<<8; + else if(size == BITS_32) { + res |= ((u32)data[off++])<<8; + res |= ((u32)data[off++])<<16; + res |= ((u32)data[off++])<<24; + } + return res; +} + +void cheatSearch(const CheatSearchData *cs, int compare, int size, + bool isSigned) +{ + if(compare < 0 || compare > SEARCH_GE) + return; + int inc = 1; + if(size == BITS_16) + inc = 2; + else if(size == BITS_32) + inc = 4; + + if(isSigned) { + bool (*func)(s32,s32) = cheatSearchSignedFunc[compare]; + + for(int i = 0; i < cs->count; i++) { + CheatSearchBlock *block = &cs->blocks[i]; + int size2 = block->size; + u8 *bits = block->bits; + u8 *data = block->data; + u8 *saved = block->saved; + + for(int j = 0; j < size2; j += inc) { + if(IS_BIT_SET(bits, j)) { + s32 a = cheatSearchSignedRead(data, j, size); + s32 b = cheatSearchSignedRead(saved,j, size); + + if(!func(a, b)) { + CLEAR_BIT(bits, j); + if(size == BITS_16) + CLEAR_BIT(bits, j+1); + if(size == BITS_32) { + CLEAR_BIT(bits, j+2); + CLEAR_BIT(bits, j+3); + } + } + } + } + } + } else { + bool (*func)(u32,u32) = cheatSearchFunc[compare]; + + for(int i = 0; i < cs->count; i++) { + CheatSearchBlock *block = &cs->blocks[i]; + int size2 = block->size; + u8 *bits = block->bits; + u8 *data = block->data; + u8 *saved = block->saved; + + for(int j = 0; j < size2; j += inc) { + if(IS_BIT_SET(bits, j)) { + u32 a = cheatSearchRead(data, j, size); + u32 b = cheatSearchRead(saved,j, size); + + if(!func(a, b)) { + CLEAR_BIT(bits, j); + if(size == BITS_16) + CLEAR_BIT(bits, j+1); + if(size == BITS_32) { + CLEAR_BIT(bits, j+2); + CLEAR_BIT(bits, j+3); + } + } + } + } + } + } +} + +void cheatSearchValue(const CheatSearchData *cs, int compare, int size, + bool isSigned, u32 value) +{ + if(compare < 0 || compare > SEARCH_GE) + return; + int inc = 1; + if(size == BITS_16) + inc = 2; + else if(size == BITS_32) + inc = 4; + + if(isSigned) { + bool (*func)(s32,s32) = cheatSearchSignedFunc[compare]; + + for(int i = 0; i < cs->count; i++) { + CheatSearchBlock *block = &cs->blocks[i]; + int size2 = block->size; + u8 *bits = block->bits; + u8 *data = block->data; + + for(int j = 0; j < size2; j += inc) { + if(IS_BIT_SET(bits, j)) { + s32 a = cheatSearchSignedRead(data, j, size); + s32 b = (s32)value; + + if(!func(a, b)) { + CLEAR_BIT(bits, j); + if(size == BITS_16) + CLEAR_BIT(bits, j+1); + if(size == BITS_32) { + CLEAR_BIT(bits, j+2); + CLEAR_BIT(bits, j+3); + } + } + } + } + } + } else { + bool (*func)(u32,u32) = cheatSearchFunc[compare]; + + for(int i = 0; i < cs->count; i++) { + CheatSearchBlock *block = &cs->blocks[i]; + int size2 = block->size; + u8 *bits = block->bits; + u8 *data = block->data; + + for(int j = 0; j < size2; j += inc) { + if(IS_BIT_SET(bits, j)) { + u32 a = cheatSearchRead(data, j, size); + + if(!func(a, value)) { + CLEAR_BIT(bits, j); + if(size == BITS_16) + CLEAR_BIT(bits, j+1); + if(size == BITS_32) { + CLEAR_BIT(bits, j+2); + CLEAR_BIT(bits, j+3); + } + } + } + } + } + } +} + +int cheatSearchGetCount(const CheatSearchData *cs, int size) +{ + int res = 0; + int inc = 1; + if(size == BITS_16) + inc = 2; + else if(size == BITS_32) + inc = 4; + + for(int i = 0; i < cs->count; i++) { + CheatSearchBlock *block = &cs->blocks[i]; + + int size2 = block->size; + u8 *bits = block->bits; + for(int j = 0; j < size2; j += inc) { + if(IS_BIT_SET(bits, j)) + res++; + } + } + return res; +} + +void cheatSearchUpdateValues(const CheatSearchData *cs) +{ + for(int i = 0; i < cs->count; i++) { + CheatSearchBlock *block = &cs->blocks[i]; + + memcpy(block->saved, block->data, block->size); + } +} + diff --git a/src/gba/CheatSearch.h b/src/gba/CheatSearch.h new file mode 100644 index 0000000..4f6cccd --- /dev/null +++ b/src/gba/CheatSearch.h @@ -0,0 +1,54 @@ +#ifndef CHEATSEARCH_H +#define CHEATSEARCH_H + +#include "../System.h" + +struct CheatSearchBlock { + int size; + u32 offset; + u8 *bits; + u8 *data; + u8 *saved; +}; + +struct CheatSearchData { + int count; + CheatSearchBlock *blocks; +}; + +enum { + SEARCH_EQ, + SEARCH_NE, + SEARCH_LT, + SEARCH_LE, + SEARCH_GT, + SEARCH_GE +}; + +enum { + BITS_8, + BITS_16, + BITS_32 +}; + +#define SET_BIT(bits,off) \ + (bits)[(off) >> 3] |= (1 << ((off) & 7)) + +#define CLEAR_BIT(bits, off) \ + (bits)[(off) >> 3] &= ~(1 << ((off) & 7)) + +#define IS_BIT_SET(bits, off) \ + (bits)[(off) >> 3] & (1 << ((off) & 7)) + +extern CheatSearchData cheatSearchData; + +void cheatSearchCleanup(CheatSearchData *cs); +void cheatSearchStart(const CheatSearchData *cs); +void cheatSearch(const CheatSearchData *cs, int compare, int size, bool isSigned); +void cheatSearchValue(const CheatSearchData *cs, int compare, int size, bool isSigned, u32 value); +int cheatSearchGetCount(const CheatSearchData *cs, int size); +void cheatSearchUpdateValues(const CheatSearchData *cs); +s32 cheatSearchSignedRead(u8 *data, int off, int size); +u32 cheatSearchRead(u8 *data, int off, int size); + +#endif // CHEATSEARCH_H diff --git a/src/gba/Cheats.cpp b/src/gba/Cheats.cpp new file mode 100644 index 0000000..0288800 --- /dev/null +++ b/src/gba/Cheats.cpp @@ -0,0 +1,2900 @@ +#ifndef __LIBRETRO__ +#include +#include +#include +#include + +#include "GBA.h" +#include "GBAinline.h" +#include "Cheats.h" +#include "Globals.h" +#include "../NLS.h" +#include "../Util.h" + +/** + * Gameshark code types: (based on AR v1.0) + * + * NNNNNNNN 001DC0DE - ID code for the game (game 4 character name) from ROM + * DEADFACE XXXXXXXX - changes decryption seeds // Not supported by VBA. + * 0AAAAAAA 000000YY - 8-bit constant write + * 1AAAAAAA 0000YYYY - 16-bit constant write + * 2AAAAAAA YYYYYYYY - 32-bit constant write + * 30XXAAAA YYYYYYYY - 32bit Group Write, 8/16/32bit Sub/Add (depending on the XX value). + * 6AAAAAAA Z000YYYY - 16-bit ROM Patch (address >> 1). Z selects the Rom Patching register. + * - AR v1/2 hardware only supports Z=0. + * - AR v3 hardware should support Z=0,1,2 or 3. + * 8A1AAAAA 000000YY - 8-bit button write + * 8A2AAAAA 0000YYYY - 16-bit button write + * 8A4AAAAA YYYYYYYY - 32-bit button write // BUGGY ! Only writes 00000000 on the AR v1.0. + * 80F00000 0000YYYY - button slow motion + * DAAAAAAA 00Z0YYYY - Z = 0 : if 16-bit value at address != YYYY skip next line + * - Z = 1 : if 16-bit value at address == YYYY skip next line + * - Z = 2 : if 16-bit value at address > YYYY (Unsigned) skip next line + * - Z = 3 : if 16-bit value at address < YYYY (Unsigned) skip next line + * E0CCYYYY ZAAAAAAA - Z = 0 : if 16-bit value at address != YYYY skip CC lines + * - Z = 1 : if 16-bit value at address == YYYY skip CC lines + * - Z = 2 : if 16-bit value at address > YYYY (Unsigned) skip CC lines + * - Z = 3 : if 16-bit value at address < YYYY (Unsigned) skip CC lines + * FAAAAAAA 0000YYYY - Master code function + * + * + * + * CodeBreaker codes types: (based on the CBA clone "Cheatcode S" v1.1) + * + * 0000AAAA 000Y - Game CRC (Y are flags: 8 - CRC, 2 - DI) + * 1AAAAAAA YYYY - Master Code function (store address at ((YYYY << 0x16) + * + 0x08000100)) + * 2AAAAAAA YYYY - 16-bit or + * 3AAAAAAA YYYY - 8-bit constant write + * 4AAAAAAA YYYY - Slide code + * XXXXCCCC IIII (C is count and I is address increment, X is value incr.) + * 5AAAAAAA CCCC - Super code (Write bytes to address, 2*CCCC is count) + * BBBBBBBB BBBB + * 6AAAAAAA YYYY - 16-bit and + * 7AAAAAAA YYYY - if address contains 16-bit value enable next code + * 8AAAAAAA YYYY - 16-bit constant write + * 9AAAAAAA YYYY - change decryption (when first code only?) + * AAAAAAAA YYYY - if address does not contain 16-bit value enable next code + * BAAAAAAA YYYY - if 16-bit value at address <= YYYY skip next code + * CAAAAAAA YYYY - if 16-bit value at address >= YYYY skip next code + * D00000X0 YYYY - if button keys ... enable next code (else skip next code) + * EAAAAAAA YYYY - increase 16/32bit value stored in address + * FAAAAAAA YYYY - if 16-bit value at address AND YYYY = 0 then skip next code + **/ + +#define UNKNOWN_CODE -1 +#define INT_8_BIT_WRITE 0 +#define INT_16_BIT_WRITE 1 +#define INT_32_BIT_WRITE 2 +#define GSA_16_BIT_ROM_PATCH 3 +#define GSA_8_BIT_GS_WRITE 4 +#define GSA_16_BIT_GS_WRITE 5 +#define GSA_32_BIT_GS_WRITE 6 +#define CBA_IF_KEYS_PRESSED 7 +#define CBA_IF_TRUE 8 +#define CBA_SLIDE_CODE 9 +#define CBA_IF_FALSE 10 +#define CBA_AND 11 +#define GSA_8_BIT_GS_WRITE2 12 +#define GSA_16_BIT_GS_WRITE2 13 +#define GSA_32_BIT_GS_WRITE2 14 +#define GSA_16_BIT_ROM_PATCH2C 15 +#define GSA_8_BIT_SLIDE 16 +#define GSA_16_BIT_SLIDE 17 +#define GSA_32_BIT_SLIDE 18 +#define GSA_8_BIT_IF_TRUE 19 +#define GSA_32_BIT_IF_TRUE 20 +#define GSA_8_BIT_IF_FALSE 21 +#define GSA_32_BIT_IF_FALSE 22 +#define GSA_8_BIT_FILL 23 +#define GSA_16_BIT_FILL 24 +#define GSA_8_BIT_IF_TRUE2 25 +#define GSA_16_BIT_IF_TRUE2 26 +#define GSA_32_BIT_IF_TRUE2 27 +#define GSA_8_BIT_IF_FALSE2 28 +#define GSA_16_BIT_IF_FALSE2 29 +#define GSA_32_BIT_IF_FALSE2 30 +#define GSA_SLOWDOWN 31 +#define CBA_ADD 32 +#define CBA_OR 33 +#define CBA_LT 34 +#define CBA_GT 35 +#define CBA_SUPER 36 +#define GSA_8_BIT_POINTER 37 +#define GSA_16_BIT_POINTER 38 +#define GSA_32_BIT_POINTER 39 +#define GSA_8_BIT_ADD 40 +#define GSA_16_BIT_ADD 41 +#define GSA_32_BIT_ADD 42 +#define GSA_8_BIT_IF_LOWER_U 43 +#define GSA_16_BIT_IF_LOWER_U 44 +#define GSA_32_BIT_IF_LOWER_U 45 +#define GSA_8_BIT_IF_HIGHER_U 46 +#define GSA_16_BIT_IF_HIGHER_U 47 +#define GSA_32_BIT_IF_HIGHER_U 48 +#define GSA_8_BIT_IF_AND 49 +#define GSA_16_BIT_IF_AND 50 +#define GSA_32_BIT_IF_AND 51 +#define GSA_8_BIT_IF_LOWER_U2 52 +#define GSA_16_BIT_IF_LOWER_U2 53 +#define GSA_32_BIT_IF_LOWER_U2 54 +#define GSA_8_BIT_IF_HIGHER_U2 55 +#define GSA_16_BIT_IF_HIGHER_U2 56 +#define GSA_32_BIT_IF_HIGHER_U2 57 +#define GSA_8_BIT_IF_AND2 58 +#define GSA_16_BIT_IF_AND2 59 +#define GSA_32_BIT_IF_AND2 60 +#define GSA_ALWAYS 61 +#define GSA_ALWAYS2 62 +#define GSA_8_BIT_IF_LOWER_S 63 +#define GSA_16_BIT_IF_LOWER_S 64 +#define GSA_32_BIT_IF_LOWER_S 65 +#define GSA_8_BIT_IF_HIGHER_S 66 +#define GSA_16_BIT_IF_HIGHER_S 67 +#define GSA_32_BIT_IF_HIGHER_S 68 +#define GSA_8_BIT_IF_LOWER_S2 69 +#define GSA_16_BIT_IF_LOWER_S2 70 +#define GSA_32_BIT_IF_LOWER_S2 71 +#define GSA_8_BIT_IF_HIGHER_S2 72 +#define GSA_16_BIT_IF_HIGHER_S2 73 +#define GSA_32_BIT_IF_HIGHER_S2 74 +#define GSA_16_BIT_WRITE_IOREGS 75 +#define GSA_32_BIT_WRITE_IOREGS 76 +#define GSA_CODES_ON 77 +#define GSA_8_BIT_IF_TRUE3 78 +#define GSA_16_BIT_IF_TRUE3 79 +#define GSA_32_BIT_IF_TRUE3 80 +#define GSA_8_BIT_IF_FALSE3 81 +#define GSA_16_BIT_IF_FALSE3 82 +#define GSA_32_BIT_IF_FALSE3 83 +#define GSA_8_BIT_IF_LOWER_S3 84 +#define GSA_16_BIT_IF_LOWER_S3 85 +#define GSA_32_BIT_IF_LOWER_S3 86 +#define GSA_8_BIT_IF_HIGHER_S3 87 +#define GSA_16_BIT_IF_HIGHER_S3 88 +#define GSA_32_BIT_IF_HIGHER_S3 89 +#define GSA_8_BIT_IF_LOWER_U3 90 +#define GSA_16_BIT_IF_LOWER_U3 91 +#define GSA_32_BIT_IF_LOWER_U3 92 +#define GSA_8_BIT_IF_HIGHER_U3 93 +#define GSA_16_BIT_IF_HIGHER_U3 94 +#define GSA_32_BIT_IF_HIGHER_U3 95 +#define GSA_8_BIT_IF_AND3 96 +#define GSA_16_BIT_IF_AND3 97 +#define GSA_32_BIT_IF_AND3 98 +#define GSA_ALWAYS3 99 +#define GSA_16_BIT_ROM_PATCH2D 100 +#define GSA_16_BIT_ROM_PATCH2E 101 +#define GSA_16_BIT_ROM_PATCH2F 102 +#define GSA_GROUP_WRITE 103 +#define GSA_32_BIT_ADD2 104 +#define GSA_32_BIT_SUB2 105 +#define GSA_16_BIT_IF_LOWER_OR_EQ_U 106 +#define GSA_16_BIT_IF_HIGHER_OR_EQ_U 107 +#define GSA_16_BIT_MIF_TRUE 108 +#define GSA_16_BIT_MIF_FALSE 109 +#define GSA_16_BIT_MIF_LOWER_OR_EQ_U 110 +#define GSA_16_BIT_MIF_HIGHER_OR_EQ_U 111 +#define MASTER_CODE 112 +#define CHEATS_16_BIT_WRITE 114 +#define CHEATS_32_BIT_WRITE 115 + +CheatsData cheatsList[100]; +int cheatsNumber = 0; +u32 rompatch2addr [4]; +u16 rompatch2val [4]; +u16 rompatch2oldval [4]; + +u8 cheatsCBASeedBuffer[0x30]; +u32 cheatsCBASeed[4]; +u32 cheatsCBATemporaryValue = 0; +u16 cheatsCBATable[256]; +bool cheatsCBATableGenerated = false; +u16 super = 0; +extern u32 mastercode; + +u8 cheatsCBACurrentSeed[12] = { + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +u32 seeds_v1[4]; +u32 seeds_v3[4]; + +u32 seed_gen(u8 upper, u8 seed, u8 *deadtable1, u8 *deadtable2); + +//seed tables for AR v1 +u8 v1_deadtable1[256] = { + 0x31, 0x1C, 0x23, 0xE5, 0x89, 0x8E, 0xA1, 0x37, 0x74, 0x6D, 0x67, 0xFC, 0x1F, 0xC0, 0xB1, 0x94, + 0x3B, 0x05, 0x56, 0x86, 0x00, 0x24, 0xF0, 0x17, 0x72, 0xA2, 0x3D, 0x1B, 0xE3, 0x17, 0xC5, 0x0B, + 0xB9, 0xE2, 0xBD, 0x58, 0x71, 0x1B, 0x2C, 0xFF, 0xE4, 0xC9, 0x4C, 0x5E, 0xC9, 0x55, 0x33, 0x45, + 0x7C, 0x3F, 0xB2, 0x51, 0xFE, 0x10, 0x7E, 0x75, 0x3C, 0x90, 0x8D, 0xDA, 0x94, 0x38, 0xC3, 0xE9, + 0x95, 0xEA, 0xCE, 0xA6, 0x06, 0xE0, 0x4F, 0x3F, 0x2A, 0xE3, 0x3A, 0xE4, 0x43, 0xBD, 0x7F, 0xDA, + 0x55, 0xF0, 0xEA, 0xCB, 0x2C, 0xA8, 0x47, 0x61, 0xA0, 0xEF, 0xCB, 0x13, 0x18, 0x20, 0xAF, 0x3E, + 0x4D, 0x9E, 0x1E, 0x77, 0x51, 0xC5, 0x51, 0x20, 0xCF, 0x21, 0xF9, 0x39, 0x94, 0xDE, 0xDD, 0x79, + 0x4E, 0x80, 0xC4, 0x9D, 0x94, 0xD5, 0x95, 0x01, 0x27, 0x27, 0xBD, 0x6D, 0x78, 0xB5, 0xD1, 0x31, + 0x6A, 0x65, 0x74, 0x74, 0x58, 0xB3, 0x7C, 0xC9, 0x5A, 0xED, 0x50, 0x03, 0xC4, 0xA2, 0x94, 0x4B, + 0xF0, 0x58, 0x09, 0x6F, 0x3E, 0x7D, 0xAE, 0x7D, 0x58, 0xA0, 0x2C, 0x91, 0xBB, 0xE1, 0x70, 0xEB, + 0x73, 0xA6, 0x9A, 0x44, 0x25, 0x90, 0x16, 0x62, 0x53, 0xAE, 0x08, 0xEB, 0xDC, 0xF0, 0xEE, 0x77, + 0xC2, 0xDE, 0x81, 0xE8, 0x30, 0x89, 0xDB, 0xFE, 0xBC, 0xC2, 0xDF, 0x26, 0xE9, 0x8B, 0xD6, 0x93, + 0xF0, 0xCB, 0x56, 0x90, 0xC0, 0x46, 0x68, 0x15, 0x43, 0xCB, 0xE9, 0x98, 0xE3, 0xAF, 0x31, 0x25, + 0x4D, 0x7B, 0xF3, 0xB1, 0x74, 0xE2, 0x64, 0xAC, 0xD9, 0xF6, 0xA0, 0xD5, 0x0B, 0x9B, 0x49, 0x52, + 0x69, 0x3B, 0x71, 0x00, 0x2F, 0xBB, 0xBA, 0x08, 0xB1, 0xAE, 0xBB, 0xB3, 0xE1, 0xC9, 0xA6, 0x7F, + 0x17, 0x97, 0x28, 0x72, 0x12, 0x6E, 0x91, 0xAE, 0x3A, 0xA2, 0x35, 0x46, 0x27, 0xF8, 0x12, 0x50 +}; +u8 v1_deadtable2[256] = { + 0xD8, 0x65, 0x04, 0xC2, 0x65, 0xD5, 0xB0, 0x0C, 0xDF, 0x9D, 0xF0, 0xC3, 0x9A, 0x17, 0xC9, 0xA6, + 0xE1, 0xAC, 0x0D, 0x14, 0x2F, 0x3C, 0x2C, 0x87, 0xA2, 0xBF, 0x4D, 0x5F, 0xAC, 0x2D, 0x9D, 0xE1, + 0x0C, 0x9C, 0xE7, 0x7F, 0xFC, 0xA8, 0x66, 0x59, 0xAC, 0x18, 0xD7, 0x05, 0xF0, 0xBF, 0xD1, 0x8B, + 0x35, 0x9F, 0x59, 0xB4, 0xBA, 0x55, 0xB2, 0x85, 0xFD, 0xB1, 0x72, 0x06, 0x73, 0xA4, 0xDB, 0x48, + 0x7B, 0x5F, 0x67, 0xA5, 0x95, 0xB9, 0xA5, 0x4A, 0xCF, 0xD1, 0x44, 0xF3, 0x81, 0xF5, 0x6D, 0xF6, + 0x3A, 0xC3, 0x57, 0x83, 0xFA, 0x8E, 0x15, 0x2A, 0xA2, 0x04, 0xB2, 0x9D, 0xA8, 0x0D, 0x7F, 0xB8, + 0x0F, 0xF6, 0xAC, 0xBE, 0x97, 0xCE, 0x16, 0xE6, 0x31, 0x10, 0x60, 0x16, 0xB5, 0x83, 0x45, 0xEE, + 0xD7, 0x5F, 0x2C, 0x08, 0x58, 0xB1, 0xFD, 0x7E, 0x79, 0x00, 0x34, 0xAD, 0xB5, 0x31, 0x34, 0x39, + 0xAF, 0xA8, 0xDD, 0x52, 0x6A, 0xB0, 0x60, 0x35, 0xB8, 0x1D, 0x52, 0xF5, 0xF5, 0x30, 0x00, 0x7B, + 0xF4, 0xBA, 0x03, 0xCB, 0x3A, 0x84, 0x14, 0x8A, 0x6A, 0xEF, 0x21, 0xBD, 0x01, 0xD8, 0xA0, 0xD4, + 0x43, 0xBE, 0x23, 0xE7, 0x76, 0x27, 0x2C, 0x3F, 0x4D, 0x3F, 0x43, 0x18, 0xA7, 0xC3, 0x47, 0xA5, + 0x7A, 0x1D, 0x02, 0x55, 0x09, 0xD1, 0xFF, 0x55, 0x5E, 0x17, 0xA0, 0x56, 0xF4, 0xC9, 0x6B, 0x90, + 0xB4, 0x80, 0xA5, 0x07, 0x22, 0xFB, 0x22, 0x0D, 0xD9, 0xC0, 0x5B, 0x08, 0x35, 0x05, 0xC1, 0x75, + 0x4F, 0xD0, 0x51, 0x2D, 0x2E, 0x5E, 0x69, 0xE7, 0x3B, 0xC2, 0xDA, 0xFF, 0xF6, 0xCE, 0x3E, 0x76, + 0xE8, 0x36, 0x8C, 0x39, 0xD8, 0xF3, 0xE9, 0xA6, 0x42, 0xE6, 0xC1, 0x4C, 0x05, 0xBE, 0x17, 0xF2, + 0x5C, 0x1B, 0x19, 0xDB, 0x0F, 0xF3, 0xF8, 0x49, 0xEB, 0x36, 0xF6, 0x40, 0x6F, 0xAD, 0xC1, 0x8C +}; + +//seed tables for AR v3 +u8 v3_deadtable1[256] = { + 0xD0, 0xFF, 0xBA, 0xE5, 0xC1, 0xC7, 0xDB, 0x5B, 0x16, 0xE3, 0x6E, 0x26, 0x62, 0x31, 0x2E, 0x2A, + 0xD1, 0xBB, 0x4A, 0xE6, 0xAE, 0x2F, 0x0A, 0x90, 0x29, 0x90, 0xB6, 0x67, 0x58, 0x2A, 0xB4, 0x45, + 0x7B, 0xCB, 0xF0, 0x73, 0x84, 0x30, 0x81, 0xC2, 0xD7, 0xBE, 0x89, 0xD7, 0x4E, 0x73, 0x5C, 0xC7, + 0x80, 0x1B, 0xE5, 0xE4, 0x43, 0xC7, 0x46, 0xD6, 0x6F, 0x7B, 0xBF, 0xED, 0xE5, 0x27, 0xD1, 0xB5, + 0xD0, 0xD8, 0xA3, 0xCB, 0x2B, 0x30, 0xA4, 0xF0, 0x84, 0x14, 0x72, 0x5C, 0xFF, 0xA4, 0xFB, 0x54, + 0x9D, 0x70, 0xE2, 0xFF, 0xBE, 0xE8, 0x24, 0x76, 0xE5, 0x15, 0xFB, 0x1A, 0xBC, 0x87, 0x02, 0x2A, + 0x58, 0x8F, 0x9A, 0x95, 0xBD, 0xAE, 0x8D, 0x0C, 0xA5, 0x4C, 0xF2, 0x5C, 0x7D, 0xAD, 0x51, 0xFB, + 0xB1, 0x22, 0x07, 0xE0, 0x29, 0x7C, 0xEB, 0x98, 0x14, 0xC6, 0x31, 0x97, 0xE4, 0x34, 0x8F, 0xCC, + 0x99, 0x56, 0x9F, 0x78, 0x43, 0x91, 0x85, 0x3F, 0xC2, 0xD0, 0xD1, 0x80, 0xD1, 0x77, 0xA7, 0xE2, + 0x43, 0x99, 0x1D, 0x2F, 0x8B, 0x6A, 0xE4, 0x66, 0x82, 0xF7, 0x2B, 0x0B, 0x65, 0x14, 0xC0, 0xC2, + 0x1D, 0x96, 0x78, 0x1C, 0xC4, 0xC3, 0xD2, 0xB1, 0x64, 0x07, 0xD7, 0x6F, 0x02, 0xE9, 0x44, 0x31, + 0xDB, 0x3C, 0xEB, 0x93, 0xED, 0x9A, 0x57, 0x05, 0xB9, 0x0E, 0xAF, 0x1F, 0x48, 0x11, 0xDC, 0x35, + 0x6C, 0xB8, 0xEE, 0x2A, 0x48, 0x2B, 0xBC, 0x89, 0x12, 0x59, 0xCB, 0xD1, 0x18, 0xEA, 0x72, 0x11, + 0x01, 0x75, 0x3B, 0xB5, 0x56, 0xF4, 0x8B, 0xA0, 0x41, 0x75, 0x86, 0x7B, 0x94, 0x12, 0x2D, 0x4C, + 0x0C, 0x22, 0xC9, 0x4A, 0xD8, 0xB1, 0x8D, 0xF0, 0x55, 0x2E, 0x77, 0x50, 0x1C, 0x64, 0x77, 0xAA, + 0x3E, 0xAC, 0xD3, 0x3D, 0xCE, 0x60, 0xCA, 0x5D, 0xA0, 0x92, 0x78, 0xC6, 0x51, 0xFE, 0xF9, 0x30 +}; +u8 v3_deadtable2[256] = { + 0xAA, 0xAF, 0xF0, 0x72, 0x90, 0xF7, 0x71, 0x27, 0x06, 0x11, 0xEB, 0x9C, 0x37, 0x12, 0x72, 0xAA, + 0x65, 0xBC, 0x0D, 0x4A, 0x76, 0xF6, 0x5C, 0xAA, 0xB0, 0x7A, 0x7D, 0x81, 0xC1, 0xCE, 0x2F, 0x9F, + 0x02, 0x75, 0x38, 0xC8, 0xFC, 0x66, 0x05, 0xC2, 0x2C, 0xBD, 0x91, 0xAD, 0x03, 0xB1, 0x88, 0x93, + 0x31, 0xC6, 0xAB, 0x40, 0x23, 0x43, 0x76, 0x54, 0xCA, 0xE7, 0x00, 0x96, 0x9F, 0xD8, 0x24, 0x8B, + 0xE4, 0xDC, 0xDE, 0x48, 0x2C, 0xCB, 0xF7, 0x84, 0x1D, 0x45, 0xE5, 0xF1, 0x75, 0xA0, 0xED, 0xCD, + 0x4B, 0x24, 0x8A, 0xB3, 0x98, 0x7B, 0x12, 0xB8, 0xF5, 0x63, 0x97, 0xB3, 0xA6, 0xA6, 0x0B, 0xDC, + 0xD8, 0x4C, 0xA8, 0x99, 0x27, 0x0F, 0x8F, 0x94, 0x63, 0x0F, 0xB0, 0x11, 0x94, 0xC7, 0xE9, 0x7F, + 0x3B, 0x40, 0x72, 0x4C, 0xDB, 0x84, 0x78, 0xFE, 0xB8, 0x56, 0x08, 0x80, 0xDF, 0x20, 0x2F, 0xB9, + 0x66, 0x2D, 0x60, 0x63, 0xF5, 0x18, 0x15, 0x1B, 0x86, 0x85, 0xB9, 0xB4, 0x68, 0x0E, 0xC6, 0xD1, + 0x8A, 0x81, 0x2B, 0xB3, 0xF6, 0x48, 0xF0, 0x4F, 0x9C, 0x28, 0x1C, 0xA4, 0x51, 0x2F, 0xD7, 0x4B, + 0x17, 0xE7, 0xCC, 0x50, 0x9F, 0xD0, 0xD1, 0x40, 0x0C, 0x0D, 0xCA, 0x83, 0xFA, 0x5E, 0xCA, 0xEC, + 0xBF, 0x4E, 0x7C, 0x8F, 0xF0, 0xAE, 0xC2, 0xD3, 0x28, 0x41, 0x9B, 0xC8, 0x04, 0xB9, 0x4A, 0xBA, + 0x72, 0xE2, 0xB5, 0x06, 0x2C, 0x1E, 0x0B, 0x2C, 0x7F, 0x11, 0xA9, 0x26, 0x51, 0x9D, 0x3F, 0xF8, + 0x62, 0x11, 0x2E, 0x89, 0xD2, 0x9D, 0x35, 0xB1, 0xE4, 0x0A, 0x4D, 0x93, 0x01, 0xA7, 0xD1, 0x2D, + 0x00, 0x87, 0xE2, 0x2D, 0xA4, 0xE9, 0x0A, 0x06, 0x66, 0xF8, 0x1F, 0x44, 0x75, 0xB5, 0x6B, 0x1C, + 0xFC, 0x31, 0x09, 0x48, 0xA3, 0xFF, 0x92, 0x12, 0x58, 0xE9, 0xFA, 0xAE, 0x4F, 0xE2, 0xB4, 0xCC +}; + +#define debuggerReadMemory(addr) \ + READ32LE((&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])) + +#define debuggerReadHalfWord(addr) \ + READ16LE((&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])) + +#define debuggerReadByte(addr) \ + map[(addr)>>24].address[(addr) & map[(addr)>>24].mask] + +#define debuggerWriteMemory(addr, value) \ + WRITE32LE(&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask], value) + +#define debuggerWriteHalfWord(addr, value) \ + WRITE16LE(&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask], value) + +#define debuggerWriteByte(addr, value) \ + map[(addr)>>24].address[(addr) & map[(addr)>>24].mask] = (value) + + +#define CHEAT_IS_HEX(a) ( ((a)>='A' && (a) <='F') || ((a) >='0' && (a) <= '9')) + +#define CHEAT_PATCH_ROM_16BIT(a,v) \ + WRITE16LE(((u16 *)&rom[(a) & 0x1ffffff]), v); + +#define CHEAT_PATCH_ROM_32BIT(a,v) \ + WRITE32LE(((u32 *)&rom[(a) & 0x1ffffff]), v); + +static bool isMultilineWithData(int i) +{ + // we consider it a multiline code if it has more than one line of data + // otherwise, it can still be considered a single code + // (Only CBA codes can be true multilines !!!) + if(i < cheatsNumber && i >= 0) + switch(cheatsList[i].size) { + case INT_8_BIT_WRITE: + case INT_16_BIT_WRITE: + case INT_32_BIT_WRITE: + case GSA_16_BIT_ROM_PATCH: + case GSA_8_BIT_GS_WRITE: + case GSA_16_BIT_GS_WRITE: + case GSA_32_BIT_GS_WRITE: + case CBA_AND: + case CBA_IF_KEYS_PRESSED: + case CBA_IF_TRUE: + case CBA_IF_FALSE: + case GSA_8_BIT_IF_TRUE: + case GSA_32_BIT_IF_TRUE: + case GSA_8_BIT_IF_FALSE: + case GSA_32_BIT_IF_FALSE: + case GSA_8_BIT_FILL: + case GSA_16_BIT_FILL: + case GSA_8_BIT_IF_TRUE2: + case GSA_16_BIT_IF_TRUE2: + case GSA_32_BIT_IF_TRUE2: + case GSA_8_BIT_IF_FALSE2: + case GSA_16_BIT_IF_FALSE2: + case GSA_32_BIT_IF_FALSE2: + case GSA_SLOWDOWN: + case CBA_ADD: + case CBA_OR: + case CBA_LT: + case CBA_GT: + case GSA_8_BIT_POINTER: + case GSA_16_BIT_POINTER: + case GSA_32_BIT_POINTER: + case GSA_8_BIT_ADD: + case GSA_16_BIT_ADD: + case GSA_32_BIT_ADD: + case GSA_8_BIT_IF_LOWER_U: + case GSA_16_BIT_IF_LOWER_U: + case GSA_32_BIT_IF_LOWER_U: + case GSA_8_BIT_IF_HIGHER_U: + case GSA_16_BIT_IF_HIGHER_U: + case GSA_32_BIT_IF_HIGHER_U: + case GSA_8_BIT_IF_AND: + case GSA_16_BIT_IF_AND: + case GSA_32_BIT_IF_AND: + case GSA_8_BIT_IF_LOWER_U2: + case GSA_16_BIT_IF_LOWER_U2: + case GSA_32_BIT_IF_LOWER_U2: + case GSA_8_BIT_IF_HIGHER_U2: + case GSA_16_BIT_IF_HIGHER_U2: + case GSA_32_BIT_IF_HIGHER_U2: + case GSA_8_BIT_IF_AND2: + case GSA_16_BIT_IF_AND2: + case GSA_32_BIT_IF_AND2: + case GSA_ALWAYS: + case GSA_ALWAYS2: + case GSA_8_BIT_IF_LOWER_S: + case GSA_16_BIT_IF_LOWER_S: + case GSA_32_BIT_IF_LOWER_S: + case GSA_8_BIT_IF_HIGHER_S: + case GSA_16_BIT_IF_HIGHER_S: + case GSA_32_BIT_IF_HIGHER_S: + case GSA_8_BIT_IF_LOWER_S2: + case GSA_16_BIT_IF_LOWER_S2: + case GSA_32_BIT_IF_LOWER_S2: + case GSA_8_BIT_IF_HIGHER_S2: + case GSA_16_BIT_IF_HIGHER_S2: + case GSA_32_BIT_IF_HIGHER_S2: + case GSA_16_BIT_WRITE_IOREGS: + case GSA_32_BIT_WRITE_IOREGS: + case GSA_CODES_ON: + case GSA_8_BIT_IF_TRUE3: + case GSA_16_BIT_IF_TRUE3: + case GSA_32_BIT_IF_TRUE3: + case GSA_8_BIT_IF_FALSE3: + case GSA_16_BIT_IF_FALSE3: + case GSA_32_BIT_IF_FALSE3: + case GSA_8_BIT_IF_LOWER_S3: + case GSA_16_BIT_IF_LOWER_S3: + case GSA_32_BIT_IF_LOWER_S3: + case GSA_8_BIT_IF_HIGHER_S3: + case GSA_16_BIT_IF_HIGHER_S3: + case GSA_32_BIT_IF_HIGHER_S3: + case GSA_8_BIT_IF_LOWER_U3: + case GSA_16_BIT_IF_LOWER_U3: + case GSA_32_BIT_IF_LOWER_U3: + case GSA_8_BIT_IF_HIGHER_U3: + case GSA_16_BIT_IF_HIGHER_U3: + case GSA_32_BIT_IF_HIGHER_U3: + case GSA_8_BIT_IF_AND3: + case GSA_16_BIT_IF_AND3: + case GSA_32_BIT_IF_AND3: + case GSA_ALWAYS3: + case GSA_8_BIT_GS_WRITE2: + case GSA_16_BIT_GS_WRITE2: + case GSA_32_BIT_GS_WRITE2: + case GSA_16_BIT_ROM_PATCH2C: + case GSA_16_BIT_ROM_PATCH2D: + case GSA_16_BIT_ROM_PATCH2E: + case GSA_16_BIT_ROM_PATCH2F: + case GSA_8_BIT_SLIDE: + case GSA_16_BIT_SLIDE: + case GSA_32_BIT_SLIDE: + case GSA_GROUP_WRITE: + case GSA_32_BIT_ADD2: + case GSA_32_BIT_SUB2: + case GSA_16_BIT_IF_LOWER_OR_EQ_U: + case GSA_16_BIT_IF_HIGHER_OR_EQ_U: + case GSA_16_BIT_MIF_TRUE: + case GSA_16_BIT_MIF_FALSE: + case GSA_16_BIT_MIF_LOWER_OR_EQ_U: + case GSA_16_BIT_MIF_HIGHER_OR_EQ_U: + case MASTER_CODE: + case CHEATS_16_BIT_WRITE: + case CHEATS_32_BIT_WRITE: + return false; + // the codes below have two lines of data + case CBA_SLIDE_CODE: + case CBA_SUPER: + return true; + } + return false; +} + +static int getCodeLength(int num) +{ + if(num >= cheatsNumber || num < 0) + return 1; + + // this is for all the codes that are true multiline + switch(cheatsList[num].size) { + case INT_8_BIT_WRITE: + case INT_16_BIT_WRITE: + case INT_32_BIT_WRITE: + case GSA_16_BIT_ROM_PATCH: + case GSA_8_BIT_GS_WRITE: + case GSA_16_BIT_GS_WRITE: + case GSA_32_BIT_GS_WRITE: + case CBA_AND: + case GSA_8_BIT_FILL: + case GSA_16_BIT_FILL: + case GSA_SLOWDOWN: + case CBA_ADD: + case CBA_OR: + case GSA_8_BIT_POINTER: + case GSA_16_BIT_POINTER: + case GSA_32_BIT_POINTER: + case GSA_8_BIT_ADD: + case GSA_16_BIT_ADD: + case GSA_32_BIT_ADD: + case GSA_CODES_ON: + case GSA_8_BIT_IF_TRUE3: + case GSA_16_BIT_IF_TRUE3: + case GSA_32_BIT_IF_TRUE3: + case GSA_8_BIT_IF_FALSE3: + case GSA_16_BIT_IF_FALSE3: + case GSA_32_BIT_IF_FALSE3: + case GSA_8_BIT_IF_LOWER_S3: + case GSA_16_BIT_IF_LOWER_S3: + case GSA_32_BIT_IF_LOWER_S3: + case GSA_8_BIT_IF_HIGHER_S3: + case GSA_16_BIT_IF_HIGHER_S3: + case GSA_32_BIT_IF_HIGHER_S3: + case GSA_8_BIT_IF_LOWER_U3: + case GSA_16_BIT_IF_LOWER_U3: + case GSA_32_BIT_IF_LOWER_U3: + case GSA_8_BIT_IF_HIGHER_U3: + case GSA_16_BIT_IF_HIGHER_U3: + case GSA_32_BIT_IF_HIGHER_U3: + case GSA_8_BIT_IF_AND3: + case GSA_16_BIT_IF_AND3: + case GSA_32_BIT_IF_AND3: + case GSA_8_BIT_IF_LOWER_U: + case GSA_16_BIT_IF_LOWER_U: + case GSA_32_BIT_IF_LOWER_U: + case GSA_8_BIT_IF_HIGHER_U: + case GSA_16_BIT_IF_HIGHER_U: + case GSA_32_BIT_IF_HIGHER_U: + case GSA_8_BIT_IF_AND: + case GSA_16_BIT_IF_AND: + case GSA_32_BIT_IF_AND: + case GSA_ALWAYS: + case GSA_8_BIT_IF_LOWER_S: + case GSA_16_BIT_IF_LOWER_S: + case GSA_32_BIT_IF_LOWER_S: + case GSA_8_BIT_IF_HIGHER_S: + case GSA_16_BIT_IF_HIGHER_S: + case GSA_32_BIT_IF_HIGHER_S: + case GSA_16_BIT_WRITE_IOREGS: + case GSA_32_BIT_WRITE_IOREGS: + case GSA_8_BIT_GS_WRITE2: + case GSA_16_BIT_GS_WRITE2: + case GSA_32_BIT_GS_WRITE2: + case GSA_16_BIT_ROM_PATCH2C: + case GSA_16_BIT_ROM_PATCH2D: + case GSA_16_BIT_ROM_PATCH2E: + case GSA_16_BIT_ROM_PATCH2F: + case GSA_8_BIT_SLIDE: + case GSA_16_BIT_SLIDE: + case GSA_32_BIT_SLIDE: + case GSA_8_BIT_IF_TRUE: + case GSA_32_BIT_IF_TRUE: + case GSA_8_BIT_IF_FALSE: + case GSA_32_BIT_IF_FALSE: + case CBA_LT: + case CBA_GT: + case CBA_IF_TRUE: + case CBA_IF_FALSE: + case GSA_8_BIT_IF_TRUE2: + case GSA_16_BIT_IF_TRUE2: + case GSA_32_BIT_IF_TRUE2: + case GSA_8_BIT_IF_FALSE2: + case GSA_16_BIT_IF_FALSE2: + case GSA_32_BIT_IF_FALSE2: + case GSA_8_BIT_IF_LOWER_U2: + case GSA_16_BIT_IF_LOWER_U2: + case GSA_32_BIT_IF_LOWER_U2: + case GSA_8_BIT_IF_HIGHER_U2: + case GSA_16_BIT_IF_HIGHER_U2: + case GSA_32_BIT_IF_HIGHER_U2: + case GSA_8_BIT_IF_AND2: + case GSA_16_BIT_IF_AND2: + case GSA_32_BIT_IF_AND2: + case GSA_ALWAYS2: + case GSA_8_BIT_IF_LOWER_S2: + case GSA_16_BIT_IF_LOWER_S2: + case GSA_32_BIT_IF_LOWER_S2: + case GSA_8_BIT_IF_HIGHER_S2: + case GSA_16_BIT_IF_HIGHER_S2: + case GSA_32_BIT_IF_HIGHER_S2: + case GSA_GROUP_WRITE: + case GSA_32_BIT_ADD2: + case GSA_32_BIT_SUB2: + case GSA_16_BIT_IF_LOWER_OR_EQ_U: + case GSA_16_BIT_IF_HIGHER_OR_EQ_U: + case GSA_16_BIT_MIF_TRUE: + case GSA_16_BIT_MIF_FALSE: + case GSA_16_BIT_MIF_LOWER_OR_EQ_U: + case GSA_16_BIT_MIF_HIGHER_OR_EQ_U: + case MASTER_CODE: + case CHEATS_16_BIT_WRITE: + case CHEATS_32_BIT_WRITE: + case UNKNOWN_CODE: + return 1; + case CBA_IF_KEYS_PRESSED: + case CBA_SLIDE_CODE: + return 2; + case CBA_SUPER: + return ((((cheatsList[num].value-1) & 0xFFFF)/3) + 1); + } + return 1; +} + +int cheatsCheckKeys(u32 keys, u32 extended) +{ + bool onoff = true; + int ticks = 0; + int i; + mastercode = 0; + + for (i = 0; i<4; i++) + if (rompatch2addr [i] != 0) { + CHEAT_PATCH_ROM_16BIT(rompatch2addr [i],rompatch2oldval [i]); + rompatch2addr [i] = 0; + } + + for (i = 0; i < cheatsNumber; i++) { + if(!cheatsList[i].enabled) { + // make sure we skip other lines in this code + i += getCodeLength(i)-1; + continue; + } + switch(cheatsList[i].size) { + case GSA_CODES_ON: + onoff = true; + break; + case GSA_SLOWDOWN: + // check if button was pressed and released, if so toggle our state + if((cheatsList[i].status & 4) && !(extended & 4)) + cheatsList[i].status ^= 1; + if(extended & 4) + cheatsList[i].status |= 4; + else + cheatsList[i].status &= ~4; + + if(cheatsList[i].status & 1) + ticks += ((cheatsList[i].value & 0xFFFF) * 7); + break; + case GSA_8_BIT_SLIDE: + i++; + if(i < cheatsNumber) { + u32 addr = cheatsList[i-1].value; + u8 value = cheatsList[i].rawaddress; + int vinc = (cheatsList[i].value >> 24) & 255; + int count = (cheatsList[i].value >> 16) & 255; + int ainc = (cheatsList[i].value & 0xffff); + while(count > 0) { + CPUWriteByte(addr, value); + value += vinc; + addr += ainc; + count--; + } + } + break; + case GSA_16_BIT_SLIDE: + i++; + if(i < cheatsNumber) { + u32 addr = cheatsList[i-1].value; + u16 value = cheatsList[i].rawaddress; + int vinc = (cheatsList[i].value >> 24) & 255; + int count = (cheatsList[i].value >> 16) & 255; + int ainc = (cheatsList[i].value & 0xffff)*2; + while(count > 0) { + CPUWriteHalfWord(addr, value); + value += vinc; + addr += ainc; + count--; + } + } + break; + case GSA_32_BIT_SLIDE: + i++; + if(i < cheatsNumber) { + u32 addr = cheatsList[i-1].value; + u32 value = cheatsList[i].rawaddress; + int vinc = (cheatsList[i].value >> 24) & 255; + int count = (cheatsList[i].value >> 16) & 255; + int ainc = (cheatsList[i].value & 0xffff)*4; + while(count > 0) { + CPUWriteMemory(addr, value); + value += vinc; + addr += ainc; + count--; + } + } + break; + case GSA_8_BIT_GS_WRITE2: + i++; + if(i < cheatsNumber) { + if(extended & 4) { + CPUWriteByte(cheatsList[i-1].value, cheatsList[i].address); + } + } + break; + case GSA_16_BIT_GS_WRITE2: + i++; + if(i < cheatsNumber) { + if(extended & 4) { + CPUWriteHalfWord(cheatsList[i-1].value, cheatsList[i].address); + } + } + break; + case GSA_32_BIT_GS_WRITE2: + i++; + if(i < cheatsNumber) { + if(extended & 4) { + CPUWriteMemory(cheatsList[i-1].value, cheatsList[i].address); + } + } + break; + case GSA_16_BIT_ROM_PATCH: + if((cheatsList[i].status & 1) == 0) { + if(CPUReadHalfWord(cheatsList[i].address) != cheatsList[i].value) { + cheatsList[i].oldValue = CPUReadHalfWord(cheatsList[i].address); + cheatsList[i].status |= 1; + CHEAT_PATCH_ROM_16BIT(cheatsList[i].address, cheatsList[i].value); + } + } + break; + case GSA_16_BIT_ROM_PATCH2C: + i++; + if(i < cheatsNumber) { + rompatch2addr [0] = ((cheatsList[i-1].value & 0x00FFFFFF) << 1) + 0x8000000; + rompatch2oldval [0] = CPUReadHalfWord(rompatch2addr [0]); + rompatch2val [0] = cheatsList[i].rawaddress & 0xFFFF; + } + break; + case GSA_16_BIT_ROM_PATCH2D: + i++; + if(i < cheatsNumber) { + rompatch2addr [1] = ((cheatsList[i-1].value & 0x00FFFFFF) << 1) + 0x8000000; + rompatch2oldval [1] = CPUReadHalfWord(rompatch2addr [1]); + rompatch2val [1] = cheatsList[i].rawaddress & 0xFFFF; + } + break; + case GSA_16_BIT_ROM_PATCH2E: + i++; + if(i < cheatsNumber) { + rompatch2addr [2] = ((cheatsList[i-1].value & 0x00FFFFFF) << 1) + 0x8000000; + rompatch2oldval [2] = CPUReadHalfWord(rompatch2addr [2]); + rompatch2val [2] = cheatsList[i].rawaddress & 0xFFFF; + } + break; + case GSA_16_BIT_ROM_PATCH2F: + i++; + if(i < cheatsNumber) { + rompatch2addr [3] = ((cheatsList[i-1].value & 0x00FFFFFF) << 1) + 0x8000000; + rompatch2oldval [3] = CPUReadHalfWord(rompatch2addr [3]); + rompatch2val [3] = cheatsList[i].rawaddress & 0xFFFF; + } + break; + case MASTER_CODE: + mastercode = cheatsList[i].address; + break; + } + if (onoff) { + switch(cheatsList[i].size) { + case INT_8_BIT_WRITE: + CPUWriteByte(cheatsList[i].address, cheatsList[i].value); + break; + case INT_16_BIT_WRITE: + CPUWriteHalfWord(cheatsList[i].address, cheatsList[i].value); + break; + case INT_32_BIT_WRITE: + CPUWriteMemory(cheatsList[i].address, cheatsList[i].value); + break; + case GSA_8_BIT_GS_WRITE: + if(extended & 4) { + CPUWriteByte(cheatsList[i].address, cheatsList[i].value); + } + break; + case GSA_16_BIT_GS_WRITE: + if(extended & 4) { + CPUWriteHalfWord(cheatsList[i].address, cheatsList[i].value); + } + break; + case GSA_32_BIT_GS_WRITE: + if(extended & 4) { + CPUWriteMemory(cheatsList[i].address, cheatsList[i].value); + } + break; + case CBA_IF_KEYS_PRESSED: + { + u16 value = cheatsList[i].value; + u32 addr = cheatsList[i].address; + if((addr & 0xF0) == 0x20) { + if((keys & value) == 0) { + i++; + } + } else if((addr & 0xF0) == 0x10) { + if((keys & value) == value) { + i++; + } + } else if((addr & 0xF0) == 0x00) { + if(((~keys) & 0x3FF) == value) { + i++; + } + } + } + break; + case CBA_IF_TRUE: + if(CPUReadHalfWord(cheatsList[i].address) != cheatsList[i].value) { + i++; + } + break; + case CBA_SLIDE_CODE: + { + u32 address = cheatsList[i].address; + u16 value = cheatsList[i].value; + i++; + if(i < cheatsNumber) { + int count = ((cheatsList[i].address - 1) & 0xFFFF); + u16 vinc = (cheatsList[i].address >> 16) & 0xFFFF; + int inc = cheatsList[i].value; + for(int x = 0; x <= count ; x++) { + CPUWriteHalfWord(address, value); + address += inc; + value += vinc; + } + } + } + break; + case CBA_IF_FALSE: + if(CPUReadHalfWord(cheatsList[i].address) == cheatsList[i].value){ + i++; + } + break; + case CBA_AND: + CPUWriteHalfWord(cheatsList[i].address, + CPUReadHalfWord(cheatsList[i].address) & + cheatsList[i].value); + break; + case GSA_8_BIT_IF_TRUE: + if(CPUReadByte(cheatsList[i].address) != cheatsList[i].value) { + i++; + } + break; + case GSA_32_BIT_IF_TRUE: + if(CPUReadMemory(cheatsList[i].address) != cheatsList[i].value) { + i++; + } + break; + case GSA_8_BIT_IF_FALSE: + if(CPUReadByte(cheatsList[i].address) == cheatsList[i].value) { + i++; + } + break; + case GSA_32_BIT_IF_FALSE: + if(CPUReadMemory(cheatsList[i].address) == cheatsList[i].value) { + i++; + } + break; + case GSA_8_BIT_FILL: + { + u32 addr = cheatsList[i].address; + u8 v = cheatsList[i].value & 0xff; + u32 end = addr + (cheatsList[i].value >> 8); + do { + CPUWriteByte(addr, v); + addr++; + } while (addr <= end); + } + break; + case GSA_16_BIT_FILL: + { + u32 addr = cheatsList[i].address; + u16 v = cheatsList[i].value & 0xffff; + u32 end = addr + ((cheatsList[i].value >> 16) << 1); + do { + CPUWriteHalfWord(addr, v); + addr+=2; + } while (addr <= end); + } + break; + case GSA_8_BIT_IF_TRUE2: + if(CPUReadByte(cheatsList[i].address) != cheatsList[i].value) { + i+=2; + } + break; + case GSA_16_BIT_IF_TRUE2: + if(CPUReadHalfWord(cheatsList[i].address) != cheatsList[i].value) { + i+=2; + } + break; + case GSA_32_BIT_IF_TRUE2: + if(CPUReadMemory(cheatsList[i].address) != cheatsList[i].value) { + i+=2; + } + break; + case GSA_8_BIT_IF_FALSE2: + if(CPUReadByte(cheatsList[i].address) == cheatsList[i].value) { + i+=2; + } + break; + case GSA_16_BIT_IF_FALSE2: + if(CPUReadHalfWord(cheatsList[i].address) == cheatsList[i].value) { + i+=2; + } + break; + case GSA_32_BIT_IF_FALSE2: + if(CPUReadMemory(cheatsList[i].address) == cheatsList[i].value) { + i+=2; + } + break; + case CBA_ADD: + if ((cheatsList[i].address & 1) == 0) { + CPUWriteHalfWord(cheatsList[i].address, + CPUReadHalfWord(cheatsList[i].address) + + cheatsList[i].value); + } else { + CPUWriteMemory(cheatsList[i].address & 0x0FFFFFFE, + CPUReadMemory(cheatsList[i].address & 0x0FFFFFFE) + + cheatsList[i].value); + } + break; + case CBA_OR: + CPUWriteHalfWord(cheatsList[i].address, + CPUReadHalfWord(cheatsList[i].address) | + cheatsList[i].value); + break; + case CBA_GT: + if (!(CPUReadHalfWord(cheatsList[i].address) > cheatsList[i].value)){ + i++; + } + break; + case CBA_LT: + if (!(CPUReadHalfWord(cheatsList[i].address) < cheatsList[i].value)){ + i++; + } + break; + case CBA_SUPER: + { + int count = 2*((cheatsList[i].value -1) & 0xFFFF)+1; + u32 address = cheatsList[i].address; + for(int x = 0; x <= count; x++) { + u8 b; + int res = x % 6; + if (res==0) + i++; + if(res < 4) + b = (cheatsList[i].address >> (24-8*res)) & 0xFF; + else + b = (cheatsList[i].value >> (8 - 8*(res-4))) & 0xFF; + CPUWriteByte(address, b); + address++; + } + } + break; + case GSA_8_BIT_POINTER : + if (((CPUReadMemory(cheatsList[i].address)>=0x02000000) && (CPUReadMemory(cheatsList[i].address)<0x02040000)) || + ((CPUReadMemory(cheatsList[i].address)>=0x03000000) && (CPUReadMemory(cheatsList[i].address)<0x03008000))) + { + CPUWriteByte(CPUReadMemory(cheatsList[i].address)+((cheatsList[i].value & 0xFFFFFF00) >> 8), + cheatsList[i].value & 0xFF); + } + break; + case GSA_16_BIT_POINTER : + if (((CPUReadMemory(cheatsList[i].address)>=0x02000000) && (CPUReadMemory(cheatsList[i].address)<0x02040000)) || + ((CPUReadMemory(cheatsList[i].address)>=0x03000000) && (CPUReadMemory(cheatsList[i].address)<0x03008000))) + { + CPUWriteHalfWord(CPUReadMemory(cheatsList[i].address)+((cheatsList[i].value & 0xFFFF0000) >> 15), + cheatsList[i].value & 0xFFFF); + } + break; + case GSA_32_BIT_POINTER : + if (((CPUReadMemory(cheatsList[i].address)>=0x02000000) && (CPUReadMemory(cheatsList[i].address)<0x02040000)) || + ((CPUReadMemory(cheatsList[i].address)>=0x03000000) && (CPUReadMemory(cheatsList[i].address)<0x03008000))) + { + CPUWriteMemory(CPUReadMemory(cheatsList[i].address), + cheatsList[i].value); + } + break; + case GSA_8_BIT_ADD : + CPUWriteByte(cheatsList[i].address, + ((cheatsList[i].value & 0xFF) + CPUReadMemory(cheatsList[i].address)) & 0xFF); + break; + case GSA_16_BIT_ADD : + CPUWriteHalfWord(cheatsList[i].address, + ((cheatsList[i].value & 0xFFFF) + CPUReadMemory(cheatsList[i].address)) & 0xFFFF); + break; + case GSA_32_BIT_ADD : + CPUWriteMemory(cheatsList[i].address , + (cheatsList[i].value + CPUReadMemory(cheatsList[i].address)) & 0xFFFFFFFF); + break; + case GSA_8_BIT_IF_LOWER_U: + if (!(CPUReadByte(cheatsList[i].address) < (cheatsList[i].value & 0xFF))) { + i++; + } + break; + case GSA_16_BIT_IF_LOWER_U: + if (!(CPUReadHalfWord(cheatsList[i].address) < (cheatsList[i].value & 0xFFFF))) { + i++; + } + break; + case GSA_32_BIT_IF_LOWER_U: + if (!(CPUReadMemory(cheatsList[i].address) < cheatsList[i].value)) { + i++; + } + break; + case GSA_8_BIT_IF_HIGHER_U: + if (!(CPUReadByte(cheatsList[i].address) > (cheatsList[i].value & 0xFF))) { + i++; + } + break; + case GSA_16_BIT_IF_HIGHER_U: + if (!(CPUReadHalfWord(cheatsList[i].address) > (cheatsList[i].value & 0xFFFF))) { + i++; + } + break; + case GSA_32_BIT_IF_HIGHER_U: + if (!(CPUReadMemory(cheatsList[i].address) > cheatsList[i].value)) { + i++; + } + break; + case GSA_8_BIT_IF_AND: + if (!(CPUReadByte(cheatsList[i].address) & (cheatsList[i].value & 0xFF))) { + i++; + } + break; + case GSA_16_BIT_IF_AND: + if (!(CPUReadHalfWord(cheatsList[i].address) & (cheatsList[i].value & 0xFFFF))) { + i++; + } + break; + case GSA_32_BIT_IF_AND: + if (!(CPUReadMemory(cheatsList[i].address) & cheatsList[i].value)) { + i++; + } + break; + case GSA_8_BIT_IF_LOWER_U2: + if (!(CPUReadByte(cheatsList[i].address) < (cheatsList[i].value & 0xFF))) { + i+=2; + } + break; + case GSA_16_BIT_IF_LOWER_U2: + if (!(CPUReadHalfWord(cheatsList[i].address) < (cheatsList[i].value & 0xFFFF))) { + i+=2; + } + break; + case GSA_32_BIT_IF_LOWER_U2: + if (!(CPUReadMemory(cheatsList[i].address) < cheatsList[i].value)) { + i+=2; + } + break; + case GSA_8_BIT_IF_HIGHER_U2: + if (!(CPUReadByte(cheatsList[i].address) > (cheatsList[i].value & 0xFF))) { + i+=2; + } + break; + case GSA_16_BIT_IF_HIGHER_U2: + if (!(CPUReadHalfWord(cheatsList[i].address) > (cheatsList[i].value & 0xFFFF))) { + i+=2; + } + break; + case GSA_32_BIT_IF_HIGHER_U2: + if (!(CPUReadMemory(cheatsList[i].address) > cheatsList[i].value)) { + i+=2; + } + break; + case GSA_8_BIT_IF_AND2: + if (!(CPUReadByte(cheatsList[i].address) & (cheatsList[i].value & 0xFF))) { + i+=2; + } + break; + case GSA_16_BIT_IF_AND2: + if (!(CPUReadHalfWord(cheatsList[i].address) & (cheatsList[i].value & 0xFFFF))) { + i+=2; + } + break; + case GSA_32_BIT_IF_AND2: + if (!(CPUReadMemory(cheatsList[i].address) & cheatsList[i].value)) { + i+=2; + } + break; + case GSA_ALWAYS: + i++; + break; + case GSA_ALWAYS2: + i+=2; + break; + case GSA_8_BIT_IF_LOWER_S: + if (!((s8)CPUReadByte(cheatsList[i].address) < ((s8)cheatsList[i].value & 0xFF))) { + i++; + } + break; + case GSA_16_BIT_IF_LOWER_S: + if (!((s16)CPUReadHalfWord(cheatsList[i].address) < ((s16)cheatsList[i].value & 0xFFFF))) { + i++; + } + break; + case GSA_32_BIT_IF_LOWER_S: + if (!((s32)CPUReadMemory(cheatsList[i].address) < (s32)cheatsList[i].value)) { + i++; + } + break; + case GSA_8_BIT_IF_HIGHER_S: + if (!((s8)CPUReadByte(cheatsList[i].address) > ((s8)cheatsList[i].value & 0xFF))) { + i++; + } + break; + case GSA_16_BIT_IF_HIGHER_S: + if (!((s16)CPUReadHalfWord(cheatsList[i].address) > ((s16)cheatsList[i].value & 0xFFFF))) { + i++; + } + break; + case GSA_32_BIT_IF_HIGHER_S: + if (!((s32)CPUReadMemory(cheatsList[i].address) > (s32)cheatsList[i].value)) { + i++; + } + break; + case GSA_8_BIT_IF_LOWER_S2: + if (!((s8)CPUReadByte(cheatsList[i].address) < ((s8)cheatsList[i].value & 0xFF))) { + i+=2; + } + break; + case GSA_16_BIT_IF_LOWER_S2: + if (!((s16)CPUReadHalfWord(cheatsList[i].address) < ((s16)cheatsList[i].value & 0xFFFF))) { + i+=2; + } + break; + case GSA_32_BIT_IF_LOWER_S2: + if (!((s32)CPUReadMemory(cheatsList[i].address) < (s32)cheatsList[i].value)) { + i+=2; + } + break; + case GSA_8_BIT_IF_HIGHER_S2: + if (!((s8)CPUReadByte(cheatsList[i].address) > ((s8)cheatsList[i].value & 0xFF))) { + i+=2; + } + break; + case GSA_16_BIT_IF_HIGHER_S2: + if (!((s16)CPUReadHalfWord(cheatsList[i].address) > ((s16)cheatsList[i].value & 0xFFFF))) { + i+=2; + } + break; + case GSA_32_BIT_IF_HIGHER_S2: + if (!((s32)CPUReadMemory(cheatsList[i].address) > (s32)cheatsList[i].value)) { + i+=2; + } + break; + case GSA_16_BIT_WRITE_IOREGS: + if ((cheatsList[i].address <= 0x3FF) && (cheatsList[i].address != 0x6) && + (cheatsList[i].address != 0x130)) + ioMem[cheatsList[i].address & 0x3FE]=cheatsList[i].value & 0xFFFF; + break; + case GSA_32_BIT_WRITE_IOREGS: + if (cheatsList[i].address<=0x3FF) + { + if (((cheatsList[i].address & 0x3FC) != 0x6) && ((cheatsList[i].address & 0x3FC) != 0x130)) + ioMem[cheatsList[i].address & 0x3FC]= (cheatsList[i].value & 0xFFFF); + if ((((cheatsList[i].address & 0x3FC)+2) != 0x6) && ((cheatsList[i].address & 0x3FC) +2) != 0x130) + ioMem[(cheatsList[i].address & 0x3FC) + 2 ]= ((cheatsList[i].value>>16 ) & 0xFFFF); + } + break; + case GSA_8_BIT_IF_TRUE3: + if(CPUReadByte(cheatsList[i].address) != cheatsList[i].value) { + onoff=false; + } + break; + case GSA_16_BIT_IF_TRUE3: + if(CPUReadHalfWord(cheatsList[i].address) != cheatsList[i].value) { + onoff=false; + } + break; + case GSA_32_BIT_IF_TRUE3: + if(CPUReadMemory(cheatsList[i].address) != cheatsList[i].value) { + onoff=false; + } + break; + case GSA_8_BIT_IF_FALSE3: + if(CPUReadByte(cheatsList[i].address) == cheatsList[i].value) { + onoff=false; + } + break; + case GSA_16_BIT_IF_FALSE3: + if(CPUReadHalfWord(cheatsList[i].address) == cheatsList[i].value) { + onoff=false; + } + break; + case GSA_32_BIT_IF_FALSE3: + if(CPUReadMemory(cheatsList[i].address) == cheatsList[i].value) { + onoff=false; + } + break; + case GSA_8_BIT_IF_LOWER_S3: + if (!((s8)CPUReadByte(cheatsList[i].address) < ((s8)cheatsList[i].value & 0xFF))) { + onoff=false; + } + break; + case GSA_16_BIT_IF_LOWER_S3: + if (!((s16)CPUReadHalfWord(cheatsList[i].address) < ((s16)cheatsList[i].value & 0xFFFF))) { + onoff=false; + } + break; + case GSA_32_BIT_IF_LOWER_S3: + if (!((s32)CPUReadMemory(cheatsList[i].address) < (s32)cheatsList[i].value)) { + onoff=false; + } + break; + case GSA_8_BIT_IF_HIGHER_S3: + if (!((s8)CPUReadByte(cheatsList[i].address) > ((s8)cheatsList[i].value & 0xFF))) { + onoff=false; + } + break; + case GSA_16_BIT_IF_HIGHER_S3: + if (!((s16)CPUReadHalfWord(cheatsList[i].address) > ((s16)cheatsList[i].value & 0xFFFF))) { + onoff=false; + } + break; + case GSA_32_BIT_IF_HIGHER_S3: + if (!((s32)CPUReadMemory(cheatsList[i].address) > (s32)cheatsList[i].value)) { + onoff=false; + } + break; + case GSA_8_BIT_IF_LOWER_U3: + if (!(CPUReadByte(cheatsList[i].address) < (cheatsList[i].value & 0xFF))) { + onoff=false; + } + break; + case GSA_16_BIT_IF_LOWER_U3: + if (!(CPUReadHalfWord(cheatsList[i].address) < (cheatsList[i].value & 0xFFFF))) { + onoff=false; + } + break; + case GSA_32_BIT_IF_LOWER_U3: + if (!(CPUReadMemory(cheatsList[i].address) < cheatsList[i].value)) { + onoff=false; + } + break; + case GSA_8_BIT_IF_HIGHER_U3: + if (!(CPUReadByte(cheatsList[i].address) > (cheatsList[i].value & 0xFF))) { + onoff=false; + } + break; + case GSA_16_BIT_IF_HIGHER_U3: + if (!(CPUReadHalfWord(cheatsList[i].address) > (cheatsList[i].value & 0xFFFF))) { + onoff=false; + } + break; + case GSA_32_BIT_IF_HIGHER_U3: + if (!(CPUReadMemory(cheatsList[i].address) > cheatsList[i].value)) { + onoff=false; + } + break; + case GSA_8_BIT_IF_AND3: + if (!(CPUReadByte(cheatsList[i].address) & (cheatsList[i].value & 0xFF))) { + onoff=false; + } + break; + case GSA_16_BIT_IF_AND3: + if (!(CPUReadHalfWord(cheatsList[i].address) & (cheatsList[i].value & 0xFFFF))) { + onoff=false; + } + break; + case GSA_32_BIT_IF_AND3: + if (!(CPUReadMemory(cheatsList[i].address) & cheatsList[i].value)) { + onoff=false; + } + break; + case GSA_ALWAYS3: + if (!(CPUReadMemory(cheatsList[i].address) & cheatsList[i].value)) { + onoff=false; + } + break; + case GSA_GROUP_WRITE: + { + int count = ((cheatsList[i].address) & 0xFFFE) +1; + u32 value = cheatsList[i].value; + if (count==0) + i++; + else + for (int x = 1; x <= count; x++) { + if ((x % 2) ==0){ + if (x cheatsList[i].value) { + i++; + } + break; + case GSA_16_BIT_IF_HIGHER_OR_EQ_U: + if(CPUReadHalfWord(cheatsList[i].address) < cheatsList[i].value) { + i++; + } + break; + case GSA_16_BIT_MIF_TRUE: + if(CPUReadHalfWord(cheatsList[i].address) != cheatsList[i].value) { + i+=((cheatsList[i].rawaddress >> 0x10) & 0xFF); + } + break; + case GSA_16_BIT_MIF_FALSE: + if(CPUReadHalfWord(cheatsList[i].address) == cheatsList[i].value) { + i+=(cheatsList[i].rawaddress >> 0x10) & 0xFF; + } + break; + case GSA_16_BIT_MIF_LOWER_OR_EQ_U: + if(CPUReadHalfWord(cheatsList[i].address) > cheatsList[i].value) { + i+=(cheatsList[i].rawaddress >> 0x10) & 0xFF; + } + break; + case GSA_16_BIT_MIF_HIGHER_OR_EQ_U: + if(CPUReadHalfWord(cheatsList[i].address) < cheatsList[i].value) { + i+=(cheatsList[i].rawaddress >> 0x10) & 0xFF; + } + break; + case CHEATS_16_BIT_WRITE: + if ((cheatsList[i].address>>24)>=0x08) { + CHEAT_PATCH_ROM_16BIT(cheatsList[i].address, cheatsList[i].value); + } else { + CPUWriteHalfWord(cheatsList[i].address, cheatsList[i].value); + } + break; + case CHEATS_32_BIT_WRITE: + if ((cheatsList[i].address>>24)>=0x08) { + CHEAT_PATCH_ROM_32BIT(cheatsList[i].address, cheatsList[i].value); + } else { + CPUWriteMemory(cheatsList[i].address, cheatsList[i].value); + } + break; + } + } + } + for (i = 0; i<4; i++) + if (rompatch2addr [i] != 0) + CHEAT_PATCH_ROM_16BIT(rompatch2addr [i],rompatch2val [i]); + return ticks; +} + +void cheatsAdd(const char *codeStr, + const char *desc, + u32 rawaddress, + u32 address, + u32 value, + int code, + int size) +{ + if(cheatsNumber < 100) { + int x = cheatsNumber; + cheatsList[x].code = code; + cheatsList[x].size = size; + cheatsList[x].rawaddress = rawaddress; + cheatsList[x].address = address; + cheatsList[x].value = value; + strcpy(cheatsList[x].codestring, codeStr); + strcpy(cheatsList[x].desc, desc); + cheatsList[x].enabled = true; + cheatsList[x].status = 0; + + // we only store the old value for this simple codes. ROM patching + // is taken care when it actually patches the ROM + switch(cheatsList[x].size) { + case INT_8_BIT_WRITE: + cheatsList[x].oldValue = CPUReadByte(address); + break; + case INT_16_BIT_WRITE: + cheatsList[x].oldValue = CPUReadHalfWord(address); + break; + case INT_32_BIT_WRITE: + cheatsList[x].oldValue = CPUReadMemory(address); + break; + case CHEATS_16_BIT_WRITE: + cheatsList[x].oldValue = CPUReadHalfWord(address); + break; + case CHEATS_32_BIT_WRITE: + cheatsList[x].oldValue = CPUReadMemory(address); + break; + } + cheatsNumber++; + } +} + +void cheatsDelete(int number, bool restore) +{ + if(number < cheatsNumber && number >= 0) { + int x = number; + + if(restore) { + switch(cheatsList[x].size) { + case INT_8_BIT_WRITE: + CPUWriteByte(cheatsList[x].address, (u8)cheatsList[x].oldValue); + break; + case INT_16_BIT_WRITE: + CPUWriteHalfWord(cheatsList[x].address, (u16)cheatsList[x].oldValue); + break; + case INT_32_BIT_WRITE: + CPUWriteMemory(cheatsList[x].address, cheatsList[x].oldValue); + break; + case CHEATS_16_BIT_WRITE: + if ((cheatsList[x].address>>24)>=0x08) { + CHEAT_PATCH_ROM_16BIT(cheatsList[x].address, cheatsList[x].oldValue); + } else { + CPUWriteHalfWord(cheatsList[x].address, cheatsList[x].oldValue); + } + break; + case CHEATS_32_BIT_WRITE: + if ((cheatsList[x].address>>24)>=0x08) { + CHEAT_PATCH_ROM_32BIT(cheatsList[x].address, cheatsList[x].oldValue); + } else { + CPUWriteMemory(cheatsList[x].address, cheatsList[x].oldValue); + } + case GSA_16_BIT_ROM_PATCH: + if(cheatsList[x].status & 1) { + cheatsList[x].status &= ~1; + CHEAT_PATCH_ROM_16BIT(cheatsList[x].address, + cheatsList[x].oldValue); + } + break; + case GSA_16_BIT_ROM_PATCH2C: + case GSA_16_BIT_ROM_PATCH2D: + case GSA_16_BIT_ROM_PATCH2E: + case GSA_16_BIT_ROM_PATCH2F: + if(cheatsList[x].status & 1) { + cheatsList[x].status &= ~1; + } + break; + case MASTER_CODE: + mastercode=0; + break; + } + } + if((x+1) < cheatsNumber) { + memcpy(&cheatsList[x], &cheatsList[x+1], sizeof(CheatsData)* + (cheatsNumber-x-1)); + } + cheatsNumber--; + } +} + +void cheatsDeleteAll(bool restore) +{ + for(int i = cheatsNumber-1; i >= 0; i--) { + cheatsDelete(i, restore); + } +} + +void cheatsEnable(int i) +{ + if(i >= 0 && i < cheatsNumber) { + cheatsList[i].enabled = true; + mastercode = 0; + } +} + +void cheatsDisable(int i) +{ + if(i >= 0 && i < cheatsNumber) { + switch(cheatsList[i].size) { + case GSA_16_BIT_ROM_PATCH: + if(cheatsList[i].status & 1) { + cheatsList[i].status &= ~1; + CHEAT_PATCH_ROM_16BIT(cheatsList[i].address, + cheatsList[i].oldValue); + } + break; + case GSA_16_BIT_ROM_PATCH2C: + case GSA_16_BIT_ROM_PATCH2D: + case GSA_16_BIT_ROM_PATCH2E: + case GSA_16_BIT_ROM_PATCH2F: + if(cheatsList[i].status & 1) { + cheatsList[i].status &= ~1; + } + break; + case MASTER_CODE: + mastercode=0; + break; + } + cheatsList[i].enabled = false; + } +} + +bool cheatsVerifyCheatCode(const char *code, const char *desc) +{ + size_t len = strlen(code); + if(len != 11 && len != 13 && len != 17) { + systemMessage(MSG_INVALID_CHEAT_CODE, N_("Invalid cheat code '%s': wrong length"), code); + return false; + } + + if(code[8] != ':') { + systemMessage(MSG_INVALID_CHEAT_CODE, N_("Invalid cheat code '%s': no colon"), code); + return false; + } + + size_t i; + for(i = 0; i < 8; i++) { + if(!CHEAT_IS_HEX(code[i])) { + // wrong cheat + systemMessage(MSG_INVALID_CHEAT_CODE, + N_("Invalid cheat code '%s': first part is not hex"), code); + return false; + } + } + for(i = 9; i < len; i++) { + if(!CHEAT_IS_HEX(code[i])) { + // wrong cheat + systemMessage(MSG_INVALID_CHEAT_CODE, + N_("Invalid cheat code '%s' second part is not hex"), code); + return false; + } + } + + u32 address = 0; + u32 value = 0; + + char buffer[10]; + strncpy(buffer, code, 8); + buffer[8] = 0; + sscanf(buffer, "%x", &address); + + switch(address >> 24) { + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + break; + default: + systemMessage(MSG_INVALID_CHEAT_CODE_ADDRESS, + N_("Invalid cheat code address: %08x"), + address); + return false; + } + + strncpy(buffer, &code[9], 8); + sscanf(buffer, "%x", &value); + int type = 0; + if(len == 13) + type = 114; + if(len == 17) + type = 115; + cheatsAdd(code, desc, address, address, value, type, type); + return true; +} + +void cheatsAddCheatCode(const char *code, const char *desc) +{ + cheatsVerifyCheatCode(code, desc); +} + +u16 cheatsGSAGetDeadface(bool v3) +{ + for(int i = cheatsNumber-1; i >= 0; i--) + if ((cheatsList[i].address == 0xDEADFACE) && (cheatsList[i].code == (v3 ? 257 : 256))) + return cheatsList[i].value & 0xFFFF; + return 0; +} + +void cheatsGSAChangeEncryption(u16 value, bool v3) { + int i; + u8 *deadtable1, *deadtable2; + + if (v3) { + deadtable1 = (u8*)(&v3_deadtable1); + deadtable2 = (u8*)(&v3_deadtable2); + for (i = 0; i < 4; i++) + seeds_v3[i] = seed_gen(((value & 0xFF00) >> 8), (value & 0xFF) + i, deadtable1, deadtable2); + } + else { + deadtable1 = (u8*)(&v1_deadtable1); + deadtable2 = (u8*)(&v1_deadtable2); + for (i = 0; i < 4; i++){ + seeds_v1[i] = seed_gen(((value & 0xFF00) >> 8), (value & 0xFF) + i, deadtable1, deadtable2); + } + } +} + +u32 seed_gen(u8 upper, u8 seed, u8 *deadtable1, u8 *deadtable2) { + int i; + u32 newseed = 0; + + for (i = 0; i < 4; i++) + newseed = ((newseed << 8) | ((deadtable1[(i + upper) & 0xFF] + deadtable2[seed]) & 0xFF)); + + return newseed; +} + +void cheatsDecryptGSACode(u32& address, u32& value, bool v3) +{ + u32 rollingseed = 0xC6EF3720; + u32 *seeds = v3 ? seeds_v3 : seeds_v1; + + int bitsleft = 32; + while (bitsleft > 0) { + value -= ((((address << 4) + seeds[2]) ^ (address + rollingseed)) ^ + ((address >> 5) + seeds[3])); + address -= ((((value << 4) + seeds[0]) ^ (value + rollingseed)) ^ + ((value >> 5) + seeds[1])); + rollingseed -= 0x9E3779B9; + bitsleft--; + } +} + +void cheatsAddGSACode(const char *code, const char *desc, bool v3) +{ + if(strlen(code) != 16) { + // wrong cheat + systemMessage(MSG_INVALID_GSA_CODE, + N_("Invalid GSA code. Format is XXXXXXXXYYYYYYYY")); + return; + } + + int i; + for(i = 0; i < 16; i++) { + if(!CHEAT_IS_HEX(code[i])) { + // wrong cheat + systemMessage(MSG_INVALID_GSA_CODE, + N_("Invalid GSA code. Format is XXXXXXXXYYYYYYYY")); + return; + } + } + + char buffer[10]; + strncpy(buffer, code, 8); + buffer[8] = 0; + u32 address; + sscanf(buffer, "%x", &address); + strncpy(buffer, &code[8], 8); + buffer[8] = 0; + u32 value; + sscanf(buffer, "%x", &value); + cheatsGSAChangeEncryption(cheatsGSAGetDeadface (v3), v3); + cheatsDecryptGSACode(address, value, v3); + + if(value == 0x1DC0DE) { + u32 gamecode = READ32LE(((u32 *)&rom[0xac])); + if(gamecode != address) { + char buffer[5]; + *((u32 *)buffer) = address; + buffer[4] = 0; + char buffer2[5]; + *((u32 *)buffer2) = READ32LE(((u32 *)&rom[0xac])); + buffer2[4] = 0; + systemMessage(MSG_GBA_CODE_WARNING, N_("Warning: cheats are for game %s. Current game is %s.\nCodes may not work correctly."), + buffer, buffer2); + } + cheatsAdd(code, desc, address, address & 0x0FFFFFFF, value, v3 ? 257 : 256, + UNKNOWN_CODE); + return; + } + if(isMultilineWithData(cheatsNumber-1)) { + cheatsAdd(code, desc, address, address, value, v3 ? 257 : 256, UNKNOWN_CODE); + return; + } + if(v3) { + int type = ((address >> 25) & 127) | ((address >> 17) & 0x80); + u32 addr = (address & 0x00F00000) << 4 | (address & 0x0003FFFF); + u16 mcode = (address>>24 & 0xFF); + + if ((mcode & 0xFE) == 0xC4) + { + cheatsAdd(code, desc, address, (address & 0x1FFFFFF) | (0x08000000), + value, 257, MASTER_CODE); + mastercode = (address & 0x1FFFFFF) | (0x08000000); + } + else + switch(type) { + case 0x00: + if(address == 0) { + type = (value >> 25) & 127; + addr = (value & 0x00F00000) << 4 | (value & 0x0003FFFF); + switch(type) { + case 0x04: + cheatsAdd(code, desc, address, 0, value & 0x00FFFFFF, 257, GSA_SLOWDOWN); + break; + case 0x08: + cheatsAdd(code, desc, address, 0, addr, 257, GSA_8_BIT_GS_WRITE2); + break; + case 0x09: + cheatsAdd(code, desc, address, 0, addr, 257, GSA_16_BIT_GS_WRITE2); + break; + case 0x0a: + cheatsAdd(code, desc, address, 0, addr, 257, GSA_32_BIT_GS_WRITE2); + break; + case 0x0c: + cheatsAdd(code, desc, address, 0, value & 0x00FFFFFF, 257, GSA_16_BIT_ROM_PATCH2C); + break; + case 0x0d: + cheatsAdd(code, desc, address, 0, value & 0x00FFFFFF, 257, GSA_16_BIT_ROM_PATCH2D); + break; + case 0x0e: + cheatsAdd(code, desc, address, 0, value & 0x00FFFFFF, 257, GSA_16_BIT_ROM_PATCH2E); + break; + case 0x0f: + cheatsAdd(code, desc, address, 0, value & 0x00FFFFFF, 257, GSA_16_BIT_ROM_PATCH2F); + break; + case 0x20: + cheatsAdd(code, desc, address, 0, addr, 257, GSA_CODES_ON); + break; + case 0x40: + cheatsAdd(code, desc, address, 0, addr, 257, GSA_8_BIT_SLIDE); + break; + case 0x41: + cheatsAdd(code, desc, address, 0, addr, 257, GSA_16_BIT_SLIDE); + break; + case 0x42: + cheatsAdd(code, desc, address, 0, addr, 257, GSA_32_BIT_SLIDE); + break; + default: + cheatsAdd(code, desc, address, address, value, 257, UNKNOWN_CODE); + break; + } + } else + cheatsAdd(code, desc, address, addr, value, 257, GSA_8_BIT_FILL); + break; + case 0x01: + cheatsAdd(code, desc, address, addr, value, 257, GSA_16_BIT_FILL); + break; + case 0x02: + cheatsAdd(code, desc, address, addr, value, 257, INT_32_BIT_WRITE); + break; + case 0x04: + cheatsAdd(code, desc, address, addr, value, 257, GSA_8_BIT_IF_TRUE); + break; + case 0x05: + cheatsAdd(code, desc, address, addr, value, 257, CBA_IF_TRUE); + break; + case 0x06: + cheatsAdd(code, desc, address, addr, value, 257, GSA_32_BIT_IF_TRUE); + break; + case 0x07: + cheatsAdd(code, desc, address, addr, value, 257, GSA_ALWAYS); + break; + case 0x08: + cheatsAdd(code, desc, address, addr, value, 257, GSA_8_BIT_IF_FALSE); + break; + case 0x09: + cheatsAdd(code, desc, address, addr, value, 257, CBA_IF_FALSE); + break; + case 0x0a: + cheatsAdd(code, desc, address, addr, value, 257, GSA_32_BIT_IF_FALSE); + break; + case 0xc: + cheatsAdd(code, desc, address, addr, value, 257, GSA_8_BIT_IF_LOWER_S); + break; + case 0xd: + cheatsAdd(code, desc, address, addr, value, 257, GSA_16_BIT_IF_LOWER_S); + break; + case 0xe: + cheatsAdd(code, desc, address, addr, value, 257, GSA_32_BIT_IF_LOWER_S); + break; + case 0x10: + cheatsAdd(code, desc, address, addr, value, 257, GSA_8_BIT_IF_HIGHER_S); + break; + case 0x11: + cheatsAdd(code, desc, address, addr, value, 257, GSA_16_BIT_IF_HIGHER_S); + break; + case 0x12: + cheatsAdd(code, desc, address, addr, value, 257, GSA_32_BIT_IF_HIGHER_S); + break; + case 0x14: + cheatsAdd(code, desc, address, addr, value, 257, GSA_8_BIT_IF_LOWER_U); + break; + case 0x15: + cheatsAdd(code, desc, address, addr, value, 257, GSA_16_BIT_IF_LOWER_U); + break; + case 0x16: + cheatsAdd(code, desc, address, addr, value, 257, GSA_32_BIT_IF_LOWER_U); + break; + case 0x18: + cheatsAdd(code, desc, address, addr, value, 257, GSA_8_BIT_IF_HIGHER_U); + break; + case 0x19: + cheatsAdd(code, desc, address, addr, value, 257, GSA_16_BIT_IF_HIGHER_U); + break; + case 0x1A: + cheatsAdd(code, desc, address, addr, value, 257, GSA_32_BIT_IF_HIGHER_U); + break; + case 0x1C: + cheatsAdd(code, desc, address, addr, value, 257, GSA_8_BIT_IF_AND); + break; + case 0x1D: + cheatsAdd(code, desc, address, addr, value, 257, GSA_16_BIT_IF_AND); + break; + case 0x1E: + cheatsAdd(code, desc, address, addr, value, 257, GSA_32_BIT_IF_AND); + break; + case 0x20: + cheatsAdd(code, desc, address, addr, value, 257, GSA_8_BIT_POINTER); + break; + case 0x21: + cheatsAdd(code, desc, address, addr, value, 257, GSA_16_BIT_POINTER); + break; + case 0x22: + cheatsAdd(code, desc, address, addr, value, 257, GSA_32_BIT_POINTER); + break; + case 0x24: + cheatsAdd(code, desc, address, addr, value, 257, GSA_8_BIT_IF_TRUE2); + break; + case 0x25: + cheatsAdd(code, desc, address, addr, value, 257, GSA_16_BIT_IF_TRUE2); + break; + case 0x26: + cheatsAdd(code, desc, address, addr, value, 257, GSA_32_BIT_IF_TRUE2); + break; + case 0x27: + cheatsAdd(code, desc, address, addr, value, 257, GSA_ALWAYS2); + break; + case 0x28: + cheatsAdd(code, desc, address, addr, value, 257, GSA_8_BIT_IF_FALSE2); + break; + case 0x29: + cheatsAdd(code, desc, address, addr, value, 257, GSA_16_BIT_IF_FALSE2); + break; + case 0x2a: + cheatsAdd(code, desc, address, addr, value, 257, GSA_32_BIT_IF_FALSE2); + break; + case 0x2c: + cheatsAdd(code, desc, address, addr, value, 257, GSA_8_BIT_IF_LOWER_S2); + break; + case 0x2d: + cheatsAdd(code, desc, address, addr, value, 257, GSA_16_BIT_IF_LOWER_S2); + break; + case 0x2e: + cheatsAdd(code, desc, address, addr, value, 257, GSA_32_BIT_IF_LOWER_S2); + break; + case 0x30: + cheatsAdd(code, desc, address, addr, value, 257, GSA_8_BIT_IF_HIGHER_S2); + break; + case 0x31: + cheatsAdd(code, desc, address, addr, value, 257, GSA_16_BIT_IF_HIGHER_S2); + break; + case 0x32: + cheatsAdd(code, desc, address, addr, value, 257, GSA_32_BIT_IF_HIGHER_S2); + break; + case 0x34: + cheatsAdd(code, desc, address, addr, value, 257, GSA_8_BIT_IF_LOWER_U2); + break; + case 0x35: + cheatsAdd(code, desc, address, addr, value, 257, GSA_16_BIT_IF_LOWER_U2); + break; + case 0x36: + cheatsAdd(code, desc, address, addr, value, 257, GSA_32_BIT_IF_LOWER_U2); + break; + case 0x38: + cheatsAdd(code, desc, address, addr, value, 257, GSA_8_BIT_IF_HIGHER_U2); + break; + case 0x39: + cheatsAdd(code, desc, address, addr, value, 257, GSA_16_BIT_IF_HIGHER_U2); + break; + case 0x3A: + cheatsAdd(code, desc, address, addr, value, 257, GSA_32_BIT_IF_HIGHER_U2); + break; + case 0x3C: + cheatsAdd(code, desc, address, addr, value, 257, GSA_8_BIT_IF_AND2); + break; + case 0x3D: + cheatsAdd(code, desc, address, addr, value, 257, GSA_16_BIT_IF_AND2); + break; + case 0x3E: + cheatsAdd(code, desc, address, addr, value, 257, GSA_32_BIT_IF_AND2); + break; + case 0x40: + cheatsAdd(code, desc, address, addr, value, 257, GSA_8_BIT_ADD); + break; + case 0x41: + cheatsAdd(code, desc, address, addr, value, 257, GSA_16_BIT_ADD); + break; + case 0x42: + cheatsAdd(code, desc, address, addr, value, 257, GSA_32_BIT_ADD); + break; + case 0x44: + cheatsAdd(code, desc, address, addr, value, 257, GSA_8_BIT_IF_TRUE3); + break; + case 0x45: + cheatsAdd(code, desc, address, addr, value, 257, GSA_16_BIT_IF_TRUE3); + break; + case 0x46: + cheatsAdd(code, desc, address, addr, value, 257, GSA_32_BIT_IF_TRUE3); + break; + case 0x47: + cheatsAdd(code, desc, address, addr, value, 257, GSA_ALWAYS3); + break; + case 0x48: + cheatsAdd(code, desc, address, addr, value, 257, GSA_8_BIT_IF_FALSE3); + break; + case 0x49: + cheatsAdd(code, desc, address, addr, value, 257, GSA_16_BIT_IF_FALSE3); + break; + case 0x4a: + cheatsAdd(code, desc, address, addr, value, 257, GSA_32_BIT_IF_FALSE3); + break; + case 0x4c: + cheatsAdd(code, desc, address, addr, value, 257, GSA_8_BIT_IF_LOWER_S3); + break; + case 0x4d: + cheatsAdd(code, desc, address, addr, value, 257, GSA_16_BIT_IF_LOWER_S3); + break; + case 0x4e: + cheatsAdd(code, desc, address, addr, value, 257, GSA_32_BIT_IF_LOWER_S3); + break; + case 0x50: + cheatsAdd(code, desc, address, addr, value, 257, GSA_8_BIT_IF_HIGHER_S3); + break; + case 0x51: + cheatsAdd(code, desc, address, addr, value, 257, GSA_16_BIT_IF_HIGHER_S3); + break; + case 0x52: + cheatsAdd(code, desc, address, addr, value, 257, GSA_32_BIT_IF_HIGHER_S3); + break; + case 0x54: + cheatsAdd(code, desc, address, addr, value, 257, GSA_8_BIT_IF_LOWER_U3); + break; + case 0x55: + cheatsAdd(code, desc, address, addr, value, 257, GSA_16_BIT_IF_LOWER_U3); + break; + case 0x56: + cheatsAdd(code, desc, address, addr, value, 257, GSA_32_BIT_IF_LOWER_U3); + break; + case 0x58: + cheatsAdd(code, desc, address, addr, value, 257, GSA_8_BIT_IF_HIGHER_U3); + break; + case 0x59: + cheatsAdd(code, desc, address, addr, value, 257, GSA_16_BIT_IF_HIGHER_U3); + break; + case 0x5a: + cheatsAdd(code, desc, address, addr, value, 257, GSA_32_BIT_IF_HIGHER_U3); + break; + case 0x5c: + cheatsAdd(code, desc, address, addr, value, 257, GSA_8_BIT_IF_AND3); + break; + case 0x5d: + cheatsAdd(code, desc, address, addr, value, 257, GSA_16_BIT_IF_AND3); + break; + case 0x5e: + cheatsAdd(code, desc, address, addr, value, 257, GSA_32_BIT_IF_AND3); + break; + case 0x63: + cheatsAdd(code, desc, address, addr, value, 257, GSA_16_BIT_WRITE_IOREGS); + break; + case 0xE3: + cheatsAdd(code, desc, address, addr, value, 257, GSA_32_BIT_WRITE_IOREGS); + break; + default: + cheatsAdd(code, desc, address, address, value, 257, UNKNOWN_CODE); + break; + } + } else { + int type = (address >> 28) & 15; + switch(type) { + case 0: + case 1: + case 2: + cheatsAdd(code, desc, address, address & 0x0FFFFFFF, value, 256, type); + break; + case 3: + switch ((address >> 0x10) & 0xFF){ + case 0x00: + cheatsAdd(code, desc, address, address & 0x0FFFFFFF, value, 256, GSA_GROUP_WRITE); + break; + case 0x10: + cheatsAdd(code, desc, address, value & 0x0FFFFFFF, address & 0xFF, 256, GSA_32_BIT_ADD ); + break; + case 0x20: + cheatsAdd(code, desc, address, value & 0x0FFFFFFF, (~(address & 0xFF)+1), 256, GSA_32_BIT_ADD ); + break; + case 0x30: + cheatsAdd(code, desc, address, value & 0x0FFFFFFF, address & 0xFFFF, 256, GSA_32_BIT_ADD ); + break; + case 0x40: + cheatsAdd(code, desc, address, value & 0x0FFFFFFF, (~(address & 0xFFFF)+1), 256, GSA_32_BIT_ADD ); + break; + case 0x50: + cheatsAdd(code, desc, address, address & 0x0FFFFFFF, value, 256, GSA_32_BIT_ADD2); + break; + case 0x60: + cheatsAdd(code, desc, address, address & 0x0FFFFFFF, value, 256, GSA_32_BIT_SUB2); + break; + default: + // unsupported code + cheatsAdd(code, desc, address, address, value, 256, + UNKNOWN_CODE); + break; + } + break; + case 6: + address <<= 1; + type = (value >> 24) & 0xFF; + if(type == 0x00) { + cheatsAdd(code, desc, address, address & 0x0FFFFFFF, value & 0xFFFF, 256, + GSA_16_BIT_ROM_PATCH); + break; + } + // unsupported code + cheatsAdd(code, desc, address, address, value, 256, + UNKNOWN_CODE); + break; + case 8: + switch((address >> 20) & 15) { + case 1: + cheatsAdd(code, desc, address, address & 0x0F0FFFFF, value, 256, + GSA_8_BIT_GS_WRITE); + break; + case 2: + cheatsAdd(code, desc, address, address & 0x0F0FFFFF, value, 256, + GSA_16_BIT_GS_WRITE); + break; + case 4: + // This code is buggy : the value is always set to 0 ! + cheatsAdd(code, desc, address, address & 0x0F0FFFFF, 0, 256, + GSA_32_BIT_GS_WRITE); + break; + case 15: + cheatsAdd(code, desc, address, 0, value & 0xFFFF, 256, GSA_SLOWDOWN); + break; + default: + // unsupported code + cheatsAdd(code, desc, address, address, value, 256, + UNKNOWN_CODE); + break; + } + break; + case 0x0d: + if(address != 0xDEADFACE) { + switch((value >> 20) & 0xF) { + case 0: + cheatsAdd(code, desc, address, address & 0x0FFFFFFF, value & 0xFFFF, 256, + CBA_IF_TRUE); + break; + case 1: + cheatsAdd(code, desc, address, address & 0x0FFFFFFF, value & 0xFFFF, 256, + CBA_IF_FALSE); + break; + case 2: + cheatsAdd(code, desc, address, address & 0x0FFFFFFF, value & 0xFFFF, 256, + GSA_16_BIT_IF_LOWER_OR_EQ_U); + break; + case 3: + cheatsAdd(code, desc, address, address & 0x0FFFFFFF, value & 0xFFFF, 256, + GSA_16_BIT_IF_HIGHER_OR_EQ_U); + break; + default: + // unsupported code + cheatsAdd(code, desc, address, address, value, 256, + UNKNOWN_CODE); + break; + } + } else + cheatsAdd(code, desc, address, address, value, 256, + UNKNOWN_CODE); + break; + case 0x0e: + switch((value >> 28) & 0xF) { + case 0: + cheatsAdd(code, desc, address, value & 0x0FFFFFFF, address & 0xFFFF, 256, + GSA_16_BIT_MIF_TRUE); + break; + case 1: + cheatsAdd(code, desc, address, value & 0x0FFFFFFF, address & 0xFFFF, 256, + GSA_16_BIT_MIF_FALSE); + break; + case 2: + cheatsAdd(code, desc, address, value & 0x0FFFFFFF, address & 0xFFFF, 256, + GSA_16_BIT_MIF_LOWER_OR_EQ_U); + break; + case 3: + cheatsAdd(code, desc, address, value & 0x0FFFFFFF, address & 0xFFFF, 256, + GSA_16_BIT_MIF_HIGHER_OR_EQ_U); + break; + default: + // unsupported code + cheatsAdd(code, desc, address, address, value, 256, + UNKNOWN_CODE); + break; + } + break; + case 0x0f: + cheatsAdd(code, desc, address, (address & 0xFFFFFFF), value, 256, MASTER_CODE); + mastercode = (address & 0xFFFFFFF); + break; + default: + // unsupported code + cheatsAdd(code, desc, address, address, value, 256, + UNKNOWN_CODE); + break; + } + } +} + +bool cheatsImportGSACodeFile(const char *name, int game, bool v3) +{ + FILE *f = fopen(name, "rb"); + if(!f) + return false; + + int games = 0; + int len = 0; + fseek(f, 0x1e, SEEK_CUR); + fread(&games, 1, 4, f); + bool found = false; + int g = 0; + while(games > 0) { + if(g == game) { + found = true; + break; + } + fread(&len, 1, 4, f); + fseek(f,len,SEEK_CUR); + int codes = 0; + fread(&codes, 1, 4, f); + while(codes > 0) { + fread(&len, 1, 4, f); + fseek(f, len, SEEK_CUR); + fseek(f, 8, SEEK_CUR); + fread(&len, 1, 4, f); + fseek(f, len*12, SEEK_CUR); + codes--; + } + games--; + g++; + } + if(found) { + char desc[256]; + char code[17]; + fread(&len, 1, 4, f); + fseek(f, len, SEEK_CUR); + int codes = 0; + fread(&codes, 1, 4, f); + while(codes > 0) { + fread(&len, 1, 4, f); + fread(desc, 1, len, f); + desc[len] =0; + desc[31] = 0; + fread(&len, 1, 4, f); + fseek(f, len, SEEK_CUR); + fseek(f, 4, SEEK_CUR); + fread(&len, 1, 4, f); + while(len) { + fseek(f, 4, SEEK_CUR); + fread(code, 1, 8, f); + fseek(f, 4, SEEK_CUR); + fread(&code[8], 1, 8, f); + code[16] = 0; + cheatsAddGSACode(code, desc, v3); + len -= 2; + } + codes--; + } + } + fclose(f); + return false; +} + +void cheatsCBAReverseArray(u8 *array, u8 *dest) +{ + dest[0] = array[3]; + dest[1] = array[2]; + dest[2] = array[1]; + dest[3] = array[0]; + dest[4] = array[5]; + dest[5] = array[4]; +} + +void chatsCBAScramble(u8 *array, int count, u8 b) +{ + u8 *x = array + (count >> 3); + u8 *y = array + (b >> 3); + u32 z = *x & (1 << (count & 7)); + u32 x0 = (*x & (~(1 << (count & 7)))); + if (z != 0) + z = 1; + if ((*y & (1 << (b & 7))) != 0) + x0 |= (1 << (count & 7)); + *x = x0; + u32 temp = *y & (~(1 << (b & 7))); + if (z != 0) + temp |= (1 << (b & 7)); + *y = temp; +} + +u32 cheatsCBAGetValue(u8 *array) +{ + return array[0] | array[1]<<8 | array[2] << 16 | array[3]<<24; +} + +u16 cheatsCBAGetData(u8 *array) +{ + return array[4] | array[5]<<8; +} + +void cheatsCBAArrayToValue(u8 *array, u8 *dest) +{ + dest[0] = array[3]; + dest[1] = array[2]; + dest[2] = array[1]; + dest[3] = array[0]; + dest[4] = array[5]; + dest[5] = array[4]; +} + +void cheatsCBAParseSeedCode(u32 address, u32 value, u32 *array) +{ + array[0] = 1; + array[1] = value & 0xFF; + array[2] = (address >> 0x10) & 0xFF; + array[3] = (value >> 8) & 0xFF; + array[4] = (address >> 0x18) & 0x0F; + array[5] = address & 0xFFFF; + array[6] = address; + array[7] = value; +} + +u32 cheatsCBAEncWorker() +{ + u32 x = (cheatsCBATemporaryValue * 0x41c64e6d) + 0x3039; + u32 y = (x * 0x41c64e6d) + 0x3039; + u32 z = x >> 0x10; + x = ((y >> 0x10) & 0x7fff) << 0x0f; + z = (z << 0x1e) | x; + x = (y * 0x41c64e6d) + 0x3039; + cheatsCBATemporaryValue = x; + return z | ((x >> 0x10) & 0x7fff); +} + +#define ROR(v, s) \ + (((v) >> (s)) | (((v) & ((1 << (s))-1)) << (32 - (s)))) + +u32 cheatsCBACalcIndex(u32 x, u32 y) +{ + if(y != 0) { + if(y == 1) + x = 0; + else if(x == y) + x = 0; + if(y < 1) + return x; + else if(x < y) + return x; + u32 x0 = 1; + + while(y < 0x10000000) { + if(y < x) { + y = y << 4; + x0 = x0 << 4; + } else break; + } + + while(y < 0x80000000) { + if(y < x) { + y = y << 1; + x0 = x0 << 1; + } else break; + } + + loop: + u32 z = 0; + if(x >= y) + x -= y; + if(x >= (y >> 1)) { + x -= (y >> 1); + z |= ROR(x0, 1); + } + if(x >= (y >> 2)) { + x -= (y >> 2); + z |= ROR(x0, 2); + } + if(x >= (y >> 3)) { + x -= (y >> 3); + z |= ROR(x0, 3); + } + + u32 temp = x0; + + if(x != 0) { + x0 = x0 >> 4; + if(x0 != 0) { + y = y >> 4; + goto loop; + } + } + + z = z & 0xe0000000; + + if(z != 0) { + if((temp & 7) == 0) + return x; + } else + return x; + + if((z & ROR(temp, 3)) != 0) + x += y >> 3; + if((z & ROR(temp, 2)) != 0) + x += y >> 2; + if((z & ROR(temp, 1)) != 0) + x += y >> 1; + return x; + } else { + } + // should not happen in the current code + return 0; +} + +void cheatsCBAUpdateSeedBuffer(u32 a, u8 *buffer, int count) +{ + int i; + for(i = 0; i < count; i++) + buffer[i] = i; + for(i = 0; (u32)i < a; i++) { + u32 a = cheatsCBACalcIndex(cheatsCBAEncWorker(), count); + u32 b = cheatsCBACalcIndex(cheatsCBAEncWorker(), count); + u32 t = buffer[a]; + buffer[a] = buffer[b]; + buffer[b] = t; + } +} + +void cheatsCBAChangeEncryption(u32 *seed) +{ + int i; + + cheatsCBATemporaryValue = (seed[1] ^ 0x1111); + cheatsCBAUpdateSeedBuffer(0x50, cheatsCBASeedBuffer, 0x30); + cheatsCBATemporaryValue = 0x4efad1c3; + + for(i = 0; (u32)i < seed[4]; i++) { + cheatsCBATemporaryValue = cheatsCBAEncWorker(); + } + cheatsCBASeed[2] = cheatsCBAEncWorker(); + cheatsCBASeed[3] = cheatsCBAEncWorker(); + + cheatsCBATemporaryValue = seed[3] ^ 0xf254; + + for(i = 0; (u32)i < seed[3]; i++) { + cheatsCBATemporaryValue = cheatsCBAEncWorker(); + } + + cheatsCBASeed[0] = cheatsCBAEncWorker(); + cheatsCBASeed[1] = cheatsCBAEncWorker(); + + *((u32 *)&cheatsCBACurrentSeed[0]) = seed[6]; + *((u32 *)&cheatsCBACurrentSeed[4]) = seed[7]; + *((u32 *)&cheatsCBACurrentSeed[8]) = 0; +} + +u16 cheatsCBAGenValue(u32 x, u32 y, u32 z) +{ + y <<= 0x10; + z <<= 0x10; + x <<= 0x18; + u32 x0 = (int)y >> 0x10; + z = (int)z >> 0x10; + x = (int)x >> 0x10; + for(int i = 0; i < 8; i++) { + u32 temp = z ^ x; + if ((int)temp >= 0) { + temp = z << 0x11; + } + else { + temp = z << 0x01; + temp ^= x0; + temp = temp << 0x10; + } + z = (int)temp >> 0x10; + temp = x << 0x11; + x = (int)temp >> 0x10; + } + return z & 0xffff; +} + +void cheatsCBAGenTable() { + for (int i = 0; i < 0x100; i++) { + cheatsCBATable[i] = cheatsCBAGenValue(i, 0x1021, 0); + } + cheatsCBATableGenerated = true; +} + +u16 cheatsCBACalcCRC(u8 *rom, int count) +{ + u32 crc = 0xffffffff; + + if (count & 3) { + // 0x08000EAE + } else { + count = (count >> 2) - 1; + if(count != -1) { + while(count != -1) { + crc = (((crc << 0x08) ^ cheatsCBATable[(((u32)crc << 0x10) >> 0x18) + ^ *rom++]) << 0x10) >> 0x10; + crc = (((crc << 0x08) ^ cheatsCBATable[(((u32)crc << 0x10) >> 0x18) + ^ *rom++]) << 0x10) >> 0x10; + crc = (((crc << 0x08) ^ cheatsCBATable[(((u32)crc << 0x10) >> 0x18) + ^ *rom++]) << 0x10) >> 0x10; + crc = (((crc << 0x08) ^ cheatsCBATable[(((u32)crc << 0x10) >> 0x18) + ^ *rom++]) << 0x10) >> 0x10; + count--; + } + } + } + return crc & 0xffff; +} + +void cheatsCBADecrypt(u8 *decrypt) +{ + u8 buffer[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + u8 *array = &buffer[1]; + + cheatsCBAReverseArray(decrypt, array); + + for(int count = 0x2f; count >= 0; count--) { + chatsCBAScramble(array, count, cheatsCBASeedBuffer[count]); + } + cheatsCBAArrayToValue(array, decrypt); + *((u32 *)decrypt) = cheatsCBAGetValue(decrypt) ^ + cheatsCBASeed[0]; + *((u16 *)(decrypt+4)) = (cheatsCBAGetData(decrypt) ^ + cheatsCBASeed[1]) & 0xffff; + + cheatsCBAReverseArray(decrypt, array); + + u32 cs = cheatsCBAGetValue(cheatsCBACurrentSeed); + for(int i = 0; i <= 4; i++) { + array[i] = ((cs >> 8) ^ array[i+1]) ^ array[i] ; + } + + array[5] = (cs >> 8) ^ array[5]; + + for(int j = 5; j >=0; j--) { + array[j] = (cs ^ array[j-1]) ^ array[j]; + } + + cheatsCBAArrayToValue(array, decrypt); + + *((u32 *)decrypt) = cheatsCBAGetValue(decrypt) + ^ cheatsCBASeed[2]; + *((u16 *)(decrypt+4)) = (cheatsCBAGetData(decrypt) + ^ cheatsCBASeed[3]) & 0xffff; +} + +int cheatsCBAGetCount() +{ + int count = 0; + for(int i = 0; i < cheatsNumber; i++) { + if(cheatsList[i].code == 512) + count++; + } + return count; +} + +bool cheatsCBAShouldDecrypt() +{ + for(int i = 0; i < cheatsNumber; i++) { + if(cheatsList[i].code == 512) { + return (cheatsList[i].codestring[0] == '9'); + } + } + return false; +} + +void cheatsAddCBACode(const char *code, const char *desc) +{ + if(strlen(code) != 13) { + // wrong cheat + systemMessage(MSG_INVALID_CBA_CODE, + N_("Invalid CBA code. Format is XXXXXXXX YYYY.")); + return; + } + + int i; + for(i = 0; i < 8; i++) { + if(!CHEAT_IS_HEX(code[i])) { + // wrong cheat + systemMessage(MSG_INVALID_CBA_CODE, + N_("Invalid CBA code. Format is XXXXXXXX YYYY.")); + return; + } + } + + if(code[8] != ' ') { + systemMessage(MSG_INVALID_CBA_CODE, + N_("Invalid CBA code. Format is XXXXXXXX YYYY.")); + return; + } + + for(i = 9; i < 13; i++) { + if(!CHEAT_IS_HEX(code[i])) { + // wrong cheat + systemMessage(MSG_INVALID_CBA_CODE, + N_("Invalid CBA code. Format is XXXXXXXX YYYY.")); + return; + } + } + + char buffer[10]; + strncpy(buffer, code, 8); + buffer[8] = 0; + u32 address; + sscanf(buffer, "%x", &address); + strncpy(buffer, &code[9], 4); + buffer[4] = 0; + u32 value; + sscanf(buffer, "%x", &value); + + u8 array[8] = { + address & 255, + (address >> 8) & 255, + (address >> 16) & 255, + (address >> 24) & 255, + (value & 255), + (value >> 8) & 255, + 0, + 0 + }; + + if(cheatsCBAGetCount() == 0 && + (address >> 28) == 9) { + u32 seed[8]; + cheatsCBAParseSeedCode(address, value, seed); + cheatsCBAChangeEncryption(seed); + cheatsAdd(code, desc, address, address & 0x0FFFFFFF, value, 512, UNKNOWN_CODE); + } else { + if(cheatsCBAShouldDecrypt()) + cheatsCBADecrypt(array); + + address = READ32LE(((u32 *)array)); + value = READ16LE(((u16 *)&array[4])); + + int type = (address >> 28) & 15; + + if(isMultilineWithData(cheatsNumber-1) || (super>0)) { + cheatsAdd(code, desc, address, address, value, 512, UNKNOWN_CODE); + if (super>0) + super-= 1; + return; + } + + switch(type) { + case 0x00: + { + if(!cheatsCBATableGenerated) + cheatsCBAGenTable(); + u32 crc = cheatsCBACalcCRC(rom, 0x10000); + if(crc != address) { + systemMessage(MSG_CBA_CODE_WARNING, + N_("Warning: Codes seem to be for a different game.\nCodes may not work correctly.")); + } + cheatsAdd(code, desc, address, address & 0x0FFFFFFF, value, 512, + UNKNOWN_CODE); + } + break; + case 0x01: + cheatsAdd(code, desc, address, (address & 0x1FFFFFF) | 0x08000000, value, 512, MASTER_CODE); + mastercode = (address & 0x1FFFFFF) | 0x08000000; + break; + case 0x02: + cheatsAdd(code, desc, address, address & 0x0FFFFFFE, value, 512, + CBA_OR); + break; + case 0x03: + cheatsAdd(code, desc, address, address & 0x0FFFFFFF, value, 512, + INT_8_BIT_WRITE); + break; + case 0x04: + cheatsAdd(code, desc, address, address & 0x0FFFFFFE, value, 512, + CBA_SLIDE_CODE); + break; + case 0x05: + cheatsAdd(code, desc, address, address & 0x0FFFFFFE, value, 512, + CBA_SUPER); + super = getCodeLength(cheatsNumber-1); + break; + case 0x06: + cheatsAdd(code, desc, address, address & 0x0FFFFFFE, value, 512, + CBA_AND); + break; + case 0x07: + cheatsAdd(code, desc, address, address & 0x0FFFFFFE, value, 512, + CBA_IF_TRUE); + break; + case 0x08: + cheatsAdd(code, desc, address, address & 0x0FFFFFFE, value, 512, + INT_16_BIT_WRITE); + break; + case 0x0a: + cheatsAdd(code, desc, address, address & 0x0FFFFFFE, value, 512, + CBA_IF_FALSE); + break; + case 0x0b: + cheatsAdd(code, desc, address, address & 0x0FFFFFFE, value, 512, + CBA_GT); + break; + case 0x0c: + cheatsAdd(code, desc, address, address & 0x0FFFFFFE, value, 512, + CBA_LT); + break; + case 0x0d: + if ((address & 0xF0)<0x30) + cheatsAdd(code, desc, address, address & 0xF0, value, 512, + CBA_IF_KEYS_PRESSED); + break; + case 0x0e: + cheatsAdd(code, desc, address, address & 0x0FFFFFFF, value & 0x8000 ? value | 0xFFFF0000 : value, 512, + CBA_ADD); + break; + case 0x0f: + cheatsAdd(code, desc, address, address & 0x0FFFFFFE, value, 512, + GSA_16_BIT_IF_AND); + break; + default: + // unsupported code + cheatsAdd(code, desc, address, address & 0xFFFFFFFF, value, 512, + UNKNOWN_CODE); + break; + } + } +} + +void cheatsSaveGame(gzFile file) +{ + utilWriteInt(file, cheatsNumber); + + utilGzWrite(file, cheatsList, sizeof(cheatsList)); +} + +void cheatsReadGame(gzFile file, int version) +{ + cheatsNumber = 0; + + cheatsNumber = utilReadInt(file); + + if (version > 8) + utilGzRead(file, cheatsList, sizeof(cheatsList)); + + + bool firstCodeBreaker = true; + + for(int i = 0; i < cheatsNumber; i++) { + if (version <9) + { + cheatsList[i].code = utilReadInt(file); + cheatsList[i].size = utilReadInt(file); + cheatsList[i].status = utilReadInt(file); + cheatsList[i].enabled = utilReadInt(file) ? true : false; + utilGzRead(file, &cheatsList[i].address, sizeof(u32)); + cheatsList[i].rawaddress = cheatsList[i].address; + utilGzRead(file, &cheatsList[i].value, sizeof(u32)); + utilGzRead(file, &cheatsList[i].oldValue, sizeof(u32)); + utilGzRead(file, &cheatsList[i].codestring, 20*sizeof(char)); + utilGzRead(file, &cheatsList[i].desc, 32*sizeof(char)); + } + + cheatsList[i].status = 0; + if(!cheatsList[i].codestring[0]) { + switch(cheatsList[i].size) { + case 0: + sprintf(cheatsList[i].codestring, "%08x:%02x", cheatsList[i].address, + cheatsList[i].value); + break; + case 1: + sprintf(cheatsList[i].codestring, "%08x:%04x", cheatsList[i].address, + cheatsList[i].value); + break; + case 2: + sprintf(cheatsList[i].codestring, "%08x:%08x", cheatsList[i].address, + cheatsList[i].value); + break; + } + } + + if(cheatsList[i].enabled) { + cheatsEnable(i); + } + + if(cheatsList[i].code == 512 && firstCodeBreaker) { + firstCodeBreaker = false; + char buffer[10]; + strncpy(buffer, cheatsList[i].codestring, 8); + buffer[8] = 0; + u32 address; + sscanf(buffer, "%x", &address); + if((address >> 28) == 9) { + strncpy(buffer, &cheatsList[i].codestring[9], 4); + buffer[4] = 0; + u32 value; + sscanf(buffer, "%x", &value); + + u32 seed[8]; + cheatsCBAParseSeedCode(address, value, seed); + cheatsCBAChangeEncryption(seed); + } + } + } +} + + +// skip the cheat list data +void cheatsReadGameSkip( gzFile file, int version ) +{ + int nCheats = 0; + nCheats = utilReadInt( file ); + + if( version >= 9 ) { + utilGzSeek( file, sizeof( cheatsList ), SEEK_CUR ); + } + + for( int i = 0; i < nCheats; i++ ) { + if( version < 9 ) { + utilGzSeek( file, ( 7 * sizeof(int) ) + ( 52 * sizeof(char) ), SEEK_CUR ); + } + } +} + + +void cheatsSaveCheatList(const char *file) +{ + if(cheatsNumber == 0) + return; + FILE *f = fopen(file, "wb"); + if(f == NULL) + return; + int version = 1; + fwrite(&version, 1, sizeof(version), f); + int type = 1; + fwrite(&type, 1, sizeof(type), f); + fwrite(&cheatsNumber, 1, sizeof(cheatsNumber), f); + fwrite(cheatsList, 1, sizeof(cheatsList), f); + fclose(f); +} + +bool cheatsLoadCheatList(const char *file) +{ + + int count = 0; + + FILE *f = fopen(file, "rb"); + + if(f == NULL) + return false; + + int version = 0; + + if(fread(&version, 1, sizeof(version), f) != sizeof(version)) { + fclose(f); + return false; + } + + if(version != 1) { + systemMessage(MSG_UNSUPPORTED_CHEAT_LIST_VERSION, + N_("Unsupported cheat list version %d"), version); + fclose(f); + return false; + } + + int type = 0; + if(fread(&type, 1, sizeof(type), f) != sizeof(type)) { + fclose(f); + return false; + } + + + if((type != 0) && (type != 1)) { + systemMessage(MSG_UNSUPPORTED_CHEAT_LIST_TYPE, + N_("Unsupported cheat list type %d"), type); + fclose(f); + return false; + } + + if(fread(&count, 1, sizeof(count), f) != sizeof(count)) { + fclose(f); + return false; + } + if (type == 1) + { + if(fread(cheatsList, 1, sizeof(cheatsList), f) != sizeof(cheatsList)) { + fclose(f); + return false; + } + } + else if (type == 0) + { + for(int i = 0; i < count; i++) { + fread(&cheatsList[i].code, 1, sizeof(int),f); + fread(&cheatsList[i].size, 1, sizeof(int),f); + fread(&cheatsList[i].status, 1, sizeof(int),f); + fread(&cheatsList[i].enabled, 1, sizeof(int),f); + cheatsList[i].enabled = cheatsList[i].enabled ? true : false; + fread(&cheatsList[i].address, 1, sizeof(u32),f); + cheatsList[i].rawaddress = cheatsList[i].address; + fread(&cheatsList[i].value, 1, sizeof(u32),f); + fread(&cheatsList[i].oldValue, 1, sizeof(u32),f); + fread(&cheatsList[i].codestring, 1, 20*sizeof(char),f); + if(fread(&cheatsList[i].desc, 1, 32*sizeof(char),f) != 32*sizeof(char)) { + fclose(f); + return false; + } + } + } + + bool firstCodeBreaker = true; + + for(int i = 0; i < count; i++) { + cheatsList[i].status = 0; // remove old status as it is not used + if(!cheatsList[i].codestring[0]) { + switch(cheatsList[i].size) { + case 0: + sprintf(cheatsList[i].codestring, "%08x:%02x", cheatsList[i].address, + cheatsList[i].value); + break; + case 1: + sprintf(cheatsList[i].codestring, "%08x:%04x", cheatsList[i].address, + cheatsList[i].value); + break; + case 2: + sprintf(cheatsList[i].codestring, "%08x:%08x", cheatsList[i].address, + cheatsList[i].value); + break; + } + } + + if(cheatsList[i].code == 512 && firstCodeBreaker) { + firstCodeBreaker = false; + char buffer[10]; + strncpy(buffer, cheatsList[i].codestring, 8); + buffer[8] = 0; + u32 address; + sscanf(buffer, "%x", &address); + if((address >> 28) == 9) { + strncpy(buffer, &cheatsList[i].codestring[9], 4); + buffer[4] = 0; + u32 value; + sscanf(buffer, "%x", &value); + + u32 seed[8]; + cheatsCBAParseSeedCode(address, value, seed); + cheatsCBAChangeEncryption(seed); + } + } + } + cheatsNumber = count; + fclose(f); + return true; +} + +extern int cpuNextEvent; + +extern void debuggerBreakOnWrite(u32 , u32, u32, int, int); + +#ifdef BKPT_SUPPORT +static u8 cheatsGetType(u32 address) +{ + switch(address >> 24) { + case 2: + return freezeWorkRAM[address & 0x3FFFF]; + case 3: + return freezeInternalRAM[address & 0x7FFF]; + case 5: + return freezePRAM[address & 0x3FC]; + case 6: + if (address > 0x06010000) + return freezeVRAM[address & 0x17FFF]; + else + return freezeVRAM[address & 0x1FFFF]; + case 7: + return freezeOAM[address & 0x3FC]; + } + return 0; +} +#endif + +void cheatsWriteMemory(u32 address, u32 value) +{ +#ifdef BKPT_SUPPORT +#ifdef SDL + if(cheatsNumber == 0) { + int type = cheatsGetType(address); + u32 oldValue = debuggerReadMemory(address); + if(type == 1 || (type == 2 && oldValue != value)) { + debuggerBreakOnWrite(address, oldValue, value, 2, type); + cpuNextEvent = 0; + } + debuggerWriteMemory(address, value); + } +#endif +#endif +} + +void cheatsWriteHalfWord(u32 address, u16 value) +{ +#ifdef BKPT_SUPPORT +#ifdef SDL + if(cheatsNumber == 0) { + int type = cheatsGetType(address); + u16 oldValue = debuggerReadHalfWord(address); + if(type == 1 || (type == 2 && oldValue != value)) { + debuggerBreakOnWrite(address, oldValue, value, 1, type); + cpuNextEvent = 0; + } + debuggerWriteHalfWord(address, value); + } +#endif +#endif +} + +#if defined BKPT_SUPPORT && defined SDL +void cheatsWriteByte(u32 address, u8 value) +#else +void cheatsWriteByte(u32, u8) +#endif +{ +#ifdef BKPT_SUPPORT +#ifdef SDL + if(cheatsNumber == 0) { + int type = cheatsGetType(address); + u8 oldValue = debuggerReadByte(address); + if(type == 1 || (type == 2 && oldValue != value)) { + debuggerBreakOnWrite(address, oldValue, value, 0, type); + cpuNextEvent = 0; + } + debuggerWriteByte(address, value); + } +#endif +#endif +} +#endif diff --git a/src/gba/Cheats.h b/src/gba/Cheats.h new file mode 100644 index 0000000..a930880 --- /dev/null +++ b/src/gba/Cheats.h @@ -0,0 +1,39 @@ +#ifndef CHEATS_H +#define CHEATS_H + +struct CheatsData { + int code; + int size; + int status; + bool enabled; + u32 rawaddress; + u32 address; + u32 value; + u32 oldValue; + char codestring[20]; + char desc[32]; +}; + +void cheatsAdd(const char *codeStr, const char *desc, u32 rawaddress, u32 address, u32 value, int code, int size); +void cheatsAddCheatCode(const char *code, const char *desc); +void cheatsAddGSACode(const char *code, const char *desc, bool v3); +void cheatsAddCBACode(const char *code, const char *desc); +bool cheatsImportGSACodeFile(const char *name, int game, bool v3); +void cheatsDelete(int number, bool restore); +void cheatsDeleteAll(bool restore); +void cheatsEnable(int number); +void cheatsDisable(int number); +void cheatsSaveGame(gzFile file); +void cheatsReadGame(gzFile file, int version); +void cheatsReadGameSkip(gzFile file, int version); +void cheatsSaveCheatList(const char *file); +bool cheatsLoadCheatList(const char *file); +void cheatsWriteMemory(u32 address, u32 value); +void cheatsWriteHalfWord(u32 address, u16 value); +void cheatsWriteByte(u32 address, u8 value); +int cheatsCheckKeys(u32 keys, u32 extended); + +extern int cheatsNumber; +extern CheatsData cheatsList[100]; + +#endif // CHEATS_H diff --git a/src/gba/EEprom.cpp b/src/gba/EEprom.cpp new file mode 100644 index 0000000..4dbb3b8 --- /dev/null +++ b/src/gba/EEprom.cpp @@ -0,0 +1,221 @@ +#include +#include +#include "GBA.h" +#include "EEprom.h" +#include "../Util.h" + +extern int cpuDmaCount; + +int eepromMode = EEPROM_IDLE; +int eepromByte = 0; +int eepromBits = 0; +int eepromAddress = 0; + +#ifdef __LIBRETRO__ +// Workaround for broken-by-design GBA save semantics +extern u8 libretro_save_buf[0x20000 + 0x2000]; +u8 *eepromData = libretro_save_buf + 0x20000; +#else +u8 eepromData[0x2000]; +#endif + +u8 eepromBuffer[16]; +bool eepromInUse = false; +int eepromSize = 512; + +variable_desc eepromSaveData[] = { + { &eepromMode, sizeof(int) }, + { &eepromByte, sizeof(int) }, + { &eepromBits , sizeof(int) }, + { &eepromAddress , sizeof(int) }, + { &eepromInUse, sizeof(bool) }, + { &eepromData[0], 512 }, + { &eepromBuffer[0], 16 }, + { NULL, 0 } +}; + +void eepromInit() +{ +#ifdef __LIBRETRO__ + memset(eepromData, 255, 0x2000); +#else + memset(eepromData, 255, sizeof(eepromData)); +#endif +} + +void eepromReset() +{ + eepromMode = EEPROM_IDLE; + eepromByte = 0; + eepromBits = 0; + eepromAddress = 0; + eepromInUse = false; + eepromSize = 512; +} + +#ifdef __LIBRETRO__ +void eepromSaveGame(uint8_t *& data) +{ + utilWriteDataMem(data, eepromSaveData); + utilWriteIntMem(data, eepromSize); + utilWriteMem(data, eepromData, 0x2000); +} + +void eepromReadGame(const uint8_t *& data, int version) +{ + utilReadDataMem(data, eepromSaveData); + if (version >= SAVE_GAME_VERSION_3) { + eepromSize = utilReadIntMem(data); + utilReadMem(eepromData, data, 0x2000); + } else { + // prior to 0.7.1, only 4K EEPROM was supported + eepromSize = 512; + } +} +#else +void eepromSaveGame(gzFile gzFile) +{ + utilWriteData(gzFile, eepromSaveData); + utilWriteInt(gzFile, eepromSize); + utilGzWrite(gzFile, eepromData, 0x2000); +} + +void eepromReadGame(gzFile gzFile, int version) +{ + utilReadData(gzFile, eepromSaveData); + if(version >= SAVE_GAME_VERSION_3) { + eepromSize = utilReadInt(gzFile); + utilGzRead(gzFile, eepromData, 0x2000); + } else { + // prior to 0.7.1, only 4K EEPROM was supported + eepromSize = 512; + } +} + +void eepromReadGameSkip(gzFile gzFile, int version) +{ + // skip the eeprom data in a save game + utilReadDataSkip(gzFile, eepromSaveData); + if(version >= SAVE_GAME_VERSION_3) { + utilGzSeek(gzFile, sizeof(int), SEEK_CUR); + utilGzSeek(gzFile, 0x2000, SEEK_CUR); + } +} +#endif + +int eepromRead(u32 /* address */) +{ + switch(eepromMode) { + case EEPROM_IDLE: + case EEPROM_READADDRESS: + case EEPROM_WRITEDATA: + return 1; + case EEPROM_READDATA: + { + eepromBits++; + if(eepromBits == 4) { + eepromMode = EEPROM_READDATA2; + eepromBits = 0; + eepromByte = 0; + } + return 0; + } + case EEPROM_READDATA2: + { + int data = 0; + int address = eepromAddress << 3; + int mask = 1 << (7 - (eepromBits & 7)); + data = (eepromData[address+eepromByte] & mask) ? 1 : 0; + eepromBits++; + if((eepromBits & 7) == 0) + eepromByte++; + if(eepromBits == 0x40) + eepromMode = EEPROM_IDLE; + return data; + } + default: + return 0; + } + return 1; +} + +void eepromWrite(u32 /* address */, u8 value) +{ + if(cpuDmaCount == 0) + return; + int bit = value & 1; + switch(eepromMode) { + case EEPROM_IDLE: + eepromByte = 0; + eepromBits = 1; + eepromBuffer[eepromByte] = bit; + eepromMode = EEPROM_READADDRESS; + break; + case EEPROM_READADDRESS: + eepromBuffer[eepromByte] <<= 1; + eepromBuffer[eepromByte] |= bit; + eepromBits++; + if((eepromBits & 7) == 0) { + eepromByte++; + } + if(cpuDmaCount == 0x11 || cpuDmaCount == 0x51) { + if(eepromBits == 0x11) { + eepromInUse = true; + eepromSize = 0x2000; + eepromAddress = ((eepromBuffer[0] & 0x3F) << 8) | + ((eepromBuffer[1] & 0xFF)); + if(!(eepromBuffer[0] & 0x40)) { + eepromBuffer[0] = bit; + eepromBits = 1; + eepromByte = 0; + eepromMode = EEPROM_WRITEDATA; + } else { + eepromMode = EEPROM_READDATA; + eepromByte = 0; + eepromBits = 0; + } + } + } else { + if(eepromBits == 9) { + eepromInUse = true; + eepromAddress = (eepromBuffer[0] & 0x3F); + if(!(eepromBuffer[0] & 0x40)) { + eepromBuffer[0] = bit; + eepromBits = 1; + eepromByte = 0; + eepromMode = EEPROM_WRITEDATA; + } else { + eepromMode = EEPROM_READDATA; + eepromByte = 0; + eepromBits = 0; + } + } + } + break; + case EEPROM_READDATA: + case EEPROM_READDATA2: + // should we reset here? + eepromMode = EEPROM_IDLE; + break; + case EEPROM_WRITEDATA: + eepromBuffer[eepromByte] <<= 1; + eepromBuffer[eepromByte] |= bit; + eepromBits++; + if((eepromBits & 7) == 0) { + eepromByte++; + } + if(eepromBits == 0x40) { + eepromInUse = true; + // write data; + for(int i = 0; i < 8; i++) { + eepromData[(eepromAddress << 3) + i] = eepromBuffer[i]; + } + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } else if(eepromBits == 0x41) { + eepromMode = EEPROM_IDLE; + eepromByte = 0; + eepromBits = 0; + } + break; + } +} diff --git a/src/gba/EEprom.h b/src/gba/EEprom.h new file mode 100644 index 0000000..e72f5b8 --- /dev/null +++ b/src/gba/EEprom.h @@ -0,0 +1,30 @@ +#ifndef EEPROM_H +#define EEPROM_H + +#ifdef __LIBRETRO__ +extern void eepromSaveGame(u8* &data); +extern void eepromReadGame(const u8 *&data, int version); +#else +extern void eepromSaveGame(gzFile _gzFile); +extern void eepromReadGame(gzFile _gzFile, int version); +#endif +extern void eepromReadGameSkip(gzFile _gzFile, int version); +extern int eepromRead(u32 address); +extern void eepromWrite(u32 address, u8 value); +extern void eepromInit(); +extern void eepromReset(); +#ifdef __LIBRETRO__ +extern u8 *eepromData; +#else +extern u8 eepromData[0x2000]; +#endif +extern bool eepromInUse; +extern int eepromSize; + +#define EEPROM_IDLE 0 +#define EEPROM_READADDRESS 1 +#define EEPROM_READDATA 2 +#define EEPROM_READDATA2 3 +#define EEPROM_WRITEDATA 4 + +#endif // EEPROM_H diff --git a/src/gba/Flash.cpp b/src/gba/Flash.cpp new file mode 100644 index 0000000..0b4a17d --- /dev/null +++ b/src/gba/Flash.cpp @@ -0,0 +1,281 @@ +#include +#include +#include +#include "GBA.h" +#include "Globals.h" +#include "Flash.h" +#include "Sram.h" +#include "../Util.h" + +#define FLASH_READ_ARRAY 0 +#define FLASH_CMD_1 1 +#define FLASH_CMD_2 2 +#define FLASH_AUTOSELECT 3 +#define FLASH_CMD_3 4 +#define FLASH_CMD_4 5 +#define FLASH_CMD_5 6 +#define FLASH_ERASE_COMPLETE 7 +#define FLASH_PROGRAM 8 +#define FLASH_SETBANK 9 + +#ifdef __LIBRETRO__ +extern uint8_t libretro_save_buf[0x20000 + 0x2000]; +uint8_t *flashSaveMemory = libretro_save_buf; +#else +uint8_t flashSaveMemory[FLASH_128K_SZ]; +#endif + +int flashState = FLASH_READ_ARRAY; +int flashReadState = FLASH_READ_ARRAY; +int flashSize = 0x10000; +int flashDeviceID = 0x1b; +int flashManufacturerID = 0x32; +int flashBank = 0; + +static variable_desc flashSaveData[] = { + { &flashState, sizeof(int) }, + { &flashReadState, sizeof(int) }, + { &flashSaveMemory[0], 0x10000 }, + { NULL, 0 } +}; + +static variable_desc flashSaveData2[] = { + { &flashState, sizeof(int) }, + { &flashReadState, sizeof(int) }, + { &flashSize, sizeof(int) }, + { &flashSaveMemory[0], 0x20000 }, + { NULL, 0 } +}; + +static variable_desc flashSaveData3[] = { + { &flashState, sizeof(int) }, + { &flashReadState, sizeof(int) }, + { &flashSize, sizeof(int) }, + { &flashBank, sizeof(int) }, + { &flashSaveMemory[0], 0x20000 }, + { NULL, 0 } +}; + +void flashInit() +{ +#ifdef __LIBRETRO__ + memset(flashSaveMemory, 0xff, 0x20000); +#else + memset(flashSaveMemory, 0xff, sizeof(flashSaveMemory)); +#endif +} + +void flashReset() +{ + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + flashBank = 0; +} + +#ifdef __LIBRETRO__ +void flashSaveGame(uint8_t *& data) +{ + utilWriteDataMem(data, flashSaveData3); +} + +void flashReadGame(const uint8_t *& data, int) +{ + utilReadDataMem(data, flashSaveData3); +} +#else +void flashSaveGame(gzFile gzFile) +{ + utilWriteData(gzFile, flashSaveData3); +} + +void flashReadGame(gzFile gzFile, int version) +{ + if(version < SAVE_GAME_VERSION_5) + utilReadData(gzFile, flashSaveData); + else if(version < SAVE_GAME_VERSION_7) { + utilReadData(gzFile, flashSaveData2); + flashBank = 0; + flashSetSize(flashSize); + } else { + utilReadData(gzFile, flashSaveData3); + } +} + +void flashReadGameSkip(gzFile gzFile, int version) +{ + // skip the flash data in a save game + if(version < SAVE_GAME_VERSION_5) + utilReadDataSkip(gzFile, flashSaveData); + else if(version < SAVE_GAME_VERSION_7) { + utilReadDataSkip(gzFile, flashSaveData2); + } else { + utilReadDataSkip(gzFile, flashSaveData3); + } +} +#endif + + +void flashSetSize(int size) +{ + // log("Setting flash size to %d\n", size); + if(size == 0x10000) { + flashDeviceID = 0x1b; + flashManufacturerID = 0x32; + } else { + flashDeviceID = 0x13; //0x09; + flashManufacturerID = 0x62; //0xc2; + } + // Added to make 64k saves compatible with 128k ones + // (allow wrongfuly set 64k saves to work for Pokemon games) + if ((size == 0x20000) && (flashSize == 0x10000)) + memcpy((u8 *)(flashSaveMemory+0x10000), (u8 *)(flashSaveMemory), 0x10000); + flashSize = size; +} + +u8 flashRead(u32 address) +{ + // log("Reading %08x from %08x\n", address, reg[15].I); + // log("Current read state is %d\n", flashReadState); + address &= 0xFFFF; + + switch(flashReadState) { + case FLASH_READ_ARRAY: + return flashSaveMemory[(flashBank << 16) + address]; + case FLASH_AUTOSELECT: + switch(address & 0xFF) { + case 0: + // manufacturer ID + return flashManufacturerID; + case 1: + // device ID + return flashDeviceID; + } + break; + case FLASH_ERASE_COMPLETE: + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + return 0xFF; + }; + return 0; +} + +void flashSaveDecide(u32 address, u8 byte) +{ + // log("Deciding save type %08x\n", address); + if(address == 0x0e005555) { + saveType = 2; + cpuSaveGameFunc = flashWrite; + } else { + saveType = 1; + cpuSaveGameFunc = sramWrite; + } + + (*cpuSaveGameFunc)(address, byte); +} + +void flashDelayedWrite(u32 address, u8 byte) +{ + saveType = 2; + cpuSaveGameFunc = flashWrite; + flashWrite(address, byte); +} + +void flashWrite(u32 address, u8 byte) +{ + // log("Writing %02x at %08x\n", byte, address); + // log("Current state is %d\n", flashState); + address &= 0xFFFF; + switch(flashState) { + case FLASH_READ_ARRAY: + if(address == 0x5555 && byte == 0xAA) + flashState = FLASH_CMD_1; + break; + case FLASH_CMD_1: + if(address == 0x2AAA && byte == 0x55) + flashState = FLASH_CMD_2; + else + flashState = FLASH_READ_ARRAY; + break; + case FLASH_CMD_2: + if(address == 0x5555) { + if(byte == 0x90) { + flashState = FLASH_AUTOSELECT; + flashReadState = FLASH_AUTOSELECT; + } else if(byte == 0x80) { + flashState = FLASH_CMD_3; + } else if(byte == 0xF0) { + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + } else if(byte == 0xA0) { + flashState = FLASH_PROGRAM; + } else if(byte == 0xB0 && flashSize == 0x20000) { + flashState = FLASH_SETBANK; + } else { + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + } + } else { + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + } + break; + case FLASH_CMD_3: + if(address == 0x5555 && byte == 0xAA) { + flashState = FLASH_CMD_4; + } else { + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + } + break; + case FLASH_CMD_4: + if(address == 0x2AAA && byte == 0x55) { + flashState = FLASH_CMD_5; + } else { + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + } + break; + case FLASH_CMD_5: + if(byte == 0x30) { + // SECTOR ERASE + memset(&flashSaveMemory[(flashBank << 16) + (address & 0xF000)], + 0, + 0x1000); + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + flashReadState = FLASH_ERASE_COMPLETE; + } else if(byte == 0x10) { + // CHIP ERASE + memset(flashSaveMemory, 0, flashSize); + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + flashReadState = FLASH_ERASE_COMPLETE; + } else { + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + } + break; + case FLASH_AUTOSELECT: + if(byte == 0xF0) { + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + } else if(address == 0x5555 && byte == 0xAA) + flashState = FLASH_CMD_1; + else { + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + } + break; + case FLASH_PROGRAM: + flashSaveMemory[(flashBank<<16)+address] = byte; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + break; + case FLASH_SETBANK: + if(address == 0) { + flashBank = (byte & 1); + } + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + break; + } +} diff --git a/src/gba/Flash.h b/src/gba/Flash.h new file mode 100644 index 0000000..73ef1a5 --- /dev/null +++ b/src/gba/Flash.h @@ -0,0 +1,29 @@ +#ifndef FLASH_H +#define FLASH_H + +#define FLASH_128K_SZ 0x20000 + +#ifdef __LIBRETRO__ +extern void flashSaveGame(u8 *& data); +extern void flashReadGame(const u8 *& data, int); +#else +extern void flashSaveGame(gzFile _gzFile); +extern void flashReadGame(gzFile _gzFile, int version); +#endif +extern void flashReadGameSkip(gzFile _gzFile, int version); +extern u8 flashRead(u32 address); +extern void flashWrite(u32 address, u8 byte); +extern void flashDelayedWrite(u32 address, u8 byte); +#ifdef __LIBRETRO__ +extern uint8_t *flashSaveMemory; +#else +extern u8 flashSaveMemory[FLASH_128K_SZ]; +#endif +extern void flashSaveDecide(u32 address, u8 byte); +extern void flashReset(); +extern void flashSetSize(int size); +extern void flashInit(); + +extern int flashSize; + +#endif // FLASH_H diff --git a/src/gba/GBA-arm.cpp b/src/gba/GBA-arm.cpp new file mode 100644 index 0000000..732f447 --- /dev/null +++ b/src/gba/GBA-arm.cpp @@ -0,0 +1,2967 @@ +#include +#include +#include +#include +#include + +#include "GBA.h" +#include "GBAcpu.h" +#include "GBAinline.h" +#include "Globals.h" +#include "EEprom.h" +#include "Flash.h" +#include "Sound.h" +#include "Sram.h" +#include "bios.h" +#include "Cheats.h" +#include "../NLS.h" +#include "elf.h" +#include "../Util.h" +#include "../System.h" +#include "agbprint.h" +#ifdef PROFILING +#include "prof/prof.h" +#endif + +#ifdef _MSC_VER + // Disable "empty statement" warnings + #pragma warning(disable: 4390) + // Visual C's inline assembler treats "offset" as a reserved word, so we + // tell it otherwise. If you want to use it, write "OFFSET" in capitals. + #define offset offset_ +#endif + +/////////////////////////////////////////////////////////////////////////// + +static int clockTicks; + +static INSN_REGPARM void armUnknownInsn(u32 opcode) +{ +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_UNDEFINED) { + log("Undefined ARM instruction %08x at %08x\n", opcode, + armNextPC-4); + } +#endif + CPUUndefinedException(); +} + +#ifdef BKPT_SUPPORT +static INSN_REGPARM void armBreakpoint(u32 opcode) +{ + reg[15].I -= 4; + armNextPC -= 4; + dbgSignal(5, (opcode & 0x0f) | ((opcode>>4) & 0xfff0)); + clockTicks = -1; +} +#endif + + +// Subroutine to count instructions (for debugging/optimizing) +//#define INSN_COUNTER // comment out if you don't want it +#ifdef INSN_COUNTER +static void count(u32 opcode, int cond_res) +{ + static int insncount = 0; // number of insns seen + static int executed = 0; // number of insns executed + static int mergewith[4096]; // map instructions to routines + static int count[4096]; // count of each 12-bit code + int index = ((opcode>>16)&0xFF0) | ((opcode>>4)&0x0F); + static FILE *outfile = NULL; + + if (!insncount) { + for (int i = 0; i < 4096; i++) { + for (int j = 0; j < i; j++) { + if (armInsnTable[i] == armInsnTable[j]) + break; + } + mergewith[i] = j; + } + outfile = fopen("VBA-armcount.txt", "w"); + } + if (cond_res) { + count[mergewith[index]]++; + executed++; + } + insncount++; + if (outfile && insncount%1000000 == 0) { + fprintf(outfile, "Total instructions: %d\n", insncount); + fprintf(outfile, "Instructions executed: %d\n", executed); + for (int i = 0; i < 4096; i++) { + if (count[i]) + fprintf(outfile, "arm%03X: %d\n", i, count[i]); + } + } +} +#endif + +// Common macros ////////////////////////////////////////////////////////// + +#ifdef BKPT_SUPPORT +#define CONSOLE_OUTPUT(a,b) do { \ + if ((opcode == 0xe0000000) && (reg[0].I == 0xC0DED00D)) { \ + dbgOutput((a), (b)); \ +} while (0) +#else +#define CONSOLE_OUTPUT(a,b) /* nothing */ +#endif + +#define NEG(i) ((i) >> 31) +#define POS(i) ((~(i)) >> 31) + +// The following macros are used for optimization; any not defined for a +// particular compiler/CPU combination default to the C core versions. +// +// ALU_INIT_C: Used at the beginning of ALU instructions (AND/EOR/...). +// (ALU_INIT_NC) Can consist of variable declarations, like the C core, +// or the start of a continued assembly block, like the +// x86-optimized version. The _C version is used when the +// carry flag from the shift operation is needed (logical +// operations that set condition codes, like ANDS); the +// _NC version is used when the carry result is ignored. +// VALUE_XXX: Retrieve the second operand's value for an ALU instruction. +// The _C and _NC versions are used the same way as ALU_INIT. +// OP_XXX: ALU operations. XXX is the instruction name. +// ALU_FINISH: Appended to all ALU instructions. Usually empty, but if +// ALU_INIT started a block ALU_FINISH can be used to end it +// (as with the asm(...) statement in the x86 core). +// SETCOND_NONE: Used in multiply instructions in place of SETCOND_MUL +// when the condition codes are not set. Usually empty. +// SETCOND_MUL: Used in multiply instructions to set the condition codes. +// ROR_IMM_MSR: Used to rotate the immediate operand for MSR. +// ROR_OFFSET: Used to rotate the `offset' parameter for LDR and STR +// instructions. +// RRX_OFFSET: Used to rotate (RRX) the `offset' parameter for LDR and +// STR instructions. + +#ifndef C_CORE + +#if 0 // definitions have changed +//#ifdef __POWERPC__ + #define OP_SUBS \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("subco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[base].I), \ + "r" (value) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define OP_RSBS \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("subfco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[base].I), \ + "r" (value) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define OP_ADDS \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("addco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[base].I), \ + "r" (value) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define OP_ADCS \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("mtspr xer, %4\n" \ + "addeo. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[base].I), \ + "r" (value), \ + "r" (C_FLAG << 29) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define OP_SBCS \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("mtspr xer, %4\n" \ + "subfeo. %0, %3, %2\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[base].I), \ + "r" (value), \ + "r" (C_FLAG << 29) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define OP_RSCS \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("mtspr xer, %4\n" \ + "subfeo. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[base].I), \ + "r" (value), \ + "r" (C_FLAG << 29) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define OP_CMP \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("subco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[base].I), \ + "r" (value) \ + ); \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define OP_CMN \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("addco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[base].I), \ + "r" (value) \ + ); \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + +#else // !__POWERPC__ + +// Macros to emit instructions in the format used by the particular compiler. +// We use GNU assembler syntax: "op src, dest" rather than "op dest, src" + +#ifdef __GNUC__ + #define ALU_HEADER asm("mov %%ecx, %%edi; " + #define ALU_TRAILER : "=D" (opcode) : "c" (opcode) : "eax", "ebx", "edx", "esi") + #define EMIT0(op) #op"; " + #define EMIT1(op,arg) #op" "arg"; " + #define EMIT2(op,src,dest) #op" "src", "dest"; " + #define KONST(val) "$"#val + #define ASMVAR(cvar) ASMVAR2 (__USER_LABEL_PREFIX__, cvar) + #define ASMVAR2(prefix,cvar) STRING (prefix) cvar + #define STRING(x) #x + #define VAR(var) ASMVAR(#var) + #define VARL(var) ASMVAR(#var) + #define REGREF1(index) ASMVAR("reg("index")") + #define REGREF2(index,scale) ASMVAR("reg(,"index","#scale")") + #define LABEL(n) #n": " + #define LABELREF(n,dir) #n#dir + #define al "%%al" + #define ah "%%ah" + #define eax "%%eax" + #define bl "%%bl" + #define bh "%%bh" + #define ebx "%%ebx" + #define cl "%%cl" + #define ch "%%ch" + #define ecx "%%ecx" + #define dl "%%dl" + #define dh "%%dh" + #define edx "%%edx" + #define esp "%%esp" + #define ebp "%%ebp" + #define esi "%%esi" + #define edi "%%edi" + #define movzx movzb +#else + #define ALU_HEADER __asm { __asm mov ecx, opcode + #define ALU_TRAILER } + #define EMIT0(op) __asm op + #define EMIT1(op,arg) __asm op arg + #define EMIT2(op,src,dest) __asm op dest, src + #define KONST(val) val + #define VAR(var) var + #define VARL(var) dword ptr var + #define REGREF1(index) reg[index] + #define REGREF2(index,scale) reg[index*scale] + #define LABEL(n) __asm l##n: + #define LABELREF(n,dir) l##n +#endif + +//X//#ifndef _MSC_VER +// ALU op register usage: +// EAX -> 2nd operand value, result (RSB/RSC) +// EBX -> C_OUT (carry flag from shift/rotate) +// ECX -> opcode (input), shift/rotate count +// EDX -> Rn (base) value, result (all except RSB/RSC) +// ESI -> Rd (destination) index * 4 + +// Helper macros for loading value / shift count +#define VALUE_LOAD_IMM \ + EMIT2(and, KONST(0x0F), eax) \ + EMIT2(mov, REGREF2(eax,4), eax) \ + EMIT2(shr, KONST(7), ecx) \ + EMIT2(and, KONST(0x1F), ecx) +#define VALUE_LOAD_REG \ + EMIT2(and, KONST(0x0F), eax) \ + EMIT2(cmp, KONST(0x0F), eax) \ + EMIT2(mov, REGREF2(eax,4), eax) \ + EMIT1(jne, LABELREF(3,f)) \ + EMIT2(add, KONST(4), eax) \ + LABEL(3) \ + EMIT2(movzx, ch, ecx) \ + EMIT2(and, KONST(0x0F), ecx) \ + EMIT2(mov, REGREF2(ecx,4), ecx) + +// Helper macros for setting flags +#define SETCOND_LOGICAL \ + EMIT1(sets, VAR(N_FLAG)) \ + EMIT1(setz, VAR(Z_FLAG)) \ + EMIT2(mov, bl, VAR(C_FLAG)) +#define SETCOND_ADD \ + EMIT1(sets, VAR(N_FLAG)) \ + EMIT1(setz, VAR(Z_FLAG)) \ + EMIT1(seto, VAR(V_FLAG)) \ + EMIT1(setc, VAR(C_FLAG)) +#define SETCOND_SUB \ + EMIT1(sets, VAR(N_FLAG)) \ + EMIT1(setz, VAR(Z_FLAG)) \ + EMIT1(seto, VAR(V_FLAG)) \ + EMIT1(setnc, VAR(C_FLAG)) + +// ALU initialization +#define ALU_INIT(LOAD_C_FLAG) \ + ALU_HEADER \ + LOAD_C_FLAG \ + EMIT2(mov, ecx, edx) \ + EMIT2(shr, KONST(14), edx) \ + EMIT2(mov, ecx, eax) \ + EMIT2(mov, ecx, esi) \ + EMIT2(shr, KONST(10), esi) \ + EMIT2(and, KONST(0x3C), edx) \ + EMIT2(mov, REGREF1(edx), edx) \ + EMIT2(and, KONST(0x3C), esi) + +#define LOAD_C_FLAG_YES EMIT2(mov, VAR(C_FLAG), bl) +#define LOAD_C_FLAG_NO /*nothing*/ +#define ALU_INIT_C ALU_INIT(LOAD_C_FLAG_YES) +#define ALU_INIT_NC ALU_INIT(LOAD_C_FLAG_NO) + +// Macros to load the value operand for an ALU op; these all set N/Z +// according to the value + +// OP Rd,Rb,Rm LSL # +#define VALUE_LSL_IMM_C \ + VALUE_LOAD_IMM \ + EMIT1(jnz, LABELREF(1,f)) \ + EMIT1(jmp, LABELREF(0,f)) \ + LABEL(1) \ + EMIT2(shl, cl, eax) \ + EMIT1(setc, bl) \ + LABEL(0) +#define VALUE_LSL_IMM_NC \ + VALUE_LOAD_IMM \ + EMIT2(shl, cl, eax) + +// OP Rd,Rb,Rm LSL Rs +#define VALUE_LSL_REG_C \ + VALUE_LOAD_REG \ + EMIT2(test, cl, cl) \ + EMIT1(jz, LABELREF(0,f)) \ + EMIT2(cmp, KONST(0x20), cl) \ + EMIT1(je, LABELREF(1,f)) \ + EMIT1(ja, LABELREF(2,f)) \ + EMIT2(shl, cl, eax) \ + EMIT1(setc, bl) \ + EMIT1(jmp, LABELREF(0,f)) \ + LABEL(1) \ + EMIT2(test, KONST(1), al) \ + EMIT1(setnz, bl) \ + EMIT2(xor, eax, eax) \ + EMIT1(jmp, LABELREF(0,f)) \ + LABEL(2) \ + EMIT2(xor, ebx, ebx) \ + EMIT2(xor, eax, eax) \ + LABEL(0) +#define VALUE_LSL_REG_NC \ + VALUE_LOAD_REG \ + EMIT2(cmp, KONST(0x20), cl) \ + EMIT1(jae, LABELREF(1,f)) \ + EMIT2(shl, cl, eax) \ + EMIT1(jmp, LABELREF(0,f)) \ + LABEL(1) \ + EMIT2(xor, eax, eax) \ + LABEL(0) + +// OP Rd,Rb,Rm LSR # +#define VALUE_LSR_IMM_C \ + VALUE_LOAD_IMM \ + EMIT1(jz, LABELREF(1,f)) \ + EMIT2(shr, cl, eax) \ + EMIT1(setc, bl) \ + EMIT1(jmp, LABELREF(0,f)) \ + LABEL(1) \ + EMIT2(test, eax, eax) \ + EMIT1(sets, bl) \ + EMIT2(xor, eax, eax) \ + LABEL(0) +#define VALUE_LSR_IMM_NC \ + VALUE_LOAD_IMM \ + EMIT1(jz, LABELREF(1,f)) \ + EMIT2(shr, cl, eax) \ + EMIT1(jmp, LABELREF(0,f)) \ + LABEL(1) \ + EMIT2(xor, eax, eax) \ + LABEL(0) + +// OP Rd,Rb,Rm LSR Rs +#define VALUE_LSR_REG_C \ + VALUE_LOAD_REG \ + EMIT2(test, cl, cl) \ + EMIT1(jz, LABELREF(0,f)) \ + EMIT2(cmp, KONST(0x20), cl) \ + EMIT1(je, LABELREF(1,f)) \ + EMIT1(ja, LABELREF(2,f)) \ + EMIT2(shr, cl, eax) \ + EMIT1(setc, bl) \ + EMIT1(jmp, LABELREF(0,f)) \ + LABEL(1) \ + EMIT2(test, eax, eax) \ + EMIT1(sets, bl) \ + EMIT2(xor, eax, eax) \ + EMIT1(jmp, LABELREF(0,f)) \ + LABEL(2) \ + EMIT2(xor, ebx, ebx) \ + EMIT2(xor, eax, eax) \ + LABEL(0) +#define VALUE_LSR_REG_NC \ + VALUE_LOAD_REG \ + EMIT2(cmp, KONST(0x20), cl) \ + EMIT1(jae, LABELREF(1,f)) \ + EMIT2(shr, cl, eax) \ + EMIT1(jmp, LABELREF(0,f)) \ + LABEL(1) \ + EMIT2(xor, eax, eax) \ + LABEL(0) + +// OP Rd,Rb,Rm ASR # +#define VALUE_ASR_IMM_C \ + VALUE_LOAD_IMM \ + EMIT1(jz, LABELREF(1,f)) \ + EMIT2(sar, cl, eax) \ + EMIT1(setc, bl) \ + EMIT1(jmp, LABELREF(0,f)) \ + LABEL(1) \ + EMIT2(sar, KONST(31), eax) \ + EMIT1(sets, bl) \ + LABEL(0) +#define VALUE_ASR_IMM_NC \ + VALUE_LOAD_IMM \ + EMIT1(jz, LABELREF(1,f)) \ + EMIT2(sar, cl, eax) \ + EMIT1(jmp, LABELREF(0,f)) \ + LABEL(1) \ + EMIT2(sar, KONST(31), eax) \ + LABEL(0) + +// OP Rd,Rb,Rm ASR Rs +#define VALUE_ASR_REG_C \ + VALUE_LOAD_REG \ + EMIT2(test, cl, cl) \ + EMIT1(jz, LABELREF(0,f)) \ + EMIT2(cmp, KONST(0x20), cl) \ + EMIT1(jae, LABELREF(1,f)) \ + EMIT2(sar, cl, eax) \ + EMIT1(setc, bl) \ + EMIT1(jmp, LABELREF(0,f)) \ + LABEL(1) \ + EMIT2(sar, KONST(31), eax) \ + EMIT1(sets, bl) \ + LABEL(0) +#define VALUE_ASR_REG_NC \ + VALUE_LOAD_REG \ + EMIT2(cmp, KONST(0x20), cl) \ + EMIT1(jae, LABELREF(1,f)) \ + EMIT2(sar, cl, eax) \ + EMIT1(jmp, LABELREF(0,f)) \ + LABEL(1) \ + EMIT2(sar, KONST(31), eax) \ + LABEL(0) + +// OP Rd,Rb,Rm ROR # +#define VALUE_ROR_IMM_C \ + VALUE_LOAD_IMM \ + EMIT1(jz, LABELREF(1,f)) \ + EMIT2(ror, cl, eax) \ + EMIT1(jmp, LABELREF(0,f)) \ + LABEL(1) \ + EMIT2(bt, KONST(0), ebx) \ + EMIT2(rcr, KONST(1), eax) \ + LABEL(0) \ + EMIT1(setc, bl) +#define VALUE_ROR_IMM_NC \ + VALUE_LOAD_IMM \ + EMIT1(jz, LABELREF(1,f)) \ + EMIT2(ror, cl, eax) \ + EMIT1(jmp, LABELREF(0,f)) \ + LABEL(1) \ + EMIT2(bt, KONST(0), VARL(C_FLAG)) \ + EMIT2(rcr, KONST(1), eax) \ + LABEL(0) + +// OP Rd,Rb,Rm ROR Rs +#define VALUE_ROR_REG_C \ + VALUE_LOAD_REG \ + EMIT2(bt, KONST(0), ebx) \ + EMIT2(ror, cl, eax) \ + EMIT1(setc, bl) +#define VALUE_ROR_REG_NC \ + VALUE_LOAD_REG \ + EMIT2(ror, cl, eax) + +// OP Rd,Rb,# ROR # +#define VALUE_IMM_C \ + EMIT2(movzx, ch, ecx) \ + EMIT2(add, ecx, ecx) \ + EMIT2(movzx, al, eax) \ + EMIT2(bt, KONST(0), ebx) \ + EMIT2(ror, cl, eax) \ + EMIT1(setc, bl) +#define VALUE_IMM_NC \ + EMIT2(movzx, ch, ecx) \ + EMIT2(add, ecx, ecx) \ + EMIT2(movzx, al, eax) \ + EMIT2(ror, cl, eax) + +// Macros to perform ALU ops + +// Set condition codes iff the destination register is not R15 (PC) +#define CHECK_PC(OP, SETCOND) \ + EMIT2(cmp, KONST(0x3C), esi) \ + EMIT1(je, LABELREF(8,f)) \ + OP SETCOND \ + EMIT1(jmp, LABELREF(9,f)) \ + LABEL(8) \ + OP \ + LABEL(9) + +#define OP_AND \ + EMIT2(and, eax, edx) \ + EMIT2(mov, edx, REGREF1(esi)) +#define OP_ANDS CHECK_PC(OP_AND, SETCOND_LOGICAL) +#define OP_EOR \ + EMIT2(xor, eax, edx) \ + EMIT2(mov, edx, REGREF1(esi)) +#define OP_EORS CHECK_PC(OP_EOR, SETCOND_LOGICAL) +#define OP_SUB \ + EMIT2(sub, eax, edx) \ + EMIT2(mov, edx, REGREF1(esi)) +#define OP_SUBS CHECK_PC(OP_SUB, SETCOND_SUB) +#define OP_RSB \ + EMIT2(sub, edx, eax) \ + EMIT2(mov, eax, REGREF1(esi)) +#define OP_RSBS CHECK_PC(OP_RSB, SETCOND_SUB) +#define OP_ADD \ + EMIT2(add, eax, edx) \ + EMIT2(mov, edx, REGREF1(esi)) +#define OP_ADDS CHECK_PC(OP_ADD, SETCOND_ADD) +#define OP_ADC \ + EMIT2(bt, KONST(0), VARL(C_FLAG)) \ + EMIT2(adc, eax, edx) \ + EMIT2(mov, edx, REGREF1(esi)) +#define OP_ADCS CHECK_PC(OP_ADC, SETCOND_ADD) +#define OP_SBC \ + EMIT2(bt, KONST(0), VARL(C_FLAG)) \ + EMIT0(cmc) \ + EMIT2(sbb, eax, edx) \ + EMIT2(mov, edx, REGREF1(esi)) +#define OP_SBCS CHECK_PC(OP_SBC, SETCOND_SUB) +#define OP_RSC \ + EMIT2(bt, KONST(0), VARL(C_FLAG)) \ + EMIT0(cmc) \ + EMIT2(sbb, edx, eax) \ + EMIT2(mov, eax, REGREF1(esi)) +#define OP_RSCS CHECK_PC(OP_RSC, SETCOND_SUB) +#define OP_TST \ + EMIT2(and, eax, edx) \ + SETCOND_LOGICAL +#define OP_TEQ \ + EMIT2(xor, eax, edx) \ + SETCOND_LOGICAL +#define OP_CMP \ + EMIT2(sub, eax, edx) \ + SETCOND_SUB +#define OP_CMN \ + EMIT2(add, eax, edx) \ + SETCOND_ADD +#define OP_ORR \ + EMIT2(or, eax, edx) \ + EMIT2(mov, edx, REGREF1(esi)) +#define OP_ORRS CHECK_PC(OP_ORR, SETCOND_LOGICAL) +#define OP_MOV \ + EMIT2(mov, eax, REGREF1(esi)) +#define OP_MOVS CHECK_PC(EMIT2(test,eax,eax) EMIT2(mov,eax,REGREF1(esi)), SETCOND_LOGICAL) +#define OP_BIC \ + EMIT1(not, eax) \ + EMIT2(and, eax, edx) \ + EMIT2(mov, edx, REGREF1(esi)) +#define OP_BICS CHECK_PC(OP_BIC, SETCOND_LOGICAL) +#define OP_MVN \ + EMIT1(not, eax) \ + EMIT2(mov, eax, REGREF1(esi)) +#define OP_MVNS CHECK_PC(OP_MVN EMIT2(test,eax,eax), SETCOND_LOGICAL) + +// ALU cleanup macro +#define ALU_FINISH ALU_TRAILER + +// End of ALU macros +//X//#endif //_MSC_VER + +#ifdef __GNUC__ + +#define ROR_IMM_MSR \ + asm ("ror %%cl, %%eax;" \ + : "=a" (value) \ + : "a" (opcode & 0xFF), "c" (shift)); + +#define ROR_OFFSET \ + asm("ror %%cl, %0" \ + : "=r" (offset) \ + : "0" (offset), "c" (shift)); + +#define RRX_OFFSET \ + asm(EMIT2(btl,KONST(0),VAR(C_FLAG)) \ + "rcr $1, %0" \ + : "=r" (offset) \ + : "0" (offset)); + +#else // !__GNUC__, i.e. Visual C++ + +#define ROR_IMM_MSR \ + __asm { \ + __asm mov ecx, shift \ + __asm ror value, cl \ + } + + +#define ROR_OFFSET \ + __asm { \ + __asm mov ecx, shift \ + __asm ror offset, cl \ + } + +#define RRX_OFFSET \ + __asm { \ + __asm bt dword ptr C_FLAG, 0 \ + __asm rcr offset, 1 \ + } + +#endif // !__GNUC__ + +#endif // !__POWERPC__ +#endif // !C_CORE + +// C core + +#define C_SETCOND_LOGICAL \ + N_FLAG = ((s32)res < 0) ? true : false; \ + Z_FLAG = (res == 0) ? true : false; \ + C_FLAG = C_OUT; +#define C_SETCOND_ADD \ + N_FLAG = ((s32)res < 0) ? true : false; \ + Z_FLAG = (res == 0) ? true : false; \ + V_FLAG = ((NEG(lhs) & NEG(rhs) & POS(res)) | \ + (POS(lhs) & POS(rhs) & NEG(res))) ? true : false;\ + C_FLAG = ((NEG(lhs) & NEG(rhs)) | \ + (NEG(lhs) & POS(res)) | \ + (NEG(rhs) & POS(res))) ? true : false; +#define C_SETCOND_SUB \ + N_FLAG = ((s32)res < 0) ? true : false; \ + Z_FLAG = (res == 0) ? true : false; \ + V_FLAG = ((NEG(lhs) & POS(rhs) & POS(res)) | \ + (POS(lhs) & NEG(rhs) & NEG(res))) ? true : false;\ + C_FLAG = ((NEG(lhs) & POS(rhs)) | \ + (NEG(lhs) & POS(res)) | \ + (POS(rhs) & POS(res))) ? true : false; + +#ifndef ALU_INIT_C + #define ALU_INIT_C \ + int dest = (opcode>>12) & 15; \ + bool C_OUT = C_FLAG; \ + u32 value; +#endif +// OP Rd,Rb,Rm LSL # +#ifndef VALUE_LSL_IMM_C + #define VALUE_LSL_IMM_C \ + unsigned int shift = (opcode >> 7) & 0x1F; \ + if (LIKELY(!shift)) { /* LSL #0 most common? */ \ + value = reg[opcode & 0x0F].I; \ + } else { \ + u32 v = reg[opcode & 0x0F].I; \ + C_OUT = (v >> (32 - shift)) & 1 ? true : false; \ + value = v << shift; \ + } +#endif +// OP Rd,Rb,Rm LSL Rs +#ifndef VALUE_LSL_REG_C + #define VALUE_LSL_REG_C \ + u32 shift = reg[(opcode >> 8)&15].B.B0; \ + u32 rm = reg[opcode & 0x0F].I; \ + if((opcode & 0x0F) == 15) { \ + rm += 4; \ + } \ + if (LIKELY(shift)) { \ + if (shift == 32) { \ + value = 0; \ + C_OUT = (rm & 1 ? true : false); \ + } else if (LIKELY(shift < 32)) { \ + u32 v = rm; \ + C_OUT = (v >> (32 - shift)) & 1 ? true : false; \ + value = v << shift; \ + } else { \ + value = 0; \ + C_OUT = false; \ + } \ + } else { \ + value = rm; \ + } +#endif +// OP Rd,Rb,Rm LSR # +#ifndef VALUE_LSR_IMM_C + #define VALUE_LSR_IMM_C \ + u32 shift = (opcode >> 7) & 0x1F; \ + if (LIKELY(shift)) { \ + u32 v = reg[opcode & 0x0F].I; \ + C_OUT = (v >> (shift - 1)) & 1 ? true : false; \ + value = v >> shift; \ + } else { \ + value = 0; \ + C_OUT = (reg[opcode & 0x0F].I & 0x80000000) ? true : false;\ + } +#endif +// OP Rd,Rb,Rm LSR Rs +#ifndef VALUE_LSR_REG_C + #define VALUE_LSR_REG_C \ + unsigned int shift = reg[(opcode >> 8)&15].B.B0; \ + u32 rm = reg[opcode & 0x0F].I; \ + if((opcode & 0x0F) == 15) { \ + rm += 4; \ + } \ + if (LIKELY(shift)) { \ + if (shift == 32) { \ + value = 0; \ + C_OUT = (rm & 0x80000000 ? true : false);\ + } else if (LIKELY(shift < 32)) { \ + u32 v = rm; \ + C_OUT = (v >> (shift - 1)) & 1 ? true : false;\ + value = v >> shift; \ + } else { \ + value = 0; \ + C_OUT = false; \ + } \ + } else { \ + value = rm; \ + } +#endif +// OP Rd,Rb,Rm ASR # +#ifndef VALUE_ASR_IMM_C + #define VALUE_ASR_IMM_C \ + unsigned int shift = (opcode >> 7) & 0x1F; \ + if (LIKELY(shift)) { \ + /* VC++ BUG: u32 v; (s32)v>>n is optimized to shr! */ \ + s32 v = reg[opcode & 0x0F].I; \ + C_OUT = (v >> (int)(shift - 1)) & 1 ? true : false;\ + value = v >> (int)shift; \ + } else { \ + if (reg[opcode & 0x0F].I & 0x80000000) { \ + value = 0xFFFFFFFF; \ + C_OUT = true; \ + } else { \ + value = 0; \ + C_OUT = false; \ + } \ + } +#endif +// OP Rd,Rb,Rm ASR Rs +#ifndef VALUE_ASR_REG_C + #define VALUE_ASR_REG_C \ + unsigned int shift = reg[(opcode >> 8)&15].B.B0; \ + u32 rm = reg[opcode & 0x0F].I; \ + if((opcode & 0x0F) == 15) { \ + rm += 4; \ + } \ + if (LIKELY(shift < 32)) { \ + if (LIKELY(shift)) { \ + s32 v = rm; \ + C_OUT = (v >> (int)(shift - 1)) & 1 ? true : false;\ + value = v >> (int)shift; \ + } else { \ + value = rm; \ + } \ + } else { \ + if (reg[opcode & 0x0F].I & 0x80000000) { \ + value = 0xFFFFFFFF; \ + C_OUT = true; \ + } else { \ + value = 0; \ + C_OUT = false; \ + } \ + } +#endif +// OP Rd,Rb,Rm ROR # +#ifndef VALUE_ROR_IMM_C + #define VALUE_ROR_IMM_C \ + unsigned int shift = (opcode >> 7) & 0x1F; \ + if (LIKELY(shift)) { \ + u32 v = reg[opcode & 0x0F].I; \ + C_OUT = (v >> (shift - 1)) & 1 ? true : false; \ + value = ((v << (32 - shift)) | \ + (v >> shift)); \ + } else { \ + u32 v = reg[opcode & 0x0F].I; \ + C_OUT = (v & 1) ? true : false; \ + value = ((v >> 1) | \ + (C_FLAG << 31)); \ + } +#endif +// OP Rd,Rb,Rm ROR Rs +#ifndef VALUE_ROR_REG_C + #define VALUE_ROR_REG_C \ + unsigned int shift = reg[(opcode >> 8)&15].B.B0; \ + u32 rm = reg[opcode & 0x0F].I; \ + if((opcode & 0x0F) == 15) { \ + rm += 4; \ + } \ + if (LIKELY(shift & 0x1F)) { \ + u32 v = rm; \ + C_OUT = (v >> (shift - 1)) & 1 ? true : false; \ + value = ((v << (32 - shift)) | \ + (v >> shift)); \ + } else { \ + value = rm; \ + if (shift) \ + C_OUT = (value & 0x80000000 ? true : false);\ + } +#endif +// OP Rd,Rb,# ROR # +#ifndef VALUE_IMM_C + #define VALUE_IMM_C \ + int shift = (opcode & 0xF00) >> 7; \ + if (UNLIKELY(shift)) { \ + u32 v = opcode & 0xFF; \ + C_OUT = (v >> (shift - 1)) & 1 ? true : false; \ + value = ((v << (32 - shift)) | \ + (v >> shift)); \ + } else { \ + value = opcode & 0xFF; \ + } +#endif + +// Make the non-carry versions default to the carry versions +// (this is fine for C--the compiler will optimize the dead code out) +#ifndef ALU_INIT_NC + #define ALU_INIT_NC ALU_INIT_C +#endif +#ifndef VALUE_LSL_IMM_NC + #define VALUE_LSL_IMM_NC VALUE_LSL_IMM_C +#endif +#ifndef VALUE_LSL_REG_NC + #define VALUE_LSL_REG_NC VALUE_LSL_REG_C +#endif +#ifndef VALUE_LSR_IMM_NC + #define VALUE_LSR_IMM_NC VALUE_LSR_IMM_C +#endif +#ifndef VALUE_LSR_REG_NC + #define VALUE_LSR_REG_NC VALUE_LSR_REG_C +#endif +#ifndef VALUE_ASR_IMM_NC + #define VALUE_ASR_IMM_NC VALUE_ASR_IMM_C +#endif +#ifndef VALUE_ASR_REG_NC + #define VALUE_ASR_REG_NC VALUE_ASR_REG_C +#endif +#ifndef VALUE_ROR_IMM_NC + #define VALUE_ROR_IMM_NC VALUE_ROR_IMM_C +#endif +#ifndef VALUE_ROR_REG_NC + #define VALUE_ROR_REG_NC VALUE_ROR_REG_C +#endif +#ifndef VALUE_IMM_NC + #define VALUE_IMM_NC VALUE_IMM_C +#endif + +#define C_CHECK_PC(SETCOND) if (LIKELY(dest != 15)) { SETCOND } +#ifndef OP_AND + #define OP_AND \ + u32 res = reg[(opcode>>16)&15].I & value; \ + reg[dest].I = res; +#endif +#ifndef OP_ANDS + #define OP_ANDS OP_AND C_CHECK_PC(C_SETCOND_LOGICAL) +#endif +#ifndef OP_EOR + #define OP_EOR \ + u32 res = reg[(opcode>>16)&15].I ^ value; \ + reg[dest].I = res; +#endif +#ifndef OP_EORS + #define OP_EORS OP_EOR C_CHECK_PC(C_SETCOND_LOGICAL) +#endif +#ifndef OP_SUB + #define OP_SUB \ + u32 lhs = reg[(opcode>>16)&15].I; \ + u32 rhs = value; \ + u32 res = lhs - rhs; \ + reg[dest].I = res; +#endif +#ifndef OP_SUBS + #define OP_SUBS OP_SUB C_CHECK_PC(C_SETCOND_SUB) +#endif +#ifndef OP_RSB + #define OP_RSB \ + u32 lhs = value; \ + u32 rhs = reg[(opcode>>16)&15].I; \ + u32 res = lhs - rhs; \ + reg[dest].I = res; +#endif +#ifndef OP_RSBS + #define OP_RSBS OP_RSB C_CHECK_PC(C_SETCOND_SUB) +#endif +#ifndef OP_ADD + #define OP_ADD \ + u32 lhs = reg[(opcode>>16)&15].I; \ + u32 rhs = value; \ + u32 res = lhs + rhs; \ + reg[dest].I = res; +#endif +#ifndef OP_ADDS + #define OP_ADDS OP_ADD C_CHECK_PC(C_SETCOND_ADD) +#endif +#ifndef OP_ADC + #define OP_ADC \ + u32 lhs = reg[(opcode>>16)&15].I; \ + u32 rhs = value; \ + u32 res = lhs + rhs + (u32)C_FLAG; \ + reg[dest].I = res; +#endif +#ifndef OP_ADCS + #define OP_ADCS OP_ADC C_CHECK_PC(C_SETCOND_ADD) +#endif +#ifndef OP_SBC + #define OP_SBC \ + u32 lhs = reg[(opcode>>16)&15].I; \ + u32 rhs = value; \ + u32 res = lhs - rhs - !((u32)C_FLAG); \ + reg[dest].I = res; +#endif +#ifndef OP_SBCS + #define OP_SBCS OP_SBC C_CHECK_PC(C_SETCOND_SUB) +#endif +#ifndef OP_RSC + #define OP_RSC \ + u32 lhs = value; \ + u32 rhs = reg[(opcode>>16)&15].I; \ + u32 res = lhs - rhs - !((u32)C_FLAG); \ + reg[dest].I = res; +#endif +#ifndef OP_RSCS + #define OP_RSCS OP_RSC C_CHECK_PC(C_SETCOND_SUB) +#endif +#ifndef OP_TST + #define OP_TST \ + u32 res = reg[(opcode >> 16) & 0x0F].I & value; \ + C_SETCOND_LOGICAL; +#endif +#ifndef OP_TEQ + #define OP_TEQ \ + u32 res = reg[(opcode >> 16) & 0x0F].I ^ value; \ + C_SETCOND_LOGICAL; +#endif +#ifndef OP_CMP + #define OP_CMP \ + u32 lhs = reg[(opcode>>16)&15].I; \ + u32 rhs = value; \ + u32 res = lhs - rhs; \ + C_SETCOND_SUB; +#endif +#ifndef OP_CMN + #define OP_CMN \ + u32 lhs = reg[(opcode>>16)&15].I; \ + u32 rhs = value; \ + u32 res = lhs + rhs; \ + C_SETCOND_ADD; +#endif +#ifndef OP_ORR + #define OP_ORR \ + u32 res = reg[(opcode >> 16) & 0x0F].I | value; \ + reg[dest].I = res; +#endif +#ifndef OP_ORRS + #define OP_ORRS OP_ORR C_CHECK_PC(C_SETCOND_LOGICAL) +#endif +#ifndef OP_MOV + #define OP_MOV \ + u32 res = value; \ + reg[dest].I = res; +#endif +#ifndef OP_MOVS + #define OP_MOVS OP_MOV C_CHECK_PC(C_SETCOND_LOGICAL) +#endif +#ifndef OP_BIC + #define OP_BIC \ + u32 res = reg[(opcode >> 16) & 0x0F].I & (~value); \ + reg[dest].I = res; +#endif +#ifndef OP_BICS + #define OP_BICS OP_BIC C_CHECK_PC(C_SETCOND_LOGICAL) +#endif +#ifndef OP_MVN + #define OP_MVN \ + u32 res = ~value; \ + reg[dest].I = res; +#endif +#ifndef OP_MVNS + #define OP_MVNS OP_MVN C_CHECK_PC(C_SETCOND_LOGICAL) +#endif + +#ifndef SETCOND_NONE + #define SETCOND_NONE /*nothing*/ +#endif +#ifndef SETCOND_MUL + #define SETCOND_MUL \ + N_FLAG = ((s32)reg[dest].I < 0) ? true : false; \ + Z_FLAG = reg[dest].I ? false : true; +#endif +#ifndef SETCOND_MULL + #define SETCOND_MULL \ + N_FLAG = (reg[dest].I & 0x80000000) ? true : false;\ + Z_FLAG = reg[dest].I || reg[acc].I ? false : true; +#endif + +#ifndef ALU_FINISH + #define ALU_FINISH /*nothing*/ +#endif + +#ifndef ROR_IMM_MSR + #define ROR_IMM_MSR \ + u32 v = opcode & 0xff; \ + value = ((v << (32 - shift)) | (v >> shift)); +#endif +#ifndef ROR_OFFSET + #define ROR_OFFSET \ + offset = ((offset << (32 - shift)) | (offset >> shift)); +#endif +#ifndef RRX_OFFSET + #define RRX_OFFSET \ + offset = ((offset >> 1) | ((int)C_FLAG << 31)); +#endif + +// ALU ops (except multiply) ////////////////////////////////////////////// + +// ALU_INIT: init code (ALU_INIT_C or ALU_INIT_NC) +// GETVALUE: load value and shift/rotate (VALUE_XXX) +// OP: ALU operation (OP_XXX) +// MODECHANGE: MODECHANGE_NO or MODECHANGE_YES +// ISREGSHIFT: 1 for insns of the form ...,Rn LSL/etc Rs; 0 otherwise +// ALU_INIT, GETVALUE, OP, and ALU_FINISH are concatenated in order. +#define ALU_INSN(ALU_INIT, GETVALUE, OP, MODECHANGE, ISREGSHIFT) \ + ALU_INIT GETVALUE OP ALU_FINISH; \ + if (LIKELY((opcode & 0x0000F000) != 0x0000F000)) { \ + clockTicks = 1 + ISREGSHIFT \ + + codeTicksAccessSeq32(armNextPC); \ + } else { \ + MODECHANGE; \ + if (armState) { \ + reg[15].I &= 0xFFFFFFFC; \ + armNextPC = reg[15].I; \ + reg[15].I += 4; \ + ARM_PREFETCH; \ + } else { \ + reg[15].I &= 0xFFFFFFFE; \ + armNextPC = reg[15].I; \ + reg[15].I += 2; \ + THUMB_PREFETCH; \ + } \ + clockTicks = 3 + ISREGSHIFT \ + + codeTicksAccess32(armNextPC) \ + + codeTicksAccessSeq32(armNextPC) \ + + codeTicksAccessSeq32(armNextPC); \ + } + +#define MODECHANGE_NO /*nothing*/ +#define MODECHANGE_YES CPUSwitchMode(reg[17].I & 0x1f, false); + +#define DEFINE_ALU_INSN_C(CODE1, CODE2, OP, MODECHANGE) \ + static INSN_REGPARM void arm##CODE1##0(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_LSL_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ + static INSN_REGPARM void arm##CODE1##1(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_LSL_REG_C, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ + static INSN_REGPARM void arm##CODE1##2(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_LSR_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ + static INSN_REGPARM void arm##CODE1##3(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_LSR_REG_C, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ + static INSN_REGPARM void arm##CODE1##4(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_ASR_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ + static INSN_REGPARM void arm##CODE1##5(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_ASR_REG_C, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ + static INSN_REGPARM void arm##CODE1##6(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_ROR_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ + static INSN_REGPARM void arm##CODE1##7(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_ROR_REG_C, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ + static INSN_REGPARM void arm##CODE2##0(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); } +#define DEFINE_ALU_INSN_NC(CODE1, CODE2, OP, MODECHANGE) \ + static INSN_REGPARM void arm##CODE1##0(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_LSL_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ + static INSN_REGPARM void arm##CODE1##1(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_LSL_REG_NC, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ + static INSN_REGPARM void arm##CODE1##2(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_LSR_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ + static INSN_REGPARM void arm##CODE1##3(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_LSR_REG_NC, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ + static INSN_REGPARM void arm##CODE1##4(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_ASR_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ + static INSN_REGPARM void arm##CODE1##5(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_ASR_REG_NC, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ + static INSN_REGPARM void arm##CODE1##6(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_ROR_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ + static INSN_REGPARM void arm##CODE1##7(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_ROR_REG_NC, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ + static INSN_REGPARM void arm##CODE2##0(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); } + +// AND +DEFINE_ALU_INSN_NC(00, 20, AND, NO) +// ANDS +DEFINE_ALU_INSN_C (01, 21, ANDS, YES) + +// EOR +DEFINE_ALU_INSN_NC(02, 22, EOR, NO) +// EORS +DEFINE_ALU_INSN_C (03, 23, EORS, YES) + +// SUB +DEFINE_ALU_INSN_NC(04, 24, SUB, NO) +// SUBS +DEFINE_ALU_INSN_NC(05, 25, SUBS, YES) + +// RSB +DEFINE_ALU_INSN_NC(06, 26, RSB, NO) +// RSBS +DEFINE_ALU_INSN_NC(07, 27, RSBS, YES) + +// ADD +DEFINE_ALU_INSN_NC(08, 28, ADD, NO) +// ADDS +DEFINE_ALU_INSN_NC(09, 29, ADDS, YES) + +// ADC +DEFINE_ALU_INSN_NC(0A, 2A, ADC, NO) +// ADCS +DEFINE_ALU_INSN_NC(0B, 2B, ADCS, YES) + +// SBC +DEFINE_ALU_INSN_NC(0C, 2C, SBC, NO) +// SBCS +DEFINE_ALU_INSN_NC(0D, 2D, SBCS, YES) + +// RSC +DEFINE_ALU_INSN_NC(0E, 2E, RSC, NO) +// RSCS +DEFINE_ALU_INSN_NC(0F, 2F, RSCS, YES) + +// TST +DEFINE_ALU_INSN_C (11, 31, TST, NO) + +// TEQ +DEFINE_ALU_INSN_C (13, 33, TEQ, NO) + +// CMP +DEFINE_ALU_INSN_NC(15, 35, CMP, NO) + +// CMN +DEFINE_ALU_INSN_NC(17, 37, CMN, NO) + +// ORR +DEFINE_ALU_INSN_NC(18, 38, ORR, NO) +// ORRS +DEFINE_ALU_INSN_C (19, 39, ORRS, YES) + +// MOV +DEFINE_ALU_INSN_NC(1A, 3A, MOV, NO) +// MOVS +DEFINE_ALU_INSN_C (1B, 3B, MOVS, YES) + +// BIC +DEFINE_ALU_INSN_NC(1C, 3C, BIC, NO) +// BICS +DEFINE_ALU_INSN_C (1D, 3D, BICS, YES) + +// MVN +DEFINE_ALU_INSN_NC(1E, 3E, MVN, NO) +// MVNS +DEFINE_ALU_INSN_C (1F, 3F, MVNS, YES) + +// Multiply instructions ////////////////////////////////////////////////// + +// OP: OP_MUL, OP_MLA etc. +// SETCOND: SETCOND_NONE, SETCOND_MUL, or SETCOND_MULL +// CYCLES: base cycle count (1, 2, or 3) +#define MUL_INSN(OP, SETCOND, CYCLES) \ + int mult = (opcode & 0x0F); \ + u32 rs = reg[(opcode >> 8) & 0x0F].I; \ + int acc = (opcode >> 12) & 0x0F; /* or destLo */ \ + int dest = (opcode >> 16) & 0x0F; /* or destHi */ \ + OP; \ + SETCOND; \ + if ((s32)rs < 0) \ + rs = ~rs; \ + if ((rs & 0xFFFFFF00) == 0) \ + clockTicks += 0; \ + else if ((rs & 0xFFFF0000) == 0) \ + clockTicks += 1; \ + else if ((rs & 0xFF000000) == 0) \ + clockTicks += 2; \ + else \ + clockTicks += 3; \ + if (busPrefetchCount == 0) \ + busPrefetchCount = ((busPrefetchCount+1)<> 32); +#define OP_MLAL(SIGN) \ + SIGN##64 res = ((SIGN##64)reg[dest].I<<32 | reg[acc].I)\ + + ((SIGN##64)(SIGN##32)reg[mult].I \ + * (SIGN##64)(SIGN##32)rs); \ + reg[acc].I = (u32)res; \ + reg[dest].I = (u32)(res >> 32); +#define OP_UMULL OP_MULL(u) +#define OP_UMLAL OP_MLAL(u) +#define OP_SMULL OP_MULL(s) +#define OP_SMLAL OP_MLAL(s) + +// MUL Rd, Rm, Rs +static INSN_REGPARM void arm009(u32 opcode) { MUL_INSN(OP_MUL, SETCOND_NONE, 1); } +// MULS Rd, Rm, Rs +static INSN_REGPARM void arm019(u32 opcode) { MUL_INSN(OP_MUL, SETCOND_MUL, 1); } + +// MLA Rd, Rm, Rs, Rn +static INSN_REGPARM void arm029(u32 opcode) { MUL_INSN(OP_MLA, SETCOND_NONE, 2); } +// MLAS Rd, Rm, Rs, Rn +static INSN_REGPARM void arm039(u32 opcode) { MUL_INSN(OP_MLA, SETCOND_MUL, 2); } + +// UMULL RdLo, RdHi, Rn, Rs +static INSN_REGPARM void arm089(u32 opcode) { MUL_INSN(OP_UMULL, SETCOND_NONE, 2); } +// UMULLS RdLo, RdHi, Rn, Rs +static INSN_REGPARM void arm099(u32 opcode) { MUL_INSN(OP_UMULL, SETCOND_MULL, 2); } + +// UMLAL RdLo, RdHi, Rn, Rs +static INSN_REGPARM void arm0A9(u32 opcode) { MUL_INSN(OP_UMLAL, SETCOND_NONE, 3); } +// UMLALS RdLo, RdHi, Rn, Rs +static INSN_REGPARM void arm0B9(u32 opcode) { MUL_INSN(OP_UMLAL, SETCOND_MULL, 3); } + +// SMULL RdLo, RdHi, Rm, Rs +static INSN_REGPARM void arm0C9(u32 opcode) { MUL_INSN(OP_SMULL, SETCOND_NONE, 2); } +// SMULLS RdLo, RdHi, Rm, Rs +static INSN_REGPARM void arm0D9(u32 opcode) { MUL_INSN(OP_SMULL, SETCOND_MULL, 2); } + +// SMLAL RdLo, RdHi, Rm, Rs +static INSN_REGPARM void arm0E9(u32 opcode) { MUL_INSN(OP_SMLAL, SETCOND_NONE, 3); } +// SMLALS RdLo, RdHi, Rm, Rs +static INSN_REGPARM void arm0F9(u32 opcode) { MUL_INSN(OP_SMLAL, SETCOND_MULL, 3); } + +// Misc instructions ////////////////////////////////////////////////////// + +// SWP Rd, Rm, [Rn] +static INSN_REGPARM void arm109(u32 opcode) +{ + u32 address = reg[(opcode >> 16) & 15].I; + u32 temp = CPUReadMemory(address); + CPUWriteMemory(address, reg[opcode&15].I); + reg[(opcode >> 12) & 15].I = temp; + clockTicks = 4 + dataTicksAccess32(address) + dataTicksAccess32(address) + + codeTicksAccess32(armNextPC); +} + +// SWPB Rd, Rm, [Rn] +static INSN_REGPARM void arm149(u32 opcode) +{ + u32 address = reg[(opcode >> 16) & 15].I; + u32 temp = CPUReadByte(address); + CPUWriteByte(address, reg[opcode&15].B.B0); + reg[(opcode>>12)&15].I = temp; + clockTicks = 4 + dataTicksAccess32(address) + dataTicksAccess32(address) + + codeTicksAccess32(armNextPC); +} + +// MRS Rd, CPSR +static INSN_REGPARM void arm100(u32 opcode) +{ + if (LIKELY((opcode & 0x0FFF0FFF) == 0x010F0000)) { + CPUUpdateCPSR(); + reg[(opcode >> 12) & 0x0F].I = reg[16].I; + } else { + armUnknownInsn(opcode); + } +} + +// MRS Rd, SPSR +static INSN_REGPARM void arm140(u32 opcode) +{ + if (LIKELY((opcode & 0x0FFF0FFF) == 0x014F0000)) { + reg[(opcode >> 12) & 0x0F].I = reg[17].I; + } else { + armUnknownInsn(opcode); + } +} + +// MSR CPSR_fields, Rm +static INSN_REGPARM void arm120(u32 opcode) +{ + if (LIKELY((opcode & 0x0FF0FFF0) == 0x0120F000)) { + CPUUpdateCPSR(); + u32 value = reg[opcode & 15].I; + u32 newValue = reg[16].I; + if (armMode > 0x10) { + if (opcode & 0x00010000) + newValue = (newValue & 0xFFFFFF00) | (value & 0x000000FF); + if (opcode & 0x00020000) + newValue = (newValue & 0xFFFF00FF) | (value & 0x0000FF00); + if (opcode & 0x00040000) + newValue = (newValue & 0xFF00FFFF) | (value & 0x00FF0000); + } + if (opcode & 0x00080000) + newValue = (newValue & 0x00FFFFFF) | (value & 0xFF000000); + newValue |= 0x10; + CPUSwitchMode(newValue & 0x1F, false); + reg[16].I = newValue; + CPUUpdateFlags(); + if (!armState) { // this should not be allowed, but it seems to work + THUMB_PREFETCH; + reg[15].I = armNextPC + 2; + } + } else { + armUnknownInsn(opcode); + } +} + +// MSR SPSR_fields, Rm +static INSN_REGPARM void arm160(u32 opcode) +{ + if (LIKELY((opcode & 0x0FF0FFF0) == 0x0160F000)) { + u32 value = reg[opcode & 15].I; + if (armMode > 0x10 && armMode < 0x1F) { + if (opcode & 0x00010000) + reg[17].I = (reg[17].I & 0xFFFFFF00) | (value & 0x000000FF); + if (opcode & 0x00020000) + reg[17].I = (reg[17].I & 0xFFFF00FF) | (value & 0x0000FF00); + if (opcode & 0x00040000) + reg[17].I = (reg[17].I & 0xFF00FFFF) | (value & 0x00FF0000); + if (opcode & 0x00080000) + reg[17].I = (reg[17].I & 0x00FFFFFF) | (value & 0xFF000000); + } + } else { + armUnknownInsn(opcode); + } +} + +// MSR CPSR_fields, # +static INSN_REGPARM void arm320(u32 opcode) +{ + if (LIKELY((opcode & 0x0FF0F000) == 0x0320F000)) { + CPUUpdateCPSR(); + u32 value = opcode & 0xFF; + int shift = (opcode & 0xF00) >> 7; + if (shift) { + ROR_IMM_MSR; + } + u32 newValue = reg[16].I; + if (armMode > 0x10) { + if (opcode & 0x00010000) + newValue = (newValue & 0xFFFFFF00) | (value & 0x000000FF); + if (opcode & 0x00020000) + newValue = (newValue & 0xFFFF00FF) | (value & 0x0000FF00); + if (opcode & 0x00040000) + newValue = (newValue & 0xFF00FFFF) | (value & 0x00FF0000); + } + if (opcode & 0x00080000) + newValue = (newValue & 0x00FFFFFF) | (value & 0xFF000000); + + newValue |= 0x10; + + CPUSwitchMode(newValue & 0x1F, false); + reg[16].I = newValue; + CPUUpdateFlags(); + if (!armState) { // this should not be allowed, but it seems to work + THUMB_PREFETCH; + reg[15].I = armNextPC + 2; + } + } else { + armUnknownInsn(opcode); + } +} + +// MSR SPSR_fields, # +static INSN_REGPARM void arm360(u32 opcode) +{ + if (LIKELY((opcode & 0x0FF0F000) == 0x0360F000)) { + if (armMode > 0x10 && armMode < 0x1F) { + u32 value = opcode & 0xFF; + int shift = (opcode & 0xF00) >> 7; + if (shift) { + ROR_IMM_MSR; + } + if (opcode & 0x00010000) + reg[17].I = (reg[17].I & 0xFFFFFF00) | (value & 0x000000FF); + if (opcode & 0x00020000) + reg[17].I = (reg[17].I & 0xFFFF00FF) | (value & 0x0000FF00); + if (opcode & 0x00040000) + reg[17].I = (reg[17].I & 0xFF00FFFF) | (value & 0x00FF0000); + if (opcode & 0x00080000) + reg[17].I = (reg[17].I & 0x00FFFFFF) | (value & 0xFF000000); + } + } else { + armUnknownInsn(opcode); + } +} + +// BX Rm +static INSN_REGPARM void arm121(u32 opcode) +{ + if (LIKELY((opcode & 0x0FFFFFF0) == 0x012FFF10)) { + int base = opcode & 0x0F; + busPrefetchCount = 0; + armState = reg[base].I & 1 ? false : true; + if (armState) { + reg[15].I = reg[base].I & 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + ARM_PREFETCH; + clockTicks = 3 + codeTicksAccessSeq32(armNextPC) + + codeTicksAccessSeq32(armNextPC) + + codeTicksAccess32(armNextPC); + } else { + reg[15].I = reg[base].I & 0xFFFFFFFE; + armNextPC = reg[15].I; + reg[15].I += 2; + THUMB_PREFETCH; + clockTicks = 3 + codeTicksAccessSeq16(armNextPC) + + codeTicksAccessSeq16(armNextPC) + + codeTicksAccess16(armNextPC); + } + } else { + armUnknownInsn(opcode); + } +} + +// Load/store ///////////////////////////////////////////////////////////// + +#define OFFSET_IMM \ + int offset = opcode & 0xFFF; +#define OFFSET_IMM8 \ + int offset = ((opcode & 0x0F) | ((opcode>>4) & 0xF0)); +#define OFFSET_REG \ + int offset = reg[opcode & 15].I; +#define OFFSET_LSL \ + int offset = reg[opcode & 15].I << ((opcode>>7) & 31); +#define OFFSET_LSR \ + int shift = (opcode >> 7) & 31; \ + int offset = shift ? reg[opcode & 15].I >> shift : 0; +#define OFFSET_ASR \ + int shift = (opcode >> 7) & 31; \ + int offset; \ + if (shift) \ + offset = (int)((s32)reg[opcode & 15].I >> shift);\ + else if (reg[opcode & 15].I & 0x80000000) \ + offset = 0xFFFFFFFF; \ + else \ + offset = 0; +#define OFFSET_ROR \ + int shift = (opcode >> 7) & 31; \ + u32 offset = reg[opcode & 15].I; \ + if (shift) { \ + ROR_OFFSET; \ + } else { \ + RRX_OFFSET; \ + } + +#define ADDRESS_POST (reg[base].I) +#define ADDRESS_PREDEC (reg[base].I - offset) +#define ADDRESS_PREINC (reg[base].I + offset) + +#define OP_STR CPUWriteMemory(address, reg[dest].I) +#define OP_STRH CPUWriteHalfWord(address, reg[dest].W.W0) +#define OP_STRB CPUWriteByte(address, reg[dest].B.B0) +#define OP_LDR reg[dest].I = CPUReadMemory(address) +#define OP_LDRH reg[dest].I = CPUReadHalfWord(address) +#define OP_LDRB reg[dest].I = CPUReadByte(address) +#define OP_LDRSH reg[dest].I = (u32)CPUReadHalfWordSigned(address) +#define OP_LDRSB reg[dest].I = (s8)CPUReadByte(address) + +#define WRITEBACK_NONE /*nothing*/ +#define WRITEBACK_PRE reg[base].I = address +#define WRITEBACK_POSTDEC reg[base].I = address - offset +#define WRITEBACK_POSTINC reg[base].I = address + offset + +#define LDRSTR_INIT(CALC_OFFSET, CALC_ADDRESS) \ + if (busPrefetchCount == 0) \ + busPrefetch = busPrefetchEnable; \ + int dest = (opcode >> 12) & 15; \ + int base = (opcode >> 16) & 15; \ + CALC_OFFSET; \ + u32 address = CALC_ADDRESS; + +#define STR(CALC_OFFSET, CALC_ADDRESS, STORE_DATA, WRITEBACK1, WRITEBACK2, SIZE) \ + LDRSTR_INIT(CALC_OFFSET, CALC_ADDRESS); \ + WRITEBACK1; \ + STORE_DATA; \ + WRITEBACK2; \ + clockTicks = 2 + dataTicksAccess##SIZE(address) \ + + codeTicksAccess32(armNextPC); +#define LDR(CALC_OFFSET, CALC_ADDRESS, LOAD_DATA, WRITEBACK, SIZE) \ + LDRSTR_INIT(CALC_OFFSET, CALC_ADDRESS); \ + LOAD_DATA; \ + if (dest != base) \ + { \ + WRITEBACK; \ + } \ + clockTicks = 0; \ + if (dest == 15) { \ + reg[15].I &= 0xFFFFFFFC; \ + armNextPC = reg[15].I; \ + reg[15].I += 4; \ + ARM_PREFETCH; \ + clockTicks += 2 + dataTicksAccessSeq32(address) \ + + dataTicksAccessSeq32(address);\ + } \ + clockTicks += 3 + dataTicksAccess##SIZE(address) \ + + codeTicksAccess32(armNextPC); +#define STR_POSTDEC(CALC_OFFSET, STORE_DATA, SIZE) \ + STR(CALC_OFFSET, ADDRESS_POST, STORE_DATA, WRITEBACK_NONE, WRITEBACK_POSTDEC, SIZE) +#define STR_POSTINC(CALC_OFFSET, STORE_DATA, SIZE) \ + STR(CALC_OFFSET, ADDRESS_POST, STORE_DATA, WRITEBACK_NONE, WRITEBACK_POSTINC, SIZE) +#define STR_PREDEC(CALC_OFFSET, STORE_DATA, SIZE) \ + STR(CALC_OFFSET, ADDRESS_PREDEC, STORE_DATA, WRITEBACK_NONE, WRITEBACK_NONE, SIZE) +#define STR_PREDEC_WB(CALC_OFFSET, STORE_DATA, SIZE) \ + STR(CALC_OFFSET, ADDRESS_PREDEC, STORE_DATA, WRITEBACK_PRE, WRITEBACK_NONE, SIZE) +#define STR_PREINC(CALC_OFFSET, STORE_DATA, SIZE) \ + STR(CALC_OFFSET, ADDRESS_PREINC, STORE_DATA, WRITEBACK_NONE, WRITEBACK_NONE, SIZE) +#define STR_PREINC_WB(CALC_OFFSET, STORE_DATA, SIZE) \ + STR(CALC_OFFSET, ADDRESS_PREINC, STORE_DATA, WRITEBACK_PRE, WRITEBACK_NONE, SIZE) +#define LDR_POSTDEC(CALC_OFFSET, LOAD_DATA, SIZE) \ + LDR(CALC_OFFSET, ADDRESS_POST, LOAD_DATA, WRITEBACK_POSTDEC, SIZE) +#define LDR_POSTINC(CALC_OFFSET, LOAD_DATA, SIZE) \ + LDR(CALC_OFFSET, ADDRESS_POST, LOAD_DATA, WRITEBACK_POSTINC, SIZE) +#define LDR_PREDEC(CALC_OFFSET, LOAD_DATA, SIZE) \ + LDR(CALC_OFFSET, ADDRESS_PREDEC, LOAD_DATA, WRITEBACK_NONE, SIZE) +#define LDR_PREDEC_WB(CALC_OFFSET, LOAD_DATA, SIZE) \ + LDR(CALC_OFFSET, ADDRESS_PREDEC, LOAD_DATA, WRITEBACK_PRE, SIZE) +#define LDR_PREINC(CALC_OFFSET, LOAD_DATA, SIZE) \ + LDR(CALC_OFFSET, ADDRESS_PREINC, LOAD_DATA, WRITEBACK_NONE, SIZE) +#define LDR_PREINC_WB(CALC_OFFSET, LOAD_DATA, SIZE) \ + LDR(CALC_OFFSET, ADDRESS_PREINC, LOAD_DATA, WRITEBACK_PRE, SIZE) + +// STRH Rd, [Rn], -Rm +static INSN_REGPARM void arm00B(u32 opcode) { STR_POSTDEC(OFFSET_REG, OP_STRH, 16); } +// STRH Rd, [Rn], #-offset +static INSN_REGPARM void arm04B(u32 opcode) { STR_POSTDEC(OFFSET_IMM8, OP_STRH, 16); } +// STRH Rd, [Rn], Rm +static INSN_REGPARM void arm08B(u32 opcode) { STR_POSTINC(OFFSET_REG, OP_STRH, 16); } +// STRH Rd, [Rn], #offset +static INSN_REGPARM void arm0CB(u32 opcode) { STR_POSTINC(OFFSET_IMM8, OP_STRH, 16); } +// STRH Rd, [Rn, -Rm] +static INSN_REGPARM void arm10B(u32 opcode) { STR_PREDEC(OFFSET_REG, OP_STRH, 16); } +// STRH Rd, [Rn, -Rm]! +static INSN_REGPARM void arm12B(u32 opcode) { STR_PREDEC_WB(OFFSET_REG, OP_STRH, 16); } +// STRH Rd, [Rn, -#offset] +static INSN_REGPARM void arm14B(u32 opcode) { STR_PREDEC(OFFSET_IMM8, OP_STRH, 16); } +// STRH Rd, [Rn, -#offset]! +static INSN_REGPARM void arm16B(u32 opcode) { STR_PREDEC_WB(OFFSET_IMM8, OP_STRH, 16); } +// STRH Rd, [Rn, Rm] +static INSN_REGPARM void arm18B(u32 opcode) { STR_PREINC(OFFSET_REG, OP_STRH, 16); } +// STRH Rd, [Rn, Rm]! +static INSN_REGPARM void arm1AB(u32 opcode) { STR_PREINC_WB(OFFSET_REG, OP_STRH, 16); } +// STRH Rd, [Rn, #offset] +static INSN_REGPARM void arm1CB(u32 opcode) { STR_PREINC(OFFSET_IMM8, OP_STRH, 16); } +// STRH Rd, [Rn, #offset]! +static INSN_REGPARM void arm1EB(u32 opcode) { STR_PREINC_WB(OFFSET_IMM8, OP_STRH, 16); } + +// LDRH Rd, [Rn], -Rm +static INSN_REGPARM void arm01B(u32 opcode) { LDR_POSTDEC(OFFSET_REG, OP_LDRH, 16); } +// LDRH Rd, [Rn], #-offset +static INSN_REGPARM void arm05B(u32 opcode) { LDR_POSTDEC(OFFSET_IMM8, OP_LDRH, 16); } +// LDRH Rd, [Rn], Rm +static INSN_REGPARM void arm09B(u32 opcode) { LDR_POSTINC(OFFSET_REG, OP_LDRH, 16); } +// LDRH Rd, [Rn], #offset +static INSN_REGPARM void arm0DB(u32 opcode) { LDR_POSTINC(OFFSET_IMM8, OP_LDRH, 16); } +// LDRH Rd, [Rn, -Rm] +static INSN_REGPARM void arm11B(u32 opcode) { LDR_PREDEC(OFFSET_REG, OP_LDRH, 16); } +// LDRH Rd, [Rn, -Rm]! +static INSN_REGPARM void arm13B(u32 opcode) { LDR_PREDEC_WB(OFFSET_REG, OP_LDRH, 16); } +// LDRH Rd, [Rn, -#offset] +static INSN_REGPARM void arm15B(u32 opcode) { LDR_PREDEC(OFFSET_IMM8, OP_LDRH, 16); } +// LDRH Rd, [Rn, -#offset]! +static INSN_REGPARM void arm17B(u32 opcode) { LDR_PREDEC_WB(OFFSET_IMM8, OP_LDRH, 16); } +// LDRH Rd, [Rn, Rm] +static INSN_REGPARM void arm19B(u32 opcode) { LDR_PREINC(OFFSET_REG, OP_LDRH, 16); } +// LDRH Rd, [Rn, Rm]! +static INSN_REGPARM void arm1BB(u32 opcode) { LDR_PREINC_WB(OFFSET_REG, OP_LDRH, 16); } +// LDRH Rd, [Rn, #offset] +static INSN_REGPARM void arm1DB(u32 opcode) { LDR_PREINC(OFFSET_IMM8, OP_LDRH, 16); } +// LDRH Rd, [Rn, #offset]! +static INSN_REGPARM void arm1FB(u32 opcode) { LDR_PREINC_WB(OFFSET_IMM8, OP_LDRH, 16); } + +// LDRSB Rd, [Rn], -Rm +static INSN_REGPARM void arm01D(u32 opcode) { LDR_POSTDEC(OFFSET_REG, OP_LDRSB, 16); } +// LDRSB Rd, [Rn], #-offset +static INSN_REGPARM void arm05D(u32 opcode) { LDR_POSTDEC(OFFSET_IMM8, OP_LDRSB, 16); } +// LDRSB Rd, [Rn], Rm +static INSN_REGPARM void arm09D(u32 opcode) { LDR_POSTINC(OFFSET_REG, OP_LDRSB, 16); } +// LDRSB Rd, [Rn], #offset +static INSN_REGPARM void arm0DD(u32 opcode) { LDR_POSTINC(OFFSET_IMM8, OP_LDRSB, 16); } +// LDRSB Rd, [Rn, -Rm] +static INSN_REGPARM void arm11D(u32 opcode) { LDR_PREDEC(OFFSET_REG, OP_LDRSB, 16); } +// LDRSB Rd, [Rn, -Rm]! +static INSN_REGPARM void arm13D(u32 opcode) { LDR_PREDEC_WB(OFFSET_REG, OP_LDRSB, 16); } +// LDRSB Rd, [Rn, -#offset] +static INSN_REGPARM void arm15D(u32 opcode) { LDR_PREDEC(OFFSET_IMM8, OP_LDRSB, 16); } +// LDRSB Rd, [Rn, -#offset]! +static INSN_REGPARM void arm17D(u32 opcode) { LDR_PREDEC_WB(OFFSET_IMM8, OP_LDRSB, 16); } +// LDRSB Rd, [Rn, Rm] +static INSN_REGPARM void arm19D(u32 opcode) { LDR_PREINC(OFFSET_REG, OP_LDRSB, 16); } +// LDRSB Rd, [Rn, Rm]! +static INSN_REGPARM void arm1BD(u32 opcode) { LDR_PREINC_WB(OFFSET_REG, OP_LDRSB, 16); } +// LDRSB Rd, [Rn, #offset] +static INSN_REGPARM void arm1DD(u32 opcode) { LDR_PREINC(OFFSET_IMM8, OP_LDRSB, 16); } +// LDRSB Rd, [Rn, #offset]! +static INSN_REGPARM void arm1FD(u32 opcode) { LDR_PREINC_WB(OFFSET_IMM8, OP_LDRSB, 16); } + +// LDRSH Rd, [Rn], -Rm +static INSN_REGPARM void arm01F(u32 opcode) { LDR_POSTDEC(OFFSET_REG, OP_LDRSH, 16); } +// LDRSH Rd, [Rn], #-offset +static INSN_REGPARM void arm05F(u32 opcode) { LDR_POSTDEC(OFFSET_IMM8, OP_LDRSH, 16); } +// LDRSH Rd, [Rn], Rm +static INSN_REGPARM void arm09F(u32 opcode) { LDR_POSTINC(OFFSET_REG, OP_LDRSH, 16); } +// LDRSH Rd, [Rn], #offset +static INSN_REGPARM void arm0DF(u32 opcode) { LDR_POSTINC(OFFSET_IMM8, OP_LDRSH, 16); } +// LDRSH Rd, [Rn, -Rm] +static INSN_REGPARM void arm11F(u32 opcode) { LDR_PREDEC(OFFSET_REG, OP_LDRSH, 16); } +// LDRSH Rd, [Rn, -Rm]! +static INSN_REGPARM void arm13F(u32 opcode) { LDR_PREDEC_WB(OFFSET_REG, OP_LDRSH, 16); } +// LDRSH Rd, [Rn, -#offset] +static INSN_REGPARM void arm15F(u32 opcode) { LDR_PREDEC(OFFSET_IMM8, OP_LDRSH, 16); } +// LDRSH Rd, [Rn, -#offset]! +static INSN_REGPARM void arm17F(u32 opcode) { LDR_PREDEC_WB(OFFSET_IMM8, OP_LDRSH, 16); } +// LDRSH Rd, [Rn, Rm] +static INSN_REGPARM void arm19F(u32 opcode) { LDR_PREINC(OFFSET_REG, OP_LDRSH, 16); } +// LDRSH Rd, [Rn, Rm]! +static INSN_REGPARM void arm1BF(u32 opcode) { LDR_PREINC_WB(OFFSET_REG, OP_LDRSH, 16); } +// LDRSH Rd, [Rn, #offset] +static INSN_REGPARM void arm1DF(u32 opcode) { LDR_PREINC(OFFSET_IMM8, OP_LDRSH, 16); } +// LDRSH Rd, [Rn, #offset]! +static INSN_REGPARM void arm1FF(u32 opcode) { LDR_PREINC_WB(OFFSET_IMM8, OP_LDRSH, 16); } + +// STR[T] Rd, [Rn], -# +// Note: STR and STRT do the same thing on the GBA (likewise for LDR/LDRT etc) +static INSN_REGPARM void arm400(u32 opcode) { STR_POSTDEC(OFFSET_IMM, OP_STR, 32); } +// LDR[T] Rd, [Rn], -# +static INSN_REGPARM void arm410(u32 opcode) { LDR_POSTDEC(OFFSET_IMM, OP_LDR, 32); } +// STRB[T] Rd, [Rn], -# +static INSN_REGPARM void arm440(u32 opcode) { STR_POSTDEC(OFFSET_IMM, OP_STRB, 16); } +// LDRB[T] Rd, [Rn], -# +static INSN_REGPARM void arm450(u32 opcode) { LDR_POSTDEC(OFFSET_IMM, OP_LDRB, 16); } +// STR[T] Rd, [Rn], # +static INSN_REGPARM void arm480(u32 opcode) { STR_POSTINC(OFFSET_IMM, OP_STR, 32); } +// LDR Rd, [Rn], # +static INSN_REGPARM void arm490(u32 opcode) { LDR_POSTINC(OFFSET_IMM, OP_LDR, 32); } +// STRB[T] Rd, [Rn], # +static INSN_REGPARM void arm4C0(u32 opcode) { STR_POSTINC(OFFSET_IMM, OP_STRB, 16); } +// LDRB[T] Rd, [Rn], # +static INSN_REGPARM void arm4D0(u32 opcode) { LDR_POSTINC(OFFSET_IMM, OP_LDRB, 16); } +// STR Rd, [Rn, -#] +static INSN_REGPARM void arm500(u32 opcode) { STR_PREDEC(OFFSET_IMM, OP_STR, 32); } +// LDR Rd, [Rn, -#] +static INSN_REGPARM void arm510(u32 opcode) { LDR_PREDEC(OFFSET_IMM, OP_LDR, 32); } +// STR Rd, [Rn, -#]! +static INSN_REGPARM void arm520(u32 opcode) { STR_PREDEC_WB(OFFSET_IMM, OP_STR, 32); } +// LDR Rd, [Rn, -#]! +static INSN_REGPARM void arm530(u32 opcode) { LDR_PREDEC_WB(OFFSET_IMM, OP_LDR, 32); } +// STRB Rd, [Rn, -#] +static INSN_REGPARM void arm540(u32 opcode) { STR_PREDEC(OFFSET_IMM, OP_STRB, 16); } +// LDRB Rd, [Rn, -#] +static INSN_REGPARM void arm550(u32 opcode) { LDR_PREDEC(OFFSET_IMM, OP_LDRB, 16); } +// STRB Rd, [Rn, -#]! +static INSN_REGPARM void arm560(u32 opcode) { STR_PREDEC_WB(OFFSET_IMM, OP_STRB, 16); } +// LDRB Rd, [Rn, -#]! +static INSN_REGPARM void arm570(u32 opcode) { LDR_PREDEC_WB(OFFSET_IMM, OP_LDRB, 16); } +// STR Rd, [Rn, #] +static INSN_REGPARM void arm580(u32 opcode) { STR_PREINC(OFFSET_IMM, OP_STR, 32); } +// LDR Rd, [Rn, #] +static INSN_REGPARM void arm590(u32 opcode) { LDR_PREINC(OFFSET_IMM, OP_LDR, 32); } +// STR Rd, [Rn, #]! +static INSN_REGPARM void arm5A0(u32 opcode) { STR_PREINC_WB(OFFSET_IMM, OP_STR, 32); } +// LDR Rd, [Rn, #]! +static INSN_REGPARM void arm5B0(u32 opcode) { LDR_PREINC_WB(OFFSET_IMM, OP_LDR, 32); } +// STRB Rd, [Rn, #] +static INSN_REGPARM void arm5C0(u32 opcode) { STR_PREINC(OFFSET_IMM, OP_STRB, 16); } +// LDRB Rd, [Rn, #] +static INSN_REGPARM void arm5D0(u32 opcode) { LDR_PREINC(OFFSET_IMM, OP_LDRB, 16); } +// STRB Rd, [Rn, #]! +static INSN_REGPARM void arm5E0(u32 opcode) { STR_PREINC_WB(OFFSET_IMM, OP_STRB, 16); } +// LDRB Rd, [Rn, #]! +static INSN_REGPARM void arm5F0(u32 opcode) { LDR_PREINC_WB(OFFSET_IMM, OP_LDRB, 16); } + +// STR[T] Rd, [Rn], -Rm, LSL # +static INSN_REGPARM void arm600(u32 opcode) { STR_POSTDEC(OFFSET_LSL, OP_STR, 32); } +// STR[T] Rd, [Rn], -Rm, LSR # +static INSN_REGPARM void arm602(u32 opcode) { STR_POSTDEC(OFFSET_LSR, OP_STR, 32); } +// STR[T] Rd, [Rn], -Rm, ASR # +static INSN_REGPARM void arm604(u32 opcode) { STR_POSTDEC(OFFSET_ASR, OP_STR, 32); } +// STR[T] Rd, [Rn], -Rm, ROR # +static INSN_REGPARM void arm606(u32 opcode) { STR_POSTDEC(OFFSET_ROR, OP_STR, 32); } +// LDR[T] Rd, [Rn], -Rm, LSL # +static INSN_REGPARM void arm610(u32 opcode) { LDR_POSTDEC(OFFSET_LSL, OP_LDR, 32); } +// LDR[T] Rd, [Rn], -Rm, LSR # +static INSN_REGPARM void arm612(u32 opcode) { LDR_POSTDEC(OFFSET_LSR, OP_LDR, 32); } +// LDR[T] Rd, [Rn], -Rm, ASR # +static INSN_REGPARM void arm614(u32 opcode) { LDR_POSTDEC(OFFSET_ASR, OP_LDR, 32); } +// LDR[T] Rd, [Rn], -Rm, ROR # +static INSN_REGPARM void arm616(u32 opcode) { LDR_POSTDEC(OFFSET_ROR, OP_LDR, 32); } +// STRB[T] Rd, [Rn], -Rm, LSL # +static INSN_REGPARM void arm640(u32 opcode) { STR_POSTDEC(OFFSET_LSL, OP_STRB, 16); } +// STRB[T] Rd, [Rn], -Rm, LSR # +static INSN_REGPARM void arm642(u32 opcode) { STR_POSTDEC(OFFSET_LSR, OP_STRB, 16); } +// STRB[T] Rd, [Rn], -Rm, ASR # +static INSN_REGPARM void arm644(u32 opcode) { STR_POSTDEC(OFFSET_ASR, OP_STRB, 16); } +// STRB[T] Rd, [Rn], -Rm, ROR # +static INSN_REGPARM void arm646(u32 opcode) { STR_POSTDEC(OFFSET_ROR, OP_STRB, 16); } +// LDRB[T] Rd, [Rn], -Rm, LSL # +static INSN_REGPARM void arm650(u32 opcode) { LDR_POSTDEC(OFFSET_LSL, OP_LDRB, 16); } +// LDRB[T] Rd, [Rn], -Rm, LSR # +static INSN_REGPARM void arm652(u32 opcode) { LDR_POSTDEC(OFFSET_LSR, OP_LDRB, 16); } +// LDRB[T] Rd, [Rn], -Rm, ASR # +static INSN_REGPARM void arm654(u32 opcode) { LDR_POSTDEC(OFFSET_ASR, OP_LDRB, 16); } +// LDRB Rd, [Rn], -Rm, ROR # +static INSN_REGPARM void arm656(u32 opcode) { LDR_POSTDEC(OFFSET_ROR, OP_LDRB, 16); } +// STR[T] Rd, [Rn], Rm, LSL # +static INSN_REGPARM void arm680(u32 opcode) { STR_POSTINC(OFFSET_LSL, OP_STR, 32); } +// STR[T] Rd, [Rn], Rm, LSR # +static INSN_REGPARM void arm682(u32 opcode) { STR_POSTINC(OFFSET_LSR, OP_STR, 32); } +// STR[T] Rd, [Rn], Rm, ASR # +static INSN_REGPARM void arm684(u32 opcode) { STR_POSTINC(OFFSET_ASR, OP_STR, 32); } +// STR[T] Rd, [Rn], Rm, ROR # +static INSN_REGPARM void arm686(u32 opcode) { STR_POSTINC(OFFSET_ROR, OP_STR, 32); } +// LDR[T] Rd, [Rn], Rm, LSL # +static INSN_REGPARM void arm690(u32 opcode) { LDR_POSTINC(OFFSET_LSL, OP_LDR, 32); } +// LDR[T] Rd, [Rn], Rm, LSR # +static INSN_REGPARM void arm692(u32 opcode) { LDR_POSTINC(OFFSET_LSR, OP_LDR, 32); } +// LDR[T] Rd, [Rn], Rm, ASR # +static INSN_REGPARM void arm694(u32 opcode) { LDR_POSTINC(OFFSET_ASR, OP_LDR, 32); } +// LDR[T] Rd, [Rn], Rm, ROR # +static INSN_REGPARM void arm696(u32 opcode) { LDR_POSTINC(OFFSET_ROR, OP_LDR, 32); } +// STRB[T] Rd, [Rn], Rm, LSL # +static INSN_REGPARM void arm6C0(u32 opcode) { STR_POSTINC(OFFSET_LSL, OP_STRB, 16); } +// STRB[T] Rd, [Rn], Rm, LSR # +static INSN_REGPARM void arm6C2(u32 opcode) { STR_POSTINC(OFFSET_LSR, OP_STRB, 16); } +// STRB[T] Rd, [Rn], Rm, ASR # +static INSN_REGPARM void arm6C4(u32 opcode) { STR_POSTINC(OFFSET_ASR, OP_STRB, 16); } +// STRB[T] Rd, [Rn], Rm, ROR # +static INSN_REGPARM void arm6C6(u32 opcode) { STR_POSTINC(OFFSET_ROR, OP_STRB, 16); } +// LDRB[T] Rd, [Rn], Rm, LSL # +static INSN_REGPARM void arm6D0(u32 opcode) { LDR_POSTINC(OFFSET_LSL, OP_LDRB, 16); } +// LDRB[T] Rd, [Rn], Rm, LSR # +static INSN_REGPARM void arm6D2(u32 opcode) { LDR_POSTINC(OFFSET_LSR, OP_LDRB, 16); } +// LDRB[T] Rd, [Rn], Rm, ASR # +static INSN_REGPARM void arm6D4(u32 opcode) { LDR_POSTINC(OFFSET_ASR, OP_LDRB, 16); } +// LDRB[T] Rd, [Rn], Rm, ROR # +static INSN_REGPARM void arm6D6(u32 opcode) { LDR_POSTINC(OFFSET_ROR, OP_LDRB, 16); } +// STR Rd, [Rn, -Rm, LSL #] +static INSN_REGPARM void arm700(u32 opcode) { STR_PREDEC(OFFSET_LSL, OP_STR, 32); } +// STR Rd, [Rn, -Rm, LSR #] +static INSN_REGPARM void arm702(u32 opcode) { STR_PREDEC(OFFSET_LSR, OP_STR, 32); } +// STR Rd, [Rn, -Rm, ASR #] +static INSN_REGPARM void arm704(u32 opcode) { STR_PREDEC(OFFSET_ASR, OP_STR, 32); } +// STR Rd, [Rn, -Rm, ROR #] +static INSN_REGPARM void arm706(u32 opcode) { STR_PREDEC(OFFSET_ROR, OP_STR, 32); } +// LDR Rd, [Rn, -Rm, LSL #] +static INSN_REGPARM void arm710(u32 opcode) { LDR_PREDEC(OFFSET_LSL, OP_LDR, 32); } +// LDR Rd, [Rn, -Rm, LSR #] +static INSN_REGPARM void arm712(u32 opcode) { LDR_PREDEC(OFFSET_LSR, OP_LDR, 32); } +// LDR Rd, [Rn, -Rm, ASR #] +static INSN_REGPARM void arm714(u32 opcode) { LDR_PREDEC(OFFSET_ASR, OP_LDR, 32); } +// LDR Rd, [Rn, -Rm, ROR #] +static INSN_REGPARM void arm716(u32 opcode) { LDR_PREDEC(OFFSET_ROR, OP_LDR, 32); } +// STR Rd, [Rn, -Rm, LSL #]! +static INSN_REGPARM void arm720(u32 opcode) { STR_PREDEC_WB(OFFSET_LSL, OP_STR, 32); } +// STR Rd, [Rn, -Rm, LSR #]! +static INSN_REGPARM void arm722(u32 opcode) { STR_PREDEC_WB(OFFSET_LSR, OP_STR, 32); } +// STR Rd, [Rn, -Rm, ASR #]! +static INSN_REGPARM void arm724(u32 opcode) { STR_PREDEC_WB(OFFSET_ASR, OP_STR, 32); } +// STR Rd, [Rn, -Rm, ROR #]! +static INSN_REGPARM void arm726(u32 opcode) { STR_PREDEC_WB(OFFSET_ROR, OP_STR, 32); } +// LDR Rd, [Rn, -Rm, LSL #]! +static INSN_REGPARM void arm730(u32 opcode) { LDR_PREDEC_WB(OFFSET_LSL, OP_LDR, 32); } +// LDR Rd, [Rn, -Rm, LSR #]! +static INSN_REGPARM void arm732(u32 opcode) { LDR_PREDEC_WB(OFFSET_LSR, OP_LDR, 32); } +// LDR Rd, [Rn, -Rm, ASR #]! +static INSN_REGPARM void arm734(u32 opcode) { LDR_PREDEC_WB(OFFSET_ASR, OP_LDR, 32); } +// LDR Rd, [Rn, -Rm, ROR #]! +static INSN_REGPARM void arm736(u32 opcode) { LDR_PREDEC_WB(OFFSET_ROR, OP_LDR, 32); } +// STRB Rd, [Rn, -Rm, LSL #] +static INSN_REGPARM void arm740(u32 opcode) { STR_PREDEC(OFFSET_LSL, OP_STRB, 16); } +// STRB Rd, [Rn, -Rm, LSR #] +static INSN_REGPARM void arm742(u32 opcode) { STR_PREDEC(OFFSET_LSR, OP_STRB, 16); } +// STRB Rd, [Rn, -Rm, ASR #] +static INSN_REGPARM void arm744(u32 opcode) { STR_PREDEC(OFFSET_ASR, OP_STRB, 16); } +// STRB Rd, [Rn, -Rm, ROR #] +static INSN_REGPARM void arm746(u32 opcode) { STR_PREDEC(OFFSET_ROR, OP_STRB, 16); } +// LDRB Rd, [Rn, -Rm, LSL #] +static INSN_REGPARM void arm750(u32 opcode) { LDR_PREDEC(OFFSET_LSL, OP_LDRB, 16); } +// LDRB Rd, [Rn, -Rm, LSR #] +static INSN_REGPARM void arm752(u32 opcode) { LDR_PREDEC(OFFSET_LSR, OP_LDRB, 16); } +// LDRB Rd, [Rn, -Rm, ASR #] +static INSN_REGPARM void arm754(u32 opcode) { LDR_PREDEC(OFFSET_ASR, OP_LDRB, 16); } +// LDRB Rd, [Rn, -Rm, ROR #] +static INSN_REGPARM void arm756(u32 opcode) { LDR_PREDEC(OFFSET_ROR, OP_LDRB, 16); } +// STRB Rd, [Rn, -Rm, LSL #]! +static INSN_REGPARM void arm760(u32 opcode) { STR_PREDEC_WB(OFFSET_LSL, OP_STRB, 16); } +// STRB Rd, [Rn, -Rm, LSR #]! +static INSN_REGPARM void arm762(u32 opcode) { STR_PREDEC_WB(OFFSET_LSR, OP_STRB, 16); } +// STRB Rd, [Rn, -Rm, ASR #]! +static INSN_REGPARM void arm764(u32 opcode) { STR_PREDEC_WB(OFFSET_ASR, OP_STRB, 16); } +// STRB Rd, [Rn, -Rm, ROR #]! +static INSN_REGPARM void arm766(u32 opcode) { STR_PREDEC_WB(OFFSET_ROR, OP_STRB, 16); } +// LDRB Rd, [Rn, -Rm, LSL #]! +static INSN_REGPARM void arm770(u32 opcode) { LDR_PREDEC_WB(OFFSET_LSL, OP_LDRB, 16); } +// LDRB Rd, [Rn, -Rm, LSR #]! +static INSN_REGPARM void arm772(u32 opcode) { LDR_PREDEC_WB(OFFSET_LSR, OP_LDRB, 16); } +// LDRB Rd, [Rn, -Rm, ASR #]! +static INSN_REGPARM void arm774(u32 opcode) { LDR_PREDEC_WB(OFFSET_ASR, OP_LDRB, 16); } +// LDRB Rd, [Rn, -Rm, ROR #]! +static INSN_REGPARM void arm776(u32 opcode) { LDR_PREDEC_WB(OFFSET_ROR, OP_LDRB, 16); } +// STR Rd, [Rn, Rm, LSL #] +static INSN_REGPARM void arm780(u32 opcode) { STR_PREINC(OFFSET_LSL, OP_STR, 32); } +// STR Rd, [Rn, Rm, LSR #] +static INSN_REGPARM void arm782(u32 opcode) { STR_PREINC(OFFSET_LSR, OP_STR, 32); } +// STR Rd, [Rn, Rm, ASR #] +static INSN_REGPARM void arm784(u32 opcode) { STR_PREINC(OFFSET_ASR, OP_STR, 32); } +// STR Rd, [Rn, Rm, ROR #] +static INSN_REGPARM void arm786(u32 opcode) { STR_PREINC(OFFSET_ROR, OP_STR, 32); } +// LDR Rd, [Rn, Rm, LSL #] +static INSN_REGPARM void arm790(u32 opcode) { LDR_PREINC(OFFSET_LSL, OP_LDR, 32); } +// LDR Rd, [Rn, Rm, LSR #] +static INSN_REGPARM void arm792(u32 opcode) { LDR_PREINC(OFFSET_LSR, OP_LDR, 32); } +// LDR Rd, [Rn, Rm, ASR #] +static INSN_REGPARM void arm794(u32 opcode) { LDR_PREINC(OFFSET_ASR, OP_LDR, 32); } +// LDR Rd, [Rn, Rm, ROR #] +static INSN_REGPARM void arm796(u32 opcode) { LDR_PREINC(OFFSET_ROR, OP_LDR, 32); } +// STR Rd, [Rn, Rm, LSL #]! +static INSN_REGPARM void arm7A0(u32 opcode) { STR_PREINC_WB(OFFSET_LSL, OP_STR, 32); } +// STR Rd, [Rn, Rm, LSR #]! +static INSN_REGPARM void arm7A2(u32 opcode) { STR_PREINC_WB(OFFSET_LSR, OP_STR, 32); } +// STR Rd, [Rn, Rm, ASR #]! +static INSN_REGPARM void arm7A4(u32 opcode) { STR_PREINC_WB(OFFSET_ASR, OP_STR, 32); } +// STR Rd, [Rn, Rm, ROR #]! +static INSN_REGPARM void arm7A6(u32 opcode) { STR_PREINC_WB(OFFSET_ROR, OP_STR, 32); } +// LDR Rd, [Rn, Rm, LSL #]! +static INSN_REGPARM void arm7B0(u32 opcode) { LDR_PREINC_WB(OFFSET_LSL, OP_LDR, 32); } +// LDR Rd, [Rn, Rm, LSR #]! +static INSN_REGPARM void arm7B2(u32 opcode) { LDR_PREINC_WB(OFFSET_LSR, OP_LDR, 32); } +// LDR Rd, [Rn, Rm, ASR #]! +static INSN_REGPARM void arm7B4(u32 opcode) { LDR_PREINC_WB(OFFSET_ASR, OP_LDR, 32); } +// LDR Rd, [Rn, Rm, ROR #]! +static INSN_REGPARM void arm7B6(u32 opcode) { LDR_PREINC_WB(OFFSET_ROR, OP_LDR, 32); } +// STRB Rd, [Rn, Rm, LSL #] +static INSN_REGPARM void arm7C0(u32 opcode) { STR_PREINC(OFFSET_LSL, OP_STRB, 16); } +// STRB Rd, [Rn, Rm, LSR #] +static INSN_REGPARM void arm7C2(u32 opcode) { STR_PREINC(OFFSET_LSR, OP_STRB, 16); } +// STRB Rd, [Rn, Rm, ASR #] +static INSN_REGPARM void arm7C4(u32 opcode) { STR_PREINC(OFFSET_ASR, OP_STRB, 16); } +// STRB Rd, [Rn, Rm, ROR #] +static INSN_REGPARM void arm7C6(u32 opcode) { STR_PREINC(OFFSET_ROR, OP_STRB, 16); } +// LDRB Rd, [Rn, Rm, LSL #] +static INSN_REGPARM void arm7D0(u32 opcode) { LDR_PREINC(OFFSET_LSL, OP_LDRB, 16); } +// LDRB Rd, [Rn, Rm, LSR #] +static INSN_REGPARM void arm7D2(u32 opcode) { LDR_PREINC(OFFSET_LSR, OP_LDRB, 16); } +// LDRB Rd, [Rn, Rm, ASR #] +static INSN_REGPARM void arm7D4(u32 opcode) { LDR_PREINC(OFFSET_ASR, OP_LDRB, 16); } +// LDRB Rd, [Rn, Rm, ROR #] +static INSN_REGPARM void arm7D6(u32 opcode) { LDR_PREINC(OFFSET_ROR, OP_LDRB, 16); } +// STRB Rd, [Rn, Rm, LSL #]! +static INSN_REGPARM void arm7E0(u32 opcode) { STR_PREINC_WB(OFFSET_LSL, OP_STRB, 16); } +// STRB Rd, [Rn, Rm, LSR #]! +static INSN_REGPARM void arm7E2(u32 opcode) { STR_PREINC_WB(OFFSET_LSR, OP_STRB, 16); } +// STRB Rd, [Rn, Rm, ASR #]! +static INSN_REGPARM void arm7E4(u32 opcode) { STR_PREINC_WB(OFFSET_ASR, OP_STRB, 16); } +// STRB Rd, [Rn, Rm, ROR #]! +static INSN_REGPARM void arm7E6(u32 opcode) { STR_PREINC_WB(OFFSET_ROR, OP_STRB, 16); } +// LDRB Rd, [Rn, Rm, LSL #]! +static INSN_REGPARM void arm7F0(u32 opcode) { LDR_PREINC_WB(OFFSET_LSL, OP_LDRB, 16); } +// LDRB Rd, [Rn, Rm, LSR #]! +static INSN_REGPARM void arm7F2(u32 opcode) { LDR_PREINC_WB(OFFSET_LSR, OP_LDRB, 16); } +// LDRB Rd, [Rn, Rm, ASR #]! +static INSN_REGPARM void arm7F4(u32 opcode) { LDR_PREINC_WB(OFFSET_ASR, OP_LDRB, 16); } +// LDRB Rd, [Rn, Rm, ROR #]! +static INSN_REGPARM void arm7F6(u32 opcode) { LDR_PREINC_WB(OFFSET_ROR, OP_LDRB, 16); } + +// STM/LDM //////////////////////////////////////////////////////////////// + +#define STM_REG(bit,num) \ + if (opcode & (1U<<(bit))) { \ + CPUWriteMemory(address, reg[(num)].I); \ + if (!count) { \ + clockTicks += 1 + dataTicksAccess32(address);\ + } else { \ + clockTicks += 1 + dataTicksAccessSeq32(address);\ + } \ + count++; \ + address += 4; \ + } +#define STMW_REG(bit,num) \ + if (opcode & (1U<<(bit))) { \ + CPUWriteMemory(address, reg[(num)].I); \ + if (!count) { \ + clockTicks += 1 + dataTicksAccess32(address);\ + } else { \ + clockTicks += 1 + dataTicksAccessSeq32(address);\ + } \ + reg[base].I = temp; \ + count++; \ + address += 4; \ + } +#define LDM_REG(bit,num) \ + if (opcode & (1U<<(bit))) { \ + reg[(num)].I = CPUReadMemory(address); \ + if (!count) { \ + clockTicks += 1 + dataTicksAccess32(address);\ + } else { \ + clockTicks += 1 + dataTicksAccessSeq32(address);\ + } \ + count++; \ + address += 4; \ + } +#define STM_LOW(STORE_REG) \ + STORE_REG(0, 0); \ + STORE_REG(1, 1); \ + STORE_REG(2, 2); \ + STORE_REG(3, 3); \ + STORE_REG(4, 4); \ + STORE_REG(5, 5); \ + STORE_REG(6, 6); \ + STORE_REG(7, 7); +#define STM_HIGH(STORE_REG) \ + STORE_REG(8, 8); \ + STORE_REG(9, 9); \ + STORE_REG(10, 10); \ + STORE_REG(11, 11); \ + STORE_REG(12, 12); \ + STORE_REG(13, 13); \ + STORE_REG(14, 14); +#define STM_HIGH_2(STORE_REG) \ + if (armMode == 0x11) { \ + STORE_REG(8, R8_FIQ); \ + STORE_REG(9, R9_FIQ); \ + STORE_REG(10, R10_FIQ); \ + STORE_REG(11, R11_FIQ); \ + STORE_REG(12, R12_FIQ); \ + } else { \ + STORE_REG(8, 8); \ + STORE_REG(9, 9); \ + STORE_REG(10, 10); \ + STORE_REG(11, 11); \ + STORE_REG(12, 12); \ + } \ + if (armMode != 0x10 && armMode != 0x1F) { \ + STORE_REG(13, R13_USR); \ + STORE_REG(14, R14_USR); \ + } else { \ + STORE_REG(13, 13); \ + STORE_REG(14, 14); \ + } +#define STM_PC \ + if (opcode & (1U<<15)) { \ + CPUWriteMemory(address, reg[15].I+4); \ + if (!count) { \ + clockTicks += 1 + dataTicksAccess32(address);\ + } else { \ + clockTicks += 1 + dataTicksAccessSeq32(address);\ + } \ + count++; \ + } +#define STMW_PC \ + if (opcode & (1U<<15)) { \ + CPUWriteMemory(address, reg[15].I+4); \ + if (!count) { \ + clockTicks += 1 + dataTicksAccess32(address);\ + } else { \ + clockTicks += 1 + dataTicksAccessSeq32(address);\ + } \ + reg[base].I = temp; \ + count++; \ + } +#define LDM_LOW \ + LDM_REG(0, 0); \ + LDM_REG(1, 1); \ + LDM_REG(2, 2); \ + LDM_REG(3, 3); \ + LDM_REG(4, 4); \ + LDM_REG(5, 5); \ + LDM_REG(6, 6); \ + LDM_REG(7, 7); +#define LDM_HIGH \ + LDM_REG(8, 8); \ + LDM_REG(9, 9); \ + LDM_REG(10, 10); \ + LDM_REG(11, 11); \ + LDM_REG(12, 12); \ + LDM_REG(13, 13); \ + LDM_REG(14, 14); +#define LDM_HIGH_2 \ + if (armMode == 0x11) { \ + LDM_REG(8, R8_FIQ); \ + LDM_REG(9, R9_FIQ); \ + LDM_REG(10, R10_FIQ); \ + LDM_REG(11, R11_FIQ); \ + LDM_REG(12, R12_FIQ); \ + } else { \ + LDM_REG(8, 8); \ + LDM_REG(9, 9); \ + LDM_REG(10, 10); \ + LDM_REG(11, 11); \ + LDM_REG(12, 12); \ + } \ + if (armMode != 0x10 && armMode != 0x1F) { \ + LDM_REG(13, R13_USR); \ + LDM_REG(14, R14_USR); \ + } else { \ + LDM_REG(13, 13); \ + LDM_REG(14, 14); \ + } +#define STM_ALL \ + STM_LOW(STM_REG); \ + STM_HIGH(STM_REG); \ + STM_PC; +#define STMW_ALL \ + STM_LOW(STMW_REG); \ + STM_HIGH(STMW_REG); \ + STMW_PC; +#define LDM_ALL \ + LDM_LOW; \ + LDM_HIGH; \ + if (opcode & (1U<<15)) { \ + reg[15].I = CPUReadMemory(address); \ + if (!count) { \ + clockTicks += 1 + dataTicksAccess32(address);\ + } else { \ + clockTicks += 1 + dataTicksAccessSeq32(address);\ + } \ + count++; \ + } \ + if (opcode & (1U<<15)) { \ + armNextPC = reg[15].I; \ + reg[15].I += 4; \ + ARM_PREFETCH; \ + clockTicks += 1 + codeTicksAccessSeq32(armNextPC);\ + } +#define STM_ALL_2 \ + STM_LOW(STM_REG); \ + STM_HIGH_2(STM_REG); \ + STM_PC; +#define STMW_ALL_2 \ + STM_LOW(STMW_REG); \ + STM_HIGH_2(STMW_REG); \ + STMW_PC; +#define LDM_ALL_2 \ + LDM_LOW; \ + if (opcode & (1U<<15)) { \ + LDM_HIGH; \ + reg[15].I = CPUReadMemory(address); \ + if (!count) { \ + clockTicks += 1 + dataTicksAccess32(address); \ + } else { \ + clockTicks += 1 + dataTicksAccessSeq32(address); \ + } \ + count++; \ + } else { \ + LDM_HIGH_2; \ + } +#define LDM_ALL_2B \ + if (opcode & (1U<<15)) { \ + CPUSwitchMode(reg[17].I & 0x1F, false); \ + if (armState) { \ + armNextPC = reg[15].I & 0xFFFFFFFC; \ + reg[15].I = armNextPC + 4; \ + ARM_PREFETCH; \ + } else { \ + armNextPC = reg[15].I & 0xFFFFFFFE; \ + reg[15].I = armNextPC + 2; \ + THUMB_PREFETCH; \ + } \ + clockTicks += 1 + codeTicksAccessSeq32(armNextPC);\ + } + + +// STMDA Rn, {Rlist} +static INSN_REGPARM void arm800(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp + 4) & 0xFFFFFFFC; + int count = 0; + STM_ALL; + clockTicks += 1 + codeTicksAccess32(armNextPC); +} + +// LDMDA Rn, {Rlist} +static INSN_REGPARM void arm810(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp + 4) & 0xFFFFFFFC; + int count = 0; + LDM_ALL; + clockTicks += 2 + codeTicksAccess32(armNextPC); +} + +// STMDA Rn!, {Rlist} +static INSN_REGPARM void arm820(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp+4) & 0xFFFFFFFC; + int count = 0; + STMW_ALL; + clockTicks += 1 + codeTicksAccess32(armNextPC); +} + +// LDMDA Rn!, {Rlist} +static INSN_REGPARM void arm830(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp + 4) & 0xFFFFFFFC; + int count = 0; + LDM_ALL; + clockTicks += 2 + codeTicksAccess32(armNextPC); + if (!(opcode & (1U << base))) + reg[base].I = temp; +} + +// STMDA Rn, {Rlist}^ +static INSN_REGPARM void arm840(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp+4) & 0xFFFFFFFC; + int count = 0; + STM_ALL_2; + clockTicks += 1 + codeTicksAccess32(armNextPC); +} + +// LDMDA Rn, {Rlist}^ +static INSN_REGPARM void arm850(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp + 4) & 0xFFFFFFFC; + int count = 0; + LDM_ALL_2; + LDM_ALL_2B; + clockTicks += 2 + codeTicksAccess32(armNextPC); +} + +// STMDA Rn!, {Rlist}^ +static INSN_REGPARM void arm860(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp+4) & 0xFFFFFFFC; + int count = 0; + STMW_ALL_2; + clockTicks += 1 + codeTicksAccess32(armNextPC); +} + +// LDMDA Rn!, {Rlist}^ +static INSN_REGPARM void arm870(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp + 4) & 0xFFFFFFFC; + int count = 0; + LDM_ALL_2; + if (!(opcode & (1U << base))) + reg[base].I = temp; + LDM_ALL_2B; + clockTicks += 2 + codeTicksAccess32(armNextPC); +} + +// STMIA Rn, {Rlist} +static INSN_REGPARM void arm880(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = reg[base].I & 0xFFFFFFFC; + int count = 0; + STM_ALL; + clockTicks += 1 + codeTicksAccess32(armNextPC); +} + +// LDMIA Rn, {Rlist} +static INSN_REGPARM void arm890(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = reg[base].I & 0xFFFFFFFC; + int count = 0; + LDM_ALL; + clockTicks += 2 + codeTicksAccess32(armNextPC); +} + +// STMIA Rn!, {Rlist} +static INSN_REGPARM void arm8A0(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = reg[base].I & 0xFFFFFFFC; + int count = 0; + u32 temp = reg[base].I + + 4 * (cpuBitsSet[opcode & 0xFF] + cpuBitsSet[(opcode >> 8) & 255]); + STMW_ALL; + clockTicks += 1 + codeTicksAccess32(armNextPC); +} + +// LDMIA Rn!, {Rlist} +static INSN_REGPARM void arm8B0(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I + + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = reg[base].I & 0xFFFFFFFC; + int count = 0; + LDM_ALL; + clockTicks += 2 + codeTicksAccess32(armNextPC); + if (!(opcode & (1U << base))) + reg[base].I = temp; +} + +// STMIA Rn, {Rlist}^ +static INSN_REGPARM void arm8C0(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = reg[base].I & 0xFFFFFFFC; + int count = 0; + STM_ALL_2; + clockTicks += 1 + codeTicksAccess32(armNextPC); +} + +// LDMIA Rn, {Rlist}^ +static INSN_REGPARM void arm8D0(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = reg[base].I & 0xFFFFFFFC; + int count = 0; + LDM_ALL_2; + LDM_ALL_2B; + clockTicks += 2 + codeTicksAccess32(armNextPC); +} + +// STMIA Rn!, {Rlist}^ +static INSN_REGPARM void arm8E0(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = reg[base].I & 0xFFFFFFFC; + int count = 0; + u32 temp = reg[base].I + + 4 * (cpuBitsSet[opcode & 0xFF] + cpuBitsSet[(opcode >> 8) & 255]); + STMW_ALL_2; + clockTicks += 1 + codeTicksAccess32(armNextPC); +} + +// LDMIA Rn!, {Rlist}^ +static INSN_REGPARM void arm8F0(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I + + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = reg[base].I & 0xFFFFFFFC; + int count = 0; + LDM_ALL_2; + if (!(opcode & (1U << base))) + reg[base].I = temp; + LDM_ALL_2B; + clockTicks += 2 + codeTicksAccess32(armNextPC); +} + +// STMDB Rn, {Rlist} +static INSN_REGPARM void arm900(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + int count = 0; + STM_ALL; + clockTicks += 1 + codeTicksAccess32(armNextPC); +} + +// LDMDB Rn, {Rlist} +static INSN_REGPARM void arm910(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + int count = 0; + LDM_ALL; + clockTicks += 2 + codeTicksAccess32(armNextPC); +} + +// STMDB Rn!, {Rlist} +static INSN_REGPARM void arm920(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + int count = 0; + STMW_ALL; + clockTicks += 1 + codeTicksAccess32(armNextPC); +} + +// LDMDB Rn!, {Rlist} +static INSN_REGPARM void arm930(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + int count = 0; + LDM_ALL; + clockTicks += 2 + codeTicksAccess32(armNextPC); + if (!(opcode & (1U << base))) + reg[base].I = temp; +} + +// STMDB Rn, {Rlist}^ +static INSN_REGPARM void arm940(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + int count = 0; + STM_ALL_2; + clockTicks += 1 + codeTicksAccess32(armNextPC); +} + +// LDMDB Rn, {Rlist}^ +static INSN_REGPARM void arm950(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + int count = 0; + LDM_ALL_2; + LDM_ALL_2B; + clockTicks += 2 + codeTicksAccess32(armNextPC); +} + +// STMDB Rn!, {Rlist}^ +static INSN_REGPARM void arm960(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + int count = 0; + STMW_ALL_2; + clockTicks += 1 + codeTicksAccess32(armNextPC); +} + +// LDMDB Rn!, {Rlist}^ +static INSN_REGPARM void arm970(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + int count = 0; + LDM_ALL_2; + if (!(opcode & (1U << base))) + reg[base].I = temp; + LDM_ALL_2B; + clockTicks += 2 + codeTicksAccess32(armNextPC); +} + +// STMIB Rn, {Rlist} +static INSN_REGPARM void arm980(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = (reg[base].I+4) & 0xFFFFFFFC; + int count = 0; + STM_ALL; + clockTicks += 1 + codeTicksAccess32(armNextPC); +} + +// LDMIB Rn, {Rlist} +static INSN_REGPARM void arm990(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = (reg[base].I+4) & 0xFFFFFFFC; + int count = 0; + LDM_ALL; + clockTicks += 2 + codeTicksAccess32(armNextPC); +} + +// STMIB Rn!, {Rlist} +static INSN_REGPARM void arm9A0(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = (reg[base].I+4) & 0xFFFFFFFC; + int count = 0; + u32 temp = reg[base].I + + 4 * (cpuBitsSet[opcode & 0xFF] + cpuBitsSet[(opcode >> 8) & 255]); + STMW_ALL; + clockTicks += 1 + codeTicksAccess32(armNextPC); +} + +// LDMIB Rn!, {Rlist} +static INSN_REGPARM void arm9B0(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I + + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (reg[base].I+4) & 0xFFFFFFFC; + int count = 0; + LDM_ALL; + clockTicks += 2 + codeTicksAccess32(armNextPC); + if (!(opcode & (1U << base))) + reg[base].I = temp; +} + +// STMIB Rn, {Rlist}^ +static INSN_REGPARM void arm9C0(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = (reg[base].I+4) & 0xFFFFFFFC; + int count = 0; + STM_ALL_2; + clockTicks += 1 + codeTicksAccess32(armNextPC); +} + +// LDMIB Rn, {Rlist}^ +static INSN_REGPARM void arm9D0(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = (reg[base].I+4) & 0xFFFFFFFC; + int count = 0; + LDM_ALL_2; + LDM_ALL_2B; + clockTicks += 2 + codeTicksAccess32(armNextPC); +} + +// STMIB Rn!, {Rlist}^ +static INSN_REGPARM void arm9E0(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = (reg[base].I+4) & 0xFFFFFFFC; + int count = 0; + u32 temp = reg[base].I + + 4 * (cpuBitsSet[opcode & 0xFF] + cpuBitsSet[(opcode >> 8) & 255]); + STMW_ALL_2; + clockTicks += 1 + codeTicksAccess32(armNextPC); +} + +// LDMIB Rn!, {Rlist}^ +static INSN_REGPARM void arm9F0(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I + + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (reg[base].I+4) & 0xFFFFFFFC; + int count = 0; + LDM_ALL_2; + if (!(opcode & (1U << base))) + reg[base].I = temp; + LDM_ALL_2B; + clockTicks += 2 + codeTicksAccess32(armNextPC); +} + +// B/BL/SWI and (unimplemented) coproc support //////////////////////////// + +// B +static INSN_REGPARM void armA00(u32 opcode) +{ + int offset = opcode & 0x00FFFFFF; + if (offset & 0x00800000) + offset |= 0xFF000000; // negative offset + reg[15].I += offset<<2; + armNextPC = reg[15].I; + reg[15].I += 4; + ARM_PREFETCH; + clockTicks = codeTicksAccessSeq32(armNextPC) + 1; + clockTicks = (clockTicks * 2) + codeTicksAccess32(armNextPC) + 1; + busPrefetchCount = 0; +} + +// BL +static INSN_REGPARM void armB00(u32 opcode) +{ + int offset = opcode & 0x00FFFFFF; + if (offset & 0x00800000) + offset |= 0xFF000000; // negative offset + reg[14].I = reg[15].I - 4; + reg[15].I += offset<<2; + armNextPC = reg[15].I; + reg[15].I += 4; + ARM_PREFETCH; + clockTicks = codeTicksAccessSeq32(armNextPC) + 1; + clockTicks = (clockTicks * 2) + codeTicksAccess32(armNextPC) + 1; + busPrefetchCount = 0; +} + + +#ifdef GP_SUPPORT +// MRC +static INSN_REGPARM void armE01(u32 opcode) +{ +} +#else + #define armE01 armUnknownInsn +#endif + + +// SWI +static INSN_REGPARM void armF00(u32 opcode) +{ + clockTicks = codeTicksAccessSeq32(armNextPC) + 1; + clockTicks = (clockTicks * 2) + codeTicksAccess32(armNextPC) + 1; + busPrefetchCount = 0; + CPUSoftwareInterrupt(opcode & 0x00FFFFFF); +} + +// Instruction table ////////////////////////////////////////////////////// + +typedef INSN_REGPARM void (*insnfunc_t)(u32 opcode); +#define REP16(insn) \ + insn,insn,insn,insn,insn,insn,insn,insn,\ + insn,insn,insn,insn,insn,insn,insn,insn +#define REP256(insn) \ + REP16(insn),REP16(insn),REP16(insn),REP16(insn),\ + REP16(insn),REP16(insn),REP16(insn),REP16(insn),\ + REP16(insn),REP16(insn),REP16(insn),REP16(insn),\ + REP16(insn),REP16(insn),REP16(insn),REP16(insn) +#define arm_UI armUnknownInsn +#ifdef BKPT_SUPPORT + #define arm_BP armBreakpoint +#else + #define arm_BP armUnknownInsn +#endif +static insnfunc_t armInsnTable[4096] = { + arm000,arm001,arm002,arm003,arm004,arm005,arm006,arm007, // 000 + arm000,arm009,arm002,arm00B,arm004,arm_UI,arm006,arm_UI, // 008 + arm010,arm011,arm012,arm013,arm014,arm015,arm016,arm017, // 010 + arm010,arm019,arm012,arm01B,arm014,arm01D,arm016,arm01F, // 018 + arm020,arm021,arm022,arm023,arm024,arm025,arm026,arm027, // 020 + arm020,arm029,arm022,arm_UI,arm024,arm_UI,arm026,arm_UI, // 028 + arm030,arm031,arm032,arm033,arm034,arm035,arm036,arm037, // 030 + arm030,arm039,arm032,arm_UI,arm034,arm01D,arm036,arm01F, // 038 + arm040,arm041,arm042,arm043,arm044,arm045,arm046,arm047, // 040 + arm040,arm_UI,arm042,arm04B,arm044,arm_UI,arm046,arm_UI, // 048 + arm050,arm051,arm052,arm053,arm054,arm055,arm056,arm057, // 050 + arm050,arm_UI,arm052,arm05B,arm054,arm05D,arm056,arm05F, // 058 + arm060,arm061,arm062,arm063,arm064,arm065,arm066,arm067, // 060 + arm060,arm_UI,arm062,arm_UI,arm064,arm_UI,arm066,arm_UI, // 068 + arm070,arm071,arm072,arm073,arm074,arm075,arm076,arm077, // 070 + arm070,arm_UI,arm072,arm_UI,arm074,arm05D,arm076,arm05F, // 078 + arm080,arm081,arm082,arm083,arm084,arm085,arm086,arm087, // 080 + arm080,arm089,arm082,arm08B,arm084,arm_UI,arm086,arm_UI, // 088 + arm090,arm091,arm092,arm093,arm094,arm095,arm096,arm097, // 090 + arm090,arm099,arm092,arm09B,arm094,arm09D,arm096,arm09F, // 098 + arm0A0,arm0A1,arm0A2,arm0A3,arm0A4,arm0A5,arm0A6,arm0A7, // 0A0 + arm0A0,arm0A9,arm0A2,arm_UI,arm0A4,arm_UI,arm0A6,arm_UI, // 0A8 + arm0B0,arm0B1,arm0B2,arm0B3,arm0B4,arm0B5,arm0B6,arm0B7, // 0B0 + arm0B0,arm0B9,arm0B2,arm_UI,arm0B4,arm09D,arm0B6,arm09F, // 0B8 + arm0C0,arm0C1,arm0C2,arm0C3,arm0C4,arm0C5,arm0C6,arm0C7, // 0C0 + arm0C0,arm0C9,arm0C2,arm0CB,arm0C4,arm_UI,arm0C6,arm_UI, // 0C8 + arm0D0,arm0D1,arm0D2,arm0D3,arm0D4,arm0D5,arm0D6,arm0D7, // 0D0 + arm0D0,arm0D9,arm0D2,arm0DB,arm0D4,arm0DD,arm0D6,arm0DF, // 0D8 + arm0E0,arm0E1,arm0E2,arm0E3,arm0E4,arm0E5,arm0E6,arm0E7, // 0E0 + arm0E0,arm0E9,arm0E2,arm0CB,arm0E4,arm_UI,arm0E6,arm_UI, // 0E8 + arm0F0,arm0F1,arm0F2,arm0F3,arm0F4,arm0F5,arm0F6,arm0F7, // 0F0 + arm0F0,arm0F9,arm0F2,arm0DB,arm0F4,arm0DD,arm0F6,arm0DF, // 0F8 + + arm100,arm_UI,arm_UI,arm_UI,arm_UI,arm_UI,arm_UI,arm_UI, // 100 + arm_UI,arm109,arm_UI,arm10B,arm_UI,arm_UI,arm_UI,arm_UI, // 108 + arm110,arm111,arm112,arm113,arm114,arm115,arm116,arm117, // 110 + arm110,arm_UI,arm112,arm11B,arm114,arm11D,arm116,arm11F, // 118 + arm120,arm121,arm_UI,arm_UI,arm_UI,arm_UI,arm_UI,arm_BP, // 120 + arm_UI,arm_UI,arm_UI,arm12B,arm_UI,arm_UI,arm_UI,arm_UI, // 128 + arm130,arm131,arm132,arm133,arm134,arm135,arm136,arm137, // 130 + arm130,arm_UI,arm132,arm13B,arm134,arm13D,arm136,arm13F, // 138 + arm140,arm_UI,arm_UI,arm_UI,arm_UI,arm_UI,arm_UI,arm_UI, // 140 + arm_UI,arm149,arm_UI,arm14B,arm_UI,arm_UI,arm_UI,arm_UI, // 148 + arm150,arm151,arm152,arm153,arm154,arm155,arm156,arm157, // 150 + arm150,arm_UI,arm152,arm15B,arm154,arm15D,arm156,arm15F, // 158 + arm160,arm_UI,arm_UI,arm_UI,arm_UI,arm_UI,arm_UI,arm_UI, // 160 + arm_UI,arm_UI,arm_UI,arm16B,arm_UI,arm_UI,arm_UI,arm_UI, // 168 + arm170,arm171,arm172,arm173,arm174,arm175,arm176,arm177, // 170 + arm170,arm_UI,arm172,arm17B,arm174,arm17D,arm176,arm17F, // 178 + arm180,arm181,arm182,arm183,arm184,arm185,arm186,arm187, // 180 + arm180,arm_UI,arm182,arm18B,arm184,arm_UI,arm186,arm_UI, // 188 + arm190,arm191,arm192,arm193,arm194,arm195,arm196,arm197, // 190 + arm190,arm_UI,arm192,arm19B,arm194,arm19D,arm196,arm19F, // 198 + arm1A0,arm1A1,arm1A2,arm1A3,arm1A4,arm1A5,arm1A6,arm1A7, // 1A0 + arm1A0,arm_UI,arm1A2,arm1AB,arm1A4,arm_UI,arm1A6,arm_UI, // 1A8 + arm1B0,arm1B1,arm1B2,arm1B3,arm1B4,arm1B5,arm1B6,arm1B7, // 1B0 + arm1B0,arm_UI,arm1B2,arm1BB,arm1B4,arm1BD,arm1B6,arm1BF, // 1B8 + arm1C0,arm1C1,arm1C2,arm1C3,arm1C4,arm1C5,arm1C6,arm1C7, // 1C0 + arm1C0,arm_UI,arm1C2,arm1CB,arm1C4,arm_UI,arm1C6,arm_UI, // 1C8 + arm1D0,arm1D1,arm1D2,arm1D3,arm1D4,arm1D5,arm1D6,arm1D7, // 1D0 + arm1D0,arm_UI,arm1D2,arm1DB,arm1D4,arm1DD,arm1D6,arm1DF, // 1D8 + arm1E0,arm1E1,arm1E2,arm1E3,arm1E4,arm1E5,arm1E6,arm1E7, // 1E0 + arm1E0,arm_UI,arm1E2,arm1EB,arm1E4,arm_UI,arm1E6,arm_UI, // 1E8 + arm1F0,arm1F1,arm1F2,arm1F3,arm1F4,arm1F5,arm1F6,arm1F7, // 1F0 + arm1F0,arm_UI,arm1F2,arm1FB,arm1F4,arm1FD,arm1F6,arm1FF, // 1F8 + + REP16(arm200),REP16(arm210),REP16(arm220),REP16(arm230), // 200 + REP16(arm240),REP16(arm250),REP16(arm260),REP16(arm270), // 240 + REP16(arm280),REP16(arm290),REP16(arm2A0),REP16(arm2B0), // 280 + REP16(arm2C0),REP16(arm2D0),REP16(arm2E0),REP16(arm2F0), // 2C0 + REP16(arm_UI),REP16(arm310),REP16(arm320),REP16(arm330), // 300 + REP16(arm_UI),REP16(arm350),REP16(arm360),REP16(arm370), // 340 + REP16(arm380),REP16(arm390),REP16(arm3A0),REP16(arm3B0), // 380 + REP16(arm3C0),REP16(arm3D0),REP16(arm3E0),REP16(arm3F0), // 3C0 + + REP16(arm400),REP16(arm410),REP16(arm400),REP16(arm410), // 400 + REP16(arm440),REP16(arm450),REP16(arm440),REP16(arm450), // 440 + REP16(arm480),REP16(arm490),REP16(arm480),REP16(arm490), // 480 + REP16(arm4C0),REP16(arm4D0),REP16(arm4C0),REP16(arm4D0), // 4C0 + REP16(arm500),REP16(arm510),REP16(arm520),REP16(arm530), // 500 + REP16(arm540),REP16(arm550),REP16(arm560),REP16(arm570), // 540 + REP16(arm580),REP16(arm590),REP16(arm5A0),REP16(arm5B0), // 580 + REP16(arm5C0),REP16(arm5D0),REP16(arm5E0),REP16(arm5F0), // 5C0 + + arm600,arm_UI,arm602,arm_UI,arm604,arm_UI,arm606,arm_UI, // 600 + arm600,arm_UI,arm602,arm_UI,arm604,arm_UI,arm606,arm_UI, // 608 + arm610,arm_UI,arm612,arm_UI,arm614,arm_UI,arm616,arm_UI, // 610 + arm610,arm_UI,arm612,arm_UI,arm614,arm_UI,arm616,arm_UI, // 618 + arm600,arm_UI,arm602,arm_UI,arm604,arm_UI,arm606,arm_UI, // 620 + arm600,arm_UI,arm602,arm_UI,arm604,arm_UI,arm606,arm_UI, // 628 + arm610,arm_UI,arm612,arm_UI,arm614,arm_UI,arm616,arm_UI, // 630 + arm610,arm_UI,arm612,arm_UI,arm614,arm_UI,arm616,arm_UI, // 638 + arm640,arm_UI,arm642,arm_UI,arm644,arm_UI,arm646,arm_UI, // 640 + arm640,arm_UI,arm642,arm_UI,arm644,arm_UI,arm646,arm_UI, // 648 + arm650,arm_UI,arm652,arm_UI,arm654,arm_UI,arm656,arm_UI, // 650 + arm650,arm_UI,arm652,arm_UI,arm654,arm_UI,arm656,arm_UI, // 658 + arm640,arm_UI,arm642,arm_UI,arm644,arm_UI,arm646,arm_UI, // 660 + arm640,arm_UI,arm642,arm_UI,arm644,arm_UI,arm646,arm_UI, // 668 + arm650,arm_UI,arm652,arm_UI,arm654,arm_UI,arm656,arm_UI, // 670 + arm650,arm_UI,arm652,arm_UI,arm654,arm_UI,arm656,arm_UI, // 678 + arm680,arm_UI,arm682,arm_UI,arm684,arm_UI,arm686,arm_UI, // 680 + arm680,arm_UI,arm682,arm_UI,arm684,arm_UI,arm686,arm_UI, // 688 + arm690,arm_UI,arm692,arm_UI,arm694,arm_UI,arm696,arm_UI, // 690 + arm690,arm_UI,arm692,arm_UI,arm694,arm_UI,arm696,arm_UI, // 698 + arm680,arm_UI,arm682,arm_UI,arm684,arm_UI,arm686,arm_UI, // 6A0 + arm680,arm_UI,arm682,arm_UI,arm684,arm_UI,arm686,arm_UI, // 6A8 + arm690,arm_UI,arm692,arm_UI,arm694,arm_UI,arm696,arm_UI, // 6B0 + arm690,arm_UI,arm692,arm_UI,arm694,arm_UI,arm696,arm_UI, // 6B8 + arm6C0,arm_UI,arm6C2,arm_UI,arm6C4,arm_UI,arm6C6,arm_UI, // 6C0 + arm6C0,arm_UI,arm6C2,arm_UI,arm6C4,arm_UI,arm6C6,arm_UI, // 6C8 + arm6D0,arm_UI,arm6D2,arm_UI,arm6D4,arm_UI,arm6D6,arm_UI, // 6D0 + arm6D0,arm_UI,arm6D2,arm_UI,arm6D4,arm_UI,arm6D6,arm_UI, // 6D8 + arm6C0,arm_UI,arm6C2,arm_UI,arm6C4,arm_UI,arm6C6,arm_UI, // 6E0 + arm6C0,arm_UI,arm6C2,arm_UI,arm6C4,arm_UI,arm6C6,arm_UI, // 6E8 + arm6D0,arm_UI,arm6D2,arm_UI,arm6D4,arm_UI,arm6D6,arm_UI, // 6F0 + arm6D0,arm_UI,arm6D2,arm_UI,arm6D4,arm_UI,arm6D6,arm_UI, // 6F8 + + arm700,arm_UI,arm702,arm_UI,arm704,arm_UI,arm706,arm_UI, // 700 + arm700,arm_UI,arm702,arm_UI,arm704,arm_UI,arm706,arm_UI, // 708 + arm710,arm_UI,arm712,arm_UI,arm714,arm_UI,arm716,arm_UI, // 710 + arm710,arm_UI,arm712,arm_UI,arm714,arm_UI,arm716,arm_UI, // 718 + arm720,arm_UI,arm722,arm_UI,arm724,arm_UI,arm726,arm_UI, // 720 + arm720,arm_UI,arm722,arm_UI,arm724,arm_UI,arm726,arm_UI, // 728 + arm730,arm_UI,arm732,arm_UI,arm734,arm_UI,arm736,arm_UI, // 730 + arm730,arm_UI,arm732,arm_UI,arm734,arm_UI,arm736,arm_UI, // 738 + arm740,arm_UI,arm742,arm_UI,arm744,arm_UI,arm746,arm_UI, // 740 + arm740,arm_UI,arm742,arm_UI,arm744,arm_UI,arm746,arm_UI, // 748 + arm750,arm_UI,arm752,arm_UI,arm754,arm_UI,arm756,arm_UI, // 750 + arm750,arm_UI,arm752,arm_UI,arm754,arm_UI,arm756,arm_UI, // 758 + arm760,arm_UI,arm762,arm_UI,arm764,arm_UI,arm766,arm_UI, // 760 + arm760,arm_UI,arm762,arm_UI,arm764,arm_UI,arm766,arm_UI, // 768 + arm770,arm_UI,arm772,arm_UI,arm774,arm_UI,arm776,arm_UI, // 770 + arm770,arm_UI,arm772,arm_UI,arm774,arm_UI,arm776,arm_UI, // 778 + arm780,arm_UI,arm782,arm_UI,arm784,arm_UI,arm786,arm_UI, // 780 + arm780,arm_UI,arm782,arm_UI,arm784,arm_UI,arm786,arm_UI, // 788 + arm790,arm_UI,arm792,arm_UI,arm794,arm_UI,arm796,arm_UI, // 790 + arm790,arm_UI,arm792,arm_UI,arm794,arm_UI,arm796,arm_UI, // 798 + arm7A0,arm_UI,arm7A2,arm_UI,arm7A4,arm_UI,arm7A6,arm_UI, // 7A0 + arm7A0,arm_UI,arm7A2,arm_UI,arm7A4,arm_UI,arm7A6,arm_UI, // 7A8 + arm7B0,arm_UI,arm7B2,arm_UI,arm7B4,arm_UI,arm7B6,arm_UI, // 7B0 + arm7B0,arm_UI,arm7B2,arm_UI,arm7B4,arm_UI,arm7B6,arm_UI, // 7B8 + arm7C0,arm_UI,arm7C2,arm_UI,arm7C4,arm_UI,arm7C6,arm_UI, // 7C0 + arm7C0,arm_UI,arm7C2,arm_UI,arm7C4,arm_UI,arm7C6,arm_UI, // 7C8 + arm7D0,arm_UI,arm7D2,arm_UI,arm7D4,arm_UI,arm7D6,arm_UI, // 7D0 + arm7D0,arm_UI,arm7D2,arm_UI,arm7D4,arm_UI,arm7D6,arm_UI, // 7D8 + arm7E0,arm_UI,arm7E2,arm_UI,arm7E4,arm_UI,arm7E6,arm_UI, // 7E0 + arm7E0,arm_UI,arm7E2,arm_UI,arm7E4,arm_UI,arm7E6,arm_UI, // 7E8 + arm7F0,arm_UI,arm7F2,arm_UI,arm7F4,arm_UI,arm7F6,arm_UI, // 7F0 + arm7F0,arm_UI,arm7F2,arm_UI,arm7F4,arm_UI,arm7F6,arm_BP, // 7F8 + + REP16(arm800),REP16(arm810),REP16(arm820),REP16(arm830), // 800 + REP16(arm840),REP16(arm850),REP16(arm860),REP16(arm870), // 840 + REP16(arm880),REP16(arm890),REP16(arm8A0),REP16(arm8B0), // 880 + REP16(arm8C0),REP16(arm8D0),REP16(arm8E0),REP16(arm8F0), // 8C0 + REP16(arm900),REP16(arm910),REP16(arm920),REP16(arm930), // 900 + REP16(arm940),REP16(arm950),REP16(arm960),REP16(arm970), // 940 + REP16(arm980),REP16(arm990),REP16(arm9A0),REP16(arm9B0), // 980 + REP16(arm9C0),REP16(arm9D0),REP16(arm9E0),REP16(arm9F0), // 9C0 + + REP256(armA00), // A00 + REP256(armB00), // B00 + REP256(arm_UI), // C00 + REP256(arm_UI), // D00 + + arm_UI,armE01,arm_UI,armE01,arm_UI,armE01,arm_UI,armE01, // E00 + arm_UI,armE01,arm_UI,armE01,arm_UI,armE01,arm_UI,armE01, // E08 + arm_UI,armE01,arm_UI,armE01,arm_UI,armE01,arm_UI,armE01, // E10 + arm_UI,armE01,arm_UI,armE01,arm_UI,armE01,arm_UI,armE01, // E18 + REP16(arm_UI), // E20 + REP16(arm_UI), // E30 + REP16(arm_UI),REP16(arm_UI),REP16(arm_UI),REP16(arm_UI), // E40 + REP16(arm_UI),REP16(arm_UI),REP16(arm_UI),REP16(arm_UI), // E80 + REP16(arm_UI),REP16(arm_UI),REP16(arm_UI),REP16(arm_UI), // EC0 + + REP256(armF00), // F00 +}; + +// Wrapper routine (execution loop) /////////////////////////////////////// + +#if 0 +#include +static void tester(void) { + static int ran=0;if(ran)return;ran=1; + FILE*f=fopen("p:\\timing.txt","w");if(!f)return; + for (int op=/*0*/9; op> 28; + bool cond_res = true; + if (UNLIKELY(cond != 0x0E)) { // most opcodes are AL (always) + switch(cond) { + case 0x00: // EQ + cond_res = Z_FLAG; + break; + case 0x01: // NE + cond_res = !Z_FLAG; + break; + case 0x02: // CS + cond_res = C_FLAG; + break; + case 0x03: // CC + cond_res = !C_FLAG; + break; + case 0x04: // MI + cond_res = N_FLAG; + break; + case 0x05: // PL + cond_res = !N_FLAG; + break; + case 0x06: // VS + cond_res = V_FLAG; + break; + case 0x07: // VC + cond_res = !V_FLAG; + break; + case 0x08: // HI + cond_res = C_FLAG && !Z_FLAG; + break; + case 0x09: // LS + cond_res = !C_FLAG || Z_FLAG; + break; + case 0x0A: // GE + cond_res = N_FLAG == V_FLAG; + break; + case 0x0B: // LT + cond_res = N_FLAG != V_FLAG; + break; + case 0x0C: // GT + cond_res = !Z_FLAG &&(N_FLAG == V_FLAG); + break; + case 0x0D: // LE + cond_res = Z_FLAG || (N_FLAG != V_FLAG); + break; + case 0x0E: // AL (impossible, checked above) + cond_res = true; + break; + case 0x0F: + default: + // ??? + cond_res = false; + break; + } + } + + if (cond_res) + (*armInsnTable[((opcode>>16)&0xFF0) | ((opcode>>4)&0x0F)])(opcode); +#ifdef INSN_COUNTER + count(opcode, cond_res); +#endif + if (clockTicks < 0) + return 0; + if (clockTicks == 0) + clockTicks = 1 + codeTicksAccessSeq32(oldArmNextPC); + cpuTotalTicks += clockTicks; + + } while (cpuTotalTicks +#include +#include +#include +#include + +#include "GBA.h" +#include "GBAcpu.h" +#include "GBAinline.h" +#include "Globals.h" +#include "EEprom.h" +#include "Flash.h" +#include "Sound.h" +#include "Sram.h" +#include "bios.h" +#include "Cheats.h" +#include "../NLS.h" +#include "elf.h" +#include "../Util.h" +#include "../System.h" +#include "agbprint.h" +#ifdef PROFILING +#include "prof/prof.h" +#endif + +#ifdef _MSC_VER +#define snprintf _snprintf +#endif + +/////////////////////////////////////////////////////////////////////////// + +static int clockTicks; + +static INSN_REGPARM void thumbUnknownInsn(u32 opcode) +{ +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_UNDEFINED) + log("Undefined THUMB instruction %04x at %08x\n", opcode, armNextPC-2); +#endif + CPUUndefinedException(); +} + +#ifdef BKPT_SUPPORT +static INSN_REGPARM void thumbBreakpoint(u32 opcode) +{ + reg[15].I -= 2; + armNextPC -= 2; + dbgSignal(5, opcode & 255); + clockTicks = -1; +} +#endif + +// Common macros ////////////////////////////////////////////////////////// + +#ifdef BKPT_SUPPORT +# define THUMB_CONSOLE_OUTPUT(a,b) do { \ + if ((opcode == 0x4000) && (reg[0].I == 0xC0DED00D)) { \ + dbgOutput((a), (b)); \ + } \ +} while (0) +# define UPDATE_OLDREG do { \ + if (debugger_last) { \ + snprintf(oldbuffer, sizeof(oldbuffer), "%08X", \ + armState ? reg[15].I - 4 : reg[15].I - 2); \ + int i; \ + for (i = 0; i < 18; i++) { \ + oldreg[i] = reg[i].I; \ + } \ + } \ +} while (0) +#else +# define THUMB_CONSOLE_OUTPUT(a,b) +# define UPDATE_OLDREG +#endif + +#define NEG(i) ((i) >> 31) +#define POS(i) ((~(i)) >> 31) + +#ifndef C_CORE +#ifdef __GNUC__ +#ifdef __POWERPC__ + #define ADD_RD_RS_RN(N) \ + { \ + register int Flags; \ + register int Result; \ + asm volatile("addco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[source].I), \ + "r" (reg[N].I) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define ADD_RD_RS_O3(N) \ + { \ + register int Flags; \ + register int Result; \ + asm volatile("addco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[source].I), \ + "r" (N) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define ADD_RD_RS_O3_0 ADD_RD_RS_O3 + #define ADD_RN_O8(d) \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("addco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[(d)].I), \ + "r" (opcode & 255) \ + ); \ + reg[(d)].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define CMN_RD_RS \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("addco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[dest].I), \ + "r" (value) \ + ); \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define ADC_RD_RS \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("mtspr 1, %4\n" \ /* reg 1 is xer */ + "addeo. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[dest].I), \ + "r" (value), \ + "r" (C_FLAG << 29) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define SUB_RD_RS_RN(N) \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("subco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[source].I), \ + "r" (reg[N].I) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define SUB_RD_RS_O3(N) \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("subco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[source].I), \ + "r" (N) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define SUB_RD_RS_O3_0 SUB_RD_RS_O3 + #define SUB_RN_O8(d) \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("subco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[(d)].I), \ + "r" (opcode & 255) \ + ); \ + reg[(d)].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define CMP_RN_O8(d) \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("subco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[(d)].I), \ + "r" (opcode & 255) \ + ); \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define SBC_RD_RS \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("mtspr 1, %4\n" \ /* reg 1 is xer */ + "subfeo. %0, %3, %2\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[dest].I), \ + "r" (value), \ + "r" (C_FLAG << 29) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define NEG_RD_RS \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("subfco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[source].I), \ + "r" (0) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define CMP_RD_RS \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("subco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[dest].I), \ + "r" (value) \ + ); \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } +#else + #define EMIT1(op,arg) #op" "arg"; " + #define EMIT2(op,src,dest) #op" "src", "dest"; " + #define KONST(val) "$"#val + #define ASMVAR(cvar) ASMVAR2 (__USER_LABEL_PREFIX__, cvar) + #define ASMVAR2(prefix,cvar) STRING (prefix) cvar + #define STRING(x) #x + #define VAR(var) ASMVAR(#var) + #define REGREF1(index) ASMVAR("reg("index")") + #define REGREF2(index,scale) ASMVAR("reg(,"index","#scale")") + #define eax "%%eax" + #define ecx "%%ecx" + #define edx "%%edx" + #define ADD_RN_O8(d) \ + asm ("andl $0xFF, %%eax;"\ + "addl %%eax, %0;"\ + EMIT1(setsb, VAR(N_FLAG)) \ + EMIT1(setzb, VAR(Z_FLAG)) \ + EMIT1(setcb, VAR(C_FLAG)) \ + EMIT1(setob, VAR(V_FLAG)) \ + : "=m" (reg[(d)].I)); + #define CMN_RD_RS \ + asm ("add %0, %1;"\ + EMIT1(setsb, VAR(N_FLAG)) \ + EMIT1(setzb, VAR(Z_FLAG)) \ + EMIT1(setcb, VAR(C_FLAG)) \ + EMIT1(setob, VAR(V_FLAG)) \ + : \ + : "r" (value), "r" (reg[dest].I):"1"); + #define ADC_RD_RS \ + asm (EMIT2(bt,KONST(0),VAR(C_FLAG)) \ + "adc %1, %%ebx;"\ + EMIT1(setsb, VAR(N_FLAG)) \ + EMIT1(setzb, VAR(Z_FLAG)) \ + EMIT1(setcb, VAR(C_FLAG)) \ + EMIT1(setob, VAR(V_FLAG)) \ + : "=b" (reg[dest].I)\ + : "r" (value), "b" (reg[dest].I)); + #define SUB_RN_O8(d) \ + asm ("andl $0xFF, %%eax;"\ + "subl %%eax, %0;"\ + EMIT1(setsb, VAR(N_FLAG)) \ + EMIT1(setzb, VAR(Z_FLAG)) \ + EMIT1(setncb, VAR(C_FLAG)) \ + EMIT1(setob, VAR(V_FLAG)) \ + : "=m" (reg[(d)].I)); + #define MOV_RN_O8(d) \ + asm ("andl $0xFF, %%eax;"\ + EMIT2(movb,KONST(0),VAR(N_FLAG)) \ + "movl %%eax, %0;"\ + EMIT1(setzb, VAR(Z_FLAG)) \ + : "=m" (reg[(d)].I)); + #define CMP_RN_O8(d) \ + asm ("andl $0xFF, %%eax;"\ + "cmpl %%eax, %0;"\ + EMIT1(setsb, VAR(N_FLAG)) \ + EMIT1(setzb, VAR(Z_FLAG)) \ + EMIT1(setncb, VAR(C_FLAG)) \ + EMIT1(setob, VAR(V_FLAG)) \ + : \ + : "m" (reg[(d)].I)); + #define SBC_RD_RS \ + asm volatile (EMIT2(bt,KONST(0),VAR(C_FLAG)) \ + "cmc;"\ + "sbb %1, %%ebx;"\ + EMIT1(setsb, VAR(N_FLAG)) \ + EMIT1(setzb, VAR(Z_FLAG)) \ + EMIT1(setncb, VAR(C_FLAG)) \ + EMIT1(setob, VAR(V_FLAG)) \ + : "=b" (reg[dest].I)\ + : "r" (value), "b" (reg[dest].I) : "cc", "memory"); + #define LSL_RD_RS \ + asm ("shl %%cl, %%eax;"\ + EMIT1(setcb, VAR(C_FLAG)) \ + : "=a" (value)\ + : "a" (reg[dest].I), "c" (value)); + #define LSR_RD_RS \ + asm ("shr %%cl, %%eax;"\ + EMIT1(setcb, VAR(C_FLAG)) \ + : "=a" (value)\ + : "a" (reg[dest].I), "c" (value)); + #define ASR_RD_RS \ + asm ("sar %%cl, %%eax;"\ + EMIT1(setcb, VAR(C_FLAG)) \ + : "=a" (value)\ + : "a" (reg[dest].I), "c" (value)); + #define ROR_RD_RS \ + asm ("ror %%cl, %%eax;"\ + EMIT1(setcb, VAR(C_FLAG)) \ + : "=a" (value)\ + : "a" (reg[dest].I), "c" (value)); + #define NEG_RD_RS \ + asm ("neg %%ebx;"\ + EMIT1(setsb, VAR(N_FLAG)) \ + EMIT1(setzb, VAR(Z_FLAG)) \ + EMIT1(setncb, VAR(C_FLAG)) \ + EMIT1(setob, VAR(V_FLAG)) \ + : "=b" (reg[dest].I)\ + : "b" (reg[source].I)); + #define CMP_RD_RS \ + asm ("sub %0, %1;"\ + EMIT1(setsb, VAR(N_FLAG)) \ + EMIT1(setzb, VAR(Z_FLAG)) \ + EMIT1(setncb, VAR(C_FLAG)) \ + EMIT1(setob, VAR(V_FLAG)) \ + : \ + : "r" (value), "r" (reg[dest].I):"1"); + #define IMM5_INSN(OP,N) \ + asm("movl %%eax,%%ecx;" \ + "shrl $1,%%eax;" \ + "andl $7,%%ecx;" \ + "andl $0x1C,%%eax;" \ + EMIT2(movl, REGREF1(eax), edx) \ + OP \ + EMIT1(setsb, VAR(N_FLAG)) \ + EMIT1(setzb, VAR(Z_FLAG)) \ + EMIT2(movl, edx, REGREF2(ecx,4)) \ + : : "i" (N)) + #define IMM5_INSN_0(OP) \ + asm("movl %%eax,%%ecx;" \ + "shrl $1,%%eax;" \ + "andl $7,%%ecx;" \ + "andl $0x1C,%%eax;" \ + EMIT2(movl, REGREF1(eax), edx) \ + OP \ + EMIT1(setsb, VAR(N_FLAG)) \ + EMIT1(setzb, VAR(Z_FLAG)) \ + EMIT2(movl, edx, REGREF2(ecx,4)) \ + : : ) + #define IMM5_LSL \ + "shll %0,%%edx;"\ + EMIT1(setcb, VAR(C_FLAG)) + #define IMM5_LSL_0 \ + "testl %%edx,%%edx;" + #define IMM5_LSR \ + "shrl %0,%%edx;"\ + EMIT1(setcb, VAR(C_FLAG)) + #define IMM5_LSR_0 \ + "testl %%edx,%%edx;"\ + EMIT1(setsb, VAR(C_FLAG)) \ + "xorl %%edx,%%edx;" + #define IMM5_ASR \ + "sarl %0,%%edx;"\ + EMIT1(setcb, VAR(C_FLAG)) + #define IMM5_ASR_0 \ + "sarl $31,%%edx;"\ + EMIT1(setsb, VAR(C_FLAG)) + #define THREEARG_INSN(OP,N) \ + asm("movl %%eax,%%edx;" \ + "shrl $1,%%edx;" \ + "andl $0x1C,%%edx;" \ + "andl $7,%%eax;" \ + EMIT2(movl, REGREF1(edx), ecx) \ + OP(N) \ + EMIT1(setsb, VAR(N_FLAG)) \ + EMIT1(setzb, VAR(Z_FLAG)) \ + EMIT2(movl, ecx, REGREF2(eax,4)) \ + : : ) + #define ADD_RD_RS_RN(N) \ + EMIT2(add,VAR(reg)"+"#N"*4",ecx) \ + EMIT1(setcb, VAR(C_FLAG)) \ + EMIT1(setob, VAR(V_FLAG)) + #define ADD_RD_RS_O3(N) \ + "add $"#N",%%ecx;" \ + EMIT1(setcb, VAR(C_FLAG)) \ + EMIT1(setob, VAR(V_FLAG)) + #define ADD_RD_RS_O3_0(N) \ + EMIT2(movb,KONST(0),VAR(C_FLAG)) \ + "add $0,%%ecx;" \ + EMIT2(movb,KONST(0),VAR(V_FLAG)) + #define SUB_RD_RS_RN(N) \ + EMIT2(sub,VAR(reg)"+"#N"*4",ecx) \ + EMIT1(setncb, VAR(C_FLAG)) \ + EMIT1(setob, VAR(V_FLAG)) + #define SUB_RD_RS_O3(N) \ + "sub $"#N",%%ecx;" \ + EMIT1(setncb, VAR(C_FLAG)) \ + EMIT1(setob, VAR(V_FLAG)) + #define SUB_RD_RS_O3_0(N) \ + EMIT2(movb,KONST(1),VAR(C_FLAG)) \ + "sub $0,%%ecx;" \ + EMIT2(movb,KONST(0),VAR(V_FLAG)) +#endif +#else // !__GNUC__ + #define ADD_RD_RS_RN(N) \ + {\ + __asm mov eax, source\ + __asm mov ebx, dword ptr [OFFSET reg+4*eax]\ + __asm add ebx, dword ptr [OFFSET reg+4*N]\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg+4*eax], ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } + #define ADD_RD_RS_O3(N) \ + {\ + __asm mov eax, source\ + __asm mov ebx, dword ptr [OFFSET reg+4*eax]\ + __asm add ebx, N\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg+4*eax], ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } + #define ADD_RD_RS_O3_0 \ + {\ + __asm mov eax, source\ + __asm mov ebx, dword ptr [OFFSET reg+4*eax]\ + __asm add ebx, 0\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg+4*eax], ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm mov byte ptr C_FLAG, 0\ + __asm mov byte ptr V_FLAG, 0\ + } + #define ADD_RN_O8(d) \ + {\ + __asm mov ebx, opcode\ + __asm and ebx, 255\ + __asm add dword ptr [OFFSET reg+4*(d)], ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } + #define CMN_RD_RS \ + {\ + __asm mov eax, dest\ + __asm mov ebx, dword ptr [OFFSET reg+4*eax]\ + __asm add ebx, value\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } + #define ADC_RD_RS \ + {\ + __asm mov ebx, dest\ + __asm mov ebx, dword ptr [OFFSET reg+4*ebx]\ + __asm bt word ptr C_FLAG, 0\ + __asm adc ebx, value\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg+4*eax], ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } + #define SUB_RD_RS_RN(N) \ + {\ + __asm mov eax, source\ + __asm mov ebx, dword ptr [OFFSET reg+4*eax]\ + __asm sub ebx, dword ptr [OFFSET reg+4*N]\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg+4*eax], ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setnc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } + #define SUB_RD_RS_O3(N) \ + {\ + __asm mov eax, source\ + __asm mov ebx, dword ptr [OFFSET reg+4*eax]\ + __asm sub ebx, N\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg+4*eax], ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setnc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } + #define SUB_RD_RS_O3_0 \ + {\ + __asm mov eax, source\ + __asm mov ebx, dword ptr [OFFSET reg+4*eax]\ + __asm sub ebx, 0\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg+4*eax], ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm mov byte ptr C_FLAG, 1\ + __asm mov byte ptr V_FLAG, 0\ + } + #define SUB_RN_O8(d) \ + {\ + __asm mov ebx, opcode\ + __asm and ebx, 255\ + __asm sub dword ptr [OFFSET reg + 4*(d)], ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setnc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } + #define MOV_RN_O8(d) \ + {\ + __asm mov eax, opcode\ + __asm and eax, 255\ + __asm mov dword ptr [OFFSET reg+4*(d)], eax\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + } + #define CMP_RN_O8(d) \ + {\ + __asm mov eax, dword ptr [OFFSET reg+4*(d)]\ + __asm mov ebx, opcode\ + __asm and ebx, 255\ + __asm sub eax, ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setnc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } + #define SBC_RD_RS \ + {\ + __asm mov ebx, dest\ + __asm mov ebx, dword ptr [OFFSET reg + 4*ebx]\ + __asm mov eax, value\ + __asm bt word ptr C_FLAG, 0\ + __asm cmc\ + __asm sbb ebx, eax\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg + 4*eax], ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setnc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } + #define LSL_RD_RM_I5 \ + {\ + __asm mov eax, source\ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax]\ + __asm mov cl, byte ptr shift\ + __asm shl eax, cl\ + __asm mov value, eax\ + __asm setc byte ptr C_FLAG\ + } + #define LSL_RD_RS \ + {\ + __asm mov eax, dest\ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax]\ + __asm mov cl, byte ptr value\ + __asm shl eax, cl\ + __asm mov value, eax\ + __asm setc byte ptr C_FLAG\ + } + #define LSR_RD_RM_I5 \ + {\ + __asm mov eax, source\ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax]\ + __asm mov cl, byte ptr shift\ + __asm shr eax, cl\ + __asm mov value, eax\ + __asm setc byte ptr C_FLAG\ + } + #define LSR_RD_RS \ + {\ + __asm mov eax, dest\ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax]\ + __asm mov cl, byte ptr value\ + __asm shr eax, cl\ + __asm mov value, eax\ + __asm setc byte ptr C_FLAG\ + } + #define ASR_RD_RM_I5 \ + {\ + __asm mov eax, source\ + __asm mov eax, dword ptr [OFFSET reg + 4*eax]\ + __asm mov cl, byte ptr shift\ + __asm sar eax, cl\ + __asm mov value, eax\ + __asm setc byte ptr C_FLAG\ + } + #define ASR_RD_RS \ + {\ + __asm mov eax, dest\ + __asm mov eax, dword ptr [OFFSET reg + 4*eax]\ + __asm mov cl, byte ptr value\ + __asm sar eax, cl\ + __asm mov value, eax\ + __asm setc byte ptr C_FLAG\ + } + #define ROR_RD_RS \ + {\ + __asm mov eax, dest\ + __asm mov eax, dword ptr [OFFSET reg + 4*eax]\ + __asm mov cl, byte ptr value\ + __asm ror eax, cl\ + __asm mov value, eax\ + __asm setc byte ptr C_FLAG\ + } + #define NEG_RD_RS \ + {\ + __asm mov ebx, source\ + __asm mov ebx, dword ptr [OFFSET reg+4*ebx]\ + __asm neg ebx\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg+4*eax],ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setnc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } + #define CMP_RD_RS \ + {\ + __asm mov eax, dest\ + __asm mov ebx, dword ptr [OFFSET reg+4*eax]\ + __asm sub ebx, value\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setnc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } +#endif +#endif + +// C core +#ifndef ADDCARRY + #define ADDCARRY(a, b, c) \ + C_FLAG = ((NEG(a) & NEG(b)) |\ + (NEG(a) & POS(c)) |\ + (NEG(b) & POS(c))) ? true : false; +#endif +#ifndef ADDOVERFLOW + #define ADDOVERFLOW(a, b, c) \ + V_FLAG = ((NEG(a) & NEG(b) & POS(c)) |\ + (POS(a) & POS(b) & NEG(c))) ? true : false; +#endif +#ifndef SUBCARRY + #define SUBCARRY(a, b, c) \ + C_FLAG = ((NEG(a) & POS(b)) |\ + (NEG(a) & POS(c)) |\ + (POS(b) & POS(c))) ? true : false; +#endif +#ifndef SUBOVERFLOW + #define SUBOVERFLOW(a, b, c)\ + V_FLAG = ((NEG(a) & POS(b) & POS(c)) |\ + (POS(a) & NEG(b) & NEG(c))) ? true : false; +#endif +#ifndef ADD_RD_RS_RN + #define ADD_RD_RS_RN(N) \ + {\ + u32 lhs = reg[source].I;\ + u32 rhs = reg[N].I;\ + u32 res = lhs + rhs;\ + reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + ADDCARRY(lhs, rhs, res);\ + ADDOVERFLOW(lhs, rhs, res);\ + } +#endif +#ifndef ADD_RD_RS_O3 + #define ADD_RD_RS_O3(N) \ + {\ + u32 lhs = reg[source].I;\ + u32 rhs = N;\ + u32 res = lhs + rhs;\ + reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + ADDCARRY(lhs, rhs, res);\ + ADDOVERFLOW(lhs, rhs, res);\ + } +#endif +#ifndef ADD_RD_RS_O3_0 +# define ADD_RD_RS_O3_0 ADD_RD_RS_O3 +#endif +#ifndef ADD_RN_O8 + #define ADD_RN_O8(d) \ + {\ + u32 lhs = reg[(d)].I;\ + u32 rhs = (opcode & 255);\ + u32 res = lhs + rhs;\ + reg[(d)].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + ADDCARRY(lhs, rhs, res);\ + ADDOVERFLOW(lhs, rhs, res);\ + } +#endif +#ifndef CMN_RD_RS + #define CMN_RD_RS \ + {\ + u32 lhs = reg[dest].I;\ + u32 rhs = value;\ + u32 res = lhs + rhs;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + ADDCARRY(lhs, rhs, res);\ + ADDOVERFLOW(lhs, rhs, res);\ + } +#endif +#ifndef ADC_RD_RS + #define ADC_RD_RS \ + {\ + u32 lhs = reg[dest].I;\ + u32 rhs = value;\ + u32 res = lhs + rhs + (u32)C_FLAG;\ + reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + ADDCARRY(lhs, rhs, res);\ + ADDOVERFLOW(lhs, rhs, res);\ + } +#endif +#ifndef SUB_RD_RS_RN + #define SUB_RD_RS_RN(N) \ + {\ + u32 lhs = reg[source].I;\ + u32 rhs = reg[N].I;\ + u32 res = lhs - rhs;\ + reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(lhs, rhs, res);\ + SUBOVERFLOW(lhs, rhs, res);\ + } +#endif +#ifndef SUB_RD_RS_O3 + #define SUB_RD_RS_O3(N) \ + {\ + u32 lhs = reg[source].I;\ + u32 rhs = N;\ + u32 res = lhs - rhs;\ + reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(lhs, rhs, res);\ + SUBOVERFLOW(lhs, rhs, res);\ + } +#endif +#ifndef SUB_RD_RS_O3_0 +# define SUB_RD_RS_O3_0 SUB_RD_RS_O3 +#endif +#ifndef SUB_RN_O8 + #define SUB_RN_O8(d) \ + {\ + u32 lhs = reg[(d)].I;\ + u32 rhs = (opcode & 255);\ + u32 res = lhs - rhs;\ + reg[(d)].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(lhs, rhs, res);\ + SUBOVERFLOW(lhs, rhs, res);\ + } +#endif +#ifndef MOV_RN_O8 + #define MOV_RN_O8(d) \ + {\ + reg[d].I = opcode & 255;\ + N_FLAG = false;\ + Z_FLAG = (reg[d].I ? false : true);\ + } +#endif +#ifndef CMP_RN_O8 + #define CMP_RN_O8(d) \ + {\ + u32 lhs = reg[(d)].I;\ + u32 rhs = (opcode & 255);\ + u32 res = lhs - rhs;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(lhs, rhs, res);\ + SUBOVERFLOW(lhs, rhs, res);\ + } +#endif +#ifndef SBC_RD_RS + #define SBC_RD_RS \ + {\ + u32 lhs = reg[dest].I;\ + u32 rhs = value;\ + u32 res = lhs - rhs - !((u32)C_FLAG);\ + reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(lhs, rhs, res);\ + SUBOVERFLOW(lhs, rhs, res);\ + } +#endif +#ifndef LSL_RD_RM_I5 + #define LSL_RD_RM_I5 \ + {\ + C_FLAG = (reg[source].I >> (32 - shift)) & 1 ? true : false;\ + value = reg[source].I << shift;\ + } +#endif +#ifndef LSL_RD_RS + #define LSL_RD_RS \ + {\ + C_FLAG = (reg[dest].I >> (32 - value)) & 1 ? true : false;\ + value = reg[dest].I << value;\ + } +#endif +#ifndef LSR_RD_RM_I5 + #define LSR_RD_RM_I5 \ + {\ + C_FLAG = (reg[source].I >> (shift - 1)) & 1 ? true : false;\ + value = reg[source].I >> shift;\ + } +#endif +#ifndef LSR_RD_RS + #define LSR_RD_RS \ + {\ + C_FLAG = (reg[dest].I >> (value - 1)) & 1 ? true : false;\ + value = reg[dest].I >> value;\ + } +#endif +#ifndef ASR_RD_RM_I5 + #define ASR_RD_RM_I5 \ + {\ + C_FLAG = ((s32)reg[source].I >> (int)(shift - 1)) & 1 ? true : false;\ + value = (s32)reg[source].I >> (int)shift;\ + } +#endif +#ifndef ASR_RD_RS + #define ASR_RD_RS \ + {\ + C_FLAG = ((s32)reg[dest].I >> (int)(value - 1)) & 1 ? true : false;\ + value = (s32)reg[dest].I >> (int)value;\ + } +#endif +#ifndef ROR_RD_RS + #define ROR_RD_RS \ + {\ + C_FLAG = (reg[dest].I >> (value - 1)) & 1 ? true : false;\ + value = ((reg[dest].I << (32 - value)) |\ + (reg[dest].I >> value));\ + } +#endif +#ifndef NEG_RD_RS + #define NEG_RD_RS \ + {\ + u32 lhs = reg[source].I;\ + u32 rhs = 0;\ + u32 res = rhs - lhs;\ + reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(rhs, lhs, res);\ + SUBOVERFLOW(rhs, lhs, res);\ + } +#endif +#ifndef CMP_RD_RS + #define CMP_RD_RS \ + {\ + u32 lhs = reg[dest].I;\ + u32 rhs = value;\ + u32 res = lhs - rhs;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(lhs, rhs, res);\ + SUBOVERFLOW(lhs, rhs, res);\ + } +#endif +#ifndef IMM5_INSN + #define IMM5_INSN(OP,N) \ + int dest = opcode & 0x07;\ + int source = (opcode >> 3) & 0x07;\ + u32 value;\ + OP(N);\ + reg[dest].I = value;\ + N_FLAG = (value & 0x80000000 ? true : false);\ + Z_FLAG = (value ? false : true); + #define IMM5_INSN_0(OP) \ + int dest = opcode & 0x07;\ + int source = (opcode >> 3) & 0x07;\ + u32 value;\ + OP;\ + reg[dest].I = value;\ + N_FLAG = (value & 0x80000000 ? true : false);\ + Z_FLAG = (value ? false : true); + #define IMM5_LSL(N) \ + int shift = N;\ + LSL_RD_RM_I5; + #define IMM5_LSL_0 \ + value = reg[source].I; + #define IMM5_LSR(N) \ + int shift = N;\ + LSR_RD_RM_I5; + #define IMM5_LSR_0 \ + C_FLAG = reg[source].I & 0x80000000 ? true : false;\ + value = 0; + #define IMM5_ASR(N) \ + int shift = N;\ + ASR_RD_RM_I5; + #define IMM5_ASR_0 \ + if(reg[source].I & 0x80000000) {\ + value = 0xFFFFFFFF;\ + C_FLAG = true;\ + } else {\ + value = 0;\ + C_FLAG = false;\ + } +#endif +#ifndef THREEARG_INSN + #define THREEARG_INSN(OP,N) \ + int dest = opcode & 0x07; \ + int source = (opcode >> 3) & 0x07; \ + OP(N); +#endif + +// Shift instructions ///////////////////////////////////////////////////// + +#define DEFINE_IMM5_INSN(OP,BASE) \ + static INSN_REGPARM void thumb##BASE##_00(u32 opcode) { IMM5_INSN_0(OP##_0); } \ + static INSN_REGPARM void thumb##BASE##_01(u32 opcode) { IMM5_INSN(OP, 1); } \ + static INSN_REGPARM void thumb##BASE##_02(u32 opcode) { IMM5_INSN(OP, 2); } \ + static INSN_REGPARM void thumb##BASE##_03(u32 opcode) { IMM5_INSN(OP, 3); } \ + static INSN_REGPARM void thumb##BASE##_04(u32 opcode) { IMM5_INSN(OP, 4); } \ + static INSN_REGPARM void thumb##BASE##_05(u32 opcode) { IMM5_INSN(OP, 5); } \ + static INSN_REGPARM void thumb##BASE##_06(u32 opcode) { IMM5_INSN(OP, 6); } \ + static INSN_REGPARM void thumb##BASE##_07(u32 opcode) { IMM5_INSN(OP, 7); } \ + static INSN_REGPARM void thumb##BASE##_08(u32 opcode) { IMM5_INSN(OP, 8); } \ + static INSN_REGPARM void thumb##BASE##_09(u32 opcode) { IMM5_INSN(OP, 9); } \ + static INSN_REGPARM void thumb##BASE##_0A(u32 opcode) { IMM5_INSN(OP,10); } \ + static INSN_REGPARM void thumb##BASE##_0B(u32 opcode) { IMM5_INSN(OP,11); } \ + static INSN_REGPARM void thumb##BASE##_0C(u32 opcode) { IMM5_INSN(OP,12); } \ + static INSN_REGPARM void thumb##BASE##_0D(u32 opcode) { IMM5_INSN(OP,13); } \ + static INSN_REGPARM void thumb##BASE##_0E(u32 opcode) { IMM5_INSN(OP,14); } \ + static INSN_REGPARM void thumb##BASE##_0F(u32 opcode) { IMM5_INSN(OP,15); } \ + static INSN_REGPARM void thumb##BASE##_10(u32 opcode) { IMM5_INSN(OP,16); } \ + static INSN_REGPARM void thumb##BASE##_11(u32 opcode) { IMM5_INSN(OP,17); } \ + static INSN_REGPARM void thumb##BASE##_12(u32 opcode) { IMM5_INSN(OP,18); } \ + static INSN_REGPARM void thumb##BASE##_13(u32 opcode) { IMM5_INSN(OP,19); } \ + static INSN_REGPARM void thumb##BASE##_14(u32 opcode) { IMM5_INSN(OP,20); } \ + static INSN_REGPARM void thumb##BASE##_15(u32 opcode) { IMM5_INSN(OP,21); } \ + static INSN_REGPARM void thumb##BASE##_16(u32 opcode) { IMM5_INSN(OP,22); } \ + static INSN_REGPARM void thumb##BASE##_17(u32 opcode) { IMM5_INSN(OP,23); } \ + static INSN_REGPARM void thumb##BASE##_18(u32 opcode) { IMM5_INSN(OP,24); } \ + static INSN_REGPARM void thumb##BASE##_19(u32 opcode) { IMM5_INSN(OP,25); } \ + static INSN_REGPARM void thumb##BASE##_1A(u32 opcode) { IMM5_INSN(OP,26); } \ + static INSN_REGPARM void thumb##BASE##_1B(u32 opcode) { IMM5_INSN(OP,27); } \ + static INSN_REGPARM void thumb##BASE##_1C(u32 opcode) { IMM5_INSN(OP,28); } \ + static INSN_REGPARM void thumb##BASE##_1D(u32 opcode) { IMM5_INSN(OP,29); } \ + static INSN_REGPARM void thumb##BASE##_1E(u32 opcode) { IMM5_INSN(OP,30); } \ + static INSN_REGPARM void thumb##BASE##_1F(u32 opcode) { IMM5_INSN(OP,31); } + +// LSL Rd, Rm, #Imm 5 +DEFINE_IMM5_INSN(IMM5_LSL,00) +// LSR Rd, Rm, #Imm 5 +DEFINE_IMM5_INSN(IMM5_LSR,08) +// ASR Rd, Rm, #Imm 5 +DEFINE_IMM5_INSN(IMM5_ASR,10) + +// 3-argument ADD/SUB ///////////////////////////////////////////////////// + +#define DEFINE_REG3_INSN(OP,BASE) \ + static INSN_REGPARM void thumb##BASE##_0(u32 opcode) { THREEARG_INSN(OP,0); } \ + static INSN_REGPARM void thumb##BASE##_1(u32 opcode) { THREEARG_INSN(OP,1); } \ + static INSN_REGPARM void thumb##BASE##_2(u32 opcode) { THREEARG_INSN(OP,2); } \ + static INSN_REGPARM void thumb##BASE##_3(u32 opcode) { THREEARG_INSN(OP,3); } \ + static INSN_REGPARM void thumb##BASE##_4(u32 opcode) { THREEARG_INSN(OP,4); } \ + static INSN_REGPARM void thumb##BASE##_5(u32 opcode) { THREEARG_INSN(OP,5); } \ + static INSN_REGPARM void thumb##BASE##_6(u32 opcode) { THREEARG_INSN(OP,6); } \ + static INSN_REGPARM void thumb##BASE##_7(u32 opcode) { THREEARG_INSN(OP,7); } + +#define DEFINE_IMM3_INSN(OP,BASE) \ + static INSN_REGPARM void thumb##BASE##_0(u32 opcode) { THREEARG_INSN(OP##_0,0); } \ + static INSN_REGPARM void thumb##BASE##_1(u32 opcode) { THREEARG_INSN(OP,1); } \ + static INSN_REGPARM void thumb##BASE##_2(u32 opcode) { THREEARG_INSN(OP,2); } \ + static INSN_REGPARM void thumb##BASE##_3(u32 opcode) { THREEARG_INSN(OP,3); } \ + static INSN_REGPARM void thumb##BASE##_4(u32 opcode) { THREEARG_INSN(OP,4); } \ + static INSN_REGPARM void thumb##BASE##_5(u32 opcode) { THREEARG_INSN(OP,5); } \ + static INSN_REGPARM void thumb##BASE##_6(u32 opcode) { THREEARG_INSN(OP,6); } \ + static INSN_REGPARM void thumb##BASE##_7(u32 opcode) { THREEARG_INSN(OP,7); } + +// ADD Rd, Rs, Rn +DEFINE_REG3_INSN(ADD_RD_RS_RN,18) +// SUB Rd, Rs, Rn +DEFINE_REG3_INSN(SUB_RD_RS_RN,1A) +// ADD Rd, Rs, #Offset3 +DEFINE_IMM3_INSN(ADD_RD_RS_O3,1C) +// SUB Rd, Rs, #Offset3 +DEFINE_IMM3_INSN(SUB_RD_RS_O3,1E) + +// MOV/CMP/ADD/SUB immediate ////////////////////////////////////////////// + +// MOV R0, #Offset8 +static INSN_REGPARM void thumb20(u32 opcode) { MOV_RN_O8(0); } +// MOV R1, #Offset8 +static INSN_REGPARM void thumb21(u32 opcode) { MOV_RN_O8(1); } +// MOV R2, #Offset8 +static INSN_REGPARM void thumb22(u32 opcode) { MOV_RN_O8(2); } +// MOV R3, #Offset8 +static INSN_REGPARM void thumb23(u32 opcode) { MOV_RN_O8(3); } +// MOV R4, #Offset8 +static INSN_REGPARM void thumb24(u32 opcode) { MOV_RN_O8(4); } +// MOV R5, #Offset8 +static INSN_REGPARM void thumb25(u32 opcode) { MOV_RN_O8(5); } +// MOV R6, #Offset8 +static INSN_REGPARM void thumb26(u32 opcode) { MOV_RN_O8(6); } +// MOV R7, #Offset8 +static INSN_REGPARM void thumb27(u32 opcode) { MOV_RN_O8(7); } + +// CMP R0, #Offset8 +static INSN_REGPARM void thumb28(u32 opcode) { CMP_RN_O8(0); } +// CMP R1, #Offset8 +static INSN_REGPARM void thumb29(u32 opcode) { CMP_RN_O8(1); } +// CMP R2, #Offset8 +static INSN_REGPARM void thumb2A(u32 opcode) { CMP_RN_O8(2); } +// CMP R3, #Offset8 +static INSN_REGPARM void thumb2B(u32 opcode) { CMP_RN_O8(3); } +// CMP R4, #Offset8 +static INSN_REGPARM void thumb2C(u32 opcode) { CMP_RN_O8(4); } +// CMP R5, #Offset8 +static INSN_REGPARM void thumb2D(u32 opcode) { CMP_RN_O8(5); } +// CMP R6, #Offset8 +static INSN_REGPARM void thumb2E(u32 opcode) { CMP_RN_O8(6); } +// CMP R7, #Offset8 +static INSN_REGPARM void thumb2F(u32 opcode) { CMP_RN_O8(7); } + +// ADD R0,#Offset8 +static INSN_REGPARM void thumb30(u32 opcode) { ADD_RN_O8(0); } +// ADD R1,#Offset8 +static INSN_REGPARM void thumb31(u32 opcode) { ADD_RN_O8(1); } +// ADD R2,#Offset8 +static INSN_REGPARM void thumb32(u32 opcode) { ADD_RN_O8(2); } +// ADD R3,#Offset8 +static INSN_REGPARM void thumb33(u32 opcode) { ADD_RN_O8(3); } +// ADD R4,#Offset8 +static INSN_REGPARM void thumb34(u32 opcode) { ADD_RN_O8(4); } +// ADD R5,#Offset8 +static INSN_REGPARM void thumb35(u32 opcode) { ADD_RN_O8(5); } +// ADD R6,#Offset8 +static INSN_REGPARM void thumb36(u32 opcode) { ADD_RN_O8(6); } +// ADD R7,#Offset8 +static INSN_REGPARM void thumb37(u32 opcode) { ADD_RN_O8(7); } + +// SUB R0,#Offset8 +static INSN_REGPARM void thumb38(u32 opcode) { SUB_RN_O8(0); } +// SUB R1,#Offset8 +static INSN_REGPARM void thumb39(u32 opcode) { SUB_RN_O8(1); } +// SUB R2,#Offset8 +static INSN_REGPARM void thumb3A(u32 opcode) { SUB_RN_O8(2); } +// SUB R3,#Offset8 +static INSN_REGPARM void thumb3B(u32 opcode) { SUB_RN_O8(3); } +// SUB R4,#Offset8 +static INSN_REGPARM void thumb3C(u32 opcode) { SUB_RN_O8(4); } +// SUB R5,#Offset8 +static INSN_REGPARM void thumb3D(u32 opcode) { SUB_RN_O8(5); } +// SUB R6,#Offset8 +static INSN_REGPARM void thumb3E(u32 opcode) { SUB_RN_O8(6); } +// SUB R7,#Offset8 +static INSN_REGPARM void thumb3F(u32 opcode) { SUB_RN_O8(7); } + +// ALU operations ///////////////////////////////////////////////////////// + +// AND Rd, Rs +static INSN_REGPARM void thumb40_0(u32 opcode) +{ + int dest = opcode & 7; + reg[dest].I &= reg[(opcode >> 3)&7].I; + N_FLAG = reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = reg[dest].I ? false : true; + THUMB_CONSOLE_OUTPUT(NULL, reg[2].I); +} + +// EOR Rd, Rs +static INSN_REGPARM void thumb40_1(u32 opcode) +{ + int dest = opcode & 7; + reg[dest].I ^= reg[(opcode >> 3)&7].I; + N_FLAG = reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = reg[dest].I ? false : true; +} + +// LSL Rd, Rs +static INSN_REGPARM void thumb40_2(u32 opcode) +{ + int dest = opcode & 7; + u32 value = reg[(opcode >> 3)&7].B.B0; + if(value) { + if(value == 32) { + value = 0; + C_FLAG = (reg[dest].I & 1 ? true : false); + } else if(value < 32) { + LSL_RD_RS; + } else { + value = 0; + C_FLAG = false; + } + reg[dest].I = value; + } + N_FLAG = reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = reg[dest].I ? false : true; + clockTicks = codeTicksAccess16(armNextPC)+2; +} + +// LSR Rd, Rs +static INSN_REGPARM void thumb40_3(u32 opcode) +{ + int dest = opcode & 7; + u32 value = reg[(opcode >> 3)&7].B.B0; + if(value) { + if(value == 32) { + value = 0; + C_FLAG = (reg[dest].I & 0x80000000 ? true : false); + } else if(value < 32) { + LSR_RD_RS; + } else { + value = 0; + C_FLAG = false; + } + reg[dest].I = value; + } + N_FLAG = reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = reg[dest].I ? false : true; + clockTicks = codeTicksAccess16(armNextPC)+2; +} + +// ASR Rd, Rs +static INSN_REGPARM void thumb41_0(u32 opcode) +{ + int dest = opcode & 7; + u32 value = reg[(opcode >> 3)&7].B.B0; + if(value) { + if(value < 32) { + ASR_RD_RS; + reg[dest].I = value; + } else { + if(reg[dest].I & 0x80000000){ + reg[dest].I = 0xFFFFFFFF; + C_FLAG = true; + } else { + reg[dest].I = 0x00000000; + C_FLAG = false; + } + } + } + N_FLAG = reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = reg[dest].I ? false : true; + clockTicks = codeTicksAccess16(armNextPC)+2; +} + +// ADC Rd, Rs +static INSN_REGPARM void thumb41_1(u32 opcode) +{ + int dest = opcode & 0x07; + u32 value = reg[(opcode >> 3)&7].I; + ADC_RD_RS; +} + +// SBC Rd, Rs +static INSN_REGPARM void thumb41_2(u32 opcode) +{ + int dest = opcode & 0x07; + u32 value = reg[(opcode >> 3)&7].I; + SBC_RD_RS; +} + +// ROR Rd, Rs +static INSN_REGPARM void thumb41_3(u32 opcode) +{ + int dest = opcode & 7; + u32 value = reg[(opcode >> 3)&7].B.B0; + + if(value) { + value = value & 0x1f; + if(value == 0) { + C_FLAG = (reg[dest].I & 0x80000000 ? true : false); + } else { + ROR_RD_RS; + reg[dest].I = value; + } + } + clockTicks = codeTicksAccess16(armNextPC)+2; + N_FLAG = reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = reg[dest].I ? false : true; +} + +// TST Rd, Rs +static INSN_REGPARM void thumb42_0(u32 opcode) +{ + u32 value = reg[opcode & 7].I & reg[(opcode >> 3) & 7].I; + N_FLAG = value & 0x80000000 ? true : false; + Z_FLAG = value ? false : true; +} + +// NEG Rd, Rs +static INSN_REGPARM void thumb42_1(u32 opcode) +{ + int dest = opcode & 7; + int source = (opcode >> 3) & 7; + NEG_RD_RS; +} + +// CMP Rd, Rs +static INSN_REGPARM void thumb42_2(u32 opcode) +{ + int dest = opcode & 7; + u32 value = reg[(opcode >> 3)&7].I; + CMP_RD_RS; +} + +// CMN Rd, Rs +static INSN_REGPARM void thumb42_3(u32 opcode) +{ + int dest = opcode & 7; + u32 value = reg[(opcode >> 3)&7].I; + CMN_RD_RS; +} + +// ORR Rd, Rs +static INSN_REGPARM void thumb43_0(u32 opcode) +{ + int dest = opcode & 7; + reg[dest].I |= reg[(opcode >> 3) & 7].I; + Z_FLAG = reg[dest].I ? false : true; + N_FLAG = reg[dest].I & 0x80000000 ? true : false; +} + +// MUL Rd, Rs +static INSN_REGPARM void thumb43_1(u32 opcode) +{ + clockTicks = 1; + int dest = opcode & 7; + u32 rm = reg[dest].I; + reg[dest].I = reg[(opcode >> 3) & 7].I * rm; + if (((s32)rm) < 0) + rm = ~rm; + if ((rm & 0xFFFFFF00) == 0) + clockTicks += 0; + else if ((rm & 0xFFFF0000) == 0) + clockTicks += 1; + else if ((rm & 0xFF000000) == 0) + clockTicks += 2; + else + clockTicks += 3; + busPrefetchCount = (busPrefetchCount<>(8-clockTicks)); + clockTicks += codeTicksAccess16(armNextPC) + 1; + Z_FLAG = reg[dest].I ? false : true; + N_FLAG = reg[dest].I & 0x80000000 ? true : false; +} + +// BIC Rd, Rs +static INSN_REGPARM void thumb43_2(u32 opcode) +{ + int dest = opcode & 7; + reg[dest].I &= (~reg[(opcode >> 3) & 7].I); + Z_FLAG = reg[dest].I ? false : true; + N_FLAG = reg[dest].I & 0x80000000 ? true : false; +} + +// MVN Rd, Rs +static INSN_REGPARM void thumb43_3(u32 opcode) +{ + int dest = opcode & 7; + reg[dest].I = ~reg[(opcode >> 3) & 7].I; + Z_FLAG = reg[dest].I ? false : true; + N_FLAG = reg[dest].I & 0x80000000 ? true : false; +} + +// High-register instructions and BX ////////////////////////////////////// + +// ADD Rd, Hs +static INSN_REGPARM void thumb44_1(u32 opcode) +{ + reg[opcode&7].I += reg[((opcode>>3)&7)+8].I; +} + +// ADD Hd, Rs +static INSN_REGPARM void thumb44_2(u32 opcode) +{ + reg[(opcode&7)+8].I += reg[(opcode>>3)&7].I; + if((opcode&7) == 7) { + reg[15].I &= 0xFFFFFFFE; + armNextPC = reg[15].I; + reg[15].I += 2; + THUMB_PREFETCH; + clockTicks = codeTicksAccessSeq16(armNextPC)*2 + + codeTicksAccess16(armNextPC) + 3; + } +} + +// ADD Hd, Hs +static INSN_REGPARM void thumb44_3(u32 opcode) +{ + reg[(opcode&7)+8].I += reg[((opcode>>3)&7)+8].I; + if((opcode&7) == 7) { + reg[15].I &= 0xFFFFFFFE; + armNextPC = reg[15].I; + reg[15].I += 2; + THUMB_PREFETCH; + clockTicks = codeTicksAccessSeq16(armNextPC)*2 + + codeTicksAccess16(armNextPC) + 3; + } +} + +// CMP Rd, Hs +static INSN_REGPARM void thumb45_1(u32 opcode) +{ + int dest = opcode & 7; + u32 value = reg[((opcode>>3)&7)+8].I; + CMP_RD_RS; +} + +// CMP Hd, Rs +static INSN_REGPARM void thumb45_2(u32 opcode) +{ + int dest = (opcode & 7) + 8; + u32 value = reg[(opcode>>3)&7].I; + CMP_RD_RS; +} + +// CMP Hd, Hs +static INSN_REGPARM void thumb45_3(u32 opcode) +{ + int dest = (opcode & 7) + 8; + u32 value = reg[((opcode>>3)&7)+8].I; + CMP_RD_RS; +} + +// MOV Rd, Rs +static INSN_REGPARM void thumb46_0(u32 opcode) +{ + reg[opcode&7].I = reg[((opcode>>3)&7)].I; + clockTicks = codeTicksAccessSeq16(armNextPC) + 1; +} + +// MOV Rd, Hs +static INSN_REGPARM void thumb46_1(u32 opcode) +{ + reg[opcode&7].I = reg[((opcode>>3)&7)+8].I; + clockTicks = codeTicksAccessSeq16(armNextPC) + 1; +} + +// MOV Hd, Rs +static INSN_REGPARM void thumb46_2(u32 opcode) +{ + reg[(opcode&7)+8].I = reg[(opcode>>3)&7].I; + if((opcode&7) == 7) { + UPDATE_OLDREG; + reg[15].I &= 0xFFFFFFFE; + armNextPC = reg[15].I; + reg[15].I += 2; + THUMB_PREFETCH; + clockTicks = codeTicksAccessSeq16(armNextPC)*2 + + codeTicksAccess16(armNextPC) + 3; + } +} + +// MOV Hd, Hs +static INSN_REGPARM void thumb46_3(u32 opcode) +{ + reg[(opcode&7)+8].I = reg[((opcode>>3)&7)+8].I; + if((opcode&7) == 7) { + UPDATE_OLDREG; + reg[15].I &= 0xFFFFFFFE; + armNextPC = reg[15].I; + reg[15].I += 2; + THUMB_PREFETCH; + clockTicks = codeTicksAccessSeq16(armNextPC)*2 + + codeTicksAccess16(armNextPC) + 3; + } +} + + +// BX Rs +static INSN_REGPARM void thumb47(u32 opcode) +{ + int base = (opcode >> 3) & 15; + busPrefetchCount=0; + UPDATE_OLDREG; + reg[15].I = reg[base].I; + if(reg[base].I & 1) { + armState = false; + reg[15].I &= 0xFFFFFFFE; + armNextPC = reg[15].I; + reg[15].I += 2; + THUMB_PREFETCH; + clockTicks = codeTicksAccessSeq16(armNextPC)*2 + codeTicksAccess16(armNextPC) + 3; + } else { + armState = true; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + ARM_PREFETCH; + clockTicks = codeTicksAccessSeq32(armNextPC)*2 + codeTicksAccess32(armNextPC) + 3; + } +} + +// Load/store instructions //////////////////////////////////////////////// + +// LDR R0~R7,[PC, #Imm] +static INSN_REGPARM void thumb48(u32 opcode) +{ + u8 regist = (opcode >> 8) & 7; + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = (reg[15].I & 0xFFFFFFFC) + ((opcode & 0xFF) << 2); + reg[regist].I = CPUReadMemoryQuick(address); + busPrefetchCount=0; + clockTicks = 3 + dataTicksAccess32(address) + codeTicksAccess16(armNextPC); +} + +// STR Rd, [Rs, Rn] +static INSN_REGPARM void thumb50(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[(opcode>>3)&7].I + reg[(opcode>>6)&7].I; + CPUWriteMemory(address, reg[opcode & 7].I); + clockTicks = dataTicksAccess32(address) + codeTicksAccess16(armNextPC) + 2; +} + +// STRH Rd, [Rs, Rn] +static INSN_REGPARM void thumb52(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[(opcode>>3)&7].I + reg[(opcode>>6)&7].I; + CPUWriteHalfWord(address, reg[opcode&7].W.W0); + clockTicks = dataTicksAccess16(address) + codeTicksAccess16(armNextPC) + 2; +} + +// STRB Rd, [Rs, Rn] +static INSN_REGPARM void thumb54(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[(opcode>>3)&7].I + reg[(opcode >>6)&7].I; + CPUWriteByte(address, reg[opcode & 7].B.B0); + clockTicks = dataTicksAccess16(address) + codeTicksAccess16(armNextPC) + 2; +} + +// LDSB Rd, [Rs, Rn] +static INSN_REGPARM void thumb56(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[(opcode>>3)&7].I + reg[(opcode>>6)&7].I; + reg[opcode&7].I = (s8)CPUReadByte(address); + clockTicks = 3 + dataTicksAccess16(address) + codeTicksAccess16(armNextPC); +} + +// LDR Rd, [Rs, Rn] +static INSN_REGPARM void thumb58(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[(opcode>>3)&7].I + reg[(opcode>>6)&7].I; + reg[opcode&7].I = CPUReadMemory(address); + clockTicks = 3 + dataTicksAccess32(address) + codeTicksAccess16(armNextPC); +} + +// LDRH Rd, [Rs, Rn] +static INSN_REGPARM void thumb5A(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[(opcode>>3)&7].I + reg[(opcode>>6)&7].I; + reg[opcode&7].I = CPUReadHalfWord(address); + clockTicks = 3 + dataTicksAccess32(address) + codeTicksAccess16(armNextPC); +} + +// LDRB Rd, [Rs, Rn] +static INSN_REGPARM void thumb5C(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[(opcode>>3)&7].I + reg[(opcode>>6)&7].I; + reg[opcode&7].I = CPUReadByte(address); + clockTicks = 3 + dataTicksAccess16(address) + codeTicksAccess16(armNextPC); +} + +// LDSH Rd, [Rs, Rn] +static INSN_REGPARM void thumb5E(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[(opcode>>3)&7].I + reg[(opcode>>6)&7].I; + reg[opcode&7].I = (u32)CPUReadHalfWordSigned(address); + clockTicks = 3 + dataTicksAccess16(address) + codeTicksAccess16(armNextPC); +} + +// STR Rd, [Rs, #Imm] +static INSN_REGPARM void thumb60(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[(opcode>>3)&7].I + (((opcode>>6)&31)<<2); + CPUWriteMemory(address, reg[opcode&7].I); + clockTicks = dataTicksAccess32(address) + codeTicksAccess16(armNextPC) + 2; +} + +// LDR Rd, [Rs, #Imm] +static INSN_REGPARM void thumb68(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[(opcode>>3)&7].I + (((opcode>>6)&31)<<2); + reg[opcode&7].I = CPUReadMemory(address); + clockTicks = 3 + dataTicksAccess32(address) + codeTicksAccess16(armNextPC); +} + +// STRB Rd, [Rs, #Imm] +static INSN_REGPARM void thumb70(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[(opcode>>3)&7].I + (((opcode>>6)&31)); + CPUWriteByte(address, reg[opcode&7].B.B0); + clockTicks = dataTicksAccess16(address) + codeTicksAccess16(armNextPC) + 2; +} + +// LDRB Rd, [Rs, #Imm] +static INSN_REGPARM void thumb78(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[(opcode>>3)&7].I + (((opcode>>6)&31)); + reg[opcode&7].I = CPUReadByte(address); + clockTicks = 3 + dataTicksAccess16(address) + codeTicksAccess16(armNextPC); +} + +// STRH Rd, [Rs, #Imm] +static INSN_REGPARM void thumb80(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[(opcode>>3)&7].I + (((opcode>>6)&31)<<1); + CPUWriteHalfWord(address, reg[opcode&7].W.W0); + clockTicks = dataTicksAccess16(address) + codeTicksAccess16(armNextPC) + 2; +} + +// LDRH Rd, [Rs, #Imm] +static INSN_REGPARM void thumb88(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[(opcode>>3)&7].I + (((opcode>>6)&31)<<1); + reg[opcode&7].I = CPUReadHalfWord(address); + clockTicks = 3 + dataTicksAccess16(address) + codeTicksAccess16(armNextPC); +} + +// STR R0~R7, [SP, #Imm] +static INSN_REGPARM void thumb90(u32 opcode) +{ + u8 regist = (opcode >> 8) & 7; + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[13].I + ((opcode&255)<<2); + CPUWriteMemory(address, reg[regist].I); + clockTicks = dataTicksAccess32(address) + codeTicksAccess16(armNextPC) + 2; +} + +// LDR R0~R7, [SP, #Imm] +static INSN_REGPARM void thumb98(u32 opcode) +{ + u8 regist = (opcode >> 8) & 7; + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[13].I + ((opcode&255)<<2); + reg[regist].I = CPUReadMemoryQuick(address); + clockTicks = 3 + dataTicksAccess32(address) + codeTicksAccess16(armNextPC); +} + +// PC/stack-related /////////////////////////////////////////////////////// + +// ADD R0~R7, PC, Imm +static INSN_REGPARM void thumbA0(u32 opcode) +{ + u8 regist = (opcode >> 8) & 7; + reg[regist].I = (reg[15].I & 0xFFFFFFFC) + ((opcode&255)<<2); + clockTicks = 1 + codeTicksAccess16(armNextPC); +} + +// ADD R0~R7, SP, Imm +static INSN_REGPARM void thumbA8(u32 opcode) +{ + u8 regist = (opcode >> 8) & 7; + reg[regist].I = reg[13].I + ((opcode&255)<<2); + clockTicks = 1 + codeTicksAccess16(armNextPC); +} + +// ADD SP, Imm +static INSN_REGPARM void thumbB0(u32 opcode) +{ + int offset = (opcode & 127) << 2; + if(opcode & 0x80) + offset = -offset; + reg[13].I += offset; + clockTicks = 1 + codeTicksAccess16(armNextPC); +} + +// Push and pop /////////////////////////////////////////////////////////// + +#define PUSH_REG(val, r) \ + if (opcode & (val)) { \ + CPUWriteMemory(address, reg[(r)].I); \ + if (!count) { \ + clockTicks += 1 + dataTicksAccess32(address); \ + } else { \ + clockTicks += 1 + dataTicksAccessSeq32(address); \ + } \ + count++; \ + address += 4; \ + } + +#define POP_REG(val, r) \ + if (opcode & (val)) { \ + reg[(r)].I = CPUReadMemory(address); \ + if (!count) { \ + clockTicks += 1 + dataTicksAccess32(address); \ + } else { \ + clockTicks += 1 + dataTicksAccessSeq32(address); \ + } \ + count++; \ + address += 4; \ + } + +// PUSH {Rlist} +static INSN_REGPARM void thumbB4(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int count = 0; + u32 temp = reg[13].I - 4 * cpuBitsSet[opcode & 0xff]; + u32 address = temp & 0xFFFFFFFC; + PUSH_REG(1, 0); + PUSH_REG(2, 1); + PUSH_REG(4, 2); + PUSH_REG(8, 3); + PUSH_REG(16, 4); + PUSH_REG(32, 5); + PUSH_REG(64, 6); + PUSH_REG(128, 7); + clockTicks += 1 + codeTicksAccess16(armNextPC); + reg[13].I = temp; +} + +// PUSH {Rlist, LR} +static INSN_REGPARM void thumbB5(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int count = 0; + u32 temp = reg[13].I - 4 - 4 * cpuBitsSet[opcode & 0xff]; + u32 address = temp & 0xFFFFFFFC; + PUSH_REG(1, 0); + PUSH_REG(2, 1); + PUSH_REG(4, 2); + PUSH_REG(8, 3); + PUSH_REG(16, 4); + PUSH_REG(32, 5); + PUSH_REG(64, 6); + PUSH_REG(128, 7); + PUSH_REG(256, 14); + clockTicks += 1 + codeTicksAccess16(armNextPC); + reg[13].I = temp; +} + +// POP {Rlist} +static INSN_REGPARM void thumbBC(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int count = 0; + u32 address = reg[13].I & 0xFFFFFFFC; + u32 temp = reg[13].I + 4*cpuBitsSet[opcode & 0xFF]; + POP_REG(1, 0); + POP_REG(2, 1); + POP_REG(4, 2); + POP_REG(8, 3); + POP_REG(16, 4); + POP_REG(32, 5); + POP_REG(64, 6); + POP_REG(128, 7); + reg[13].I = temp; + clockTicks = 2 + codeTicksAccess16(armNextPC); +} + +// POP {Rlist, PC} +static INSN_REGPARM void thumbBD(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int count = 0; + u32 address = reg[13].I & 0xFFFFFFFC; + u32 temp = reg[13].I + 4 + 4*cpuBitsSet[opcode & 0xFF]; + POP_REG(1, 0); + POP_REG(2, 1); + POP_REG(4, 2); + POP_REG(8, 3); + POP_REG(16, 4); + POP_REG(32, 5); + POP_REG(64, 6); + POP_REG(128, 7); + reg[15].I = (CPUReadMemory(address) & 0xFFFFFFFE); + if (!count) { + clockTicks += 1 + dataTicksAccess32(address); + } else { + clockTicks += 1 + dataTicksAccessSeq32(address); + } + count++; + armNextPC = reg[15].I; + reg[15].I += 2; + reg[13].I = temp; + THUMB_PREFETCH; + busPrefetchCount = 0; + clockTicks += 3 + codeTicksAccess16(armNextPC) + codeTicksAccess16(armNextPC); +} + +// Load/store multiple //////////////////////////////////////////////////// + +#define THUMB_STM_REG(val,r,b) \ + if(opcode & (val)) { \ + CPUWriteMemory(address, reg[(r)].I); \ + reg[(b)].I = temp; \ + if (!count) { \ + clockTicks += 1 + dataTicksAccess32(address); \ + } else { \ + clockTicks += 1 + dataTicksAccessSeq32(address); \ + } \ + count++; \ + address += 4; \ + } + +#define THUMB_LDM_REG(val,r) \ + if(opcode & (val)) { \ + reg[(r)].I = CPUReadMemory(address); \ + if (!count) { \ + clockTicks += 1 + dataTicksAccess32(address); \ + } else { \ + clockTicks += 1 + dataTicksAccessSeq32(address); \ + } \ + count++; \ + address += 4; \ + } + +// STM R0~7!, {Rlist} +static INSN_REGPARM void thumbC0(u32 opcode) +{ + u8 regist = (opcode >> 8) & 7; + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[regist].I & 0xFFFFFFFC; + u32 temp = reg[regist].I + 4*cpuBitsSet[opcode & 0xff]; + int count = 0; + // store + THUMB_STM_REG(1, 0, regist); + THUMB_STM_REG(2, 1, regist); + THUMB_STM_REG(4, 2, regist); + THUMB_STM_REG(8, 3, regist); + THUMB_STM_REG(16, 4, regist); + THUMB_STM_REG(32, 5, regist); + THUMB_STM_REG(64, 6, regist); + THUMB_STM_REG(128, 7, regist); + clockTicks = 1 + codeTicksAccess16(armNextPC); +} + +// LDM R0~R7!, {Rlist} +static INSN_REGPARM void thumbC8(u32 opcode) +{ + u8 regist = (opcode >> 8) & 7; + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[regist].I & 0xFFFFFFFC; + u32 temp = reg[regist].I + 4*cpuBitsSet[opcode & 0xFF]; + int count = 0; + // load + THUMB_LDM_REG(1, 0); + THUMB_LDM_REG(2, 1); + THUMB_LDM_REG(4, 2); + THUMB_LDM_REG(8, 3); + THUMB_LDM_REG(16, 4); + THUMB_LDM_REG(32, 5); + THUMB_LDM_REG(64, 6); + THUMB_LDM_REG(128, 7); + clockTicks = 2 + codeTicksAccess16(armNextPC); + if(!(opcode & (1<>6])(opcode); + + if (clockTicks < 0) + return 0; + if (clockTicks==0) + clockTicks = codeTicksAccessSeq16(oldArmNextPC) + 1; + cpuTotalTicks += clockTicks; + + } while (cpuTotalTicks < cpuNextEvent && !armState && !holdState && !SWITicks); + return 1; +} diff --git a/src/gba/GBA.cpp b/src/gba/GBA.cpp new file mode 100644 index 0000000..2e056af --- /dev/null +++ b/src/gba/GBA.cpp @@ -0,0 +1,4430 @@ +#include +#include +#include +#include +#include +#include "GBA.h" +#include "GBAcpu.h" +#include "GBAinline.h" +#include "Globals.h" +#include "GBAGfx.h" +#include "EEprom.h" +#include "Flash.h" +#include "Sound.h" +#include "Sram.h" +#include "bios.h" +#include "Cheats.h" +#include "../NLS.h" +#include "elf.h" +#include "../Util.h" +#include "../common/Port.h" +#include "../System.h" +#include "agbprint.h" +#include "GBALink.h" + + +#include +using namespace std; + +#ifdef PROFILING +#include "prof/prof.h" +#endif + +#ifdef __GNUC__ +#define _stricmp strcasecmp +#endif + +extern int emulating; + +int SWITicks = 0; +int IRQTicks = 0; + +u32 mastercode = 0; +int layerEnableDelay = 0; +bool busPrefetch = false; +bool busPrefetchEnable = false; +u32 busPrefetchCount = 0; +int cpuDmaTicksToUpdate = 0; +int cpuDmaCount = 0; +bool cpuDmaHack = false; +u32 cpuDmaLast = 0; +int dummyAddress = 0; + +bool cpuBreakLoop = false; +int cpuNextEvent = 0; + +int gbaSaveType = 0; // used to remember the save type on reset +bool intState = false; +bool stopState = false; +bool holdState = false; +int holdType = 0; +bool cpuSramEnabled = true; +bool cpuFlashEnabled = true; +bool cpuEEPROMEnabled = true; +bool cpuEEPROMSensorEnabled = false; + +u32 cpuPrefetch[2]; + +int cpuTotalTicks = 0; +#ifdef PROFILING +int profilingTicks = 0; +int profilingTicksReload = 0; +static profile_segment *profilSegment = NULL; +#endif + +#ifdef BKPT_SUPPORT +u8 freezeWorkRAM[0x40000]; +u8 freezeInternalRAM[0x8000]; +u8 freezeVRAM[0x18000]; +u8 freezePRAM[0x400]; +u8 freezeOAM[0x400]; +bool debugger_last; +#endif + +int lcdTicks = (useBios && !skipBios) ? 1008 : 208; +u8 timerOnOffDelay = 0; +u16 timer0Value = 0; +bool timer0On = false; +int timer0Ticks = 0; +int timer0Reload = 0; +int timer0ClockReload = 0; +u16 timer1Value = 0; +bool timer1On = false; +int timer1Ticks = 0; +int timer1Reload = 0; +int timer1ClockReload = 0; +u16 timer2Value = 0; +bool timer2On = false; +int timer2Ticks = 0; +int timer2Reload = 0; +int timer2ClockReload = 0; +u16 timer3Value = 0; +bool timer3On = false; +int timer3Ticks = 0; +int timer3Reload = 0; +int timer3ClockReload = 0; +u32 dma0Source = 0; +u32 dma0Dest = 0; +u32 dma1Source = 0; +u32 dma1Dest = 0; +u32 dma2Source = 0; +u32 dma2Dest = 0; +u32 dma3Source = 0; +u32 dma3Dest = 0; +void (*cpuSaveGameFunc)(u32,u8) = flashSaveDecide; +void (*renderLine)() = mode0RenderLine; +bool fxOn = false; +bool windowOn = false; +int frameCount = 0; +char buffer[1024]; +u32 lastTime = 0; +int count = 0; + +int capture = 0; +int capturePrevious = 0; +int captureNumber = 0; + +int armOpcodeCount = 0; +int thumbOpcodeCount = 0; + +const int TIMER_TICKS[4] = { + 0, + 6, + 8, + 10 +}; + +const u32 objTilesAddress [3] = {0x010000, 0x014000, 0x014000}; +const u8 gamepakRamWaitState[4] = { 4, 3, 2, 8 }; +const u8 gamepakWaitState[4] = { 4, 3, 2, 8 }; +const u8 gamepakWaitState0[2] = { 2, 1 }; +const u8 gamepakWaitState1[2] = { 4, 1 }; +const u8 gamepakWaitState2[2] = { 8, 1 }; +const bool isInRom [16]= + { false, false, false, false, false, false, false, false, + true, true, true, true, true, true, false, false }; + +u8 memoryWait[16] = + { 0, 0, 2, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 0 }; +u8 memoryWait32[16] = + { 0, 0, 5, 0, 0, 1, 1, 0, 7, 7, 9, 9, 13, 13, 4, 0 }; +u8 memoryWaitSeq[16] = + { 0, 0, 2, 0, 0, 0, 0, 0, 2, 2, 4, 4, 8, 8, 4, 0 }; +u8 memoryWaitSeq32[16] = + { 0, 0, 5, 0, 0, 1, 1, 0, 5, 5, 9, 9, 17, 17, 4, 0 }; + +// The videoMemoryWait constants are used to add some waitstates +// if the opcode access video memory data outside of vblank/hblank +// It seems to happen on only one ticks for each pixel. +// Not used for now (too problematic with current code). +//const u8 videoMemoryWait[16] = +// {0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + +u8 biosProtected[4]; + +#ifdef WORDS_BIGENDIAN +bool cpuBiosSwapped = false; +#endif + +u32 myROM[] = { +0xEA000006, +0xEA000093, +0xEA000006, +0x00000000, +0x00000000, +0x00000000, +0xEA000088, +0x00000000, +0xE3A00302, +0xE1A0F000, +0xE92D5800, +0xE55EC002, +0xE28FB03C, +0xE79BC10C, +0xE14FB000, +0xE92D0800, +0xE20BB080, +0xE38BB01F, +0xE129F00B, +0xE92D4004, +0xE1A0E00F, +0xE12FFF1C, +0xE8BD4004, +0xE3A0C0D3, +0xE129F00C, +0xE8BD0800, +0xE169F00B, +0xE8BD5800, +0xE1B0F00E, +0x0000009C, +0x0000009C, +0x0000009C, +0x0000009C, +0x000001F8, +0x000001F0, +0x000000AC, +0x000000A0, +0x000000FC, +0x00000168, +0xE12FFF1E, +0xE1A03000, +0xE1A00001, +0xE1A01003, +0xE2113102, +0x42611000, +0xE033C040, +0x22600000, +0xE1B02001, +0xE15200A0, +0x91A02082, +0x3AFFFFFC, +0xE1500002, +0xE0A33003, +0x20400002, +0xE1320001, +0x11A020A2, +0x1AFFFFF9, +0xE1A01000, +0xE1A00003, +0xE1B0C08C, +0x22600000, +0x42611000, +0xE12FFF1E, +0xE92D0010, +0xE1A0C000, +0xE3A01001, +0xE1500001, +0x81A000A0, +0x81A01081, +0x8AFFFFFB, +0xE1A0000C, +0xE1A04001, +0xE3A03000, +0xE1A02001, +0xE15200A0, +0x91A02082, +0x3AFFFFFC, +0xE1500002, +0xE0A33003, +0x20400002, +0xE1320001, +0x11A020A2, +0x1AFFFFF9, +0xE0811003, +0xE1B010A1, +0xE1510004, +0x3AFFFFEE, +0xE1A00004, +0xE8BD0010, +0xE12FFF1E, +0xE0010090, +0xE1A01741, +0xE2611000, +0xE3A030A9, +0xE0030391, +0xE1A03743, +0xE2833E39, +0xE0030391, +0xE1A03743, +0xE2833C09, +0xE283301C, +0xE0030391, +0xE1A03743, +0xE2833C0F, +0xE28330B6, +0xE0030391, +0xE1A03743, +0xE2833C16, +0xE28330AA, +0xE0030391, +0xE1A03743, +0xE2833A02, +0xE2833081, +0xE0030391, +0xE1A03743, +0xE2833C36, +0xE2833051, +0xE0030391, +0xE1A03743, +0xE2833CA2, +0xE28330F9, +0xE0000093, +0xE1A00840, +0xE12FFF1E, +0xE3A00001, +0xE3A01001, +0xE92D4010, +0xE3A03000, +0xE3A04001, +0xE3500000, +0x1B000004, +0xE5CC3301, +0xEB000002, +0x0AFFFFFC, +0xE8BD4010, +0xE12FFF1E, +0xE3A0C301, +0xE5CC3208, +0xE15C20B8, +0xE0110002, +0x10222000, +0x114C20B8, +0xE5CC4208, +0xE12FFF1E, +0xE92D500F, +0xE3A00301, +0xE1A0E00F, +0xE510F004, +0xE8BD500F, +0xE25EF004, +0xE59FD044, +0xE92D5000, +0xE14FC000, +0xE10FE000, +0xE92D5000, +0xE3A0C302, +0xE5DCE09C, +0xE35E00A5, +0x1A000004, +0x05DCE0B4, +0x021EE080, +0xE28FE004, +0x159FF018, +0x059FF018, +0xE59FD018, +0xE8BD5000, +0xE169F00C, +0xE8BD5000, +0xE25EF004, +0x03007FF0, +0x09FE2000, +0x09FFC000, +0x03007FE0 +}; + +variable_desc saveGameStruct[] = { + { &DISPCNT , sizeof(u16) }, + { &DISPSTAT , sizeof(u16) }, + { &VCOUNT , sizeof(u16) }, + { &BG0CNT , sizeof(u16) }, + { &BG1CNT , sizeof(u16) }, + { &BG2CNT , sizeof(u16) }, + { &BG3CNT , sizeof(u16) }, + { &BG0HOFS , sizeof(u16) }, + { &BG0VOFS , sizeof(u16) }, + { &BG1HOFS , sizeof(u16) }, + { &BG1VOFS , sizeof(u16) }, + { &BG2HOFS , sizeof(u16) }, + { &BG2VOFS , sizeof(u16) }, + { &BG3HOFS , sizeof(u16) }, + { &BG3VOFS , sizeof(u16) }, + { &BG2PA , sizeof(u16) }, + { &BG2PB , sizeof(u16) }, + { &BG2PC , sizeof(u16) }, + { &BG2PD , sizeof(u16) }, + { &BG2X_L , sizeof(u16) }, + { &BG2X_H , sizeof(u16) }, + { &BG2Y_L , sizeof(u16) }, + { &BG2Y_H , sizeof(u16) }, + { &BG3PA , sizeof(u16) }, + { &BG3PB , sizeof(u16) }, + { &BG3PC , sizeof(u16) }, + { &BG3PD , sizeof(u16) }, + { &BG3X_L , sizeof(u16) }, + { &BG3X_H , sizeof(u16) }, + { &BG3Y_L , sizeof(u16) }, + { &BG3Y_H , sizeof(u16) }, + { &WIN0H , sizeof(u16) }, + { &WIN1H , sizeof(u16) }, + { &WIN0V , sizeof(u16) }, + { &WIN1V , sizeof(u16) }, + { &WININ , sizeof(u16) }, + { &WINOUT , sizeof(u16) }, + { &MOSAIC , sizeof(u16) }, + { &BLDMOD , sizeof(u16) }, + { &COLEV , sizeof(u16) }, + { &COLY , sizeof(u16) }, + { &DM0SAD_L , sizeof(u16) }, + { &DM0SAD_H , sizeof(u16) }, + { &DM0DAD_L , sizeof(u16) }, + { &DM0DAD_H , sizeof(u16) }, + { &DM0CNT_L , sizeof(u16) }, + { &DM0CNT_H , sizeof(u16) }, + { &DM1SAD_L , sizeof(u16) }, + { &DM1SAD_H , sizeof(u16) }, + { &DM1DAD_L , sizeof(u16) }, + { &DM1DAD_H , sizeof(u16) }, + { &DM1CNT_L , sizeof(u16) }, + { &DM1CNT_H , sizeof(u16) }, + { &DM2SAD_L , sizeof(u16) }, + { &DM2SAD_H , sizeof(u16) }, + { &DM2DAD_L , sizeof(u16) }, + { &DM2DAD_H , sizeof(u16) }, + { &DM2CNT_L , sizeof(u16) }, + { &DM2CNT_H , sizeof(u16) }, + { &DM3SAD_L , sizeof(u16) }, + { &DM3SAD_H , sizeof(u16) }, + { &DM3DAD_L , sizeof(u16) }, + { &DM3DAD_H , sizeof(u16) }, + { &DM3CNT_L , sizeof(u16) }, + { &DM3CNT_H , sizeof(u16) }, + { &TM0D , sizeof(u16) }, + { &TM0CNT , sizeof(u16) }, + { &TM1D , sizeof(u16) }, + { &TM1CNT , sizeof(u16) }, + { &TM2D , sizeof(u16) }, + { &TM2CNT , sizeof(u16) }, + { &TM3D , sizeof(u16) }, + { &TM3CNT , sizeof(u16) }, + { &P1 , sizeof(u16) }, + { &IE , sizeof(u16) }, + { &IF , sizeof(u16) }, + { &IME , sizeof(u16) }, + { &holdState, sizeof(bool) }, + { &holdType, sizeof(int) }, + { &lcdTicks, sizeof(int) }, + { &timer0On , sizeof(bool) }, + { &timer0Ticks , sizeof(int) }, + { &timer0Reload , sizeof(int) }, + { &timer0ClockReload , sizeof(int) }, + { &timer1On , sizeof(bool) }, + { &timer1Ticks , sizeof(int) }, + { &timer1Reload , sizeof(int) }, + { &timer1ClockReload , sizeof(int) }, + { &timer2On , sizeof(bool) }, + { &timer2Ticks , sizeof(int) }, + { &timer2Reload , sizeof(int) }, + { &timer2ClockReload , sizeof(int) }, + { &timer3On , sizeof(bool) }, + { &timer3Ticks , sizeof(int) }, + { &timer3Reload , sizeof(int) }, + { &timer3ClockReload , sizeof(int) }, + { &dma0Source , sizeof(u32) }, + { &dma0Dest , sizeof(u32) }, + { &dma1Source , sizeof(u32) }, + { &dma1Dest , sizeof(u32) }, + { &dma2Source , sizeof(u32) }, + { &dma2Dest , sizeof(u32) }, + { &dma3Source , sizeof(u32) }, + { &dma3Dest , sizeof(u32) }, + { &fxOn, sizeof(bool) }, + { &windowOn, sizeof(bool) }, + { &N_FLAG , sizeof(bool) }, + { &C_FLAG , sizeof(bool) }, + { &Z_FLAG , sizeof(bool) }, + { &V_FLAG , sizeof(bool) }, + { &armState , sizeof(bool) }, + { &armIrqEnable , sizeof(bool) }, + { &armNextPC , sizeof(u32) }, + { &armMode , sizeof(int) }, + { &saveType , sizeof(int) }, + { NULL, 0 } +}; + +static int romSize = 0x2000000; + +#ifdef PROFILING +void cpuProfil(profile_segment *seg) +{ + profilSegment = seg; +} + +void cpuEnableProfiling(int hz) +{ + if(hz == 0) + hz = 100; + profilingTicks = profilingTicksReload = 16777216 / hz; + profSetHertz(hz); +} +#endif + + +inline int CPUUpdateTicks() +{ + int cpuLoopTicks = lcdTicks; + + if(soundTicks < cpuLoopTicks) + cpuLoopTicks = soundTicks; + + if(timer0On && (timer0Ticks < cpuLoopTicks)) { + cpuLoopTicks = timer0Ticks; + } + if(timer1On && !(TM1CNT & 4) && (timer1Ticks < cpuLoopTicks)) { + cpuLoopTicks = timer1Ticks; + } + if(timer2On && !(TM2CNT & 4) && (timer2Ticks < cpuLoopTicks)) { + cpuLoopTicks = timer2Ticks; + } + if(timer3On && !(TM3CNT & 4) && (timer3Ticks < cpuLoopTicks)) { + cpuLoopTicks = timer3Ticks; + } +#ifdef PROFILING + if(profilingTicksReload != 0) { + if(profilingTicks < cpuLoopTicks) { + cpuLoopTicks = profilingTicks; + } + } +#endif + + if (SWITicks) { + if (SWITicks < cpuLoopTicks) + cpuLoopTicks = SWITicks; + } + + if (IRQTicks) { + if (IRQTicks < cpuLoopTicks) + cpuLoopTicks = IRQTicks; + } + + return cpuLoopTicks; +} + +void CPUUpdateWindow0() +{ + int x00 = WIN0H>>8; + int x01 = WIN0H & 255; + + if(x00 <= x01) { + for(int i = 0; i < 240; i++) { + gfxInWin0[i] = (i >= x00 && i < x01); + } + } else { + for(int i = 0; i < 240; i++) { + gfxInWin0[i] = (i >= x00 || i < x01); + } + } +} + +void CPUUpdateWindow1() +{ + int x00 = WIN1H>>8; + int x01 = WIN1H & 255; + + if(x00 <= x01) { + for(int i = 0; i < 240; i++) { + gfxInWin1[i] = (i >= x00 && i < x01); + } + } else { + for(int i = 0; i < 240; i++) { + gfxInWin1[i] = (i >= x00 || i < x01); + } + } +} + +extern u32 line0[240]; +extern u32 line1[240]; +extern u32 line2[240]; +extern u32 line3[240]; + +#define CLEAR_ARRAY(a) \ + {\ + u32 *array = (a);\ + for(int i = 0; i < 240; i++) {\ + *array++ = 0x80000000;\ + }\ + }\ + +void CPUUpdateRenderBuffers(bool force) +{ + if(!(layerEnable & 0x0100) || force) { + CLEAR_ARRAY(line0); + } + if(!(layerEnable & 0x0200) || force) { + CLEAR_ARRAY(line1); + } + if(!(layerEnable & 0x0400) || force) { + CLEAR_ARRAY(line2); + } + if(!(layerEnable & 0x0800) || force) { + CLEAR_ARRAY(line3); + } +} + +#ifdef __LIBRETRO__ +#include + +unsigned int CPUWriteState(u8* data, unsigned size) +{ + uint8_t *orig = data; + + utilWriteIntMem(data, SAVE_GAME_VERSION); + utilWriteMem(data, &rom[0xa0], 16); + utilWriteIntMem(data, useBios); + utilWriteMem(data, ®[0], sizeof(reg)); + + utilWriteDataMem(data, saveGameStruct); + + utilWriteIntMem(data, stopState); + utilWriteIntMem(data, IRQTicks); + + utilWriteMem(data, internalRAM, 0x8000); + utilWriteMem(data, paletteRAM, 0x400); + utilWriteMem(data, workRAM, 0x40000); + utilWriteMem(data, vram, 0x20000); + utilWriteMem(data, oam, 0x400); + utilWriteMem(data, pix, 4 * 241 * 162); + utilWriteMem(data, ioMem, 0x400); + + eepromSaveGame(data); + flashSaveGame(data); + soundSaveGame(data); + rtcSaveGame(data); + + return (ptrdiff_t)data - (ptrdiff_t)orig; +} + +bool CPUWriteMemState(char *memory, int available) +{ + return false; +} +#else +static bool CPUWriteState(gzFile gzFile) +{ + utilWriteInt(gzFile, SAVE_GAME_VERSION); + + utilGzWrite(gzFile, &rom[0xa0], 16); + + utilWriteInt(gzFile, useBios); + + utilGzWrite(gzFile, ®[0], sizeof(reg)); + + utilWriteData(gzFile, saveGameStruct); + + // new to version 0.7.1 + utilWriteInt(gzFile, stopState); + // new to version 0.8 + utilWriteInt(gzFile, IRQTicks); + + utilGzWrite(gzFile, internalRAM, 0x8000); + utilGzWrite(gzFile, paletteRAM, 0x400); + utilGzWrite(gzFile, workRAM, 0x40000); + utilGzWrite(gzFile, vram, 0x20000); + utilGzWrite(gzFile, oam, 0x400); + utilGzWrite(gzFile, pix, 4*241*162); + utilGzWrite(gzFile, ioMem, 0x400); + + eepromSaveGame(gzFile); + flashSaveGame(gzFile); + soundSaveGame(gzFile); + + cheatsSaveGame(gzFile); + + // version 1.5 + rtcSaveGame(gzFile); + + return true; +} + +bool CPUWriteState(const char *file) +{ + gzFile gzFile = utilGzOpen(file, "wb"); + + if(gzFile == NULL) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), file); + return false; + } + + bool res = CPUWriteState(gzFile); + + utilGzClose(gzFile); + + return res; +} + + +bool CPUWriteMemState(char *memory, int available) +{ + gzFile gzFile = utilMemGzOpen(memory, available, "w"); + + if(gzFile == NULL) { + return false; + } + + bool res = CPUWriteState(gzFile); + + long pos = utilGzMemTell(gzFile)+8; + + if(pos >= (available)) + res = false; + + utilGzClose(gzFile); + + return res; +} +#endif + + +#ifdef __LIBRETRO__ +bool CPUReadState(const u8* data, unsigned size) +{ + // Don't really care about version. + int version = utilReadIntMem(data); + if (version != SAVE_GAME_VERSION) + return false; + + char romname[16]; + utilReadMem(romname, data, 16); + if (memcmp(&rom[0xa0], romname, 16) != 0) + return false; + + // Don't care about use bios ... + utilReadIntMem(data); + + utilReadMem(®[0], data, sizeof(reg)); + + utilReadDataMem(data, saveGameStruct); + + stopState = utilReadIntMem(data) ? true : false; + + IRQTicks = utilReadIntMem(data); + if (IRQTicks > 0) + intState = true; + else + { + intState = false; + IRQTicks = 0; + } + + utilReadMem(internalRAM, data, 0x8000); + utilReadMem(paletteRAM, data, 0x400); + utilReadMem(workRAM, data, 0x40000); + utilReadMem(vram, data, 0x20000); + utilReadMem(oam, data, 0x400); + utilReadMem(pix, data, 4*241*162); + utilReadMem(ioMem, data, 0x400); + + eepromReadGame(data, version); + flashReadGame(data, version); + soundReadGame(data, version); + rtcReadGame(data); + + //// Copypasta stuff ... + // set pointers! + layerEnable = layerSettings & DISPCNT; + + CPUUpdateRender(); + + // CPU Update Render Buffers set to true + CLEAR_ARRAY(line0); + CLEAR_ARRAY(line1); + CLEAR_ARRAY(line2); + CLEAR_ARRAY(line3); + // End of CPU Update Render Buffers set to true + + CPUUpdateWindow0(); + CPUUpdateWindow1(); + gbaSaveType = 0; + switch(saveType) { + case 0: + cpuSaveGameFunc = flashSaveDecide; + break; + case 1: + cpuSaveGameFunc = sramWrite; + gbaSaveType = 1; + break; + case 2: + cpuSaveGameFunc = flashWrite; + gbaSaveType = 2; + break; + case 3: + break; + case 5: + gbaSaveType = 5; + break; + default: +#ifdef CELL_VBA_DEBUG + systemMessage(MSG_UNSUPPORTED_SAVE_TYPE, + N_("Unsupported save type %d"), saveType); +#endif + break; + } + if(eepromInUse) + gbaSaveType = 3; + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + if(armState) { + ARM_PREFETCH; + } else { + THUMB_PREFETCH; + } + + CPUUpdateRegister(0x204, CPUReadHalfWordQuick(0x4000204)); + + return true; +} +#else +static bool CPUReadState(gzFile gzFile) +{ + int version = utilReadInt(gzFile); + + if(version > SAVE_GAME_VERSION || version < SAVE_GAME_VERSION_1) { + systemMessage(MSG_UNSUPPORTED_VBA_SGM, + N_("Unsupported VisualBoyAdvance save game version %d"), + version); + return false; + } + + u8 romname[17]; + + utilGzRead(gzFile, romname, 16); + + if(memcmp(&rom[0xa0], romname, 16) != 0) { + romname[16]=0; + for(int i = 0; i < 16; i++) + if(romname[i] < 32) + romname[i] = 32; + systemMessage(MSG_CANNOT_LOAD_SGM, N_("Cannot load save game for %s"), romname); + return false; + } + + bool ub = utilReadInt(gzFile) ? true : false; + + if(ub != useBios) { + if(useBios) + systemMessage(MSG_SAVE_GAME_NOT_USING_BIOS, + N_("Save game is not using the BIOS files")); + else + systemMessage(MSG_SAVE_GAME_USING_BIOS, + N_("Save game is using the BIOS file")); + return false; + } + + utilGzRead(gzFile, ®[0], sizeof(reg)); + + utilReadData(gzFile, saveGameStruct); + + if(version < SAVE_GAME_VERSION_3) + stopState = false; + else + stopState = utilReadInt(gzFile) ? true : false; + + if(version < SAVE_GAME_VERSION_4) + { + IRQTicks = 0; + intState = false; + } + else + { + IRQTicks = utilReadInt(gzFile); + if (IRQTicks>0) + intState = true; + else + { + intState = false; + IRQTicks = 0; + } + } + + utilGzRead(gzFile, internalRAM, 0x8000); + utilGzRead(gzFile, paletteRAM, 0x400); + utilGzRead(gzFile, workRAM, 0x40000); + utilGzRead(gzFile, vram, 0x20000); + utilGzRead(gzFile, oam, 0x400); + if(version < SAVE_GAME_VERSION_6) + utilGzRead(gzFile, pix, 4*240*160); + else + utilGzRead(gzFile, pix, 4*241*162); + utilGzRead(gzFile, ioMem, 0x400); + + if(skipSaveGameBattery) { + // skip eeprom data + eepromReadGameSkip(gzFile, version); + // skip flash data + flashReadGameSkip(gzFile, version); + } else { + eepromReadGame(gzFile, version); + flashReadGame(gzFile, version); + } + soundReadGame(gzFile, version); + + if(version > SAVE_GAME_VERSION_1) { + if(skipSaveGameCheats) { + // skip cheats list data + cheatsReadGameSkip(gzFile, version); + } else { + cheatsReadGame(gzFile, version); + } + } + if(version > SAVE_GAME_VERSION_6) { + rtcReadGame(gzFile); + } + + if(version <= SAVE_GAME_VERSION_7) { + u32 temp; +#define SWAP(a,b,c) \ + temp = (a);\ + (a) = (b)<<16|(c);\ + (b) = (temp) >> 16;\ + (c) = (temp) & 0xFFFF; + + SWAP(dma0Source, DM0SAD_H, DM0SAD_L); + SWAP(dma0Dest, DM0DAD_H, DM0DAD_L); + SWAP(dma1Source, DM1SAD_H, DM1SAD_L); + SWAP(dma1Dest, DM1DAD_H, DM1DAD_L); + SWAP(dma2Source, DM2SAD_H, DM2SAD_L); + SWAP(dma2Dest, DM2DAD_H, DM2DAD_L); + SWAP(dma3Source, DM3SAD_H, DM3SAD_L); + SWAP(dma3Dest, DM3DAD_H, DM3DAD_L); + } + + if(version <= SAVE_GAME_VERSION_8) { + timer0ClockReload = TIMER_TICKS[TM0CNT & 3]; + timer1ClockReload = TIMER_TICKS[TM1CNT & 3]; + timer2ClockReload = TIMER_TICKS[TM2CNT & 3]; + timer3ClockReload = TIMER_TICKS[TM3CNT & 3]; + + timer0Ticks = ((0x10000 - TM0D) << timer0ClockReload) - timer0Ticks; + timer1Ticks = ((0x10000 - TM1D) << timer1ClockReload) - timer1Ticks; + timer2Ticks = ((0x10000 - TM2D) << timer2ClockReload) - timer2Ticks; + timer3Ticks = ((0x10000 - TM3D) << timer3ClockReload) - timer3Ticks; + interp_rate(); + } + + // set pointers! + layerEnable = layerSettings & DISPCNT; + + CPUUpdateRender(); + CPUUpdateRenderBuffers(true); + CPUUpdateWindow0(); + CPUUpdateWindow1(); + gbaSaveType = 0; + switch(saveType) { + case 0: + cpuSaveGameFunc = flashSaveDecide; + break; + case 1: + cpuSaveGameFunc = sramWrite; + gbaSaveType = 1; + break; + case 2: + cpuSaveGameFunc = flashWrite; + gbaSaveType = 2; + break; + case 3: + break; + case 5: + gbaSaveType = 5; + break; + default: + systemMessage(MSG_UNSUPPORTED_SAVE_TYPE, + N_("Unsupported save type %d"), saveType); + break; + } + if(eepromInUse) + gbaSaveType = 3; + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + if(armState) { + ARM_PREFETCH; + } else { + THUMB_PREFETCH; + } + + CPUUpdateRegister(0x204, CPUReadHalfWordQuick(0x4000204)); + + return true; +} + +bool CPUReadMemState(char *memory, int available) +{ + gzFile gzFile = utilMemGzOpen(memory, available, "r"); + + bool res = CPUReadState(gzFile); + + utilGzClose(gzFile); + + return res; +} + +bool CPUReadState(const char * file) +{ + gzFile gzFile = utilGzOpen(file, "rb"); + + if(gzFile == NULL) + return false; + + bool res = CPUReadState(gzFile); + + utilGzClose(gzFile); + + return res; +} +#endif + +bool CPUExportEepromFile(const char *fileName) +{ + if(eepromInUse) { + FILE *file = fopen(fileName, "wb"); + + if(!file) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), + fileName); + return false; + } + + for(int i = 0; i < eepromSize;) { + for(int j = 0; j < 8; j++) { + if(fwrite(&eepromData[i+7-j], 1, 1, file) != 1) { + fclose(file); + return false; + } + } + i += 8; + } + fclose(file); + } + return true; +} + +bool CPUWriteBatteryFile(const char *fileName) +{ + if(gbaSaveType == 0) { + if(eepromInUse) + gbaSaveType = 3; + else switch(saveType) { + case 1: + gbaSaveType = 1; + break; + case 2: + gbaSaveType = 2; + break; + } + } + if((gbaSaveType) && (gbaSaveType!=5)) { + FILE *file = fopen(fileName, "wb"); + + if(!file) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), + fileName); + return false; + } + + // only save if Flash/Sram in use or EEprom in use + if(gbaSaveType != 3) { + if(gbaSaveType == 2) { + if(fwrite(flashSaveMemory, 1, flashSize, file) != (size_t)flashSize) { + fclose(file); + return false; + } + } else { + if(fwrite(flashSaveMemory, 1, 0x10000, file) != 0x10000) { + fclose(file); + return false; + } + } + } else { + if(fwrite(eepromData, 1, eepromSize, file) != (size_t)eepromSize) { + fclose(file); + return false; + } + } + fclose(file); + } + return true; +} + +bool CPUReadGSASnapshot(const char *fileName) +{ + int i; + FILE *file = fopen(fileName, "rb"); + + if(!file) { + systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName); + return false; + } + + // check file size to know what we should read + fseek(file, 0, SEEK_END); + + // long size = ftell(file); + fseek(file, 0x0, SEEK_SET); + fread(&i, 1, 4, file); + fseek(file, i, SEEK_CUR); // Skip SharkPortSave +// fseek(file, 4, SEEK_CUR); // skip some sort of flag + fread(&i, 1, 4, file); // name length + fseek(file, i, SEEK_CUR); // skip name + fread(&i, 1, 4, file); // desc length + fseek(file, i, SEEK_CUR); // skip desc + fread(&i, 1, 4, file); // notes length + fseek(file, i, SEEK_CUR); // skip notes + int saveSize; + fread(&saveSize, 1, 4, file); // read length + saveSize -= 0x1c; // remove header size + char buffer[17]; + char buffer2[17]; + fread(buffer, 1, 16, file); + buffer[16] = 0; + for(i = 0; i < 16; i++) + if(buffer[i] < 32) + buffer[i] = 32; + memcpy(buffer2, &rom[0xa0], 16); + buffer2[16] = 0; + for(i = 0; i < 16; i++) + if(buffer2[i] < 32) + buffer2[i] = 32; + if(memcmp(buffer, buffer2, 16)) { + systemMessage(MSG_CANNOT_IMPORT_SNAPSHOT_FOR, + N_("Cannot import snapshot for %s. Current game is %s"), + buffer, + buffer2); + fclose(file); + return false; + } + fseek(file, 12, SEEK_CUR); // skip some flags + if(saveSize >= 65536) { + if(fread(flashSaveMemory, 1, saveSize, file) != (size_t)saveSize) { + fclose(file); + return false; + } + } else { + systemMessage(MSG_UNSUPPORTED_SNAPSHOT_FILE, + N_("Unsupported snapshot file %s"), + fileName); + fclose(file); + return false; + } + fclose(file); + CPUReset(); + return true; +} + +bool CPUReadGSASPSnapshot(const char *fileName) +{ + const char gsvfooter[] = "xV4\x12"; + const size_t namepos=0x0c, namesz=12; + const size_t footerpos=0x42c, footersz=4; + + char footer[footersz+1], romname[namesz+1], savename[namesz+1];; + FILE *file = fopen(fileName, "rb"); + + if(!file) { + systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName); + return false; + } + + // read save name + fseek(file, namepos, SEEK_SET); + fread(savename, 1, namesz, file); + savename[namesz] = 0; + + memcpy(romname, &rom[0xa0], namesz); + romname[namesz] = 0; + + if(memcmp(romname, savename, namesz)) { + systemMessage(MSG_CANNOT_IMPORT_SNAPSHOT_FOR, + N_("Cannot import snapshot for %s. Current game is %s"), + savename, + romname); + fclose(file); + return false; + } + + // read footer tag + fseek(file, footerpos, SEEK_SET); + fread(footer, 1, footersz, file); + footer[footersz] = 0; + + if(memcmp(footer, gsvfooter, footersz)) { + systemMessage(0, + N_("Unsupported snapshot file %s. Footer '%s' at %u should be '%s'"), + fileName, + footer, + footerpos, + gsvfooter); + fclose(file); + return false; + } + + // Read up to 128k save + fread(flashSaveMemory, 1, FLASH_128K_SZ, file); + + fclose(file); + CPUReset(); + return true; +} + + +bool CPUWriteGSASnapshot(const char *fileName, + const char *title, + const char *desc, + const char *notes) +{ + FILE *file = fopen(fileName, "wb"); + + if(!file) { + systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName); + return false; + } + + u8 buffer[17]; + + utilPutDword(buffer, 0x0d); // SharkPortSave length + fwrite(buffer, 1, 4, file); + fwrite("SharkPortSave", 1, 0x0d, file); + utilPutDword(buffer, 0x000f0000); + fwrite(buffer, 1, 4, file); // save type 0x000f0000 = GBA save + utilPutDword(buffer, (u32)strlen(title)); + fwrite(buffer, 1, 4, file); // title length + fwrite(title, 1, strlen(title), file); + utilPutDword(buffer, (u32)strlen(desc)); + fwrite(buffer, 1, 4, file); // desc length + fwrite(desc, 1, strlen(desc), file); + utilPutDword(buffer, (u32)strlen(notes)); + fwrite(buffer, 1, 4, file); // notes length + fwrite(notes, 1, strlen(notes), file); + int saveSize = 0x10000; + if(gbaSaveType == 2) + saveSize = flashSize; + int totalSize = saveSize + 0x1c; + + utilPutDword(buffer, totalSize); // length of remainder of save - CRC + fwrite(buffer, 1, 4, file); + + char *temp = new char[0x2001c]; + memset(temp, 0, 28); + memcpy(temp, &rom[0xa0], 16); // copy internal name + temp[0x10] = rom[0xbe]; // reserved area (old checksum) + temp[0x11] = rom[0xbf]; // reserved area (old checksum) + temp[0x12] = rom[0xbd]; // complement check + temp[0x13] = rom[0xb0]; // maker + temp[0x14] = 1; // 1 save ? + memcpy(&temp[0x1c], flashSaveMemory, saveSize); // copy save + fwrite(temp, 1, totalSize, file); // write save + header + u32 crc = 0; + + for(int i = 0; i < totalSize; i++) { + crc += ((u32)temp[i] << (crc % 0x18)); + } + + utilPutDword(buffer, crc); + fwrite(buffer, 1, 4, file); // CRC? + + fclose(file); + delete [] temp; + return true; +} + +bool CPUImportEepromFile(const char *fileName) +{ + FILE *file = fopen(fileName, "rb"); + + if(!file) + return false; + + // check file size to know what we should read + fseek(file, 0, SEEK_END); + + long size = ftell(file); + fseek(file, 0, SEEK_SET); + if(size == 512 || size == 0x2000) { + if(fread(eepromData, 1, size, file) != (size_t)size) { + fclose(file); + return false; + } + for(int i = 0; i < size;) { + u8 tmp = eepromData[i]; + eepromData[i] = eepromData[7-i]; + eepromData[7-i] = tmp; + i++; + tmp = eepromData[i]; + eepromData[i] = eepromData[7-i]; + eepromData[7-i] = tmp; + i++; + tmp = eepromData[i]; + eepromData[i] = eepromData[7-i]; + eepromData[7-i] = tmp; + i++; + tmp = eepromData[i]; + eepromData[i] = eepromData[7-i]; + eepromData[7-i] = tmp; + i++; + i += 4; + } + } else { + fclose(file); + return false; + } + fclose(file); + return true; +} + +bool CPUReadBatteryFile(const char *fileName) +{ + FILE *file = fopen(fileName, "rb"); + + if(!file) + return false; + + // check file size to know what we should read + fseek(file, 0, SEEK_END); + + long size = ftell(file); + fseek(file, 0, SEEK_SET); + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + if(size == 512 || size == 0x2000) { + if(fread(eepromData, 1, size, file) != (size_t)size) { + fclose(file); + return false; + } + } else { + if(size == 0x20000) { + if(fread(flashSaveMemory, 1, 0x20000, file) != 0x20000) { + fclose(file); + return false; + } + flashSetSize(0x20000); + } else { + if(fread(flashSaveMemory, 1, 0x10000, file) != 0x10000) { + fclose(file); + return false; + } + flashSetSize(0x10000); + } + } + fclose(file); + return true; +} + +bool CPUWritePNGFile(const char *fileName) +{ + return utilWritePNGFile(fileName, 240, 160, pix); +} + +bool CPUWriteBMPFile(const char *fileName) +{ + return utilWriteBMPFile(fileName, 240, 160, pix); +} + +bool CPUIsZipFile(const char * file) +{ + if(strlen(file) > 4) { + const char * p = strrchr(file,'.'); + + if(p != NULL) { + if(_stricmp(p, ".zip") == 0) + return true; + } + } + + return false; +} + +bool CPUIsGBAImage(const char * file) +{ + cpuIsMultiBoot = false; + if(strlen(file) > 4) { + const char * p = strrchr(file,'.'); + + if(p != NULL) { + if(_stricmp(p, ".gba") == 0) + return true; + if(_stricmp(p, ".agb") == 0) + return true; + if(_stricmp(p, ".bin") == 0) + return true; + if(_stricmp(p, ".elf") == 0) + return true; + if(_stricmp(p, ".mb") == 0) { + cpuIsMultiBoot = true; + return true; + } + } + } + + return false; +} + +bool CPUIsGBABios(const char * file) +{ + if(strlen(file) > 4) { + const char * p = strrchr(file,'.'); + + if(p != NULL) { + if(_stricmp(p, ".gba") == 0) + return true; + if(_stricmp(p, ".agb") == 0) + return true; + if(_stricmp(p, ".bin") == 0) + return true; + if(_stricmp(p, ".bios") == 0) + return true; + if(_stricmp(p, ".rom") == 0) + return true; + } + } + + return false; +} + +bool CPUIsELF(const char *file) +{ + if(file == NULL) + return false; + + if(strlen(file) > 4) { + const char * p = strrchr(file,'.'); + + if(p != NULL) { + if(_stricmp(p, ".elf") == 0) + return true; + } + } + return false; +} + +void CPUCleanUp() +{ +#ifdef PROFILING + if(profilingTicksReload) { + profCleanup(); + } +#endif + + if(rom != NULL) { + free(rom); + rom = NULL; + } + + if(vram != NULL) { + free(vram); + vram = NULL; + } + + if(paletteRAM != NULL) { + free(paletteRAM); + paletteRAM = NULL; + } + + if(internalRAM != NULL) { + free(internalRAM); + internalRAM = NULL; + } + + if(workRAM != NULL) { + free(workRAM); + workRAM = NULL; + } + + if(bios != NULL) { + free(bios); + bios = NULL; + } + + if(pix != NULL) { + free(pix); + pix = NULL; + } + + if(oam != NULL) { + free(oam); + oam = NULL; + } + + if(ioMem != NULL) { + free(ioMem); + ioMem = NULL; + } + +#ifndef NO_DEBUGGER + elfCleanUp(); +#endif //NO_DEBUGGER + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + emulating = 0; +} + +int CPULoadRom(const char *szFile) +{ + romSize = 0x2000000; + if(rom != NULL) { + CPUCleanUp(); + } + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + rom = (u8 *)malloc(0x2000000); + if(rom == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "ROM"); + return 0; + } + workRAM = (u8 *)calloc(1, 0x40000); + if(workRAM == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "WRAM"); + return 0; + } + + u8 *whereToLoad = cpuIsMultiBoot ? workRAM : rom; + +#ifndef NO_DEBUGGER + if(CPUIsELF(szFile)) { + FILE *f = fopen(szFile, "rb"); + if(!f) { + systemMessage(MSG_ERROR_OPENING_IMAGE, N_("Error opening image %s"), + szFile); + free(rom); + rom = NULL; + free(workRAM); + workRAM = NULL; + return 0; + } + bool res = elfRead(szFile, romSize, f); + if(!res || romSize == 0) { + free(rom); + rom = NULL; + free(workRAM); + workRAM = NULL; + elfCleanUp(); + return 0; + } + } else +#endif //NO_DEBUGGER + if(szFile!=NULL) + { + if(!utilLoad(szFile, + utilIsGBAImage, + whereToLoad, + romSize)) { + free(rom); + rom = NULL; + free(workRAM); + workRAM = NULL; + return 0; + } + } + + u16 *temp = (u16 *)(rom+((romSize+1)&~1)); + int i; + for(i = (romSize+1)&~1; i < 0x2000000; i+=2) { + WRITE16LE(temp, (i >> 1) & 0xFFFF); + temp++; + } + + bios = (u8 *)calloc(1,0x4000); + if(bios == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "BIOS"); + CPUCleanUp(); + return 0; + } + internalRAM = (u8 *)calloc(1,0x8000); + if(internalRAM == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "IRAM"); + CPUCleanUp(); + return 0; + } + paletteRAM = (u8 *)calloc(1,0x400); + if(paletteRAM == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "PRAM"); + CPUCleanUp(); + return 0; + } + vram = (u8 *)calloc(1, 0x20000); + if(vram == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "VRAM"); + CPUCleanUp(); + return 0; + } + oam = (u8 *)calloc(1, 0x400); + if(oam == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "OAM"); + CPUCleanUp(); + return 0; + } + pix = (u8 *)calloc(1, 4 * 241 * 162); + if(pix == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "PIX"); + CPUCleanUp(); + return 0; + } + ioMem = (u8 *)calloc(1, 0x400); + if(ioMem == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "IO"); + CPUCleanUp(); + return 0; + } + + flashInit(); + eepromInit(); + + CPUUpdateRenderBuffers(true); + + return romSize; +} + +void doMirroring (bool b) +{ + u32 mirroredRomSize = (((romSize)>>20) & 0x3F)<<20; + u32 mirroredRomAddress = romSize; + if ((mirroredRomSize <=0x800000) && (b)) + { + mirroredRomAddress = mirroredRomSize; + if (mirroredRomSize==0) + mirroredRomSize=0x100000; + while (mirroredRomAddress<0x01000000) + { + memcpy ((u16 *)(rom+mirroredRomAddress), (u16 *)(rom), mirroredRomSize); + mirroredRomAddress+=mirroredRomSize; + } + } +} + +void CPUUpdateRender() +{ + switch(DISPCNT & 7) { + case 0: + if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode0RenderLine; + else if(fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode0RenderLineNoWindow; + else + renderLine = mode0RenderLineAll; + break; + case 1: + if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode1RenderLine; + else if(fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode1RenderLineNoWindow; + else + renderLine = mode1RenderLineAll; + break; + case 2: + if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode2RenderLine; + else if(fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode2RenderLineNoWindow; + else + renderLine = mode2RenderLineAll; + break; + case 3: + if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode3RenderLine; + else if(fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode3RenderLineNoWindow; + else + renderLine = mode3RenderLineAll; + break; + case 4: + if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode4RenderLine; + else if(fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode4RenderLineNoWindow; + else + renderLine = mode4RenderLineAll; + break; + case 5: + if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode5RenderLine; + else if(fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode5RenderLineNoWindow; + else + renderLine = mode5RenderLineAll; + default: + break; + } +} + +void CPUUpdateCPSR() +{ + u32 CPSR = reg[16].I & 0x40; + if(N_FLAG) + CPSR |= 0x80000000; + if(Z_FLAG) + CPSR |= 0x40000000; + if(C_FLAG) + CPSR |= 0x20000000; + if(V_FLAG) + CPSR |= 0x10000000; + if(!armState) + CPSR |= 0x00000020; + if(!armIrqEnable) + CPSR |= 0x80; + CPSR |= (armMode & 0x1F); + reg[16].I = CPSR; +} + +void CPUUpdateFlags(bool breakLoop) +{ + u32 CPSR = reg[16].I; + + N_FLAG = (CPSR & 0x80000000) ? true: false; + Z_FLAG = (CPSR & 0x40000000) ? true: false; + C_FLAG = (CPSR & 0x20000000) ? true: false; + V_FLAG = (CPSR & 0x10000000) ? true: false; + armState = (CPSR & 0x20) ? false : true; + armIrqEnable = (CPSR & 0x80) ? false : true; + if(breakLoop) { + if (armIrqEnable && (IF & IE) && (IME & 1)) + cpuNextEvent = cpuTotalTicks; + } +} + +void CPUUpdateFlags() +{ + CPUUpdateFlags(true); +} + +#ifdef WORDS_BIGENDIAN +static void CPUSwap(volatile u32 *a, volatile u32 *b) +{ + volatile u32 c = *b; + *b = *a; + *a = c; +} +#else +static void CPUSwap(u32 *a, u32 *b) +{ + u32 c = *b; + *b = *a; + *a = c; +} +#endif + +void CPUSwitchMode(int mode, bool saveState, bool breakLoop) +{ + // if(armMode == mode) + // return; + + CPUUpdateCPSR(); + + switch(armMode) { + case 0x10: + case 0x1F: + reg[R13_USR].I = reg[13].I; + reg[R14_USR].I = reg[14].I; + reg[17].I = reg[16].I; + break; + case 0x11: + CPUSwap(®[R8_FIQ].I, ®[8].I); + CPUSwap(®[R9_FIQ].I, ®[9].I); + CPUSwap(®[R10_FIQ].I, ®[10].I); + CPUSwap(®[R11_FIQ].I, ®[11].I); + CPUSwap(®[R12_FIQ].I, ®[12].I); + reg[R13_FIQ].I = reg[13].I; + reg[R14_FIQ].I = reg[14].I; + reg[SPSR_FIQ].I = reg[17].I; + break; + case 0x12: + reg[R13_IRQ].I = reg[13].I; + reg[R14_IRQ].I = reg[14].I; + reg[SPSR_IRQ].I = reg[17].I; + break; + case 0x13: + reg[R13_SVC].I = reg[13].I; + reg[R14_SVC].I = reg[14].I; + reg[SPSR_SVC].I = reg[17].I; + break; + case 0x17: + reg[R13_ABT].I = reg[13].I; + reg[R14_ABT].I = reg[14].I; + reg[SPSR_ABT].I = reg[17].I; + break; + case 0x1b: + reg[R13_UND].I = reg[13].I; + reg[R14_UND].I = reg[14].I; + reg[SPSR_UND].I = reg[17].I; + break; + } + + u32 CPSR = reg[16].I; + u32 SPSR = reg[17].I; + + switch(mode) { + case 0x10: + case 0x1F: + reg[13].I = reg[R13_USR].I; + reg[14].I = reg[R14_USR].I; + reg[16].I = SPSR; + break; + case 0x11: + CPUSwap(®[8].I, ®[R8_FIQ].I); + CPUSwap(®[9].I, ®[R9_FIQ].I); + CPUSwap(®[10].I, ®[R10_FIQ].I); + CPUSwap(®[11].I, ®[R11_FIQ].I); + CPUSwap(®[12].I, ®[R12_FIQ].I); + reg[13].I = reg[R13_FIQ].I; + reg[14].I = reg[R14_FIQ].I; + if(saveState) + reg[17].I = CPSR; + else + reg[17].I = reg[SPSR_FIQ].I; + break; + case 0x12: + reg[13].I = reg[R13_IRQ].I; + reg[14].I = reg[R14_IRQ].I; + reg[16].I = SPSR; + if(saveState) + reg[17].I = CPSR; + else + reg[17].I = reg[SPSR_IRQ].I; + break; + case 0x13: + reg[13].I = reg[R13_SVC].I; + reg[14].I = reg[R14_SVC].I; + reg[16].I = SPSR; + if(saveState) + reg[17].I = CPSR; + else + reg[17].I = reg[SPSR_SVC].I; + break; + case 0x17: + reg[13].I = reg[R13_ABT].I; + reg[14].I = reg[R14_ABT].I; + reg[16].I = SPSR; + if(saveState) + reg[17].I = CPSR; + else + reg[17].I = reg[SPSR_ABT].I; + break; + case 0x1b: + reg[13].I = reg[R13_UND].I; + reg[14].I = reg[R14_UND].I; + reg[16].I = SPSR; + if(saveState) + reg[17].I = CPSR; + else + reg[17].I = reg[SPSR_UND].I; + break; + default: + systemMessage(MSG_UNSUPPORTED_ARM_MODE, N_("Unsupported ARM mode %02x"), mode); + break; + } + armMode = mode; + CPUUpdateFlags(breakLoop); + CPUUpdateCPSR(); +} + +void CPUSwitchMode(int mode, bool saveState) +{ + CPUSwitchMode(mode, saveState, true); +} + +void CPUUndefinedException() +{ + u32 PC = reg[15].I; + bool savedArmState = armState; + CPUSwitchMode(0x1b, true, false); + reg[14].I = PC - (savedArmState ? 4 : 2); + reg[15].I = 0x04; + armState = true; + armIrqEnable = false; + armNextPC = 0x04; + ARM_PREFETCH; + reg[15].I += 4; +} + +void CPUSoftwareInterrupt() +{ + u32 PC = reg[15].I; + bool savedArmState = armState; + CPUSwitchMode(0x13, true, false); + reg[14].I = PC - (savedArmState ? 4 : 2); + reg[15].I = 0x08; + armState = true; + armIrqEnable = false; + armNextPC = 0x08; + ARM_PREFETCH; + reg[15].I += 4; +} + +void CPUSoftwareInterrupt(int comment) +{ + static bool disableMessage = false; + if(armState) comment >>= 16; +#ifdef BKPT_SUPPORT + if(comment == 0xff) { + dbgOutput(NULL, reg[0].I); + return; + } +#endif +#ifdef PROFILING + if(comment == 0xfe) { + profStartup(reg[0].I, reg[1].I); + return; + } + if(comment == 0xfd) { + profControl(reg[0].I); + return; + } + if(comment == 0xfc) { + profCleanup(); + return; + } + if(comment == 0xfb) { + profCount(); + return; + } +#endif + if(comment == 0xfa) { + agbPrintFlush(); + return; + } +#ifdef SDL + if(comment == 0xf9) { + emulating = 0; + cpuNextEvent = cpuTotalTicks; + cpuBreakLoop = true; + return; + } +#endif + if(useBios) { +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("SWI: %08x at %08x (0x%08x,0x%08x,0x%08x,VCOUNT = %2d)\n", comment, + armState ? armNextPC - 4: armNextPC -2, + reg[0].I, + reg[1].I, + reg[2].I, + VCOUNT); + } +#endif + CPUSoftwareInterrupt(); + return; + } + // This would be correct, but it causes problems if uncommented + // else { + // biosProtected = 0xe3a02004; + // } + + switch(comment) { + case 0x00: + BIOS_SoftReset(); + ARM_PREFETCH; + break; + case 0x01: + BIOS_RegisterRamReset(); + break; + case 0x02: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + /*log("Halt: (VCOUNT = %2d)\n", + VCOUNT);*/ + } +#endif + holdState = true; + holdType = -1; + cpuNextEvent = cpuTotalTicks; + break; + case 0x03: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + /*log("Stop: (VCOUNT = %2d)\n", + VCOUNT);*/ + } +#endif + holdState = true; + holdType = -1; + stopState = true; + cpuNextEvent = cpuTotalTicks; + break; + case 0x04: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("IntrWait: 0x%08x,0x%08x (VCOUNT = %2d)\n", + reg[0].I, + reg[1].I, + VCOUNT); + } +#endif + CPUSoftwareInterrupt(); + break; + case 0x05: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("VBlankIntrWait: (VCOUNT = %2d)\n", + VCOUNT); + } +#endif + CPUSoftwareInterrupt(); + break; + case 0x06: + CPUSoftwareInterrupt(); + break; + case 0x07: + CPUSoftwareInterrupt(); + break; + case 0x08: + BIOS_Sqrt(); + break; + case 0x09: + BIOS_ArcTan(); + break; + case 0x0A: + BIOS_ArcTan2(); + break; + case 0x0B: + { + int len = (reg[2].I & 0x1FFFFF) >>1; + if (!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + len) & 0xe000000) == 0)) + { + if ((reg[2].I >> 24) & 1) + { + if ((reg[2].I >> 26) & 1) + SWITicks = (7 + memoryWait32[(reg[1].I>>24) & 0xF]) * (len>>1); + else + SWITicks = (8 + memoryWait[(reg[1].I>>24) & 0xF]) * (len); + } + else + { + if ((reg[2].I >> 26) & 1) + SWITicks = (10 + memoryWait32[(reg[0].I>>24) & 0xF] + + memoryWait32[(reg[1].I>>24) & 0xF]) * (len>>1); + else + SWITicks = (11 + memoryWait[(reg[0].I>>24) & 0xF] + + memoryWait[(reg[1].I>>24) & 0xF]) * len; + } + } + } + BIOS_CpuSet(); + break; + case 0x0C: + { + int len = (reg[2].I & 0x1FFFFF) >>5; + if (!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + len) & 0xe000000) == 0)) + { + if ((reg[2].I >> 24) & 1) + SWITicks = (6 + memoryWait32[(reg[1].I>>24) & 0xF] + + 7 * (memoryWaitSeq32[(reg[1].I>>24) & 0xF] + 1)) * len; + else + SWITicks = (9 + memoryWait32[(reg[0].I>>24) & 0xF] + + memoryWait32[(reg[1].I>>24) & 0xF] + + 7 * (memoryWaitSeq32[(reg[0].I>>24) & 0xF] + + memoryWaitSeq32[(reg[1].I>>24) & 0xF] + 2)) * len; + } + } + BIOS_CpuFastSet(); + break; + case 0x0D: + BIOS_GetBiosChecksum(); + break; + case 0x0E: + BIOS_BgAffineSet(); + break; + case 0x0F: + BIOS_ObjAffineSet(); + break; + case 0x10: + { + int len = CPUReadHalfWord(reg[2].I); + if (!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + len) & 0xe000000) == 0)) + SWITicks = (32 + memoryWait[(reg[0].I>>24) & 0xF]) * len; + } + BIOS_BitUnPack(); + break; + case 0x11: + { + u32 len = CPUReadMemory(reg[0].I) >> 8; + if(!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) + SWITicks = (9 + memoryWait[(reg[1].I>>24) & 0xF]) * len; + } + BIOS_LZ77UnCompWram(); + break; + case 0x12: + { + u32 len = CPUReadMemory(reg[0].I) >> 8; + if(!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) + SWITicks = (19 + memoryWait[(reg[1].I>>24) & 0xF]) * len; + } + BIOS_LZ77UnCompVram(); + break; + case 0x13: + { + u32 len = CPUReadMemory(reg[0].I) >> 8; + if(!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) + SWITicks = (29 + (memoryWait[(reg[0].I>>24) & 0xF]<<1)) * len; + } + BIOS_HuffUnComp(); + break; + case 0x14: + { + u32 len = CPUReadMemory(reg[0].I) >> 8; + if(!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) + SWITicks = (11 + memoryWait[(reg[0].I>>24) & 0xF] + + memoryWait[(reg[1].I>>24) & 0xF]) * len; + } + BIOS_RLUnCompWram(); + break; + case 0x15: + { + u32 len = CPUReadMemory(reg[0].I) >> 9; + if(!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) + SWITicks = (34 + (memoryWait[(reg[0].I>>24) & 0xF] << 1) + + memoryWait[(reg[1].I>>24) & 0xF]) * len; + } + BIOS_RLUnCompVram(); + break; + case 0x16: + { + u32 len = CPUReadMemory(reg[0].I) >> 8; + if(!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) + SWITicks = (13 + memoryWait[(reg[0].I>>24) & 0xF] + + memoryWait[(reg[1].I>>24) & 0xF]) * len; + } + BIOS_Diff8bitUnFilterWram(); + break; + case 0x17: + { + u32 len = CPUReadMemory(reg[0].I) >> 9; + if(!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) + SWITicks = (39 + (memoryWait[(reg[0].I>>24) & 0xF]<<1) + + memoryWait[(reg[1].I>>24) & 0xF]) * len; + } + BIOS_Diff8bitUnFilterVram(); + break; + case 0x18: + { + u32 len = CPUReadMemory(reg[0].I) >> 9; + if(!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) + SWITicks = (13 + memoryWait[(reg[0].I>>24) & 0xF] + + memoryWait[(reg[1].I>>24) & 0xF]) * len; + } + BIOS_Diff16bitUnFilter(); + break; + case 0x19: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("SoundBiasSet: 0x%08x (VCOUNT = %2d)\n", + reg[0].I, + VCOUNT); + } +#endif + if(reg[0].I) + soundPause(); + else + soundResume(); + break; + case 0x1F: + BIOS_MidiKey2Freq(); + break; + case 0x2A: + BIOS_SndDriverJmpTableCopy(); + // let it go, because we don't really emulate this function + default: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("SWI: %08x at %08x (0x%08x,0x%08x,0x%08x,VCOUNT = %2d)\n", comment, + armState ? armNextPC - 4: armNextPC -2, + reg[0].I, + reg[1].I, + reg[2].I, + VCOUNT); + } +#endif + + if(!disableMessage) { + systemMessage(MSG_UNSUPPORTED_BIOS_FUNCTION, + N_("Unsupported BIOS function %02x called from %08x. A BIOS file is needed in order to get correct behaviour."), + comment, + armMode ? armNextPC - 4: armNextPC - 2); + disableMessage = true; + } + break; + } +} + +void CPUCompareVCOUNT() +{ + if(VCOUNT == (DISPSTAT >> 8)) { + DISPSTAT |= 4; + UPDATE_REG(0x04, DISPSTAT); + + if(DISPSTAT & 0x20) { + IF |= 4; + UPDATE_REG(0x202, IF); + } + } else { + DISPSTAT &= 0xFFFB; + UPDATE_REG(0x4, DISPSTAT); + } + if (layerEnableDelay>0) + { + layerEnableDelay--; + if (layerEnableDelay==1) + layerEnable = layerSettings & DISPCNT; + } + +} + +void doDMA(u32 &s, u32 &d, u32 si, u32 di, u32 c, int transfer32) +{ + int sm = s >> 24; + int dm = d >> 24; + int sw = 0; + int dw = 0; + int sc = c; + + cpuDmaHack = true; + cpuDmaCount = c; + // This is done to get the correct waitstates. + if (sm>15) + sm=15; + if (dm>15) + dm=15; + + //if ((sm>=0x05) && (sm<=0x07) || (dm>=0x05) && (dm <=0x07)) + // blank = (((DISPSTAT | ((DISPSTAT>>1)&1))==1) ? true : false); + + if(transfer32) { + s &= 0xFFFFFFFC; + if(s < 0x02000000 && (reg[15].I >> 24)) { + while(c != 0) { + CPUWriteMemory(d, 0); + d += di; + c--; + } + } else { + while(c != 0) { + cpuDmaLast = CPUReadMemory(s); + CPUWriteMemory(d, cpuDmaLast); + d += di; + s += si; + c--; + } + } + } else { + s &= 0xFFFFFFFE; + si = (int)si >> 1; + di = (int)di >> 1; + if(s < 0x02000000 && (reg[15].I >> 24)) { + while(c != 0) { + CPUWriteHalfWord(d, 0); + d += di; + c--; + } + } else { + while(c != 0) { + cpuDmaLast = CPUReadHalfWord(s); + CPUWriteHalfWord(d, cpuDmaLast); + cpuDmaLast |= (cpuDmaLast<<16); + d += di; + s += si; + c--; + } + } + } + + cpuDmaCount = 0; + + int totalTicks = 0; + + if(transfer32) { + sw =1+memoryWaitSeq32[sm & 15]; + dw =1+memoryWaitSeq32[dm & 15]; + totalTicks = (sw+dw)*(sc-1) + 6 + memoryWait32[sm & 15] + + memoryWaitSeq32[dm & 15]; + } + else + { + sw = 1+memoryWaitSeq[sm & 15]; + dw = 1+memoryWaitSeq[dm & 15]; + totalTicks = (sw+dw)*(sc-1) + 6 + memoryWait[sm & 15] + + memoryWaitSeq[dm & 15]; + } + + cpuDmaTicksToUpdate += totalTicks; + cpuDmaHack = false; +} + +void CPUCheckDMA(int reason, int dmamask) +{ + // DMA 0 + if((DM0CNT_H & 0x8000) && (dmamask & 1)) { + if(((DM0CNT_H >> 12) & 3) == reason) { + u32 sourceIncrement = 4; + u32 destIncrement = 4; + switch((DM0CNT_H >> 7) & 3) { + case 0: + break; + case 1: + sourceIncrement = (u32)-4; + break; + case 2: + sourceIncrement = 0; + break; + } + switch((DM0CNT_H >> 5) & 3) { + case 0: + break; + case 1: + destIncrement = (u32)-4; + break; + case 2: + destIncrement = 0; + break; + } +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_DMA0) { + int count = (DM0CNT_L ? DM0CNT_L : 0x4000) << 1; + if(DM0CNT_H & 0x0400) + count <<= 1; + log("DMA0: s=%08x d=%08x c=%04x count=%08x\n", dma0Source, dma0Dest, + DM0CNT_H, + count); + } +#endif + doDMA(dma0Source, dma0Dest, sourceIncrement, destIncrement, + DM0CNT_L ? DM0CNT_L : 0x4000, + DM0CNT_H & 0x0400); + + if(DM0CNT_H & 0x4000) { + IF |= 0x0100; + UPDATE_REG(0x202, IF); + cpuNextEvent = cpuTotalTicks; + } + + if(((DM0CNT_H >> 5) & 3) == 3) { + dma0Dest = DM0DAD_L | (DM0DAD_H << 16); + } + + if(!(DM0CNT_H & 0x0200) || (reason == 0)) { + DM0CNT_H &= 0x7FFF; + UPDATE_REG(0xBA, DM0CNT_H); + } + } + } + + // DMA 1 + if((DM1CNT_H & 0x8000) && (dmamask & 2)) { + if(((DM1CNT_H >> 12) & 3) == reason) { + u32 sourceIncrement = 4; + u32 destIncrement = 4; + switch((DM1CNT_H >> 7) & 3) { + case 0: + break; + case 1: + sourceIncrement = (u32)-4; + break; + case 2: + sourceIncrement = 0; + break; + } + switch((DM1CNT_H >> 5) & 3) { + case 0: + break; + case 1: + destIncrement = (u32)-4; + break; + case 2: + destIncrement = 0; + break; + } + if(reason == 3) { +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_DMA1) { + log("DMA1: s=%08x d=%08x c=%04x count=%08x\n", dma1Source, dma1Dest, + DM1CNT_H, + 16); + } +#endif + doDMA(dma1Source, dma1Dest, sourceIncrement, 0, 4, + 0x0400); + } else { +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_DMA1) { + int count = (DM1CNT_L ? DM1CNT_L : 0x4000) << 1; + if(DM1CNT_H & 0x0400) + count <<= 1; + log("DMA1: s=%08x d=%08x c=%04x count=%08x\n", dma1Source, dma1Dest, + DM1CNT_H, + count); + } +#endif + doDMA(dma1Source, dma1Dest, sourceIncrement, destIncrement, + DM1CNT_L ? DM1CNT_L : 0x4000, + DM1CNT_H & 0x0400); + } + + if(DM1CNT_H & 0x4000) { + IF |= 0x0200; + UPDATE_REG(0x202, IF); + cpuNextEvent = cpuTotalTicks; + } + + if(((DM1CNT_H >> 5) & 3) == 3) { + dma1Dest = DM1DAD_L | (DM1DAD_H << 16); + } + + if(!(DM1CNT_H & 0x0200) || (reason == 0)) { + DM1CNT_H &= 0x7FFF; + UPDATE_REG(0xC6, DM1CNT_H); + } + } + } + + // DMA 2 + if((DM2CNT_H & 0x8000) && (dmamask & 4)) { + if(((DM2CNT_H >> 12) & 3) == reason) { + u32 sourceIncrement = 4; + u32 destIncrement = 4; + switch((DM2CNT_H >> 7) & 3) { + case 0: + break; + case 1: + sourceIncrement = (u32)-4; + break; + case 2: + sourceIncrement = 0; + break; + } + switch((DM2CNT_H >> 5) & 3) { + case 0: + break; + case 1: + destIncrement = (u32)-4; + break; + case 2: + destIncrement = 0; + break; + } + if(reason == 3) { +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_DMA2) { + int count = (4) << 2; + log("DMA2: s=%08x d=%08x c=%04x count=%08x\n", dma2Source, dma2Dest, + DM2CNT_H, + count); + } +#endif + doDMA(dma2Source, dma2Dest, sourceIncrement, 0, 4, + 0x0400); + } else { +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_DMA2) { + int count = (DM2CNT_L ? DM2CNT_L : 0x4000) << 1; + if(DM2CNT_H & 0x0400) + count <<= 1; + log("DMA2: s=%08x d=%08x c=%04x count=%08x\n", dma2Source, dma2Dest, + DM2CNT_H, + count); + } +#endif + doDMA(dma2Source, dma2Dest, sourceIncrement, destIncrement, + DM2CNT_L ? DM2CNT_L : 0x4000, + DM2CNT_H & 0x0400); + } + + if(DM2CNT_H & 0x4000) { + IF |= 0x0400; + UPDATE_REG(0x202, IF); + cpuNextEvent = cpuTotalTicks; + } + + if(((DM2CNT_H >> 5) & 3) == 3) { + dma2Dest = DM2DAD_L | (DM2DAD_H << 16); + } + + if(!(DM2CNT_H & 0x0200) || (reason == 0)) { + DM2CNT_H &= 0x7FFF; + UPDATE_REG(0xD2, DM2CNT_H); + } + } + } + + // DMA 3 + if((DM3CNT_H & 0x8000) && (dmamask & 8)) { + if(((DM3CNT_H >> 12) & 3) == reason) { + u32 sourceIncrement = 4; + u32 destIncrement = 4; + switch((DM3CNT_H >> 7) & 3) { + case 0: + break; + case 1: + sourceIncrement = (u32)-4; + break; + case 2: + sourceIncrement = 0; + break; + } + switch((DM3CNT_H >> 5) & 3) { + case 0: + break; + case 1: + destIncrement = (u32)-4; + break; + case 2: + destIncrement = 0; + break; + } +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_DMA3) { + int count = (DM3CNT_L ? DM3CNT_L : 0x10000) << 1; + if(DM3CNT_H & 0x0400) + count <<= 1; + log("DMA3: s=%08x d=%08x c=%04x count=%08x\n", dma3Source, dma3Dest, + DM3CNT_H, + count); + } +#endif + doDMA(dma3Source, dma3Dest, sourceIncrement, destIncrement, + DM3CNT_L ? DM3CNT_L : 0x10000, + DM3CNT_H & 0x0400); + + if(DM3CNT_H & 0x4000) { + IF |= 0x0800; + UPDATE_REG(0x202, IF); + cpuNextEvent = cpuTotalTicks; + } + + if(((DM3CNT_H >> 5) & 3) == 3) { + dma3Dest = DM3DAD_L | (DM3DAD_H << 16); + } + + if(!(DM3CNT_H & 0x0200) || (reason == 0)) { + DM3CNT_H &= 0x7FFF; + UPDATE_REG(0xDE, DM3CNT_H); + } + } + } +} + +void CPUUpdateRegister(u32 address, u16 value) +{ + switch(address) + { + case 0x00: + { // we need to place the following code in { } because we declare & initialize variables in a case statement + if((value & 7) > 5) { + // display modes above 0-5 are prohibited + DISPCNT = (value & 7); + } + bool change = (0 != ((DISPCNT ^ value) & 0x80)); + bool changeBG = (0 != ((DISPCNT ^ value) & 0x0F00)); + u16 changeBGon = ((~DISPCNT) & value) & 0x0F00; // these layers are being activated + + DISPCNT = (value & 0xFFF7); // bit 3 can only be accessed by the BIOS to enable GBC mode + UPDATE_REG(0x00, DISPCNT); + + if(changeBGon) { + layerEnableDelay = 4; + layerEnable = layerSettings & value & (~changeBGon); + } else { + layerEnable = layerSettings & value; + // CPUUpdateTicks(); + } + + windowOn = (layerEnable & 0x6000) ? true : false; + if(change && !((value & 0x80))) { + if(!(DISPSTAT & 1)) { + //lcdTicks = 1008; + // VCOUNT = 0; + // UPDATE_REG(0x06, VCOUNT); + DISPSTAT &= 0xFFFC; + UPDATE_REG(0x04, DISPSTAT); + CPUCompareVCOUNT(); + } + // (*renderLine)(); + } + CPUUpdateRender(); + // we only care about changes in BG0-BG3 + if(changeBG) { + CPUUpdateRenderBuffers(false); + } + break; + } + case 0x04: + DISPSTAT = (value & 0xFF38) | (DISPSTAT & 7); + UPDATE_REG(0x04, DISPSTAT); + break; + case 0x06: + // not writable + break; + case 0x08: + BG0CNT = (value & 0xDFCF); + UPDATE_REG(0x08, BG0CNT); + break; + case 0x0A: + BG1CNT = (value & 0xDFCF); + UPDATE_REG(0x0A, BG1CNT); + break; + case 0x0C: + BG2CNT = (value & 0xFFCF); + UPDATE_REG(0x0C, BG2CNT); + break; + case 0x0E: + BG3CNT = (value & 0xFFCF); + UPDATE_REG(0x0E, BG3CNT); + break; + case 0x10: + BG0HOFS = value & 511; + UPDATE_REG(0x10, BG0HOFS); + break; + case 0x12: + BG0VOFS = value & 511; + UPDATE_REG(0x12, BG0VOFS); + break; + case 0x14: + BG1HOFS = value & 511; + UPDATE_REG(0x14, BG1HOFS); + break; + case 0x16: + BG1VOFS = value & 511; + UPDATE_REG(0x16, BG1VOFS); + break; + case 0x18: + BG2HOFS = value & 511; + UPDATE_REG(0x18, BG2HOFS); + break; + case 0x1A: + BG2VOFS = value & 511; + UPDATE_REG(0x1A, BG2VOFS); + break; + case 0x1C: + BG3HOFS = value & 511; + UPDATE_REG(0x1C, BG3HOFS); + break; + case 0x1E: + BG3VOFS = value & 511; + UPDATE_REG(0x1E, BG3VOFS); + break; + case 0x20: + BG2PA = value; + UPDATE_REG(0x20, BG2PA); + break; + case 0x22: + BG2PB = value; + UPDATE_REG(0x22, BG2PB); + break; + case 0x24: + BG2PC = value; + UPDATE_REG(0x24, BG2PC); + break; + case 0x26: + BG2PD = value; + UPDATE_REG(0x26, BG2PD); + break; + case 0x28: + BG2X_L = value; + UPDATE_REG(0x28, BG2X_L); + gfxBG2Changed |= 1; + break; + case 0x2A: + BG2X_H = (value & 0xFFF); + UPDATE_REG(0x2A, BG2X_H); + gfxBG2Changed |= 1; + break; + case 0x2C: + BG2Y_L = value; + UPDATE_REG(0x2C, BG2Y_L); + gfxBG2Changed |= 2; + break; + case 0x2E: + BG2Y_H = value & 0xFFF; + UPDATE_REG(0x2E, BG2Y_H); + gfxBG2Changed |= 2; + break; + case 0x30: + BG3PA = value; + UPDATE_REG(0x30, BG3PA); + break; + case 0x32: + BG3PB = value; + UPDATE_REG(0x32, BG3PB); + break; + case 0x34: + BG3PC = value; + UPDATE_REG(0x34, BG3PC); + break; + case 0x36: + BG3PD = value; + UPDATE_REG(0x36, BG3PD); + break; + case 0x38: + BG3X_L = value; + UPDATE_REG(0x38, BG3X_L); + gfxBG3Changed |= 1; + break; + case 0x3A: + BG3X_H = value & 0xFFF; + UPDATE_REG(0x3A, BG3X_H); + gfxBG3Changed |= 1; + break; + case 0x3C: + BG3Y_L = value; + UPDATE_REG(0x3C, BG3Y_L); + gfxBG3Changed |= 2; + break; + case 0x3E: + BG3Y_H = value & 0xFFF; + UPDATE_REG(0x3E, BG3Y_H); + gfxBG3Changed |= 2; + break; + case 0x40: + WIN0H = value; + UPDATE_REG(0x40, WIN0H); + CPUUpdateWindow0(); + break; + case 0x42: + WIN1H = value; + UPDATE_REG(0x42, WIN1H); + CPUUpdateWindow1(); + break; + case 0x44: + WIN0V = value; + UPDATE_REG(0x44, WIN0V); + break; + case 0x46: + WIN1V = value; + UPDATE_REG(0x46, WIN1V); + break; + case 0x48: + WININ = value & 0x3F3F; + UPDATE_REG(0x48, WININ); + break; + case 0x4A: + WINOUT = value & 0x3F3F; + UPDATE_REG(0x4A, WINOUT); + break; + case 0x4C: + MOSAIC = value; + UPDATE_REG(0x4C, MOSAIC); + break; + case 0x50: + BLDMOD = value & 0x3FFF; + UPDATE_REG(0x50, BLDMOD); + fxOn = ((BLDMOD>>6)&3) != 0; + CPUUpdateRender(); + break; + case 0x52: + COLEV = value & 0x1F1F; + UPDATE_REG(0x52, COLEV); + break; + case 0x54: + COLY = value & 0x1F; + UPDATE_REG(0x54, COLY); + break; + case 0x60: + case 0x62: + case 0x64: + case 0x68: + case 0x6c: + case 0x70: + case 0x72: + case 0x74: + case 0x78: + case 0x7c: + case 0x80: + case 0x84: + soundEvent(address&0xFF, (u8)(value & 0xFF)); + soundEvent((address&0xFF)+1, (u8)(value>>8)); + break; + case 0x82: + case 0x88: + case 0xa0: + case 0xa2: + case 0xa4: + case 0xa6: + case 0x90: + case 0x92: + case 0x94: + case 0x96: + case 0x98: + case 0x9a: + case 0x9c: + case 0x9e: + soundEvent(address&0xFF, value); + break; + case 0xB0: + DM0SAD_L = value; + UPDATE_REG(0xB0, DM0SAD_L); + break; + case 0xB2: + DM0SAD_H = value & 0x07FF; + UPDATE_REG(0xB2, DM0SAD_H); + break; + case 0xB4: + DM0DAD_L = value; + UPDATE_REG(0xB4, DM0DAD_L); + break; + case 0xB6: + DM0DAD_H = value & 0x07FF; + UPDATE_REG(0xB6, DM0DAD_H); + break; + case 0xB8: + DM0CNT_L = value & 0x3FFF; + UPDATE_REG(0xB8, 0); + break; + case 0xBA: + { + bool start = ((DM0CNT_H ^ value) & 0x8000) ? true : false; + value &= 0xF7E0; + + DM0CNT_H = value; + UPDATE_REG(0xBA, DM0CNT_H); + + if(start && (value & 0x8000)) { + dma0Source = DM0SAD_L | (DM0SAD_H << 16); + dma0Dest = DM0DAD_L | (DM0DAD_H << 16); + CPUCheckDMA(0, 1); + } + } + break; + case 0xBC: + DM1SAD_L = value; + UPDATE_REG(0xBC, DM1SAD_L); + break; + case 0xBE: + DM1SAD_H = value & 0x0FFF; + UPDATE_REG(0xBE, DM1SAD_H); + break; + case 0xC0: + DM1DAD_L = value; + UPDATE_REG(0xC0, DM1DAD_L); + break; + case 0xC2: + DM1DAD_H = value & 0x07FF; + UPDATE_REG(0xC2, DM1DAD_H); + break; + case 0xC4: + DM1CNT_L = value & 0x3FFF; + UPDATE_REG(0xC4, 0); + break; + case 0xC6: + { + bool start = ((DM1CNT_H ^ value) & 0x8000) ? true : false; + value &= 0xF7E0; + + DM1CNT_H = value; + UPDATE_REG(0xC6, DM1CNT_H); + + if(start && (value & 0x8000)) { + dma1Source = DM1SAD_L | (DM1SAD_H << 16); + dma1Dest = DM1DAD_L | (DM1DAD_H << 16); + CPUCheckDMA(0, 2); + } + } + break; + case 0xC8: + DM2SAD_L = value; + UPDATE_REG(0xC8, DM2SAD_L); + break; + case 0xCA: + DM2SAD_H = value & 0x0FFF; + UPDATE_REG(0xCA, DM2SAD_H); + break; + case 0xCC: + DM2DAD_L = value; + UPDATE_REG(0xCC, DM2DAD_L); + break; + case 0xCE: + DM2DAD_H = value & 0x07FF; + UPDATE_REG(0xCE, DM2DAD_H); + break; + case 0xD0: + DM2CNT_L = value & 0x3FFF; + UPDATE_REG(0xD0, 0); + break; + case 0xD2: + { + bool start = ((DM2CNT_H ^ value) & 0x8000) ? true : false; + + value &= 0xF7E0; + + DM2CNT_H = value; + UPDATE_REG(0xD2, DM2CNT_H); + + if(start && (value & 0x8000)) { + dma2Source = DM2SAD_L | (DM2SAD_H << 16); + dma2Dest = DM2DAD_L | (DM2DAD_H << 16); + + CPUCheckDMA(0, 4); + } + } + break; + case 0xD4: + DM3SAD_L = value; + UPDATE_REG(0xD4, DM3SAD_L); + break; + case 0xD6: + DM3SAD_H = value & 0x0FFF; + UPDATE_REG(0xD6, DM3SAD_H); + break; + case 0xD8: + DM3DAD_L = value; + UPDATE_REG(0xD8, DM3DAD_L); + break; + case 0xDA: + DM3DAD_H = value & 0x0FFF; + UPDATE_REG(0xDA, DM3DAD_H); + break; + case 0xDC: + DM3CNT_L = value; + UPDATE_REG(0xDC, 0); + break; + case 0xDE: + { + bool start = ((DM3CNT_H ^ value) & 0x8000) ? true : false; + + value &= 0xFFE0; + + DM3CNT_H = value; + UPDATE_REG(0xDE, DM3CNT_H); + + if(start && (value & 0x8000)) { + dma3Source = DM3SAD_L | (DM3SAD_H << 16); + dma3Dest = DM3DAD_L | (DM3DAD_H << 16); + CPUCheckDMA(0,8); + } + } + break; + case 0x100: + timer0Reload = value; + interp_rate(); + break; + case 0x102: + timer0Value = value; + timerOnOffDelay|=1; + cpuNextEvent = cpuTotalTicks; + break; + case 0x104: + timer1Reload = value; + interp_rate(); + break; + case 0x106: + timer1Value = value; + timerOnOffDelay|=2; + cpuNextEvent = cpuTotalTicks; + break; + case 0x108: + timer2Reload = value; + break; + case 0x10A: + timer2Value = value; + timerOnOffDelay|=4; + cpuNextEvent = cpuTotalTicks; + break; + case 0x10C: + timer3Reload = value; + break; + case 0x10E: + timer3Value = value; + timerOnOffDelay|=8; + cpuNextEvent = cpuTotalTicks; + break; + + +#ifndef NO_LINK + case COMM_SIOCNT: + StartLink(value); + break; + + case COMM_SIODATA8: + UPDATE_REG(COMM_SIODATA8, value); + break; +#endif + + case 0x130: + P1 |= (value & 0x3FF); + UPDATE_REG(0x130, P1); + break; + + case 0x132: + UPDATE_REG(0x132, value & 0xC3FF); + break; + +#ifndef NO_LINK + case COMM_RCNT: + StartGPLink(value); + break; + + case COMM_JOYCNT: + { + u16 cur = READ16LE(&ioMem[COMM_JOYCNT]); + + if (value & JOYCNT_RESET) cur &= ~JOYCNT_RESET; + if (value & JOYCNT_RECV_COMPLETE) cur &= ~JOYCNT_RECV_COMPLETE; + if (value & JOYCNT_SEND_COMPLETE) cur &= ~JOYCNT_SEND_COMPLETE; + if (value & JOYCNT_INT_ENABLE) cur |= JOYCNT_INT_ENABLE; + + UPDATE_REG(COMM_JOYCNT, cur); + } + break; + + case COMM_JOY_RECV_L: + UPDATE_REG(COMM_JOY_RECV_L, value); + break; + case COMM_JOY_RECV_H: + UPDATE_REG(COMM_JOY_RECV_H, value); + break; + + case COMM_JOY_TRANS_L: + UPDATE_REG(COMM_JOY_TRANS_L, value); + UPDATE_REG(COMM_JOYSTAT, READ16LE(&ioMem[COMM_JOYSTAT]) | JOYSTAT_SEND); + break; + case COMM_JOY_TRANS_H: + UPDATE_REG(COMM_JOY_TRANS_H, value); + break; + + case COMM_JOYSTAT: + UPDATE_REG(COMM_JOYSTAT, (READ16LE(&ioMem[COMM_JOYSTAT]) & 0xf) | (value & 0xf0)); + break; +#endif + + case 0x200: + IE = value & 0x3FFF; + UPDATE_REG(0x200, IE); + if ((IME & 1) && (IF & IE) && armIrqEnable) + cpuNextEvent = cpuTotalTicks; + break; + case 0x202: + IF ^= (value & IF); + UPDATE_REG(0x202, IF); + break; + case 0x204: + { + memoryWait[0x0e] = memoryWaitSeq[0x0e] = gamepakRamWaitState[value & 3]; + + if(!speedHack) { + memoryWait[0x08] = memoryWait[0x09] = gamepakWaitState[(value >> 2) & 3]; + memoryWaitSeq[0x08] = memoryWaitSeq[0x09] = + gamepakWaitState0[(value >> 4) & 1]; + + memoryWait[0x0a] = memoryWait[0x0b] = gamepakWaitState[(value >> 5) & 3]; + memoryWaitSeq[0x0a] = memoryWaitSeq[0x0b] = + gamepakWaitState1[(value >> 7) & 1]; + + memoryWait[0x0c] = memoryWait[0x0d] = gamepakWaitState[(value >> 8) & 3]; + memoryWaitSeq[0x0c] = memoryWaitSeq[0x0d] = + gamepakWaitState2[(value >> 10) & 1]; + } else { + memoryWait[0x08] = memoryWait[0x09] = 3; + memoryWaitSeq[0x08] = memoryWaitSeq[0x09] = 1; + + memoryWait[0x0a] = memoryWait[0x0b] = 3; + memoryWaitSeq[0x0a] = memoryWaitSeq[0x0b] = 1; + + memoryWait[0x0c] = memoryWait[0x0d] = 3; + memoryWaitSeq[0x0c] = memoryWaitSeq[0x0d] = 1; + } + + for(int i = 8; i < 15; i++) { + memoryWait32[i] = memoryWait[i] + memoryWaitSeq[i] + 1; + memoryWaitSeq32[i] = memoryWaitSeq[i]*2 + 1; + } + + if((value & 0x4000) == 0x4000) { + busPrefetchEnable = true; + busPrefetch = false; + busPrefetchCount = 0; + } else { + busPrefetchEnable = false; + busPrefetch = false; + busPrefetchCount = 0; + } + UPDATE_REG(0x204, value & 0x7FFF); + + } + break; + case 0x208: + IME = value & 1; + UPDATE_REG(0x208, IME); + if ((IME & 1) && (IF & IE) && armIrqEnable) + cpuNextEvent = cpuTotalTicks; + break; + case 0x300: + if(value != 0) + value &= 0xFFFE; + UPDATE_REG(0x300, value); + break; + default: + UPDATE_REG(address&0x3FE, value); + break; + } +} + +void applyTimer () +{ + if (timerOnOffDelay & 1) + { + timer0ClockReload = TIMER_TICKS[timer0Value & 3]; + if(!timer0On && (timer0Value & 0x80)) { + // reload the counter + TM0D = timer0Reload; + timer0Ticks = (0x10000 - TM0D) << timer0ClockReload; + UPDATE_REG(0x100, TM0D); + } + timer0On = timer0Value & 0x80 ? true : false; + TM0CNT = timer0Value & 0xC7; + interp_rate(); + UPDATE_REG(0x102, TM0CNT); + // CPUUpdateTicks(); + } + if (timerOnOffDelay & 2) + { + timer1ClockReload = TIMER_TICKS[timer1Value & 3]; + if(!timer1On && (timer1Value & 0x80)) { + // reload the counter + TM1D = timer1Reload; + timer1Ticks = (0x10000 - TM1D) << timer1ClockReload; + UPDATE_REG(0x104, TM1D); + } + timer1On = timer1Value & 0x80 ? true : false; + TM1CNT = timer1Value & 0xC7; + interp_rate(); + UPDATE_REG(0x106, TM1CNT); + } + if (timerOnOffDelay & 4) + { + timer2ClockReload = TIMER_TICKS[timer2Value & 3]; + if(!timer2On && (timer2Value & 0x80)) { + // reload the counter + TM2D = timer2Reload; + timer2Ticks = (0x10000 - TM2D) << timer2ClockReload; + UPDATE_REG(0x108, TM2D); + } + timer2On = timer2Value & 0x80 ? true : false; + TM2CNT = timer2Value & 0xC7; + UPDATE_REG(0x10A, TM2CNT); + } + if (timerOnOffDelay & 8) + { + timer3ClockReload = TIMER_TICKS[timer3Value & 3]; + if(!timer3On && (timer3Value & 0x80)) { + // reload the counter + TM3D = timer3Reload; + timer3Ticks = (0x10000 - TM3D) << timer3ClockReload; + UPDATE_REG(0x10C, TM3D); + } + timer3On = timer3Value & 0x80 ? true : false; + TM3CNT = timer3Value & 0xC7; + UPDATE_REG(0x10E, TM3CNT); + } + cpuNextEvent = CPUUpdateTicks(); + timerOnOffDelay = 0; +} + +u8 cpuBitsSet[256]; +u8 cpuLowestBitSet[256]; + +void CPUInit(const char *biosFileName, bool useBiosFile) +{ +#ifdef WORDS_BIGENDIAN + if(!cpuBiosSwapped) { + for(unsigned int i = 0; i < sizeof(myROM)/4; i++) { + WRITE32LE(&myROM[i], myROM[i]); + } + cpuBiosSwapped = true; + } +#endif + gbaSaveType = 0; + eepromInUse = 0; + saveType = 0; + useBios = false; + + if(useBiosFile) { + int size = 0x4000; + if(utilLoad(biosFileName, + CPUIsGBABios, + bios, + size)) { + if(size == 0x4000) + useBios = true; + else + systemMessage(MSG_INVALID_BIOS_FILE_SIZE, N_("Invalid BIOS file size")); + } + } + + if(!useBios) { + memcpy(bios, myROM, sizeof(myROM)); + } + + int i = 0; + + biosProtected[0] = 0x00; + biosProtected[1] = 0xf0; + biosProtected[2] = 0x29; + biosProtected[3] = 0xe1; + + for(i = 0; i < 256; i++) { + int count = 0; + int j; + for(j = 0; j < 8; j++) + if(i & (1 << j)) + count++; + cpuBitsSet[i] = count; + + for(j = 0; j < 8; j++) + if(i & (1 << j)) + break; + cpuLowestBitSet[i] = j; + } + + for(i = 0; i < 0x400; i++) + ioReadable[i] = true; + for(i = 0x10; i < 0x48; i++) + ioReadable[i] = false; + for(i = 0x4c; i < 0x50; i++) + ioReadable[i] = false; + for(i = 0x54; i < 0x60; i++) + ioReadable[i] = false; + for(i = 0x8c; i < 0x90; i++) + ioReadable[i] = false; + for(i = 0xa0; i < 0xb8; i++) + ioReadable[i] = false; + for(i = 0xbc; i < 0xc4; i++) + ioReadable[i] = false; + for(i = 0xc8; i < 0xd0; i++) + ioReadable[i] = false; + for(i = 0xd4; i < 0xdc; i++) + ioReadable[i] = false; + for(i = 0xe0; i < 0x100; i++) + ioReadable[i] = false; + for(i = 0x110; i < 0x120; i++) + ioReadable[i] = false; + for(i = 0x12c; i < 0x130; i++) + ioReadable[i] = false; + for(i = 0x138; i < 0x140; i++) + ioReadable[i] = false; + for(i = 0x144; i < 0x150; i++) + ioReadable[i] = false; + for(i = 0x15c; i < 0x200; i++) + ioReadable[i] = false; + for(i = 0x20c; i < 0x300; i++) + ioReadable[i] = false; + for(i = 0x304; i < 0x400; i++) + ioReadable[i] = false; + + if(romSize < 0x1fe2000) { + *((u16 *)&rom[0x1fe209c]) = 0xdffa; // SWI 0xFA + *((u16 *)&rom[0x1fe209e]) = 0x4770; // BX LR + } else { + agbPrintEnable(false); + } +} + +void CPUReset() +{ + if(gbaSaveType == 0) { + if(eepromInUse) + gbaSaveType = 3; + else + switch(saveType) { + case 1: + gbaSaveType = 1; + break; + case 2: + gbaSaveType = 2; + break; + } + } + rtcReset(); + // clean registers + memset(®[0], 0, sizeof(reg)); + // clean OAM + memset(oam, 0, 0x400); + // clean palette + memset(paletteRAM, 0, 0x400); + // clean picture + memset(pix, 0, 4*160*240); + // clean vram + memset(vram, 0, 0x20000); + // clean io memory + memset(ioMem, 0, 0x400); + + DISPCNT = 0x0080; + DISPSTAT = 0x0000; + VCOUNT = (useBios && !skipBios) ? 0 :0x007E; + BG0CNT = 0x0000; + BG1CNT = 0x0000; + BG2CNT = 0x0000; + BG3CNT = 0x0000; + BG0HOFS = 0x0000; + BG0VOFS = 0x0000; + BG1HOFS = 0x0000; + BG1VOFS = 0x0000; + BG2HOFS = 0x0000; + BG2VOFS = 0x0000; + BG3HOFS = 0x0000; + BG3VOFS = 0x0000; + BG2PA = 0x0100; + BG2PB = 0x0000; + BG2PC = 0x0000; + BG2PD = 0x0100; + BG2X_L = 0x0000; + BG2X_H = 0x0000; + BG2Y_L = 0x0000; + BG2Y_H = 0x0000; + BG3PA = 0x0100; + BG3PB = 0x0000; + BG3PC = 0x0000; + BG3PD = 0x0100; + BG3X_L = 0x0000; + BG3X_H = 0x0000; + BG3Y_L = 0x0000; + BG3Y_H = 0x0000; + WIN0H = 0x0000; + WIN1H = 0x0000; + WIN0V = 0x0000; + WIN1V = 0x0000; + WININ = 0x0000; + WINOUT = 0x0000; + MOSAIC = 0x0000; + BLDMOD = 0x0000; + COLEV = 0x0000; + COLY = 0x0000; + DM0SAD_L = 0x0000; + DM0SAD_H = 0x0000; + DM0DAD_L = 0x0000; + DM0DAD_H = 0x0000; + DM0CNT_L = 0x0000; + DM0CNT_H = 0x0000; + DM1SAD_L = 0x0000; + DM1SAD_H = 0x0000; + DM1DAD_L = 0x0000; + DM1DAD_H = 0x0000; + DM1CNT_L = 0x0000; + DM1CNT_H = 0x0000; + DM2SAD_L = 0x0000; + DM2SAD_H = 0x0000; + DM2DAD_L = 0x0000; + DM2DAD_H = 0x0000; + DM2CNT_L = 0x0000; + DM2CNT_H = 0x0000; + DM3SAD_L = 0x0000; + DM3SAD_H = 0x0000; + DM3DAD_L = 0x0000; + DM3DAD_H = 0x0000; + DM3CNT_L = 0x0000; + DM3CNT_H = 0x0000; + TM0D = 0x0000; + TM0CNT = 0x0000; + TM1D = 0x0000; + TM1CNT = 0x0000; + TM2D = 0x0000; + TM2CNT = 0x0000; + TM3D = 0x0000; + TM3CNT = 0x0000; + P1 = 0x03FF; + IE = 0x0000; + IF = 0x0000; + IME = 0x0000; + + armMode = 0x1F; + + if(cpuIsMultiBoot) { + reg[13].I = 0x03007F00; + reg[15].I = 0x02000000; + reg[16].I = 0x00000000; + reg[R13_IRQ].I = 0x03007FA0; + reg[R13_SVC].I = 0x03007FE0; + armIrqEnable = true; + } else { + if(useBios && !skipBios) { + reg[15].I = 0x00000000; + armMode = 0x13; + armIrqEnable = false; + } else { + reg[13].I = 0x03007F00; + reg[15].I = 0x08000000; + reg[16].I = 0x00000000; + reg[R13_IRQ].I = 0x03007FA0; + reg[R13_SVC].I = 0x03007FE0; + armIrqEnable = true; + } + } + armState = true; + C_FLAG = V_FLAG = N_FLAG = Z_FLAG = false; + UPDATE_REG(0x00, DISPCNT); + UPDATE_REG(0x06, VCOUNT); + UPDATE_REG(0x20, BG2PA); + UPDATE_REG(0x26, BG2PD); + UPDATE_REG(0x30, BG3PA); + UPDATE_REG(0x36, BG3PD); + UPDATE_REG(0x130, P1); + UPDATE_REG(0x88, 0x200); + + // disable FIQ + reg[16].I |= 0x40; + + CPUUpdateCPSR(); + + armNextPC = reg[15].I; + reg[15].I += 4; + + // reset internal state + holdState = false; + holdType = 0; + + biosProtected[0] = 0x00; + biosProtected[1] = 0xf0; + biosProtected[2] = 0x29; + biosProtected[3] = 0xe1; + + lcdTicks = (useBios && !skipBios) ? 1008 : 208; + timer0On = false; + timer0Ticks = 0; + timer0Reload = 0; + timer0ClockReload = 0; + timer1On = false; + timer1Ticks = 0; + timer1Reload = 0; + timer1ClockReload = 0; + timer2On = false; + timer2Ticks = 0; + timer2Reload = 0; + timer2ClockReload = 0; + timer3On = false; + timer3Ticks = 0; + timer3Reload = 0; + timer3ClockReload = 0; + dma0Source = 0; + dma0Dest = 0; + dma1Source = 0; + dma1Dest = 0; + dma2Source = 0; + dma2Dest = 0; + dma3Source = 0; + dma3Dest = 0; + cpuSaveGameFunc = flashSaveDecide; + renderLine = mode0RenderLine; + fxOn = false; + windowOn = false; + frameCount = 0; + saveType = 0; + layerEnable = DISPCNT & layerSettings; + + CPUUpdateRenderBuffers(true); + + for(int i = 0; i < 256; i++) { + map[i].address = (u8 *)&dummyAddress; + map[i].mask = 0; + } + + map[0].address = bios; + map[0].mask = 0x3FFF; + map[2].address = workRAM; + map[2].mask = 0x3FFFF; + map[3].address = internalRAM; + map[3].mask = 0x7FFF; + map[4].address = ioMem; + map[4].mask = 0x3FF; + map[5].address = paletteRAM; + map[5].mask = 0x3FF; + map[6].address = vram; + map[6].mask = 0x1FFFF; + map[7].address = oam; + map[7].mask = 0x3FF; + map[8].address = rom; + map[8].mask = 0x1FFFFFF; + map[9].address = rom; + map[9].mask = 0x1FFFFFF; + map[10].address = rom; + map[10].mask = 0x1FFFFFF; + map[12].address = rom; + map[12].mask = 0x1FFFFFF; + map[14].address = flashSaveMemory; + map[14].mask = 0xFFFF; + + eepromReset(); + flashReset(); + + soundReset(); + + CPUUpdateWindow0(); + CPUUpdateWindow1(); + + // make sure registers are correctly initialized if not using BIOS + if(!useBios) { + if(cpuIsMultiBoot) + BIOS_RegisterRamReset(0xfe); + else + BIOS_RegisterRamReset(0xff); + } else { + if(cpuIsMultiBoot) + BIOS_RegisterRamReset(0xfe); + } + + switch(cpuSaveType) { + case 0: // automatic + cpuSramEnabled = true; + cpuFlashEnabled = true; + cpuEEPROMEnabled = true; + cpuEEPROMSensorEnabled = false; + saveType = gbaSaveType = 0; + break; + case 1: // EEPROM + cpuSramEnabled = false; + cpuFlashEnabled = false; + cpuEEPROMEnabled = true; + cpuEEPROMSensorEnabled = false; + saveType = gbaSaveType = 3; + // EEPROM usage is automatically detected + break; + case 2: // SRAM + cpuSramEnabled = true; + cpuFlashEnabled = false; + cpuEEPROMEnabled = false; + cpuEEPROMSensorEnabled = false; + cpuSaveGameFunc = sramDelayedWrite; // to insure we detect the write + saveType = gbaSaveType = 1; + break; + case 3: // FLASH + cpuSramEnabled = false; + cpuFlashEnabled = true; + cpuEEPROMEnabled = false; + cpuEEPROMSensorEnabled = false; + cpuSaveGameFunc = flashDelayedWrite; // to insure we detect the write + saveType = gbaSaveType = 2; + break; + case 4: // EEPROM+Sensor + cpuSramEnabled = false; + cpuFlashEnabled = false; + cpuEEPROMEnabled = true; + cpuEEPROMSensorEnabled = true; + // EEPROM usage is automatically detected + saveType = gbaSaveType = 3; + break; + case 5: // NONE + cpuSramEnabled = false; + cpuFlashEnabled = false; + cpuEEPROMEnabled = false; + cpuEEPROMSensorEnabled = false; + // no save at all + saveType = gbaSaveType = 5; + break; + } + + ARM_PREFETCH; + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + cpuDmaHack = false; + + lastTime = systemGetClock(); + + SWITicks = 0; +} + +void CPUInterrupt() +{ + u32 PC = reg[15].I; + bool savedState = armState; + CPUSwitchMode(0x12, true, false); + reg[14].I = PC; + if(!savedState) + reg[14].I += 2; + reg[15].I = 0x18; + armState = true; + armIrqEnable = false; + + armNextPC = reg[15].I; + reg[15].I += 4; + ARM_PREFETCH; + + // if(!holdState) + biosProtected[0] = 0x02; + biosProtected[1] = 0xc0; + biosProtected[2] = 0x5e; + biosProtected[3] = 0xe5; +} + +void CPULoop(int ticks) +{ + int clockTicks; + int timerOverflow = 0; + // variable used by the CPU core + cpuTotalTicks = 0; + +#ifndef NO_LINK + // shuffle2: what's the purpose? + if(gba_link_enabled) + cpuNextEvent = 1; +#endif + + cpuBreakLoop = false; + cpuNextEvent = CPUUpdateTicks(); + if(cpuNextEvent > ticks) + cpuNextEvent = ticks; + + + for(;;) { +#ifndef FINAL_VERSION + if(systemDebug) { + if(systemDebug >= 10 && !holdState) { + CPUUpdateCPSR(); +#ifdef BKPT_SUPPORT + if (debugger_last) + { + 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 + 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); + } else if(!holdState) { + winlog("PC=%08x\n", armNextPC); + } + } +#endif /* FINAL_VERSION */ + + if(!holdState && !SWITicks) { + if(armState) { + armOpcodeCount++; + if (!armExecute()) + return; + } else { + thumbOpcodeCount++; + if (!thumbExecute()) + return; + } + clockTicks = 0; + } else + clockTicks = CPUUpdateTicks(); + + cpuTotalTicks += clockTicks; + + + if(cpuTotalTicks >= cpuNextEvent) { + int remainingTicks = cpuTotalTicks - cpuNextEvent; + + if (SWITicks) + { + SWITicks-=clockTicks; + if (SWITicks<0) + SWITicks = 0; + } + + clockTicks = cpuNextEvent; + cpuTotalTicks = 0; + + updateLoop: + + if (IRQTicks) + { + IRQTicks -= clockTicks; + if (IRQTicks<0) + IRQTicks = 0; + } + + lcdTicks -= clockTicks; + + //cout << "lcdTicks is " << lcdTicks << " " << DISPSTAT << endl; + if(lcdTicks <= 0) { + if(DISPSTAT & 1) { // V-BLANK + // if in V-Blank mode, keep computing... + if(DISPSTAT & 2) { + lcdTicks += 1008; + VCOUNT++; + UPDATE_REG(0x06, VCOUNT); + DISPSTAT &= 0xFFFD; + UPDATE_REG(0x04, DISPSTAT); + CPUCompareVCOUNT(); + } else { + lcdTicks += 224; + DISPSTAT |= 2; + UPDATE_REG(0x04, DISPSTAT); + if(DISPSTAT & 16) { + IF |= 2; + UPDATE_REG(0x202, IF); + } + } + + if(VCOUNT > 227) { //Reaching last line + DISPSTAT &= 0xFFFC; + UPDATE_REG(0x04, DISPSTAT); + VCOUNT = 0; + UPDATE_REG(0x06, VCOUNT); + CPUCompareVCOUNT(); + } + } else { + int framesToSkip = systemFrameSkip; + if(speedup) + framesToSkip = 9; // try 6 FPS during speedup + + if(DISPSTAT & 2) { + // if in H-Blank, leave it and move to drawing mode + VCOUNT++; + UPDATE_REG(0x06, VCOUNT); + + lcdTicks += 1008; + DISPSTAT &= 0xFFFD; + if(VCOUNT == 160) { + count++; + systemFrame(); + + if((count % 10) == 0) { + system10Frames(60); + } + if(count == 60) { + u32 time = systemGetClock(); + if(time != lastTime) { + u32 t = 100000/(time - lastTime); + systemShowSpeed(t); + } else + systemShowSpeed(0); + lastTime = time; + count = 0; + } + u32 joy = 0; + // update joystick information + if(systemReadJoypads()) + // read default joystick + joy = systemReadJoypad(-1); + P1 = 0x03FF ^ (joy & 0x3FF); + if(cpuEEPROMSensorEnabled) + systemUpdateMotionSensor(); + UPDATE_REG(0x130, P1); + u16 P1CNT = READ16LE(((u16 *)&ioMem[0x132])); + // this seems wrong, but there are cases where the game + // can enter the stop state without requesting an IRQ from + // the joypad. + if((P1CNT & 0x4000) || stopState) { + u16 p1 = (0x3FF ^ P1) & 0x3FF; + if(P1CNT & 0x8000) { + if(p1 == (P1CNT & 0x3FF)) { + IF |= 0x1000; + UPDATE_REG(0x202, IF); + } + } else { + if(p1 & P1CNT) { + IF |= 0x1000; + UPDATE_REG(0x202, IF); + } + } + } + + u32 ext = (joy >> 10); + // If no (m) code is enabled, apply the cheats at each LCDline + if((cheatsEnabled) && (mastercode==0)) + remainingTicks += cheatsCheckKeys(P1^0x3FF, ext); + speedup = (ext & 1) ? true : false; + capture = (ext & 2) ? true : false; + + if(capture && !capturePrevious) { + captureNumber++; + systemScreenCapture(captureNumber); + } + capturePrevious = capture; + + DISPSTAT |= 1; + DISPSTAT &= 0xFFFD; + UPDATE_REG(0x04, DISPSTAT); + if(DISPSTAT & 0x0008) { + IF |= 1; + UPDATE_REG(0x202, IF); + } + CPUCheckDMA(1, 0x0f); + if(frameCount >= framesToSkip) { + systemDrawScreen(); + frameCount = 0; + } else + frameCount++; + if(systemPauseOnFrame()) + ticks = 0; + } + + UPDATE_REG(0x04, DISPSTAT); + CPUCompareVCOUNT(); + + } else { + //cout << "frameCount " << frameCount << " " << framesToSkip << endl; + if(frameCount >= framesToSkip) + { + (*renderLine)(); + //cout <<"systemColorDepth is "<< systemColorDepth << endl; + switch(systemColorDepth) { + case 16: + { + u16 *dest = (u16 *)pix + 242 * (VCOUNT+1); + for(int x = 0; x < 240;) { + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + } + // for filters that read past the screen + *dest++ = 0; + } + break; + case 24: + { + u8 *dest = (u8 *)pix + 240 * VCOUNT * 3; + for(int x = 0; x < 240;) { + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + } + } + break; + case 32: + { + u32 *dest = (u32 *)pix + 241 * (VCOUNT+1); + for(int x = 0; x < 240; ) { +// cout << "set " << lineMix[x] << " " << systemColorMap32[lineMix[x] & 0xffff] << " to dest" << endl; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + } + } + break; + } + } + // entering H-Blank + DISPSTAT |= 2; + UPDATE_REG(0x04, DISPSTAT); + lcdTicks += 224; + CPUCheckDMA(2, 0x0f); + if(DISPSTAT & 16) { + IF |= 2; + UPDATE_REG(0x202, IF); + } + } + } + } + + // we shouldn't be doing sound in stop state, but we loose synchronization + // if sound is disabled, so in stop state, soundTick will just produce + // mute sound + soundTicks -= clockTicks; + if(soundTicks <= 0) { + psoundTickfn(); + soundTicks += SOUND_CLOCK_TICKS; + } + + if(!stopState) { + if(timer0On) { + timer0Ticks -= clockTicks; + if(timer0Ticks <= 0) { + timer0Ticks += (0x10000 - timer0Reload) << timer0ClockReload; + timerOverflow |= 1; + soundTimerOverflow(0); + if(TM0CNT & 0x40) { + IF |= 0x08; + UPDATE_REG(0x202, IF); + } + } + TM0D = 0xFFFF - (timer0Ticks >> timer0ClockReload); + UPDATE_REG(0x100, TM0D); + } + + if(timer1On) { + if(TM1CNT & 4) { + if(timerOverflow & 1) { + TM1D++; + if(TM1D == 0) { + TM1D += timer1Reload; + timerOverflow |= 2; + soundTimerOverflow(1); + if(TM1CNT & 0x40) { + IF |= 0x10; + UPDATE_REG(0x202, IF); + } + } + UPDATE_REG(0x104, TM1D); + } + } else { + timer1Ticks -= clockTicks; + if(timer1Ticks <= 0) { + timer1Ticks += (0x10000 - timer1Reload) << timer1ClockReload; + timerOverflow |= 2; + soundTimerOverflow(1); + if(TM1CNT & 0x40) { + IF |= 0x10; + UPDATE_REG(0x202, IF); + } + } + TM1D = 0xFFFF - (timer1Ticks >> timer1ClockReload); + UPDATE_REG(0x104, TM1D); + } + } + + if(timer2On) { + if(TM2CNT & 4) { + if(timerOverflow & 2) { + TM2D++; + if(TM2D == 0) { + TM2D += timer2Reload; + timerOverflow |= 4; + if(TM2CNT & 0x40) { + IF |= 0x20; + UPDATE_REG(0x202, IF); + } + } + UPDATE_REG(0x108, TM2D); + } + } else { + timer2Ticks -= clockTicks; + if(timer2Ticks <= 0) { + timer2Ticks += (0x10000 - timer2Reload) << timer2ClockReload; + timerOverflow |= 4; + if(TM2CNT & 0x40) { + IF |= 0x20; + UPDATE_REG(0x202, IF); + } + } + TM2D = 0xFFFF - (timer2Ticks >> timer2ClockReload); + UPDATE_REG(0x108, TM2D); + } + } + + if(timer3On) { + if(TM3CNT & 4) { + if(timerOverflow & 4) { + TM3D++; + if(TM3D == 0) { + TM3D += timer3Reload; + if(TM3CNT & 0x40) { + IF |= 0x40; + UPDATE_REG(0x202, IF); + } + } + UPDATE_REG(0x10C, TM3D); + } + } else { + timer3Ticks -= clockTicks; + if(timer3Ticks <= 0) { + timer3Ticks += (0x10000 - timer3Reload) << timer3ClockReload; + if(TM3CNT & 0x40) { + IF |= 0x40; + UPDATE_REG(0x202, IF); + } + } + TM3D = 0xFFFF - (timer3Ticks >> timer3ClockReload); + UPDATE_REG(0x10C, TM3D); + } + } + } + + timerOverflow = 0; + + + +#ifdef PROFILING + profilingTicks -= clockTicks; + if(profilingTicks <= 0) { + profilingTicks += profilingTicksReload; + if(profilSegment) { + profile_segment *seg = profilSegment; + do { + u16 *b = (u16 *)seg->sbuf; + int pc = ((reg[15].I - seg->s_lowpc) * seg->s_scale)/0x10000; + if(pc >= 0 && pc < seg->ssiz) { + b[pc]++; + break; + } + + seg = seg->next; + } while(seg); + } + } +#endif + + ticks -= clockTicks; + + if (gba_joybus_enabled) + JoyBusUpdate(clockTicks); + +#ifndef NO_LINK + if (gba_link_enabled) + LinkUpdate(clockTicks); +#endif + + cpuNextEvent = CPUUpdateTicks(); + + if(cpuDmaTicksToUpdate > 0) { + if(cpuDmaTicksToUpdate > cpuNextEvent) + clockTicks = cpuNextEvent; + else + clockTicks = cpuDmaTicksToUpdate; + cpuDmaTicksToUpdate -= clockTicks; + if(cpuDmaTicksToUpdate < 0) + cpuDmaTicksToUpdate = 0; + goto updateLoop; + } + +#ifndef NO_LINK + // shuffle2: what's the purpose? + if(gba_link_enabled) + cpuNextEvent = 1; +#endif + + if(IF && (IME & 1) && armIrqEnable) { + int res = IF & IE; + if(stopState) + res &= 0x3080; + if(res) { + if (intState) + { + if (!IRQTicks) + { + CPUInterrupt(); + intState = false; + holdState = false; + stopState = false; + holdType = 0; + } + } + else + { + if (!holdState) + { + intState = true; + IRQTicks=7; + if (cpuNextEvent> IRQTicks) + cpuNextEvent = IRQTicks; + } + else + { + CPUInterrupt(); + holdState = false; + stopState = false; + holdType = 0; + } + } + + // Stops the SWI Ticks emulation if an IRQ is executed + //(to avoid problems with nested IRQ/SWI) + if (SWITicks) + SWITicks = 0; + } + } + + if(remainingTicks > 0) { + if(remainingTicks > cpuNextEvent) + clockTicks = cpuNextEvent; + else + clockTicks = remainingTicks; + remainingTicks -= clockTicks; + if(remainingTicks < 0) + remainingTicks = 0; + goto updateLoop; + } + + if (timerOnOffDelay) + applyTimer(); + + if(cpuNextEvent > ticks) + cpuNextEvent = ticks; + + if(ticks <= 0 || cpuBreakLoop) + break; + + } + } +} + +#ifdef TILED_RENDERING +union u8h +{ + struct + { + /* 0*/ unsigned lo:4; + /* 4*/ unsigned hi:4; + } __attribute__ ((packed)); + u8 val; +}; + +union TileEntry +{ + struct + { + /* 0*/ unsigned tileNum:10; + /*12*/ unsigned hFlip:1; + /*13*/ unsigned vFlip:1; + /*14*/ unsigned palette:4; + }; + u16 val; +}; + +struct TileLine +{ + u32 pixels[8]; +}; + +typedef const TileLine (*TileReader) (const u16 *, const int, const u8 *, u16 *, const u32); + +static inline void gfxDrawPixel(u32 *dest, const u8 color, const u16 *palette, const u32 prio) +{ + *dest = color ? (READ16LE(&palette[color]) | prio): 0x80000000; +} + +inline const TileLine gfxReadTile(const u16 *screenSource, const int yyy, const u8 *charBase, u16 *palette, const u32 prio) +{ + TileEntry tile; + tile.val = READ16LE(screenSource); + + int tileY = yyy & 7; + if (tile.vFlip) tileY = 7 - tileY; + TileLine tileLine; + + const u8 *tileBase = &charBase[tile.tileNum * 64 + tileY * 8]; + + if (!tile.hFlip) + { + gfxDrawPixel(&tileLine.pixels[0], tileBase[0], palette, prio); + gfxDrawPixel(&tileLine.pixels[1], tileBase[1], palette, prio); + gfxDrawPixel(&tileLine.pixels[2], tileBase[2], palette, prio); + gfxDrawPixel(&tileLine.pixels[3], tileBase[3], palette, prio); + gfxDrawPixel(&tileLine.pixels[4], tileBase[4], palette, prio); + gfxDrawPixel(&tileLine.pixels[5], tileBase[5], palette, prio); + gfxDrawPixel(&tileLine.pixels[6], tileBase[6], palette, prio); + gfxDrawPixel(&tileLine.pixels[7], tileBase[7], palette, prio); + } + else + { + gfxDrawPixel(&tileLine.pixels[0], tileBase[7], palette, prio); + gfxDrawPixel(&tileLine.pixels[1], tileBase[6], palette, prio); + gfxDrawPixel(&tileLine.pixels[2], tileBase[5], palette, prio); + gfxDrawPixel(&tileLine.pixels[3], tileBase[4], palette, prio); + gfxDrawPixel(&tileLine.pixels[4], tileBase[3], palette, prio); + gfxDrawPixel(&tileLine.pixels[5], tileBase[2], palette, prio); + gfxDrawPixel(&tileLine.pixels[6], tileBase[1], palette, prio); + gfxDrawPixel(&tileLine.pixels[7], tileBase[0], palette, prio); + } + + return tileLine; +} + +inline const TileLine gfxReadTilePal(const u16 *screenSource, const int yyy, const u8 *charBase, u16 *palette, const u32 prio) +{ + TileEntry tile; + tile.val = READ16LE(screenSource); + + int tileY = yyy & 7; + if (tile.vFlip) tileY = 7 - tileY; + palette += tile.palette * 16; + TileLine tileLine; + + const u8h *tileBase = (u8h*) &charBase[tile.tileNum * 32 + tileY * 4]; + + if (!tile.hFlip) + { + gfxDrawPixel(&tileLine.pixels[0], tileBase[0].lo, palette, prio); + gfxDrawPixel(&tileLine.pixels[1], tileBase[0].hi, palette, prio); + gfxDrawPixel(&tileLine.pixels[2], tileBase[1].lo, palette, prio); + gfxDrawPixel(&tileLine.pixels[3], tileBase[1].hi, palette, prio); + gfxDrawPixel(&tileLine.pixels[4], tileBase[2].lo, palette, prio); + gfxDrawPixel(&tileLine.pixels[5], tileBase[2].hi, palette, prio); + gfxDrawPixel(&tileLine.pixels[6], tileBase[3].lo, palette, prio); + gfxDrawPixel(&tileLine.pixels[7], tileBase[3].hi, palette, prio); + } + else + { + gfxDrawPixel(&tileLine.pixels[0], tileBase[3].hi, palette, prio); + gfxDrawPixel(&tileLine.pixels[1], tileBase[3].lo, palette, prio); + gfxDrawPixel(&tileLine.pixels[2], tileBase[2].hi, palette, prio); + gfxDrawPixel(&tileLine.pixels[3], tileBase[2].lo, palette, prio); + gfxDrawPixel(&tileLine.pixels[4], tileBase[1].hi, palette, prio); + gfxDrawPixel(&tileLine.pixels[5], tileBase[1].lo, palette, prio); + gfxDrawPixel(&tileLine.pixels[6], tileBase[0].hi, palette, prio); + gfxDrawPixel(&tileLine.pixels[7], tileBase[0].lo, palette, prio); + } + + return tileLine; +} + +static inline void gfxDrawTile(const TileLine &tileLine, u32 *line) +{ + memcpy(line, tileLine.pixels, sizeof(tileLine.pixels)); +} + +static inline void gfxDrawTileClipped(const TileLine &tileLine, u32 *line, const int start, int w) +{ + memcpy(line, tileLine.pixels + start, w * sizeof(u32)); +} + +template +static void gfxDrawTextScreen(u16 control, u16 hofs, u16 vofs, + u32 *line) +{ + u16 *palette = (u16 *)paletteRAM; + u8 *charBase = &vram[((control >> 2) & 0x03) * 0x4000]; + u16 *screenBase = (u16 *)&vram[((control >> 8) & 0x1f) * 0x800]; + u32 prio = ((control & 3)<<25) + 0x1000000; + int sizeX = 256; + int sizeY = 256; + switch ((control >> 14) & 3) + { + case 0: + break; + case 1: + sizeX = 512; + break; + case 2: + sizeY = 512; + break; + case 3: + sizeX = 512; + sizeY = 512; + break; + } + + int maskX = sizeX-1; + int maskY = sizeY-1; + + bool mosaicOn = (control & 0x40) ? true : false; + + int xxx = hofs & maskX; + int yyy = (vofs + VCOUNT) & maskY; + int mosaicX = (MOSAIC & 0x000F)+1; + int mosaicY = ((MOSAIC & 0x00F0)>>4)+1; + + if (mosaicOn) + { + if ((VCOUNT % mosaicY) != 0) + { + mosaicY = VCOUNT - (VCOUNT % mosaicY); + yyy = (vofs + mosaicY) & maskY; + } + } + + if (yyy > 255 && sizeY > 256) + { + yyy &= 255; + screenBase += 0x400; + if (sizeX > 256) + screenBase += 0x400; + } + + int yshift = ((yyy>>3)<<5); + + u16 *screenSource = screenBase + 0x400 * (xxx>>8) + ((xxx & 255)>>3) + yshift; + int x = 0; + const int firstTileX = xxx & 7; + + // First tile, if clipped + if (firstTileX) + { + gfxDrawTileClipped(readTile(screenSource, yyy, charBase, palette, prio), &line[x], firstTileX, 8 - firstTileX); + screenSource++; + x += 8 - firstTileX; + xxx += 8 - firstTileX; + + if (xxx == 256 && sizeX > 256) + { + screenSource = screenBase + 0x400 + yshift; + } + else if (xxx >= sizeX) + { + xxx = 0; + screenSource = screenBase + yshift; + } + } + + // Middle tiles, full + while (x < 240 - firstTileX) + { + gfxDrawTile(readTile(screenSource, yyy, charBase, palette, prio), &line[x]); + screenSource++; + xxx += 8; + x += 8; + + if (xxx == 256 && sizeX > 256) + { + screenSource = screenBase + 0x400 + yshift; + } + else if (xxx >= sizeX) + { + xxx = 0; + screenSource = screenBase + yshift; + } + } + + // Last tile, if clipped + if (firstTileX) + { + gfxDrawTileClipped(readTile(screenSource, yyy, charBase, palette, prio), &line[x], 0, firstTileX); + } + + if (mosaicOn) + { + if (mosaicX > 1) + { + int m = 1; + for (int i = 0; i < 239; i++) + { + line[i+1] = line[i]; + m++; + if (m == mosaicX) + { + m = 1; + i++; + } + } + } + } +} + +void gfxDrawTextScreen(u16 control, u16 hofs, u16 vofs, u32 *line) +{ + if (control & 0x80) // 1 pal / 256 col + gfxDrawTextScreen(control, hofs, vofs, line); + else // 16 pal / 16 col + gfxDrawTextScreen(control, hofs, vofs, line); +} +#endif + + +struct EmulatedSystem GBASystem = { + // emuMain + CPULoop, + // emuReset + CPUReset, + // emuCleanUp + CPUCleanUp, + // emuReadBattery + CPUReadBatteryFile, + // emuWriteBattery + CPUWriteBatteryFile, + // emuReadState + CPUReadState, + // emuWriteState + CPUWriteState, + // emuReadMemState +#ifdef __LIBRETRO__ + NULL, +#else + CPUReadMemState, +#endif + // emuWriteMemState + CPUWriteMemState, + // emuWritePNG + CPUWritePNGFile, + // emuWriteBMP + CPUWriteBMPFile, + // emuUpdateCPSR + CPUUpdateCPSR, + // emuHasDebugger + true, + // emuCount +#ifdef FINAL_VERSION + 250000 +#else + 5000 +#endif +}; diff --git a/src/gba/GBA.h b/src/gba/GBA.h new file mode 100644 index 0000000..7da9df1 --- /dev/null +++ b/src/gba/GBA.h @@ -0,0 +1,147 @@ +#ifndef GBA_H +#define GBA_H + +#include "../System.h" + +#define SAVE_GAME_VERSION_1 1 +#define SAVE_GAME_VERSION_2 2 +#define SAVE_GAME_VERSION_3 3 +#define SAVE_GAME_VERSION_4 4 +#define SAVE_GAME_VERSION_5 5 +#define SAVE_GAME_VERSION_6 6 +#define SAVE_GAME_VERSION_7 7 +#define SAVE_GAME_VERSION_8 8 +#define SAVE_GAME_VERSION_9 9 +#define SAVE_GAME_VERSION_10 10 +#define SAVE_GAME_VERSION SAVE_GAME_VERSION_10 + +typedef struct { + u8 *address; + u32 mask; +} memoryMap; + +typedef union { + struct { +#ifdef WORDS_BIGENDIAN + u8 B3; + u8 B2; + u8 B1; + u8 B0; +#else + u8 B0; + u8 B1; + u8 B2; + u8 B3; +#endif + } B; + struct { +#ifdef WORDS_BIGENDIAN + u16 W1; + u16 W0; +#else + u16 W0; + u16 W1; +#endif + } W; +#ifdef WORDS_BIGENDIAN + volatile u32 I; +#else + u32 I; +#endif +} reg_pair; + +#ifndef NO_GBA_MAP +extern memoryMap map[256]; +#endif + +extern reg_pair reg[45]; +extern u8 biosProtected[4]; + +extern bool N_FLAG; +extern bool Z_FLAG; +extern bool C_FLAG; +extern bool V_FLAG; +extern bool armIrqEnable; +extern bool armState; +extern int armMode; +extern void (*cpuSaveGameFunc)(u32,u8); + +#ifdef BKPT_SUPPORT +extern u8 freezeWorkRAM[0x40000]; +extern u8 freezeInternalRAM[0x8000]; +extern u8 freezeVRAM[0x18000]; +extern u8 freezeOAM[0x400]; +extern u8 freezePRAM[0x400]; +extern bool debugger_last; +extern int oldreg[18]; +extern char oldbuffer[10]; +#endif + +extern bool CPUReadGSASnapshot(const char *); +extern bool CPUReadGSASPSnapshot(const char *); +extern bool CPUWriteGSASnapshot(const char *, const char *, const char *, const char *); +extern bool CPUWriteBatteryFile(const char *); +extern bool CPUReadBatteryFile(const char *); +extern bool CPUExportEepromFile(const char *); +extern bool CPUImportEepromFile(const char *); +extern bool CPUWritePNGFile(const char *); +extern bool CPUWriteBMPFile(const char *); +extern void CPUCleanUp(); +extern void CPUUpdateRender(); +extern void CPUUpdateRenderBuffers(bool); +extern bool CPUReadMemState(char *, int); +extern bool CPUWriteMemState(char *, int); +#ifdef __LIBRETRO__ +extern bool CPUReadState(const u8*, unsigned); +extern unsigned int CPUWriteState(u8 *data, unsigned int size); +#else +extern bool CPUReadState(const char *); +extern bool CPUWriteState(const char *); +#endif +extern int CPULoadRom(const char *); +extern void doMirroring(bool); +extern void CPUUpdateRegister(u32, u16); +extern void applyTimer (); +extern void CPUInit(const char *,bool); +extern void CPUReset(); +extern void CPULoop(int); +extern void CPUCheckDMA(int,int); +extern bool CPUIsGBAImage(const char *); +extern bool CPUIsZipFile(const char *); +#ifdef PROFILING +#include "prof/prof.h" +extern void cpuProfil(profile_segment *seg); +extern void cpuEnableProfiling(int hz); +#endif + +extern struct EmulatedSystem GBASystem; + +#define R13_IRQ 18 +#define R14_IRQ 19 +#define SPSR_IRQ 20 +#define R13_USR 26 +#define R14_USR 27 +#define R13_SVC 28 +#define R14_SVC 29 +#define SPSR_SVC 30 +#define R13_ABT 31 +#define R14_ABT 32 +#define SPSR_ABT 33 +#define R13_UND 34 +#define R14_UND 35 +#define SPSR_UND 36 +#define R8_FIQ 37 +#define R9_FIQ 38 +#define R10_FIQ 39 +#define R11_FIQ 40 +#define R12_FIQ 41 +#define R13_FIQ 42 +#define R14_FIQ 43 +#define SPSR_FIQ 44 + +#include "Cheats.h" +#include "Globals.h" +#include "EEprom.h" +#include "Flash.h" + +#endif // GBA_H diff --git a/src/gba/GBAGfx.cpp b/src/gba/GBAGfx.cpp new file mode 100644 index 0000000..398e238 --- /dev/null +++ b/src/gba/GBAGfx.cpp @@ -0,0 +1,25 @@ +#include "../System.h" + +int coeff[32] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}; + +u32 line0[240]; +u32 line1[240]; +u32 line2[240]; +u32 line3[240]; +u32 lineOBJ[240]; +u32 lineOBJWin[240]; +u32 lineMix[240]; +bool gfxInWin0[240]; +bool gfxInWin1[240]; +int lineOBJpixleft[128]; + +int gfxBG2Changed = 0; +int gfxBG3Changed = 0; + +int gfxBG2X = 0; +int gfxBG2Y = 0; +int gfxBG3X = 0; +int gfxBG3Y = 0; +int gfxLastVCOUNT = 0; diff --git a/src/gba/GBAGfx.h b/src/gba/GBAGfx.h new file mode 100644 index 0000000..e90ef43 --- /dev/null +++ b/src/gba/GBAGfx.h @@ -0,0 +1,1583 @@ +#ifndef GFX_H +#define GFX_H + +#include "GBA.h" +#include "Globals.h" + +#include "../common/Port.h" + +//#define SPRITE_DEBUG + +#ifdef TILED_RENDERING +extern void gfxDrawTextScreen(u16, u16, u16, u32 *); +#else +static void gfxDrawTextScreen(u16, u16, u16, u32 *); +#endif +static void gfxDrawRotScreen(u16, + u16, u16, + u16, u16, + u16, u16, + u16, u16, + int&, int&, + int, + u32*); +static void gfxDrawRotScreen16Bit(u16, + u16, u16, + u16, u16, + u16, u16, + u16, u16, + int&, int&, + int, + u32*); +static void gfxDrawRotScreen256(u16, + u16, u16, + u16, u16, + u16, u16, + u16, u16, + int&, int&, + int, + u32*); +static void gfxDrawRotScreen16Bit160(u16, + u16, u16, + u16, u16, + u16, u16, + u16, u16, + int&, int&, + int, + u32*); +static void gfxDrawSprites(u32 *); +static void gfxIncreaseBrightness(u32 *line, int coeff); +static void gfxDecreaseBrightness(u32 *line, int coeff); +static void gfxAlphaBlend(u32 *ta, u32 *tb, int ca, int cb); + +void mode0RenderLine(); +void mode0RenderLineNoWindow(); +void mode0RenderLineAll(); + +void mode1RenderLine(); +void mode1RenderLineNoWindow(); +void mode1RenderLineAll(); + +void mode2RenderLine(); +void mode2RenderLineNoWindow(); +void mode2RenderLineAll(); + +void mode3RenderLine(); +void mode3RenderLineNoWindow(); +void mode3RenderLineAll(); + +void mode4RenderLine(); +void mode4RenderLineNoWindow(); +void mode4RenderLineAll(); + +void mode5RenderLine(); +void mode5RenderLineNoWindow(); +void mode5RenderLineAll(); + +extern int coeff[32]; +extern u32 line0[240]; +extern u32 line1[240]; +extern u32 line2[240]; +extern u32 line3[240]; +extern u32 lineOBJ[240]; +extern u32 lineOBJWin[240]; +extern u32 lineMix[240]; +extern bool gfxInWin0[240]; +extern bool gfxInWin1[240]; +extern int lineOBJpixleft[128]; + +extern int gfxBG2Changed; +extern int gfxBG3Changed; + +extern int gfxBG2X; +extern int gfxBG2Y; +extern int gfxBG3X; +extern int gfxBG3Y; +extern int gfxLastVCOUNT; + +static inline void gfxClearArray(u32 *array) +{ + for(int i = 0; i < 240; i++) { + *array++ = 0x80000000; + } +} + +#ifndef TILED_RENDERING +static inline void gfxDrawTextScreen(u16 control, u16 hofs, u16 vofs, + u32 *line) +{ + u16 *palette = (u16 *)paletteRAM; + u8 *charBase = &vram[((control >> 2) & 0x03) * 0x4000]; + u16 *screenBase = (u16 *)&vram[((control >> 8) & 0x1f) * 0x800]; + u32 prio = ((control & 3)<<25) + 0x1000000; + int sizeX = 256; + int sizeY = 256; + switch((control >> 14) & 3) { + case 0: + break; + case 1: + sizeX = 512; + break; + case 2: + sizeY = 512; + break; + case 3: + sizeX = 512; + sizeY = 512; + break; + } + + int maskX = sizeX-1; + int maskY = sizeY-1; + + bool mosaicOn = (control & 0x40) ? true : false; + + int xxx = hofs & maskX; + int yyy = (vofs + VCOUNT) & maskY; + int mosaicX = (MOSAIC & 0x000F)+1; + int mosaicY = ((MOSAIC & 0x00F0)>>4)+1; + + if(mosaicOn) { + if((VCOUNT % mosaicY) != 0) { + mosaicY = VCOUNT - (VCOUNT % mosaicY); + yyy = (vofs + mosaicY) & maskY; + } + } + + if(yyy > 255 && sizeY > 256) { + yyy &= 255; + screenBase += 0x400; + if(sizeX > 256) + screenBase += 0x400; + } + + int yshift = ((yyy>>3)<<5); + if((control) & 0x80) { + u16 *screenSource = screenBase + 0x400 * (xxx>>8) + ((xxx & 255)>>3) + yshift; + for(int x = 0; x < 240; x++) { + u16 data = READ16LE(screenSource); + + int tile = data & 0x3FF; + int tileX = (xxx & 7); + int tileY = yyy & 7; + + if(tileX == 7) + screenSource++; + + if(data & 0x0400) + tileX = 7 - tileX; + if(data & 0x0800) + tileY = 7 - tileY; + + u8 color = charBase[tile * 64 + tileY * 8 + tileX]; + + line[x] = color ? (READ16LE(&palette[color]) | prio): 0x80000000; + + xxx++; + if(xxx == 256) { + if(sizeX > 256) + screenSource = screenBase + 0x400 + yshift; + else { + screenSource = screenBase + yshift; + xxx = 0; + } + } else if(xxx >= sizeX) { + xxx = 0; + screenSource = screenBase + yshift; + } + } + } else { + u16 *screenSource = screenBase + 0x400*(xxx>>8)+((xxx&255)>>3) + + yshift; + for(int x = 0; x < 240; x++) { + u16 data = READ16LE(screenSource); + + int tile = data & 0x3FF; + int tileX = (xxx & 7); + int tileY = yyy & 7; + + if(tileX == 7) + screenSource++; + + if(data & 0x0400) + tileX = 7 - tileX; + if(data & 0x0800) + tileY = 7 - tileY; + + u8 color = charBase[(tile<<5) + (tileY<<2) + (tileX>>1)]; + + if(tileX & 1) { + color = (color >> 4); + } else { + color &= 0x0F; + } + + int pal = (data>>8) & 0xF0; + line[x] = color ? (READ16LE(&palette[pal + color])|prio): 0x80000000; + + xxx++; + if(xxx == 256) { + if(sizeX > 256) + screenSource = screenBase + 0x400 + yshift; + else { + screenSource = screenBase + yshift; + xxx = 0; + } + } else if(xxx >= sizeX) { + xxx = 0; + screenSource = screenBase + yshift; + } + } + } + if(mosaicOn) { + if(mosaicX > 1) { + int m = 1; + for(int i = 0; i < 239; i++) { + line[i+1] = line[i]; + m++; + if(m == mosaicX) { + m = 1; + i++; + } + } + } + } +} +#endif + +static inline void gfxDrawRotScreen(u16 control, + u16 x_l, u16 x_h, + u16 y_l, u16 y_h, + u16 pa, u16 pb, + u16 pc, u16 pd, + int& currentX, int& currentY, + int changed, + u32 *line) +{ + u16 *palette = (u16 *)paletteRAM; + u8 *charBase = &vram[((control >> 2) & 0x03) * 0x4000]; + u8 *screenBase = (u8 *)&vram[((control >> 8) & 0x1f) * 0x800]; + int prio = ((control & 3) << 25) + 0x1000000; + + int sizeX = 128; + int sizeY = 128; + switch((control >> 14) & 3) { + case 0: + break; + case 1: + sizeX = sizeY = 256; + break; + case 2: + sizeX = sizeY = 512; + break; + case 3: + sizeX = sizeY = 1024; + break; + } + + int maskX = sizeX-1; + int maskY = sizeY-1; + + int yshift = ((control >> 14) & 3)+4; + + int dx = pa & 0x7FFF; + if(pa & 0x8000) + dx |= 0xFFFF8000; + int dmx = pb & 0x7FFF; + if(pb & 0x8000) + dmx |= 0xFFFF8000; + int dy = pc & 0x7FFF; + if(pc & 0x8000) + dy |= 0xFFFF8000; + int dmy = pd & 0x7FFF; + if(pd & 0x8000) + dmy |= 0xFFFF8000; + + if(VCOUNT == 0) + changed = 3; + + if(changed & 1) { + currentX = (x_l) | ((x_h & 0x07FF)<<16); + if(x_h & 0x0800) + currentX |= 0xF8000000; + } else { + currentX += dmx; + } + + if(changed & 2) { + currentY = (y_l) | ((y_h & 0x07FF)<<16); + if(y_h & 0x0800) + currentY |= 0xF8000000; + } else { + currentY += dmy; + } + + int realX = currentX; + int realY = currentY; + + if(control & 0x40) { + int mosaicY = ((MOSAIC & 0xF0)>>4) + 1; + int y = (VCOUNT % mosaicY); + realX -= y*dmx; + realY -= y*dmy; + } + + if(control & 0x2000) { + for(int x = 0; x < 240; x++) { + int xxx = (realX >> 8) & maskX; + int yyy = (realY >> 8) & maskY; + + int tile = screenBase[(xxx>>3) + ((yyy>>3)<> 8); + int yyy = (realY >> 8); + + if(xxx < 0 || + yyy < 0 || + xxx >= sizeX || + yyy >= sizeY) { + line[x] = 0x80000000; + } else { + int tile = screenBase[(xxx>>3) + ((yyy>>3)< 1) { + int m = 1; + for(int i = 0; i < 239; i++) { + line[i+1] = line[i]; + m++; + if(m == mosaicX) { + m = 1; + i++; + } + } + } + } +} + +static inline void gfxDrawRotScreen16Bit(u16 control, + u16 x_l, u16 x_h, + u16 y_l, u16 y_h, + u16 pa, u16 pb, + u16 pc, u16 pd, + int& currentX, int& currentY, + int changed, + u32 *line) +{ + u16 *screenBase = (u16 *)&vram[0]; + int prio = ((control & 3) << 25) + 0x1000000; + int sizeX = 240; + int sizeY = 160; + + int startX = (x_l) | ((x_h & 0x07FF)<<16); + if(x_h & 0x0800) + startX |= 0xF8000000; + int startY = (y_l) | ((y_h & 0x07FF)<<16); + if(y_h & 0x0800) + startY |= 0xF8000000; + + int dx = pa & 0x7FFF; + if(pa & 0x8000) + dx |= 0xFFFF8000; + int dmx = pb & 0x7FFF; + if(pb & 0x8000) + dmx |= 0xFFFF8000; + int dy = pc & 0x7FFF; + if(pc & 0x8000) + dy |= 0xFFFF8000; + int dmy = pd & 0x7FFF; + if(pd & 0x8000) + dmy |= 0xFFFF8000; + + if(VCOUNT == 0) + changed = 3; + + if(changed & 1) { + currentX = (x_l) | ((x_h & 0x07FF)<<16); + if(x_h & 0x0800) + currentX |= 0xF8000000; + } else + currentX += dmx; + + if(changed & 2) { + currentY = (y_l) | ((y_h & 0x07FF)<<16); + if(y_h & 0x0800) + currentY |= 0xF8000000; + } else { + currentY += dmy; + } + + int realX = currentX; + int realY = currentY; + + if(control & 0x40) { + int mosaicY = ((MOSAIC & 0xF0)>>4) + 1; + int y = (VCOUNT % mosaicY); + realX -= y*dmx; + realY -= y*dmy; + } + + int xxx = (realX >> 8); + int yyy = (realY >> 8); + + for(int x = 0; x < 240; x++) { + if(xxx < 0 || + yyy < 0 || + xxx >= sizeX || + yyy >= sizeY) { + line[x] = 0x80000000; + } else { + line[x] = (READ16LE(&screenBase[yyy * sizeX + xxx]) | prio); + } + realX += dx; + realY += dy; + + xxx = (realX >> 8); + yyy = (realY >> 8); + } + + if(control & 0x40) { + int mosaicX = (MOSAIC & 0xF) + 1; + if(mosaicX > 1) { + int m = 1; + for(int i = 0; i < 239; i++) { + line[i+1] = line[i]; + m++; + if(m == mosaicX) { + m = 1; + i++; + } + } + } + } +} + +static inline void gfxDrawRotScreen256(u16 control, + u16 x_l, u16 x_h, + u16 y_l, u16 y_h, + u16 pa, u16 pb, + u16 pc, u16 pd, + int ¤tX, int& currentY, + int changed, + u32 *line) +{ + u16 *palette = (u16 *)paletteRAM; + u8 *screenBase = (DISPCNT & 0x0010) ? &vram[0xA000] : &vram[0x0000]; + int prio = ((control & 3) << 25) + 0x1000000; + int sizeX = 240; + int sizeY = 160; + + int startX = (x_l) | ((x_h & 0x07FF)<<16); + if(x_h & 0x0800) + startX |= 0xF8000000; + int startY = (y_l) | ((y_h & 0x07FF)<<16); + if(y_h & 0x0800) + startY |= 0xF8000000; + + int dx = pa & 0x7FFF; + if(pa & 0x8000) + dx |= 0xFFFF8000; + int dmx = pb & 0x7FFF; + if(pb & 0x8000) + dmx |= 0xFFFF8000; + int dy = pc & 0x7FFF; + if(pc & 0x8000) + dy |= 0xFFFF8000; + int dmy = pd & 0x7FFF; + if(pd & 0x8000) + dmy |= 0xFFFF8000; + + if(VCOUNT == 0) + changed = 3; + + if(changed & 1) { + currentX = (x_l) | ((x_h & 0x07FF)<<16); + if(x_h & 0x0800) + currentX |= 0xF8000000; + } else { + currentX += dmx; + } + + if(changed & 2) { + currentY = (y_l) | ((y_h & 0x07FF)<<16); + if(y_h & 0x0800) + currentY |= 0xF8000000; + } else { + currentY += dmy; + } + + int realX = currentX; + int realY = currentY; + + if(control & 0x40) { + int mosaicY = ((MOSAIC & 0xF0)>>4) + 1; + int y = VCOUNT - (VCOUNT % mosaicY); + realX = startX + y*dmx; + realY = startY + y*dmy; + } + + int xxx = (realX >> 8); + int yyy = (realY >> 8); + + for(int x = 0; x < 240; x++) { + if(xxx < 0 || + yyy < 0 || + xxx >= sizeX || + yyy >= sizeY) { + line[x] = 0x80000000; + } else { + u8 color = screenBase[yyy * 240 + xxx]; + + line[x] = color ? (READ16LE(&palette[color])|prio): 0x80000000; + } + realX += dx; + realY += dy; + + xxx = (realX >> 8); + yyy = (realY >> 8); + } + + if(control & 0x40) { + int mosaicX = (MOSAIC & 0xF) + 1; + if(mosaicX > 1) { + int m = 1; + for(int i = 0; i < 239; i++) { + line[i+1] = line[i]; + m++; + if(m == mosaicX) { + m = 1; + i++; + } + } + } + } +} + +static inline void gfxDrawRotScreen16Bit160(u16 control, + u16 x_l, u16 x_h, + u16 y_l, u16 y_h, + u16 pa, u16 pb, + u16 pc, u16 pd, + int& currentX, int& currentY, + int changed, + u32 *line) +{ + u16 *screenBase = (DISPCNT & 0x0010) ? (u16 *)&vram[0xa000] : + (u16 *)&vram[0]; + int prio = ((control & 3) << 25) + 0x1000000; + int sizeX = 160; + int sizeY = 128; + + int startX = (x_l) | ((x_h & 0x07FF)<<16); + if(x_h & 0x0800) + startX |= 0xF8000000; + int startY = (y_l) | ((y_h & 0x07FF)<<16); + if(y_h & 0x0800) + startY |= 0xF8000000; + + int dx = pa & 0x7FFF; + if(pa & 0x8000) + dx |= 0xFFFF8000; + int dmx = pb & 0x7FFF; + if(pb & 0x8000) + dmx |= 0xFFFF8000; + int dy = pc & 0x7FFF; + if(pc & 0x8000) + dy |= 0xFFFF8000; + int dmy = pd & 0x7FFF; + if(pd & 0x8000) + dmy |= 0xFFFF8000; + + if(VCOUNT == 0) + changed = 3; + + if(changed & 1) { + currentX = (x_l) | ((x_h & 0x07FF)<<16); + if(x_h & 0x0800) + currentX |= 0xF8000000; + } else { + currentX += dmx; + } + + if(changed & 2) { + currentY = (y_l) | ((y_h & 0x07FF)<<16); + if(y_h & 0x0800) + currentY |= 0xF8000000; + } else { + currentY += dmy; + } + + int realX = currentX; + int realY = currentY; + + if(control & 0x40) { + int mosaicY = ((MOSAIC & 0xF0)>>4) + 1; + int y = VCOUNT - (VCOUNT % mosaicY); + realX = startX + y*dmx; + realY = startY + y*dmy; + } + + int xxx = (realX >> 8); + int yyy = (realY >> 8); + + for(int x = 0; x < 240; x++) { + if(xxx < 0 || + yyy < 0 || + xxx >= sizeX || + yyy >= sizeY) { + line[x] = 0x80000000; + } else { + line[x] = (READ16LE(&screenBase[yyy * sizeX + xxx]) | prio); + } + realX += dx; + realY += dy; + + xxx = (realX >> 8); + yyy = (realY >> 8); + } + + if(control & 0x40) { + int mosaicX = (MOSAIC & 0xF) + 1; + if(mosaicX > 1) { + int m = 1; + for(int i = 0; i < 239; i++) { + line[i+1] = line[i]; + m++; + if(m == mosaicX) { + m = 1; + i++; + } + } + } + } +} + +static inline void gfxDrawSprites(u32 *lineOBJ) +{ + // lineOBJpix is used to keep track of the drawn OBJs + // and to stop drawing them if the 'maximum number of OBJ per line' + // has been reached. + int lineOBJpix = (DISPCNT & 0x20) ? 954 : 1226; + int m=0; + gfxClearArray(lineOBJ); + if(layerEnable & 0x1000) { + u16 *sprites = (u16 *)oam; + u16 *spritePalette = &((u16 *)paletteRAM)[256]; + int mosaicY = ((MOSAIC & 0xF000)>>12) + 1; + int mosaicX = ((MOSAIC & 0xF00)>>8) + 1; + for(int x = 0; x < 128 ; x++) { + u16 a0 = READ16LE(sprites++); + u16 a1 = READ16LE(sprites++); + u16 a2 = READ16LE(sprites++); + sprites++; + + lineOBJpixleft[x]=lineOBJpix; + + lineOBJpix-=2; + if (lineOBJpix<=0) + continue; + + if ((a0 & 0x0c00) == 0x0c00) + a0 &=0xF3FF; + + if ((a0>>14) == 3) + { + a0 &= 0x3FFF; + a1 &= 0x3FFF; + } + + int sizeX = 8<<(a1>>14); + int sizeY = sizeX; + + if ((a0>>14) & 1) + { + if (sizeX<32) + sizeX<<=1; + if (sizeY>8) + sizeY>>=1; + } + else if ((a0>>14) & 2) + { + if (sizeX>8) + sizeX>>=1; + if (sizeY<32) + sizeY<<=1; + } + +#ifdef SPRITE_DEBUG + int maskX = sizeX-1; + int maskY = sizeY-1; +#endif + + int sy = (a0 & 255); + int sx = (a1 & 0x1FF); + + // computes ticks used by OBJ-WIN if OBJWIN is enabled + if (((a0 & 0x0c00) == 0x0800) && (layerEnable & 0x8000)) + { + if ((a0 & 0x0300) == 0x0300) + { + sizeX<<=1; + sizeY<<=1; + } + if((sy+sizeY) > 256) + sy -= 256; + if ((sx+sizeX)> 512) + sx-=512; + if (sx<0) + { + sizeX+=sx; + sx = 0; + } + else if ((sx+sizeX)>240) + sizeX=240-sx; + if ((VCOUNT>=sy) && (VCOUNT 256) + sy -= 256; + int t = VCOUNT - sy; + if((t >= 0) && (t < fieldY)) { + int startpix = 0; + if ((sx+fieldX)> 512) + { + startpix=512-sx; + } + if (lineOBJpix>0) + if((sx < 240) || startpix) { + lineOBJpix-=8; + // int t2 = t - (fieldY >> 1); + int rot = (a1 >> 9) & 0x1F; + u16 *OAM = (u16 *)oam; + int dx = READ16LE(&OAM[3 + (rot << 4)]); + if(dx & 0x8000) + dx |= 0xFFFF8000; + int dmx = READ16LE(&OAM[7 + (rot << 4)]); + if(dmx & 0x8000) + dmx |= 0xFFFF8000; + int dy = READ16LE(&OAM[11 + (rot << 4)]); + if(dy & 0x8000) + dy |= 0xFFFF8000; + int dmy = READ16LE(&OAM[15 + (rot << 4)]); + if(dmy & 0x8000) + dmy |= 0xFFFF8000; + + if(a0 & 0x1000) { + t -= (t % mosaicY); + } + + int realX = ((sizeX) << 7) - (fieldX >> 1)*dx - (fieldY>>1)*dmx + + t * dmx; + int realY = ((sizeY) << 7) - (fieldX >> 1)*dy - (fieldY>>1)*dmy + + t * dmy; + + u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); + + if(a0 & 0x2000) { + int c = (a2 & 0x3FF); + if((DISPCNT & 7) > 2 && (c < 512)) + continue; + int inc = 32; + if(DISPCNT & 0x40) + inc = sizeX >> 2; + else + c &= 0x3FE; + for(int x = 0; x < fieldX; x++) { + if (x >= startpix) + lineOBJpix-=2; + if (lineOBJpix<0) + continue; + int xxx = realX >> 8; + int yyy = realY >> 8; + + if(xxx < 0 || xxx >= sizeX || + yyy < 0 || yyy >= sizeY || + sx >= 240); + else { + u32 color = vram[0x10000 + ((((c + (yyy>>3) * inc)<<5) + + ((yyy & 7)<<3) + ((xxx >> 3)<<6) + + (xxx & 7))&0x7FFF)]; + if ((color==0) && (((prio >> 25)&3) < + ((lineOBJ[sx]>>25)&3))) { + lineOBJ[sx] = (lineOBJ[sx] & 0xF9FFFFFF) | prio; + if((a0 & 0x1000) && m) + lineOBJ[sx]=(lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } else if((color) && (prio < (lineOBJ[sx]&0xFF000000))) { + lineOBJ[sx] = READ16LE(&spritePalette[color]) | prio; + if((a0 & 0x1000) && m) + lineOBJ[sx]=(lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } + + if (a0 & 0x1000) { + m++; + if (m==mosaicX) + m=0; + } +#ifdef SPRITE_DEBUG + if(t == 0 || t == maskY || x == 0 || x == maskX) + lineOBJ[sx] = 0x001F; +#endif + } + sx = (sx+1)&511; + realX += dx; + realY += dy; + } + } else { + int c = (a2 & 0x3FF); + if((DISPCNT & 7) > 2 && (c < 512)) + continue; + + int inc = 32; + if(DISPCNT & 0x40) + inc = sizeX >> 3; + int palette = (a2 >> 8) & 0xF0; + for(int x = 0; x < fieldX; x++) { + if (x >= startpix) + lineOBJpix-=2; + if (lineOBJpix<0) + continue; + int xxx = realX >> 8; + int yyy = realY >> 8; + if(xxx < 0 || xxx >= sizeX || + yyy < 0 || yyy >= sizeY || + sx >= 240); + else { + u32 color = vram[0x10000 + ((((c + (yyy>>3) * inc)<<5) + + ((yyy & 7)<<2) + ((xxx >> 3)<<5) + + ((xxx & 7)>>1))&0x7FFF)]; + if(xxx & 1) + color >>= 4; + else + color &= 0x0F; + + if ((color==0) && (((prio >> 25)&3) < + ((lineOBJ[sx]>>25)&3))) { + lineOBJ[sx] = (lineOBJ[sx] & 0xF9FFFFFF) | prio; + if((a0 & 0x1000) && m) + lineOBJ[sx]=(lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } else if((color) && (prio < (lineOBJ[sx]&0xFF000000))) { + lineOBJ[sx] = READ16LE(&spritePalette[palette+color]) | prio; + if((a0 & 0x1000) && m) + lineOBJ[sx]=(lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } + } + if((a0 & 0x1000) && m) { + m++; + if (m==mosaicX) + m=0; + } + +#ifdef SPRITE_DEBUG + if(t == 0 || t == maskY || x == 0 || x == maskX) + lineOBJ[sx] = 0x001F; +#endif + sx = (sx+1)&511; + realX += dx; + realY += dy; + + } + } + } + } + } else { + if(sy+sizeY > 256) + sy -= 256; + int t = VCOUNT - sy; + if((t >= 0) && (t < sizeY)) { + int startpix = 0; + if ((sx+sizeX)> 512) + { + startpix=512-sx; + } + if((sx < 240) || startpix) { + lineOBJpix+=2; + if(a0 & 0x2000) { + if(a1 & 0x2000) + t = sizeY - t - 1; + int c = (a2 & 0x3FF); + if((DISPCNT & 7) > 2 && (c < 512)) + continue; + + int inc = 32; + if(DISPCNT & 0x40) { + inc = sizeX >> 2; + } else { + c &= 0x3FE; + } + int xxx = 0; + if(a1 & 0x1000) + xxx = sizeX-1; + + if(a0 & 0x1000) { + t -= (t % mosaicY); + } + + int address = 0x10000 + ((((c+ (t>>3) * inc) << 5) + + ((t & 7) << 3) + ((xxx>>3)<<6) + (xxx & 7)) & 0x7FFF); + + if(a1 & 0x1000) + xxx = 7; + u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); + + for(int xx = 0; xx < sizeX; xx++) { + if (xx >= startpix) + lineOBJpix--; + if (lineOBJpix<0) + continue; + if(sx < 240) { + u8 color = vram[address]; + if ((color==0) && (((prio >> 25)&3) < + ((lineOBJ[sx]>>25)&3))) { + lineOBJ[sx] = (lineOBJ[sx] & 0xF9FFFFFF) | prio; + if((a0 & 0x1000) && m) + lineOBJ[sx]=(lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } else if((color) && (prio < (lineOBJ[sx]&0xFF000000))) { + lineOBJ[sx] = READ16LE(&spritePalette[color]) | prio; + if((a0 & 0x1000) && m) + lineOBJ[sx]=(lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } + + if (a0 & 0x1000) { + m++; + if (m==mosaicX) + m=0; + } + +#ifdef SPRITE_DEBUG + if(t == 0 || t == maskY || xx == 0 || xx == maskX) + lineOBJ[sx] = 0x001F; +#endif + } + + sx = (sx+1) & 511; + if(a1 & 0x1000) { + xxx--; + address--; + if(xxx == -1) { + address -= 56; + xxx = 7; + } + if(address < 0x10000) + address += 0x8000; + } else { + xxx++; + address++; + if(xxx == 8) { + address += 56; + xxx = 0; + } + if(address > 0x17fff) + address -= 0x8000; + } + } + } else { + if(a1 & 0x2000) + t = sizeY - t - 1; + int c = (a2 & 0x3FF); + if((DISPCNT & 7) > 2 && (c < 512)) + continue; + + int inc = 32; + if(DISPCNT & 0x40) { + inc = sizeX >> 3; + } + int xxx = 0; + if(a1 & 0x1000) + xxx = sizeX - 1; + + if(a0 & 0x1000) { + t -= (t % mosaicY); + } + + int address = 0x10000 + ((((c + (t>>3) * inc)<<5) + + ((t & 7)<<2) + ((xxx>>3)<<5) + ((xxx & 7) >> 1))&0x7FFF); + u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); + int palette = (a2 >> 8) & 0xF0; + if(a1 & 0x1000) { + xxx = 7; + for(int xx = sizeX - 1; xx >= 0; xx--) { + if (xx >= startpix) + lineOBJpix--; + if (lineOBJpix<0) + continue; + if(sx < 240) { + u8 color = vram[address]; + if(xx & 1) { + color = (color >> 4); + } else + color &= 0x0F; + + if ((color==0) && (((prio >> 25)&3) < + ((lineOBJ[sx]>>25)&3))) { + lineOBJ[sx] = (lineOBJ[sx] & 0xF9FFFFFF) | prio; + if((a0 & 0x1000) && m) + lineOBJ[sx]=(lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } else if((color) && (prio < (lineOBJ[sx]&0xFF000000))) { + lineOBJ[sx] = READ16LE(&spritePalette[palette + color]) | prio; + if((a0 & 0x1000) && m) + lineOBJ[sx]=(lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } + } + if (a0 & 0x1000) { + m++; + if (m==mosaicX) + m=0; + } +#ifdef SPRITE_DEBUG + if(t == 0 || t == maskY || xx == 0 || xx == maskX) + lineOBJ[sx] = 0x001F; +#endif + sx = (sx+1) & 511; + xxx--; + if(!(xx & 1)) + address--; + if(xxx == -1) { + xxx = 7; + address -= 28; + } + if(address < 0x10000) + address += 0x8000; + } + } else { + for(int xx = 0; xx < sizeX; xx++) { + if (xx >= startpix) + lineOBJpix--; + if (lineOBJpix<0) + continue; + if(sx < 240) { + u8 color = vram[address]; + if(xx & 1) { + color = (color >> 4); + } else + color &= 0x0F; + + if ((color==0) && (((prio >> 25)&3) < + ((lineOBJ[sx]>>25)&3))) { + lineOBJ[sx] = (lineOBJ[sx] & 0xF9FFFFFF) | prio; + if((a0 & 0x1000) && m) + lineOBJ[sx]=(lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } else if((color) && (prio < (lineOBJ[sx]&0xFF000000))) { + lineOBJ[sx] = READ16LE(&spritePalette[palette + color]) | prio; + if((a0 & 0x1000) && m) + lineOBJ[sx]=(lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + + } + } + if (a0 & 0x1000) { + m++; + if (m==mosaicX) + m=0; + } +#ifdef SPRITE_DEBUG + if(t == 0 || t == maskY || xx == 0 || xx == maskX) + lineOBJ[sx] = 0x001F; +#endif + sx = (sx+1) & 511; + xxx++; + if(xx & 1) + address++; + if(xxx == 8) { + address += 28; + xxx = 0; + } + if(address > 0x17fff) + address -= 0x8000; + } + } + } + } + } + } + } + } +} + +static inline void gfxDrawOBJWin(u32 *lineOBJWin) +{ + gfxClearArray(lineOBJWin); + if((layerEnable & 0x9000) == 0x9000) { + u16 *sprites = (u16 *)oam; + // u16 *spritePalette = &((u16 *)paletteRAM)[256]; + for(int x = 0; x < 128 ; x++) { + int lineOBJpix = lineOBJpixleft[x]; + u16 a0 = READ16LE(sprites++); + u16 a1 = READ16LE(sprites++); + u16 a2 = READ16LE(sprites++); + sprites++; + + if (lineOBJpix<=0) + continue; + + // ignores non OBJ-WIN and disabled OBJ-WIN + if(((a0 & 0x0c00) != 0x0800) || ((a0 & 0x0300) == 0x0200)) + continue; + + if ((a0 & 0x0c00) == 0x0c00) + a0 &=0xF3FF; + + if ((a0>>14) == 3) + { + a0 &= 0x3FFF; + a1 &= 0x3FFF; + } + + int sizeX = 8<<(a1>>14); + int sizeY = sizeX; + + if ((a0>>14) & 1) + { + if (sizeX<32) + sizeX<<=1; + if (sizeY>8) + sizeY>>=1; + } + else if ((a0>>14) & 2) + { + if (sizeX>8) + sizeX>>=1; + if (sizeY<32) + sizeY<<=1; + } + + int sy = (a0 & 255); + + if(a0 & 0x0100) { + int fieldX = sizeX; + int fieldY = sizeY; + if(a0 & 0x0200) { + fieldX <<= 1; + fieldY <<= 1; + } + if((sy+fieldY) > 256) + sy -= 256; + int t = VCOUNT - sy; + if((t >= 0) && (t < fieldY)) { + int sx = (a1 & 0x1FF); + int startpix = 0; + if ((sx+fieldX)> 512) + { + startpix=512-sx; + } + if((sx < 240) || startpix) { + lineOBJpix-=8; + // int t2 = t - (fieldY >> 1); + int rot = (a1 >> 9) & 0x1F; + u16 *OAM = (u16 *)oam; + int dx = READ16LE(&OAM[3 + (rot << 4)]); + if(dx & 0x8000) + dx |= 0xFFFF8000; + int dmx = READ16LE(&OAM[7 + (rot << 4)]); + if(dmx & 0x8000) + dmx |= 0xFFFF8000; + int dy = READ16LE(&OAM[11 + (rot << 4)]); + if(dy & 0x8000) + dy |= 0xFFFF8000; + int dmy = READ16LE(&OAM[15 + (rot << 4)]); + if(dmy & 0x8000) + dmy |= 0xFFFF8000; + + int realX = ((sizeX) << 7) - (fieldX >> 1)*dx - (fieldY>>1)*dmx + + t * dmx; + int realY = ((sizeY) << 7) - (fieldX >> 1)*dy - (fieldY>>1)*dmy + + t * dmy; + + // u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); + + if(a0 & 0x2000) { + int c = (a2 & 0x3FF); + if((DISPCNT & 7) > 2 && (c < 512)) + continue; + int inc = 32; + if(DISPCNT & 0x40) + inc = sizeX >> 2; + else + c &= 0x3FE; + for(int x = 0; x < fieldX; x++) { + if (x >= startpix) + lineOBJpix-=2; + if (lineOBJpix<0) + continue; + int xxx = realX >> 8; + int yyy = realY >> 8; + + if(xxx < 0 || xxx >= sizeX || + yyy < 0 || yyy >= sizeY || + sx >= 240) { + } else { + u32 color = vram[0x10000 + ((((c + (yyy>>3) * inc)<<5) + + ((yyy & 7)<<3) + ((xxx >> 3)<<6) + + (xxx & 7))&0x7fff)]; + if(color) { + lineOBJWin[sx] = 1; + } + } + sx = (sx+1)&511; + realX += dx; + realY += dy; + } + } else { + int c = (a2 & 0x3FF); + if((DISPCNT & 7) > 2 && (c < 512)) + continue; + + int inc = 32; + if(DISPCNT & 0x40) + inc = sizeX >> 3; + // int palette = (a2 >> 8) & 0xF0; + for(int x = 0; x < fieldX; x++) { + if (x >= startpix) + lineOBJpix-=2; + if (lineOBJpix<0) + continue; + int xxx = realX >> 8; + int yyy = realY >> 8; + + // if(x == 0 || x == (sizeX-1) || + // t == 0 || t == (sizeY-1)) { + // lineOBJ[sx] = 0x001F | prio; + // } else { + if(xxx < 0 || xxx >= sizeX || + yyy < 0 || yyy >= sizeY || + sx >= 240) { + } else { + u32 color = vram[0x10000 + ((((c + (yyy>>3) * inc)<<5) + + ((yyy & 7)<<2) + ((xxx >> 3)<<5) + + ((xxx & 7)>>1))&0x7fff)]; + if(xxx & 1) + color >>= 4; + else + color &= 0x0F; + + if(color) { + lineOBJWin[sx] = 1; + } + } + // } + sx = (sx+1)&511; + realX += dx; + realY += dy; + } + } + } + } + } else { + if((sy+sizeY) > 256) + sy -= 256; + int t = VCOUNT - sy; + if((t >= 0) && (t < sizeY)) { + int sx = (a1 & 0x1FF); + int startpix = 0; + if ((sx+sizeX)> 512) + { + startpix=512-sx; + } + if((sx < 240) || startpix) { + lineOBJpix+=2; + if(a0 & 0x2000) { + if(a1 & 0x2000) + t = sizeY - t - 1; + int c = (a2 & 0x3FF); + if((DISPCNT & 7) > 2 && (c < 512)) + continue; + + int inc = 32; + if(DISPCNT & 0x40) { + inc = sizeX >> 2; + } else { + c &= 0x3FE; + } + int xxx = 0; + if(a1 & 0x1000) + xxx = sizeX-1; + int address = 0x10000 + ((((c+ (t>>3) * inc) << 5) + + ((t & 7) << 3) + ((xxx>>3)<<6) + (xxx & 7))&0x7fff); + if(a1 & 0x1000) + xxx = 7; + // u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); + for(int xx = 0; xx < sizeX; xx++) { + if (xx >= startpix) + lineOBJpix--; + if (lineOBJpix<0) + continue; + if(sx < 240) { + u8 color = vram[address]; + if(color) { + lineOBJWin[sx] = 1; + } + } + + sx = (sx+1) & 511; + if(a1 & 0x1000) { + xxx--; + address--; + if(xxx == -1) { + address -= 56; + xxx = 7; + } + if(address < 0x10000) + address += 0x8000; + } else { + xxx++; + address++; + if(xxx == 8) { + address += 56; + xxx = 0; + } + if(address > 0x17fff) + address -= 0x8000; + } + } + } else { + if(a1 & 0x2000) + t = sizeY - t - 1; + int c = (a2 & 0x3FF); + if((DISPCNT & 7) > 2 && (c < 512)) + continue; + + int inc = 32; + if(DISPCNT & 0x40) { + inc = sizeX >> 3; + } + int xxx = 0; + if(a1 & 0x1000) + xxx = sizeX - 1; + int address = 0x10000 + ((((c + (t>>3) * inc)<<5) + + ((t & 7)<<2) + ((xxx>>3)<<5) + ((xxx & 7) >> 1))&0x7fff); + // u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); + // int palette = (a2 >> 8) & 0xF0; + if(a1 & 0x1000) { + xxx = 7; + for(int xx = sizeX - 1; xx >= 0; xx--) { + if (xx >= startpix) + lineOBJpix--; + if (lineOBJpix<0) + continue; + if(sx < 240) { + u8 color = vram[address]; + if(xx & 1) { + color = (color >> 4); + } else + color &= 0x0F; + + if(color) { + lineOBJWin[sx] = 1; + } + } + sx = (sx+1) & 511; + xxx--; + if(!(xx & 1)) + address--; + if(xxx == -1) { + xxx = 7; + address -= 28; + } + if(address < 0x10000) + address += 0x8000; + } + } else { + for(int xx = 0; xx < sizeX; xx++) { + if (xx >= startpix) + lineOBJpix--; + if (lineOBJpix<0) + continue; + if(sx < 240) { + u8 color = vram[address]; + if(xx & 1) { + color = (color >> 4); + } else + color &= 0x0F; + + if(color) { + lineOBJWin[sx] = 1; + } + } + sx = (sx+1) & 511; + xxx++; + if(xx & 1) + address++; + if(xxx == 8) { + address += 28; + xxx = 0; + } + if(address > 0x17fff) + address -= 0x8000; + } + } + } + } + } + } + } + } +} + +static inline u32 gfxIncreaseBrightness(u32 color, int coeff) +{ + color &= 0xffff; + color = ((color << 16) | color) & 0x3E07C1F; + + color = color + (((0x3E07C1F - color) * coeff) >> 4); + color &= 0x3E07C1F; + + return (color >> 16) | color; +} + +static inline void gfxIncreaseBrightness(u32 *line, int coeff) +{ + for(int x = 0; x < 240; x++) { + u32 color = *line; + int r = (color & 0x1F); + int g = ((color >> 5) & 0x1F); + int b = ((color >> 10) & 0x1F); + + r = r + (((31 - r) * coeff) >> 4); + g = g + (((31 - g) * coeff) >> 4); + b = b + (((31 - b) * coeff) >> 4); + if(r > 31) + r = 31; + if(g > 31) + g = 31; + if(b > 31) + b = 31; + *line++ = (color & 0xFFFF0000) | (b << 10) | (g << 5) | r; + } +} + +static inline u32 gfxDecreaseBrightness(u32 color, int coeff) +{ + color &= 0xffff; + color = ((color << 16) | color) & 0x3E07C1F; + + color = color - (((color * coeff) >> 4) & 0x3E07C1F); + + return (color >> 16) | color; +} + +static inline void gfxDecreaseBrightness(u32 *line, int coeff) +{ + for(int x = 0; x < 240; x++) { + u32 color = *line; + int r = (color & 0x1F); + int g = ((color >> 5) & 0x1F); + int b = ((color >> 10) & 0x1F); + + r = r - ((r * coeff) >> 4); + g = g - ((g * coeff) >> 4); + b = b - ((b * coeff) >> 4); + if(r < 0) + r = 0; + if(g < 0) + g = 0; + if(b < 0) + b = 0; + *line++ = (color & 0xFFFF0000) | (b << 10) | (g << 5) | r; + } +} + +static inline u32 gfxAlphaBlend(u32 color, u32 color2, int ca, int cb) +{ + if(color < 0x80000000) { + color&=0xffff; + color2&=0xffff; + + color = ((color << 16) | color) & 0x03E07C1F; + color2 = ((color2 << 16) | color2) & 0x03E07C1F; + color = ((color * ca) + (color2 * cb)) >> 4; + + if ((ca + cb)>16) + { + if (color & 0x20) + color |= 0x1f; + if (color & 0x8000) + color |= 0x7C00; + if (color & 0x4000000) + color |= 0x03E00000; + } + + color &= 0x03E07C1F; + color = (color >> 16) | color; + } + return color; +} + +static inline void gfxAlphaBlend(u32 *ta, u32 *tb, int ca, int cb) +{ + for(int x = 0; x < 240; x++) { + u32 color = *ta; + if(color < 0x80000000) { + int r = (color & 0x1F); + int g = ((color >> 5) & 0x1F); + int b = ((color >> 10) & 0x1F); + u32 color2 = (*tb++); + int r0 = (color2 & 0x1F); + int g0 = ((color2 >> 5) & 0x1F); + int b0 = ((color2 >> 10) & 0x1F); + + r = ((r * ca) + (r0 * cb)) >> 4; + g = ((g * ca) + (g0 * cb)) >> 4; + b = ((b * ca) + (b0 * cb)) >> 4; + + if(r > 31) + r = 31; + if(g > 31) + g = 31; + if(b > 31) + b = 31; + + *ta++ = (color & 0xFFFF0000) | (b << 10) | (g << 5) | r; + } else { + ta++; + tb++; + } + } +} + +#endif // GFX_H diff --git a/src/gba/GBALink.cpp b/src/gba/GBALink.cpp new file mode 100644 index 0000000..3c78d8d --- /dev/null +++ b/src/gba/GBALink.cpp @@ -0,0 +1,1521 @@ +// This file was written by denopqrihg +// with major changes by tjm +#include +#include + +// malloc.h does not seem to exist on Mac OS 10.7 +#ifdef __APPLE__ +#include +#else +#include +#endif + +int vbaid = 0; +const char *MakeInstanceFilename(const char *Input) +{ + if (vbaid == 0) + return Input; + + static char *result=NULL; + if (result!=NULL) + free(result); + + result = (char *)malloc(strlen(Input)+3); + char *p = strrchr((char *)Input, '.'); + sprintf(result, "%.*s-%d.%s", (int)(p-Input), Input, vbaid+1, p+1); + return result; +} + +#ifndef NO_LINK + +// Joybus +bool gba_joybus_enabled = false; + +// If disabled, gba core won't call any (non-joybus) link functions +bool gba_link_enabled = false; + +#define LOCAL_LINK_NAME "VBA link memory" +#define IP_LINK_PORT 5738 + +#include "../common/Port.h" +#include "GBA.h" +#include "GBALink.h" +#include "GBASockClient.h" +#ifdef ENABLE_NLS +#include +#define _(x) gettext(x) +#else +#define _(x) x +#endif +#define N_(x) x +#if (defined __WIN32__ || defined _WIN32) +#include +#else +#include +#include +#include +#include +#include +#define ReleaseSemaphore(sem, nrel, orel) do { \ + for(int i = 0; i < nrel; i++) \ + sem_post(sem); \ +} while(0) +#define WAIT_TIMEOUT -1 +#ifdef HAVE_SEM_TIMEDWAIT +int WaitForSingleObject(sem_t *s, int t) +{ + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += t/1000; + ts.tv_nsec += (t%1000) * 1000000; + do { + if(!sem_timedwait(s, &ts)) + return 0; + } while(errno == EINTR); + return WAIT_TIMEOUT; +} + +// urg.. MacOSX has no sem_timedwait (POSIX) or semtimedop (SYSV) +// so we'll have to simulate it.. +// MacOSX also has no clock_gettime, and since both are "real-time", assume +// anyone who doesn't have one also doesn't have the other + +// 2 ways to do this: +// - poll & sleep loop +// - poll & wait for timer interrupt loop + +// the first consumes more CPU and requires selection of a good sleep value + +// the second may interfere with other timers running on system, and +// requires that a dummy signal handler be installed for SIGALRM +#else +#include +#ifndef TIMEDWAIT_ALRM +#define TIMEDWAIT_ALRM 1 +#endif +#if TIMEDWAIT_ALRM +#include +static void alrmhand(int sig) +{ +} +#endif +int WaitForSingleObject(sem_t *s, int t) +{ +#if !TIMEDWAIT_ALRM + struct timeval ts; + gettimeofday(&ts, NULL); + ts.tv_sec += t/1000; + ts.tv_usec += (t%1000) * 1000; +#else + struct sigaction sa, osa; + sigaction(SIGALRM, NULL, &osa); + sa = osa; + sa.sa_flags &= ~SA_RESTART; + sa.sa_handler = alrmhand; + sigaction(SIGALRM, &sa, NULL); + struct itimerval tv, otv; + tv.it_value.tv_sec = t / 1000; + tv.it_value.tv_usec = (t%1000) * 1000; + // this should be 0/0, but in the wait loop, it's possible to + // have the signal fire while not in sem_wait(). This will ensure + // another signal within 1ms + tv.it_interval.tv_sec = 0; + tv.it_interval.tv_usec = 999; + setitimer(ITIMER_REAL, &tv, &otv); +#endif + while(1) { +#if !TIMEDWAIT_ALRM + if(!sem_trywait(s)) + return 0; + struct timeval ts2; + gettimeofday(&ts2, NULL); + if(ts2.tv_sec > ts.tv_sec || (ts2.tv_sec == ts.tv_sec && + ts2.tv_usec > ts.tv_usec)) { + return WAIT_TIMEOUT; + } + // is .1 ms short enough? long enough? who knows? + struct timespec ts3; + ts3.tv_sec = 0; + ts3.tv_nsec = 100000; + nanosleep(&ts3, NULL); +#else + if(!sem_wait(s)) { + setitimer(ITIMER_REAL, &otv, NULL); + sigaction(SIGALRM, &osa, NULL); + return 0; + } + getitimer(ITIMER_REAL, &tv); + if(tv.it_value.tv_sec || tv.it_value.tv_usec > 999) + continue; + setitimer(ITIMER_REAL, &otv, NULL); + sigaction(SIGALRM, &osa, NULL); + break; +#endif + } + return WAIT_TIMEOUT; +} +#endif +#endif + +#define UPDATE_REG(address, value) WRITE16LE(((u16 *)&ioMem[address]),value) + +int linktime = 0; + +GBASockClient* dol = NULL; +sf::IPAddress joybusHostAddr = sf::IPAddress::LocalHost; + +// Hodgepodge +u8 tspeed = 3; +u8 transfer = 0; +LINKDATA *linkmem = NULL; +int linkid = 0; +#if (defined __WIN32__ || defined _WIN32) +HANDLE linksync[4]; +#else +sem_t *linksync[4]; +#endif +int savedlinktime = 0; +#if (defined __WIN32__ || defined _WIN32) +HANDLE mmf = NULL; +#else +int mmf = -1; +#endif +char linkevent[] = +#if !(defined __WIN32__ || defined _WIN32) + "/" +#endif + "VBA link event "; +static int i, j; +int linktimeout = 1000; +LANLINKDATA lanlink; +u16 linkdata[4]; +lserver ls; +lclient lc; +bool oncewait = false, after = false; + +// RFU crap (except for numtransfers note...should probably check that out) +bool rfu_enabled = false; +u8 rfu_cmd, rfu_qsend, rfu_qrecv; +int rfu_state, rfu_polarity, rfu_counter, rfu_masterq; +// numtransfers seems to be used interchangeably with linkmem->numtransfers +// in rfu code; probably a bug? +int rfu_transfer_end; +// in local comm, setting this keeps slaves from trying to communicate even +// when master isn't +u16 numtransfers = 0; +u32 rfu_masterdata[32]; + +// time to end of single GBA's transfer, in 16.78 MHz clock ticks +// first index is GBA # +int trtimedata[4][4] = { + // 9600 38400 57600 115200 + {34080, 8520, 5680, 2840}, + {65536, 16384, 10923, 5461}, + {99609, 24903, 16602, 8301}, + {133692, 33423, 22282, 11141} +}; + +// time to end of transfer +// for 3 slaves, this is time to transfer machine 4 +// for < 3 slaves, this is time to transfer last machine + time to detect lack +// of start bit from next slave +// first index is (# of slaves) - 1 +int trtimeend[3][4] = { + // 9600 38400 57600 115200 + {72527, 18132, 12088, 6044}, + {106608, 26652, 17768, 8884}, + {133692, 33423, 22282, 11141} +}; + +int gbtime = 1024; + +int GetSIOMode(u16, u16); + +void LinkClientThread(void *); +void LinkServerThread(void *); + +int StartServer(void); + +u16 StartRFU(u16); + +void StartLink(u16 value) +{ + if (ioMem == NULL) + return; + + if (rfu_enabled) { + UPDATE_REG(COMM_SIOCNT, StartRFU(value)); + return; + } + + switch (GetSIOMode(value, READ16LE(&ioMem[COMM_RCNT]))) { + case MULTIPLAYER: { + bool start = (value & 0x80) && !linkid && !transfer && gba_link_enabled; + u16 si = value & 4; + // clear start, seqno, si (RO on slave, start = pulse on master) + value &= 0xff4b; + // get current si. This way, on slaves, it is low during xfer + if(linkid) { + if(!transfer) + value |= 4; + else + value |= READ16LE(&ioMem[COMM_SIOCNT]) & 4; + } + if (start) { + if (lanlink.active) + { + if (lanlink.connected) + { + linkdata[0] = READ16LE(&ioMem[COMM_SIODATA8]); + savedlinktime = linktime; + tspeed = value & 3; + ls.Send(); + transfer = 1; + linktime = 0; + UPDATE_REG(COMM_SIOMULTI0, linkdata[0]); + UPDATE_REG(COMM_SIOMULTI1, 0xffff); + WRITE32LE(&ioMem[COMM_SIOMULTI2], 0xffffffff); + if (lanlink.speed&&oncewait == false) + ls.howmanytimes++; + after = false; + value &= ~0x40; + } else + value |= 0x40; // comm error + } + else if (linkmem->numgbas > 1) + { + // find first active attached GBA + // doing this first reduces the potential + // race window size for new connections + int n = linkmem->numgbas + 1; + int f = linkmem->linkflags; + int m; + do { + n--; + m = (1 << n) - 1; + } while((f & m) != m); + linkmem->trgbas = n; + + // before starting xfer, make pathetic attempt + // at clearing out any previous stuck xfer + // this will fail if a slave was stuck for + // too long + for(int i = 0; i < 4; i++) + while(WaitForSingleObject(linksync[i], 0) != WAIT_TIMEOUT); + + // transmit first value + linkmem->linkcmd = ('M' << 8) + (value & 3); + linkmem->linkdata[0] = READ16LE(&ioMem[COMM_SIODATA8]); + + // start up slaves & sync clocks + numtransfers = linkmem->numtransfers; + if (numtransfers != 0) + linkmem->lastlinktime = linktime; + else + linkmem->lastlinktime = 0; + + if ((++numtransfers) == 0) + linkmem->numtransfers = 2; + else + linkmem->numtransfers = numtransfers; + + transfer = 1; + linktime = 0; + tspeed = value & 3; + WRITE32LE(&ioMem[COMM_SIOMULTI0], 0xffffffff); + WRITE32LE(&ioMem[COMM_SIOMULTI2], 0xffffffff); + value &= ~0x40; + } + } + value |= (transfer != 0) << 7; + value |= (linkid && !transfer ? 0xc : 8); // set SD (high), SI (low on master) + value |= linkid << 4; // set seq + UPDATE_REG(COMM_SIOCNT, value); + if (linkid) + // SC low -> transfer in progress + // not sure why SO is low + UPDATE_REG(COMM_RCNT, transfer ? 6 : 7); + else + // SI is always low on master + // SO, SC always low during transfer + // not sure why SO low otherwise + UPDATE_REG(COMM_RCNT, transfer ? 2 : 3); + break; + } + case NORMAL8: + case NORMAL32: + case UART: + default: + UPDATE_REG(COMM_SIOCNT, value); + break; + } +} + +void StartGPLink(u16 value) +{ + UPDATE_REG(COMM_RCNT, value); + + if (!value) + return; + + switch (GetSIOMode(READ16LE(&ioMem[COMM_SIOCNT]), value)) { + case MULTIPLAYER: + value &= 0xc0f0; + value |= 3; + if (linkid) + value |= 4; + UPDATE_REG(COMM_SIOCNT, ((READ16LE(&ioMem[COMM_SIOCNT])&0xff8b)|(linkid ? 0xc : 8)|(linkid<<4))); + break; + + case GP: + if (rfu_enabled) + rfu_state = RFU_INIT; + break; + } +} + +void JoyBusConnect() +{ + delete dol; + dol = NULL; + + dol = new GBASockClient(joybusHostAddr); +} + +void JoyBusShutdown() +{ + delete dol; + dol = NULL; +} + +void JoyBusUpdate(int ticks) +{ + linktime += ticks; + static int lastjoybusupdate = 0; + + // Kinda ugly hack to update joybus stuff intermittently + if (linktime > lastjoybusupdate + 0x3000) + { + lastjoybusupdate = linktime; + + char data[5] = {0x10, 0, 0, 0, 0}; // init with invalid cmd + std::vector resp; + + if (!dol) + JoyBusConnect(); + + u8 cmd = dol->ReceiveCmd(data); + switch (cmd) { + case JOY_CMD_RESET: + UPDATE_REG(COMM_JOYCNT, READ16LE(&ioMem[COMM_JOYCNT]) | JOYCNT_RESET); + + case JOY_CMD_STATUS: + resp.push_back(0x00); // GBA device ID + resp.push_back(0x04); + break; + + case JOY_CMD_READ: + resp.push_back((u8)(READ16LE(&ioMem[COMM_JOY_TRANS_L]) & 0xff)); + resp.push_back((u8)(READ16LE(&ioMem[COMM_JOY_TRANS_L]) >> 8)); + resp.push_back((u8)(READ16LE(&ioMem[COMM_JOY_TRANS_H]) & 0xff)); + resp.push_back((u8)(READ16LE(&ioMem[COMM_JOY_TRANS_H]) >> 8)); + UPDATE_REG(COMM_JOYSTAT, READ16LE(&ioMem[COMM_JOYSTAT]) & ~JOYSTAT_SEND); + UPDATE_REG(COMM_JOYCNT, READ16LE(&ioMem[COMM_JOYCNT]) | JOYCNT_SEND_COMPLETE); + break; + + case JOY_CMD_WRITE: + UPDATE_REG(COMM_JOY_RECV_L, (u16)((u16)data[2] << 8) | (u8)data[1]); + UPDATE_REG(COMM_JOY_RECV_H, (u16)((u16)data[4] << 8) | (u8)data[3]); + UPDATE_REG(COMM_JOYSTAT, READ16LE(&ioMem[COMM_JOYSTAT]) | JOYSTAT_RECV); + UPDATE_REG(COMM_JOYCNT, READ16LE(&ioMem[COMM_JOYCNT]) | JOYCNT_RECV_COMPLETE); + break; + + default: + return; // ignore + } + + resp.push_back((u8)READ16LE(&ioMem[COMM_JOYSTAT])); + dol->Send(resp); + + // Generate SIO interrupt if we can + if ( ((cmd == JOY_CMD_RESET) || (cmd == JOY_CMD_READ) || (cmd == JOY_CMD_WRITE)) + && (READ16LE(&ioMem[COMM_JOYCNT]) & JOYCNT_INT_ENABLE) ) + { + IF |= 0x80; + UPDATE_REG(0x202, IF); + } + } +} + +static void ReInitLink(); + +void LinkUpdate(int ticks) +{ + // this actually gets called every single instruction, so keep default + // path as short as possible + + linktime += ticks; + + if (rfu_enabled) + { + rfu_transfer_end -= ticks; + if (transfer && rfu_transfer_end <= 0) + { + transfer = 0; + if (READ16LE(&ioMem[COMM_SIOCNT]) & 0x4000) + { + IF |= 0x80; + UPDATE_REG(0x202, IF); + } + UPDATE_REG(COMM_SIOCNT, READ16LE(&ioMem[COMM_SIOCNT]) & 0xff7f); + } + return; + } + + if (lanlink.active) + { + if (lanlink.connected) + { + if (after) + { + if (linkid && linktime > 6044) { + lc.Recv(); + oncewait = true; + } + else + return; + } + + if (linkid && !transfer && lc.numtransfers > 0 && linktime >= savedlinktime) + { + linkdata[linkid] = READ16LE(&ioMem[COMM_SIODATA8]); + + lc.Send(); + + UPDATE_REG(COMM_SIODATA32_L, linkdata[0]); + UPDATE_REG(COMM_SIOCNT, READ16LE(&ioMem[COMM_SIOCNT]) | 0x80); + transfer = 1; + if (lc.numtransfers==1) + linktime = 0; + else + linktime -= savedlinktime; + } + + if (transfer && linktime >= trtimeend[lanlink.numslaves-1][tspeed]) + { + if (READ16LE(&ioMem[COMM_SIOCNT]) & 0x4000) + { + IF |= 0x80; + UPDATE_REG(0x202, IF); + } + + UPDATE_REG(COMM_SIOCNT, (READ16LE(&ioMem[COMM_SIOCNT]) & 0xff0f) | (linkid << 4)); + transfer = 0; + linktime -= trtimeend[lanlink.numslaves-1][tspeed]; + oncewait = false; + + if (!lanlink.speed) + { + if (linkid) + lc.Recv(); + else + ls.Recv(); // WTF is the point of this? + + UPDATE_REG(COMM_SIOMULTI1, linkdata[1]); + UPDATE_REG(COMM_SIOMULTI2, linkdata[2]); + UPDATE_REG(COMM_SIOMULTI3, linkdata[3]); + oncewait = true; + + } else { + + after = true; + if (lanlink.numslaves == 1) + { + UPDATE_REG(COMM_SIOMULTI1, linkdata[1]); + UPDATE_REG(COMM_SIOMULTI2, linkdata[2]); + UPDATE_REG(COMM_SIOMULTI3, linkdata[3]); + } + } + } + } + return; + } + + // slave startup depends on detecting change in numtransfers + // and syncing clock with master (after first transfer) + // this will fail if > ~2 minutes have passed since last transfer due + // to integer overflow + if(!transfer && numtransfers && linktime < 0) { + linktime = 0; + // there is a very, very, small chance that this will abort + // a transfer that was just started + linkmem->numtransfers = numtransfers = 0; + } + if (linkid && !transfer && linktime >= linkmem->lastlinktime && + linkmem->numtransfers != numtransfers) + { + numtransfers = linkmem->numtransfers; + if(!numtransfers) + return; + + // if this or any previous machine was dropped, no transfer + // can take place + if(linkmem->trgbas <= linkid) { + transfer = 0; + numtransfers = 0; + // if this is the one that was dropped, reconnect + if(!(linkmem->linkflags & (1 << linkid))) + ReInitLink(); + return; + } + + // sync clock + if (numtransfers == 1) + linktime = 0; + else + linktime -= linkmem->lastlinktime; + + // there's really no point to this switch; 'M' is the only + // possible command. +#if 0 + switch ((linkmem->linkcmd) >> 8) + { + case 'M': +#endif + tspeed = linkmem->linkcmd & 3; + transfer = 1; + WRITE32LE(&ioMem[COMM_SIOMULTI0], 0xffffffff); + WRITE32LE(&ioMem[COMM_SIOMULTI2], 0xffffffff); + UPDATE_REG(COMM_SIOCNT, READ16LE(&ioMem[COMM_SIOCNT]) & ~0x40 | 0x80); +#if 0 + break; + } +#endif + } + + if (!transfer) + return; + + if (transfer <= linkmem->trgbas && linktime >= trtimedata[transfer-1][tspeed]) + { + // transfer #n -> wait for value n - 1 + if(transfer > 1 && linkid != transfer - 1) { + if(WaitForSingleObject(linksync[transfer - 1], linktimeout) == WAIT_TIMEOUT) { + // assume slave has dropped off if timed out + if(!linkid) { + linkmem->trgbas = transfer - 1; + int f = linkmem->linkflags; + f &= ~(1 << (transfer - 1)); + linkmem->linkflags = f; + if(f < (1 << transfer) - 1) + linkmem->numgbas = transfer - 1; + char message[30]; + sprintf(message, _("Player %d disconnected."), transfer - 1); + systemScreenMessage(message); + } + transfer = linkmem->trgbas + 1; + // next cycle, transfer will finish up + return; + } + } + // now that value is available, store it + UPDATE_REG((COMM_SIOMULTI0 - 2) + (transfer<<1), linkmem->linkdata[transfer-1]); + + // transfer machine's value at start of its transfer cycle + if(linkid == transfer) { + // skip if dropped + if(linkmem->trgbas <= linkid) { + transfer = 0; + numtransfers = 0; + // if this is the one that was dropped, reconnect + if(!(linkmem->linkflags & (1 << linkid))) + ReInitLink(); + return; + } + // SI becomes low + UPDATE_REG(COMM_SIOCNT, READ16LE(&ioMem[COMM_SIOCNT]) & ~4); + UPDATE_REG(COMM_RCNT, 10); + linkmem->linkdata[linkid] = READ16LE(&ioMem[COMM_SIODATA8]); + ReleaseSemaphore(linksync[linkid], linkmem->numgbas-1, NULL); + } + if(linkid == transfer - 1) { + // SO becomes low to begin next trasnfer + // may need to set DDR as well + UPDATE_REG(COMM_RCNT, 0x22); + } + + // next cycle + transfer++; + } + + if (transfer > linkmem->trgbas && linktime >= trtimeend[transfer-3][tspeed]) + { + // wait for slaves to finish + // this keeps unfinished slaves from screwing up last xfer + // not strictly necessary; may just slow things down + if(!linkid) { + for(int i = 2; i < transfer; i++) + if(WaitForSingleObject(linksync[0], linktimeout) == WAIT_TIMEOUT) { + // impossible to determine which slave died + // so leave them alone for now + systemScreenMessage(_("Unknown slave timed out; resetting comm")); + linkmem->numtransfers = numtransfers = 0; + break; + } + } else if(linkmem->trgbas > linkid) + // signal master that this slave is finished + ReleaseSemaphore(linksync[0], 1, NULL); + linktime -= trtimeend[transfer - 3][tspeed]; + transfer = 0; + u16 value = READ16LE(&ioMem[COMM_SIOCNT]); + if(!linkid) + value |= 4; // SI becomes high on slaves after xfer + UPDATE_REG(COMM_SIOCNT, (value & 0xff0f) | (linkid << 4)); + // SC/SI high after transfer + UPDATE_REG(COMM_RCNT, linkid ? 15 : 11); + if (value & 0x4000) + { + IF |= 0x80; + UPDATE_REG(0x202, IF); + } + } + + return; +} + +inline int GetSIOMode(u16 siocnt, u16 rcnt) +{ + if (!(rcnt & 0x8000)) + { + switch (siocnt & 0x3000) { + case 0x0000: return NORMAL8; + case 0x1000: return NORMAL32; + case 0x2000: return MULTIPLAYER; + case 0x3000: return UART; + } + } + + if (rcnt & 0x4000) + return JOYBUS; + + return GP; +} + +// The GBA wireless RFU (see adapter3.txt) +// Just try to avert your eyes for now ^^ (note, it currently can be called, tho) +u16 StartRFU(u16 value) +{ + switch (GetSIOMode(value, READ16LE(&ioMem[COMM_RCNT]))) { + case NORMAL8: + rfu_polarity = 0; + return value; + break; + + case NORMAL32: + if (value & 8) + value &= 0xfffb; // A kind of acknowledge procedure + else + value |= 4; + + if (value & 0x80) + { + if ((value&3) == 1) + rfu_transfer_end = 2048; + else + rfu_transfer_end = 256; + + u16 a = READ16LE(&ioMem[COMM_SIODATA32_H]); + + switch (rfu_state) { + case RFU_INIT: + if (READ32LE(&ioMem[COMM_SIODATA32_L]) == 0xb0bb8001) + rfu_state = RFU_COMM; // end of startup + + UPDATE_REG(COMM_SIODATA32_H, READ16LE(&ioMem[COMM_SIODATA32_L])); + UPDATE_REG(COMM_SIODATA32_L, a); + break; + + case RFU_COMM: + if (a == 0x9966) + { + rfu_cmd = ioMem[COMM_SIODATA32_L]; + if ((rfu_qsend=ioMem[0x121]) != 0) { + rfu_state = RFU_SEND; + rfu_counter = 0; + } + if (rfu_cmd == 0x25 || rfu_cmd == 0x24) { + linkmem->rfu_q[vbaid] = rfu_qsend; + } + UPDATE_REG(COMM_SIODATA32_L, 0); + UPDATE_REG(COMM_SIODATA32_H, 0x8000); + } + else if (a == 0x8000) + { + switch (rfu_cmd) { + case 0x1a: // check if someone joined + if (linkmem->rfu_request[vbaid] != 0) { + rfu_state = RFU_RECV; + rfu_qrecv = 1; + } + linkid = -1; + rfu_cmd |= 0x80; + break; + + case 0x1e: // receive broadcast data + case 0x1d: // no visible difference + rfu_polarity = 0; + rfu_state = RFU_RECV; + rfu_qrecv = 7; + rfu_counter = 0; + rfu_cmd |= 0x80; + break; + + case 0x30: + linkmem->rfu_request[vbaid] = 0; + linkmem->rfu_q[vbaid] = 0; + linkid = 0; + numtransfers = 0; + rfu_cmd |= 0x80; + if (linkmem->numgbas == 2) + ReleaseSemaphore(linksync[1-vbaid], 1, NULL); + break; + + case 0x11: // ? always receives 0xff - I suspect it's something for 3+ players + case 0x13: // unknown + case 0x20: // this has something to do with 0x1f + case 0x21: // this too + rfu_cmd |= 0x80; + rfu_polarity = 0; + rfu_state = 3; + rfu_qrecv = 1; + break; + + case 0x26: + if(linkid>0){ + rfu_qrecv = rfu_masterq; + } + if((rfu_qrecv=linkmem->rfu_q[1-vbaid])!=0){ + rfu_state = RFU_RECV; + rfu_counter = 0; + } + rfu_cmd |= 0x80; + break; + + case 0x24: // send data + if((numtransfers++)==0) linktime = 1; + linkmem->rfu_linktime[vbaid] = linktime; + if(linkmem->numgbas==2){ + ReleaseSemaphore(linksync[1-vbaid], 1, NULL); + WaitForSingleObject(linksync[vbaid], linktimeout); + } + rfu_cmd |= 0x80; + linktime = 0; + linkid = -1; + break; + + case 0x25: // send & wait for data + case 0x1f: // pick a server + case 0x10: // init + case 0x16: // send broadcast data + case 0x17: // setup or something ? + case 0x27: // wait for data ? + case 0x3d: // init + default: + rfu_cmd |= 0x80; + break; + + case 0xa5: // 2nd part of send&wait function 0x25 + case 0xa7: // 2nd part of wait function 0x27 + if (linkid == -1) { + linkid++; + linkmem->rfu_linktime[vbaid] = 0; + } + if (linkid&&linkmem->rfu_request[1-vbaid] == 0) { + linkmem->rfu_q[1-vbaid] = 0; + rfu_transfer_end = 256; + rfu_polarity = 1; + rfu_cmd = 0x29; + linktime = 0; + break; + } + if ((numtransfers++) == 0) + linktime = 0; + linkmem->rfu_linktime[vbaid] = linktime; + if (linkmem->numgbas == 2) { + if (!linkid || (linkid && numtransfers)) + ReleaseSemaphore(linksync[1-vbaid], 1, NULL); + WaitForSingleObject(linksync[vbaid], linktimeout); + } + if ( linkid > 0) { + memcpy(rfu_masterdata, linkmem->rfu_data[1-vbaid], 128); + rfu_masterq = linkmem->rfu_q[1-vbaid]; + } + rfu_transfer_end = linkmem->rfu_linktime[1-vbaid] - linktime + 256; + + if (rfu_transfer_end < 256) + rfu_transfer_end = 256; + + linktime = -rfu_transfer_end; + rfu_polarity = 1; + rfu_cmd = 0x28; + break; + } + UPDATE_REG(COMM_SIODATA32_H, 0x9966); + UPDATE_REG(COMM_SIODATA32_L, (rfu_qrecv<<8) | rfu_cmd); + + } else { + + UPDATE_REG(COMM_SIODATA32_L, 0); + UPDATE_REG(COMM_SIODATA32_H, 0x8000); + } + break; + + case RFU_SEND: + if(--rfu_qsend == 0) + rfu_state = RFU_COMM; + + switch (rfu_cmd) { + case 0x16: + linkmem->rfu_bdata[vbaid][rfu_counter++] = READ32LE(&ioMem[COMM_SIODATA32_L]); + break; + + case 0x17: + linkid = 1; + break; + + case 0x1f: + linkmem->rfu_request[1-vbaid] = 1; + break; + + case 0x24: + case 0x25: + linkmem->rfu_data[vbaid][rfu_counter++] = READ32LE(&ioMem[COMM_SIODATA32_L]); + break; + } + UPDATE_REG(COMM_SIODATA32_L, 0); + UPDATE_REG(COMM_SIODATA32_H, 0x8000); + break; + + case RFU_RECV: + if (--rfu_qrecv == 0) + rfu_state = RFU_COMM; + + switch (rfu_cmd) { + case 0x9d: + case 0x9e: + if (rfu_counter == 0) { + UPDATE_REG(COMM_SIODATA32_L, 0x61f1); + UPDATE_REG(COMM_SIODATA32_H, 0); + rfu_counter++; + break; + } + UPDATE_REG(COMM_SIODATA32_L, linkmem->rfu_bdata[1-vbaid][rfu_counter-1]&0xffff); + UPDATE_REG(COMM_SIODATA32_H, linkmem->rfu_bdata[1-vbaid][rfu_counter-1]>>16); + rfu_counter++; + break; + + case 0xa6: + if (linkid>0) { + UPDATE_REG(COMM_SIODATA32_L, rfu_masterdata[rfu_counter]&0xffff); + UPDATE_REG(COMM_SIODATA32_H, rfu_masterdata[rfu_counter++]>>16); + } else { + UPDATE_REG(COMM_SIODATA32_L, linkmem->rfu_data[1-vbaid][rfu_counter]&0xffff); + UPDATE_REG(COMM_SIODATA32_H, linkmem->rfu_data[1-vbaid][rfu_counter++]>>16); + } + break; + + case 0x93: // it seems like the game doesn't care about this value + UPDATE_REG(COMM_SIODATA32_L, 0x1234); // put anything in here + UPDATE_REG(COMM_SIODATA32_H, 0x0200); // also here, but it should be 0200 + break; + + case 0xa0: + case 0xa1: + UPDATE_REG(COMM_SIODATA32_L, 0x641b); + UPDATE_REG(COMM_SIODATA32_H, 0x0000); + break; + + case 0x9a: + UPDATE_REG(COMM_SIODATA32_L, 0x61f9); + UPDATE_REG(COMM_SIODATA32_H, 0); + break; + + case 0x91: + UPDATE_REG(COMM_SIODATA32_L, 0x00ff); + UPDATE_REG(COMM_SIODATA32_H, 0x0000); + break; + + default: + UPDATE_REG(COMM_SIODATA32_L, 0x0173); + UPDATE_REG(COMM_SIODATA32_H, 0x0000); + break; + } + break; + } + transfer = 1; + } + + if (rfu_polarity) + value ^= 4; // sometimes it's the other way around + + default: + return value; + } +} + +////////////////////////////////////////////////////////////////////////// +// Probably from here down needs to be replaced with SFML goodness :) +// tjm: what SFML goodness? SFML for network, yes, but not for IPC + +bool InitLink() +{ + linkid = 0; + +#if (defined __WIN32__ || defined _WIN32) + if((mmf=CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(LINKDATA), LOCAL_LINK_NAME))==NULL){ + systemMessage(0, N_("Error creating file mapping")); + return false; + } + + if(GetLastError() == ERROR_ALREADY_EXISTS) + vbaid = 1; + else + vbaid = 0; + + + if((linkmem=(LINKDATA *)MapViewOfFile(mmf, FILE_MAP_WRITE, 0, 0, sizeof(LINKDATA)))==NULL){ + CloseHandle(mmf); + systemMessage(0, N_("Error mapping file")); + return false; + } +#else + if((mmf = shm_open("/" LOCAL_LINK_NAME, O_RDWR|O_CREAT|O_EXCL, 0777)) < 0) { + vbaid = 1; + mmf = shm_open("/" LOCAL_LINK_NAME, O_RDWR, 0); + } else + vbaid = 0; + if(mmf < 0 || ftruncate(mmf, sizeof(LINKDATA)) < 0 || + !(linkmem = (LINKDATA *)mmap(NULL, sizeof(LINKDATA), + PROT_READ|PROT_WRITE, MAP_SHARED, + mmf, 0))) { + systemMessage(0, N_("Error creating file mapping")); + if(mmf) { + if(!vbaid) + shm_unlink("/" LOCAL_LINK_NAME); + close(mmf); + } + } +#endif + + // get lowest-numbered available machine slot + bool firstone = !vbaid; + if(firstone) { + linkmem->linkflags = 1; + linkmem->numgbas = 1; + linkmem->numtransfers=0; + for(i=0;i<4;i++) + linkmem->linkdata[i] = 0xffff; + } else { + // FIXME: this should be done while linkmem is locked + // (no xfer in progress, no other vba trying to connect) + int n = linkmem->numgbas; + int f = linkmem->linkflags; + for(int i = 0; i <= n; i++) + if(!(f & (1 << i))) { + vbaid = i; + break; + } + if(vbaid == 4){ +#if (defined __WIN32__ || defined _WIN32) + UnmapViewOfFile(linkmem); + CloseHandle(mmf); +#else + munmap(linkmem, sizeof(LINKDATA)); + if(!vbaid) + shm_unlink("/" LOCAL_LINK_NAME); + close(mmf); +#endif + systemMessage(0, N_("5 or more GBAs not supported.")); + return false; + } + if(vbaid == n) + linkmem->numgbas = n + 1; + linkmem->linkflags = f | (1 << vbaid); + } + linkid = vbaid; + + for(i=0;i<4;i++){ + linkevent[sizeof(linkevent)-2]=(char)i+'1'; +#if (defined __WIN32__ || defined _WIN32) + linksync[i] = firstone ? + CreateSemaphore(NULL, 0, 4, linkevent) : + OpenSemaphore(SEMAPHORE_ALL_ACCESS, false, linkevent); + if(linksync[i] == NULL) { + UnmapViewOfFile(linkmem); + CloseHandle(mmf); + for(j=0;jlinkflags; + int n = linkmem->numgbas; + if(f & (1 << linkid)) { + systemMessage(0, N_("Lost link; reinitialize to reconnect")); + return; + } + linkmem->linkflags |= 1 << linkid; + if(n < linkid + 1) + linkmem->numgbas = linkid + 1; + numtransfers = linkmem->numtransfers; + systemScreenMessage(_("Lost link; reconnected")); +} + +void CloseLink(void){ + if(lanlink.connected){ + if(linkid){ + char outbuffer[4]; + outbuffer[0] = 4; + outbuffer[1] = -32; + if(lanlink.type==0) lanlink.tcpsocket.Send(outbuffer, 4); + } else { + char outbuffer[12]; + int i; + outbuffer[0] = 12; + outbuffer[1] = -32; + for(i=1;i<=lanlink.numslaves;i++){ + if(lanlink.type==0){ + ls.tcpsocket[i].Send(outbuffer, 12); + } + ls.tcpsocket[i].Close(); + } + } + } + int f = linkmem->linkflags; + f &= ~(1 << linkid); + if(f & 0xf) { + linkmem->linkflags = f; + int n = linkmem->numgbas; + for(int i = 0; i < n; i--) + if(f <= (1 << (i + 1)) - 1) { + linkmem->numgbas = i + 1; + break; + } + } + + for(i=0;i<4;i++){ + if(linksync[i]!=NULL){ +#if (defined __WIN32__ || defined _WIN32) + ReleaseSemaphore(linksync[i], 1, NULL); + CloseHandle(linksync[i]); +#else + sem_close(linksync[i]); + if(!(f & 0xf)) { + linkevent[sizeof(linkevent)-2]=(char)i+'1'; + sem_unlink(linkevent); + } +#endif + } + } +#if (defined __WIN32__ || defined _WIN32) + CloseHandle(mmf); + UnmapViewOfFile(linkmem); + + // FIXME: move to caller + // (but there are no callers, so why bother?) + //regSetDwordValue("LAN", lanlink.active); +#else + if(!(f & 0xf)) + shm_unlink("/" LOCAL_LINK_NAME); + munmap(linkmem, sizeof(LINKDATA)); + close(mmf); +#endif + return; +} + +// call this to clean up crashed program's shared state +// or to use TCP on same machine (for testing) +// this may be necessary under MSW as well, but I wouldn't know how +void CleanLocalLink() +{ +#if !(defined __WIN32__ || defined _WIN32) + shm_unlink("/" LOCAL_LINK_NAME); + for(int i = 0; i < 4; i++) { + linkevent[sizeof(linkevent) - 2] = '1' + i; + sem_unlink(linkevent); + } +#endif +} + +// Server +lserver::lserver(void){ + intinbuffer = (s32*)inbuffer; + u16inbuffer = (u16*)inbuffer; + intoutbuffer = (s32*)outbuffer; + u16outbuffer = (u16*)outbuffer; + oncewait = false; +} + +bool lserver::Init(ServerInfoDisplay *sid){ + // too bad Listen() doesn't take an address as well + // then again, old code used INADDR_ANY anyway + if(!lanlink.tcpsocket.Listen(IP_LINK_PORT)) + // Note: old code closed socket & retried once on bind failure + return false; // FIXME: error code? + + if(lanlink.thread!=NULL){ + lanlink.terminate = true; + WaitForSingleObject(linksync[vbaid], 500); + lanlink.thread = NULL; + } + lanlink.terminate = false; + linkid = 0; + + // should probably use GetPublicAddress() + sid->ShowServerIP(sf::IPAddress::GetLocalAddress()); + + lanlink.thread = new sf::Thread(LinkServerThread, sid); + lanlink.thread->Launch(); + + return true; + +} + +void LinkServerThread(void *_sid){ + ServerInfoDisplay *sid = (ServerInfoDisplay *)_sid; + sf::Selector fdset; + char inbuffer[256], outbuffer[256]; + s32 *intinbuffer = (s32*)inbuffer; + u16 *u16inbuffer = (u16*)inbuffer; + s32 *intoutbuffer = (s32*)outbuffer; + u16 *u16outbuffer = (u16*)outbuffer; + + i = 0; + + while(iShowConnect(i); + } + } + sid->Ping(); + } + + lanlink.connected = true; + + sid->Connected(); + + for(i=1;i<=lanlink.numslaves;i++){ + outbuffer[0] = 4; + ls.tcpsocket[i].Send(outbuffer, 4); + } + +CloseInfoDisplay: + delete sid; + return; +} + +void lserver::Send(void){ + if(lanlink.type==0){ // TCP + if(savedlinktime==-1){ + outbuffer[0] = 4; + outbuffer[1] = -32; //0xe0 + for(i=1;i<=lanlink.numslaves;i++){ + tcpsocket[i].Send(outbuffer, 4); + size_t nr; + tcpsocket[i].Receive(inbuffer, 4, nr); + } + } + outbuffer[1] = tspeed; + WRITE16LE(&u16outbuffer[1], linkdata[0]); + WRITE32LE(&intoutbuffer[1], savedlinktime); + if(lanlink.numslaves==1){ + if(lanlink.type==0){ + outbuffer[0] = 8; + tcpsocket[1].Send(outbuffer, 8); + } + } + else if(lanlink.numslaves==2){ + WRITE16LE(&u16outbuffer[4], linkdata[2]); + if(lanlink.type==0){ + outbuffer[0] = 10; + tcpsocket[1].Send(outbuffer, 10); + WRITE16LE(&u16outbuffer[4], linkdata[1]); + tcpsocket[2].Send(outbuffer, 10); + } + } else { + if(lanlink.type==0){ + outbuffer[0] = 12; + WRITE16LE(&u16outbuffer[4], linkdata[2]); + WRITE16LE(&u16outbuffer[5], linkdata[3]); + tcpsocket[1].Send(outbuffer, 12); + WRITE16LE(&u16outbuffer[4], linkdata[1]); + tcpsocket[2].Send(outbuffer, 12); + WRITE16LE(&u16outbuffer[5], linkdata[2]); + tcpsocket[3].Send(outbuffer, 12); + } + } + } + return; +} + +void lserver::Recv(void){ + int numbytes; + if(lanlink.type==0){ // TCP + fdset.Clear(); + for(i=0;i1) memmove(inbuffer, inbuffer+inbuffer[0]*(howmanytimes-1), inbuffer[0]); + if(inbuffer[1]==-32){ + char message[30]; + lanlink.connected = false; + sprintf(message, _("Player %d disconnected."), i+2); + systemScreenMessage(message); + outbuffer[0] = 4; + outbuffer[1] = -32; + for(i=1;iConnectStart(addr); + lanlink.terminate = false; + lanlink.thread = new sf::Thread(LinkClientThread, cid); + lanlink.thread->Launch(); + return true; +} + +void LinkClientThread(void *_cid){ + ClientInfoDisplay *cid = (ClientInfoDisplay *)_cid; + sf::Selector fdset; + int numbytes; + char inbuffer[16]; + u16 *u16inbuffer = (u16*)inbuffer; + unsigned long block = 0; + + while(lanlink.tcpsocket.Connect(lc.serverport, lc.serveraddr) != sf::Socket::Done) { + // stupid SFML has no way of giving what sort of error occurred + // so we'll just have to do a retry loop, I guess. + cid->Ping(); + if(lanlink.terminate) + goto CloseInfoDisplay; + // old code had broken sleep on socket, which isn't + // even connected yet + // corrected sleep on socket worked, but this is more sane + // and probably less portable... works with mingw32 at least +#if (defined __WIN32__ || defined _WIN32) + Sleep(100); // in milliseconds +#else + usleep(100000); // in microseconds +#endif + } + + numbytes = 0; + size_t got; + while(numbytes<4) { + lanlink.tcpsocket.Receive(inbuffer+numbytes, 4 - numbytes, got); + numbytes += got; + fdset.Clear(); + fdset.Add(lanlink.tcpsocket); + fdset.Wait(0.1); + cid->Ping(); + if(lanlink.terminate) { + lanlink.tcpsocket.Close(); + goto CloseInfoDisplay; + } + } + linkid = (int)READ16LE(&u16inbuffer[0]); + lanlink.numslaves = (int)READ16LE(&u16inbuffer[1]); + + cid->ShowConnect(linkid + 1, lanlink.numslaves - linkid); + + numbytes = 0; + inbuffer[0] = 1; + while(numbytesPing(); + if(lanlink.terminate) { + lanlink.tcpsocket.Close(); + goto CloseInfoDisplay; + } + } + + lanlink.connected = true; + + cid->Connected(); + +CloseInfoDisplay: + delete cid; + return; +} + +void lclient::CheckConn(void){ + size_t nr; + lanlink.tcpsocket.Receive(inbuffer, 1, nr); + numbytes = nr; + if(numbytes>0){ + while(numbytes +#include + +class ServerInfoDisplay +{ +public: + virtual void ShowServerIP(const sf::IPAddress& addr) = 0; + virtual void ShowConnect(const int player) = 0; + virtual void Ping() = 0; + virtual void Connected() = 0; +}; + +typedef struct { + u16 linkdata[5]; + u16 linkcmd; + u16 numtransfers; + int lastlinktime; + u8 numgbas; + u8 trgbas; + u8 linkflags; + int rfu_q[4]; + u8 rfu_request[4]; + int rfu_linktime[4]; + u32 rfu_bdata[4][7]; + u32 rfu_data[4][32]; +} LINKDATA; + +class lserver{ + int numbytes; + sf::Selector fdset; + //timeval udptimeout; + char inbuffer[256], outbuffer[256]; + s32 *intinbuffer; + u16 *u16inbuffer; + s32 *intoutbuffer; + u16 *u16outbuffer; + int counter; + int done; +public: + int howmanytimes; + sf::SocketTCP tcpsocket[4]; + sf::IPAddress udpaddr[4]; + lserver(void); + bool Init(ServerInfoDisplay *); + void Send(void); + void Recv(void); +}; + +class ClientInfoDisplay { +public: + virtual void ConnectStart(const sf::IPAddress& addr) = 0; + virtual void Ping() = 0; + virtual void ShowConnect(const int player, const int togo) = 0; + virtual void Connected() = 0; +}; + +class lclient{ + sf::Selector fdset; + char inbuffer[256], outbuffer[256]; + s32 *intinbuffer; + u16 *u16inbuffer; + s32 *intoutbuffer; + u16 *u16outbuffer; + int numbytes; +public: + sf::IPAddress serveraddr; + unsigned short serverport; + sf::SocketTCP noblock; + int numtransfers; + lclient(void); + bool Init(sf::IPAddress, ClientInfoDisplay *); + void Send(void); + void Recv(void); + void CheckConn(void); +}; + +typedef struct { + sf::SocketTCP tcpsocket; + //sf::SocketUDP udpsocket; + int numslaves; + sf::Thread *thread; + int type; + bool server; + bool terminate; + bool connected; + bool speed; + bool active; +} LANLINKDATA; + +extern bool gba_joybus_enabled; +extern bool gba_link_enabled; + +extern sf::IPAddress joybusHostAddr; +extern void JoyBusConnect(); +extern void JoyBusShutdown(); +extern void JoyBusUpdate(int ticks); + +extern bool InitLink(); +extern void CloseLink(); +extern void StartLink(u16); +extern void StartGPLink(u16); +extern void LinkUpdate(int); +extern void CleanLocalLink(); +extern LANLINKDATA lanlink; +extern int vbaid; +extern bool rfu_enabled; +extern int linktimeout; +extern lclient lc; +extern lserver ls; +extern int linkid; + +#else + +// stubs to keep #ifdef's out of mainline +const bool gba_joybus_enabled = false; +const bool gba_link_enabled = false; + +inline void JoyBusConnect() { } +inline void JoyBusShutdown() { } +inline void JoyBusUpdate(int) { } + +inline bool InitLink() { return true; } +inline void CloseLink() { } +inline void StartLink(u16) { } +inline void StartGPLink(u16) { } +inline void LinkUpdate(int) { } +inline void CleanLocalLink() { } +#endif + +#endif /* GBA_GBALINK_H */ diff --git a/src/gba/GBASockClient.cpp b/src/gba/GBASockClient.cpp new file mode 100644 index 0000000..05843f5 --- /dev/null +++ b/src/gba/GBASockClient.cpp @@ -0,0 +1,42 @@ +#ifndef NO_LINK + +#include "GBASockClient.h" + +// Currently only for Joybus communications + +GBASockClient::GBASockClient(sf::IPAddress _server_addr) +{ + if (!_server_addr.IsValid()) + server_addr = sf::IPAddress::LocalHost; + else + server_addr = _server_addr; + + client.Connect(0xd6ba, server_addr); + //client.SetBlocking(false); +} + +GBASockClient::~GBASockClient() +{ + client.Close(); +} + +void GBASockClient::Send(std::vector data) +{ + char* plain_data = new char[data.size()]; + std::copy(data.begin(), data.end(), plain_data); + + client.Send(plain_data, data.size()); + + delete[] plain_data; +} + +// Returns cmd for convenience +char GBASockClient::ReceiveCmd(char* data_in) +{ + std::size_t num_received; + client.Receive(data_in, 5, num_received); + + return data_in[0]; +} + +#endif // NO_LINK diff --git a/src/gba/GBASockClient.h b/src/gba/GBASockClient.h new file mode 100644 index 0000000..937959d --- /dev/null +++ b/src/gba/GBASockClient.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include "../common/Types.h" + +class GBASockClient : public sf::SocketTCP +{ +public: + GBASockClient(sf::IPAddress server_addr); + ~GBASockClient(); + + void Send(std::vector data); + char ReceiveCmd(char* data_in); + +private: + sf::IPAddress server_addr; + sf::SocketTCP client; +}; diff --git a/src/gba/GBAcpu.h b/src/gba/GBAcpu.h new file mode 100644 index 0000000..3b22e00 --- /dev/null +++ b/src/gba/GBAcpu.h @@ -0,0 +1,287 @@ +#ifndef GBACPU_H +#define GBACPU_H + +extern int armExecute(); +extern int thumbExecute(); + +#ifdef __GNUC__ +#ifndef __APPLE__ +# define INSN_REGPARM __attribute__((regparm(1))) +#else +# define INSN_REGPARM /*nothing*/ +#endif +# define LIKELY(x) __builtin_expect(!!(x),1) +# define UNLIKELY(x) __builtin_expect(!!(x),0) +#else +# define INSN_REGPARM /*nothing*/ +# define LIKELY(x) (x) +# define UNLIKELY(x) (x) +#endif + +#define UPDATE_REG(address, value)\ + {\ + WRITE16LE(((u16 *)&ioMem[address]),value);\ + }\ + +#define ARM_PREFETCH \ + {\ + cpuPrefetch[0] = CPUReadMemoryQuick(armNextPC);\ + cpuPrefetch[1] = CPUReadMemoryQuick(armNextPC+4);\ + } + +#define THUMB_PREFETCH \ + {\ + cpuPrefetch[0] = CPUReadHalfWordQuick(armNextPC);\ + cpuPrefetch[1] = CPUReadHalfWordQuick(armNextPC+2);\ + } + +#define ARM_PREFETCH_NEXT \ + cpuPrefetch[1] = CPUReadMemoryQuick(armNextPC+4); + +#define THUMB_PREFETCH_NEXT\ + cpuPrefetch[1] = CPUReadHalfWordQuick(armNextPC+2); + + +extern int SWITicks; +extern u32 mastercode; +extern bool busPrefetch; +extern bool busPrefetchEnable; +extern u32 busPrefetchCount; +extern int cpuNextEvent; +extern bool holdState; +extern u32 cpuPrefetch[2]; +extern int cpuTotalTicks; +extern u8 memoryWait[16]; +extern u8 memoryWait32[16]; +extern u8 memoryWaitSeq[16]; +extern u8 memoryWaitSeq32[16]; +extern u8 cpuBitsSet[256]; +extern u8 cpuLowestBitSet[256]; +extern void CPUSwitchMode(int mode, bool saveState, bool breakLoop); +extern void CPUSwitchMode(int mode, bool saveState); +extern void CPUUpdateCPSR(); +extern void CPUUpdateFlags(bool breakLoop); +extern void CPUUpdateFlags(); +extern void CPUUndefinedException(); +extern void CPUSoftwareInterrupt(); +extern void CPUSoftwareInterrupt(int comment); + + +// Waitstates when accessing data +inline int dataTicksAccess16(u32 address) // DATA 8/16bits NON SEQ +{ + int addr = (address>>24)&15; + int value = memoryWait[addr]; + + if ((addr>=0x08) || (addr < 0x02)) + { + busPrefetchCount=0; + busPrefetch=false; + } + else if (busPrefetch) + { + int waitState = value; + if (!waitState) + waitState = 1; + busPrefetchCount = ((busPrefetchCount+1)<>24)&15; + int value = memoryWait32[addr]; + + if ((addr>=0x08) || (addr < 0x02)) + { + busPrefetchCount=0; + busPrefetch=false; + } + else if (busPrefetch) + { + int waitState = value; + if (!waitState) + waitState = 1; + busPrefetchCount = ((busPrefetchCount+1)<>24)&15; + int value = memoryWaitSeq[addr]; + + if ((addr>=0x08) || (addr < 0x02)) + { + busPrefetchCount=0; + busPrefetch=false; + } + else if (busPrefetch) + { + int waitState = value; + if (!waitState) + waitState = 1; + busPrefetchCount = ((busPrefetchCount+1)<>24)&15; + int value = memoryWaitSeq32[addr]; + + if ((addr>=0x08) || (addr < 0x02)) + { + busPrefetchCount=0; + busPrefetch=false; + } + else if (busPrefetch) + { + int waitState = value; + if (!waitState) + waitState = 1; + busPrefetchCount = ((busPrefetchCount+1)<>24)&15; + + if ((addr>=0x08) && (addr<=0x0D)) + { + if (busPrefetchCount&0x1) + { + if (busPrefetchCount&0x2) + { + busPrefetchCount = ((busPrefetchCount&0xFF)>>2) | (busPrefetchCount&0xFFFFFF00); + return 0; + } + busPrefetchCount = ((busPrefetchCount&0xFF)>>1) | (busPrefetchCount&0xFFFFFF00); + return memoryWaitSeq[addr]-1; + } + else + { + busPrefetchCount=0; + return memoryWait[addr]; + } + } + else + { + busPrefetchCount = 0; + return memoryWait[addr]; + } +} + +inline int codeTicksAccess32(u32 address) // ARM NON SEQ +{ + int addr = (address>>24)&15; + + if ((addr>=0x08) && (addr<=0x0D)) + { + if (busPrefetchCount&0x1) + { + if (busPrefetchCount&0x2) + { + busPrefetchCount = ((busPrefetchCount&0xFF)>>2) | (busPrefetchCount&0xFFFFFF00); + return 0; + } + busPrefetchCount = ((busPrefetchCount&0xFF)>>1) | (busPrefetchCount&0xFFFFFF00); + return memoryWaitSeq[addr] - 1; + } + else + { + busPrefetchCount = 0; + return memoryWait32[addr]; + } + } + else + { + busPrefetchCount = 0; + return memoryWait32[addr]; + } +} + +inline int codeTicksAccessSeq16(u32 address) // THUMB SEQ +{ + int addr = (address>>24)&15; + + if ((addr>=0x08) && (addr<=0x0D)) + { + if (busPrefetchCount&0x1) + { + busPrefetchCount = ((busPrefetchCount&0xFF)>>1) | (busPrefetchCount&0xFFFFFF00); + return 0; + } + else + if (busPrefetchCount>0xFF) + { + busPrefetchCount=0; + return memoryWait[addr]; + } + else + return memoryWaitSeq[addr]; + } + else + { + busPrefetchCount = 0; + return memoryWaitSeq[addr]; + } +} + +inline int codeTicksAccessSeq32(u32 address) // ARM SEQ +{ + int addr = (address>>24)&15; + + if ((addr>=0x08) && (addr<=0x0D)) + { + if (busPrefetchCount&0x1) + { + if (busPrefetchCount&0x2) + { + busPrefetchCount = ((busPrefetchCount&0xFF)>>2) | (busPrefetchCount&0xFFFFFF00); + return 0; + } + busPrefetchCount = ((busPrefetchCount&0xFF)>>1) | (busPrefetchCount&0xFFFFFF00); + return memoryWaitSeq[addr]; + } + else + if (busPrefetchCount>0xFF) + { + busPrefetchCount=0; + return memoryWait32[addr]; + } + else + return memoryWaitSeq32[addr]; + } + else + { + return memoryWaitSeq32[addr]; + } +} + + +// Emulates the Cheat System (m) code +inline void cpuMasterCodeCheck() +{ + if((mastercode) && (mastercode == armNextPC)) + { + u32 joy = 0; + if(systemReadJoypads()) + joy = systemReadJoypad(-1); + u32 ext = (joy >> 10); + cpuTotalTicks += cheatsCheckKeys(P1^0x3FF, ext); + } +} + +#endif // GBACPU_H diff --git a/src/gba/GBAinline.h b/src/gba/GBAinline.h new file mode 100644 index 0000000..11254f9 --- /dev/null +++ b/src/gba/GBAinline.h @@ -0,0 +1,744 @@ +#ifndef GBAINLINE_H +#define GBAINLINE_H + +#include "../System.h" +#include "../common/Port.h" +#include "RTC.h" +#include "Sound.h" +#include "agbprint.h" +#include "GBAcpu.h" +#include "GBALink.h" + +extern const u32 objTilesAddress[3]; + +extern bool stopState; +extern bool holdState; +extern int holdType; +extern int cpuNextEvent; +extern bool cpuSramEnabled; +extern bool cpuFlashEnabled; +extern bool cpuEEPROMEnabled; +extern bool cpuEEPROMSensorEnabled; +extern bool cpuDmaHack; +extern u32 cpuDmaLast; +extern bool timer0On; +extern int timer0Ticks; +extern int timer0ClockReload; +extern bool timer1On; +extern int timer1Ticks; +extern int timer1ClockReload; +extern bool timer2On; +extern int timer2Ticks; +extern int timer2ClockReload; +extern bool timer3On; +extern int timer3Ticks; +extern int timer3ClockReload; +extern int cpuTotalTicks; + +#define CPUReadByteQuick(addr) \ + map[(addr)>>24].address[(addr) & map[(addr)>>24].mask] + +#define CPUReadHalfWordQuick(addr) \ + READ16LE(((u16*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])) + +#define CPUReadMemoryQuick(addr) \ + READ32LE(((u32*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])) + +static inline u32 CPUReadMemory(u32 address) +{ + u32 value; + u32 oldAddress = address; + + if(address & 3) { + address &= ~0x03; + } + + switch(address >> 24) { + case 0: + if(reg[15].I >> 24) { + if(address < 0x4000) { +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_ILLEGAL_READ) { + log("Illegal word read from bios: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } +#endif + + value = READ32LE(((u32 *)&biosProtected)); + } + else goto unreadable; + } else + value = READ32LE(((u32 *)&bios[address & 0x3FFC])); + break; + case 2: + value = READ32LE(((u32 *)&workRAM[address & 0x3FFFC])); + break; + case 3: + value = READ32LE(((u32 *)&internalRAM[address & 0x7ffC])); + break; + case 4: + if((address < 0x4000400) && ioReadable[address & 0x3fc]) { + if(ioReadable[(address & 0x3fc) + 2]) { + value = READ32LE(((u32 *)&ioMem[address & 0x3fC])); + if ((address & 0x3fc) == COMM_JOY_RECV_L) + UPDATE_REG(COMM_JOYSTAT, READ16LE(&ioMem[COMM_JOYSTAT]) & ~JOYSTAT_RECV); + } else { + value = READ16LE(((u16 *)&ioMem[address & 0x3fc])); + } + } + else + goto unreadable; + break; + case 5: + value = READ32LE(((u32 *)&paletteRAM[address & 0x3fC])); + break; + case 6: + address = (address & 0x1fffc); + if (((DISPCNT & 7) >2) && ((address & 0x1C000) == 0x18000)) + { + value = 0; + break; + } + if ((address & 0x18000) == 0x18000) + address &= 0x17fff; + value = READ32LE(((u32 *)&vram[address])); + break; + case 7: + value = READ32LE(((u32 *)&oam[address & 0x3FC])); + break; + case 8: + case 9: + case 10: + case 11: + case 12: + value = READ32LE(((u32 *)&rom[address&0x1FFFFFC])); + break; + case 13: + value = eepromRead(address); + break; + case 14: + case 15: + value = flashRead(address) * 0x01010101; + break; + // default + default: +unreadable: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_ILLEGAL_READ) { + log("Illegal word read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } +#endif + if(cpuDmaHack) { + value = cpuDmaLast; + } else { + if(armState) { + return CPUReadMemoryQuick(reg[15].I); + } else { + return CPUReadHalfWordQuick(reg[15].I) | + CPUReadHalfWordQuick(reg[15].I) << 16; + } + } + break; + } + + if(oldAddress & 3) { +#ifdef C_CORE + int shift = (oldAddress & 3) << 3; + value = (value >> shift) | (value << (32 - shift)); +#else +#ifdef __GNUC__ + asm("and $3, %%ecx;" + "shl $3 ,%%ecx;" + "ror %%cl, %0" + : "=r" (value) + : "r" (value), "c" (oldAddress)); +#else + __asm { + mov ecx, oldAddress; + and ecx, 3; + shl ecx, 3; + ror [dword ptr value], cl; + } +#endif +#endif + } + +#ifdef GBA_LOGGING + if(oldAddress & 3) { + if(systemVerbose & VERBOSE_UNALIGNED_MEMORY) { + log("Unaligned word read from: %08x at %08x (%08x)\n", oldAddress, armMode ? + armNextPC - 4 : armNextPC - 2, value); + } + } +#endif + return value; +} + +extern u32 myROM[]; + +static inline u32 CPUReadHalfWord(u32 address) +{ + u32 value; + u32 oldAddress = address; + + if(address & 1) { + address &= ~0x01; + } + + switch(address >> 24) { + case 0: + if (reg[15].I >> 24) { + if(address < 0x4000) { +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_ILLEGAL_READ) { + log("Illegal halfword read from bios: %08x at %08x\n", oldAddress, armMode ? + armNextPC - 4 : armNextPC - 2); + } +#endif + value = READ16LE(((u16 *)&biosProtected[address&2])); + } else goto unreadable; + } else + value = READ16LE(((u16 *)&bios[address & 0x3FFE])); + break; + case 2: + value = READ16LE(((u16 *)&workRAM[address & 0x3FFFE])); + break; + case 3: + value = READ16LE(((u16 *)&internalRAM[address & 0x7ffe])); + break; + case 4: + if((address < 0x4000400) && ioReadable[address & 0x3fe]) + { + value = READ16LE(((u16 *)&ioMem[address & 0x3fe])); + if (((address & 0x3fe)>0xFF) && ((address & 0x3fe)<0x10E)) + { + if (((address & 0x3fe) == 0x100) && timer0On) + value = 0xFFFF - ((timer0Ticks-cpuTotalTicks) >> timer0ClockReload); + else + if (((address & 0x3fe) == 0x104) && timer1On && !(TM1CNT & 4)) + value = 0xFFFF - ((timer1Ticks-cpuTotalTicks) >> timer1ClockReload); + else + if (((address & 0x3fe) == 0x108) && timer2On && !(TM2CNT & 4)) + value = 0xFFFF - ((timer2Ticks-cpuTotalTicks) >> timer2ClockReload); + else + if (((address & 0x3fe) == 0x10C) && timer3On && !(TM3CNT & 4)) + value = 0xFFFF - ((timer3Ticks-cpuTotalTicks) >> timer3ClockReload); + } + } + else if((address < 0x4000400) && ioReadable[address & 0x3fc]) + { + value = 0; + } + else goto unreadable; + break; + case 5: + value = READ16LE(((u16 *)&paletteRAM[address & 0x3fe])); + break; + case 6: + address = (address & 0x1fffe); + if (((DISPCNT & 7) >2) && ((address & 0x1C000) == 0x18000)) + { + value = 0; + break; + } + if ((address & 0x18000) == 0x18000) + address &= 0x17fff; + value = READ16LE(((u16 *)&vram[address])); + break; + case 7: + value = READ16LE(((u16 *)&oam[address & 0x3fe])); + break; + case 8: + case 9: + case 10: + case 11: + case 12: + if(address == 0x80000c4 || address == 0x80000c6 || address == 0x80000c8) + value = rtcRead(address); + else + value = READ16LE(((u16 *)&rom[address & 0x1FFFFFE])); + break; + case 13: + value = eepromRead(address); + break; + case 14: + case 15: + value = flashRead(address) * 0x0101; + break; + // default + default: +unreadable: + if(cpuDmaHack) { + value = cpuDmaLast & 0xFFFF; + } else { + if(armState) { + value = CPUReadHalfWordQuick(reg[15].I + (address & 2)); + } else { + value = CPUReadHalfWordQuick(reg[15].I); + } + } +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_ILLEGAL_READ) { + log("Illegal halfword read: %08x at %08x (%08x)\n", oldAddress, reg[15].I, value); + } +#endif + return value; + } + + if(oldAddress & 1) { + value = (value >> 8) | (value << 24); + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_UNALIGNED_MEMORY) { + log("Unaligned halfword read from: %08x at %08x (%08x)\n", oldAddress, armMode ? + armNextPC - 4 : armNextPC - 2, value); + } + #endif + } + + return value; +} + +static inline s16 CPUReadHalfWordSigned(u32 address) +{ + s32 value = (s32)CPUReadHalfWord(address); + if((address & 1)) + { +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_UNALIGNED_MEMORY) { + log("Unaligned signed halfword read from: %08x at %08x (%08x)\n", address, armMode ? + armNextPC - 4 : armNextPC - 2, value); + } +#endif + } + return (s16)value; +} + +static inline u8 CPUReadByte(u32 address) +{ + switch(address >> 24) { + case 0: + if (reg[15].I >> 24) { + if(address < 0x4000) { +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_ILLEGAL_READ) { + log("Illegal byte read from bios: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } +#endif + return biosProtected[address & 3]; + } else goto unreadable; + } + return bios[address & 0x3FFF]; + case 2: + return workRAM[address & 0x3FFFF]; + case 3: + return internalRAM[address & 0x7fff]; + case 4: + if((address < 0x4000400) && ioReadable[address & 0x3ff]) + return ioMem[address & 0x3ff]; + else goto unreadable; + case 5: + return paletteRAM[address & 0x3ff]; + case 6: + address = (address & 0x1ffff); + if (((DISPCNT & 7) >2) && ((address & 0x1C000) == 0x18000)) + return 0; + if ((address & 0x18000) == 0x18000) + address &= 0x17fff; + return vram[address]; + case 7: + return oam[address & 0x3ff]; + case 8: + case 9: + case 10: + case 11: + case 12: + return rom[address & 0x1FFFFFF]; + case 13: + return eepromRead(address); + case 14: + case 15: + { + if (cpuEEPROMSensorEnabled) { + switch (address & 0x00008f00) { + case 0x8200: + return systemGetSensorX() & 255; + case 0x8300: + return (systemGetSensorX() >> 8) | 0x80; + case 0x8400: + return systemGetSensorY() & 255; + case 0x8500: + return systemGetSensorY() >> 8; + } + } + return flashRead(address); + } + // default + default: +unreadable: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_ILLEGAL_READ) { + log("Illegal byte read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } +#endif + if(cpuDmaHack) { + return cpuDmaLast & 0xFF; + } else { + if(armState) { + return CPUReadByteQuick(reg[15].I + (address & 3)); + } else { + return CPUReadByteQuick(reg[15].I + (address & 1)); + } + } + } +} + +static inline void CPUWriteMemory(u32 address, u32 value) +{ + +#ifdef GBA_LOGGING + if(address & 3) { + if(systemVerbose & VERBOSE_UNALIGNED_MEMORY) { + log("Unaligned word write: %08x to %08x from %08x\n", + value, + address, + armMode ? armNextPC - 4 : armNextPC - 2); + } + } +#endif + + address &= 0xFFFFFFFC; + + switch(address >> 24) { + case 0x02: +#ifdef BKPT_SUPPORT + if(*((u32 *)&freezeWorkRAM[address & 0x3FFFC])) + cheatsWriteMemory(address & 0x203FFFC, + value); + else +#endif + WRITE32LE(((u32 *)&workRAM[address & 0x3FFFC]), value); + break; + case 0x03: +#ifdef BKPT_SUPPORT + if(*((u32 *)&freezeInternalRAM[address & 0x7ffc])) + cheatsWriteMemory(address & 0x3007FFC, + value); + else +#endif + WRITE32LE(((u32 *)&internalRAM[address & 0x7ffC]), value); + break; + case 0x04: + if(address < 0x4000400) { + CPUUpdateRegister((address & 0x3FC), value & 0xFFFF); + CPUUpdateRegister((address & 0x3FC) + 2, (value >> 16)); + } else goto unwritable; + break; + case 0x05: +#ifdef BKPT_SUPPORT + if(*((u32 *)&freezePRAM[address & 0x3fc])) + cheatsWriteMemory(address & 0x70003FC, + value); + else +#endif + WRITE32LE(((u32 *)&paletteRAM[address & 0x3FC]), value); + break; + case 0x06: + address = (address & 0x1fffc); + if (((DISPCNT & 7) >2) && ((address & 0x1C000) == 0x18000)) + return; + if ((address & 0x18000) == 0x18000) + address &= 0x17fff; + +#ifdef BKPT_SUPPORT + if(*((u32 *)&freezeVRAM[address])) + cheatsWriteMemory(address + 0x06000000, value); + else +#endif + + WRITE32LE(((u32 *)&vram[address]), value); + break; + case 0x07: +#ifdef BKPT_SUPPORT + if(*((u32 *)&freezeOAM[address & 0x3fc])) + cheatsWriteMemory(address & 0x70003FC, + value); + else +#endif + WRITE32LE(((u32 *)&oam[address & 0x3fc]), value); + break; + case 0x0D: + if(cpuEEPROMEnabled) { + eepromWrite(address, value); + break; + } + goto unwritable; + case 0x0E: + case 0x0F: + if((!eepromInUse) | cpuSramEnabled | cpuFlashEnabled) { + (*cpuSaveGameFunc)(address, (u8)value); + break; + } + // default + default: +unwritable: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_ILLEGAL_WRITE) { + log("Illegal word write: %08x to %08x from %08x\n", + value, + address, + armMode ? armNextPC - 4 : armNextPC - 2); + } +#endif + break; + } +} + +static inline void CPUWriteHalfWord(u32 address, u16 value) +{ +#ifdef GBA_LOGGING + if(address & 1) { + if(systemVerbose & VERBOSE_UNALIGNED_MEMORY) { + log("Unaligned halfword write: %04x to %08x from %08x\n", + value, + address, + armMode ? armNextPC - 4 : armNextPC - 2); + } + } +#endif + + address &= 0xFFFFFFFE; + + switch(address >> 24) { + case 2: +#ifdef BKPT_SUPPORT + if(*((u16 *)&freezeWorkRAM[address & 0x3FFFE])) + cheatsWriteHalfWord(address & 0x203FFFE, + value); + else +#endif + WRITE16LE(((u16 *)&workRAM[address & 0x3FFFE]),value); + break; + case 3: +#ifdef BKPT_SUPPORT + if(*((u16 *)&freezeInternalRAM[address & 0x7ffe])) + cheatsWriteHalfWord(address & 0x3007ffe, + value); + else +#endif + WRITE16LE(((u16 *)&internalRAM[address & 0x7ffe]), value); + break; + case 4: + if(address < 0x4000400) + CPUUpdateRegister(address & 0x3fe, value); + else goto unwritable; + break; + case 5: +#ifdef BKPT_SUPPORT + if(*((u16 *)&freezePRAM[address & 0x03fe])) + cheatsWriteHalfWord(address & 0x70003fe, + value); + else +#endif + WRITE16LE(((u16 *)&paletteRAM[address & 0x3fe]), value); + break; + case 6: + address = (address & 0x1fffe); + if (((DISPCNT & 7) >2) && ((address & 0x1C000) == 0x18000)) + return; + if ((address & 0x18000) == 0x18000) + address &= 0x17fff; +#ifdef BKPT_SUPPORT + if(*((u16 *)&freezeVRAM[address])) + cheatsWriteHalfWord(address + 0x06000000, + value); + else +#endif + WRITE16LE(((u16 *)&vram[address]), value); + break; + case 7: +#ifdef BKPT_SUPPORT + if(*((u16 *)&freezeOAM[address & 0x03fe])) + cheatsWriteHalfWord(address & 0x70003fe, + value); + else +#endif + WRITE16LE(((u16 *)&oam[address & 0x3fe]), value); + break; + case 8: + case 9: + if(address == 0x80000c4 || address == 0x80000c6 || address == 0x80000c8) { + if(!rtcWrite(address, value)) + goto unwritable; + } else if(!agbPrintWrite(address, value)) goto unwritable; + break; + case 13: + if(cpuEEPROMEnabled) { + eepromWrite(address, (u8)value); + break; + } + goto unwritable; + case 14: + case 15: + if((!eepromInUse) | cpuSramEnabled | cpuFlashEnabled) { + (*cpuSaveGameFunc)(address, (u8)value); + break; + } + goto unwritable; + default: +unwritable: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_ILLEGAL_WRITE) { + log("Illegal halfword write: %04x to %08x from %08x\n", + value, + address, + armMode ? armNextPC - 4 : armNextPC - 2); + } +#endif + break; + } +} + +static inline void CPUWriteByte(u32 address, u8 b) +{ + switch(address >> 24) { + case 2: +#ifdef BKPT_SUPPORT + if(freezeWorkRAM[address & 0x3FFFF]) + cheatsWriteByte(address & 0x203FFFF, b); + else +#endif + workRAM[address & 0x3FFFF] = b; + break; + case 3: +#ifdef BKPT_SUPPORT + if(freezeInternalRAM[address & 0x7fff]) + cheatsWriteByte(address & 0x3007fff, b); + else +#endif + internalRAM[address & 0x7fff] = b; + break; + case 4: + if(address < 0x4000400) { + switch(address & 0x3FF) { + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x68: + case 0x69: + case 0x6c: + case 0x6d: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x78: + case 0x79: + case 0x7c: + case 0x7d: + case 0x80: + case 0x81: + case 0x84: + case 0x85: + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + case 0x98: + case 0x99: + case 0x9a: + case 0x9b: + case 0x9c: + case 0x9d: + case 0x9e: + case 0x9f: + soundEvent(address&0xFF, b); + break; + case 0x301: // HALTCNT, undocumented + if(b == 0x80) + stopState = true; + holdState = 1; + holdType = -1; + cpuNextEvent = cpuTotalTicks; + break; + default: // every other register + u32 lowerBits = address & 0x3fe; + if(address & 1) { + CPUUpdateRegister(lowerBits, (READ16LE(&ioMem[lowerBits]) & 0x00FF) | (b << 8)); + } else { + CPUUpdateRegister(lowerBits, (READ16LE(&ioMem[lowerBits]) & 0xFF00) | b); + } + } + break; + } else goto unwritable; + break; + case 5: + // no need to switch + *((u16 *)&paletteRAM[address & 0x3FE]) = (b << 8) | b; + break; + case 6: + address = (address & 0x1fffe); + if (((DISPCNT & 7) >2) && ((address & 0x1C000) == 0x18000)) + return; + if ((address & 0x18000) == 0x18000) + address &= 0x17fff; + + // no need to switch + // byte writes to OBJ VRAM are ignored + if ((address) < objTilesAddress[((DISPCNT&7)+1)>>2]) + { +#ifdef BKPT_SUPPORT + if(freezeVRAM[address]) + cheatsWriteByte(address + 0x06000000, b); + else +#endif + *((u16 *)&vram[address]) = (b << 8) | b; + } + break; + case 7: + // no need to switch + // byte writes to OAM are ignored + // *((u16 *)&oam[address & 0x3FE]) = (b << 8) | b; + break; + case 13: + if(cpuEEPROMEnabled) { + eepromWrite(address, b); + break; + } + goto unwritable; + case 14: + case 15: + if ((saveType != 5) && ((!eepromInUse) | cpuSramEnabled | cpuFlashEnabled)) { + + //if(!cpuEEPROMEnabled && (cpuSramEnabled | cpuFlashEnabled)) { + + (*cpuSaveGameFunc)(address, b); + break; + } + // default + default: +unwritable: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_ILLEGAL_WRITE) { + log("Illegal byte write: %02x to %08x from %08x\n", + b, + address, + armMode ? armNextPC - 4 : armNextPC -2 ); + } +#endif + break; + } +} + +#endif // GBAINLINE_H diff --git a/src/gba/Globals.cpp b/src/gba/Globals.cpp new file mode 100644 index 0000000..67bef6b --- /dev/null +++ b/src/gba/Globals.cpp @@ -0,0 +1,129 @@ +#include "GBA.h" + +#ifdef BKPT_SUPPORT +int oldreg[18]; +char oldbuffer[10]; +#endif + +reg_pair reg[45]; +memoryMap map[256]; +bool ioReadable[0x400]; +bool N_FLAG = 0; +bool C_FLAG = 0; +bool Z_FLAG = 0; +bool V_FLAG = 0; +bool armState = true; +bool armIrqEnable = true; +u32 armNextPC = 0x00000000; +int armMode = 0x1f; +u32 stop = 0x08000568; +int saveType = 0; +bool useBios = false; +bool skipBios = false; +int frameSkip = 1; +bool speedup = false; +bool synchronize = true; +bool cpuDisableSfx = false; +bool cpuIsMultiBoot = false; +bool parseDebug = true; +int layerSettings = 0xff00; +int layerEnable = 0xff00; +bool speedHack = false; +int cpuSaveType = 0; +bool cheatsEnabled = true; +bool mirroringEnable = false; +bool skipSaveGameBattery = false; +bool skipSaveGameCheats = false; + +// this is an optional hack to change the backdrop/background color: +// -1: disabled +// 0x0000 to 0x7FFF: set custom 15 bit color +int customBackdropColor = -1; + +u8 *bios = 0; +u8 *rom = 0; +u8 *internalRAM = 0; +u8 *workRAM = 0; +u8 *paletteRAM = 0; +u8 *vram = 0; +u8 *pix = 0; +u8 *oam = 0; +u8 *ioMem = 0; + +u16 DISPCNT = 0x0080; +u16 DISPSTAT = 0x0000; +u16 VCOUNT = 0x0000; +u16 BG0CNT = 0x0000; +u16 BG1CNT = 0x0000; +u16 BG2CNT = 0x0000; +u16 BG3CNT = 0x0000; +u16 BG0HOFS = 0x0000; +u16 BG0VOFS = 0x0000; +u16 BG1HOFS = 0x0000; +u16 BG1VOFS = 0x0000; +u16 BG2HOFS = 0x0000; +u16 BG2VOFS = 0x0000; +u16 BG3HOFS = 0x0000; +u16 BG3VOFS = 0x0000; +u16 BG2PA = 0x0100; +u16 BG2PB = 0x0000; +u16 BG2PC = 0x0000; +u16 BG2PD = 0x0100; +u16 BG2X_L = 0x0000; +u16 BG2X_H = 0x0000; +u16 BG2Y_L = 0x0000; +u16 BG2Y_H = 0x0000; +u16 BG3PA = 0x0100; +u16 BG3PB = 0x0000; +u16 BG3PC = 0x0000; +u16 BG3PD = 0x0100; +u16 BG3X_L = 0x0000; +u16 BG3X_H = 0x0000; +u16 BG3Y_L = 0x0000; +u16 BG3Y_H = 0x0000; +u16 WIN0H = 0x0000; +u16 WIN1H = 0x0000; +u16 WIN0V = 0x0000; +u16 WIN1V = 0x0000; +u16 WININ = 0x0000; +u16 WINOUT = 0x0000; +u16 MOSAIC = 0x0000; +u16 BLDMOD = 0x0000; +u16 COLEV = 0x0000; +u16 COLY = 0x0000; +u16 DM0SAD_L = 0x0000; +u16 DM0SAD_H = 0x0000; +u16 DM0DAD_L = 0x0000; +u16 DM0DAD_H = 0x0000; +u16 DM0CNT_L = 0x0000; +u16 DM0CNT_H = 0x0000; +u16 DM1SAD_L = 0x0000; +u16 DM1SAD_H = 0x0000; +u16 DM1DAD_L = 0x0000; +u16 DM1DAD_H = 0x0000; +u16 DM1CNT_L = 0x0000; +u16 DM1CNT_H = 0x0000; +u16 DM2SAD_L = 0x0000; +u16 DM2SAD_H = 0x0000; +u16 DM2DAD_L = 0x0000; +u16 DM2DAD_H = 0x0000; +u16 DM2CNT_L = 0x0000; +u16 DM2CNT_H = 0x0000; +u16 DM3SAD_L = 0x0000; +u16 DM3SAD_H = 0x0000; +u16 DM3DAD_L = 0x0000; +u16 DM3DAD_H = 0x0000; +u16 DM3CNT_L = 0x0000; +u16 DM3CNT_H = 0x0000; +u16 TM0D = 0x0000; +u16 TM0CNT = 0x0000; +u16 TM1D = 0x0000; +u16 TM1CNT = 0x0000; +u16 TM2D = 0x0000; +u16 TM2CNT = 0x0000; +u16 TM3D = 0x0000; +u16 TM3CNT = 0x0000; +u16 P1 = 0xFFFF; +u16 IE = 0x0000; +u16 IF = 0x0000; +u16 IME = 0x0000; diff --git a/src/gba/Globals.h b/src/gba/Globals.h new file mode 100644 index 0000000..7981738 --- /dev/null +++ b/src/gba/Globals.h @@ -0,0 +1,137 @@ +#ifndef GLOBALS_H +#define GLOBALS_H + +#include "../common/Types.h" +#include "GBA.h" + +#define VERBOSE_SWI 1 +#define VERBOSE_UNALIGNED_MEMORY 2 +#define VERBOSE_ILLEGAL_WRITE 4 +#define VERBOSE_ILLEGAL_READ 8 +#define VERBOSE_DMA0 16 +#define VERBOSE_DMA1 32 +#define VERBOSE_DMA2 64 +#define VERBOSE_DMA3 128 +#define VERBOSE_UNDEFINED 256 +#define VERBOSE_AGBPRINT 512 +#define VERBOSE_SOUNDOUTPUT 1024 + +extern reg_pair reg[45]; +extern bool ioReadable[0x400]; +extern bool N_FLAG; +extern bool C_FLAG; +extern bool Z_FLAG; +extern bool V_FLAG; +extern bool armState; +extern bool armIrqEnable; +extern u32 armNextPC; +extern int armMode; +extern u32 stop; +extern int saveType; +extern bool useBios; +extern bool skipBios; +extern int frameSkip; +extern bool speedup; +extern bool synchronize; +extern bool cpuDisableSfx; +extern bool cpuIsMultiBoot; +extern bool parseDebug; +extern int layerSettings; +extern int layerEnable; +extern bool speedHack; +extern int cpuSaveType; +extern bool cheatsEnabled; +extern bool mirroringEnable; +extern bool skipSaveGameBattery; // skip battery data when reading save states +extern bool skipSaveGameCheats; // skip cheat list data when reading save states +extern int customBackdropColor; + +extern u8 *bios; +extern u8 *rom; +extern u8 *internalRAM; +extern u8 *workRAM; +extern u8 *paletteRAM; +extern u8 *vram; +extern u8 *pix; +extern u8 *oam; +extern u8 *ioMem; + +extern u16 DISPCNT; +extern u16 DISPSTAT; +extern u16 VCOUNT; +extern u16 BG0CNT; +extern u16 BG1CNT; +extern u16 BG2CNT; +extern u16 BG3CNT; +extern u16 BG0HOFS; +extern u16 BG0VOFS; +extern u16 BG1HOFS; +extern u16 BG1VOFS; +extern u16 BG2HOFS; +extern u16 BG2VOFS; +extern u16 BG3HOFS; +extern u16 BG3VOFS; +extern u16 BG2PA; +extern u16 BG2PB; +extern u16 BG2PC; +extern u16 BG2PD; +extern u16 BG2X_L; +extern u16 BG2X_H; +extern u16 BG2Y_L; +extern u16 BG2Y_H; +extern u16 BG3PA; +extern u16 BG3PB; +extern u16 BG3PC; +extern u16 BG3PD; +extern u16 BG3X_L; +extern u16 BG3X_H; +extern u16 BG3Y_L; +extern u16 BG3Y_H; +extern u16 WIN0H; +extern u16 WIN1H; +extern u16 WIN0V; +extern u16 WIN1V; +extern u16 WININ; +extern u16 WINOUT; +extern u16 MOSAIC; +extern u16 BLDMOD; +extern u16 COLEV; +extern u16 COLY; +extern u16 DM0SAD_L; +extern u16 DM0SAD_H; +extern u16 DM0DAD_L; +extern u16 DM0DAD_H; +extern u16 DM0CNT_L; +extern u16 DM0CNT_H; +extern u16 DM1SAD_L; +extern u16 DM1SAD_H; +extern u16 DM1DAD_L; +extern u16 DM1DAD_H; +extern u16 DM1CNT_L; +extern u16 DM1CNT_H; +extern u16 DM2SAD_L; +extern u16 DM2SAD_H; +extern u16 DM2DAD_L; +extern u16 DM2DAD_H; +extern u16 DM2CNT_L; +extern u16 DM2CNT_H; +extern u16 DM3SAD_L; +extern u16 DM3SAD_H; +extern u16 DM3DAD_L; +extern u16 DM3DAD_H; +extern u16 DM3CNT_L; +extern u16 DM3CNT_H; +extern u16 TM0D; +extern u16 TM0CNT; +extern u16 TM1D; +extern u16 TM1CNT; +extern u16 TM2D; +extern u16 TM2CNT; +extern u16 TM3D; +extern u16 TM3CNT; +extern u16 P1; +extern u16 IE; +extern u16 IF; +extern u16 IME; + +#endif // GLOBALS_H diff --git a/src/gba/Mode0.cpp b/src/gba/Mode0.cpp new file mode 100644 index 0000000..9fbd658 --- /dev/null +++ b/src/gba/Mode0.cpp @@ -0,0 +1,507 @@ +#include "GBA.h" +#include "Globals.h" +#include "GBAGfx.h" +#include +using namespace std; +void mode0RenderLine() +{ + u16 *palette = (u16 *)paletteRAM; + + if(DISPCNT & 0x80) { + for(int x = 0; x < 240; x++) { + lineMix[x] = 0x7fff; + } + return; + } + + if(layerEnable & 0x0100) { + gfxDrawTextScreen(BG0CNT, BG0HOFS, BG0VOFS, line0); + } + + if(layerEnable & 0x0200) { + gfxDrawTextScreen(BG1CNT, BG1HOFS, BG1VOFS, line1); + } + + if(layerEnable & 0x0400) { + gfxDrawTextScreen(BG2CNT, BG2HOFS, BG2VOFS, line2); + } + + if(layerEnable & 0x0800) { + gfxDrawTextScreen(BG3CNT, BG3HOFS, BG3VOFS, line3); + } + + gfxDrawSprites(lineOBJ); + + u32 backdrop; + if(customBackdropColor == -1) { + backdrop = (READ16LE(&palette[0]) | 0x30000000); + } else { + backdrop = ((customBackdropColor & 0x7FFF) | 0x30000000); + } + + for(int x = 0; x < 240; x++) { + u32 color = backdrop; + u8 top = 0x20; + + if(line0[x] < color) { + color = line0[x]; + top = 0x01; + } + + if((u8)(line1[x]>>24) < (u8)(color >> 24)) { + color = line1[x]; + top = 0x02; + } + + if((u8)(line2[x]>>24) < (u8)(color >> 24)) { + color = line2[x]; + top = 0x04; + } + + if((u8)(line3[x]>>24) < (u8)(color >> 24)) { + color = line3[x]; + top = 0x08; + } + + if((u8)(lineOBJ[x]>>24) < (u8)(color >> 24)) { + color = lineOBJ[x]; + top = 0x10; + } + + if((top & 0x10) && (color & 0x00010000)) { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if((u8)(line0[x]>>24) < (u8)(back >> 24)) { + back = line0[x]; + top2 = 0x01; + } + + if((u8)(line1[x]>>24) < (u8)(back >> 24)) { + back = line1[x]; + top2 = 0x02; + } + + if((u8)(line2[x]>>24) < (u8)(back >> 24)) { + back = line2[x]; + top2 = 0x04; + } + + if((u8)(line3[x]>>24) < (u8)(back >> 24)) { + back = line3[x]; + top2 = 0x08; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else { + switch((BLDMOD >> 6) & 3) { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } +// cout << "write " << color << "to " << x << endl; + lineMix[x] = color; + } +} + +void mode0RenderLineNoWindow() +{ + u16 *palette = (u16 *)paletteRAM; + + if(DISPCNT & 0x80) { + for(int x = 0; x < 240; x++) { + lineMix[x] = 0x7fff; + } + return; + } + + if(layerEnable & 0x0100) { + gfxDrawTextScreen(BG0CNT, BG0HOFS, BG0VOFS, line0); + } + + if(layerEnable & 0x0200) { + gfxDrawTextScreen(BG1CNT, BG1HOFS, BG1VOFS, line1); + } + + if(layerEnable & 0x0400) { + gfxDrawTextScreen(BG2CNT, BG2HOFS, BG2VOFS, line2); + } + + if(layerEnable & 0x0800) { + gfxDrawTextScreen(BG3CNT, BG3HOFS, BG3VOFS, line3); + } + + gfxDrawSprites(lineOBJ); + + u32 backdrop; + if(customBackdropColor == -1) { + backdrop = (READ16LE(&palette[0]) | 0x30000000); + } else { + backdrop = ((customBackdropColor & 0x7FFF) | 0x30000000); + } + + int effect = (BLDMOD >> 6) & 3; + + for(int x = 0; x < 240; x++) { + u32 color = backdrop; + u8 top = 0x20; + + if(line0[x] < color) { + color = line0[x]; + top = 0x01; + } + + if(line1[x] < (color & 0xFF000000)) { + color = line1[x]; + top = 0x02; + } + + if(line2[x] < (color & 0xFF000000)) { + color = line2[x]; + top = 0x04; + } + + if(line3[x] < (color & 0xFF000000)) { + color = line3[x]; + top = 0x08; + } + + if(lineOBJ[x] < (color & 0xFF000000)) { + color = lineOBJ[x]; + top = 0x10; + } + + if(!(color & 0x00010000)) { + switch(effect) { + case 0: + break; + case 1: + { + if(top & BLDMOD) { + u32 back = backdrop; + u8 top2 = 0x20; + if(line0[x] < back) { + if(top != 0x01) { + back = line0[x]; + top2 = 0x01; + } + } + + if(line1[x] < (back & 0xFF000000)) { + if(top != 0x02) { + back = line1[x]; + top2 = 0x02; + } + } + + if(line2[x] < (back & 0xFF000000)) { + if(top != 0x04) { + back = line2[x]; + top2 = 0x04; + } + } + + if(line3[x] < (back & 0xFF000000)) { + if(top != 0x08) { + back = line3[x]; + top2 = 0x08; + } + } + + if(lineOBJ[x] < (back & 0xFF000000)) { + if(top != 0x10) { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } else { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if(line0[x] < back) { + back = line0[x]; + top2 = 0x01; + } + + if(line1[x] < (back & 0xFF000000)) { + back = line1[x]; + top2 = 0x02; + } + + if(line2[x] < (back & 0xFF000000)) { + back = line2[x]; + top2 = 0x04; + } + + if(line3[x] < (back & 0xFF000000)) { + back = line3[x]; + top2 = 0x08; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else { + switch((BLDMOD >> 6) & 3) { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } +} + +void mode0RenderLineAll() +{ + u16 *palette = (u16 *)paletteRAM; + + if(DISPCNT & 0x80) { + for(int x = 0; x < 240; x++) { + lineMix[x] = 0x7fff; + } + return; + } + + bool inWindow0 = false; + bool inWindow1 = false; + + if(layerEnable & 0x2000) { + u8 v0 = WIN0V >> 8; + u8 v1 = WIN0V & 255; + inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); + if(v1 >= v0) + inWindow0 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow0 |= (VCOUNT >= v0 || VCOUNT < v1); + } + if(layerEnable & 0x4000) { + u8 v0 = WIN1V >> 8; + u8 v1 = WIN1V & 255; + inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); + if(v1 >= v0) + inWindow1 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow1 |= (VCOUNT >= v0 || VCOUNT < v1); + } + + if((layerEnable & 0x0100)) { + gfxDrawTextScreen(BG0CNT, BG0HOFS, BG0VOFS, line0); + } + + if((layerEnable & 0x0200)) { + gfxDrawTextScreen(BG1CNT, BG1HOFS, BG1VOFS, line1); + } + + if((layerEnable & 0x0400)) { + gfxDrawTextScreen(BG2CNT, BG2HOFS, BG2VOFS, line2); + } + + if((layerEnable & 0x0800)) { + gfxDrawTextScreen(BG3CNT, BG3HOFS, BG3VOFS, line3); + } + + gfxDrawSprites(lineOBJ); + gfxDrawOBJWin(lineOBJWin); + + u32 backdrop; + if(customBackdropColor == -1) { + backdrop = (READ16LE(&palette[0]) | 0x30000000); + } else { + backdrop = ((customBackdropColor & 0x7FFF) | 0x30000000); + } + + u8 inWin0Mask = WININ & 0xFF; + u8 inWin1Mask = WININ >> 8; + u8 outMask = WINOUT & 0xFF; + + for(int x = 0; x < 240; x++) { + u32 color = backdrop; + u8 top = 0x20; + u8 mask = outMask; + + if(!(lineOBJWin[x] & 0x80000000)) { + mask = WINOUT >> 8; + } + + if(inWindow1) { + if(gfxInWin1[x]) + mask = inWin1Mask; + } + + if(inWindow0) { + if(gfxInWin0[x]) { + mask = inWin0Mask; + } + } + + if((mask & 1) && (line0[x] < color)) { + color = line0[x]; + top = 0x01; + } + + if((mask & 2) && ((u8)(line1[x]>>24) < (u8)(color >> 24))) { + color = line1[x]; + top = 0x02; + } + + if((mask & 4) && ((u8)(line2[x]>>24) < (u8)(color >> 24))) { + color = line2[x]; + top = 0x04; + } + + if((mask & 8) && ((u8)(line3[x]>>24) < (u8)(color >> 24))) { + color = line3[x]; + top = 0x08; + } + + if((mask & 16) && ((u8)(lineOBJ[x]>>24) < (u8)(color >> 24))) { + color = lineOBJ[x]; + top = 0x10; + } + + if(color & 0x00010000) { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if((mask & 1) && ((u8)(line0[x]>>24) < (u8)(back >> 24))) { + back = line0[x]; + top2 = 0x01; + } + + if((mask & 2) && ((u8)(line1[x]>>24) < (u8)(back >> 24))) { + back = line1[x]; + top2 = 0x02; + } + + if((mask & 4) && ((u8)(line2[x]>>24) < (u8)(back >> 24))) { + back = line2[x]; + top2 = 0x04; + } + + if((mask & 8) && ((u8)(line3[x]>>24) < (u8)(back >> 24))) { + back = line3[x]; + top2 = 0x08; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else { + switch((BLDMOD >> 6) & 3) { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } else if(mask & 32) { + // special FX on in the window + switch((BLDMOD >> 6) & 3) { + case 0: + break; + case 1: + { + if(top & BLDMOD) { + u32 back = backdrop; + u8 top2 = 0x20; + if((mask & 1) && (u8)(line0[x]>>24) < (u8)(back >> 24)) { + if(top != 0x01) { + back = line0[x]; + top2 = 0x01; + } + } + + if((mask & 2) && (u8)(line1[x]>>24) < (u8)(back >> 24)) { + if(top != 0x02) { + back = line1[x]; + top2 = 0x02; + } + } + + if((mask & 4) && (u8)(line2[x]>>24) < (u8)(back >> 24)) { + if(top != 0x04) { + back = line2[x]; + top2 = 0x04; + } + } + + if((mask & 8) && (u8)(line3[x]>>24) < (u8)(back >> 24)) { + if(top != 0x08) { + back = line3[x]; + top2 = 0x08; + } + } + + if((mask & 16) && (u8)(lineOBJ[x]>>24) < (u8)(back >> 24)) { + if(top != 0x10) { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + + lineMix[x] = color; + } +} diff --git a/src/gba/Mode1.cpp b/src/gba/Mode1.cpp new file mode 100644 index 0000000..4d49a94 --- /dev/null +++ b/src/gba/Mode1.cpp @@ -0,0 +1,473 @@ +#include "GBA.h" +#include "Globals.h" +#include "GBAGfx.h" + +void mode1RenderLine() +{ + u16 *palette = (u16 *)paletteRAM; + + if(DISPCNT & 0x80) { + for(int x = 0; x < 240; x++) { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + if(layerEnable & 0x0100) { + gfxDrawTextScreen(BG0CNT, BG0HOFS, BG0VOFS, line0); + } + + if(layerEnable & 0x0200) { + gfxDrawTextScreen(BG1CNT, BG1HOFS, BG1VOFS, line1); + } + + if(layerEnable & 0x0400) { + int changed = gfxBG2Changed; + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + gfxDrawRotScreen(BG2CNT, BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + BG2PA, BG2PB, BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, line2); + } + + gfxDrawSprites(lineOBJ); + + u32 backdrop; + if(customBackdropColor == -1) { + backdrop = (READ16LE(&palette[0]) | 0x30000000); + } else { + backdrop = ((customBackdropColor & 0x7FFF) | 0x30000000); + } + + for(int x = 0; x < 240; x++) { + u32 color = backdrop; + u8 top = 0x20; + + if(line0[x] < color) { + color = line0[x]; + top = 0x01; + } + + if((u8)(line1[x]>>24) < (u8)(color >> 24)) { + color = line1[x]; + top = 0x02; + } + + if((u8)(line2[x]>>24) < (u8)(color >> 24)) { + color = line2[x]; + top = 0x04; + } + + if((u8)(lineOBJ[x]>>24) < (u8)(color >> 24)) { + color = lineOBJ[x]; + top = 0x10; + } + + if((top & 0x10) && (color & 0x00010000)) { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if((u8)(line0[x]>>24) < (u8)(back >> 24)) { + back = line0[x]; + top2 = 0x01; + } + + if((u8)(line1[x]>>24) < (u8)(back >> 24)) { + back = line1[x]; + top2 = 0x02; + } + + if((u8)(line2[x]>>24) < (u8)(back >> 24)) { + back = line2[x]; + top2 = 0x04; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else { + switch((BLDMOD >> 6) & 3) { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + +void mode1RenderLineNoWindow() +{ + u16 *palette = (u16 *)paletteRAM; + + if(DISPCNT & 0x80) { + for(int x = 0; x < 240; x++) { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + if(layerEnable & 0x0100) { + gfxDrawTextScreen(BG0CNT, BG0HOFS, BG0VOFS, line0); + } + + + if(layerEnable & 0x0200) { + gfxDrawTextScreen(BG1CNT, BG1HOFS, BG1VOFS, line1); + } + + if(layerEnable & 0x0400) { + int changed = gfxBG2Changed; + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + gfxDrawRotScreen(BG2CNT, BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + BG2PA, BG2PB, BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, line2); + } + + gfxDrawSprites(lineOBJ); + + u32 backdrop; + if(customBackdropColor == -1) { + backdrop = (READ16LE(&palette[0]) | 0x30000000); + } else { + backdrop = ((customBackdropColor & 0x7FFF) | 0x30000000); + } + + for(int x = 0; x < 240; x++) { + u32 color = backdrop; + u8 top = 0x20; + + if(line0[x] < color) { + color = line0[x]; + top = 0x01; + } + + if((u8)(line1[x]>>24) < (u8)(color >> 24)) { + color = line1[x]; + top = 0x02; + } + + if((u8)(line2[x]>>24) < (u8)(color >> 24)) { + color = line2[x]; + top = 0x04; + } + + if((u8)(lineOBJ[x]>>24) < (u8)(color >> 24)) { + color = lineOBJ[x]; + top = 0x10; + } + + if(!(color & 0x00010000)) { + switch((BLDMOD >> 6) & 3) { + case 0: + break; + case 1: + { + if(top & BLDMOD) { + u32 back = backdrop; + u8 top2 = 0x20; + if((u8)(line0[x]>>24) < (u8)(back >> 24)) { + if(top != 0x01) { + back = line0[x]; + top2 = 0x01; + } + } + + if((u8)(line1[x]>>24) < (u8)(back >> 24)) { + if(top != 0x02) { + back = line1[x]; + top2 = 0x02; + } + } + + if((u8)(line2[x]>>24) < (u8)(back >> 24)) { + if(top != 0x04) { + back = line2[x]; + top2 = 0x04; + } + } + + if((u8)(lineOBJ[x]>>24) < (u8)(back >> 24)) { + if(top != 0x10) { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } else { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if((u8)(line0[x]>>24) < (u8)(back >> 24)) { + back = line0[x]; + top2 = 0x01; + } + + if((u8)(line1[x]>>24) < (u8)(back >> 24)) { + back = line1[x]; + top2 = 0x02; + } + + if((u8)(line2[x]>>24) < (u8)(back >> 24)) { + back = line2[x]; + top2 = 0x04; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else { + switch((BLDMOD >> 6) & 3) { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + +void mode1RenderLineAll() +{ + u16 *palette = (u16 *)paletteRAM; + + if(DISPCNT & 0x80) { + for(int x = 0; x < 240; x++) { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + bool inWindow0 = false; + bool inWindow1 = false; + + if(layerEnable & 0x2000) { + u8 v0 = WIN0V >> 8; + u8 v1 = WIN0V & 255; + inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); + if(v1 >= v0) + inWindow0 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow0 |= (VCOUNT >= v0 || VCOUNT < v1); + } + if(layerEnable & 0x4000) { + u8 v0 = WIN1V >> 8; + u8 v1 = WIN1V & 255; + inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); + if(v1 >= v0) + inWindow1 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow1 |= (VCOUNT >= v0 || VCOUNT < v1); + } + + if(layerEnable & 0x0100) { + gfxDrawTextScreen(BG0CNT, BG0HOFS, BG0VOFS, line0); + } + + if(layerEnable & 0x0200) { + gfxDrawTextScreen(BG1CNT, BG1HOFS, BG1VOFS, line1); + } + + if(layerEnable & 0x0400) { + int changed = gfxBG2Changed; + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + gfxDrawRotScreen(BG2CNT, BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + BG2PA, BG2PB, BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, line2); + } + + gfxDrawSprites(lineOBJ); + gfxDrawOBJWin(lineOBJWin); + + u32 backdrop; + if(customBackdropColor == -1) { + backdrop = (READ16LE(&palette[0]) | 0x30000000); + } else { + backdrop = ((customBackdropColor & 0x7FFF) | 0x30000000); + } + + u8 inWin0Mask = WININ & 0xFF; + u8 inWin1Mask = WININ >> 8; + u8 outMask = WINOUT & 0xFF; + + for(int x = 0; x < 240; x++) { + u32 color = backdrop; + u8 top = 0x20; + u8 mask = outMask; + + if(!(lineOBJWin[x] & 0x80000000)) { + mask = WINOUT >> 8; + } + + if(inWindow1) { + if(gfxInWin1[x]) + mask = inWin1Mask; + } + + if(inWindow0) { + if(gfxInWin0[x]) { + mask = inWin0Mask; + } + } + + if(line0[x] < color && (mask & 1)) { + color = line0[x]; + top = 0x01; + } + + if((u8)(line1[x]>>24) < (u8)(color >> 24) && (mask & 2)) { + color = line1[x]; + top = 0x02; + } + + if((u8)(line2[x]>>24) < (u8)(color >> 24) && (mask & 4)) { + color = line2[x]; + top = 0x04; + } + + if((u8)(lineOBJ[x]>>24) < (u8)(color >> 24) && (mask & 16)) { + color = lineOBJ[x]; + top = 0x10; + } + + if(color & 0x00010000) { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if((mask & 1) && (u8)(line0[x]>>24) < (u8)(back >> 24)) { + back = line0[x]; + top2 = 0x01; + } + + if((mask & 2) && (u8)(line1[x]>>24) < (u8)(back >> 24)) { + back = line1[x]; + top2 = 0x02; + } + + if((mask & 4) && (u8)(line2[x]>>24) < (u8)(back >> 24)) { + back = line2[x]; + top2 = 0x04; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else { + switch((BLDMOD >> 6) & 3) { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } else if(mask & 32) { + // special FX on the window + switch((BLDMOD >> 6) & 3) { + case 0: + break; + case 1: + { + if(top & BLDMOD) { + u32 back = backdrop; + u8 top2 = 0x20; + + if((mask & 1) && (u8)(line0[x]>>24) < (u8)(back >> 24)) { + if(top != 0x01) { + back = line0[x]; + top2 = 0x01; + } + } + + if((mask & 2) && (u8)(line1[x]>>24) < (u8)(back >> 24)) { + if(top != 0x02) { + back = line1[x]; + top2 = 0x02; + } + } + + if((mask & 4) && (u8)(line2[x]>>24) < (u8)(back >> 24)) { + if(top != 0x04) { + back = line2[x]; + top2 = 0x04; + } + } + + if((mask & 16) && (u8)(lineOBJ[x]>>24) < (u8)(back >> 24)) { + if(top != 0x10) { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} diff --git a/src/gba/Mode2.cpp b/src/gba/Mode2.cpp new file mode 100644 index 0000000..c5aafd2 --- /dev/null +++ b/src/gba/Mode2.cpp @@ -0,0 +1,443 @@ +#include "GBA.h" +#include "Globals.h" +#include "GBAGfx.h" + +void mode2RenderLine() +{ + u16 *palette = (u16 *)paletteRAM; + + if(DISPCNT & 0x80) { + for(int x = 0; x < 240; x++) { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + if(layerEnable & 0x0400) { + int changed = gfxBG2Changed; + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen(BG2CNT, BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + BG2PA, BG2PB, BG2PC, BG2PD, gfxBG2X, gfxBG2Y, + changed, line2); + } + + if(layerEnable & 0x0800) { + int changed = gfxBG3Changed; + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen(BG3CNT, BG3X_L, BG3X_H, BG3Y_L, BG3Y_H, + BG3PA, BG3PB, BG3PC, BG3PD, gfxBG3X, gfxBG3Y, + changed, line3); + } + + gfxDrawSprites(lineOBJ); + + u32 backdrop; + if(customBackdropColor == -1) { + backdrop = (READ16LE(&palette[0]) | 0x30000000); + } else { + backdrop = ((customBackdropColor & 0x7FFF) | 0x30000000); + } + + for(int x = 0; x < 240; x++) { + u32 color = backdrop; + u8 top = 0x20; + + + if((u8)(line2[x]>>24) < (u8)(color >> 24)) { + color = line2[x]; + top = 0x04; + } + + if((u8)(line3[x]>>24) < (u8)(color >> 24)) { + color = line3[x]; + top = 0x08; + } + + if((u8)(lineOBJ[x]>>24) < (u8)(color >> 24)) { + color = lineOBJ[x]; + top = 0x10; + } + + if((top & 0x10) && (color & 0x00010000)) { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if((u8)(line2[x]>>24) < (u8)(back >> 24)) { + back = line2[x]; + top2 = 0x04; + } + + if((u8)(line3[x]>>24) < (u8)(back >> 24)) { + back = line3[x]; + top2 = 0x08; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else { + switch((BLDMOD >> 6) & 3) { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxBG3Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + +void mode2RenderLineNoWindow() +{ + u16 *palette = (u16 *)paletteRAM; + + if(DISPCNT & 0x80) { + for(int x = 0; x < 240; x++) { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + if(layerEnable & 0x0400) { + int changed = gfxBG2Changed; + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen(BG2CNT, BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + BG2PA, BG2PB, BG2PC, BG2PD, gfxBG2X, gfxBG2Y, + changed, line2); + } + + if(layerEnable & 0x0800) { + int changed = gfxBG3Changed; + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen(BG3CNT, BG3X_L, BG3X_H, BG3Y_L, BG3Y_H, + BG3PA, BG3PB, BG3PC, BG3PD, gfxBG3X, gfxBG3Y, + changed, line3); + } + + gfxDrawSprites(lineOBJ); + + u32 backdrop; + if(customBackdropColor == -1) { + backdrop = (READ16LE(&palette[0]) | 0x30000000); + } else { + backdrop = ((customBackdropColor & 0x7FFF) | 0x30000000); + } + + for(int x = 0; x < 240; x++) { + u32 color = backdrop; + u8 top = 0x20; + + + if((u8)(line2[x]>>24) < (u8)(color >> 24)) { + color = line2[x]; + top = 0x04; + } + + if((u8)(line3[x]>>24) < (u8)(color >> 24)) { + color = line3[x]; + top = 0x08; + } + + if((u8)(lineOBJ[x]>>24) < (u8)(color >> 24)) { + color = lineOBJ[x]; + top = 0x10; + } + + if(!(color & 0x00010000)) { + switch((BLDMOD >> 6) & 3) { + case 0: + break; + case 1: + { + if(top & BLDMOD) { + u32 back = backdrop; + u8 top2 = 0x20; + + if((u8)(line2[x]>>24) < (u8)(back >> 24)) { + if(top != 0x04) { + back = line2[x]; + top2 = 0x04; + } + } + + if((u8)(line3[x]>>24) < (u8)(back >> 24)) { + if(top != 0x08) { + back = line3[x]; + top2 = 0x08; + } + } + + if((u8)(lineOBJ[x]>>24) < (u8)(back >> 24)) { + if(top != 0x10) { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } else { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if((u8)(line2[x]>>24) < (u8)(back >> 24)) { + back = line2[x]; + top2 = 0x04; + } + + if((u8)(line3[x]>>24) < (u8)(back >> 24)) { + back = line3[x]; + top2 = 0x08; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else { + switch((BLDMOD >> 6) & 3) { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxBG3Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + +void mode2RenderLineAll() +{ + u16 *palette = (u16 *)paletteRAM; + + if(DISPCNT & 0x80) { + for(int x = 0; x < 240; x++) { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + bool inWindow0 = false; + bool inWindow1 = false; + + if(layerEnable & 0x2000) { + u8 v0 = WIN0V >> 8; + u8 v1 = WIN0V & 255; + inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); + if(v1 >= v0) + inWindow0 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow0 |= (VCOUNT >= v0 || VCOUNT < v1); + } + if(layerEnable & 0x4000) { + u8 v0 = WIN1V >> 8; + u8 v1 = WIN1V & 255; + inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); + if(v1 >= v0) + inWindow1 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow1 |= (VCOUNT >= v0 || VCOUNT < v1); + } + + if(layerEnable & 0x0400) { + int changed = gfxBG2Changed; + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen(BG2CNT, BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + BG2PA, BG2PB, BG2PC, BG2PD, gfxBG2X, gfxBG2Y, + changed, line2); + } + + if(layerEnable & 0x0800) { + int changed = gfxBG3Changed; + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen(BG3CNT, BG3X_L, BG3X_H, BG3Y_L, BG3Y_H, + BG3PA, BG3PB, BG3PC, BG3PD, gfxBG3X, gfxBG3Y, + changed, line3); + } + + gfxDrawSprites(lineOBJ); + gfxDrawOBJWin(lineOBJWin); + + u32 backdrop; + if(customBackdropColor == -1) { + backdrop = (READ16LE(&palette[0]) | 0x30000000); + } else { + backdrop = ((customBackdropColor & 0x7FFF) | 0x30000000); + } + + u8 inWin0Mask = WININ & 0xFF; + u8 inWin1Mask = WININ >> 8; + u8 outMask = WINOUT & 0xFF; + + for(int x = 0; x < 240; x++) { + u32 color = backdrop; + u8 top = 0x20; + u8 mask = outMask; + + if(!(lineOBJWin[x] & 0x80000000)) { + mask = WINOUT >> 8; + } + + if(inWindow1) { + if(gfxInWin1[x]) + mask = inWin1Mask; + } + + if(inWindow0) { + if(gfxInWin0[x]) { + mask = inWin0Mask; + } + } + + if(line2[x] < color && (mask & 4)) { + color = line2[x]; + top = 0x04; + } + + if((u8)(line3[x]>>24) < (u8)(color >> 24) && (mask & 8)) { + color = line3[x]; + top = 0x08; + } + + if((u8)(lineOBJ[x]>>24) < (u8)(color >> 24) && (mask & 16)) { + color = lineOBJ[x]; + top = 0x10; + } + + if(color & 0x00010000) { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if((mask & 4) && line2[x] < back) { + back = line2[x]; + top2 = 0x04; + } + + if((mask & 8) && (u8)(line3[x]>>24) < (u8)(back >> 24)) { + back = line3[x]; + top2 = 0x08; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else { + switch((BLDMOD >> 6) & 3) { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } else if(mask & 32) { + // special FX on the window + switch((BLDMOD >> 6) & 3) { + case 0: + break; + case 1: + { + if(top & BLDMOD) { + u32 back = backdrop; + u8 top2 = 0x20; + + if((mask & 4) && line2[x] < back) { + if(top != 0x04) { + back = line2[x]; + top2 = 0x04; + } + } + + if((mask & 8) && (u8)(line3[x]>>24) < (u8)(back >> 24)) { + if(top != 0x08) { + back = line3[x]; + top2 = 0x08; + } + } + + if((mask & 16) && (u8)(lineOBJ[x]>>24) < (u8)(back >> 24)) { + if(top != 0x10) { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxBG3Changed = 0; + gfxLastVCOUNT = VCOUNT; +} diff --git a/src/gba/Mode3.cpp b/src/gba/Mode3.cpp new file mode 100644 index 0000000..c744524 --- /dev/null +++ b/src/gba/Mode3.cpp @@ -0,0 +1,374 @@ +#include "GBA.h" +#include "Globals.h" +#include "GBAGfx.h" + +void mode3RenderLine() +{ + u16 *palette = (u16 *)paletteRAM; + + if(DISPCNT & 0x80) { + for(int x = 0; x < 240; x++) { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + if(layerEnable & 0x0400) { + int changed = gfxBG2Changed; + + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen16Bit(BG2CNT, BG2X_L, BG2X_H, + BG2Y_L, BG2Y_H, BG2PA, BG2PB, + BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, + line2); + } + + gfxDrawSprites(lineOBJ); + + u32 background; + if(customBackdropColor == -1) { + background = (READ16LE(&palette[0]) | 0x30000000); + } else { + background = ((customBackdropColor & 0x7FFF) | 0x30000000); + } + + for(int x = 0; x < 240; x++) { + u32 color = background; + u8 top = 0x20; + + if(line2[x] < color) { + color = line2[x]; + top = 0x04; + } + + if((u8)(lineOBJ[x]>>24) < (u8)(color >>24)) { + color = lineOBJ[x]; + top = 0x10; + } + + if((top & 0x10) && (color & 0x00010000)) { + // semi-transparent OBJ + u32 back = background; + u8 top2 = 0x20; + + if(line2[x] < back) { + back = line2[x]; + top2 = 0x04; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else { + switch((BLDMOD >> 6) & 3) { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + +void mode3RenderLineNoWindow() +{ + u16 *palette = (u16 *)paletteRAM; + + if(DISPCNT & 0x80) { + for(int x = 0; x < 240; x++) { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + if(layerEnable & 0x0400) { + int changed = gfxBG2Changed; + + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen16Bit(BG2CNT, BG2X_L, BG2X_H, + BG2Y_L, BG2Y_H, BG2PA, BG2PB, + BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, + line2); + } + + gfxDrawSprites(lineOBJ); + + u32 background; + if(customBackdropColor == -1) { + background = (READ16LE(&palette[0]) | 0x30000000); + } else { + background = ((customBackdropColor & 0x7FFF) | 0x30000000); + } + + for(int x = 0; x < 240; x++) { + u32 color = background; + u8 top = 0x20; + + if(line2[x] < color) { + color = line2[x]; + top = 0x04; + } + + if((u8)(lineOBJ[x]>>24) < (u8)(color >>24)) { + color = lineOBJ[x]; + top = 0x10; + } + + if(!(color & 0x00010000)) { + switch((BLDMOD >> 6) & 3) { + case 0: + break; + case 1: + { + if(top & BLDMOD) { + u32 back = background; + u8 top2 = 0x20; + + if(line2[x] < back) { + if(top != 0x04) { + back = line2[x]; + top2 = 0x04; + } + } + + if((u8)(lineOBJ[x]>>24) < (u8)(back >> 24)) { + if(top != 0x10) { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } else { + // semi-transparent OBJ + u32 back = background; + u8 top2 = 0x20; + + if(line2[x] < back) { + back = line2[x]; + top2 = 0x04; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else { + switch((BLDMOD >> 6) & 3) { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + +void mode3RenderLineAll() +{ + u16 *palette = (u16 *)paletteRAM; + + if(DISPCNT & 0x80) { + for(int x = 0; x < 240; x++) { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + bool inWindow0 = false; + bool inWindow1 = false; + + if(layerEnable & 0x2000) { + u8 v0 = WIN0V >> 8; + u8 v1 = WIN0V & 255; + inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); + if(v1 >= v0) + inWindow0 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow0 |= (VCOUNT >= v0 || VCOUNT < v1); + } + if(layerEnable & 0x4000) { + u8 v0 = WIN1V >> 8; + u8 v1 = WIN1V & 255; + inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); + if(v1 >= v0) + inWindow1 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow1 |= (VCOUNT >= v0 || VCOUNT < v1); + } + + if(layerEnable & 0x0400) { + int changed = gfxBG2Changed; + + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen16Bit(BG2CNT, BG2X_L, BG2X_H, + BG2Y_L, BG2Y_H, BG2PA, BG2PB, + BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, + line2); + } + + gfxDrawSprites(lineOBJ); + gfxDrawOBJWin(lineOBJWin); + + u8 inWin0Mask = WININ & 0xFF; + u8 inWin1Mask = WININ >> 8; + u8 outMask = WINOUT & 0xFF; + + u32 background; + if(customBackdropColor == -1) { + background = (READ16LE(&palette[0]) | 0x30000000); + } else { + background = ((customBackdropColor & 0x7FFF) | 0x30000000); + } + + for(int x = 0; x < 240; x++) { + u32 color = background; + u8 top = 0x20; + u8 mask = outMask; + + if(!(lineOBJWin[x] & 0x80000000)) { + mask = WINOUT >> 8; + } + + if(inWindow1) { + if(gfxInWin1[x]) + mask = inWin1Mask; + } + + if(inWindow0) { + if(gfxInWin0[x]) { + mask = inWin0Mask; + } + } + + if((mask & 4) && (line2[x] < color)) { + color = line2[x]; + top = 0x04; + } + + if((mask & 16) && ((u8)(lineOBJ[x]>>24) < (u8)(color >>24))) { + color = lineOBJ[x]; + top = 0x10; + } + + if(color & 0x00010000) { + // semi-transparent OBJ + u32 back = background; + u8 top2 = 0x20; + + if((mask & 4) && line2[x] < back) { + back = line2[x]; + top2 = 0x04; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else { + switch((BLDMOD >> 6) & 3) { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } else if(mask & 32) { + switch((BLDMOD >> 6) & 3) { + case 0: + break; + case 1: + { + if(top & BLDMOD) { + u32 back = background; + u8 top2 = 0x20; + + if((mask & 4) && line2[x] < back) { + if(top != 0x04) { + back = line2[x]; + top2 = 0x04; + } + } + + if((mask & 16) && (u8)(lineOBJ[x]>>24) < (u8)(back >> 24)) { + if(top != 0x10) { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} diff --git a/src/gba/Mode4.cpp b/src/gba/Mode4.cpp new file mode 100644 index 0000000..01e8b1e --- /dev/null +++ b/src/gba/Mode4.cpp @@ -0,0 +1,371 @@ +#include "GBA.h" +#include "GBAGfx.h" +#include "Globals.h" + +void mode4RenderLine() +{ + u16 *palette = (u16 *)paletteRAM; + + if(DISPCNT & 0x0080) { + for(int x = 0; x < 240; x++) { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + if(layerEnable & 0x400) { + int changed = gfxBG2Changed; + + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen256(BG2CNT, BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + BG2PA, BG2PB, BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, + line2); + } + + gfxDrawSprites(lineOBJ); + + u32 backdrop; + if(customBackdropColor == -1) { + backdrop = (READ16LE(&palette[0]) | 0x30000000); + } else { + backdrop = ((customBackdropColor & 0x7FFF) | 0x30000000); + } + + for(int x = 0; x < 240; x++) { + u32 color = backdrop; + u8 top = 0x20; + + if(line2[x] < color) { + color = line2[x]; + top = 0x04; + } + + if((u8)(lineOBJ[x]>>24) < (u8)(color >> 24)) { + color = lineOBJ[x]; + top = 0x10; + } + + if((top & 0x10) && (color & 0x00010000)) { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if(line2[x] < back) { + back = line2[x]; + top2 = 0x04; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else { + switch((BLDMOD >> 6) & 3) { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + +void mode4RenderLineNoWindow() +{ + u16 *palette = (u16 *)paletteRAM; + + if(DISPCNT & 0x0080) { + for(int x = 0; x < 240; x++) { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + if(layerEnable & 0x400) { + int changed = gfxBG2Changed; + + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen256(BG2CNT, BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + BG2PA, BG2PB, BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, + line2); + } + + gfxDrawSprites(lineOBJ); + + u32 backdrop; + if(customBackdropColor == -1) { + backdrop = (READ16LE(&palette[0]) | 0x30000000); + } else { + backdrop = ((customBackdropColor & 0x7FFF) | 0x30000000); + } + + for(int x = 0; x < 240; x++) { + u32 color = backdrop; + u8 top = 0x20; + + if(line2[x] < color) { + color = line2[x]; + top = 0x04; + } + + if((u8)(lineOBJ[x]>>24) < (u8)(color >> 24)) { + color = lineOBJ[x]; + top = 0x10; + } + + if(!(color & 0x00010000)) { + switch((BLDMOD >> 6) & 3) { + case 0: + break; + case 1: + { + if(top & BLDMOD) { + u32 back = backdrop; + u8 top2 = 0x20; + + if(line2[x] < back) { + if(top != 0x04) { + back = line2[x]; + top2 = 0x04; + } + } + + if((u8)(lineOBJ[x]>>24) < (u8)(back >> 24)) { + if(top != 0x10) { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } else { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if(line2[x] < back) { + back = line2[x]; + top2 = 0x04; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else { + switch((BLDMOD >> 6) & 3) { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + +void mode4RenderLineAll() +{ + u16 *palette = (u16 *)paletteRAM; + + if(DISPCNT & 0x0080) { + for(int x = 0; x < 240; x++) { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + bool inWindow0 = false; + bool inWindow1 = false; + + if(layerEnable & 0x2000) { + u8 v0 = WIN0V >> 8; + u8 v1 = WIN0V & 255; + inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); + if(v1 >= v0) + inWindow0 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow0 |= (VCOUNT >= v0 || VCOUNT < v1); + } + if(layerEnable & 0x4000) { + u8 v0 = WIN1V >> 8; + u8 v1 = WIN1V & 255; + inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); + if(v1 >= v0) + inWindow1 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow1 |= (VCOUNT >= v0 || VCOUNT < v1); + } + + if(layerEnable & 0x400) { + int changed = gfxBG2Changed; + + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen256(BG2CNT, BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + BG2PA, BG2PB, BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, + line2); + } + + gfxDrawSprites(lineOBJ); + gfxDrawOBJWin(lineOBJWin); + + u32 backdrop; + if(customBackdropColor == -1) { + backdrop = (READ16LE(&palette[0]) | 0x30000000); + } else { + backdrop = ((customBackdropColor & 0x7FFF) | 0x30000000); + } + + u8 inWin0Mask = WININ & 0xFF; + u8 inWin1Mask = WININ >> 8; + u8 outMask = WINOUT & 0xFF; + + for(int x = 0; x < 240; x++) { + u32 color = backdrop; + u8 top = 0x20; + u8 mask = outMask; + + if(!(lineOBJWin[x] & 0x80000000)) { + mask = WINOUT >> 8; + } + + if(inWindow1) { + if(gfxInWin1[x]) + mask = inWin1Mask; + } + + if(inWindow0) { + if(gfxInWin0[x]) { + mask = inWin0Mask; + } + } + + if((mask & 4) && (line2[x] < color)) { + color = line2[x]; + top = 0x04; + } + + if((mask & 16) && ((u8)(lineOBJ[x]>>24) < (u8)(color >>24))) { + color = lineOBJ[x]; + top = 0x10; + } + + if(color & 0x00010000) { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if((mask & 4) && line2[x] < back) { + back = line2[x]; + top2 = 0x04; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else { + switch((BLDMOD >> 6) & 3) { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } else if(mask & 32) { + switch((BLDMOD >> 6) & 3) { + case 0: + break; + case 1: + { + if(top & BLDMOD) { + u32 back = backdrop; + u8 top2 = 0x20; + + if((mask & 4) && line2[x] < back) { + if(top != 0x04) { + back = line2[x]; + top2 = 0x04; + } + } + + if((mask & 16) && (u8)(lineOBJ[x]>>24) < (u8)(back >> 24)) { + if(top != 0x10) { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} diff --git a/src/gba/Mode5.cpp b/src/gba/Mode5.cpp new file mode 100644 index 0000000..11f49db --- /dev/null +++ b/src/gba/Mode5.cpp @@ -0,0 +1,374 @@ +#include "GBA.h" +#include "Globals.h" +#include "GBAGfx.h" + +void mode5RenderLine() +{ + if(DISPCNT & 0x0080) { + for(int x = 0; x < 240; x++) { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + u16 *palette = (u16 *)paletteRAM; + + if(layerEnable & 0x0400) { + int changed = gfxBG2Changed; + + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen16Bit160(BG2CNT, BG2X_L, BG2X_H, + BG2Y_L, BG2Y_H, BG2PA, BG2PB, + BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, + line2); + } + + gfxDrawSprites(lineOBJ); + + u32 background; + if(customBackdropColor == -1) { + background = (READ16LE(&palette[0]) | 0x30000000); + } else { + background = ((customBackdropColor & 0x7FFF) | 0x30000000); + } + + for(int x = 0; x < 240; x++) { + u32 color = background; + u8 top = 0x20; + + if(line2[x] < color) { + color = line2[x]; + top = 0x04; + } + + if((u8)(lineOBJ[x]>>24) < (u8)(color >>24)) { + color = lineOBJ[x]; + top = 0x10; + } + + if((top & 0x10) && (color & 0x00010000)) { + // semi-transparent OBJ + u32 back = background; + u8 top2 = 0x20; + + if(line2[x] < back) { + back = line2[x]; + top2 = 0x04; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else { + switch((BLDMOD >> 6) & 3) { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + +void mode5RenderLineNoWindow() +{ + if(DISPCNT & 0x0080) { + for(int x = 0; x < 240; x++) { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + u16 *palette = (u16 *)paletteRAM; + + if(layerEnable & 0x0400) { + int changed = gfxBG2Changed; + + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen16Bit160(BG2CNT, BG2X_L, BG2X_H, + BG2Y_L, BG2Y_H, BG2PA, BG2PB, + BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, + line2); + } + + gfxDrawSprites(lineOBJ); + + u32 background; + if(customBackdropColor == -1) { + background = (READ16LE(&palette[0]) | 0x30000000); + } else { + background = ((customBackdropColor & 0x7FFF) | 0x30000000); + } + + for(int x = 0; x < 240; x++) { + u32 color = background; + u8 top = 0x20; + + if(line2[x] < color) { + color = line2[x]; + top = 0x04; + } + + if((u8)(lineOBJ[x]>>24) < (u8)(color >>24)) { + color = lineOBJ[x]; + top = 0x10; + } + + if(!(color & 0x00010000)) { + switch((BLDMOD >> 6) & 3) { + case 0: + break; + case 1: + { + if(top & BLDMOD) { + u32 back = background; + u8 top2 = 0x20; + + if(line2[x] < back) { + if(top != 0x04) { + back = line2[x]; + top2 = 0x04; + } + } + + if((u8)(lineOBJ[x]>>24) < (u8)(back >> 24)) { + if(top != 0x10) { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } else { + // semi-transparent OBJ + u32 back = background; + u8 top2 = 0x20; + + if(line2[x] < back) { + back = line2[x]; + top2 = 0x04; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else { + switch((BLDMOD >> 6) & 3) { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + +void mode5RenderLineAll() +{ + if(DISPCNT & 0x0080) { + for(int x = 0; x < 240; x++) { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + u16 *palette = (u16 *)paletteRAM; + + if(layerEnable & 0x0400) { + int changed = gfxBG2Changed; + + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen16Bit160(BG2CNT, BG2X_L, BG2X_H, + BG2Y_L, BG2Y_H, BG2PA, BG2PB, + BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, + line2); + } + + gfxDrawSprites(lineOBJ); + gfxDrawOBJWin(lineOBJWin); + + bool inWindow0 = false; + bool inWindow1 = false; + + if(layerEnable & 0x2000) { + u8 v0 = WIN0V >> 8; + u8 v1 = WIN0V & 255; + inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); + if(v1 >= v0) + inWindow0 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow0 |= (VCOUNT >= v0 || VCOUNT < v1); + } + if(layerEnable & 0x4000) { + u8 v0 = WIN1V >> 8; + u8 v1 = WIN1V & 255; + inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); + if(v1 >= v0) + inWindow1 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow1 |= (VCOUNT >= v0 || VCOUNT < v1); + } + + u8 inWin0Mask = WININ & 0xFF; + u8 inWin1Mask = WININ >> 8; + u8 outMask = WINOUT & 0xFF; + + u32 background; + if(customBackdropColor == -1) { + background = (READ16LE(&palette[0]) | 0x30000000); + } else { + background = ((customBackdropColor & 0x7FFF) | 0x30000000); + } + + for(int x = 0; x < 240; x++) { + u32 color = background; + u8 top = 0x20; + u8 mask = outMask; + + if(!(lineOBJWin[x] & 0x80000000)) { + mask = WINOUT >> 8; + } + + if(inWindow1) { + if(gfxInWin1[x]) + mask = inWin1Mask; + } + + if(inWindow0) { + if(gfxInWin0[x]) { + mask = inWin0Mask; + } + } + + if((mask & 4) && (line2[x] < color)) { + color = line2[x]; + top = 0x04; + } + + if((mask & 16) && ((u8)(lineOBJ[x]>>24) < (u8)(color >>24))) { + color = lineOBJ[x]; + top = 0x10; + } + + if(color & 0x00010000) { + // semi-transparent OBJ + u32 back = background; + u8 top2 = 0x20; + + if((mask & 4) && line2[x] < back) { + back = line2[x]; + top2 = 0x04; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else { + switch((BLDMOD >> 6) & 3) { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } else if(mask & 32) { + switch((BLDMOD >> 6) & 3) { + case 0: + break; + case 1: + { + if(top & BLDMOD) { + u32 back = background; + u8 top2 = 0x20; + + if((mask & 4) && line2[x] < back) { + if(top != 0x04) { + back = line2[x]; + top2 = 0x04; + } + } + + if((mask & 16) && (u8)(lineOBJ[x]>>24) < (u8)(back >> 24)) { + if(top != 0x10) { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} diff --git a/src/gba/RTC.cpp b/src/gba/RTC.cpp new file mode 100644 index 0000000..bc060ac --- /dev/null +++ b/src/gba/RTC.cpp @@ -0,0 +1,227 @@ +#include "../System.h" +#include "GBA.h" +#include "Globals.h" +#include "../common/Port.h" +#include "../Util.h" +#include "../NLS.h" + +#include +#include +#include + +enum RTCSTATE +{ + IDLE = 0, + COMMAND, + DATA, + READDATA +}; + +typedef struct { + u8 byte0; + u8 byte1; + u8 byte2; + u8 command; + int dataLen; + int bits; + RTCSTATE state; + u8 data[12]; + // reserved variables for future + u8 reserved[12]; + bool reserved2; + u32 reserved3; +} RTCCLOCKDATA; + +static RTCCLOCKDATA rtcClockData; +static bool rtcEnabled = false; + +void rtcEnable(bool e) +{ + rtcEnabled = e; +} + +bool rtcIsEnabled() +{ + return rtcEnabled; +} + +u16 rtcRead(u32 address) +{ + if(rtcEnabled) { + switch(address){ + case 0x80000c8: + return rtcClockData.byte2; + break; + case 0x80000c6: + return rtcClockData.byte1; + break; + case 0x80000c4: + return rtcClockData.byte0; + break; + } + } + + return READ16LE((&rom[address & 0x1FFFFFE])); +} + +static u8 toBCD(u8 value) +{ + value = value % 100; + int l = value % 10; + int h = value / 10; + return h * 16 + l; +} + +bool rtcWrite(u32 address, u16 value) +{ + if(!rtcEnabled) + return false; + + if(address == 0x80000c8) { + rtcClockData.byte2 = (u8)value; // enable ? + } else if(address == 0x80000c6) { + rtcClockData.byte1 = (u8)value; // read/write + } else if(address == 0x80000c4) { + if(rtcClockData.byte2 & 1) { + if(rtcClockData.state == IDLE && rtcClockData.byte0 == 1 && value == 5) { + rtcClockData.state = COMMAND; + rtcClockData.bits = 0; + rtcClockData.command = 0; + } else if(!(rtcClockData.byte0 & 1) && (value & 1)) { // bit transfer + rtcClockData.byte0 = (u8)value; + switch(rtcClockData.state) { + case COMMAND: + rtcClockData.command |= ((value & 2) >> 1) << (7-rtcClockData.bits); + rtcClockData.bits++; + if(rtcClockData.bits == 8) { + rtcClockData.bits = 0; + switch(rtcClockData.command) { + case 0x60: + // not sure what this command does but it doesn't take parameters + // maybe it is a reset or stop + rtcClockData.state = IDLE; + rtcClockData.bits = 0; + break; + case 0x62: + // this sets the control state but not sure what those values are + rtcClockData.state = READDATA; + rtcClockData.dataLen = 1; + break; + case 0x63: + rtcClockData.dataLen = 1; + rtcClockData.data[0] = 0x40; + rtcClockData.state = DATA; + break; + case 0x64: + break; + case 0x65: + { + struct tm *newtime; + time_t long_time; + + time( &long_time ); /* Get time as long integer. */ + newtime = localtime( &long_time ); /* Convert to local time. */ + + rtcClockData.dataLen = 7; + rtcClockData.data[0] = toBCD(newtime->tm_year); + rtcClockData.data[1] = toBCD(newtime->tm_mon+1); + rtcClockData.data[2] = toBCD(newtime->tm_mday); + rtcClockData.data[3] = toBCD(newtime->tm_wday); + rtcClockData.data[4] = toBCD(newtime->tm_hour); + rtcClockData.data[5] = toBCD(newtime->tm_min); + rtcClockData.data[6] = toBCD(newtime->tm_sec); + rtcClockData.state = DATA; + } + break; + case 0x67: + { + struct tm *newtime; + time_t long_time; + + time( &long_time ); /* Get time as long integer. */ + newtime = localtime( &long_time ); /* Convert to local time. */ + + rtcClockData.dataLen = 3; + rtcClockData.data[0] = toBCD(newtime->tm_hour); + rtcClockData.data[1] = toBCD(newtime->tm_min); + rtcClockData.data[2] = toBCD(newtime->tm_sec); + rtcClockData.state = DATA; + } + break; + default: + systemMessage(0, N_("Unknown RTC command %02x"), rtcClockData.command); + rtcClockData.state = IDLE; + break; + } + } + break; + case DATA: + if(rtcClockData.byte1 & 2) { + } else { + rtcClockData.byte0 = (rtcClockData.byte0 & ~2) | + ((rtcClockData.data[rtcClockData.bits >> 3] >> + (rtcClockData.bits & 7)) & 1)*2; + rtcClockData.bits++; + if(rtcClockData.bits == 8*rtcClockData.dataLen) { + rtcClockData.bits = 0; + rtcClockData.state = IDLE; + } + } + break; + case READDATA: + if(!(rtcClockData.byte1 & 2)) { + } else { + rtcClockData.data[rtcClockData.bits >> 3] = + (rtcClockData.data[rtcClockData.bits >> 3] >> 1) | + ((value << 6) & 128); + rtcClockData.bits++; + if(rtcClockData.bits == 8*rtcClockData.dataLen) { + rtcClockData.bits = 0; + rtcClockData.state = IDLE; + } + } + break; + default: + break; + } + } else + rtcClockData.byte0 = (u8)value; + } + } + return true; +} + +void rtcReset() +{ + memset(&rtcClockData, 0, sizeof(rtcClockData)); + + rtcClockData.byte0 = 0; + rtcClockData.byte1 = 0; + rtcClockData.byte2 = 0; + rtcClockData.command = 0; + rtcClockData.dataLen = 0; + rtcClockData.bits = 0; + rtcClockData.state = IDLE; +} + +#ifdef __LIBRETRO__ +void rtcSaveGame(u8 *&data) +{ + utilWriteMem(data, &rtcClockData, sizeof(rtcClockData)); +} + +void rtcReadGame(const u8 *&data) +{ + utilReadMem(&rtcClockData, data, sizeof(rtcClockData)); +} +#else +void rtcSaveGame(gzFile gzFile) +{ + utilGzWrite(gzFile, &rtcClockData, sizeof(rtcClockData)); +} + +void rtcReadGame(gzFile gzFile) +{ + utilGzRead(gzFile, &rtcClockData, sizeof(rtcClockData)); +} +#endif diff --git a/src/gba/RTC.h b/src/gba/RTC.h new file mode 100644 index 0000000..a125a62 --- /dev/null +++ b/src/gba/RTC.h @@ -0,0 +1,18 @@ +#ifndef RTC_H +#define RTC_H + +u16 rtcRead(u32 address); +bool rtcWrite(u32 address, u16 value); +void rtcEnable(bool); +bool rtcIsEnabled(); +void rtcReset(); + +#ifdef __LIBRETRO__ +void rtcReadGame(const u8 *&data); +void rtcSaveGame(u8 *&data); +#else +void rtcReadGame(gzFile gzFile); +void rtcSaveGame(gzFile gzFile); +#endif + +#endif // RTC_H diff --git a/src/gba/Sound.cpp b/src/gba/Sound.cpp new file mode 100644 index 0000000..442c203 --- /dev/null +++ b/src/gba/Sound.cpp @@ -0,0 +1,837 @@ +#include + +#include "Sound.h" + +#include "GBA.h" +#include "Globals.h" +#include "../Util.h" +#include "../common/Port.h" + +#include "../apu/Gb_Apu.h" +#include "../apu/Multi_Buffer.h" + +#include "../common/SoundDriver.h" + +#define NR10 0x60 +#define NR11 0x62 +#define NR12 0x63 +#define NR13 0x64 +#define NR14 0x65 +#define NR21 0x68 +#define NR22 0x69 +#define NR23 0x6c +#define NR24 0x6d +#define NR30 0x70 +#define NR31 0x72 +#define NR32 0x73 +#define NR33 0x74 +#define NR34 0x75 +#define NR41 0x78 +#define NR42 0x79 +#define NR43 0x7c +#define NR44 0x7d +#define NR50 0x80 +#define NR51 0x81 +#define NR52 0x84 + +SoundDriver * soundDriver = 0; + +extern bool stopState; // TODO: silence sound when true + +int const SOUND_CLOCK_TICKS_ = 167772; // 1/100 second + +static u16 soundFinalWave [1600]; +long soundSampleRate = 44100; +bool soundInterpolation = true; +bool soundPaused = true; +float soundFiltering = 0.5f; +int SOUND_CLOCK_TICKS = SOUND_CLOCK_TICKS_; +int soundTicks = SOUND_CLOCK_TICKS_; + +static float soundVolume = 1.0f; +static int soundEnableFlag = 0x3ff; // emulator channels enabled +static float soundFiltering_ = -1; +static float soundVolume_ = -1; + +void interp_rate() { /* empty for now */ } + +class Gba_Pcm { +public: + void init(); + void apply_control( int idx ); + void update( int dac ); + void end_frame( blip_time_t ); + +private: + Blip_Buffer* output; + blip_time_t last_time; + int last_amp; + int shift; +}; + +class Gba_Pcm_Fifo { +public: + int which; + Gba_Pcm pcm; + + void write_control( int data ); + void write_fifo( int data ); + void timer_overflowed( int which_timer ); + + // public only so save state routines can access it + int readIndex; + int count; + int writeIndex; + u8 fifo [32]; + int dac; +private: + + int timer; + bool enabled; +}; + +static Gba_Pcm_Fifo pcm [2]; +static Gb_Apu* gb_apu; +static Stereo_Buffer* stereo_buffer; + +static Blip_Synth pcm_synth [3]; // 32 kHz, 16 kHz, 8 kHz + +static inline blip_time_t blip_time() +{ + return SOUND_CLOCK_TICKS - soundTicks; +} + +void Gba_Pcm::init() +{ + output = 0; + last_time = 0; + last_amp = 0; + shift = 0; +} + +void Gba_Pcm::apply_control( int idx ) +{ + shift = ~ioMem [SGCNT0_H] >> (2 + idx) & 1; + + int ch = 0; + if ( (soundEnableFlag >> idx & 0x100) && (ioMem [NR52] & 0x80) ) + ch = ioMem [SGCNT0_H+1] >> (idx * 4) & 3; + + Blip_Buffer* out = 0; + switch ( ch ) + { + case 1: out = stereo_buffer->right(); break; + case 2: out = stereo_buffer->left(); break; + case 3: out = stereo_buffer->center(); break; + } + + if ( output != out ) + { + if ( output ) + { + output->set_modified(); + pcm_synth [0].offset( blip_time(), -last_amp, output ); + } + last_amp = 0; + output = out; + } +} + +void Gba_Pcm::end_frame( blip_time_t time ) +{ + last_time -= time; + if ( last_time < -2048 ) + last_time = -2048; + + if ( output ) + output->set_modified(); +} + +void Gba_Pcm::update( int dac ) +{ + if ( output ) + { + blip_time_t time = blip_time(); + + dac = (s8) dac >> shift; + int delta = dac - last_amp; + if ( delta ) + { + last_amp = dac; + + int filter = 0; + if ( soundInterpolation ) + { + // base filtering on how long since last sample was output + int period = time - last_time; + + int idx = (unsigned) period / 512; + if ( idx >= 3 ) + idx = 3; + + static int const filters [4] = { 0, 0, 1, 2 }; + filter = filters [idx]; + } + + pcm_synth [filter].offset( time, delta, output ); + } + last_time = time; + } +} + +void Gba_Pcm_Fifo::timer_overflowed( int which_timer ) +{ + if ( which_timer == timer && enabled ) + { + /* Mother 3 fix, refined to not break Metroid Fusion */ + if ( count == 16 || count == 0 ) + { + // Need to fill FIFO + int saved_count = count; + CPUCheckDMA( 3, which ? 4 : 2 ); + if ( saved_count == 0 && count == 16 ) + CPUCheckDMA( 3, which ? 4 : 2 ); + if ( count == 0 ) + { + // Not filled by DMA, so fill with 16 bytes of silence + int reg = which ? FIFOB_L : FIFOA_L; + for ( int n = 8; n--; ) + { + soundEvent(reg , (u16)0); + soundEvent(reg+2, (u16)0); + } + } + } + + // Read next sample from FIFO + count--; + dac = fifo [readIndex]; + readIndex = (readIndex + 1) & 31; + pcm.update( dac ); + } +} + +void Gba_Pcm_Fifo::write_control( int data ) +{ + enabled = (data & 0x0300) ? true : false; + timer = (data & 0x0400) ? 1 : 0; + + if ( data & 0x0800 ) + { + // Reset + writeIndex = 0; + readIndex = 0; + count = 0; + dac = 0; + memset( fifo, 0, sizeof fifo ); + } + + pcm.apply_control( which ); + pcm.update( dac ); +} + +void Gba_Pcm_Fifo::write_fifo( int data ) +{ + fifo [writeIndex ] = data & 0xFF; + fifo [writeIndex+1] = data >> 8; + count += 2; + writeIndex = (writeIndex + 2) & 31; +} + +static void apply_control() +{ + pcm [0].pcm.apply_control( 0 ); + pcm [1].pcm.apply_control( 1 ); +} + +static int gba_to_gb_sound( int addr ) +{ + static const int table [0x40] = + { + 0xFF10, 0,0xFF11,0xFF12,0xFF13,0xFF14, 0, 0, + 0xFF16,0xFF17, 0, 0,0xFF18,0xFF19, 0, 0, + 0xFF1A, 0,0xFF1B,0xFF1C,0xFF1D,0xFF1E, 0, 0, + 0xFF20,0xFF21, 0, 0,0xFF22,0xFF23, 0, 0, + 0xFF24,0xFF25, 0, 0,0xFF26, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0xFF30,0xFF31,0xFF32,0xFF33,0xFF34,0xFF35,0xFF36,0xFF37, + 0xFF38,0xFF39,0xFF3A,0xFF3B,0xFF3C,0xFF3D,0xFF3E,0xFF3F, + }; + if ( addr >= 0x60 && addr < 0xA0 ) + return table [addr - 0x60]; + return 0; +} + +void soundEvent(u32 address, u8 data) +{ + int gb_addr = gba_to_gb_sound( address ); + if ( gb_addr ) + { + ioMem[address] = data; + gb_apu->write_register( blip_time(), gb_addr, data ); + + if ( address == NR52 ) + apply_control(); + } + + // TODO: what about byte writes to SGCNT0_H etc.? +} + +static void apply_volume( bool apu_only = false ) +{ + if ( !apu_only ) + soundVolume_ = soundVolume; + + if ( gb_apu ) + { + static float const apu_vols [4] = { 0.25, 0.5, 1, 0.25 }; + gb_apu->volume( soundVolume_ * apu_vols [ioMem [SGCNT0_H] & 3] ); + } + + if ( !apu_only ) + { + for ( int i = 0; i < 3; i++ ) + pcm_synth [i].volume( 0.66 / 256 * soundVolume_ ); + } +} + +static void write_SGCNT0_H( int data ) +{ + WRITE16LE( &ioMem [SGCNT0_H], data & 0x770F ); + pcm [0].write_control( data ); + pcm [1].write_control( data >> 4 ); + apply_volume( true ); +} + +void soundEvent(u32 address, u16 data) +{ + switch ( address ) + { + case SGCNT0_H: + write_SGCNT0_H( data ); + break; + + case FIFOA_L: + case FIFOA_H: + pcm [0].write_fifo( data ); + WRITE16LE( &ioMem[address], data ); + break; + + case FIFOB_L: + case FIFOB_H: + pcm [1].write_fifo( data ); + WRITE16LE( &ioMem[address], data ); + break; + + case 0x88: + data &= 0xC3FF; + WRITE16LE( &ioMem[address], data ); + break; + + default: + soundEvent( address & ~1, (u8) (data ) ); // even + soundEvent( address | 1, (u8) (data >> 8) ); // odd + break; + } +} + +void soundTimerOverflow(int timer) +{ + pcm [0].timer_overflowed( timer ); + pcm [1].timer_overflowed( timer ); +} + +static void end_frame( blip_time_t time ) +{ + pcm [0].pcm.end_frame( time ); + pcm [1].pcm.end_frame( time ); + + gb_apu ->end_frame( time ); + stereo_buffer->end_frame( time ); +} + +void flush_samples(Multi_Buffer * buffer) +{ +#ifdef __LIBRETRO__ + int numSamples = buffer->read_samples( (blip_sample_t*) soundFinalWave, buffer->samples_avail() ); + soundDriver->write(soundFinalWave, numSamples); + systemOnWriteDataToSoundBuffer(soundFinalWave, numSamples); +#else + // We want to write the data frame by frame to support legacy audio drivers + // that don't use the length parameter of the write method. + // TODO: Update the Win32 audio drivers (DS, OAL, XA2), and flush all the + // samples at once to help reducing the audio delay on all platforms. + int soundBufferLen = ( soundSampleRate / 60 ) * 4; + + // soundBufferLen should have a whole number of sample pairs + assert( soundBufferLen % (2 * sizeof *soundFinalWave) == 0 ); + + // number of samples in output buffer + int const out_buf_size = soundBufferLen / sizeof *soundFinalWave; + + // Keep filling and writing soundFinalWave until it can't be fully filled + while ( buffer->samples_avail() >= out_buf_size ) + { + buffer->read_samples( (blip_sample_t*) soundFinalWave, out_buf_size ); + if(soundPaused) + soundResume(); + + soundDriver->write(soundFinalWave, soundBufferLen); + systemOnWriteDataToSoundBuffer(soundFinalWave, soundBufferLen); + } +#endif +} + +static void apply_filtering() +{ + soundFiltering_ = soundFiltering; + + int const base_freq = (int) (32768 - soundFiltering_ * 16384); + int const nyquist = stereo_buffer->sample_rate() / 2; + + for ( int i = 0; i < 3; i++ ) + { + int cutoff = base_freq >> i; + if ( cutoff > nyquist ) + cutoff = nyquist; + pcm_synth [i].treble_eq( blip_eq_t( 0, 0, stereo_buffer->sample_rate(), cutoff ) ); + } +} + +void psoundTickfn() +{ + if ( gb_apu && stereo_buffer ) + { + // Run sound hardware to present + end_frame( SOUND_CLOCK_TICKS ); + + flush_samples(stereo_buffer); + + if ( soundFiltering_ != soundFiltering ) + apply_filtering(); + + if ( soundVolume_ != soundVolume ) + apply_volume(); + } +} + +static void apply_muting() +{ + if ( !stereo_buffer || !ioMem ) + return; + + // PCM + apply_control(); + + if ( gb_apu ) + { + // APU + for ( int i = 0; i < 4; i++ ) + { + if ( soundEnableFlag >> i & 1 ) + gb_apu->set_output( stereo_buffer->center(), + stereo_buffer->left(), stereo_buffer->right(), i ); + else + gb_apu->set_output( 0, 0, 0, i ); + } + } +} + +static void reset_apu() +{ + gb_apu->reset( gb_apu->mode_agb, true ); + + if ( stereo_buffer ) + stereo_buffer->clear(); + + soundTicks = SOUND_CLOCK_TICKS; +} + +static void remake_stereo_buffer() +{ + if ( !ioMem ) + return; + + // Clears pointers kept to old stereo_buffer + pcm [0].pcm.init(); + pcm [1].pcm.init(); + + // APU + if ( !gb_apu ) + { + gb_apu = new Gb_Apu; // TODO: handle out of memory + reset_apu(); + } + + // Stereo_Buffer + delete stereo_buffer; + stereo_buffer = 0; + + stereo_buffer = new Stereo_Buffer; // TODO: handle out of memory + stereo_buffer->set_sample_rate( soundSampleRate ); // TODO: handle out of memory + stereo_buffer->clock_rate( gb_apu->clock_rate ); + + // PCM + pcm [0].which = 0; + pcm [1].which = 1; + apply_filtering(); + + // Volume Level + apply_muting(); + apply_volume(); +} + +void soundShutdown() +{ + if (soundDriver) + { + delete soundDriver; + soundDriver = 0; + } + + systemOnSoundShutdown(); +} + +void soundPause() +{ + soundPaused = true; + if (soundDriver) + soundDriver->pause(); +} + +void soundResume() +{ + soundPaused = false; + if (soundDriver) + soundDriver->resume(); +} + +void soundSetVolume( float volume ) +{ + soundVolume = volume; +} + +float soundGetVolume() +{ + return soundVolume; +} + +void soundSetEnable(int channels) +{ + soundEnableFlag = channels; + apply_muting(); +} + +int soundGetEnable() +{ + return (soundEnableFlag & 0x30f); +} + +void soundReset() +{ + soundDriver->reset(); + + remake_stereo_buffer(); + reset_apu(); + + soundPaused = true; + SOUND_CLOCK_TICKS = SOUND_CLOCK_TICKS_; + soundTicks = SOUND_CLOCK_TICKS_; + + soundEvent( NR52, (u8) 0x80 ); +} + +bool soundInit() +{ + soundDriver = systemSoundInit(); + if ( !soundDriver ) + return false; + + if (!soundDriver->init(soundSampleRate)) + return false; + + soundPaused = true; + return true; +} + +void soundSetThrottle(unsigned short throttle) +{ + if(!soundDriver) + return; + soundDriver->setThrottle(throttle); +} + +long soundGetSampleRate() +{ + return soundSampleRate; +} + +void soundSetSampleRate(long sampleRate) +{ + if ( soundSampleRate != sampleRate ) + { + if ( systemCanChangeSoundQuality() ) + { + soundShutdown(); + soundSampleRate = sampleRate; + soundInit(); + } + else + { + soundSampleRate = sampleRate; + } + + remake_stereo_buffer(); + } +} + +static int dummy_state [16]; + +#define SKIP( type, name ) { dummy_state, sizeof (type) } + +#define LOAD( type, name ) { &name, sizeof (type) } + +static struct { + gb_apu_state_t apu; + + // old state + u8 soundDSAValue; + int soundDSBValue; +} state; + +// Old GBA sound state format +static variable_desc old_gba_state [] = +{ + SKIP( int, soundPaused ), + SKIP( int, soundPlay ), + SKIP( int, soundTicks ), + SKIP( int, SOUND_CLOCK_TICKS ), + SKIP( int, soundLevel1 ), + SKIP( int, soundLevel2 ), + SKIP( int, soundBalance ), + SKIP( int, soundMasterOn ), + SKIP( int, soundIndex ), + SKIP( int, sound1On ), + SKIP( int, sound1ATL ), + SKIP( int, sound1Skip ), + SKIP( int, sound1Index ), + SKIP( int, sound1Continue ), + SKIP( int, sound1EnvelopeVolume ), + SKIP( int, sound1EnvelopeATL ), + SKIP( int, sound1EnvelopeATLReload ), + SKIP( int, sound1EnvelopeUpDown ), + SKIP( int, sound1SweepATL ), + SKIP( int, sound1SweepATLReload ), + SKIP( int, sound1SweepSteps ), + SKIP( int, sound1SweepUpDown ), + SKIP( int, sound1SweepStep ), + SKIP( int, sound2On ), + SKIP( int, sound2ATL ), + SKIP( int, sound2Skip ), + SKIP( int, sound2Index ), + SKIP( int, sound2Continue ), + SKIP( int, sound2EnvelopeVolume ), + SKIP( int, sound2EnvelopeATL ), + SKIP( int, sound2EnvelopeATLReload ), + SKIP( int, sound2EnvelopeUpDown ), + SKIP( int, sound3On ), + SKIP( int, sound3ATL ), + SKIP( int, sound3Skip ), + SKIP( int, sound3Index ), + SKIP( int, sound3Continue ), + SKIP( int, sound3OutputLevel ), + SKIP( int, sound4On ), + SKIP( int, sound4ATL ), + SKIP( int, sound4Skip ), + SKIP( int, sound4Index ), + SKIP( int, sound4Clock ), + SKIP( int, sound4ShiftRight ), + SKIP( int, sound4ShiftSkip ), + SKIP( int, sound4ShiftIndex ), + SKIP( int, sound4NSteps ), + SKIP( int, sound4CountDown ), + SKIP( int, sound4Continue ), + SKIP( int, sound4EnvelopeVolume ), + SKIP( int, sound4EnvelopeATL ), + SKIP( int, sound4EnvelopeATLReload ), + SKIP( int, sound4EnvelopeUpDown ), + LOAD( int, soundEnableFlag ), + SKIP( int, soundControl ), + LOAD( int, pcm [0].readIndex ), + LOAD( int, pcm [0].count ), + LOAD( int, pcm [0].writeIndex ), + SKIP( u8, soundDSAEnabled ), // was bool, which was one byte on MS compiler + SKIP( int, soundDSATimer ), + LOAD( u8 [32], pcm [0].fifo ), + LOAD( u8, state.soundDSAValue ), + LOAD( int, pcm [1].readIndex ), + LOAD( int, pcm [1].count ), + LOAD( int, pcm [1].writeIndex ), + SKIP( int, soundDSBEnabled ), + SKIP( int, soundDSBTimer ), + LOAD( u8 [32], pcm [1].fifo ), + LOAD( int, state.soundDSBValue ), + + // skipped manually + //LOAD( int, soundBuffer[0][0], 6*735 }, + //LOAD( int, soundFinalWave[0], 2*735 }, + { NULL, 0 } +}; + +variable_desc old_gba_state2 [] = +{ + LOAD( u8 [0x20], state.apu.regs [0x20] ), + SKIP( int, sound3Bank ), + SKIP( int, sound3DataSize ), + SKIP( int, sound3ForcedOutput ), + { NULL, 0 } +}; + +// New state format +static variable_desc gba_state [] = +{ + // PCM + LOAD( int, pcm [0].readIndex ), + LOAD( int, pcm [0].count ), + LOAD( int, pcm [0].writeIndex ), + LOAD(u8[32],pcm[0].fifo ), + LOAD( int, pcm [0].dac ), + + SKIP( int [4], room_for_expansion ), + + LOAD( int, pcm [1].readIndex ), + LOAD( int, pcm [1].count ), + LOAD( int, pcm [1].writeIndex ), + LOAD(u8[32],pcm[1].fifo ), + LOAD( int, pcm [1].dac ), + + SKIP( int [4], room_for_expansion ), + + // APU + LOAD( u8 [0x40], state.apu.regs ), // last values written to registers and wave RAM (both banks) + LOAD( int, state.apu.frame_time ), // clocks until next frame sequencer action + LOAD( int, state.apu.frame_phase ), // next step frame sequencer will run + + LOAD( int, state.apu.sweep_freq ), // sweep's internal frequency register + LOAD( int, state.apu.sweep_delay ), // clocks until next sweep action + LOAD( int, state.apu.sweep_enabled ), + LOAD( int, state.apu.sweep_neg ), // obscure internal flag + LOAD( int, state.apu.noise_divider ), + LOAD( int, state.apu.wave_buf ), // last read byte of wave RAM + + LOAD( int [4], state.apu.delay ), // clocks until next channel action + LOAD( int [4], state.apu.length_ctr ), + LOAD( int [4], state.apu.phase ), // square/wave phase, noise LFSR + LOAD( int [4], state.apu.enabled ), // internal enabled flag + + LOAD( int [3], state.apu.env_delay ), // clocks until next envelope action + LOAD( int [3], state.apu.env_volume ), + LOAD( int [3], state.apu.env_enabled ), + + SKIP( int [13], room_for_expansion ), + + // Emulator + LOAD( int, soundEnableFlag ), + + SKIP( int [15], room_for_expansion ), + + { NULL, 0 } +}; + +// Reads and discards count bytes from in +static void skip_read( gzFile in, int count ) +{ + char buf [512]; + + while ( count ) + { + int n = sizeof buf; + if ( n > count ) + n = count; + + count -= n; + utilGzRead( in, buf, n ); + } +} + +#ifdef __LIBRETRO__ +void soundSaveGame( u8 *&out ) +#else +void soundSaveGame( gzFile out ) +#endif +{ + gb_apu->save_state( &state.apu ); + + // Be sure areas for expansion get written as zero + memset( dummy_state, 0, sizeof dummy_state ); + +#ifdef __LIBRETRO__ + utilWriteDataMem( out, gba_state ); +#else + utilWriteData( out, gba_state ); +#endif +} + +#ifndef __LIBRETRO__ +static void soundReadGameOld( gzFile in, int version ) +{ + // Read main data + utilReadData( in, old_gba_state ); + skip_read( in, 6*735 + 2*735 ); + + // Copy APU regs + static int const regs_to_copy [] = { + NR10, NR11, NR12, NR13, NR14, + NR21, NR22, NR23, NR24, + NR30, NR31, NR32, NR33, NR34, + NR41, NR42, NR43, NR44, + NR50, NR51, NR52, -1 + }; + + ioMem [NR52] |= 0x80; // old sound played even when this wasn't set (power on) + + for ( int i = 0; regs_to_copy [i] >= 0; i++ ) + state.apu.regs [gba_to_gb_sound( regs_to_copy [i] ) - 0xFF10] = ioMem [regs_to_copy [i]]; + + // Copy wave RAM to both banks + memcpy( &state.apu.regs [0x20], &ioMem [0x90], 0x10 ); + memcpy( &state.apu.regs [0x30], &ioMem [0x90], 0x10 ); + + // Read both banks of wave RAM if available + if ( version >= SAVE_GAME_VERSION_3 ) + utilReadData( in, old_gba_state2 ); + + // Restore PCM + pcm [0].dac = state.soundDSAValue; + pcm [1].dac = state.soundDSBValue; + + (void) utilReadInt( in ); // ignore quality +} +#endif + +#include + +#ifdef __LIBRETRO__ +void soundReadGame(const u8*& in, int version ) +#else +void soundReadGame( gzFile in, int version ) +#endif +{ + // Prepare APU and default state + reset_apu(); + gb_apu->save_state( &state.apu ); + + if ( version > SAVE_GAME_VERSION_9 ) +#ifdef __LIBRETRO__ + utilReadDataMem( in, gba_state ); +#else + utilReadData( in, gba_state ); + else + soundReadGameOld( in, version ); +#endif + + gb_apu->load_state( state.apu ); + write_SGCNT0_H( READ16LE( &ioMem [SGCNT0_H] ) & 0x770F ); + + apply_muting(); +} diff --git a/src/gba/Sound.h b/src/gba/Sound.h new file mode 100644 index 0000000..47750a7 --- /dev/null +++ b/src/gba/Sound.h @@ -0,0 +1,89 @@ +#ifndef SOUND_H +#define SOUND_H + +// Sound emulation setup/options and GBA sound emulation + +#include "../System.h" + +//// Setup/options (these affect GBA and GB sound) + +// Initializes sound and returns true if successful. Sets sound quality to +// current value in soundQuality global. +bool soundInit(); + +// sets the Sound throttle +void soundSetThrottle(unsigned short throttle); + +// Manages sound volume, where 1.0 is normal +void soundSetVolume( float ); +float soundGetVolume(); + +// Manages muting bitmask. The bits control the following channels: +// 0x001 Pulse 1 +// 0x002 Pulse 2 +// 0x004 Wave +// 0x008 Noise +// 0x100 PCM 1 +// 0x200 PCM 2 +void soundSetEnable( int mask ); +int soundGetEnable(); + +// Pauses/resumes system sound output +void soundPause(); +void soundResume(); +extern bool soundPaused; // current paused state + +// Cleans up sound. Afterwards, soundInit() can be called again. +void soundShutdown(); + +//// GBA sound options + +long soundGetSampleRate(); +void soundSetSampleRate(long sampleRate); + +// Sound settings +extern bool soundInterpolation; // 1 if PCM should have low-pass filtering +extern float soundFiltering; // 0.0 = none, 1.0 = max + + +//// GBA sound emulation + +// GBA sound registers +#define SGCNT0_H 0x82 +#define FIFOA_L 0xa0 +#define FIFOA_H 0xa2 +#define FIFOB_L 0xa4 +#define FIFOB_H 0xa6 + +// Resets emulated sound hardware +void soundReset(); + +// Emulates write to sound hardware +void soundEvent( u32 addr, u8 data ); +void soundEvent( u32 addr, u16 data ); // TODO: error-prone to overload like this + +// Notifies emulator that a timer has overflowed +void soundTimerOverflow( int which ); + +// Notifies emulator that PCM rate may have changed +void interp_rate(); + +// Notifies emulator that SOUND_CLOCK_TICKS clocks have passed +void psoundTickfn(); +extern int SOUND_CLOCK_TICKS; // Number of 16.8 MHz clocks between calls to soundTick() +extern int soundTicks; // Number of 16.8 MHz clocks until soundTick() will be called + +// Saves/loads emulator state +#ifdef __LIBRETRO__ +void soundSaveGame( u8 *& ); +void soundReadGame(const u8*& in, int version ); +#else +void soundSaveGame( gzFile ); +void soundReadGame( gzFile, int version ); +#endif + +class Multi_Buffer; + +void flush_samples(Multi_Buffer * buffer); + +#endif // SOUND_H diff --git a/src/gba/Sram.cpp b/src/gba/Sram.cpp new file mode 100644 index 0000000..7f1d7d2 --- /dev/null +++ b/src/gba/Sram.cpp @@ -0,0 +1,21 @@ +#include "GBA.h" +#include "Globals.h" +#include "Flash.h" +#include "Sram.h" + +u8 sramRead(u32 address) +{ + return flashSaveMemory[address & 0xFFFF]; +} +void sramDelayedWrite(u32 address, u8 byte) +{ + saveType = 1; + cpuSaveGameFunc = sramWrite; + sramWrite(address, byte); +} + +void sramWrite(u32 address, u8 byte) +{ + flashSaveMemory[address & 0xFFFF] = byte; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; +} diff --git a/src/gba/Sram.h b/src/gba/Sram.h new file mode 100644 index 0000000..fdc9aa0 --- /dev/null +++ b/src/gba/Sram.h @@ -0,0 +1,8 @@ +#ifndef SRAM_H +#define SRAM_H + +u8 sramRead(u32 address); +void sramWrite(u32 address, u8 byte); +void sramDelayedWrite(u32 address, u8 byte); + +#endif // SRAM_H diff --git a/src/gba/agbprint.cpp b/src/gba/agbprint.cpp new file mode 100644 index 0000000..290eef7 --- /dev/null +++ b/src/gba/agbprint.cpp @@ -0,0 +1,80 @@ +#include +#include + +#include "GBA.h" +#include "Globals.h" +#include "../common/Port.h" +#include "../System.h" + +#define debuggerWriteHalfWord(addr, value) \ + WRITE16LE((u16*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask], (value)) + +#define debuggerReadHalfWord(addr) \ + READ16LE(((u16*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])) + +static bool agbPrintEnabled = false; +static bool agbPrintProtect = false; + +bool agbPrintWrite(u32 address, u16 value) +{ + if(agbPrintEnabled) { + if(address == 0x9fe2ffe) { // protect + agbPrintProtect = (value != 0); + debuggerWriteHalfWord(address, value); + return true; + } else { + if(agbPrintProtect && + ((address >= 0x9fe20f8 && address <= 0x9fe20ff) // control structure + || (address >= 0x8fd0000 && address <= 0x8fdffff) + || (address >= 0x9fd0000 && address <= 0x9fdffff))) { + debuggerWriteHalfWord(address, value); + return true; + } + } + } + return false; +} + +void agbPrintReset() +{ + agbPrintProtect = false; +} + +void agbPrintEnable(bool enable) +{ + agbPrintEnabled = enable; +} + +bool agbPrintIsEnabled() +{ + return agbPrintEnabled; +} + +void agbPrintFlush() +{ + u16 get = debuggerReadHalfWord(0x9fe20fc); + u16 put = debuggerReadHalfWord(0x9fe20fe); + + u32 address = (debuggerReadHalfWord(0x9fe20fa) << 16); + if(address != 0xfd0000 && address != 0x1fd0000) { + dbgOutput("Did you forget to call AGBPrintInit?\n", 0); + // get rid of the text otherwise we will continue to be called + debuggerWriteHalfWord(0x9fe20fc, put); + return; + } + + u8 *data = &rom[address]; + + while(get != put) { + char c = data[get++]; + char s[2]; + s[0] = c; + s[1] = 0; + + if(systemVerbose & VERBOSE_AGBPRINT) + dbgOutput(s, 0); + if(c == '\n') + break; + } + debuggerWriteHalfWord(0x9fe20fc, get); +} diff --git a/src/gba/agbprint.h b/src/gba/agbprint.h new file mode 100644 index 0000000..43c323f --- /dev/null +++ b/src/gba/agbprint.h @@ -0,0 +1,10 @@ +#ifndef AGBPRINT_H +#define AGBPRINT_H + +void agbPrintEnable(bool enable); +bool agbPrintIsEnabled(); +void agbPrintReset(); +bool agbPrintWrite(u32 address, u16 value); +void agbPrintFlush(); + +#endif // AGBPRINT_H diff --git a/src/gba/armdis.cpp b/src/gba/armdis.cpp new file mode 100644 index 0000000..a40ed0e --- /dev/null +++ b/src/gba/armdis.cpp @@ -0,0 +1,724 @@ +/************************************************************************/ +/* Arm/Thumb command set disassembler */ +/************************************************************************/ +#include + +#include "../System.h" +#include "../common/Port.h" +#include "GBA.h" +#include "armdis.h" +#include "elf.h" + +struct Opcodes { + u32 mask; + u32 cval; + const char *mnemonic; +}; + +#define debuggerReadMemory(addr) \ + READ32LE(((u32*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])) + +#define debuggerReadHalfWord(addr) \ + READ16LE(((u16*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])) + +#define debuggerReadByte(addr) \ + map[(addr)>>24].address[(addr) & map[(addr)>>24].mask] + +const char hdig[] = "0123456789abcdef"; + +const char *decVals[16] = { + "0","1","2","3","4","5","6","7","8", + "9","10","11","12","13","14","15" +}; + +const char *regs[16] = { + "r0","r1","r2","r3","r4","r5","r6","r7", + "r8","r9","r10","r11","r12","sp","lr","pc" +}; + +const char *conditions[16] = { + "eq","ne","cs","cc","mi","pl","vs","vc", + "hi","ls","ge","lt","gt","le","","nv" +}; + +const char *shifts[5] = { + "lsl","lsr","asr","ror","rrx" +}; + +const char *armMultLoadStore[12] = { + // non-stack + "da","ia","db","ib", + // stack store + "ed","ea","fd","fa", + // stack load + "fa","fd","ea","ed" +}; + +const Opcodes thumbOpcodes[] = { + // Format 1 + {0xf800, 0x0000, "lsl %r0, %r3, %o"}, + {0xf800, 0x0800, "lsr %r0, %r3, %o"}, + {0xf800, 0x1000, "asr %r0, %r3, %o"}, + // Format 2 + {0xfe00, 0x1800, "add %r0, %r3, %r6"}, + {0xfe00, 0x1a00, "sub %r0, %r3, %r6"}, + {0xfe00, 0x1c00, "add %r0, %r3, %i"}, + {0xfe00, 0x1e00, "sub %r0, %r3, %i"}, + // Format 3 + {0xf800, 0x2000, "mov %r8, %O"}, + {0xf800, 0x2800, "cmp %r8, %O"}, + {0xf800, 0x3000, "add %r8, %O"}, + {0xf800, 0x3800, "sub %r8, %O"}, + // Format 4 + {0xffc0, 0x4000, "and %r0, %r3"}, + {0xffc0, 0x4040, "eor %r0, %r3"}, + {0xffc0, 0x4080, "lsl %r0, %r3"}, + {0xffc0, 0x40c0, "lsr %r0, %r3"}, + {0xffc0, 0x4100, "asr %r0, %r3"}, + {0xffc0, 0x4140, "adc %r0, %r3"}, + {0xffc0, 0x4180, "sbc %r0, %r3"}, + {0xffc0, 0x41c0, "ror %r0, %r3"}, + {0xffc0, 0x4200, "tst %r0, %r3"}, + {0xffc0, 0x4240, "neg %r0, %r3"}, + {0xffc0, 0x4280, "cmp %r0, %r3"}, + {0xffc0, 0x42c0, "cmn %r0, %r3"}, + {0xffc0, 0x4300, "orr %r0, %r3"}, + {0xffc0, 0x4340, "mul %r0, %r3"}, + {0xffc0, 0x4380, "bic %r0, %r3"}, + {0xffc0, 0x43c0, "mvn %r0, %r3"}, + // Format 5 + {0xff80, 0x4700, "bx %h36"}, + {0xfcc0, 0x4400, "[ ??? ]"}, + {0xff00, 0x4400, "add %h07, %h36"}, + {0xff00, 0x4500, "cmp %h07, %h36"}, + {0xff00, 0x4600, "mov %h07, %h36"}, + // Format 6 + {0xf800, 0x4800, "ldr %r8, [%I] (=%J)"}, + // Format 7 + {0xfa00, 0x5000, "str%b %r0, [%r3, %r6]"}, + {0xfa00, 0x5800, "ldr%b %r0, [%r3, %r6]"}, + // Format 8 + {0xfe00, 0x5200, "strh %r0, [%r3, %r6]"}, + {0xfe00, 0x5600, "ldsb %r0, [%r3, %r6]"}, + {0xfe00, 0x5a00, "ldrh %r0, [%r3, %r6]"}, + {0xfe00, 0x5e00, "ldsh %r0, [%r3, %r6]"}, + // Format 9 + {0xe800, 0x6000, "str%B %r0, [%r3, %p]"}, + {0xe800, 0x6800, "ldr%B %r0, [%r3, %p]"}, + // Format 10 + {0xf800, 0x8000, "strh %r0, [%r3, %e]"}, + {0xf800, 0x8800, "ldrh %r0, [%r3, %e]"}, + // Format 11 + {0xf800, 0x9000, "str %r8, [sp, %w]"}, + {0xf800, 0x9800, "ldr %r8, [sp, %w]"}, + // Format 12 + {0xf800, 0xa000, "add %r8, pc, %w (=%K)"}, + {0xf800, 0xa800, "add %r8, sp, %w"}, + // Format 13 + {0xff00, 0xb000, "add sp, %s"}, + // Format 14 + {0xffff, 0xb500, "push {lr}"}, + {0xff00, 0xb400, "push {%l}"}, + {0xff00, 0xb500, "push {%l,lr}"}, + {0xffff, 0xbd00, "pop {pc}"}, + {0xff00, 0xbd00, "pop {%l,pc}"}, + {0xff00, 0xbc00, "pop {%l}"}, + // Format 15 + {0xf800, 0xc000, "stmia %r8!, {%l}"}, + {0xf800, 0xc800, "ldmia %r8!, {%l}"}, + // Format 17 + {0xff00, 0xdf00, "swi %m"}, + // Format 16 + {0xf000, 0xd000, "b%c %W"}, + // Format 18 + {0xf800, 0xe000, "b %a"}, + // Format 19 + {0xf800, 0xf000, "bl %A"}, + {0xf800, 0xf800, "blh %Z"}, + {0xff00, 0xbe00, "bkpt %O"}, + // Unknown + {0x0000, 0x0000, "[ ??? ]"} +}; + +const Opcodes armOpcodes[] = { + // Undefined + {0x0e000010, 0x06000010, "[ undefined ]"}, + // Branch instructions + {0x0ff000f0, 0x01200010, "bx%c %r0"}, + {0x0f000000, 0x0a000000, "b%c %o"}, + {0x0f000000, 0x0b000000, "bl%c %o"}, + {0x0f000000, 0x0f000000, "swi%c %q"}, + // PSR transfer + {0x0fbf0fff, 0x010f0000, "mrs%c %r3, %p"}, + {0x0db0f000, 0x0120f000, "msr%c %p, %i"}, + // Multiply instructions + {0x0fe000f0, 0x00000090, "mul%c%s %r4, %r0, %r2"}, + {0x0fe000f0, 0x00200090, "mla%c%s %r4, %r0, %r2, %r3"}, + {0x0fa000f0, 0x00800090, "%umull%c%s %r3, %r4, %r0, %r2"}, + {0x0fa000f0, 0x00a00090, "%umlal%c%s %r3, %r4, %r0, %r2"}, + // Load/Store instructions + {0x0fb00ff0, 0x01000090, "swp%c%b %r3, %r0, [%r4]"}, + {0x0fb000f0, 0x01000090, "[ ??? ]"}, + {0x0c100000, 0x04000000, "str%c%b%t %r3, %a"}, + {0x0c100000, 0x04100000, "ldr%c%b%t %r3, %a"}, + {0x0e100090, 0x00000090, "str%c%h %r3, %a"}, + {0x0e100090, 0x00100090, "ldr%c%h %r3, %a"}, + {0x0e100000, 0x08000000, "stm%c%m %r4%l"}, + {0x0e100000, 0x08100000, "ldm%c%m %r4%l"}, + // Data processing + {0x0de00000, 0x00000000, "and%c%s %r3, %r4, %i"}, + {0x0de00000, 0x00200000, "eor%c%s %r3, %r4, %i"}, + {0x0de00000, 0x00400000, "sub%c%s %r3, %r4, %i"}, + {0x0de00000, 0x00600000, "rsb%c%s %r3, %r4, %i"}, + {0x0de00000, 0x00800000, "add%c%s %r3, %r4, %i"}, + {0x0de00000, 0x00a00000, "adc%c%s %r3, %r4, %i"}, + {0x0de00000, 0x00c00000, "sbc%c%s %r3, %r4, %i"}, + {0x0de00000, 0x00e00000, "rsc%c%s %r3, %r4, %i"}, + {0x0de00000, 0x01000000, "tst%c%s %r4, %i"}, + {0x0de00000, 0x01200000, "teq%c%s %r4, %i"}, + {0x0de00000, 0x01400000, "cmp%c%s %r4, %i"}, + {0x0de00000, 0x01600000, "cmn%c%s %r4, %i"}, + {0x0de00000, 0x01800000, "orr%c%s %r3, %r4, %i"}, + {0x0de00000, 0x01a00000, "mov%c%s %r3, %i"}, + {0x0de00000, 0x01c00000, "bic%c%s %r3, %r4, %i"}, + {0x0de00000, 0x01e00000, "mvn%c%s %r3, %i"}, + // Coprocessor operations + {0x0f000010, 0x0e000000, "cdp%c %P, %N, %r3, %R4, %R0%V"}, + {0x0e100000, 0x0c000000, "stc%c%L %P, %r3, %A"}, + {0x0f100010, 0x0e000010, "mcr%c %P, %N, %r3, %R4, %R0%V"}, + {0x0f100010, 0x0e100010, "mrc%c %P, %N, %r3, %R4, %R0%V"}, + // Unknown + {0x00000000, 0x00000000, "[ ??? ]"} +}; + +char* addStr(char *dest, const char *src){ + while (*src){ + *dest++ = *src++; + } + return dest; +} + +char* addHex(char *dest, int siz, u32 val){ + if (siz==0){ + siz = 28; + while ( (((val>>siz)&15)==0) && (siz>=4) ) + siz -= 4; + siz += 4; + } + while (siz>0){ + siz -= 4; + *dest++ = hdig[(val>>siz)&15]; + } + return dest; +} + +int disArm(u32 offset, char *dest, int flags){ + u32 opcode = debuggerReadMemory(offset); + + const Opcodes *sp = armOpcodes; + while( sp->cval != (opcode & sp->mask) ) + sp++; + + if (flags&DIS_VIEW_ADDRESS){ + dest = addHex(dest, 32, offset); + *dest++ = ' '; + } + if (flags&DIS_VIEW_CODE){ + dest = addHex(dest, 32, opcode); + *dest++ = ' '; + } + + const char *src = sp->mnemonic; + while (*src){ + if (*src!='%') + *dest++ = *src++; + else{ + src++; + switch (*src){ + case 'c': + dest = addStr(dest, conditions[opcode>>28]); + break; + case 'r': + dest = addStr(dest, regs[(opcode>>((*(++src)-'0')*4))&15]); + break; + case 'o': + { + *dest++ = '$'; + int off = opcode&0xffffff; + if (off&0x800000) + off |= 0xff000000; + off <<= 2; + dest = addHex(dest, 32, offset+8+off); + } + break; + case 'i': + if (opcode&(1<<25)){ + dest = addStr(dest, "#0x"); + int imm = opcode&0xff; + int rot = (opcode&0xf00)>>7; + int val = (imm<<(32-rot))|(imm>>rot); + dest = addHex(dest, 0, val); + } else{ + dest = addStr(dest, regs[opcode&0x0f]); + int shi = (opcode>>5)&3; + int sdw = (opcode>>7)&0x1f; + if ((sdw==0)&&(shi==3)) + shi = 4; + if ( (sdw) || (opcode&0x10) || (shi)) { + dest = addStr(dest, ", "); + dest = addStr(dest, shifts[shi]); + if (opcode&0x10){ + *dest++ = ' '; + dest = addStr(dest, regs[(opcode>>8)&15]); + } else { + if (sdw==0 && ( (shi==1) || (shi==2) )) + sdw = 32; + if(shi != 4) { + dest = addStr(dest, " #0x"); + dest = addHex(dest, 8, sdw); + } + } + } + } + break; + case 'p': + if (opcode&(1<<22)) + dest = addStr(dest, "spsr"); + else + dest = addStr(dest, "cpsr"); + if(opcode & 0x00F00000) { + *dest++ = '_'; + if(opcode & 0x00080000) + *dest++ = 'f'; + if(opcode & 0x00040000) + *dest++ = 's'; + if(opcode & 0x00020000) + *dest++ = 'x'; + if(opcode & 0x00010000) + *dest++ = 'c'; + } + break; + case 's': + if (opcode&(1<<20)) + *dest++ = 's'; + break; + case 'S': + if (opcode&(1<<22)) + *dest++ = 's'; + break; + case 'u': + if (opcode&(1<<22)) + *dest++ = 's'; + else + *dest++ = 'u'; + break; + case 'b': + if (opcode&(1<<22)) + *dest++ = 'b'; + break; + case 'a': + if ((opcode&0x076f0000)==0x004f0000){ + *dest++ = '['; + *dest++ = '$'; + int adr = offset+8; + int add = (opcode&15)|((opcode>>8)&0xf0); + if (opcode&(1<<23)) + adr += add; + else + adr -= add; + dest = addHex(dest, 32, adr); + *dest++ = ']'; + dest = addStr(dest, " (="); + *dest++ = '$'; + dest = addHex(dest ,32, debuggerReadMemory(adr)); + *dest++=')'; + } + if ((opcode&0x072f0000)==0x050f0000){ + *dest++ = '['; + *dest++ = '$'; + int adr = offset+8; + if (opcode&(1<<23)) + adr += opcode&0xfff; + else + adr -= opcode&0xfff; + dest = addHex(dest, 32, adr); + *dest++ = ']'; + dest = addStr(dest, " (="); + *dest++ = '$'; + dest = addHex(dest ,32, debuggerReadMemory(adr)); + *dest++=')'; + } else { + int reg = (opcode>>16)&15; + *dest++ = '['; + dest = addStr(dest, regs[reg]); + if (!(opcode&(1<<24))) + *dest++ = ']'; + if ( ((opcode&(1<<25))&&(opcode&(1<<26))) || (!(opcode&(1<<22))&&!(opcode&(1<<26))) ){ + dest = addStr(dest, ", "); + if (!(opcode&(1<<23))) + *dest++ = '-'; + dest = addStr(dest, regs[opcode&0x0f]); + int shi = (opcode>>5)&3; + if (opcode&(1<<26)){ + if ( ((opcode>>7)&0x1f) || (opcode&0x10) || (shi==1) || (shi==2)){ + dest = addStr(dest, ", "); + dest = addStr(dest, shifts[shi]); + if (opcode&0x10){ + *dest++ = ' '; + dest = addStr(dest, regs[(opcode>>8)&15]); + } else { + int sdw = (opcode>>7)&0x1f; + if (sdw==0 && ( (shi==1) || (shi==2) )) + sdw = 32; + dest = addStr(dest, " #0x"); + dest = addHex(dest, 8, sdw); + } + } + } + } else { + int off; + if (opcode&(1<<26)) + off = opcode&0xfff; + else + off = (opcode&15)|((opcode>>4)&0xf0); + if (off){ + dest = addStr(dest, ", "); + if (!(opcode&(1<<23))) + *dest++ = '-'; + dest = addStr(dest, "#0x"); + dest = addHex(dest, 0, off); + } + } + if (opcode&(1<<24)){ + *dest++ = ']'; + if (opcode&(1<<21)) + *dest++ = '!'; + } + } + break; + case 't': + if ((opcode&0x01200000)==0x01200000) + *dest++ = 't'; + break; + case 'h': + if (opcode&(1<<6)) + *dest++ = 's'; + if (opcode&(1<<5)) + *dest++ = 'h'; + else + *dest++ = 'b'; + break; + case 'm': + if (((opcode>>16)&15)==13) { + if(opcode & 0x00100000) + dest = addStr(dest, armMultLoadStore[8+((opcode>>23)&3)]); + else + dest = addStr(dest, armMultLoadStore[4+((opcode>>23)&3)]); + } else + dest = addStr(dest, armMultLoadStore[(opcode>>23)&3]); + break; + case 'l': + if (opcode&(1<<21)) + *dest++ = '!'; + dest = addStr(dest, ", {"); + { + int rlst = opcode&0xffff; + int msk = 0; + int not_first = 0; + while (msk<16){ + if (rlst&(1<>8)&15]); + break; + case 'N': + if (opcode&0x10) + dest = addStr(dest, decVals[(opcode>>21)&7]); + else + dest = addStr(dest, decVals[(opcode>>20)&15]); + break; + case 'R': + { + src++; + int reg = 4*(*src-'0'); + *dest++ = 'c'; + dest = addStr(dest, decVals[(opcode>>reg)&15]); + } + break; + case 'V': + { + int val = (opcode>>5)&7; + if (val){ + dest = addStr(dest, ", "); + dest = addStr(dest, decVals[val]); + } + } + break; + case 'L': + if (opcode&(1<<22)) + *dest++ = 'l'; + break; + case 'A': + if ((opcode&0x012f0000)==0x010f0000){ + int adr = offset+8; + int add = (opcode&0xff)<<2; + if (opcode&(1<<23)) + adr += add; + else + adr -= add; + *dest++ = '$'; + addHex(dest, 32, adr); + } else { + *dest++ = '['; + dest = addStr(dest, regs[(opcode>>16)&15]); + if (!(opcode&(1<<24))) + *dest++ = ']'; + int off = (opcode&0xff)<<2; + if (off){ + dest = addStr(dest, ", "); + if (!(opcode&(1<<23))) + *dest++ = '-'; + dest = addStr(dest, "#0x"); + dest = addHex(dest, 0, off); + } + if (opcode&(1<<24)){ + *dest++ = ']'; + if (opcode&(1<<21)) + *dest++ = '!'; + } + } + break; + } + src++; + } + } + *dest++ = 0; + + return 4; +} + +int disThumb(u32 offset, char *dest, int flags){ + u32 opcode = debuggerReadHalfWord(offset); + + const Opcodes *sp = thumbOpcodes; + int ret = 2; + while( sp->cval != (opcode & sp->mask) ) + sp++; + + if (flags&DIS_VIEW_ADDRESS){ + dest = addHex(dest, 32, offset); + *dest++ = ' '; + } + if (flags&DIS_VIEW_CODE){ + dest = addHex(dest, 16, opcode); + *dest++ = ' '; + } + + const char *src = sp->mnemonic; + while (*src){ + if (*src!='%') + *dest++ = *src++; + else { + src++; + switch (*src){ + case 'r': + src++; + dest = addStr(dest, regs[(opcode>>(*src-'0'))&7]); + break; + case 'o': + dest = addStr(dest, "#0x"); + { + int val = (opcode>>6)&0x1f; + dest = addHex(dest, 8, val); + } + break; + case 'p': + dest = addStr(dest, "#0x"); + { + int val = (opcode>>6)&0x1f; + if (!(opcode&(1<<12))) + val <<= 2; + dest = addHex(dest, 0, val); + } + break; + case 'e': + dest = addStr(dest, "#0x"); + dest = addHex(dest, 0, ((opcode>>6)&0x1f)<<1); + break; + case 'i': + dest = addStr(dest, "#0x"); + dest = addHex(dest, 0, (opcode>>6)&7); + break; + case 'h': + { + src++; + int reg = (opcode>>(*src-'0'))&7; + src++; + if (opcode&(1<<(*src-'0'))) + reg += 8; + dest = addStr(dest, regs[reg]); + } + break; + case 'O': + dest = addStr(dest, "#0x"); + dest = addHex(dest, 0, (opcode&0xff)); + break; + case 'I': + *dest++ = '$'; + dest = addHex(dest, 32, (offset&0xfffffffc)+4+((opcode&0xff)<<2)); + break; + case 'J': + { + u32 value = debuggerReadMemory((offset&0xfffffffc)+4+ + ((opcode & 0xff)<<2)); + *dest++ = '$'; + dest = addHex(dest, 32, value); + const char *s = elfGetAddressSymbol(value); + if(*s) { + *dest++ = ' '; + dest = addStr(dest, s); + } + } + break; + case 'K': + { + u32 value = (offset&0xfffffffc)+4+((opcode & 0xff)<<2); + *dest++ = '$'; + dest = addHex(dest, 32, value); + const char *s = elfGetAddressSymbol(value); + if(*s) { + *dest++ = ' '; + dest = addStr(dest, s); + } + } + break; + case 'b': + if (opcode&(1<<10)) + *dest++ = 'b'; + break; + case 'B': + if (opcode&(1<<12)) + *dest++ = 'b'; + break; + case 'w': + dest = addStr(dest, "#0x"); + dest = addHex(dest, 0, (opcode&0xff)<<2); + break; + case 'W': + *dest++ = '$'; + { + int add = opcode&0xff; + if (add&0x80) + add |= 0xffffff00; + dest = addHex(dest, 32, (offset&0xfffffffe)+4+(add<<1)); + } + break; + case 'c': + dest = addStr(dest, conditions[(opcode>>8)&15]); + break; + case 's': + if (opcode&(1<<7)) + *dest++ = '-'; + dest = addStr(dest, "#0x"); + dest = addHex(dest, 0, (opcode&0x7f)<<2); + break; + case 'l': + { + int rlst = opcode&0xff; + int msk = 0; + int not_first = 0; + while (msk<8){ + if (rlst&(1< +#include +#include + +#include "GBA.h" +#include "bios.h" +#include "GBAinline.h" +#include "Globals.h" + +s16 sineTable[256] = { + (s16)0x0000, (s16)0x0192, (s16)0x0323, (s16)0x04B5, (s16)0x0645, (s16)0x07D5, (s16)0x0964, (s16)0x0AF1, + (s16)0x0C7C, (s16)0x0E05, (s16)0x0F8C, (s16)0x1111, (s16)0x1294, (s16)0x1413, (s16)0x158F, (s16)0x1708, + (s16)0x187D, (s16)0x19EF, (s16)0x1B5D, (s16)0x1CC6, (s16)0x1E2B, (s16)0x1F8B, (s16)0x20E7, (s16)0x223D, + (s16)0x238E, (s16)0x24DA, (s16)0x261F, (s16)0x275F, (s16)0x2899, (s16)0x29CD, (s16)0x2AFA, (s16)0x2C21, + (s16)0x2D41, (s16)0x2E5A, (s16)0x2F6B, (s16)0x3076, (s16)0x3179, (s16)0x3274, (s16)0x3367, (s16)0x3453, + (s16)0x3536, (s16)0x3612, (s16)0x36E5, (s16)0x37AF, (s16)0x3871, (s16)0x392A, (s16)0x39DA, (s16)0x3A82, + (s16)0x3B20, (s16)0x3BB6, (s16)0x3C42, (s16)0x3CC5, (s16)0x3D3E, (s16)0x3DAE, (s16)0x3E14, (s16)0x3E71, + (s16)0x3EC5, (s16)0x3F0E, (s16)0x3F4E, (s16)0x3F84, (s16)0x3FB1, (s16)0x3FD3, (s16)0x3FEC, (s16)0x3FFB, + (s16)0x4000, (s16)0x3FFB, (s16)0x3FEC, (s16)0x3FD3, (s16)0x3FB1, (s16)0x3F84, (s16)0x3F4E, (s16)0x3F0E, + (s16)0x3EC5, (s16)0x3E71, (s16)0x3E14, (s16)0x3DAE, (s16)0x3D3E, (s16)0x3CC5, (s16)0x3C42, (s16)0x3BB6, + (s16)0x3B20, (s16)0x3A82, (s16)0x39DA, (s16)0x392A, (s16)0x3871, (s16)0x37AF, (s16)0x36E5, (s16)0x3612, + (s16)0x3536, (s16)0x3453, (s16)0x3367, (s16)0x3274, (s16)0x3179, (s16)0x3076, (s16)0x2F6B, (s16)0x2E5A, + (s16)0x2D41, (s16)0x2C21, (s16)0x2AFA, (s16)0x29CD, (s16)0x2899, (s16)0x275F, (s16)0x261F, (s16)0x24DA, + (s16)0x238E, (s16)0x223D, (s16)0x20E7, (s16)0x1F8B, (s16)0x1E2B, (s16)0x1CC6, (s16)0x1B5D, (s16)0x19EF, + (s16)0x187D, (s16)0x1708, (s16)0x158F, (s16)0x1413, (s16)0x1294, (s16)0x1111, (s16)0x0F8C, (s16)0x0E05, + (s16)0x0C7C, (s16)0x0AF1, (s16)0x0964, (s16)0x07D5, (s16)0x0645, (s16)0x04B5, (s16)0x0323, (s16)0x0192, + (s16)0x0000, (s16)0xFE6E, (s16)0xFCDD, (s16)0xFB4B, (s16)0xF9BB, (s16)0xF82B, (s16)0xF69C, (s16)0xF50F, + (s16)0xF384, (s16)0xF1FB, (s16)0xF074, (s16)0xEEEF, (s16)0xED6C, (s16)0xEBED, (s16)0xEA71, (s16)0xE8F8, + (s16)0xE783, (s16)0xE611, (s16)0xE4A3, (s16)0xE33A, (s16)0xE1D5, (s16)0xE075, (s16)0xDF19, (s16)0xDDC3, + (s16)0xDC72, (s16)0xDB26, (s16)0xD9E1, (s16)0xD8A1, (s16)0xD767, (s16)0xD633, (s16)0xD506, (s16)0xD3DF, + (s16)0xD2BF, (s16)0xD1A6, (s16)0xD095, (s16)0xCF8A, (s16)0xCE87, (s16)0xCD8C, (s16)0xCC99, (s16)0xCBAD, + (s16)0xCACA, (s16)0xC9EE, (s16)0xC91B, (s16)0xC851, (s16)0xC78F, (s16)0xC6D6, (s16)0xC626, (s16)0xC57E, + (s16)0xC4E0, (s16)0xC44A, (s16)0xC3BE, (s16)0xC33B, (s16)0xC2C2, (s16)0xC252, (s16)0xC1EC, (s16)0xC18F, + (s16)0xC13B, (s16)0xC0F2, (s16)0xC0B2, (s16)0xC07C, (s16)0xC04F, (s16)0xC02D, (s16)0xC014, (s16)0xC005, + (s16)0xC000, (s16)0xC005, (s16)0xC014, (s16)0xC02D, (s16)0xC04F, (s16)0xC07C, (s16)0xC0B2, (s16)0xC0F2, + (s16)0xC13B, (s16)0xC18F, (s16)0xC1EC, (s16)0xC252, (s16)0xC2C2, (s16)0xC33B, (s16)0xC3BE, (s16)0xC44A, + (s16)0xC4E0, (s16)0xC57E, (s16)0xC626, (s16)0xC6D6, (s16)0xC78F, (s16)0xC851, (s16)0xC91B, (s16)0xC9EE, + (s16)0xCACA, (s16)0xCBAD, (s16)0xCC99, (s16)0xCD8C, (s16)0xCE87, (s16)0xCF8A, (s16)0xD095, (s16)0xD1A6, + (s16)0xD2BF, (s16)0xD3DF, (s16)0xD506, (s16)0xD633, (s16)0xD767, (s16)0xD8A1, (s16)0xD9E1, (s16)0xDB26, + (s16)0xDC72, (s16)0xDDC3, (s16)0xDF19, (s16)0xE075, (s16)0xE1D5, (s16)0xE33A, (s16)0xE4A3, (s16)0xE611, + (s16)0xE783, (s16)0xE8F8, (s16)0xEA71, (s16)0xEBED, (s16)0xED6C, (s16)0xEEEF, (s16)0xF074, (s16)0xF1FB, + (s16)0xF384, (s16)0xF50F, (s16)0xF69C, (s16)0xF82B, (s16)0xF9BB, (s16)0xFB4B, (s16)0xFCDD, (s16)0xFE6E +}; + +void BIOS_ArcTan() +{ +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("ArcTan: %08x (VCOUNT=%2d)\n", + reg[0].I, + VCOUNT); + } +#endif + + s32 a = -(((s32)(reg[0].I*reg[0].I)) >> 14); + s32 b = ((0xA9 * a) >> 14) + 0x390; + b = ((b * a) >> 14) + 0x91C; + b = ((b * a) >> 14) + 0xFB6; + b = ((b * a) >> 14) + 0x16AA; + b = ((b * a) >> 14) + 0x2081; + b = ((b * a) >> 14) + 0x3651; + b = ((b * a) >> 14) + 0xA2F9; + a = ((s32)reg[0].I * b) >> 16; + reg[0].I = a; + +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("ArcTan: return=%08x\n", + reg[0].I); + } +#endif +} + +void BIOS_ArcTan2() +{ +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("ArcTan2: %08x,%08x (VCOUNT=%2d)\n", + reg[0].I, + reg[1].I, + VCOUNT); + } +#endif + + s32 x = reg[0].I; + s32 y = reg[1].I; + u32 res = 0; + if (y == 0) { + res = ((x>>16) & 0x8000); + } else { + if (x == 0) { + res = ((y>>16) & 0x8000) + 0x4000; + } else { + if ((abs(x) > abs(y)) || ((abs(x) == abs(y)) && (!((x<0) && (y<0))))) { + reg[1].I = x; + reg[0].I = y << 14; + BIOS_Div(); + BIOS_ArcTan(); + if (x < 0) + res = 0x8000 + reg[0].I; + else + res = (((y>>16) & 0x8000)<<1) + reg[0].I; + } else { + reg[0].I = x << 14; + BIOS_Div(); + BIOS_ArcTan(); + res = (0x4000 + ((y>>16) & 0x8000)) - reg[0].I; + } + } + } + reg[0].I = res; + +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("ArcTan2: return=%08x\n", + reg[0].I); + } +#endif +} + +void BIOS_BitUnPack() +{ +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("BitUnPack: %08x,%08x,%08x (VCOUNT=%2d)\n", + reg[0].I, + reg[1].I, + reg[2].I, + VCOUNT); + } +#endif + + u32 source = reg[0].I; + u32 dest = reg[1].I; + u32 header = reg[2].I; + + int len = CPUReadHalfWord(header); + // check address + if(((source & 0xe000000) == 0) || + ((source + len) & 0xe000000) == 0) + return; + + int bits = CPUReadByte(header+2); + int revbits = 8 - bits; + // u32 value = 0; + u32 base = CPUReadMemory(header+4); + bool addBase = (base & 0x80000000) ? true : false; + base &= 0x7fffffff; + int dataSize = CPUReadByte(header+3); + + int data = 0; + int bitwritecount = 0; + while(1) { + len -= 1; + if(len < 0) + break; + int mask = 0xff >> revbits; + u8 b = CPUReadByte(source); + source++; + int bitcount = 0; + while(1) { + if(bitcount >= 8) + break; + u32 d = b & mask; + u32 temp = d >> bitcount; + if(d || addBase) { + temp += base; + } + data |= temp << bitwritecount; + bitwritecount += dataSize; + if(bitwritecount >= 32) { + CPUWriteMemory(dest, data); + dest += 4; + data = 0; + bitwritecount = 0; + } + mask <<= bits; + bitcount += bits; + } + } +} + +void BIOS_GetBiosChecksum() +{ + reg[0].I=0xBAAE187F; +} + +void BIOS_BgAffineSet() +{ +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("BgAffineSet: %08x,%08x,%08x (VCOUNT=%2d)\n", + reg[0].I, + reg[1].I, + reg[2].I, + VCOUNT); + } +#endif + + u32 src = reg[0].I; + u32 dest = reg[1].I; + int num = reg[2].I; + + for(int i = 0; i < num; i++) { + s32 cx = CPUReadMemory(src); + src+=4; + s32 cy = CPUReadMemory(src); + src+=4; + s16 dispx = CPUReadHalfWord(src); + src+=2; + s16 dispy = CPUReadHalfWord(src); + src+=2; + s16 rx = CPUReadHalfWord(src); + src+=2; + s16 ry = CPUReadHalfWord(src); + src+=2; + u16 theta = CPUReadHalfWord(src)>>8; + src+=4; // keep structure alignment + s32 a = sineTable[(theta+0x40)&255]; + s32 b = sineTable[theta]; + + s16 dx = (rx * a)>>14; + s16 dmx = (rx * b)>>14; + s16 dy = (ry * b)>>14; + s16 dmy = (ry * a)>>14; + + CPUWriteHalfWord(dest, dx); + dest += 2; + CPUWriteHalfWord(dest, -dmx); + dest += 2; + CPUWriteHalfWord(dest, dy); + dest += 2; + CPUWriteHalfWord(dest, dmy); + dest += 2; + + s32 startx = cx - dx * dispx + dmx * dispy; + s32 starty = cy - dy * dispx - dmy * dispy; + + CPUWriteMemory(dest, startx); + dest += 4; + CPUWriteMemory(dest, starty); + dest += 4; + } +} + +void BIOS_CpuSet() +{ +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("CpuSet: 0x%08x,0x%08x,0x%08x (VCOUNT=%d)\n", reg[0].I, reg[1].I, + reg[2].I, VCOUNT); + } +#endif + + u32 source = reg[0].I; + u32 dest = reg[1].I; + u32 cnt = reg[2].I; + + if(((source & 0xe000000) == 0) || + ((source + (((cnt << 11)>>9) & 0x1fffff)) & 0xe000000) == 0) + return; + + int count = cnt & 0x1FFFFF; + + // 32-bit ? + if((cnt >> 26) & 1) { + // needed for 32-bit mode! + source &= 0xFFFFFFFC; + dest &= 0xFFFFFFFC; + // fill ? + if((cnt >> 24) & 1) { + u32 value = (source>0x0EFFFFFF ? 0x1CAD1CAD : CPUReadMemory(source)); + while(count) { + CPUWriteMemory(dest, value); + dest += 4; + count--; + } + } else { + // copy + while(count) { + CPUWriteMemory(dest, (source>0x0EFFFFFF ? 0x1CAD1CAD : CPUReadMemory(source))); + source += 4; + dest += 4; + count--; + } + } + } else { + // 16-bit fill? + if((cnt >> 24) & 1) { + u16 value = (source>0x0EFFFFFF ? 0x1CAD : CPUReadHalfWord(source)); + while(count) { + CPUWriteHalfWord(dest, value); + dest += 2; + count--; + } + } else { + // copy + while(count) { + CPUWriteHalfWord(dest, (source>0x0EFFFFFF ? 0x1CAD : CPUReadHalfWord(source))); + source += 2; + dest += 2; + count--; + } + } + } +} + +void BIOS_CpuFastSet() +{ +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("CpuFastSet: 0x%08x,0x%08x,0x%08x (VCOUNT=%d)\n", reg[0].I, reg[1].I, + reg[2].I, VCOUNT); + } +#endif + + u32 source = reg[0].I; + u32 dest = reg[1].I; + u32 cnt = reg[2].I; + + if(((source & 0xe000000) == 0) || + ((source + (((cnt << 11)>>9) & 0x1fffff)) & 0xe000000) == 0) + return; + + // needed for 32-bit mode! + source &= 0xFFFFFFFC; + dest &= 0xFFFFFFFC; + + int count = cnt & 0x1FFFFF; + + // fill? + if((cnt >> 24) & 1) { + while(count > 0) { + // BIOS always transfers 32 bytes at a time + u32 value = (source>0x0EFFFFFF ? 0xBAFFFFFB : CPUReadMemory(source)); + for(int i = 0; i < 8; i++) { + CPUWriteMemory(dest, value); + dest += 4; + } + count -= 8; + } + } else { + // copy + while(count > 0) { + // BIOS always transfers 32 bytes at a time + for(int i = 0; i < 8; i++) { + CPUWriteMemory(dest, (source>0x0EFFFFFF ? 0xBAFFFFFB :CPUReadMemory(source))); + source += 4; + dest += 4; + } + count -= 8; + } + } +} + +void BIOS_Diff8bitUnFilterWram() +{ +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("Diff8bitUnFilterWram: 0x%08x,0x%08x (VCOUNT=%d)\n", reg[0].I, + reg[1].I, VCOUNT); + } +#endif + + u32 source = reg[0].I; + u32 dest = reg[1].I; + + u32 header = CPUReadMemory(source); + source += 4; + + if(((source & 0xe000000) == 0) || + (((source + ((header >> 8) & 0x1fffff)) & 0xe000000) == 0)) + return; + + int len = header >> 8; + + u8 data = CPUReadByte(source++); + CPUWriteByte(dest++, data); + len--; + + while(len > 0) { + u8 diff = CPUReadByte(source++); + data += diff; + CPUWriteByte(dest++, data); + len--; + } +} + +void BIOS_Diff8bitUnFilterVram() +{ +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("Diff8bitUnFilterVram: 0x%08x,0x%08x (VCOUNT=%d)\n", reg[0].I, + reg[1].I, VCOUNT); + } +#endif + + u32 source = reg[0].I; + u32 dest = reg[1].I; + + u32 header = CPUReadMemory(source); + source += 4; + + if(((source & 0xe000000) == 0) || + ((source + ((header >> 8) & 0x1fffff)) & 0xe000000) == 0) + return; + + int len = header >> 8; + + u8 data = CPUReadByte(source++); + u16 writeData = data; + int shift = 8; + int bytes = 1; + + while(len >= 2) { + u8 diff = CPUReadByte(source++); + data += diff; + writeData |= (data << shift); + bytes++; + shift += 8; + if(bytes == 2) { + CPUWriteHalfWord(dest, writeData); + dest += 2; + len -= 2; + bytes = 0; + writeData = 0; + shift = 0; + } + } +} + +void BIOS_Diff16bitUnFilter() +{ +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("Diff16bitUnFilter: 0x%08x,0x%08x (VCOUNT=%d)\n", reg[0].I, + reg[1].I, VCOUNT); + } +#endif + + u32 source = reg[0].I; + u32 dest = reg[1].I; + + u32 header = CPUReadMemory(source); + source += 4; + + if(((source & 0xe000000) == 0) || + ((source + ((header >> 8) & 0x1fffff)) & 0xe000000) == 0) + return; + + int len = header >> 8; + + u16 data = CPUReadHalfWord(source); + source += 2; + CPUWriteHalfWord(dest, data); + dest += 2; + len -= 2; + + while(len >= 2) { + u16 diff = CPUReadHalfWord(source); + source += 2; + data += diff; + CPUWriteHalfWord(dest, data); + dest += 2; + len -= 2; + } +} + +void BIOS_Div() +{ +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("Div: 0x%08x,0x%08x (VCOUNT=%d)\n", + reg[0].I, + reg[1].I, + VCOUNT); + } +#endif + + int number = reg[0].I; + int denom = reg[1].I; + + if(denom != 0) { + reg[0].I = number / denom; + reg[1].I = number % denom; + s32 temp = (s32)reg[0].I; + reg[3].I = temp < 0 ? (u32)-temp : (u32)temp; + } +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("Div: return=0x%08x,0x%08x,0x%08x\n", + reg[0].I, + reg[1].I, + reg[3].I); + } +#endif +} + +void BIOS_DivARM() +{ +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("DivARM: 0x%08x, (VCOUNT=%d)\n", + reg[0].I, + VCOUNT); + } +#endif + + u32 temp = reg[0].I; + reg[0].I = reg[1].I; + reg[1].I = temp; + BIOS_Div(); +} + +void BIOS_HuffUnComp() +{ +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("HuffUnComp: 0x%08x,0x%08x (VCOUNT=%d)\n", + reg[0].I, + reg[1].I, + VCOUNT); + } +#endif + + u32 source = reg[0].I; + u32 dest = reg[1].I; + + u32 header = CPUReadMemory(source); + source += 4; + + if(((source & 0xe000000) == 0) || + ((source + ((header >> 8) & 0x1fffff)) & 0xe000000) == 0) + return; + + u8 treeSize = CPUReadByte(source++); + + u32 treeStart = source; + + source += ((treeSize+1)<<1)-1; // minus because we already skipped one byte + + int len = header >> 8; + + u32 mask = 0x80000000; + u32 data = CPUReadMemory(source); + source += 4; + + int pos = 0; + u8 rootNode = CPUReadByte(treeStart); + u8 currentNode = rootNode; + bool writeData = false; + int byteShift = 0; + int byteCount = 0; + u32 writeValue = 0; + + if((header & 0x0F) == 8) { + while(len > 0) { + // take left + if(pos == 0) + pos++; + else + pos += (((currentNode & 0x3F)+1)<<1); + + if(data & mask) { + // right + if(currentNode & 0x40) + writeData = true; + currentNode = CPUReadByte(treeStart+pos+1); + } else { + // left + if(currentNode & 0x80) + writeData = true; + currentNode = CPUReadByte(treeStart+pos); + } + + if(writeData) { + writeValue |= (currentNode << byteShift); + byteCount++; + byteShift += 8; + + pos = 0; + currentNode = rootNode; + writeData = false; + + if(byteCount == 4) { + byteCount = 0; + byteShift = 0; + CPUWriteMemory(dest, writeValue); + writeValue = 0; + dest += 4; + len -= 4; + } + } + mask >>= 1; + if(mask == 0) { + mask = 0x80000000; + data = CPUReadMemory(source); + source += 4; + } + } + } else { + int halfLen = 0; + int value = 0; + while(len > 0) { + // take left + if(pos == 0) + pos++; + else + pos += (((currentNode & 0x3F)+1)<<1); + + if((data & mask)) { + // right + if(currentNode & 0x40) + writeData = true; + currentNode = CPUReadByte(treeStart+pos+1); + } else { + // left + if(currentNode & 0x80) + writeData = true; + currentNode = CPUReadByte(treeStart+pos); + } + + if(writeData) { + if(halfLen == 0) + value |= currentNode; + else + value |= (currentNode<<4); + + halfLen += 4; + if(halfLen == 8) { + writeValue |= (value << byteShift); + byteCount++; + byteShift += 8; + + halfLen = 0; + value = 0; + + if(byteCount == 4) { + byteCount = 0; + byteShift = 0; + CPUWriteMemory(dest, writeValue); + dest += 4; + writeValue = 0; + len -= 4; + } + } + pos = 0; + currentNode = rootNode; + writeData = false; + } + mask >>= 1; + if(mask == 0) { + mask = 0x80000000; + data = CPUReadMemory(source); + source += 4; + } + } + } +} + +void BIOS_LZ77UnCompVram() +{ +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("LZ77UnCompVram: 0x%08x,0x%08x (VCOUNT=%d)\n", + reg[0].I, + reg[1].I, + VCOUNT); + } +#endif + + u32 source = reg[0].I; + u32 dest = reg[1].I; + + u32 header = CPUReadMemory(source); + source += 4; + + if(((source & 0xe000000) == 0) || + ((source + ((header >> 8) & 0x1fffff)) & 0xe000000) == 0) + return; + + int byteCount = 0; + int byteShift = 0; + u32 writeValue = 0; + + int len = header >> 8; + + while(len > 0) { + u8 d = CPUReadByte(source++); + + if(d) { + for(int i = 0; i < 8; i++) { + if(d & 0x80) { + u16 data = CPUReadByte(source++) << 8; + data |= CPUReadByte(source++); + int length = (data >> 12) + 3; + int offset = (data & 0x0FFF); + u32 windowOffset = dest + byteCount - offset - 1; + for(int i2 = 0; i2 < length; i2++) { + writeValue |= (CPUReadByte(windowOffset++) << byteShift); + byteShift += 8; + byteCount++; + + if(byteCount == 2) { + CPUWriteHalfWord(dest, writeValue); + dest += 2; + byteCount = 0; + byteShift = 0; + writeValue = 0; + } + len--; + if(len == 0) + return; + } + } else { + writeValue |= (CPUReadByte(source++) << byteShift); + byteShift += 8; + byteCount++; + if(byteCount == 2) { + CPUWriteHalfWord(dest, writeValue); + dest += 2; + byteCount = 0; + byteShift = 0; + writeValue = 0; + } + len--; + if(len == 0) + return; + } + d <<= 1; + } + } else { + for(int i = 0; i < 8; i++) { + writeValue |= (CPUReadByte(source++) << byteShift); + byteShift += 8; + byteCount++; + if(byteCount == 2) { + CPUWriteHalfWord(dest, writeValue); + dest += 2; + byteShift = 0; + byteCount = 0; + writeValue = 0; + } + len--; + if(len == 0) + return; + } + } + } +} + +void BIOS_LZ77UnCompWram() +{ +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("LZ77UnCompWram: 0x%08x,0x%08x (VCOUNT=%d)\n", reg[0].I, reg[1].I, + VCOUNT); + } +#endif + + u32 source = reg[0].I; + u32 dest = reg[1].I; + + u32 header = CPUReadMemory(source); + source += 4; + + if(((source & 0xe000000) == 0) || + ((source + ((header >> 8) & 0x1fffff)) & 0xe000000) == 0) + return; + + int len = header >> 8; + + while(len > 0) { + u8 d = CPUReadByte(source++); + + if(d) { + for(int i = 0; i < 8; i++) { + if(d & 0x80) { + u16 data = CPUReadByte(source++) << 8; + data |= CPUReadByte(source++); + int length = (data >> 12) + 3; + int offset = (data & 0x0FFF); + u32 windowOffset = dest - offset - 1; + for(int i2 = 0; i2 < length; i2++) { + CPUWriteByte(dest++, CPUReadByte(windowOffset++)); + len--; + if(len == 0) + return; + } + } else { + CPUWriteByte(dest++, CPUReadByte(source++)); + len--; + if(len == 0) + return; + } + d <<= 1; + } + } else { + for(int i = 0; i < 8; i++) { + CPUWriteByte(dest++, CPUReadByte(source++)); + len--; + if(len == 0) + return; + } + } + } +} + +void BIOS_ObjAffineSet() +{ +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("ObjAffineSet: 0x%08x,0x%08x,0x%08x,0x%08x (VCOUNT=%d)\n", + reg[0].I, + reg[1].I, + reg[2].I, + reg[3].I, + VCOUNT); + } +#endif + + u32 src = reg[0].I; + u32 dest = reg[1].I; + int num = reg[2].I; + int offset = reg[3].I; + + for(int i = 0; i < num; i++) { + s16 rx = CPUReadHalfWord(src); + src+=2; + s16 ry = CPUReadHalfWord(src); + src+=2; + u16 theta = CPUReadHalfWord(src)>>8; + src+=4; // keep structure alignment + + s32 a = (s32)sineTable[(theta+0x40)&255]; + s32 b = (s32)sineTable[theta]; + + s16 dx = ((s32)rx * a)>>14; + s16 dmx = ((s32)rx * b)>>14; + s16 dy = ((s32)ry * b)>>14; + s16 dmy = ((s32)ry * a)>>14; + + CPUWriteHalfWord(dest, dx); + dest += offset; + CPUWriteHalfWord(dest, -dmx); + dest += offset; + CPUWriteHalfWord(dest, dy); + dest += offset; + CPUWriteHalfWord(dest, dmy); + dest += offset; + } +} + +void BIOS_RegisterRamReset(u32 flags) +{ + // no need to trace here. this is only called directly from GBA.cpp + // to emulate bios initialization + + CPUUpdateRegister(0x0, 0x80); + + if(flags) { + if(flags & 0x01) { + // clear work RAM + memset(workRAM, 0, 0x40000); + } + if(flags & 0x02) { + // clear internal RAM + memset(internalRAM, 0, 0x7e00); // don't clear 0x7e00-0x7fff + } + if(flags & 0x04) { + // clear palette RAM + memset(paletteRAM, 0, 0x400); + } + if(flags & 0x08) { + // clear VRAM + memset(vram, 0, 0x18000); + } + if(flags & 0x10) { + // clean OAM + memset(oam, 0, 0x400); + } + + if(flags & 0x80) { + int i; + for(i = 0; i < 0x10; i++) + CPUUpdateRegister(0x200+i*2, 0); + + for(i = 0; i < 0xF; i++) + CPUUpdateRegister(0x4+i*2, 0); + + for(i = 0; i < 0x20; i++) + CPUUpdateRegister(0x20+i*2, 0); + + for(i = 0; i < 0x18; i++) + CPUUpdateRegister(0xb0+i*2, 0); + + CPUUpdateRegister(0x130, 0); + CPUUpdateRegister(0x20, 0x100); + CPUUpdateRegister(0x30, 0x100); + CPUUpdateRegister(0x26, 0x100); + CPUUpdateRegister(0x36, 0x100); + } + + if(flags & 0x20) { + int i; + for(i = 0; i < 8; i++) + CPUUpdateRegister(0x110+i*2, 0); + CPUUpdateRegister(0x134, 0x8000); + for(i = 0; i < 7; i++) + CPUUpdateRegister(0x140+i*2, 0); + } + + if(flags & 0x40) { + int i; + CPUWriteByte(0x4000084, 0); + CPUWriteByte(0x4000084, 0x80); + CPUWriteMemory(0x4000080, 0x880e0000); + CPUUpdateRegister(0x88, CPUReadHalfWord(0x4000088)&0x3ff); + CPUWriteByte(0x4000070, 0x70); + for(i = 0; i < 8; i++) + CPUUpdateRegister(0x90+i*2, 0); + CPUWriteByte(0x4000070, 0); + for(i = 0; i < 8; i++) + CPUUpdateRegister(0x90+i*2, 0); + CPUWriteByte(0x4000084, 0); + } + } +} + +void BIOS_RegisterRamReset() +{ +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("RegisterRamReset: 0x%08x (VCOUNT=%d)\n", + reg[0].I, + VCOUNT); + } +#endif + + BIOS_RegisterRamReset(reg[0].I); +} + +void BIOS_RLUnCompVram() +{ +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("RLUnCompVram: 0x%08x,0x%08x (VCOUNT=%d)\n", + reg[0].I, + reg[1].I, + VCOUNT); + } +#endif + + u32 source = reg[0].I; + u32 dest = reg[1].I; + + u32 header = CPUReadMemory(source & 0xFFFFFFFC); + source += 4; + + if(((source & 0xe000000) == 0) || + ((source + ((header >> 8) & 0x1fffff)) & 0xe000000) == 0) + return; + + int len = header >> 8; + int byteCount = 0; + int byteShift = 0; + u32 writeValue = 0; + + while(len > 0) { + u8 d = CPUReadByte(source++); + int l = d & 0x7F; + if(d & 0x80) { + u8 data = CPUReadByte(source++); + l += 3; + for(int i = 0;i < l; i++) { + writeValue |= (data << byteShift); + byteShift += 8; + byteCount++; + + if(byteCount == 2) { + CPUWriteHalfWord(dest, writeValue); + dest += 2; + byteCount = 0; + byteShift = 0; + writeValue = 0; + } + len--; + if(len == 0) + return; + } + } else { + l++; + for(int i = 0; i < l; i++) { + writeValue |= (CPUReadByte(source++) << byteShift); + byteShift += 8; + byteCount++; + if(byteCount == 2) { + CPUWriteHalfWord(dest, writeValue); + dest += 2; + byteCount = 0; + byteShift = 0; + writeValue = 0; + } + len--; + if(len == 0) + return; + } + } + } +} + +void BIOS_RLUnCompWram() +{ +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("RLUnCompWram: 0x%08x,0x%08x (VCOUNT=%d)\n", + reg[0].I, + reg[1].I, + VCOUNT); + } +#endif + + u32 source = reg[0].I; + u32 dest = reg[1].I; + + u32 header = CPUReadMemory(source & 0xFFFFFFFC); + source += 4; + + if(((source & 0xe000000) == 0) || + ((source + ((header >> 8) & 0x1fffff)) & 0xe000000) == 0) + return; + + int len = header >> 8; + + while(len > 0) { + u8 d = CPUReadByte(source++); + int l = d & 0x7F; + if(d & 0x80) { + u8 data = CPUReadByte(source++); + l += 3; + for(int i = 0;i < l; i++) { + CPUWriteByte(dest++, data); + len--; + if(len == 0) + return; + } + } else { + l++; + for(int i = 0; i < l; i++) { + CPUWriteByte(dest++, CPUReadByte(source++)); + len--; + if(len == 0) + return; + } + } + } +} + +void BIOS_SoftReset() +{ +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("SoftReset: (VCOUNT=%d)\n", VCOUNT); + } +#endif + + armState = true; + armMode = 0x1F; + armIrqEnable = false; + C_FLAG = V_FLAG = N_FLAG = Z_FLAG = false; + reg[13].I = 0x03007F00; + reg[14].I = 0x00000000; + reg[16].I = 0x00000000; + reg[R13_IRQ].I = 0x03007FA0; + reg[R14_IRQ].I = 0x00000000; + reg[SPSR_IRQ].I = 0x00000000; + reg[R13_SVC].I = 0x03007FE0; + reg[R14_SVC].I = 0x00000000; + reg[SPSR_SVC].I = 0x00000000; + u8 b = internalRAM[0x7ffa]; + + memset(&internalRAM[0x7e00], 0, 0x200); + + if(b) { + armNextPC = 0x02000000; + reg[15].I = 0x02000004; + } else { + armNextPC = 0x08000000; + reg[15].I = 0x08000004; + } +} + +void BIOS_Sqrt() +{ +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("Sqrt: %08x (VCOUNT=%2d)\n", + reg[0].I, + VCOUNT); + } +#endif + reg[0].I = (u32)sqrt((double)reg[0].I); +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("Sqrt: return=%08x\n", + reg[0].I); + } +#endif +} + +void BIOS_MidiKey2Freq() +{ +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("MidiKey2Freq: WaveData=%08x mk=%08x fp=%08x\n", + reg[0].I, + reg[1].I, + reg[2].I); + } +#endif + int freq = CPUReadMemory(reg[0].I+4); + double tmp; + tmp = ((double)(180 - reg[1].I)) - ((double)reg[2].I / 256.f); + tmp = pow((double)2.f, tmp / 12.f); + reg[0].I = (int)((double)freq / tmp); + +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("MidiKey2Freq: return %08x\n", + reg[0].I); + } +#endif +} + +void BIOS_SndDriverJmpTableCopy() +{ +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("SndDriverJmpTableCopy: dest=%08x\n", + reg[0].I); + } +#endif + for(int i = 0; i < 0x24; i++) { + CPUWriteMemory(reg[0].I, 0x9c); + reg[0].I += 4; + } +} diff --git a/src/gba/bios.h b/src/gba/bios.h new file mode 100644 index 0000000..68495cf --- /dev/null +++ b/src/gba/bios.h @@ -0,0 +1,29 @@ +#ifndef BIOS_H +#define BIOS_H + +extern void BIOS_ArcTan(); +extern void BIOS_ArcTan2(); +extern void BIOS_BitUnPack(); +extern void BIOS_GetBiosChecksum(); +extern void BIOS_BgAffineSet(); +extern void BIOS_CpuSet(); +extern void BIOS_CpuFastSet(); +extern void BIOS_Diff8bitUnFilterWram(); +extern void BIOS_Diff8bitUnFilterVram(); +extern void BIOS_Diff16bitUnFilter(); +extern void BIOS_Div(); +extern void BIOS_DivARM(); +extern void BIOS_HuffUnComp(); +extern void BIOS_LZ77UnCompVram(); +extern void BIOS_LZ77UnCompWram(); +extern void BIOS_ObjAffineSet(); +extern void BIOS_RegisterRamReset(); +extern void BIOS_RegisterRamReset(u32); +extern void BIOS_RLUnCompVram(); +extern void BIOS_RLUnCompWram(); +extern void BIOS_SoftReset(); +extern void BIOS_Sqrt(); +extern void BIOS_MidiKey2Freq(); +extern void BIOS_SndDriverJmpTableCopy(); + +#endif // BIOS_H diff --git a/src/gba/elf.cpp b/src/gba/elf.cpp new file mode 100644 index 0000000..2733185 --- /dev/null +++ b/src/gba/elf.cpp @@ -0,0 +1,2983 @@ +#include +#include +#include + +#include "GBA.h" +#include "../common/Port.h" +#include "elf.h" +#include "../NLS.h" + +#define elfReadMemory(addr) \ + READ32LE((&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])) + +#define DW_TAG_array_type 0x01 +#define DW_TAG_enumeration_type 0x04 +#define DW_TAG_formal_parameter 0x05 +#define DW_TAG_label 0x0a +#define DW_TAG_lexical_block 0x0b +#define DW_TAG_member 0x0d +#define DW_TAG_pointer_type 0x0f +#define DW_TAG_reference_type 0x10 +#define DW_TAG_compile_unit 0x11 +#define DW_TAG_structure_type 0x13 +#define DW_TAG_subroutine_type 0x15 +#define DW_TAG_typedef 0x16 +#define DW_TAG_union_type 0x17 +#define DW_TAG_unspecified_parameters 0x18 +#define DW_TAG_inheritance 0x1c +#define DW_TAG_inlined_subroutine 0x1d +#define DW_TAG_subrange_type 0x21 +#define DW_TAG_base_type 0x24 +#define DW_TAG_const_type 0x26 +#define DW_TAG_enumerator 0x28 +#define DW_TAG_subprogram 0x2e +#define DW_TAG_variable 0x34 +#define DW_TAG_volatile_type 0x35 + +#define DW_AT_sibling 0x01 +#define DW_AT_location 0x02 +#define DW_AT_name 0x03 +#define DW_AT_byte_size 0x0b +#define DW_AT_bit_offset 0x0c +#define DW_AT_bit_size 0x0d +#define DW_AT_stmt_list 0x10 +#define DW_AT_low_pc 0x11 +#define DW_AT_high_pc 0x12 +#define DW_AT_language 0x13 +#define DW_AT_compdir 0x1b +#define DW_AT_const_value 0x1c +#define DW_AT_containing_type 0x1d +#define DW_AT_inline 0x20 +#define DW_AT_producer 0x25 +#define DW_AT_prototyped 0x27 +#define DW_AT_upper_bound 0x2f +#define DW_AT_abstract_origin 0x31 +#define DW_AT_accessibility 0x32 +#define DW_AT_artificial 0x34 +#define DW_AT_data_member_location 0x38 +#define DW_AT_decl_file 0x3a +#define DW_AT_decl_line 0x3b +#define DW_AT_declaration 0x3c +#define DW_AT_encoding 0x3e +#define DW_AT_external 0x3f +#define DW_AT_frame_base 0x40 +#define DW_AT_macro_info 0x43 +#define DW_AT_specification 0x47 +#define DW_AT_type 0x49 +#define DW_AT_virtuality 0x4c +#define DW_AT_vtable_elem_location 0x4d +// DWARF 2.1/3.0 extensions +#define DW_AT_entry_pc 0x52 +#define DW_AT_ranges 0x55 +// ARM Compiler extensions +#define DW_AT_proc_body 0x2000 +#define DW_AT_save_offset 0x2001 +#define DW_AT_user_2002 0x2002 +// MIPS extensions +#define DW_AT_MIPS_linkage_name 0x2007 + +#define DW_FORM_addr 0x01 +#define DW_FORM_data2 0x05 +#define DW_FORM_data4 0x06 +#define DW_FORM_string 0x08 +#define DW_FORM_block 0x09 +#define DW_FORM_block1 0x0a +#define DW_FORM_data1 0x0b +#define DW_FORM_flag 0x0c +#define DW_FORM_sdata 0x0d +#define DW_FORM_strp 0x0e +#define DW_FORM_udata 0x0f +#define DW_FORM_ref_addr 0x10 +#define DW_FORM_ref4 0x13 +#define DW_FORM_ref_udata 0x15 +#define DW_FORM_indirect 0x16 + +#define DW_OP_addr 0x03 +#define DW_OP_plus_uconst 0x23 +#define DW_OP_reg0 0x50 +#define DW_OP_reg1 0x51 +#define DW_OP_reg2 0x52 +#define DW_OP_reg3 0x53 +#define DW_OP_reg4 0x54 +#define DW_OP_reg5 0x55 +#define DW_OP_reg6 0x56 +#define DW_OP_reg7 0x57 +#define DW_OP_reg8 0x58 +#define DW_OP_reg9 0x59 +#define DW_OP_reg10 0x5a +#define DW_OP_reg11 0x5b +#define DW_OP_reg12 0x5c +#define DW_OP_reg13 0x5d +#define DW_OP_reg14 0x5e +#define DW_OP_reg15 0x5f +#define DW_OP_fbreg 0x91 + +#define DW_LNS_extended_op 0x00 +#define DW_LNS_copy 0x01 +#define DW_LNS_advance_pc 0x02 +#define DW_LNS_advance_line 0x03 +#define DW_LNS_set_file 0x04 +#define DW_LNS_set_column 0x05 +#define DW_LNS_negate_stmt 0x06 +#define DW_LNS_set_basic_block 0x07 +#define DW_LNS_const_add_pc 0x08 +#define DW_LNS_fixed_advance_pc 0x09 + +#define DW_LNE_end_sequence 0x01 +#define DW_LNE_set_address 0x02 +#define DW_LNE_define_file 0x03 + +#define DW_CFA_advance_loc 0x01 +#define DW_CFA_offset 0x02 +#define DW_CFA_restore 0x03 +#define DW_CFA_set_loc 0x01 +#define DW_CFA_advance_loc1 0x02 +#define DW_CFA_advance_loc2 0x03 +#define DW_CFA_advance_loc4 0x04 +#define DW_CFA_offset_extended 0x05 +#define DW_CFA_restore_extended 0x06 +#define DW_CFA_undefined 0x07 +#define DW_CFA_same_value 0x08 +#define DW_CFA_register 0x09 +#define DW_CFA_remember_state 0x0a +#define DW_CFA_restore_state 0x0b +#define DW_CFA_def_cfa 0x0c +#define DW_CFA_def_cfa_register 0x0d +#define DW_CFA_def_cfa_offset 0x0e +#define DW_CFA_nop 0x00 + +#define CASE_TYPE_TAG \ + case DW_TAG_const_type:\ + case DW_TAG_volatile_type:\ + case DW_TAG_pointer_type:\ + case DW_TAG_base_type:\ + case DW_TAG_array_type:\ + case DW_TAG_structure_type:\ + case DW_TAG_union_type:\ + case DW_TAG_typedef:\ + case DW_TAG_subroutine_type:\ + case DW_TAG_enumeration_type:\ + case DW_TAG_enumerator:\ + case DW_TAG_reference_type + +struct ELFcie { + ELFcie *next; + u32 offset; + u8 *augmentation; + u32 codeAlign; + s32 dataAlign; + int returnAddress; + u8 *data; + u32 dataLen; +}; + +struct ELFfde { + ELFcie *cie; + u32 address; + u32 end; + u8 *data; + u32 dataLen; +}; + +enum ELFRegMode { + REG_NOT_SET, + REG_OFFSET, + REG_REGISTER +}; + + +struct ELFFrameStateRegister { + ELFRegMode mode; + int reg; + s32 offset; +}; + +struct ELFFrameStateRegisters { + ELFFrameStateRegister regs[16]; + ELFFrameStateRegisters *previous; +}; + +enum ELFCfaMode { + CFA_NOT_SET, + CFA_REG_OFFSET +}; + +struct ELFFrameState { + ELFFrameStateRegisters registers; + + ELFCfaMode cfaMode; + int cfaRegister; + s32 cfaOffset; + + u32 pc; + + int dataAlign; + int codeAlign; + int returnAddress; +}; + +extern bool cpuIsMultiBoot; + +Symbol *elfSymbols = NULL; +char *elfSymbolsStrTab = NULL; +int elfSymbolsCount = 0; + +ELFSectionHeader **elfSectionHeaders = NULL; +char *elfSectionHeadersStringTable = NULL; +int elfSectionHeadersCount = 0; +u8 *elfFileData = NULL; + +CompileUnit *elfCompileUnits = NULL; +DebugInfo *elfDebugInfo = NULL; +char *elfDebugStrings = NULL; + +ELFcie *elfCies = NULL; +ELFfde **elfFdes = NULL; +int elfFdeCount = 0; + +CompileUnit *elfCurrentUnit = NULL; + +u32 elfRead4Bytes(u8 *); +u16 elfRead2Bytes(u8 *); + +CompileUnit *elfGetCompileUnit(u32 addr) +{ + if(elfCompileUnits) { + CompileUnit *unit = elfCompileUnits; + while(unit) { + if(unit->lowPC) { + if(addr >= unit->lowPC && addr < unit->highPC) + return unit; + } else { + ARanges *r = unit->ranges; + if(r) { + int count = r->count; + for(int j = 0; j < count; j++) { + if(addr >= r->ranges[j].lowPC && addr < r->ranges[j].highPC) + return unit; + } + } + } + unit = unit->next; + } + } + return NULL; +} + +const char *elfGetAddressSymbol(u32 addr) +{ + static char buffer[256]; + + CompileUnit *unit = elfGetCompileUnit(addr); + // found unit, need to find function + if(unit) { + Function *func = unit->functions; + while(func) { + if(addr >= func->lowPC && addr < func->highPC) { + int offset = addr - func->lowPC; + const char *name = func->name; + if(!name) + name = ""; + if(offset) + sprintf(buffer, "%s+%d", name, offset); + else + strcpy(buffer, name); + return buffer; + } + func = func->next; + } + } + + if(elfSymbolsCount) { + for(int i = 0; i < elfSymbolsCount; i++) { + Symbol *s = &elfSymbols[i]; + if((addr >= s->value) && addr < (s->value+s->size)) { + int offset = addr-s->value; + const char *name = s->name; + if(name == NULL) + name = ""; + if(offset) + sprintf(buffer, "%s+%d", name, addr-s->value); + else + strcpy(buffer, name); + return buffer; + } else if(addr == s->value) { + if(s->name) + strcpy(buffer, s->name); + else + strcpy(buffer, ""); + return buffer; + } + } + } + + return ""; +} + +bool elfFindLineInModule(u32 *addr, const char *name, int line) +{ + CompileUnit *unit = elfCompileUnits; + + while(unit) { + if(unit->lineInfoTable) { + int i; + int count = unit->lineInfoTable->fileCount; + char *found = NULL; + for(i = 0; i < count; i++) { + if(strcmp(name, unit->lineInfoTable->files[i]) == 0) { + found = unit->lineInfoTable->files[i]; + break; + } + } + // found a matching filename... try to find line now + if(found) { + LineInfoItem *table = unit->lineInfoTable->lines; + count = unit->lineInfoTable->number; + for(i = 0; i < count; i++) { + if(table[i].file == found && table[i].line == line) { + *addr = table[i].address; + return true; + } + } + // we can only find a single match + return false; + } + } + unit = unit->next; + } + return false; +} + +int elfFindLine(CompileUnit *unit, Function * /* func */, u32 addr, const char **f) +{ + int currentLine = -1; + if(unit->hasLineInfo) { + int count = unit->lineInfoTable->number; + LineInfoItem *table = unit->lineInfoTable->lines; + int i; + for(i = 0; i < count; i++) { + if(addr <= table[i].address) + break; + } + if(i == count) + i--; + *f = table[i].file; + currentLine = table[i].line; + } + return currentLine; +} + +bool elfFindLineInUnit(u32 *addr, CompileUnit *unit, int line) +{ + if(unit->hasLineInfo) { + int count = unit->lineInfoTable->number; + LineInfoItem *table = unit->lineInfoTable->lines; + int i; + for(i = 0; i < count; i++) { + if(line == table[i].line) { + *addr = table[i].address; + return true; + } + } + } + return false; +} + +bool elfGetCurrentFunction(u32 addr, Function **f, CompileUnit **u) +{ + CompileUnit *unit = elfGetCompileUnit(addr); + // found unit, need to find function + if(unit) { + Function *func = unit->functions; + while(func) { + if(addr >= func->lowPC && addr < func->highPC) { + *f = func; + *u = unit; + return true; + } + func = func->next; + } + } + return false; +} + +bool elfGetObject(const char *name, Function *f, CompileUnit *u, Object **o) +{ + if(f && u) { + Object *v = f->variables; + + while(v) { + if(strcmp(name, v->name) == 0) { + *o = v; + return true; + } + v = v->next; + } + v = f->parameters; + while(v) { + if(strcmp(name, v->name) == 0) { + *o = v; + return true; + } + v = v->next; + } + v = u->variables; + while(v) { + if(strcmp(name, v->name) == 0) { + *o = v; + return true; + } + v = v->next; + } + } + + CompileUnit *c = elfCompileUnits; + + while(c) { + if(c != u) { + Object *v = c->variables; + while(v) { + if(strcmp(name, v->name) == 0) { + *o = v; + return true; + } + v = v->next; + } + } + c = c->next; + } + + return false; +} + +const char *elfGetSymbol(int i, u32 *value, u32 *size, int *type) +{ + if(i < elfSymbolsCount) { + Symbol *s = &elfSymbols[i]; + *value = s->value; + *size = s->size; + *type = s->type; + return s->name; + } + return NULL; +} + +bool elfGetSymbolAddress(const char *sym, u32 *addr, u32 *size, int *type) +{ + if(elfSymbolsCount) { + for(int i = 0; i < elfSymbolsCount; i++) { + Symbol *s = &elfSymbols[i]; + if(strcmp(sym, s->name) == 0) { + *addr = s->value; + *size = s->size; + *type = s->type; + return true; + } + } + } + return false; +} + +ELFfde *elfGetFde(u32 address) +{ + if(elfFdes) { + int i; + for(i = 0; i < elfFdeCount; i++) { + if(address >= elfFdes[i]->address && + address < elfFdes[i]->end) { + return elfFdes[i]; + } + } + } + + return NULL; +} + +void elfExecuteCFAInstructions(ELFFrameState *state, u8 *data, u32 len, + u32 pc) +{ + u8 *end = data + len; + int bytes; + int reg; + ELFFrameStateRegisters *fs; + + while(data < end && state->pc < pc) { + u8 op = *data++; + + switch(op >> 6) { + case DW_CFA_advance_loc: + state->pc += (op & 0x3f) * state->codeAlign; + break; + case DW_CFA_offset: + reg = op & 0x3f; + state->registers.regs[reg].mode = REG_OFFSET; + state->registers.regs[reg].offset = state->dataAlign * + (s32)elfReadLEB128(data, &bytes); + data += bytes; + break; + case DW_CFA_restore: + // we don't care much about the other possible settings, + // so just setting to unset is enough for now + state->registers.regs[op & 0x3f].mode = REG_NOT_SET; + break; + case 0: + switch(op & 0x3f) { + case DW_CFA_nop: + break; + case DW_CFA_advance_loc1: + state->pc += state->codeAlign * (*data++); + break; + case DW_CFA_advance_loc2: + state->pc += state->codeAlign * elfRead2Bytes(data); + data += 2; + break; + case DW_CFA_advance_loc4: + state->pc += state->codeAlign * elfRead4Bytes(data); + data += 4; + break; + case DW_CFA_offset_extended: + reg = elfReadLEB128(data, &bytes); + data += bytes; + state->registers.regs[reg].mode = REG_OFFSET; + state->registers.regs[reg].offset = state->dataAlign * + (s32)elfReadLEB128(data, &bytes); + data += bytes; + break; + case DW_CFA_restore_extended: + case DW_CFA_undefined: + case DW_CFA_same_value: + reg = elfReadLEB128(data, &bytes); + data += bytes; + state->registers.regs[reg].mode = REG_NOT_SET; + break; + case DW_CFA_register: + reg = elfReadLEB128(data, &bytes); + data += bytes; + state->registers.regs[reg].mode = REG_REGISTER; + state->registers.regs[reg].reg = elfReadLEB128(data, &bytes); + data += bytes; + break; + case DW_CFA_remember_state: + fs = (ELFFrameStateRegisters *)calloc(1, + sizeof(ELFFrameStateRegisters)); + memcpy(fs, &state->registers, sizeof(ELFFrameStateRegisters)); + state->registers.previous = fs; + break; + case DW_CFA_restore_state: + if(state->registers.previous == NULL) { + printf("Error: previous frame state is NULL.\n"); + return; + } + fs = state->registers.previous; + memcpy(&state->registers, fs, sizeof(ELFFrameStateRegisters)); + free(fs); + break; + case DW_CFA_def_cfa: + state->cfaRegister = elfReadLEB128(data, &bytes); + data += bytes; + state->cfaOffset = (s32)elfReadLEB128(data, &bytes); + data += bytes; + state->cfaMode = CFA_REG_OFFSET; + break; + case DW_CFA_def_cfa_register: + state->cfaRegister = elfReadLEB128(data, &bytes); + data += bytes; + state->cfaMode = CFA_REG_OFFSET; + break; + case DW_CFA_def_cfa_offset: + state->cfaOffset = (s32)elfReadLEB128(data, &bytes); + data += bytes; + state->cfaMode = CFA_REG_OFFSET; + break; + default: + printf("Unknown CFA opcode %08x\n", op); + return; + } + break; + default: + printf("Unknown CFA opcode %08x\n", op); + return; + } + } +} + +ELFFrameState *elfGetFrameState(ELFfde *fde, u32 address) +{ + ELFFrameState *state = (ELFFrameState *)calloc(1, sizeof(ELFFrameState)); + state->pc = fde->address; + state->dataAlign = fde->cie->dataAlign; + state->codeAlign = fde->cie->codeAlign; + state->returnAddress = fde->cie->returnAddress; + + elfExecuteCFAInstructions(state, + fde->cie->data, + fde->cie->dataLen, + 0xffffffff); + elfExecuteCFAInstructions(state, + fde->data, + fde->dataLen, + address); + + return state; +} + +void elfPrintCallChain(u32 address) +{ + int count = 1; + + reg_pair regs[15]; + reg_pair newRegs[15]; + + memcpy(®s[0], ®[0], sizeof(reg_pair) * 15); + + while(count < 20) { + const char *addr = elfGetAddressSymbol(address); + if(*addr == 0) + addr = "???"; + + printf("%08x %s\n", address, addr); + + ELFfde *fde = elfGetFde(address); + + if(fde == NULL) { + break; + } + + ELFFrameState *state = elfGetFrameState(fde, address); + + if(!state) { + break; + } + + if(state->cfaMode == CFA_REG_OFFSET) { + memcpy(&newRegs[0], ®s[0], sizeof(reg_pair) * 15); + u32 addr = 0; + for(int i = 0; i < 15; i++) { + ELFFrameStateRegister *r = &state->registers. + regs[i]; + + switch(r->mode) { + case REG_NOT_SET: + newRegs[i].I = regs[i].I; + break; + case REG_OFFSET: + newRegs[i].I = elfReadMemory(regs[state->cfaRegister].I + + state->cfaOffset + + r->offset); + break; + case REG_REGISTER: + newRegs[i].I = regs[r->reg].I; + break; + default: + printf("Unknown register mode: %d\n", r->mode); + break; + } + } + memcpy(regs, newRegs, sizeof(reg_pair)*15); + addr = newRegs[14].I; + addr &= 0xfffffffe; + address = addr; + count++; + } else { + printf("CFA not set\n"); + break; + } + if(state->registers.previous) { + ELFFrameStateRegisters *prev = state->registers.previous; + + while(prev) { + ELFFrameStateRegisters *p = prev->previous; + free(prev); + prev = p; + } + } + free(state); + } +} + +u32 elfDecodeLocation(Function *f, ELFBlock *o, LocationType *type, u32 base) +{ + u32 framebase = 0; + if(f && f->frameBase) { + ELFBlock *b = f->frameBase; + switch(*b->data) { + case DW_OP_reg0: + case DW_OP_reg1: + case DW_OP_reg2: + case DW_OP_reg3: + case DW_OP_reg4: + case DW_OP_reg5: + case DW_OP_reg6: + case DW_OP_reg7: + case DW_OP_reg8: + case DW_OP_reg9: + case DW_OP_reg10: + case DW_OP_reg11: + case DW_OP_reg12: + case DW_OP_reg13: + case DW_OP_reg14: + case DW_OP_reg15: + framebase = reg[*b->data-0x50].I; + break; + default: + fprintf(stderr, "Unknown frameBase %02x\n", *b->data); + break; + } + } + + ELFBlock *loc = o; + u32 location = 0; + int bytes = 0; + if(loc) { + switch(*loc->data) { + case DW_OP_addr: + location = elfRead4Bytes(loc->data+1); + *type = LOCATION_memory; + break; + case DW_OP_plus_uconst: + location = base + elfReadLEB128(loc->data+1, &bytes); + *type = LOCATION_memory; + break; + case DW_OP_reg0: + case DW_OP_reg1: + case DW_OP_reg2: + case DW_OP_reg3: + case DW_OP_reg4: + case DW_OP_reg5: + case DW_OP_reg6: + case DW_OP_reg7: + case DW_OP_reg8: + case DW_OP_reg9: + case DW_OP_reg10: + case DW_OP_reg11: + case DW_OP_reg12: + case DW_OP_reg13: + case DW_OP_reg14: + case DW_OP_reg15: + location = *loc->data - 0x50; + *type = LOCATION_register; + break; + case DW_OP_fbreg: + { + int bytes; + s32 off = elfReadSignedLEB128(loc->data+1, &bytes); + location = framebase + off; + *type = LOCATION_memory; + } + break; + default: + fprintf(stderr, "Unknown location %02x\n", *loc->data); + break; + } + } + return location; +} + +u32 elfDecodeLocation(Function *f, ELFBlock *o, LocationType *type) +{ + return elfDecodeLocation(f, o, type, 0); +} + +// reading function + +u32 elfRead4Bytes(u8 *data) +{ + u32 value = *data++; + value |= (*data++ << 8); + value |= (*data++ << 16); + value |= (*data << 24); + return value; +} + +u16 elfRead2Bytes(u8 *data) +{ + u16 value = *data++; + value |= (*data << 8); + return value; +} + +char *elfReadString(u8 *data, int *bytesRead) +{ + if(*data == 0) { + *bytesRead = 1; + return NULL; + } + *bytesRead = (int)strlen((char *)data) + 1; + return (char *)data; +} + +s32 elfReadSignedLEB128(u8 *data, int *bytesRead) +{ + s32 result = 0; + int shift = 0; + int count = 0; + + u8 byte; + do { + byte = *data++; + count++; + result |= (byte & 0x7f) << shift; + shift += 7; + } while(byte & 0x80); + if((shift < 32) && (byte & 0x40)) + result |= -(1 << shift); + *bytesRead = count; + return result; +} + +u32 elfReadLEB128(u8 *data, int *bytesRead) +{ + u32 result = 0; + int shift = 0; + int count = 0; + u8 byte; + do { + byte = *data++; + count++; + result |= (byte & 0x7f) << shift; + shift += 7; + } while(byte & 0x80); + *bytesRead = count; + return result; +} + +u8 *elfReadSection(u8 *data, ELFSectionHeader *sh) +{ + return data + READ32LE(&sh->offset); +} + +ELFSectionHeader *elfGetSectionByName(const char *name) +{ + for(int i = 0; i < elfSectionHeadersCount; i++) { + if(strcmp(name, + &elfSectionHeadersStringTable[READ32LE(&elfSectionHeaders[i]-> + name)]) == 0) { + return elfSectionHeaders[i]; + } + } + return NULL; +} + +ELFSectionHeader *elfGetSectionByNumber(int number) +{ + if(number < elfSectionHeadersCount) { + return elfSectionHeaders[number]; + } + return NULL; +} + +CompileUnit *elfGetCompileUnitForData(u8 *data) +{ + u8 *end = elfCurrentUnit->top + 4 + elfCurrentUnit->length; + + if(data >= elfCurrentUnit->top && data < end) + return elfCurrentUnit; + + CompileUnit *unit = elfCompileUnits; + + while(unit) { + end = unit->top + 4 + unit->length; + + if(data >= unit->top && data < end) + return unit; + + unit = unit->next; + } + + printf("Error: cannot find reference to compile unit at offset %08x\n", + (int)(data - elfDebugInfo->infodata)); + exit(-1); +} + +u8 *elfReadAttribute(u8 *data, ELFAttr *attr) +{ + int bytes; + int form = attr->form; + start: + switch(form) { + case DW_FORM_addr: + attr->value = elfRead4Bytes(data); + data += 4; + break; + case DW_FORM_data2: + attr->value = elfRead2Bytes(data); + data += 2; + break; + case DW_FORM_data4: + attr->value = elfRead4Bytes(data); + data += 4; + break; + case DW_FORM_string: + attr->string = (char *)data; + data += strlen(attr->string)+1; + break; + case DW_FORM_strp: + attr->string = elfDebugStrings + elfRead4Bytes(data); + data += 4; + break; + case DW_FORM_block: + attr->block = (ELFBlock *)malloc(sizeof(ELFBlock)); + attr->block->length = elfReadLEB128(data, &bytes); + data += bytes; + attr->block->data = data; + data += attr->block->length; + break; + case DW_FORM_block1: + attr->block = (ELFBlock *)malloc(sizeof(ELFBlock)); + attr->block->length = *data++; + attr->block->data = data; + data += attr->block->length; + break; + case DW_FORM_data1: + attr->value = *data++; + break; + case DW_FORM_flag: + attr->flag = (*data++) ? true : false; + break; + case DW_FORM_sdata: + attr->value = elfReadSignedLEB128(data, &bytes); + data += bytes; + break; + case DW_FORM_udata: + attr->value = elfReadLEB128(data, &bytes); + data += bytes; + break; + case DW_FORM_ref_addr: + attr->value = (u32)((elfDebugInfo->infodata + elfRead4Bytes(data)) - elfGetCompileUnitForData(data)->top); + data += 4; + break; + case DW_FORM_ref4: + attr->value = elfRead4Bytes(data); + data += 4; + break; + case DW_FORM_ref_udata: + attr->value = (u32)((elfDebugInfo->infodata + (elfGetCompileUnitForData(data)->top - elfDebugInfo->infodata) + elfReadLEB128(data, &bytes)) - elfCurrentUnit->top); + data += bytes; + break; + case DW_FORM_indirect: + form = elfReadLEB128(data, &bytes); + data += bytes; + goto start; + default: + fprintf(stderr, "Unsupported FORM %02x\n", form); + exit(-1); + } + return data; +} + +ELFAbbrev *elfGetAbbrev(ELFAbbrev **table, u32 number) +{ + int hash = number % 121; + + ELFAbbrev *abbrev = table[hash]; + + while(abbrev) { + if(abbrev->number == number) + return abbrev; + abbrev = abbrev->next; + } + return NULL; +} + +ELFAbbrev **elfReadAbbrevs(u8 *data, u32 offset) +{ + data += offset; + ELFAbbrev **abbrevs = (ELFAbbrev **)calloc(sizeof(ELFAbbrev *)*121,1); + int bytes = 0; + u32 number = elfReadLEB128(data, &bytes); + data += bytes; + while(number) { + ELFAbbrev *abbrev = (ELFAbbrev *)calloc(sizeof(ELFAbbrev),1); + + // read tag information + abbrev->number = number; + abbrev->tag = elfReadLEB128(data, &bytes); + data += bytes; + abbrev->hasChildren = *data++ ? true: false; + + // read attributes + int name = elfReadLEB128(data, &bytes); + data += bytes; + int form = elfReadLEB128(data, &bytes); + data += bytes; + + while(name) { + if((abbrev->numAttrs % 4) == 0) { + abbrev->attrs = (ELFAttr *)realloc(abbrev->attrs, + (abbrev->numAttrs + 4) * + sizeof(ELFAttr)); + } + abbrev->attrs[abbrev->numAttrs].name = name; + abbrev->attrs[abbrev->numAttrs++].form = form; + + name = elfReadLEB128(data, &bytes); + data += bytes; + form = elfReadLEB128(data, &bytes); + data += bytes; + } + + int hash = number % 121; + abbrev->next = abbrevs[hash]; + abbrevs[hash] = abbrev; + + number = elfReadLEB128(data, &bytes); + data += bytes; + + if(elfGetAbbrev(abbrevs, number) != NULL) + break; + } + + return abbrevs; +} + +void elfParseCFA(u8 *top) +{ + ELFSectionHeader *h = elfGetSectionByName(".debug_frame"); + + if(h == NULL) { + return; + } + + u8 *data = elfReadSection(top, h); + + u8 *topOffset = data; + + u8 *end = data + READ32LE(&h->size); + + ELFcie *cies = NULL; + + while(data < end) { + u32 offset = (u32)(data - topOffset); + u32 len = elfRead4Bytes(data); + data += 4; + + u8 *dataEnd = data + len; + + u32 id = elfRead4Bytes(data); + data += 4; + + if(id == 0xffffffff) { + // skip version + (*data)++; + + ELFcie *cie = (ELFcie *)calloc(1, sizeof(ELFcie)); + + cie->next = cies; + cies = cie; + + cie->offset = offset; + + cie->augmentation = data; + while(*data) + data++; + data++; + + if(*cie->augmentation) { + fprintf(stderr, "Error: augmentation not supported\n"); + exit(-1); + } + + int bytes; + cie->codeAlign = elfReadLEB128(data, &bytes); + data += bytes; + + cie->dataAlign = elfReadSignedLEB128(data, &bytes); + data += bytes; + + cie->returnAddress = *data++; + + cie->data = data; + cie->dataLen = (u32)(dataEnd - data); + } else { + ELFfde *fde = (ELFfde *)calloc(1, sizeof(ELFfde)); + + ELFcie *cie = cies; + + while(cie != NULL) { + if(cie->offset == id) + break; + cie = cie->next; + } + + if(!cie) { + fprintf(stderr, "Cannot find CIE %08x\n", id); + exit(-1); + } + + fde->cie = cie; + + fde->address = elfRead4Bytes(data); + data += 4; + + fde->end = fde->address + elfRead4Bytes(data); + data += 4; + + fde->data = data; + fde->dataLen = (u32)(dataEnd - data); + + if((elfFdeCount %10) == 0) { + elfFdes = (ELFfde **)realloc(elfFdes, (elfFdeCount+10) * + sizeof(ELFfde *)); + } + elfFdes[elfFdeCount++] = fde; + } + data = dataEnd; + } + + elfCies = cies; +} + +void elfAddLine(LineInfo *l, u32 a, int file, int line, int *max) +{ + if(l->number == *max) { + *max += 1000; + l->lines = (LineInfoItem *)realloc(l->lines, *max*sizeof(LineInfoItem)); + } + LineInfoItem *li = &l->lines[l->number]; + li->file = l->files[file-1]; + li->address = a; + li->line = line; + l->number++; +} + +void elfParseLineInfo(CompileUnit *unit, u8 *top) +{ + ELFSectionHeader *h = elfGetSectionByName(".debug_line"); + if(h == NULL) { + fprintf(stderr, "No line information found\n"); + return; + } + LineInfo *l = unit->lineInfoTable = (LineInfo *)calloc(1, sizeof(LineInfo)); + l->number = 0; + int max = 1000; + l->lines = (LineInfoItem *)malloc(1000*sizeof(LineInfoItem)); + + u8 *data = elfReadSection(top, h); + data += unit->lineInfo; + u32 totalLen = elfRead4Bytes(data); + data += 4; + u8 *end = data + totalLen; + // u16 version = elfRead2Bytes(data); + data += 2; + // u32 offset = elfRead4Bytes(data); + data += 4; + int minInstrSize = *data++; + int defaultIsStmt = *data++; + int lineBase = (s8)*data++; + int lineRange = *data++; + int opcodeBase = *data++; + u8 *stdOpLen = (u8 *)malloc(opcodeBase * sizeof(u8)); + stdOpLen[0] = 1; + int i; + for(i = 1; i < opcodeBase; i++) + stdOpLen[i] = *data++; + + free(stdOpLen);// todo + int bytes = 0; + + char *s; + while((s = elfReadString(data, &bytes)) != NULL) { + data += bytes; + // fprintf(stderr, "Directory is %s\n", s); + } + data += bytes; + int count = 4; + int index = 0; + l->files = (char **)malloc(sizeof(char *)*count); + + while((s = elfReadString(data, &bytes)) != NULL) { + l->files[index++] = s; + + data += bytes; + // directory + elfReadLEB128(data, &bytes); + data += bytes; + // time + elfReadLEB128(data, &bytes); + data += bytes; + // size + elfReadLEB128(data, &bytes); + data += bytes; + // fprintf(stderr, "File is %s\n", s); + if(index == count) { + count += 4; + l->files = (char **)realloc(l->files, sizeof(char *)*count); + } + } + l->fileCount = index; + data += bytes; + + while(data < end) { + u32 address = 0; + int file = 1; + int line = 1; + int col = 0; + int isStmt = defaultIsStmt; + int basicBlock = 0; + int endSeq = 0; + + while(!endSeq) { + int op = *data++; + switch(op) { + case DW_LNS_extended_op: + { + data++; + op = *data++; + switch(op) { + case DW_LNE_end_sequence: + endSeq = 1; + break; + case DW_LNE_set_address: + address = elfRead4Bytes(data); + data += 4; + break; + default: + fprintf(stderr, "Unknown extended LINE opcode %02x\n", op); + exit(-1); + } + } + break; + case DW_LNS_copy: + // fprintf(stderr, "Address %08x line %d (%d)\n", address, line, file); + elfAddLine(l, address, file, line, &max); + basicBlock = 0; + break; + case DW_LNS_advance_pc: + address += minInstrSize * elfReadLEB128(data, &bytes); + data += bytes; + break; + case DW_LNS_advance_line: + line += elfReadSignedLEB128(data, &bytes); + data += bytes; + break; + case DW_LNS_set_file: + file = elfReadLEB128(data, &bytes); + data += bytes; + break; + case DW_LNS_set_column: + col = elfReadLEB128(data, &bytes); + data += bytes; + break; + case DW_LNS_negate_stmt: + isStmt = !isStmt; + break; + case DW_LNS_set_basic_block: + basicBlock = 1; + break; + case DW_LNS_const_add_pc: + address += (minInstrSize *((255 - opcodeBase)/lineRange)); + break; + case DW_LNS_fixed_advance_pc: + address += elfRead2Bytes(data); + data += 2; + break; + default: + op = op - opcodeBase; + address += (op / lineRange) * minInstrSize; + line += lineBase + (op % lineRange); + elfAddLine(l, address, file, line, &max); + // fprintf(stderr, "Address %08x line %d (%d)\n", address, line,file); + basicBlock = 1; + break; + } + } + } + l->lines = (LineInfoItem *)realloc(l->lines, l->number*sizeof(LineInfoItem)); +} + +u8 *elfSkipData(u8 *data, ELFAbbrev *abbrev, ELFAbbrev **abbrevs) +{ + int i; + int bytes; + + for(i = 0; i < abbrev->numAttrs; i++) { + data = elfReadAttribute(data, &abbrev->attrs[i]); + if(abbrev->attrs[i].form == DW_FORM_block1) + free(abbrev->attrs[i].block); + } + + if(abbrev->hasChildren) { + int nesting = 1; + while(nesting) { + u32 abbrevNum = elfReadLEB128(data, &bytes); + data += bytes; + + if(!abbrevNum) { + nesting--; + continue; + } + + abbrev = elfGetAbbrev(abbrevs, abbrevNum); + + for(i = 0; i < abbrev->numAttrs; i++) { + data = elfReadAttribute(data, &abbrev->attrs[i]); + if(abbrev->attrs[i].form == DW_FORM_block1) + free(abbrev->attrs[i].block); + } + + if(abbrev->hasChildren) { + nesting++; + } + } + } + return data; +} + +Type *elfParseType(CompileUnit *unit, u32); +u8 *elfParseObject(u8 *data, ELFAbbrev *abbrev, CompileUnit *unit, + Object **object); +u8 *elfParseFunction(u8 *data, ELFAbbrev *abbrev, CompileUnit *unit, + Function **function); +void elfCleanUp(Function *); + +void elfAddType(Type *type, CompileUnit *unit, u32 offset) +{ + if(type->next == NULL) { + if(unit->types != type && type->offset == 0) { + type->offset = offset; + type->next = unit->types; + unit->types = type; + } + } +} + +void elfParseType(u8 *data, u32 offset, ELFAbbrev *abbrev, CompileUnit *unit, + Type **type) +{ + switch(abbrev->tag) { + case DW_TAG_typedef: + { + u32 typeref = 0; + char *name = NULL; + for(int i = 0; i < abbrev->numAttrs; i++) { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch(attr->name) { + case DW_AT_name: + name = attr->string; + break; + case DW_AT_type: + typeref = attr->value; + break; + case DW_AT_decl_file: + case DW_AT_decl_line: + break; + default: + fprintf(stderr, "Unknown attribute for typedef %02x\n", attr->name); + break; + } + } + if(abbrev->hasChildren) + fprintf(stderr, "Unexpected children for typedef\n"); + *type = elfParseType(unit, typeref); + if(name) + (*type)->name = name; + return; + } + break; + case DW_TAG_union_type: + case DW_TAG_structure_type: + { + Type *t = (Type *)calloc(sizeof(Type), 1); + if(abbrev->tag == DW_TAG_structure_type) + t->type = TYPE_struct; + else + t->type = TYPE_union; + + Struct *s = (Struct *)calloc(sizeof(Struct), 1); + t->structure = s; + elfAddType(t, unit, offset); + + for(int i = 0; i < abbrev->numAttrs; i++) { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch(attr->name) { + case DW_AT_name: + t->name = attr->string; + break; + case DW_AT_byte_size: + t->size = attr->value; + break; + case DW_AT_decl_file: + case DW_AT_decl_line: + case DW_AT_sibling: + case DW_AT_containing_type: // todo? + case DW_AT_declaration: + case DW_AT_specification: // TODO: + break; + default: + fprintf(stderr, "Unknown attribute for struct %02x\n", attr->name); + break; + } + } + if(abbrev->hasChildren) { + int bytes; + u32 num = elfReadLEB128(data, &bytes); + data += bytes; + int index = 0; + while(num) { + ELFAbbrev *abbr = elfGetAbbrev(unit->abbrevs, num); + + switch(abbr->tag) { + case DW_TAG_member: + { + if((index % 4) == 0) + s->members = (Member *)realloc(s->members, + sizeof(Member)*(index+4)); + Member *m = &s->members[index]; + m->location = NULL; + m->bitOffset = 0; + m->bitSize = 0; + m->byteSize = 0; + for(int i = 0; i < abbr->numAttrs; i++) { + ELFAttr *attr = &abbr->attrs[i]; + data = elfReadAttribute(data, attr); + switch(attr->name) { + case DW_AT_name: + m->name = attr->string; + break; + case DW_AT_type: + m->type = elfParseType(unit, attr->value); + break; + case DW_AT_data_member_location: + m->location = attr->block; + break; + case DW_AT_byte_size: + m->byteSize = attr->value; + break; + case DW_AT_bit_offset: + m->bitOffset = attr->value; + break; + case DW_AT_bit_size: + m->bitSize = attr->value; + break; + case DW_AT_decl_file: + case DW_AT_decl_line: + case DW_AT_accessibility: + case DW_AT_artificial: // todo? + break; + default: + fprintf(stderr, "Unknown member attribute %02x\n", + attr->name); + } + } + index++; + } + break; + case DW_TAG_subprogram: + { + Function *fnc = NULL; + data = elfParseFunction(data, abbr, unit, &fnc); + if(fnc != NULL) { + if(unit->lastFunction) + unit->lastFunction->next = fnc; + else + unit->functions = fnc; + unit->lastFunction = fnc; + } + } + break; + case DW_TAG_inheritance: + // TODO: add support + data = elfSkipData(data, abbr, unit->abbrevs); + break; + CASE_TYPE_TAG: + // skip types... parsed only when used + data = elfSkipData(data, abbr, unit->abbrevs); + break; + case DW_TAG_variable: + data = elfSkipData(data, abbr, unit->abbrevs); + break; + default: + fprintf(stderr, "Unknown struct tag %02x %s\n", abbr->tag, t->name); + data = elfSkipData(data, abbr, unit->abbrevs); + break; + } + num = elfReadLEB128(data, &bytes); + data += bytes; + } + s->memberCount = index; + } + *type = t; + return; + } + break; + case DW_TAG_base_type: + { + Type *t = (Type *)calloc(sizeof(Type), 1); + + t->type = TYPE_base; + elfAddType(t, unit, offset); + for(int i = 0; i < abbrev->numAttrs; i++) { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch(attr->name) { + case DW_AT_name: + t->name = attr->string; + break; + case DW_AT_encoding: + t->encoding = attr->value; + break; + case DW_AT_byte_size: + t->size = attr->value; + break; + case DW_AT_bit_size: + t->bitSize = attr->value; + break; + default: + fprintf(stderr, "Unknown attribute for base type %02x\n", + attr->name); + break; + } + } + if(abbrev->hasChildren) + fprintf(stderr, "Unexpected children for base type\n"); + *type = t; + return; + } + break; + case DW_TAG_pointer_type: + { + Type *t = (Type *)calloc(sizeof(Type), 1); + + t->type = TYPE_pointer; + + elfAddType(t, unit, offset); + + for(int i = 0; i < abbrev->numAttrs; i++) { + ELFAttr *attr = &abbrev->attrs[i]; + data =elfReadAttribute(data, attr); + switch(attr->name) { + case DW_AT_type: + t->pointer = elfParseType(unit, attr->value); + break; + case DW_AT_byte_size: + t->size = attr->value; + break; + default: + fprintf(stderr, "Unknown pointer type attribute %02x\n", attr->name); + break; + } + } + if(abbrev->hasChildren) + fprintf(stderr, "Unexpected children for pointer type\n"); + *type = t; + return; + } + break; + case DW_TAG_reference_type: + { + Type *t = (Type *)calloc(sizeof(Type), 1); + + t->type = TYPE_reference; + + elfAddType(t, unit, offset); + + for(int i = 0; i < abbrev->numAttrs; i++) { + ELFAttr *attr = &abbrev->attrs[i]; + data =elfReadAttribute(data, attr); + switch(attr->name) { + case DW_AT_type: + t->pointer = elfParseType(unit, attr->value); + break; + case DW_AT_byte_size: + t->size = attr->value; + break; + default: + fprintf(stderr, "Unknown ref type attribute %02x\n", attr->name); + break; + } + } + if(abbrev->hasChildren) + fprintf(stderr, "Unexpected children for ref type\n"); + *type = t; + return; + } + break; + case DW_TAG_volatile_type: + { + u32 typeref = 0; + + for(int i = 0; i < abbrev->numAttrs; i++) { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch(attr->name) { + case DW_AT_type: + typeref = attr->value; + break; + default: + fprintf(stderr, "Unknown volatile attribute for type %02x\n", + attr->name); + break; + } + } + if(abbrev->hasChildren) + fprintf(stderr, "Unexpected children for volatile type\n"); + *type = elfParseType(unit, typeref); + return; + } + break; + case DW_TAG_const_type: + { + u32 typeref = 0; + + for(int i = 0; i < abbrev->numAttrs; i++) { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch(attr->name) { + case DW_AT_type: + typeref = attr->value; + break; + default: + fprintf(stderr, "Unknown const attribute for type %02x\n", + attr->name); + break; + } + } + if(abbrev->hasChildren) + fprintf(stderr, "Unexpected children for const type\n"); + *type = elfParseType(unit, typeref); + return; + } + break; + case DW_TAG_enumeration_type: + { + Type *t = (Type *)calloc(sizeof(Type), 1); + t->type = TYPE_enum; + Enum *e = (Enum *)calloc(sizeof(Enum), 1); + t->enumeration = e; + elfAddType(t, unit, offset); + int count = 0; + for(int i = 0; i < abbrev->numAttrs; i++) { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch(attr->name) { + case DW_AT_name: + t->name = attr->string; + break; + case DW_AT_byte_size: + t->size = attr->value; + break; + case DW_AT_sibling: + case DW_AT_decl_file: + case DW_AT_decl_line: + break; + default: + fprintf(stderr, "Unknown enum attribute %02x\n", attr->name); + } + } + if(abbrev->hasChildren) { + int bytes; + u32 num = elfReadLEB128(data, &bytes); + data += bytes; + while(num) { + ELFAbbrev *abbr = elfGetAbbrev(unit->abbrevs, num); + + switch(abbr->tag) { + case DW_TAG_enumerator: + { + count++; + e->members = (EnumMember *)realloc(e->members, + count*sizeof(EnumMember)); + EnumMember *m = &e->members[count-1]; + for(int i = 0; i < abbr->numAttrs; i++) { + ELFAttr *attr = &abbr->attrs[i]; + data = elfReadAttribute(data, attr); + switch(attr->name) { + case DW_AT_name: + m->name = attr->string; + break; + case DW_AT_const_value: + m->value = attr->value; + break; + default: + fprintf(stderr, "Unknown sub param attribute %02x\n", + attr->name); + } + } + } + break; + default: + fprintf(stderr, "Unknown enum tag %02x\n", abbr->tag); + data = elfSkipData(data, abbr, unit->abbrevs); + break; + } + num = elfReadLEB128(data, &bytes); + data += bytes; + } + } + e->count = count; + *type = t; + return; + } + break; + case DW_TAG_subroutine_type: + { + Type *t = (Type *)calloc(sizeof(Type), 1); + t->type = TYPE_function; + FunctionType *f = (FunctionType *)calloc(sizeof(FunctionType), 1); + t->function = f; + elfAddType(t, unit, offset); + for(int i = 0; i < abbrev->numAttrs; i++) { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch(attr->name) { + case DW_AT_prototyped: + case DW_AT_sibling: + break; + case DW_AT_type: + f->returnType = elfParseType(unit, attr->value); + break; + default: + fprintf(stderr, "Unknown subroutine attribute %02x\n", attr->name); + } + } + if(abbrev->hasChildren) { + int bytes; + u32 num = elfReadLEB128(data, &bytes); + data += bytes; + Object *lastVar = NULL; + while(num) { + ELFAbbrev *abbr = elfGetAbbrev(unit->abbrevs, num); + + switch(abbr->tag) { + case DW_TAG_formal_parameter: + { + Object *o; + data = elfParseObject(data, abbr, unit, &o); + if(f->args) + lastVar->next = o; + else + f->args = o; + lastVar = o; + } + break; + case DW_TAG_unspecified_parameters: + // no use in the debugger yet + data = elfSkipData(data, abbr, unit->abbrevs); + break; + CASE_TYPE_TAG: + // skip types... parsed only when used + data = elfSkipData(data, abbr, unit->abbrevs); + break; + default: + fprintf(stderr, "Unknown subroutine tag %02x\n", abbr->tag); + data = elfSkipData(data, abbr, unit->abbrevs); + break; + } + num = elfReadLEB128(data, &bytes); + data += bytes; + } + } + *type = t; + return; + } + break; + case DW_TAG_array_type: + { + u32 typeref = 0; + int i; + Array *array = (Array *)calloc(sizeof(Array), 1); + Type *t = (Type *)calloc(sizeof(Type), 1); + t->type = TYPE_array; + elfAddType(t, unit, offset); + + for(i = 0; i < abbrev->numAttrs; i++) { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch(attr->name) { + case DW_AT_sibling: + break; + case DW_AT_type: + typeref = attr->value; + array->type = elfParseType(unit, typeref); + break; + default: + fprintf(stderr, "Unknown array attribute %02x\n", attr->name); + } + } + if(abbrev->hasChildren) { + int bytes; + u32 num = elfReadLEB128(data, &bytes); + data += bytes; + int index = 0; + int maxBounds = 0; + while(num) { + ELFAbbrev *abbr = elfGetAbbrev(unit->abbrevs, num); + + switch(abbr->tag) { + case DW_TAG_subrange_type: + { + if(maxBounds == index) { + maxBounds += 4; + array->bounds = (int *)realloc(array->bounds, + sizeof(int)*maxBounds); + } + for(int i = 0; i < abbr->numAttrs; i++) { + ELFAttr *attr = &abbr->attrs[i]; + data = elfReadAttribute(data, attr); + switch(attr->name) { + case DW_AT_upper_bound: + array->bounds[index] = attr->value+1; + break; + case DW_AT_type: // ignore + break; + default: + fprintf(stderr, "Unknown subrange attribute %02x\n", + attr->name); + } + } + index++; + } + break; + default: + fprintf(stderr, "Unknown array tag %02x\n", abbr->tag); + data = elfSkipData(data, abbr, unit->abbrevs); + break; + } + num = elfReadLEB128(data, &bytes); + data += bytes; + } + array->maxBounds = index; + } + t->size = array->type->size; + for(i = 0; i < array->maxBounds; i++) + t->size *= array->bounds[i]; + t->array = array; + *type = t; + return; + } + break; + default: + fprintf(stderr, "Unknown type TAG %02x\n", abbrev->tag); + exit(-1); + } +} + +Type *elfParseType(CompileUnit *unit, u32 offset) +{ + Type *t = unit->types; + + while(t) { + if(t->offset == offset) + return t; + t = t->next; + } + if(offset == 0) { + Type *t = (Type *)calloc(sizeof(Type), 1); + t->type = TYPE_void; + t->offset = 0; + elfAddType(t, unit, 0); + return t; + } + u8 *data = unit->top + offset; + int bytes; + int abbrevNum = elfReadLEB128(data, &bytes); + data += bytes; + Type *type = NULL; + + ELFAbbrev *abbrev = elfGetAbbrev(unit->abbrevs, abbrevNum); + + elfParseType(data, offset, abbrev, unit, &type); + return type; +} + +void elfGetObjectAttributes(CompileUnit *unit, u32 offset, Object *o) +{ + u8 *data = unit->top + offset; + int bytes; + u32 abbrevNum = elfReadLEB128(data, &bytes); + data += bytes; + + if(!abbrevNum) { + return; + } + + ELFAbbrev *abbrev = elfGetAbbrev(unit->abbrevs, abbrevNum); + + for(int i = 0; i < abbrev->numAttrs; i++) { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch(attr->name) { + case DW_AT_location: + o->location = attr->block; + break; + case DW_AT_name: + if(o->name == NULL) + o->name = attr->string; + break; + case DW_AT_MIPS_linkage_name: + o->name = attr->string; + break; + case DW_AT_decl_file: + o->file = attr->value; + break; + case DW_AT_decl_line: + o->line = attr->value; + break; + case DW_AT_type: + o->type = elfParseType(unit, attr->value); + break; + case DW_AT_external: + o->external = attr->flag; + break; + case DW_AT_const_value: + case DW_AT_abstract_origin: + case DW_AT_declaration: + case DW_AT_artificial: + // todo + break; + case DW_AT_specification: + // TODO: + break; + default: + fprintf(stderr, "Unknown object attribute %02x\n", attr->name); + break; + } + } +} + +u8 *elfParseObject(u8 *data, ELFAbbrev *abbrev, CompileUnit *unit, + Object **object) +{ + Object *o = (Object *)calloc(sizeof(Object), 1); + + o->next = NULL; + + for(int i = 0; i < abbrev->numAttrs; i++) { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch(attr->name) { + case DW_AT_location: + o->location = attr->block; + break; + case DW_AT_name: + if(o->name == NULL) + o->name = attr->string; + break; + case DW_AT_MIPS_linkage_name: + o->name = attr->string; + break; + case DW_AT_decl_file: + o->file = attr->value; + break; + case DW_AT_decl_line: + o->line = attr->value; + break; + case DW_AT_type: + o->type = elfParseType(unit, attr->value); + break; + case DW_AT_external: + o->external = attr->flag; + break; + case DW_AT_abstract_origin: + elfGetObjectAttributes(unit, attr->value, o); + break; + case DW_AT_const_value: + case DW_AT_declaration: + case DW_AT_artificial: + break; + case DW_AT_specification: + // TODO: + break; + default: + fprintf(stderr, "Unknown object attribute %02x\n", attr->name); + break; + } + } + *object = o; + return data; +} + +u8 *elfParseBlock(u8 *data, ELFAbbrev *abbrev, CompileUnit *unit, + Function *func, Object **lastVar) +{ + int bytes; + u32 start = func->lowPC; + u32 end = func->highPC; + + for(int i = 0; i < abbrev->numAttrs; i++) { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch(attr->name) { + case DW_AT_sibling: + break; + case DW_AT_low_pc: + start = attr->value; + break; + case DW_AT_high_pc: + end = attr->value; + break; + case DW_AT_ranges: // ignore for now + break; + default: + fprintf(stderr, "Unknown block attribute %02x\n", attr->name); + break; + } + } + + if(abbrev->hasChildren) { + int nesting = 1; + + while(nesting) { + u32 abbrevNum = elfReadLEB128(data, &bytes); + data += bytes; + + if(!abbrevNum) { + nesting--; + continue; + } + + abbrev = elfGetAbbrev(unit->abbrevs, abbrevNum); + + switch(abbrev->tag) { + CASE_TYPE_TAG: // types only parsed when used + case DW_TAG_label: // not needed + data = elfSkipData(data, abbrev, unit->abbrevs); + break; + case DW_TAG_lexical_block: + data = elfParseBlock(data, abbrev, unit, func, lastVar); + break; + case DW_TAG_subprogram: + { + Function *f = NULL; + data = elfParseFunction(data, abbrev, unit, &f); + if(f != NULL) { + if(unit->lastFunction) + unit->lastFunction->next = f; + else + unit->functions = f; + unit->lastFunction = f; + } + } + break; + case DW_TAG_variable: + { + Object *o; + data = elfParseObject(data, abbrev, unit, &o); + if(o->startScope == 0) + o->startScope = start; + if(o->endScope == 0) + o->endScope = 0; + if(func->variables) + (*lastVar)->next = o; + else + func->variables = o; + *lastVar = o; + } + break; + case DW_TAG_inlined_subroutine: + // TODO: + data = elfSkipData(data, abbrev, unit->abbrevs); + break; + default: + { + fprintf(stderr, "Unknown block TAG %02x\n", abbrev->tag); + data = elfSkipData(data, abbrev, unit->abbrevs); + } + break; + } + } + } + return data; +} + +void elfGetFunctionAttributes(CompileUnit *unit, u32 offset, Function *func) +{ + u8 *data = unit->top + offset; + int bytes; + u32 abbrevNum = elfReadLEB128(data, &bytes); + data += bytes; + + if(!abbrevNum) { + return; + } + + ELFAbbrev *abbrev = elfGetAbbrev(unit->abbrevs, abbrevNum); + + for(int i = 0; i < abbrev->numAttrs; i++) { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + + switch(attr->name) { + case DW_AT_sibling: + break; + case DW_AT_name: + if(func->name == NULL) + func->name = attr->string; + break; + case DW_AT_MIPS_linkage_name: + func->name = attr->string; + break; + case DW_AT_low_pc: + func->lowPC = attr->value; + break; + case DW_AT_high_pc: + func->highPC = attr->value; + break; + case DW_AT_decl_file: + func->file = attr->value; + break; + case DW_AT_decl_line: + func->line = attr->value; + break; + case DW_AT_external: + func->external = attr->flag; + break; + case DW_AT_frame_base: + func->frameBase = attr->block; + break; + case DW_AT_type: + func->returnType = elfParseType(unit, attr->value); + break; + case DW_AT_inline: + case DW_AT_specification: + case DW_AT_declaration: + case DW_AT_artificial: + case DW_AT_prototyped: + case DW_AT_proc_body: + case DW_AT_save_offset: + case DW_AT_user_2002: + case DW_AT_virtuality: + case DW_AT_containing_type: + case DW_AT_accessibility: + // todo; + break; + case DW_AT_vtable_elem_location: + free(attr->block); + break; + default: + fprintf(stderr, "Unknown function attribute %02x\n", attr->name); + break; + } + } + + return; +} + +u8 *elfParseFunction(u8 *data, ELFAbbrev *abbrev, CompileUnit *unit, + Function **f) +{ + Function *func = (Function *)calloc(sizeof(Function), 1); + *f = func; + + int bytes; + bool mangled = false; + bool declaration = false; + for(int i = 0; i < abbrev->numAttrs; i++) { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch(attr->name) { + case DW_AT_sibling: + break; + case DW_AT_name: + if(func->name == NULL) + func->name = attr->string; + break; + case DW_AT_MIPS_linkage_name: + func->name = attr->string; + mangled = true; + break; + case DW_AT_low_pc: + func->lowPC = attr->value; + break; + case DW_AT_high_pc: + func->highPC = attr->value; + break; + case DW_AT_prototyped: + break; + case DW_AT_decl_file: + func->file = attr->value; + break; + case DW_AT_decl_line: + func->line = attr->value; + break; + case DW_AT_external: + func->external = attr->flag; + break; + case DW_AT_frame_base: + func->frameBase = attr->block; + break; + case DW_AT_type: + func->returnType = elfParseType(unit, attr->value); + break; + case DW_AT_abstract_origin: + elfGetFunctionAttributes(unit, attr->value, func); + break; + case DW_AT_declaration: + declaration = attr->flag; + break; + case DW_AT_inline: + case DW_AT_specification: + case DW_AT_artificial: + case DW_AT_proc_body: + case DW_AT_save_offset: + case DW_AT_user_2002: + case DW_AT_virtuality: + case DW_AT_containing_type: + case DW_AT_accessibility: + // todo; + break; + case DW_AT_vtable_elem_location: + free(attr->block); + break; + default: + fprintf(stderr, "Unknown function attribute %02x\n", attr->name); + break; + } + } + + if(declaration) { + elfCleanUp(func); + free(func); + *f = NULL; + + while(1) { + u32 abbrevNum = elfReadLEB128(data, &bytes); + data += bytes; + + if(!abbrevNum) { + return data; + } + + abbrev = elfGetAbbrev(unit->abbrevs, abbrevNum); + + data = elfSkipData(data, abbrev, unit->abbrevs); + } + } + + if(abbrev->hasChildren) { + int nesting = 1; + Object *lastParam = NULL; + Object *lastVar = NULL; + + while(nesting) { + u32 abbrevNum = elfReadLEB128(data, &bytes); + data += bytes; + + if(!abbrevNum) { + nesting--; + continue; + } + + abbrev = elfGetAbbrev(unit->abbrevs, abbrevNum); + + switch(abbrev->tag) { + CASE_TYPE_TAG: // no need to parse types. only parsed when used + case DW_TAG_label: // not needed + data = elfSkipData(data, abbrev, unit->abbrevs); + break; + case DW_TAG_subprogram: + { + Function *fnc=NULL; + data = elfParseFunction(data, abbrev, unit, &fnc); + if(fnc != NULL) { + if(unit->lastFunction == NULL) + unit->functions = fnc; + else + unit->lastFunction->next = fnc; + unit->lastFunction = fnc; + } + } + break; + case DW_TAG_lexical_block: + { + data = elfParseBlock(data, abbrev, unit, func, &lastVar); + } + break; + case DW_TAG_formal_parameter: + { + Object *o; + data = elfParseObject(data, abbrev, unit, &o); + if(func->parameters) + lastParam->next = o; + else + func->parameters = o; + lastParam = o; + } + break; + case DW_TAG_variable: + { + Object *o; + data = elfParseObject(data, abbrev, unit, &o); + if(func->variables) + lastVar->next = o; + else + func->variables = o; + lastVar = o; + } + break; + case DW_TAG_unspecified_parameters: + case DW_TAG_inlined_subroutine: + { + // todo + for(int i = 0; i < abbrev->numAttrs; i++) { + data = elfReadAttribute(data, &abbrev->attrs[i]); + if(abbrev->attrs[i].form == DW_FORM_block1) + free(abbrev->attrs[i].block); + } + + if(abbrev->hasChildren) + nesting++; + } + break; + default: + { + fprintf(stderr, "Unknown function TAG %02x\n", abbrev->tag); + data = elfSkipData(data, abbrev, unit->abbrevs); + } + break; + } + } + } + return data; +} + +u8 *elfParseUnknownData(u8 *data, ELFAbbrev *abbrev, ELFAbbrev **abbrevs) +{ + int i; + int bytes; + // switch(abbrev->tag) { + // default: + fprintf(stderr, "Unknown TAG %02x\n", abbrev->tag); + + for(i = 0; i < abbrev->numAttrs; i++) { + data = elfReadAttribute(data, &abbrev->attrs[i]); + if(abbrev->attrs[i].form == DW_FORM_block1) + free(abbrev->attrs[i].block); + } + + if(abbrev->hasChildren) { + int nesting = 1; + while(nesting) { + u32 abbrevNum = elfReadLEB128(data, &bytes); + data += bytes; + + if(!abbrevNum) { + nesting--; + continue; + } + + abbrev = elfGetAbbrev(abbrevs, abbrevNum); + + fprintf(stderr, "Unknown TAG %02x\n", abbrev->tag); + + for(i = 0; i < abbrev->numAttrs; i++) { + data = elfReadAttribute(data, &abbrev->attrs[i]); + if(abbrev->attrs[i].form == DW_FORM_block1) + free(abbrev->attrs[i].block); + } + + if(abbrev->hasChildren) { + nesting++; + } + } + } + // } + return data; +} + +u8 *elfParseCompileUnitChildren(u8 *data, CompileUnit *unit) +{ + int bytes; + u32 abbrevNum = elfReadLEB128(data, &bytes); + data += bytes; + Object *lastObj = NULL; + while(abbrevNum) { + ELFAbbrev *abbrev = elfGetAbbrev(unit->abbrevs, abbrevNum); + switch(abbrev->tag) { + case DW_TAG_subprogram: + { + Function *func = NULL; + data = elfParseFunction(data, abbrev, unit, &func); + if(func != NULL) { + if(unit->lastFunction) + unit->lastFunction->next = func; + else + unit->functions = func; + unit->lastFunction = func; + } + } + break; + CASE_TYPE_TAG: + data = elfSkipData(data, abbrev, unit->abbrevs); + break; + case DW_TAG_variable: + { + Object *var = NULL; + data = elfParseObject(data, abbrev, unit, &var); + if(lastObj) + lastObj->next = var; + else + unit->variables = var; + lastObj = var; + } + break; + default: + data = elfParseUnknownData(data, abbrev, unit->abbrevs); + break; + } + + abbrevNum = elfReadLEB128(data, &bytes); + data += bytes; + } + return data; +} + + +CompileUnit *elfParseCompUnit(u8 *data, u8 *abbrevData) +{ + int bytes; + u8 *top = data; + + u32 length = elfRead4Bytes(data); + data += 4; + + u16 version = elfRead2Bytes(data); + data += 2; + + u32 offset = elfRead4Bytes(data); + data += 4; + + u8 addrSize = *data++; + + if(version != 2) { + fprintf(stderr, "Unsupported debugging information version %d\n", version); + return NULL; + } + + if(addrSize != 4) { + fprintf(stderr, "Unsupported address size %d\n", addrSize); + return NULL; + } + + ELFAbbrev **abbrevs = elfReadAbbrevs(abbrevData, offset); + + u32 abbrevNum = elfReadLEB128(data, &bytes); + data += bytes; + + ELFAbbrev *abbrev = elfGetAbbrev(abbrevs, abbrevNum); + + CompileUnit *unit = (CompileUnit *)calloc(sizeof(CompileUnit), 1); + unit->top = top; + unit->length = length; + unit->abbrevs = abbrevs; + unit->next = NULL; + + elfCurrentUnit = unit; + + int i; + + for(i = 0; i < abbrev->numAttrs; i++) { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + + switch(attr->name) { + case DW_AT_name: + unit->name = attr->string; + break; + case DW_AT_stmt_list: + unit->hasLineInfo = true; + unit->lineInfo = attr->value; + break; + case DW_AT_low_pc: + unit->lowPC = attr->value; + break; + case DW_AT_high_pc: + unit->highPC = attr->value; + break; + case DW_AT_compdir: + unit->compdir = attr->string; + break; + // ignore + case DW_AT_language: + case DW_AT_producer: + case DW_AT_macro_info: + case DW_AT_entry_pc: + break; + default: + fprintf(stderr, "Unknown attribute %02x\n", attr->name); + break; + } + } + + if(abbrev->hasChildren) + elfParseCompileUnitChildren(data, unit); + + return unit; +} + +void elfParseAranges(u8 *data) +{ + ELFSectionHeader *sh = elfGetSectionByName(".debug_aranges"); + if(sh == NULL) { + fprintf(stderr, "No aranges found\n"); + return; + } + + data = elfReadSection(data, sh); + u8 *end = data + READ32LE(&sh->size); + + int max = 4; + ARanges *ranges = (ARanges *)calloc(sizeof(ARanges), 4); + + int index = 0; + + while(data < end) { + u32 len = elfRead4Bytes(data); + data += 4; + // u16 version = elfRead2Bytes(data); + data += 2; + u32 offset = elfRead4Bytes(data); + data += 4; + // u8 addrSize = *data++; + // u8 segSize = *data++; + data += 2; // remove if uncommenting above + data += 4; + ranges[index].count = (len-20)/8; + ranges[index].offset = offset; + ranges[index].ranges = (ARange *)calloc(sizeof(ARange), (len-20)/8); + int i = 0; + while(true) { + u32 addr = elfRead4Bytes(data); + data += 4; + u32 len = elfRead4Bytes(data); + data += 4; + if(addr == 0 && len == 0) + break; + ranges[index].ranges[i].lowPC = addr; + ranges[index].ranges[i].highPC = addr+len; + i++; + } + index++; + if(index == max) { + max += 4; + ranges = (ARanges *)realloc(ranges, max*sizeof(ARanges)); + } + } + elfDebugInfo->numRanges = index; + elfDebugInfo->ranges = ranges; +} + +void elfReadSymtab(u8 *data) +{ + ELFSectionHeader *sh = elfGetSectionByName(".symtab"); + int table = READ32LE(&sh->link); + + char *strtable = (char *)elfReadSection(data, elfGetSectionByNumber(table)); + + ELFSymbol *symtab = (ELFSymbol *)elfReadSection(data, sh); + + int count = READ32LE(&sh->size) / sizeof(ELFSymbol); + elfSymbolsCount = 0; + + elfSymbols = (Symbol *)malloc(sizeof(Symbol)*count); + + int i; + + for(i = 0; i < count; i++) { + ELFSymbol *s = &symtab[i]; + int type = s->info & 15; + int binding = s->info >> 4; + + if(binding) { + Symbol *sym = &elfSymbols[elfSymbolsCount]; + sym->name = &strtable[READ32LE(&s->name)]; + sym->binding = binding; + sym->type = type; + sym->value = READ32LE(&s->value); + sym->size = READ32LE(&s->size); + elfSymbolsCount++; + } + } + for(i = 0; i < count; i++) { + ELFSymbol *s = &symtab[i]; + int bind = s->info>>4; + int type = s->info & 15; + + if(!bind) { + Symbol *sym = &elfSymbols[elfSymbolsCount]; + sym->name = &strtable[READ32LE(&s->name)]; + sym->binding = (s->info >> 4); + sym->type = type; + sym->value = READ32LE(&s->value); + sym->size = READ32LE(&s->size); + elfSymbolsCount++; + } + } + elfSymbolsStrTab = strtable; + // free(symtab); +} + +bool elfReadProgram(ELFHeader *eh, u8 *data, int& size, bool parseDebug) +{ + int count = READ16LE(&eh->e_phnum); + int i; + + if(READ32LE(&eh->e_entry) == 0x2000000) + cpuIsMultiBoot = true; + + // read program headers... should probably move this code down + u8 *p = data + READ32LE(&eh->e_phoff); + size = 0; + for(i = 0; i < count; i++) { + ELFProgramHeader *ph = (ELFProgramHeader *)p; + p += sizeof(ELFProgramHeader); + if(READ16LE(&eh->e_phentsize) != sizeof(ELFProgramHeader)) { + p += READ16LE(&eh->e_phentsize) - sizeof(ELFProgramHeader); + } + + // printf("PH %d %08x %08x %08x %08x %08x %08x %08x %08x\n", + // i, ph->type, ph->offset, ph->vaddr, ph->paddr, + // ph->filesz, ph->memsz, ph->flags, ph->align); + if(cpuIsMultiBoot) { + if(READ32LE(&ph->paddr) >= 0x2000000 && + READ32LE(&ph->paddr) <= 0x203ffff) { + memcpy(&workRAM[READ32LE(&ph->paddr) & 0x3ffff], + data + READ32LE(&ph->offset), + READ32LE(&ph->filesz)); + size += READ32LE(&ph->filesz); + } + } else { + if(READ32LE(&ph->paddr) >= 0x8000000 && + READ32LE(&ph->paddr) <= 0x9ffffff) { + memcpy(&rom[READ32LE(&ph->paddr) & 0x1ffffff], + data + READ32LE(&ph->offset), + READ32LE(&ph->filesz)); + size += READ32LE(&ph->filesz); + } + } + } + + char *stringTable = NULL; + + // read section headers + p = data + READ32LE(&eh->e_shoff); + count = READ16LE(&eh->e_shnum); + + ELFSectionHeader **sh = (ELFSectionHeader **) + malloc(sizeof(ELFSectionHeader *) * count); + + for(i = 0; i < count; i++) { + sh[i] = (ELFSectionHeader *)p; + p += sizeof(ELFSectionHeader); + if(READ16LE(&eh->e_shentsize) != sizeof(ELFSectionHeader)) + p += READ16LE(&eh->e_shentsize) - sizeof(ELFSectionHeader); + } + + if(READ16LE(&eh->e_shstrndx) != 0) { + stringTable = (char *)elfReadSection(data, + sh[READ16LE(&eh->e_shstrndx)]); + } + + elfSectionHeaders = sh; + elfSectionHeadersStringTable = stringTable; + elfSectionHeadersCount = count; + + for(i = 0; i < count; i++) { + // printf("SH %d %-20s %08x %08x %08x %08x %08x %08x %08x %08x\n", + // i, &stringTable[sh[i]->name], sh[i]->name, sh[i]->type, + // sh[i]->flags, sh[i]->addr, sh[i]->offset, sh[i]->size, + // sh[i]->link, sh[i]->info); + if(READ32LE(&sh[i]->flags) & 2) { // load section + if(cpuIsMultiBoot) { + if(READ32LE(&sh[i]->addr) >= 0x2000000 && + READ32LE(&sh[i]->addr) <= 0x203ffff) { + memcpy(&workRAM[READ32LE(&sh[i]->addr) & 0x3ffff], data + + READ32LE(&sh[i]->offset), + READ32LE(&sh[i]->size)); + size += READ32LE(&sh[i]->size); + } + } else { + if(READ32LE(&sh[i]->addr) >= 0x8000000 && + READ32LE(&sh[i]->addr) <= 0x9ffffff) { + memcpy(&rom[READ32LE(&sh[i]->addr) & 0x1ffffff], + data + READ32LE(&sh[i]->offset), + READ32LE(&sh[i]->size)); + size += READ32LE(&sh[i]->size); + } + } + } + } + + if(parseDebug) { + fprintf(stderr, "Parsing debug info\n"); + + ELFSectionHeader *dbgHeader = elfGetSectionByName(".debug_info"); + if(dbgHeader == NULL) { + fprintf(stderr, "Cannot find debug information\n"); + goto end; + } + + ELFSectionHeader *h = elfGetSectionByName(".debug_abbrev"); + if(h == NULL) { + fprintf(stderr, "Cannot find abbreviation table\n"); + goto end; + } + + elfDebugInfo = (DebugInfo *)calloc(sizeof(DebugInfo), 1); + u8 *abbrevdata = elfReadSection(data, h); + + h = elfGetSectionByName(".debug_str"); + + if(h == NULL) + elfDebugStrings = NULL; + else + elfDebugStrings = (char *)elfReadSection(data, h); + + u8 *debugdata = elfReadSection(data, dbgHeader); + + elfDebugInfo->debugdata = data; + elfDebugInfo->infodata = debugdata; + + u32 total = READ32LE(&dbgHeader->size); + u8 *end = debugdata + total; + u8 *ddata = debugdata; + + CompileUnit *last = NULL; + CompileUnit *unit = NULL; + + while(ddata < end) { + unit = elfParseCompUnit(ddata, abbrevdata); + unit->offset = (u32)(ddata-debugdata); + elfParseLineInfo(unit, data); + if(last == NULL) + elfCompileUnits = unit; + else + last->next = unit; + last = unit; + ddata += 4 + unit->length; + } + elfParseAranges(data); + CompileUnit *comp = elfCompileUnits; + while(comp) { + ARanges *r = elfDebugInfo->ranges; + for(int i = 0; i < elfDebugInfo->numRanges; i++) + if(r[i].offset == comp->offset) { + comp->ranges = &r[i]; + break; + } + comp = comp->next; + } + elfParseCFA(data); + elfReadSymtab(data); + } + end: + if(sh) { + free(sh); + } + + elfSectionHeaders = NULL; + elfSectionHeadersStringTable = NULL; + elfSectionHeadersCount = 0; + + return true; +} + +extern bool parseDebug; + +bool elfRead(const char *name, int& siz, FILE *f) +{ + fseek(f, 0, SEEK_END); + long size = ftell(f); + elfFileData = (u8 *)malloc(size); + fseek(f, 0, SEEK_SET); + int res = fread(elfFileData, 1, size, f); + fclose(f); + + if (res < 0) + { + free(elfFileData); + elfFileData = NULL; + return false; + } + + ELFHeader *header = (ELFHeader *)elfFileData; + + if(READ32LE(&header->magic) != 0x464C457F || + READ16LE(&header->e_machine) != 40 || + header->clazz != 1) { + systemMessage(0, N_("Not a valid ELF file %s"), name); + free(elfFileData); + elfFileData = NULL; + return false; + } + + if(!elfReadProgram(header, elfFileData, siz, parseDebug)) { + free(elfFileData); + elfFileData = NULL; + return false; + } + + return true; +} + +void elfCleanUp(Object *o) +{ + free(o->location); +} + +void elfCleanUp(Function *func) +{ + Object *o = func->parameters; + while(o) { + elfCleanUp(o); + Object *next = o->next; + free(o); + o = next; + } + + o = func->variables; + while(o) { + elfCleanUp(o); + Object *next = o->next; + free(o); + o = next; + } + free(func->frameBase); +} + +void elfCleanUp(ELFAbbrev **abbrevs) +{ + for(int i = 0; i < 121; i++) { + ELFAbbrev *abbrev = abbrevs[i]; + + while(abbrev) { + free(abbrev->attrs); + ELFAbbrev *next = abbrev->next; + free(abbrev); + + abbrev = next; + } + } +} + +void elfCleanUp(Type *t) +{ + switch(t->type) { + case TYPE_function: + if(t->function) { + Object *o = t->function->args; + while(o) { + elfCleanUp(o); + Object *next = o->next; + free(o); + o = next; + } + free(t->function); + } + break; + case TYPE_array: + if(t->array) { + free(t->array->bounds); + free(t->array); + } + break; + case TYPE_struct: + case TYPE_union: + if(t->structure) { + for(int i = 0; i < t->structure->memberCount; i++) { + free(t->structure->members[i].location); + } + free(t->structure->members); + free(t->structure); + } + break; + case TYPE_enum: + if(t->enumeration) { + free(t->enumeration->members); + free(t->enumeration); + } + break; + case TYPE_base: + case TYPE_pointer: + case TYPE_void: + case TYPE_reference: + break; // nothing to do + } +} + +void elfCleanUp(CompileUnit *comp) +{ + elfCleanUp(comp->abbrevs); + free(comp->abbrevs); + Function *func = comp->functions; + while(func) { + elfCleanUp(func); + Function *next = func->next; + free(func); + func = next; + } + Type *t = comp->types; + while(t) { + elfCleanUp(t); + Type *next = t->next; + free(t); + t = next; + } + Object *o = comp->variables; + while(o) { + elfCleanUp(o); + Object *next = o->next; + free(o); + o = next; + } + if(comp->lineInfoTable) { + free(comp->lineInfoTable->lines); + free(comp->lineInfoTable->files); + free(comp->lineInfoTable); + } +} + +void elfCleanUp() +{ + CompileUnit *comp = elfCompileUnits; + + while(comp) { + elfCleanUp(comp); + CompileUnit *next = comp->next; + free(comp); + comp = next; + } + elfCompileUnits = NULL; + free(elfSymbols); + elfSymbols = NULL; + // free(elfSymbolsStrTab); + elfSymbolsStrTab = NULL; + + elfDebugStrings = NULL; + if(elfDebugInfo) { + int num = elfDebugInfo->numRanges; + int i; + for(i = 0; i < num; i++) { + free(elfDebugInfo->ranges[i].ranges); + } + free(elfDebugInfo->ranges); + free(elfDebugInfo); + elfDebugInfo = NULL; + } + + if(elfFdes) { + if(elfFdeCount) { + for(int i = 0; i < elfFdeCount; i++) + free(elfFdes[i]); + } + free(elfFdes); + + elfFdes = NULL; + elfFdeCount = 0; + } + + ELFcie *cie = elfCies; + while(cie) { + ELFcie *next = cie->next; + free(cie); + cie = next; + } + elfCies = NULL; + + if(elfFileData) { + free(elfFileData); + elfFileData = NULL; + } +} diff --git a/src/gba/elf.h b/src/gba/elf.h new file mode 100644 index 0000000..6c28e6c --- /dev/null +++ b/src/gba/elf.h @@ -0,0 +1,265 @@ +#ifndef ELF_H +#define ELF_H + +enum LocationType { + LOCATION_register, + LOCATION_memory, + LOCATION_value +}; + +#define DW_ATE_boolean 0x02 +#define DW_ATE_signed 0x05 +#define DW_ATE_unsigned 0x07 +#define DW_ATE_unsigned_char 0x08 + +struct ELFHeader { + u32 magic; + u8 clazz; + u8 data; + u8 version; + u8 pad[9]; + u16 e_type; + u16 e_machine; + u32 e_version; + u32 e_entry; + u32 e_phoff; + u32 e_shoff; + u32 e_flags; + u16 e_ehsize; + u16 e_phentsize; + u16 e_phnum; + u16 e_shentsize; + u16 e_shnum; + u16 e_shstrndx; +}; + +struct ELFProgramHeader { + u32 type; + u32 offset; + u32 vaddr; + u32 paddr; + u32 filesz; + u32 memsz; + u32 flags; + u32 align; +}; + +struct ELFSectionHeader { + u32 name; + u32 type; + u32 flags; + u32 addr; + u32 offset; + u32 size; + u32 link; + u32 info; + u32 addralign; + u32 entsize; +}; + +struct ELFSymbol { + u32 name; + u32 value; + u32 size; + u8 info; + u8 other; + u16 shndx; +}; + +struct ELFBlock { + int length; + u8 *data; +}; + +struct ELFAttr { + u32 name; + u32 form; + union { + u32 value; + char *string; + u8 *data; + bool flag; + ELFBlock *block; + }; +}; + +struct ELFAbbrev { + u32 number; + u32 tag; + bool hasChildren; + int numAttrs; + ELFAttr *attrs; + ELFAbbrev *next; +}; + +enum TypeEnum { + TYPE_base, + TYPE_pointer, + TYPE_function, + TYPE_void, + TYPE_array, + TYPE_struct, + TYPE_reference, + TYPE_enum, + TYPE_union +}; + +struct Type; +struct Object; + +struct FunctionType { + Type *returnType; + Object *args; +}; + +struct Member { + char *name; + Type *type; + int bitSize; + int bitOffset; + int byteSize; + ELFBlock *location; +}; + +struct Struct { + int memberCount; + Member *members; +}; + +struct Array { + Type *type; + int maxBounds; + int *bounds; +}; + +struct EnumMember { + char *name; + u32 value; +}; + +struct Enum { + int count; + EnumMember *members; +}; + +struct Type { + u32 offset; + TypeEnum type; + const char *name; + int encoding; + int size; + int bitSize; + union { + Type *pointer; + FunctionType *function; + Array *array; + Struct *structure; + Enum *enumeration; + }; + Type *next; +}; + +struct Object { + char *name; + int file; + int line; + bool external; + Type *type; + ELFBlock *location; + u32 startScope; + u32 endScope; + Object *next; +}; + +struct Function { + char *name; + u32 lowPC; + u32 highPC; + int file; + int line; + bool external; + Type *returnType; + Object *parameters; + Object *variables; + ELFBlock *frameBase; + Function *next; +}; + +struct LineInfoItem { + u32 address; + char *file; + int line; +}; + +struct LineInfo { + int fileCount; + char **files; + int number; + LineInfoItem *lines; +}; + +struct ARange { + u32 lowPC; + u32 highPC; +}; + +struct ARanges { + u32 offset; + int count; + ARange *ranges; +}; + +struct CompileUnit { + u32 length; + u8 *top; + u32 offset; + ELFAbbrev **abbrevs; + ARanges *ranges; + char *name; + char *compdir; + u32 lowPC; + u32 highPC; + bool hasLineInfo; + u32 lineInfo; + LineInfo *lineInfoTable; + Function *functions; + Function *lastFunction; + Object *variables; + Type *types; + CompileUnit *next; +}; + +struct DebugInfo { + u8 *debugfile; + u8 *abbrevdata; + u8 *debugdata; + u8 *infodata; + int numRanges; + ARanges *ranges; +}; + +struct Symbol { + const char *name; + int type; + int binding; + u32 address; + u32 value; + u32 size; +}; + +extern u32 elfReadLEB128(u8 *, int *); +extern s32 elfReadSignedLEB128(u8 *, int *); +extern bool elfRead(const char *, int &, FILE *f); +extern bool elfGetSymbolAddress(const char *,u32 *, u32 *, int *); +extern const char *elfGetAddressSymbol(u32); +extern const char *elfGetSymbol(int, u32 *, u32 *, int *); +extern void elfCleanUp(); +extern bool elfGetCurrentFunction(u32, Function **, CompileUnit **c); +extern bool elfGetObject(const char *, Function *, CompileUnit *, Object **); +extern bool elfFindLineInUnit(u32 *, CompileUnit *, int); +extern bool elfFindLineInModule(u32 *, const char *, int); +u32 elfDecodeLocation(Function *, ELFBlock *, LocationType *); +u32 elfDecodeLocation(Function *, ELFBlock *, LocationType *, u32); +int elfFindLine(CompileUnit *unit, Function *func, u32 addr, const char **); + +#endif // ELF_H diff --git a/src/gba/gbafilter.cpp b/src/gba/gbafilter.cpp new file mode 100644 index 0000000..523ec82 --- /dev/null +++ b/src/gba/gbafilter.cpp @@ -0,0 +1,227 @@ +#include "gbafilter.h" + +#include + +extern int systemColorDepth; +extern int systemRedShift; +extern int systemGreenShift; +extern int systemBlueShift; + +extern u16 systemColorMap16[0x10000]; +extern u32 systemColorMap32[0x10000]; + +static const unsigned char curve[32] = { 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0e, 0x10, 0x12, + 0x14, 0x16, 0x18, 0x1c, 0x20, 0x28, 0x30, 0x38, + 0x40, 0x48, 0x50, 0x58, 0x60, 0x68, 0x70, 0x80, + 0x88, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0}; + +// output R G B +static const unsigned char influence[3 * 3] = { 16, 4, 4, // red + 8, 16, 8, // green + 0, 8, 16};// blue + +inline void swap(short & a, short & b) +{ + short temp = a; + a = b; + b = temp; +} + +void gbafilter_pal(u16 * buf, int count) +{ + short temp[3 * 3], s; + unsigned pix; + u8 red, green, blue; + + while (count--) + { + pix = *buf; + + s = curve[(pix >> systemGreenShift) & 0x1f]; + temp[3] = s * influence[3]; + temp[4] = s * influence[4]; + temp[5] = s * influence[5]; + + s = curve[(pix >> systemRedShift) & 0x1f]; + temp[0] = s * influence[0]; + temp[1] = s * influence[1]; + temp[2] = s * influence[2]; + + s = curve[(pix >> systemBlueShift) & 0x1f]; + temp[6] = s * influence[6]; + temp[7] = s * influence[7]; + temp[8] = s * influence[8]; + + if (temp[0] < temp[3]) swap(temp[0], temp[3]); + if (temp[0] < temp[6]) swap(temp[0], temp[6]); + if (temp[3] < temp[6]) swap(temp[3], temp[6]); + temp[3] <<= 1; + temp[0] <<= 2; + temp[0] += temp[3] + temp[6]; + + red = ((int(temp[0]) * 160) >> 17) + 4; + if (red > 31) red = 31; + + if (temp[2] < temp[5]) swap(temp[2], temp[5]); + if (temp[2] < temp[8]) swap(temp[2], temp[8]); + if (temp[5] < temp[8]) swap(temp[5], temp[8]); + temp[5] <<= 1; + temp[2] <<= 2; + temp[2] += temp[5] + temp[8]; + + blue = ((int(temp[2]) * 160) >> 17) + 4; + if (blue > 31) blue = 31; + + if (temp[1] < temp[4]) swap(temp[1], temp[4]); + if (temp[1] < temp[7]) swap(temp[1], temp[7]); + if (temp[4] < temp[7]) swap(temp[4], temp[7]); + temp[4] <<= 1; + temp[1] <<= 2; + temp[1] += temp[4] + temp[7]; + + green = ((int(temp[1]) * 160) >> 17) + 4; + if (green > 31) green = 31; + + pix = red << systemRedShift; + pix += green << systemGreenShift; + pix += blue << systemBlueShift; + + *buf++ = pix; + } +} + +void gbafilter_pal32(u32 * buf, int count) +{ + short temp[3 * 3], s; + unsigned pix; + u8 red, green, blue; + + while (count--) + { + pix = *buf; + + s = curve[(pix >> systemGreenShift) & 0x1f]; + temp[3] = s * influence[3]; + temp[4] = s * influence[4]; + temp[5] = s * influence[5]; + + s = curve[(pix >> systemRedShift) & 0x1f]; + temp[0] = s * influence[0]; + temp[1] = s * influence[1]; + temp[2] = s * influence[2]; + + s = curve[(pix >> systemBlueShift) & 0x1f]; + temp[6] = s * influence[6]; + temp[7] = s * influence[7]; + temp[8] = s * influence[8]; + + if (temp[0] < temp[3]) swap(temp[0], temp[3]); + if (temp[0] < temp[6]) swap(temp[0], temp[6]); + if (temp[3] < temp[6]) swap(temp[3], temp[6]); + temp[3] <<= 1; + temp[0] <<= 2; + temp[0] += temp[3] + temp[6]; + + //red = ((int(temp[0]) * 160) >> 17) + 4; + red = ((int(temp[0]) * 160) >> 14) + 32; + + if (temp[2] < temp[5]) swap(temp[2], temp[5]); + if (temp[2] < temp[8]) swap(temp[2], temp[8]); + if (temp[5] < temp[8]) swap(temp[5], temp[8]); + temp[5] <<= 1; + temp[2] <<= 2; + temp[2] += temp[5] + temp[8]; + + //blue = ((int(temp[2]) * 160) >> 17) + 4; + blue = ((int(temp[2]) * 160) >> 14) + 32; + + if (temp[1] < temp[4]) swap(temp[1], temp[4]); + if (temp[1] < temp[7]) swap(temp[1], temp[7]); + if (temp[4] < temp[7]) swap(temp[4], temp[7]); + temp[4] <<= 1; + temp[1] <<= 2; + temp[1] += temp[4] + temp[7]; + + //green = ((int(temp[1]) * 160) >> 17) + 4; + green = ((int(temp[1]) * 160) >> 14) + 32; + + //pix = red << redshift; + //pix += green << greenshift; + //pix += blue << blueshift; + + pix = red << (systemRedShift - 3); + pix += green << (systemGreenShift - 3); + pix += blue << (systemBlueShift - 3); + + *buf++ = pix; + } +} + +// for palette mode to work with the three spoony filters in 32bpp depth + +void gbafilter_pad(u8 * buf, int count) +{ + union + { + struct + { + u8 r; + u8 g; + u8 b; + u8 a; + } part; + unsigned whole; + } + mask; + + mask.whole = 0x1f << systemRedShift; + mask.whole += 0x1f << systemGreenShift; + mask.whole += 0x1f << systemBlueShift; + + switch (systemColorDepth) + { + case 24: + while (count--) + { + *buf++ &= mask.part.r; + *buf++ &= mask.part.g; + *buf++ &= mask.part.b; + } + break; + case 32: + while (count--) + { + *((u32*)buf) &= mask.whole; + buf += 4; + } + } +} + +/* +void UpdateSystemColorMaps(int lcd) +{ + switch(systemColorDepth) { + case 16: + { + for(int i = 0; i < 0x10000; i++) { + systemColorMap16[i] = ((i & 0x1f) << systemRedShift) | + (((i & 0x3e0) >> 5) << systemGreenShift) | + (((i & 0x7c00) >> 10) << systemBlueShift); + } + if (lcd == 1) gbafilter_pal(systemColorMap16, 0x10000); + } + break; + case 24: + case 32: + { + for(int i = 0; i < 0x10000; i++) { + systemColorMap32[i] = ((i & 0x1f) << systemRedShift) | + (((i & 0x3e0) >> 5) << systemGreenShift) | + (((i & 0x7c00) >> 10) << systemBlueShift); + } + if (lcd == 1) gbafilter_pal32(systemColorMap32, 0x10000); + } + break; + } +} +*/ diff --git a/src/gba/gbafilter.h b/src/gba/gbafilter.h new file mode 100644 index 0000000..18ff53d --- /dev/null +++ b/src/gba/gbafilter.h @@ -0,0 +1,5 @@ +#include "../System.h" + +void gbafilter_pal(u16 * buf, int count); +void gbafilter_pal32(u32 * buf, int count); +void gbafilter_pad(u8 * buf, int count); diff --git a/src/gba/remote.cpp b/src/gba/remote.cpp new file mode 100644 index 0000000..6e31edf --- /dev/null +++ b/src/gba/remote.cpp @@ -0,0 +1,727 @@ +#ifndef __LIBRETRO__ +#include +#include +#include + +#ifndef _WIN32 +# include +# include +# include +# ifdef HAVE_NETINET_IN_H +# include +# endif // HAVE_NETINET_IN_H +# ifdef HAVE_ARPA_INET_H +# include +# else // ! HAVE_ARPA_INET_H +# define socklen_t int +# endif // ! HAVE_ARPA_INET_H +# define SOCKET int +#else // _WIN32 +# include +# include +# define socklen_t int +# define close closesocket +# define read _read +# define write _write +#endif // _WIN32 + +#include "GBA.h" + +extern bool debugger; +extern void CPUUpdateCPSR(); +#ifdef SDL +extern void (*dbgMain)(); +extern void debuggerMain(); +extern void debuggerSignal(int,int); +#endif + +int remotePort = 55555; +int remoteSignal = 5; +SOCKET remoteSocket = -1; +SOCKET remoteListenSocket = -1; +bool remoteConnected = false; +bool remoteResumed = false; + +int (*remoteSendFnc)(char *, int) = NULL; +int (*remoteRecvFnc)(char *, int) = NULL; +bool (*remoteInitFnc)() = NULL; +void (*remoteCleanUpFnc)() = NULL; + +#ifndef SDL +void remoteSetSockets(SOCKET l, SOCKET r) +{ + remoteSocket = r; + remoteListenSocket = l; +} +#endif + +int remoteTcpSend(char *data, int len) +{ + return send(remoteSocket, data, len, 0); +} + +int remoteTcpRecv(char *data, int len) +{ + return recv(remoteSocket, data, len, 0); +} + +bool remoteTcpInit() +{ + if(remoteSocket == -1) { +#ifdef _WIN32 + WSADATA wsaData; + int error = WSAStartup(MAKEWORD(1,1),&wsaData); +#endif // _WIN32 + SOCKET s = socket(PF_INET, SOCK_STREAM, 0); + + remoteListenSocket = s; + +#ifdef _WIN32 + if(s == INVALID_SOCKET) { +#else + if(s < 0) { +#endif + fprintf(stderr,"Error opening socket\n"); + exit(-1); + } + int tmp = 1; + setsockopt (s, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp, sizeof (tmp)); + + // char hostname[256]; + // gethostname(hostname, 256); + + // hostent *ent = gethostbyname(hostname); + // unsigned long a = *((unsigned long *)ent->h_addr); + + sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(remotePort); + addr.sin_addr.s_addr = htonl(0); + int count = 0; + while(count < 3) { + if(bind(s, (sockaddr *)&addr, sizeof(addr))) { + addr.sin_port = htons(ntohs(addr.sin_port)+1); + } else + break; + } + if(count == 3) { + fprintf(stderr,"Error binding \n"); + exit(-1); + } + + fprintf(stderr,"Listening for a connection at port %d\n", + ntohs(addr.sin_port)); + + if(listen(s, 1)) { + fprintf(stderr, "Error listening\n"); + exit(-1); + } + socklen_t len = sizeof(addr); + +#ifdef _WIN32 + int flag = 0; + ioctlsocket(s, FIONBIO, (unsigned long *)&flag); +#endif // _WIN32 + SOCKET s2 = accept(s, (sockaddr *)&addr, &len); + if(s2 > 0) { + fprintf(stderr, "Got a connection from %s %d\n", + inet_ntoa((in_addr)addr.sin_addr), + ntohs(addr.sin_port)); + } else { +#ifdef _WIN32 + int error = WSAGetLastError(); +#endif // _WIN32 + } + //char dummy; + //recv(s2, &dummy, 1, 0); + //if(dummy != '+') { + // fprintf(stderr, "ACK not received\n"); + // exit(-1); + //} + remoteSocket = s2; + // close(s); + } + return true; +} + +void remoteTcpCleanUp() +{ + if(remoteSocket > 0) { + fprintf(stderr, "Closing remote socket\n"); + close(remoteSocket); + remoteSocket = -1; + } + if(remoteListenSocket > 0) { + fprintf(stderr, "Closing listen socket\n"); + close(remoteListenSocket); + remoteListenSocket = -1; + } +} + +int remotePipeSend(char *data, int len) +{ + int res = write(1, data, len); + return res; +} + +int remotePipeRecv(char *data, int len) +{ + int res = read(0, data, len); + return res; +} + +bool remotePipeInit() +{ +// char dummy; +// if (read(0, &dummy, 1) == 1) +// { +// if(dummy != '+') { +// fprintf(stderr, "ACK not received\n"); +// exit(-1); +// } +// } + + return true; +} + +void remotePipeCleanUp() +{ +} + +void remoteSetPort(int port) +{ + remotePort = port; +} + +void remoteSetProtocol(int p) +{ + if(p == 0) { + remoteSendFnc = remoteTcpSend; + remoteRecvFnc = remoteTcpRecv; + remoteInitFnc = remoteTcpInit; + remoteCleanUpFnc = remoteTcpCleanUp; + } else { + remoteSendFnc = remotePipeSend; + remoteRecvFnc = remotePipeRecv; + remoteInitFnc = remotePipeInit; + remoteCleanUpFnc = remotePipeCleanUp; + } +} + +void remoteInit() +{ + if(remoteInitFnc) + remoteInitFnc(); +} + +void remotePutPacket(const char *packet) +{ + const char *hex = "0123456789abcdef"; + char buffer[1024]; + + size_t count = strlen(packet); + + unsigned char csum = 0; + + char *p = buffer; + *p++ = '$'; + + for(size_t i = 0 ;i < count; i++) { + csum += packet[i]; + *p++ = packet[i]; + } + *p++ = '#'; + *p++ = hex[csum>>4]; + *p++ = hex[csum & 15]; + *p++ = 0; + // printf("Sending %s\n", buffer); + + char c = 0; + while(c != '+'){ + remoteSendFnc(buffer, (int)count + 4); + if(remoteRecvFnc(&c, 1) < 0) + return; +// fprintf(stderr,"sent:%s recieved:%c\n",buffer,c); + } +} + +#define debuggerReadMemory(addr) \ + (*(u32*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask]) + +#define debuggerReadHalfWord(addr) \ + (*(u16*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask]) + +#define debuggerReadByte(addr) \ + map[(addr)>>24].address[(addr) & map[(addr)>>24].mask] + +#define debuggerWriteMemory(addr, value) \ + *(u32*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask] = (value) + +#define debuggerWriteHalfWord(addr, value) \ + *(u16*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask] = (value) + +#define debuggerWriteByte(addr, value) \ + map[(addr)>>24].address[(addr) & map[(addr)>>24].mask] = (value) + +void remoteOutput(const char *s, u32 addr) +{ + char buffer[16384]; + + char *d = buffer; + *d++ = 'O'; + + if(s) { + char c = *s++; + while(c) { + sprintf(d, "%02x", c); + d += 2; + c = *s++; + } + } else { + char c= debuggerReadByte(addr); + addr++; + while(c) { + sprintf(d, "%02x", c); + d += 2; + c = debuggerReadByte(addr); + addr++; + } + } + remotePutPacket(buffer); + // fprintf(stderr, "Output sent %s\n", buffer); +} + +void remoteSendSignal() +{ + char buffer[1024]; + sprintf(buffer, "S%02x", remoteSignal); + remotePutPacket(buffer); +} + +void remoteSendStatus() +{ + char buffer[1024]; + sprintf(buffer, "T%02x", remoteSignal); + char *s = buffer; + s += 3; + for(int i = 0; i < 15; i++) { + u32 v = reg[i].I; + sprintf(s, "%02x:%02x%02x%02x%02x;",i, + (v & 255), + (v >> 8) & 255, + (v >> 16) & 255, + (v >> 24) & 255); + s += 12; + } + u32 v = armNextPC; + sprintf(s, "0f:%02x%02x%02x%02x;", (v & 255), + (v >> 8) & 255, + (v >> 16) & 255, + (v >> 24) & 255); + s += 12; + CPUUpdateCPSR(); + v = reg[16].I; + sprintf(s, "19:%02x%02x%02x%02x;", (v & 255), + (v >> 8) & 255, + (v >> 16) & 255, + (v >> 24) & 255); + s += 12; + *s = 0; + // printf("Sending %s\n", buffer); + remotePutPacket(buffer); +} + +void remoteBinaryWrite(char *p) +{ + u32 address; + int count; + sscanf(p,"%x,%x:", &address, &count); + // printf("Binary write for %08x %d\n", address, count); + + p = strchr(p, ':'); + p++; + for(int i = 0; i < count; i++) { + u8 b = *p++; + switch(b) { + case 0x7d: + b = *p++; + debuggerWriteByte(address, (b^0x20)); + address++; + break; + default: + debuggerWriteByte(address, b); + address++; + break; + } + } + // printf("ROM is %08x\n", debuggerReadMemory(0x8000254)); + remotePutPacket("OK"); +} + +void remoteMemoryWrite(char *p) +{ + u32 address; + int count; + sscanf(p,"%x,%x:", &address, &count); + // printf("Memory write for %08x %d\n", address, count); + + p = strchr(p, ':'); + p++; + for(int i = 0; i < count; i++) { + u8 v = 0; + char c = *p++; + if(c <= '9') + v = (c - '0') << 4; + else + v = (c + 10 - 'a') << 4; + c = *p++; + if(c <= '9') + v += (c - '0'); + else + v += (c + 10 - 'a'); + debuggerWriteByte(address, v); + address++; + } + // printf("ROM is %08x\n", debuggerReadMemory(0x8000254)); + remotePutPacket("OK"); +} + +void remoteMemoryRead(char *p) +{ + u32 address; + int count; + sscanf(p,"%x,%x:", &address, &count); + // printf("Memory read for %08x %d\n", address, count); + + char buffer[1024]; + + char *s = buffer; + for(int i = 0; i < count; i++) { + u8 b = debuggerReadByte(address); + sprintf(s, "%02x", b); + address++; + s += 2; + } + *s = 0; + remotePutPacket(buffer); +} + +void remoteStepOverRange(char *p) +{ + u32 address; + u32 final; + sscanf(p, "%x,%x", &address, &final); + + remotePutPacket("OK"); + + remoteResumed = true; + do { + CPULoop(1); + if(debugger) + break; + } while(armNextPC >= address && armNextPC < final); + + remoteResumed = false; + + remoteSendStatus(); +} + +void remoteWriteWatch(char *p, bool active) +{ + u32 address; + int count; + sscanf(p, ",%x,%x#", &address, &count); + + fprintf(stderr, "Write watch for %08x %d\n", address, count); + + if(address < 0x2000000 || address > 0x3007fff) { + remotePutPacket("E01"); + return; + } + + if(address > 0x203ffff && address < 0x3000000) { + remotePutPacket("E01"); + return; + } + + u32 final = address + count; + + if(address < 0x2040000 && final > 0x2040000) { + remotePutPacket("E01"); + return; + } else if(address < 0x3008000 && final > 0x3008000) { + remotePutPacket("E01"); + return; + } + +#ifdef BKPT_SUPPORT + for(int i = 0; i < count; i++) { + if((address >> 24) == 2) + freezeWorkRAM[address & 0x3ffff] = active; + else + freezeInternalRAM[address & 0x7fff] = active; + address++; + } +#endif + + remotePutPacket("OK"); +} + +void remoteReadRegisters(char *p) +{ + char buffer[1024]; + + char *s = buffer; + int i; + // regular registers + for(i = 0; i < 15; i++) { + u32 v = reg[i].I; + sprintf(s, "%02x%02x%02x%02x", v & 255, (v >> 8) & 255, + (v >> 16) & 255, (v >> 24) & 255); + s += 8; + } + // PC + u32 pc = armNextPC; + sprintf(s, "%02x%02x%02x%02x", pc & 255, (pc >> 8) & 255, + (pc >> 16) & 255, (pc >> 24) & 255); + s += 8; + + // floating point registers (24-bit) + for(i = 0; i < 8; i++) { + sprintf(s, "000000000000000000000000"); + s += 24; + } + + // FP status register + sprintf(s, "00000000"); + s += 8; + // CPSR + CPUUpdateCPSR(); + u32 v = reg[16].I; + sprintf(s, "%02x%02x%02x%02x", v & 255, (v >> 8) & 255, + (v >> 16) & 255, (v >> 24) & 255); + s += 8; + *s = 0; + remotePutPacket(buffer); +} + +void remoteWriteRegister(char *p) +{ + int r; + + sscanf(p, "%x=", &r); + + p = strchr(p, '='); + p++; + + char c = *p++; + + u32 v = 0; + + u8 data[4] = {0,0,0,0}; + + int i = 0; + + while(c != '#') { + u8 b = 0; + if(c <= '9') + b = (c - '0') << 4; + else + b = (c + 10 - 'a') << 4; + c = *p++; + if(c <= '9') + b += (c - '0'); + else + b += (c + 10 - 'a'); + data[i++] = b; + c = *p++; + } + + v = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); + + // printf("Write register %d=%08x\n", r, v); + reg[r].I = v; + if(r == 15) { + armNextPC = v; + if(armState) + reg[15].I = v + 4; + else + reg[15].I = v + 2; + } + remotePutPacket("OK"); +} + +extern int emulating; + +void remoteStubMain() +{ + if(!debugger) + return; + + if(remoteResumed) { + remoteSendStatus(); + remoteResumed = false; + } + + const char *hex = "0123456789abcdef"; + while(1) { + char ack; + char buffer[1024]; + int res = remoteRecvFnc(buffer, 1024); + + if(res == -1) { + fprintf(stderr, "GDB connection lost\n"); +#ifdef SDL + dbgMain = debuggerMain; + dbgSignal = debuggerSignal; +#endif + debugger = false; + break; + } else if(res == -2) + break; + if(res < 1024){ + buffer[res] = 0; + }else{ + fprintf(stderr, "res=%d\n",res); + } + +// fprintf(stderr, "res=%d Received %s\n",res, buffer); + char c = buffer[0]; + char *p = &buffer[0]; + int i = 0; + unsigned char csum = 0; + while(i < res){ + if(buffer[i] == '$'){ + i++; + csum = 0; + c = buffer[i]; + p = &buffer[i+1]; + while((i>4]) && (buffer[i+2] == hex[csum & 0xf])){ + ack = '+'; + remoteSendFnc(&ack, 1); + //fprintf(stderr, "SentACK c=%c\n",c); + //process message... + switch(c) { + case '?': + remoteSendSignal(); + break; + case 'D': + remotePutPacket("OK"); +#ifdef SDL + dbgMain = debuggerMain; + dbgSignal = debuggerSignal; +#endif + remoteResumed = true; + debugger = false; + return; + case 'e': + remoteStepOverRange(p); + break; + case 'k': + remotePutPacket("OK"); +#ifdef SDL + dbgMain = debuggerMain; + dbgSignal = debuggerSignal; +#endif + debugger = false; + emulating = false; + return; + case 'C': + remoteResumed = true; + debugger = false; + return; + case 'c': + remoteResumed = true; + debugger = false; + return; + case 's': + remoteResumed = true; + remoteSignal = 5; + CPULoop(1); + if(remoteResumed) { + remoteResumed = false; + remoteSendStatus(); + } + break; + case 'g': + remoteReadRegisters(p); + break; + case 'P': + remoteWriteRegister(p); + break; + case 'M': + remoteMemoryWrite(p); + break; + case 'm': + remoteMemoryRead(p); + break; + case 'X': + remoteBinaryWrite(p); + break; + case 'H': + remotePutPacket("OK"); + break; + case 'q': + remotePutPacket(""); + break; + case 'Z': + if(*p++ == '2') { + remoteWriteWatch(p, true); + } else + remotePutPacket(""); + break; + case 'z': + if(*p++ == '2') { + remoteWriteWatch(p, false); + } else + remotePutPacket(""); + break; + default: + { + fprintf(stderr, "Unknown packet %s\n", --p); + remotePutPacket(""); + } + break; + } + }else{ + fprintf(stderr, "bad chksum csum=%x msg=%c%c\n",csum,buffer[i+1],buffer[i+2]); + ack = '-'; + remoteSendFnc(&ack, 1); + fprintf(stderr, "SentNACK\n"); + }//if + i+=3; + }else{ + fprintf(stderr, "didn't receive chksum i=%d res=%d\n",i,res); + i++; + }//if + }else{ + if(buffer[i] != '+'){ //ingnore ACKs + fprintf(stderr, "not sure what to do with:%c i=%d res=%d\n",buffer[i],i,res); + } + i++; + }//if + }//while + } +} + +void remoteStubSignal(int sig, int number) +{ + remoteSignal = sig; + remoteResumed = false; + remoteSendStatus(); + debugger = true; +} + +void remoteCleanUp() +{ + if(remoteCleanUpFnc) + remoteCleanUpFnc(); +} +#endif diff --git a/src/gtk/cheatedit.cpp b/src/gtk/cheatedit.cpp new file mode 100644 index 0000000..6cbf58d --- /dev/null +++ b/src/gtk/cheatedit.cpp @@ -0,0 +1,123 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 2008 VBA-M development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "cheatedit.h" + +#include "intl.h" + +namespace VBA +{ + +/** + * GameBoyAdvanceCheatEditDialog + * + * A unified cheat editing dialog for multiple code types. + */ +CheatEditDialog::CheatEditDialog(GtkDialog* _pstDialog, const Glib::RefPtr& refBuilder) : + Gtk::Dialog(_pstDialog) +{ + refBuilder->get_widget("CheatDescEntry", m_poCheatDescEntry); + refBuilder->get_widget("CheatTypeComboBox", m_poCheatTypeComboBox); + refBuilder->get_widget("CheatInputTextView", m_poCheatInputTextView); + refBuilder->get_widget("CheatApplyButton", m_poCheatApplyButton); + refBuilder->get_widget("CheatCancelButton", m_poCheatCancelButton); + + // Tree View model + m_poCheatTypeStore = Gtk::ListStore::create(m_oTypeModel); + + m_poCheatTypeComboBox->set_model(m_poCheatTypeStore); + + m_poCheatApplyButton->signal_clicked().connect(sigc::mem_fun(*this, &CheatEditDialog::vOnApply)); + m_poCheatCancelButton->signal_clicked().connect(sigc::mem_fun(*this, &CheatEditDialog::vOnCancel)); +} + +Glib::RefPtr CheatEditDialog::vGetCode() +{ + return m_poCheatInputTextView->get_buffer(); +} + +Glib::ustring CheatEditDialog::vGetDesc() +{ + return m_poCheatDescEntry->get_text(); +} + +ECheatType CheatEditDialog::vGetType() +{ + Gtk::TreeModel::iterator iter = m_poCheatTypeComboBox->get_active(); + + if (iter) + { + Gtk::TreeModel::Row row = *iter; + + return row[m_oTypeModel.iType]; + } + + return CheatGeneric; +} + +void CheatEditDialog::vSetWindow(VBA::Window * _poWindow) +{ + m_poWindow = _poWindow; + + // GameBoy Advance + if (m_poWindow->eGetCartridge() == VBA::Window::CartridgeGBA) + { + Gtk::TreeModel::Row row = *(m_poCheatTypeStore->append()); + + row[m_oTypeModel.iType] = CheatGeneric; + row[m_oTypeModel.uText] = _("Generic Code"); + + row = *(m_poCheatTypeStore->append()); + + row[m_oTypeModel.iType] = CheatGSA; + row[m_oTypeModel.uText] = _("Gameshark Advance"); + + row = *(m_poCheatTypeStore->append()); + + row[m_oTypeModel.iType] = CheatCBA; + row[m_oTypeModel.uText] = _("CodeBreaker Advance"); + + m_poCheatTypeComboBox->set_active(CheatGeneric); + } + // GameBoy + else if (m_poWindow->eGetCartridge() == VBA::Window::CartridgeGB) + { + Gtk::TreeModel::Row row = *(m_poCheatTypeStore->append()); + + row[m_oTypeModel.iType] = CheatGS; + row[m_oTypeModel.uText] = _("GameShark"); + + row = *(m_poCheatTypeStore->append()); + + row[m_oTypeModel.iType] = CheatGG; + row[m_oTypeModel.uText] = _("GameGenie"); + + m_poCheatTypeComboBox->set_active(0); + } +} + +void CheatEditDialog::vOnApply() +{ + response(Gtk::RESPONSE_APPLY); +} + +void CheatEditDialog::vOnCancel() { + response(Gtk::RESPONSE_CANCEL); +} + +} // namespace VBA diff --git a/src/gtk/cheatedit.h b/src/gtk/cheatedit.h new file mode 100644 index 0000000..5b67716 --- /dev/null +++ b/src/gtk/cheatedit.h @@ -0,0 +1,85 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 2008 VBA-M development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef __VBA_CHEATEDIT_H__ +#define __VBA_CHEATEDIT_H__ + +#include +#include +#include +#include +#include + +#include "window.h" + +namespace VBA +{ + +enum ECheatType +{ + CheatGeneric, + CheatGSA, + CheatCBA, + CheatGS, + CheatGG +}; + +class EditCheatCodeColumns : public Gtk::TreeModel::ColumnRecord +{ + public: + EditCheatCodeColumns() + { + add(uText); + add(iType); + } + + ~EditCheatCodeColumns() {} + + Gtk::TreeModelColumn uText; + Gtk::TreeModelColumn iType; +}; + +class CheatEditDialog : public Gtk::Dialog +{ +public: + CheatEditDialog(GtkDialog* _pstDialog, const Glib::RefPtr& refBuilder); + Glib::RefPtr vGetCode(); + Glib::ustring vGetDesc(); + ECheatType vGetType(); + void vSetWindow(VBA::Window * _poWindow); + +private: + void vOnApply(); + void vOnCancel(); + + VBA::Window * m_poWindow; + + Gtk::Entry * m_poCheatDescEntry; + Gtk::ComboBox * m_poCheatTypeComboBox; + Gtk::TextView * m_poCheatInputTextView; + Gtk::Button * m_poCheatApplyButton; + Gtk::Button * m_poCheatCancelButton; + Glib::RefPtr m_poCheatInputBuffer; + Glib::RefPtr m_poCheatTypeStore; + EditCheatCodeColumns m_oTypeModel; +}; + +} // namespace VBA + + +#endif diff --git a/src/gtk/cheatlist.cpp b/src/gtk/cheatlist.cpp new file mode 100644 index 0000000..4502242 --- /dev/null +++ b/src/gtk/cheatlist.cpp @@ -0,0 +1,172 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 2008 VBA-M development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "cheatlist.h" + +#include + +#include "intl.h" +#include + +namespace VBA +{ + +CheatListDialog::CheatListDialog(GtkDialog* _pstDialog, const Glib::RefPtr& refBuilder) : + Gtk::Dialog(_pstDialog) +{ + refBuilder->get_widget("CheatOpenButton", m_poCheatOpenButton); + refBuilder->get_widget("CheatSaveButton", m_poCheatSaveButton); + refBuilder->get_widget("CheatAddButton", m_poCheatAddButton); + refBuilder->get_widget("CheatRemoveButton", m_poCheatRemoveButton); + refBuilder->get_widget("CheatRemoveAllButton", m_poCheatRemoveAllButton); + refBuilder->get_widget("CheatMarkAllButton", m_poCheatMarkAllButton); + refBuilder->get_widget("CheatTreeView", m_poCheatTreeView); + + // Tree View model + m_poCheatListStore = Gtk::ListStore::create(m_oRecordModel); + + m_poCheatTreeView->set_model(m_poCheatListStore); + + Gtk::CellRendererToggle* pRenderer = Gtk::manage(new Gtk::CellRendererToggle()); + + int cols_count = m_poCheatTreeView->append_column("", *pRenderer); + + pRenderer->signal_toggled().connect(sigc::mem_fun(*this, &CheatListDialog::vOnCheatToggled)); + + Gtk::TreeViewColumn* pColumn = m_poCheatTreeView->get_column(cols_count - 1); + + if (pColumn) + pColumn->add_attribute(pRenderer->property_active(), m_oRecordModel.bEnabled); + + m_poCheatTreeView->append_column("Description", m_oRecordModel.uDesc); + + m_poCheatOpenButton->signal_clicked().connect(sigc::mem_fun(*this, &CheatListDialog::vOnCheatListOpen)); + m_poCheatSaveButton->signal_clicked().connect(sigc::mem_fun(*this, &CheatListDialog::vOnCheatListSave)); + m_poCheatAddButton->signal_clicked().connect(sigc::mem_fun(*this, &CheatListDialog::vOnCheatAdd)); + m_poCheatRemoveButton->signal_clicked().connect(sigc::mem_fun(*this, &CheatListDialog::vOnCheatRemove)); + m_poCheatRemoveAllButton->signal_clicked().connect(sigc::mem_fun(*this, &CheatListDialog::vOnCheatRemoveAll)); + m_poCheatMarkAllButton->signal_clicked().connect(sigc::mem_fun(*this, &CheatListDialog::vOnCheatMarkAll)); + + bMark = false; +} + +void CheatListDialog::vOnCheatListOpen() +{ + Gtk::FileChooserDialog oDialog(*this, _("Open cheat list")); + oDialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + oDialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK); + + oDialog.set_current_folder(Glib::get_home_dir()); + + while (oDialog.run() == Gtk::RESPONSE_OK) + { + // delete existing cheats before loading the list + vRemoveAllCheats(); + + m_poCheatListStore->clear(); + + if (vCheatListOpen(oDialog.get_filename().c_str())) + { + vUpdateList(); + break; + } + } +} + +void CheatListDialog::vOnCheatListSave() +{ + Gtk::FileChooserDialog sDialog(*this, _("Save cheat list")); + sDialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + sDialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK); + + sDialog.set_current_folder(Glib::get_home_dir()); + + if (sDialog.run() == Gtk::RESPONSE_OK) + vCheatListSave(sDialog.get_filename().c_str()); +} + +void CheatListDialog::vOnCheatAdd() +{ + std::string sUiFile = VBA::Window::sGetUiFilePath("cheatedit.ui"); + Glib::RefPtr poBuilder = Gtk::Builder::create_from_file(sUiFile); + + CheatEditDialog * poDialog = 0; + poBuilder->get_widget_derived("CheatEditDialog", poDialog); + poDialog->set_transient_for(*this); + poDialog->vSetWindow(m_poWindow); + int response = poDialog->run(); + poDialog->hide(); + + if (response == Gtk::RESPONSE_APPLY) + vAddCheat(poDialog->vGetDesc(), poDialog->vGetType(), poDialog->vGetCode()); +} + +void CheatListDialog::vOnCheatRemove() +{ + Gtk::TreeModel::iterator iter = m_poCheatTreeView->get_selection()->get_selected(); + + if (iter) + { + Gtk::TreeModel::Row row = *iter; + + vRemoveCheat(row[m_oRecordModel.iIndex]); + + m_poCheatListStore->erase(iter); + } +} + +void CheatListDialog::vOnCheatRemoveAll() +{ + vRemoveAllCheats(); + + m_poCheatListStore->clear(); +} + +void CheatListDialog::vOnCheatMarkAll() +{ + Gtk::TreeModel::Children cListEntries = m_poCheatListStore->children(); + + for (Gtk::TreeModel::iterator iter = cListEntries.begin(); iter; iter++) + { + Gtk::TreeModel::Row row = *iter; + + row[m_oRecordModel.bEnabled] = bMark; + + vToggleCheat(row[m_oRecordModel.iIndex], row[m_oRecordModel.bEnabled]); + } + + bMark = !bMark; +} + +void CheatListDialog::vOnCheatToggled(Glib::ustring const& string_path) +{ + Gtk::TreeIter iter = m_poCheatListStore->get_iter(string_path); + + Gtk::TreeModel::Row row = *iter; + + row[m_oRecordModel.bEnabled] = !row[m_oRecordModel.bEnabled]; + + vToggleCheat(row[m_oRecordModel.iIndex], row[m_oRecordModel.bEnabled]); +} + +void CheatListDialog::vSetWindow(VBA::Window * _poWindow) +{ + m_poWindow = _poWindow; +} + +} // namespace VBA diff --git a/src/gtk/cheatlist.h b/src/gtk/cheatlist.h new file mode 100644 index 0000000..b8f85da --- /dev/null +++ b/src/gtk/cheatlist.h @@ -0,0 +1,91 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 2008 VBA-M development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef __VBA_CHEATLIST_H__ +#define __VBA_CHEATLIST_H__ + +#include + +#include "cheatedit.h" + +#include "window.h" + +namespace VBA +{ + +class ListCheatCodeColumns : public Gtk::TreeModel::ColumnRecord +{ + public: + ListCheatCodeColumns() + { + add(iIndex); + add(bEnabled); + add(uDesc); + } + + ~ListCheatCodeColumns() {} + + Gtk::TreeModelColumn iIndex; + Gtk::TreeModelColumn bEnabled; + Gtk::TreeModelColumn uDesc; +}; + +class CheatListDialog : public Gtk::Dialog +{ +public: + CheatListDialog(GtkDialog* _pstDialog, const Glib::RefPtr& refBuilder); + void vSetWindow(VBA::Window * _poWindow); + +protected: + virtual void vAddCheat(Glib::ustring sDesc, ECheatType type, Glib::RefPtr buffer) = 0; + virtual bool vCheatListOpen(const char *file) = 0; + virtual void vCheatListSave(const char *file) = 0; + virtual void vRemoveCheat(int index) = 0; + virtual void vRemoveAllCheats() = 0; + virtual void vToggleCheat(int index, bool enable) = 0; + virtual void vUpdateList(int previous = 0) = 0; + + Glib::RefPtr m_poCheatListStore; + ListCheatCodeColumns m_oRecordModel; + +private: + void vOnCheatListOpen(); + void vOnCheatListSave(); + void vOnCheatAdd(); + void vOnCheatRemove(); + void vOnCheatRemoveAll(); + void vOnCheatMarkAll(); + void vOnCheatToggled(Glib::ustring const& string_path); + + VBA::Window * m_poWindow; + + Gtk::ToolButton * m_poCheatOpenButton; + Gtk::ToolButton * m_poCheatSaveButton; + Gtk::ToolButton * m_poCheatAddButton; + Gtk::ToolButton * m_poCheatRemoveButton; + Gtk::ToolButton * m_poCheatRemoveAllButton; + Gtk::ToolButton * m_poCheatMarkAllButton; + Gtk::TreeView * m_poCheatTreeView; + + bool bMark; +}; + +} // namespace VBA + + +#endif diff --git a/src/gtk/configfile.cpp b/src/gtk/configfile.cpp new file mode 100644 index 0000000..433ae3e --- /dev/null +++ b/src/gtk/configfile.cpp @@ -0,0 +1,261 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "configfile.h" + +#include + +#include +#include + +namespace VBA +{ +namespace Config +{ + +using std::string; +using Glib::IOChannel; + +Line::Line(const string & _rsKey, const string & _rsValue) : + m_sKey(_rsKey), + m_sValue(_rsValue) +{ +} + +Section::Section(const string & _rsName) : + m_sName(_rsName) +{ +} + +bool Section::bKeyExists(const string & _rsKey) +{ + for (iterator it = begin(); it != end(); it++) + { + if (it->m_sKey == _rsKey) + { + return true; + } + } + return false; +} + +void Section::vSetKey(const string & _rsKey, const string & _rsValue) +{ + for (iterator it = begin(); it != end(); it++) + { + if (it->m_sKey == _rsKey) + { + it->m_sValue = _rsValue; + return; + } + } + push_back(Line(_rsKey, _rsValue)); +} + +string Section::sGetKey(const string & _rsKey) const +{ + for (const_iterator it = begin(); it != end(); it++) + { + if (it->m_sKey == _rsKey) + { + return it->m_sValue; + } + } + throw KeyNotFound(m_sName, _rsKey); +} + +void Section::vRemoveKey(const string & _rsKey) +{ + for (iterator it = begin(); it != end(); it++) + { + if (it->m_sKey == _rsKey) + { + erase(it); + return; + } + } +} + +File::File() +{ +} + +File::File(const string & _rsFile) +{ + vLoad(_rsFile); +} + +File::~File() +{ +} + +bool File::bSectionExists(const string & _rsName) +{ + for (iterator it = begin(); it != end(); it++) + { + if (it->sGetName() == _rsName) + { + return true; + } + } + return false; +} + +Section * File::poAddSection(const string & _rsName) +{ + Section * poSection = NULL; + for (iterator it = begin(); it != end(); it++) + { + if (it->sGetName() == _rsName) + { + poSection = &(*it); + } + } + if (poSection == NULL) + { + push_back(Section(_rsName)); + poSection = &back(); + } + return poSection; +} + +Section * File::poGetSection(const string & _rsName) +{ + for (iterator it = begin(); it != end(); it++) + { + if (it->sGetName() == _rsName) + { + return &(*it); + } + } + throw SectionNotFound(_rsName); +} + +void File::vRemoveSection(const string & _rsName) +{ + for (iterator it = begin(); it != end(); it++) + { + if (it->sGetName() == _rsName) + { + erase(it); + return; + } + } +} + +void File::vLoad(const string & _rsFile, + bool _bAddSection, + bool _bAddKey) +{ + string sBuffer = Glib::file_get_contents(_rsFile); + Section * poSection = NULL; + char ** lines = g_strsplit(sBuffer.c_str(), "\n", 0); + char * tmp; + int i = 0; + while (lines[i]) + { + if (lines[i][0] == '[') + { + if ((tmp = strchr(lines[i], ']'))) + { + *tmp = '\0'; + if (_bAddSection) + { + poSection = poAddSection(&lines[i][1]); + } + else + { + try + { + poSection = poGetSection(&lines[i][1]); + } + catch (...) + { + poSection = NULL; + } + } + } + } + else if (lines[i][0] != '#' && poSection != NULL) + { + if ((tmp = strchr(lines[i], '='))) + { + *tmp = '\0'; + tmp++; + if (_bAddKey || poSection->bKeyExists(lines[i])) + { + poSection->vSetKey(lines[i], tmp); + } + } + } + i++; + } + g_strfreev(lines); +} + +void File::vSave(const string & _rsFile) +{ + Glib::RefPtr poFile = IOChannel::create_from_file(_rsFile, "w"); + poFile->set_encoding(""); + + for (const_iterator poSection = begin(); + poSection != end(); + poSection++) + { + string sName = "[" + poSection->sGetName() + "]\n"; + poFile->write(sName); + + for (Section::const_iterator poLine = poSection->begin(); + poLine != poSection->end(); + poLine++) + { + string sLine = poLine->m_sKey + "=" + poLine->m_sValue + "\n"; + poFile->write(sLine); + } + poFile->write("\n"); + } +} + +void File::vClear() +{ + clear(); +} + +std::ostream & operator<<(std::ostream & _roOut, const File & _roFile) +{ + for (File::const_iterator poSection = _roFile.begin(); + poSection != _roFile.end(); + poSection++) + { + string sName = "[" + poSection->sGetName() + "]\n"; + _roOut << sName; + + for (Section::const_iterator poLine = poSection->begin(); + poLine != poSection->end(); + poLine++) + { + string sLine = poLine->m_sKey + "=" + poLine->m_sValue + "\n"; + _roOut << sLine; + } + _roOut << "\n"; + } + return _roOut; +} + +} // namespace Config +} // namespace VBA diff --git a/src/gtk/configfile.h b/src/gtk/configfile.h new file mode 100644 index 0000000..e14034b --- /dev/null +++ b/src/gtk/configfile.h @@ -0,0 +1,202 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef __VBA_CONFIGFILE_H__ +#define __VBA_CONFIGFILE_H__ + +#include +#include + +namespace VBA +{ +namespace Config +{ + +class NotFound +{ +public: + virtual ~NotFound() {} + +protected: + NotFound() {} +}; + +class SectionNotFound : public NotFound +{ +public: + SectionNotFound(const std::string & _rsName) : + m_sName(_rsName) + { + } + virtual ~SectionNotFound() {} + + inline std::string sGetName() const { return m_sName; } + +private: + std::string m_sName; +}; + +class KeyNotFound : public NotFound +{ +public: + KeyNotFound(const std::string & _rsSection, const std::string & _rsKey) : + m_sSection(_rsSection), + m_sKey(_rsKey) + { + } + virtual ~KeyNotFound() {} + + inline std::string sGetSection() const { return m_sSection; } + inline std::string sGetKey() const { return m_sKey; } + +private: + std::string m_sSection; + std::string m_sKey; +}; + +class Line +{ +public: + Line(const std::string & _rsKey, const std::string & _rsValue); + + std::string m_sKey; + std::string m_sValue; +}; + +class Section : private std::list +{ +public: + explicit Section(const std::string & _rsName); + + inline std::string sGetName() const { return m_sName; } + + bool bKeyExists(const std::string & _rsKey); + void vSetKey(const std::string & _rsKey, const std::string & _rsValue); + std::string sGetKey(const std::string & _rsKey) const; + void vRemoveKey(const std::string & _rsKey); + + template + void vSetKey(const std::string & _rsKey, const T & _rValue); + + template + T oGetKey(const std::string & _rsKey) const; + + // read only + typedef std::list::const_iterator const_iterator; + inline const_iterator begin() const + { + return std::list::begin(); + } + inline const_iterator end() const + { + return std::list::end(); + } + +private: + inline iterator begin() + { + return std::list::begin(); + } + inline iterator end() + { + return std::list::end(); + } + + std::string m_sName; +}; + +class File : private std::list
+{ +public: + File(); + File(const std::string & _rsFile); + virtual ~File(); + + bool bSectionExists(const std::string & _rsName); + Section * poAddSection(const std::string & _rsName); + Section * poGetSection(const std::string & _rsName); + void vRemoveSection(const std::string & _rsName); + void vLoad(const std::string & _rsFile, + bool _bAddSection = true, + bool _bAddKey = true); + void vSave(const std::string & _rsFile); + void vClear(); + + // read only + typedef std::list
::const_iterator const_iterator; + inline const_iterator begin() const + { + return std::list
::begin(); + } + inline const_iterator end() const + { + return std::list
::end(); + } + +private: + inline iterator begin() + { + return std::list
::begin(); + } + inline iterator end() + { + return std::list
::end(); + } +}; + +// debug +std::ostream & operator<<(std::ostream & _roOut, const File & _roConfig); + +template +void Section::vSetKey(const std::string & _rsKey, const T & _rValue) +{ + std::ostringstream oOut; + oOut << _rValue; + for (iterator it = begin(); it != end(); it++) + { + if (it->m_sKey == _rsKey) + { + it->m_sValue = oOut.str(); + return; + } + } + push_back(Line(_rsKey, oOut.str())); +} + +template +T Section::oGetKey(const std::string & _rsKey) const +{ + T oValue; + for (const_iterator it = begin(); it != end(); it++) + { + if (it->m_sKey == _rsKey) + { + std::istringstream oIn(it->m_sValue); + oIn >> oValue; + return oValue; + } + } + throw KeyNotFound(m_sName, _rsKey); +} + +} // namespace Config +} // namespace VBA + + +#endif // __VBA_CONFIGFILE_H__ diff --git a/src/gtk/directoriesconfig.cpp b/src/gtk/directoriesconfig.cpp new file mode 100644 index 0000000..28f3a50 --- /dev/null +++ b/src/gtk/directoriesconfig.cpp @@ -0,0 +1,69 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 2008 VBA-M development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "directoriesconfig.h" + +#include + +#include "intl.h" + +namespace VBA +{ + +const DirectoriesConfigDialog::SDirEntry DirectoriesConfigDialog::m_astDirs[] = +{ + { "gba_roms", N_("GBA roms :"), "GBARomsDirEntry" }, + { "gb_roms", N_("GB roms :"), "GBRomsDirEntry" }, + { "batteries", N_("Batteries :"), "BatteriesDirEntry" }, + { "cheats", N_("Cheats :"), "CheatsDirEntry" }, + { "saves", N_("Saves :"), "SavesDirEntry" }, + { "captures", N_("Captures :"), "CapturesDirEntry" } +}; + +DirectoriesConfigDialog::DirectoriesConfigDialog(Config::Section * _poConfig) : + Gtk::Dialog(_("Directories config"), true, true), + m_poConfig(_poConfig) +{ + Gtk::Table * poTable = Gtk::manage( new Gtk::Table(G_N_ELEMENTS(m_astDirs), 2, false)); + poTable->set_border_width(5); + poTable->set_spacings(5); + + for (guint i = 0; i < G_N_ELEMENTS(m_astDirs); i++) + { + Gtk::Label * poLabel = Gtk::manage( new Gtk::Label(gettext(m_astDirs[i].m_csLabel), Gtk::ALIGN_RIGHT) ); + m_poButtons[i] = Gtk::manage( new Gtk::FileChooserButton(Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER) ); + m_poButtons[i]->set_current_folder(m_poConfig->sGetKey(m_astDirs[i].m_csKey)); + + poTable->attach(* poLabel, 0, 1, i, i + 1); + poTable->attach(* m_poButtons[i], 1, 2, i, i + 1); + } + + add_button(Gtk::Stock::CLOSE, Gtk::RESPONSE_CLOSE); + get_vbox()->pack_start(* poTable); + show_all_children(); +} + +void DirectoriesConfigDialog::on_response(int response_id) +{ + for (guint i = 0; i < G_N_ELEMENTS(m_astDirs); i++) + { + m_poConfig->vSetKey(m_astDirs[i].m_csKey, m_poButtons[i]->get_current_folder()); + } +} + +} // namespace VBA diff --git a/src/gtk/directoriesconfig.h b/src/gtk/directoriesconfig.h new file mode 100644 index 0000000..7447784 --- /dev/null +++ b/src/gtk/directoriesconfig.h @@ -0,0 +1,56 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 2008 VBA-M development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef __VBA_DIRECTORIESCONFIG_H__ +#define __VBA_DIRECTORIESCONFIG_H__ + +#include +#include +#include +#include + +#include "configfile.h" + +namespace VBA +{ + +class DirectoriesConfigDialog : public Gtk::Dialog +{ +public: + DirectoriesConfigDialog(Config::Section * _poConfig); + +protected: + void on_response(int response_id); + +private: + struct SDirEntry + { + const char * m_csKey; + const char * m_csLabel; + const char * m_csFileChooserButton; + }; + + Config::Section * m_poConfig; + static const SDirEntry m_astDirs[]; + Gtk::FileChooserButton * m_poButtons[6]; +}; + +} // namespace VBA + + +#endif // __VBA_DIRECTORIESCONFIG_H__ diff --git a/src/gtk/displayconfig.cpp b/src/gtk/displayconfig.cpp new file mode 100644 index 0000000..d446c81 --- /dev/null +++ b/src/gtk/displayconfig.cpp @@ -0,0 +1,145 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 2008 VBA-M development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "displayconfig.h" + +#include +#include +#include +#include + +#include "intl.h" +#include "filters.h" + +namespace VBA +{ + +DisplayConfigDialog::DisplayConfigDialog(GtkDialog* _pstDialog, const Glib::RefPtr& refBuilder) : + Gtk::Dialog(_pstDialog), + m_poConfig(0) +{ + refBuilder->get_widget("FiltersComboBox", m_poFiltersComboBox); + refBuilder->get_widget("IBFiltersComboBox", m_poIBFiltersComboBox); + refBuilder->get_widget("DefaultScaleComboBox", m_poDefaultScaleComboBox); + refBuilder->get_widget("OutputOpenGL", m_poOutputOpenGLRadioButton); + refBuilder->get_widget("OutputCairo", m_poOutputCairoRadioButton); + + m_poFiltersComboBox->signal_changed().connect(sigc::mem_fun(*this, &DisplayConfigDialog::vOnFilterChanged)); + m_poIBFiltersComboBox->signal_changed().connect(sigc::mem_fun(*this, &DisplayConfigDialog::vOnFilterIBChanged)); + m_poDefaultScaleComboBox->signal_changed().connect(sigc::mem_fun(*this, &DisplayConfigDialog::vOnScaleChanged)); + m_poOutputOpenGLRadioButton->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &DisplayConfigDialog::vOnOutputChanged), VBA::Window::OutputOpenGL)); + m_poOutputCairoRadioButton->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &DisplayConfigDialog::vOnOutputChanged), VBA::Window::OutputCairo)); + + + // Populate the filters combobox + Glib::RefPtr poFiltersListStore; + poFiltersListStore = Glib::RefPtr::cast_static(refBuilder->get_object("FiltersListStore")); + + for (guint i = FirstFilter; i <= LastFilter; i++) + { + Gtk::TreeModel::Row row = *(poFiltersListStore->append()); + row->set_value(0, std::string(pcsGetFilterName((EFilter)i))); + } + + // Populate the interframe blending filters combobox + Glib::RefPtr poIBFiltersListStore; + poIBFiltersListStore = Glib::RefPtr::cast_static(refBuilder->get_object("IBFiltersListStore")); + + for (guint i = FirstFilterIB; i <= LastFilterIB; i++) + { + Gtk::TreeModel::Row row = *(poIBFiltersListStore->append()); + row->set_value(0, std::string(pcsGetFilterIBName((EFilterIB)i))); + } +} + +void DisplayConfigDialog::vSetConfig(Config::Section * _poConfig, VBA::Window * _poWindow) +{ + m_poConfig = _poConfig; + m_poWindow = _poWindow; + + int iDefaultFilter = m_poConfig->oGetKey("filter2x"); + m_poFiltersComboBox->set_active(iDefaultFilter); + + int iDefaultFilterIB = m_poConfig->oGetKey("filterIB"); + m_poIBFiltersComboBox->set_active(iDefaultFilterIB); + + int iDefaultScale = m_poConfig->oGetKey("scale"); + m_poDefaultScaleComboBox->set_active(iDefaultScale - 1); + + // Set the default output module + VBA::Window::EVideoOutput _eOutput = (VBA::Window::EVideoOutput)m_poConfig->oGetKey("output"); + switch (_eOutput) + { + case VBA::Window::OutputOpenGL: + m_poOutputOpenGLRadioButton->set_active(); + break; + default: + m_poOutputCairoRadioButton->set_active(); + break; + } +} + +void DisplayConfigDialog::vOnFilterChanged() +{ + int iFilter = m_poFiltersComboBox->get_active_row_number(); + if (iFilter >= 0) + { + m_poConfig->vSetKey("filter2x", iFilter); + m_poWindow->vApplyConfigFilter(); + } +} + +void DisplayConfigDialog::vOnFilterIBChanged() +{ + int iFilterIB = m_poIBFiltersComboBox->get_active_row_number(); + if (iFilterIB >= 0) + { + m_poConfig->vSetKey("filterIB", iFilterIB); + m_poWindow->vApplyConfigFilterIB(); + } +} + +void DisplayConfigDialog::vOnOutputChanged(VBA::Window::EVideoOutput _eOutput) +{ + VBA::Window::EVideoOutput eOldOutput = (VBA::Window::EVideoOutput)m_poConfig->oGetKey("output"); + + if (_eOutput == eOldOutput) + return; + + if (_eOutput == VBA::Window::OutputOpenGL && m_poOutputOpenGLRadioButton->get_active()) + { + m_poConfig->vSetKey("output", VBA::Window::OutputOpenGL); + m_poWindow->vApplyConfigScreenArea(); + } else if (_eOutput == VBA::Window::OutputCairo && m_poOutputCairoRadioButton->get_active()) + { + m_poConfig->vSetKey("output", VBA::Window::OutputCairo); + m_poWindow->vApplyConfigScreenArea(); + } +} + +void DisplayConfigDialog::vOnScaleChanged() +{ + int iScale = m_poDefaultScaleComboBox->get_active_row_number() + 1; + if (iScale > 0) + { + m_poConfig->vSetKey("scale", iScale); + m_poWindow->vUpdateScreen(); + } +} + +} // namespace VBA diff --git a/src/gtk/displayconfig.h b/src/gtk/displayconfig.h new file mode 100644 index 0000000..9879f39 --- /dev/null +++ b/src/gtk/displayconfig.h @@ -0,0 +1,58 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 2008 VBA-M development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef __VBA_DISPLAYCONFIG_H__ +#define __VBA_DISPLAYCONFIG_H__ + +#include +#include +#include + +#include "configfile.h" +#include "window.h" + +namespace VBA +{ + +class DisplayConfigDialog : public Gtk::Dialog +{ +public: + DisplayConfigDialog(GtkDialog* _pstDialog, const Glib::RefPtr& refBuilder); + + void vSetConfig(Config::Section * _poConfig, VBA::Window * _poWindow); + +private: + void vOnFilterChanged(); + void vOnFilterIBChanged(); + void vOnOutputChanged(VBA::Window::EVideoOutput _eOutput); + void vOnScaleChanged(); + + VBA::Window * m_poWindow; + + Config::Section * m_poConfig; + Gtk::ComboBox * m_poFiltersComboBox; + Gtk::ComboBox * m_poIBFiltersComboBox; + Gtk::ComboBox * m_poDefaultScaleComboBox; + Gtk::RadioButton * m_poOutputOpenGLRadioButton; + Gtk::RadioButton * m_poOutputCairoRadioButton; +}; + +} // namespace VBA + + +#endif // __VBA_DISPLAYCONFIG_H__ diff --git a/src/gtk/filters.cpp b/src/gtk/filters.cpp new file mode 100644 index 0000000..fae3652 --- /dev/null +++ b/src/gtk/filters.cpp @@ -0,0 +1,105 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "filters.h" +#include "intl.h" + +void _2xSaI (u8 *, u32, u8 *, u8 *, u32, int, int); +void _2xSaI32 (u8 *, u32, u8 *, u8 *, u32, int, int); +void Super2xSaI (u8 *, u32, u8 *, u8 *, u32, int, int); +void Super2xSaI32 (u8 *, u32, u8 *, u8 *, u32, int, int); +void SuperEagle (u8 *, u32, u8 *, u8 *, u32, int, int); +void SuperEagle32 (u8 *, u32, u8 *, u8 *, u32, int, int); +void Pixelate (u8 *, u32, u8 *, u8 *, u32, int, int); +void Pixelate32 (u8 *, u32, u8 *, u8 *, u32, int, int); +void AdMame2x (u8 *, u32, u8 *, u8 *, u32, int, int); +void AdMame2x32 (u8 *, u32, u8 *, u8 *, u32, int, int); +void Bilinear (u8 *, u32, u8 *, u8 *, u32, int, int); +void Bilinear32 (u8 *, u32, u8 *, u8 *, u32, int, int); +void BilinearPlus (u8 *, u32, u8 *, u8 *, u32, int, int); +void BilinearPlus32(u8 *, u32, u8 *, u8 *, u32, int, int); +void Scanlines (u8 *, u32, u8 *, u8 *, u32, int, int); +void Scanlines32 (u8 *, u32, u8 *, u8 *, u32, int, int); +void ScanlinesTV (u8 *, u32, u8 *, u8 *, u32, int, int); +void ScanlinesTV32 (u8 *, u32, u8 *, u8 *, u32, int, int); +void hq2x (u8 *, u32, u8 *, u8 *, u32, int, int); +void hq2x32 (u8 *, u32, u8 *, u8 *, u32, int, int); +void lq2x (u8 *, u32, u8 *, u8 *, u32, int, int); +void lq2x32 (u8 *, u32, u8 *, u8 *, u32, int, int); + +void SmartIB (u8 *, u32, int, int); +void SmartIB32 (u8 *, u32, int, int); +void MotionBlurIB (u8 *, u32, int, int); +void MotionBlurIB32(u8 *, u32, int, int); + +namespace VBA +{ + +struct { + char m_csName[30]; + int m_iEnlargeFactor; + Filter m_apvFunc[2]; +} +static const astFilters[] = +{ + { N_("None"), 1, { 0, 0 } }, + { N_("2xSaI"), 2, { _2xSaI, _2xSaI32 } }, + { N_("Super 2xSaI"), 2, { Super2xSaI, Super2xSaI32 } }, + { N_("Super Eagle"), 2, { SuperEagle, SuperEagle32 } }, + { N_("Pixelate"), 2, { Pixelate, Pixelate32 } }, + { N_("AdvanceMAME Scale2x"), 2, { AdMame2x, AdMame2x32 } }, + { N_("Bilinear"), 2, { Bilinear, Bilinear32 } }, + { N_("Bilinear Plus"), 2, { BilinearPlus, BilinearPlus32 } }, + { N_("Scanlines"), 2, { Scanlines, Scanlines32 } }, + { N_("TV Mode"), 2, { ScanlinesTV, ScanlinesTV32 } }, + { N_("hq2x"), 2, { hq2x, hq2x32 } }, + { N_("lq2x"), 2, { lq2x, lq2x32 } } +}; + +struct { + char m_csName[30]; + FilterIB m_apvFunc[2]; +} +static const astFiltersIB[] = +{ + { N_("None"), { 0, 0 } }, + { N_("Smart interframe blending"), { SmartIB, SmartIB32 } }, + { N_("Interframe motion blur"), { MotionBlurIB, MotionBlurIB32 } } +}; + +Filter pvGetFilter(EFilter _eFilter, EFilterDepth _eDepth) +{ + return astFilters[_eFilter].m_apvFunc[_eDepth]; +} + +const char* pcsGetFilterName(const EFilter _eFilter) +{ + return gettext(astFilters[_eFilter].m_csName); +} + +FilterIB pvGetFilterIB(EFilterIB _eFilterIB, EFilterDepth _eDepth) +{ + return astFiltersIB[_eFilterIB].m_apvFunc[_eDepth]; +} + +const char* pcsGetFilterIBName(const EFilterIB _eFilterIB) +{ + return gettext(astFiltersIB[_eFilterIB].m_csName); +} + +} // namespace VBA diff --git a/src/gtk/filters.h b/src/gtk/filters.h new file mode 100644 index 0000000..bc4dea6 --- /dev/null +++ b/src/gtk/filters.h @@ -0,0 +1,75 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef __VBA_FILTERS_H__ +#define __VBA_FILTERS_H__ + +#include "../System.h" + +int Init_2xSaI(u32); + +namespace VBA +{ + +typedef void (*Filter)(u8 *, u32, u8 *, u8 *, u32, int, int); +typedef void (*FilterIB)(u8 *, u32, int, int); + +enum EFilter +{ + FirstFilter, + FilterNone = FirstFilter, + Filter2xSaI, + FilterSuper2xSaI, + FilterSuperEagle, + FilterPixelate, + FilterAdMame2x, + FilterBilinear, + FilterBilinearPlus, + FilterScanlines, + FilterScanlinesTV, + FilterHq2x, + FilterLq2x, + LastFilter = FilterLq2x +}; + +enum EFilterIB +{ + FirstFilterIB, + FilterIBNone = FirstFilterIB, + FilterIBSmart, + FilterIBMotionBlur, + LastFilterIB = FilterIBMotionBlur +}; + +enum EFilterDepth +{ + FilterDepth16, + FilterDepth32 +}; + +Filter pvGetFilter(EFilter _eFilter, EFilterDepth _eDepth); +const char* pcsGetFilterName(const EFilter _eFilter); + +FilterIB pvGetFilterIB(EFilterIB _eFilterIB, EFilterDepth _eDepth); +const char* pcsGetFilterIBName(const EFilterIB _eFilterIB); + +} // namespace VBA + + +#endif // __VBA_FILTERS_H__ diff --git a/src/gtk/gameboyadvancecheatlist.cpp b/src/gtk/gameboyadvancecheatlist.cpp new file mode 100644 index 0000000..8d947b0 --- /dev/null +++ b/src/gtk/gameboyadvancecheatlist.cpp @@ -0,0 +1,153 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 2008 VBA-M development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "gameboyadvancecheatlist.h" +#include "tools.h" + +#include + +namespace VBA +{ + +GameBoyAdvanceCheatListDialog::GameBoyAdvanceCheatListDialog(GtkDialog* _pstDialog, const Glib::RefPtr& refBuilder) : + CheatListDialog(_pstDialog, refBuilder) +{ + vUpdateList(); +} + +void GameBoyAdvanceCheatListDialog::vAddCheat(Glib::ustring sDesc, ECheatType type, Glib::RefPtr buffer) +{ + int previous = cheatsNumber; + + switch (type) + { + // Generic Code + case CheatGeneric: + { + std::vector tokens; + + vTokenize(buffer->get_text(), tokens); + + for (std::vector::iterator it = tokens.begin(); + it != tokens.end(); + it++) + { + Glib::ustring sToken = it->uppercase(); + + cheatsAddCheatCode(sToken.c_str(), sDesc.c_str()); + } + + break; + } + // Gameshark Advance & CodeBreaker Advance + case CheatGSA: + case CheatCBA: + { + std::vector tokens; + + Glib::ustring sToken; + Glib::ustring sCode; + Glib::ustring sPart = ""; + + vTokenize(buffer->get_text(), tokens); + + for (std::vector::iterator it = tokens.begin(); + it != tokens.end(); + it++) + { + + sToken = it->uppercase(); + const char *cToken = sToken.c_str(); + + if (sToken.size() == 16) + cheatsAddGSACode(cToken, sDesc.c_str(), false); + else if (sToken.size() == 12) + { + sCode = sToken.substr(0,8); + sCode += " "; + sCode += sToken.substr(9,4); + cheatsAddCBACode(sCode.c_str(), sDesc.c_str()); + } + else + if (sPart.empty()) + sPart = sToken; + else + { + if (sToken.size() == 4) + { + sCode = sPart; + sCode += " "; + sCode += cToken; + cheatsAddCBACode(sCode.c_str(), sDesc.c_str()); + } + else + { + sCode = sPart + sToken; + cheatsAddGSACode(sCode.c_str(), sDesc.c_str(), true); + } + + sPart = ""; + } + } // end of loop + + } // end of case + default:; // silence warnings + } // end of switch + + vUpdateList(previous); +} + +bool GameBoyAdvanceCheatListDialog::vCheatListOpen(const char *file) +{ + return cheatsLoadCheatList(file); +} + +void GameBoyAdvanceCheatListDialog::vCheatListSave(const char *file) +{ + cheatsSaveCheatList(file); +} + +void GameBoyAdvanceCheatListDialog::vRemoveCheat(int index) { + cheatsDelete(index, false); +} + +void GameBoyAdvanceCheatListDialog::vRemoveAllCheats() { + cheatsDeleteAll(false); +} + +void GameBoyAdvanceCheatListDialog::vToggleCheat(int index, bool enable) { + if (enable) + cheatsEnable(index); + else + cheatsDisable(index); +} + +void GameBoyAdvanceCheatListDialog::vUpdateList(int previous) +{ + for (int i = previous; i < cheatsNumber; i++) + { + // Add row for each newly added cheat + Gtk::TreeModel::Row row = *(m_poCheatListStore->append()); + + row[m_oRecordModel.iIndex] = i; + row[m_oRecordModel.bEnabled] = cheatsList[i].enabled; + row[m_oRecordModel.uDesc] = cheatsList[i].desc; + } +} + +} // namespace VBA diff --git a/src/gtk/gameboyadvancecheatlist.h b/src/gtk/gameboyadvancecheatlist.h new file mode 100644 index 0000000..2677024 --- /dev/null +++ b/src/gtk/gameboyadvancecheatlist.h @@ -0,0 +1,49 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 2008 VBA-M development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef __VBA_GAMEBOYADVANCECHEATLIST_H__ +#define __VBA_GAMEBOYADVANCECHEATLIST_H__ + +#include "../System.h" +#include "../gba/Cheats.h" +#include "../gba/GBA.h" +#include "../gba/Globals.h" +#include "cheatlist.h" + +namespace VBA +{ + +class GameBoyAdvanceCheatListDialog : public CheatListDialog +{ +public: + GameBoyAdvanceCheatListDialog(GtkDialog* _pstDialog, const Glib::RefPtr& refBuilder); + +protected: + void vAddCheat(Glib::ustring sDesc, ECheatType type, Glib::RefPtr buffer); + bool vCheatListOpen(const char *file); + void vCheatListSave(const char *file); + void vRemoveCheat(int index); + void vRemoveAllCheats(); + void vToggleCheat(int index, bool enable); + void vUpdateList(int previous = 0); +}; + +} // namespace VBA + + +#endif diff --git a/src/gtk/gameboyadvanceconfig.cpp b/src/gtk/gameboyadvanceconfig.cpp new file mode 100644 index 0000000..5b2b311 --- /dev/null +++ b/src/gtk/gameboyadvanceconfig.cpp @@ -0,0 +1,134 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 2008 VBA-M development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "gameboyadvanceconfig.h" + +#include "intl.h" + +namespace VBA +{ + +GameBoyAdvanceConfigDialog::GameBoyAdvanceConfigDialog(GtkDialog* _pstDialog, const Glib::RefPtr& refBuilder) : + Gtk::Dialog(_pstDialog), + m_poConfig(0) +{ + refBuilder->get_widget("SaveTypeComboBox", m_poSaveTypeComboBox); + refBuilder->get_widget("FlashSizeComboBox", m_poFlashSizeComboBox); + refBuilder->get_widget("BiosCheckButton", m_poBiosCheckButton); + refBuilder->get_widget("BiosFileChooserButton", m_poBiosFileChooserButton); + refBuilder->get_widget("RTCCheckButton", m_poRTCCheckButton); + + m_poSaveTypeComboBox->signal_changed().connect(sigc::mem_fun(*this, &GameBoyAdvanceConfigDialog::vOnSaveTypeChanged)); + m_poFlashSizeComboBox->signal_changed().connect(sigc::mem_fun(*this, &GameBoyAdvanceConfigDialog::vOnFlashSizeChanged)); + m_poBiosCheckButton->signal_toggled().connect(sigc::mem_fun(*this, &GameBoyAdvanceConfigDialog::vOnUseBiosChanged)); + m_poBiosFileChooserButton->signal_selection_changed().connect(sigc::mem_fun(*this, &GameBoyAdvanceConfigDialog::vOnBiosSelectionChanged)); + m_poRTCCheckButton->signal_toggled().connect(sigc::mem_fun(*this, &GameBoyAdvanceConfigDialog::vOnEnableRTCChanged)); +} + +void GameBoyAdvanceConfigDialog::vSetConfig(Config::Section * _poConfig, VBA::Window * _poWindow) +{ + m_poConfig = _poConfig; + m_poWindow = _poWindow; + + VBA::Window::ESaveType eDefaultSaveType = (VBA::Window::ESaveType)m_poConfig->oGetKey("save_type"); + m_poSaveTypeComboBox->set_active(eDefaultSaveType); + + int iDefaultFlashSize = m_poConfig->oGetKey("flash_size"); + if (iDefaultFlashSize == 128) + { + m_poFlashSizeComboBox->set_active(1); + } + else + { + m_poFlashSizeComboBox->set_active(0); + } + + bool bUseBios = m_poConfig->oGetKey("use_bios_file"); + m_poBiosCheckButton->set_active(bUseBios); + m_poBiosFileChooserButton->set_sensitive(bUseBios); + + std::string sBios = m_poConfig->oGetKey("bios_file"); + m_poBiosFileChooserButton->set_filename(sBios); + + const char * acsPattern[] = + { + "*.[bB][iI][nN]", "*.[aA][gG][bB]", "*.[gG][bB][aA]", + "*.[bB][iI][oO][sS]", "*.[zZ][iI][pP]", "*.[zZ]", "*.[gG][zZ]" + }; + + Gtk::FileFilter oAllFilter; + oAllFilter.set_name(_("All files")); + oAllFilter.add_pattern("*"); + + Gtk::FileFilter oBiosFilter; + oBiosFilter.set_name(_("Gameboy Advance BIOS")); + for (guint i = 0; i < G_N_ELEMENTS(acsPattern); i++) + { + oBiosFilter.add_pattern(acsPattern[i]); + } + + m_poBiosFileChooserButton->add_filter(oAllFilter); + m_poBiosFileChooserButton->add_filter(oBiosFilter); + m_poBiosFileChooserButton->set_filter(oBiosFilter); + + bool bEnableRTC = m_poConfig->oGetKey("enable_rtc"); + m_poRTCCheckButton->set_active(bEnableRTC); +} + +void GameBoyAdvanceConfigDialog::vOnSaveTypeChanged() +{ + int iSaveType = m_poSaveTypeComboBox->get_active_row_number(); + m_poConfig->vSetKey("save_type", iSaveType); + m_poWindow->vApplyConfigGBASaveType(); +} + +void GameBoyAdvanceConfigDialog::vOnFlashSizeChanged() +{ + int iFlashSize = m_poFlashSizeComboBox->get_active_row_number(); + if (iFlashSize == 0) + { + m_poConfig->vSetKey("flash_size", 64); + } + else + { + m_poConfig->vSetKey("flash_size", 128); + } + + m_poWindow->vApplyConfigGBAFlashSize(); +} + +void GameBoyAdvanceConfigDialog::vOnUseBiosChanged() +{ + bool bUseBios = m_poBiosCheckButton->get_active(); + m_poConfig->vSetKey("use_bios_file", bUseBios); + m_poBiosFileChooserButton->set_sensitive(bUseBios); +} + +void GameBoyAdvanceConfigDialog::vOnBiosSelectionChanged() +{ + std::string sBios = m_poBiosFileChooserButton->get_filename(); + m_poConfig->vSetKey("bios_file", sBios); +} + +void GameBoyAdvanceConfigDialog::vOnEnableRTCChanged() +{ + bool bEnableRTC = m_poRTCCheckButton->get_active(); + m_poConfig->vSetKey("enable_rtc", bEnableRTC); +} + +} // namespace VBA diff --git a/src/gtk/gameboyadvanceconfig.h b/src/gtk/gameboyadvanceconfig.h new file mode 100644 index 0000000..3d2294b --- /dev/null +++ b/src/gtk/gameboyadvanceconfig.h @@ -0,0 +1,58 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 2008 VBA-M development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef __VBA_GAMEBOYADVANCECONFIG_H__ +#define __VBA_GAMEBOYADVANCECONFIG_H__ + +#include +#include + +#include "configfile.h" +#include "window.h" + +namespace VBA +{ + +class GameBoyAdvanceConfigDialog : public Gtk::Dialog +{ +public: + GameBoyAdvanceConfigDialog(GtkDialog* _pstDialog, const Glib::RefPtr& refBuilder); + + void vSetConfig(Config::Section * _poConfig, VBA::Window * _poWindow); + +private: + void vOnSaveTypeChanged(); + void vOnFlashSizeChanged(); + void vOnUseBiosChanged(); + void vOnBiosSelectionChanged(); + void vOnEnableRTCChanged(); + + VBA::Window * m_poWindow; + + Config::Section * m_poConfig; + Gtk::ComboBox * m_poSaveTypeComboBox; + Gtk::ComboBox * m_poFlashSizeComboBox; + Gtk::CheckButton * m_poBiosCheckButton; + Gtk::FileChooserButton * m_poBiosFileChooserButton; + Gtk::CheckButton * m_poRTCCheckButton; +}; + +} // namespace VBA + + +#endif // __VBA_GAMEBOYADVANCECONFIG_H__ diff --git a/src/gtk/gameboycheatlist.cpp b/src/gtk/gameboycheatlist.cpp new file mode 100644 index 0000000..3657f0a --- /dev/null +++ b/src/gtk/gameboycheatlist.cpp @@ -0,0 +1,122 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 2008 VBA-M development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "gameboycheatlist.h" +#include "tools.h" + +#include + +namespace VBA +{ + +GameBoyCheatListDialog::GameBoyCheatListDialog(GtkDialog* _pstDialog, const Glib::RefPtr& refBuilder) : + CheatListDialog(_pstDialog, refBuilder) +{ + vUpdateList(); +} + +void GameBoyCheatListDialog::vAddCheat(Glib::ustring sDesc, ECheatType type, Glib::RefPtr buffer) +{ + int previous = gbCheatNumber; + + switch (type) + { + // GameShark + case CheatGS: + { + std::vector tokens; + + vTokenize(buffer->get_text(), tokens); + + for (std::vector::iterator it = tokens.begin(); + it != tokens.end(); + it++) + { + Glib::ustring sToken = it->uppercase(); + + gbAddGsCheat(sToken.c_str(), sDesc.c_str()); + } + + break; + } + // GameGenie + case CheatGG: + { + std::vector tokens; + + vTokenize(buffer->get_text(), tokens); + + for (std::vector::iterator it = tokens.begin(); + it != tokens.end(); + it++) + { + Glib::ustring sToken = it->uppercase(); + + gbAddGgCheat(sToken.c_str(), sDesc.c_str()); + } + + break; + } + default:; // silence warnings + } + // end of switch + + vUpdateList(previous); +} + +bool GameBoyCheatListDialog::vCheatListOpen(const char *file) +{ + return gbCheatsLoadCheatList(file); +} + +void GameBoyCheatListDialog::vCheatListSave(const char *file) +{ + gbCheatsSaveCheatList(file); +} + +void GameBoyCheatListDialog::vRemoveCheat(int index) +{ + gbCheatRemove(index); +} + +void GameBoyCheatListDialog::vRemoveAllCheats() +{ + gbCheatRemoveAll(); +} + +void GameBoyCheatListDialog::vToggleCheat(int index, bool enable) { + if (enable) + gbCheatEnable(index); + else + gbCheatDisable(index); +} + +void GameBoyCheatListDialog::vUpdateList(int previous) +{ + for (int i = previous; i < gbCheatNumber; i++) + { + // Add row for each newly added cheat + Gtk::TreeModel::Row row = *(m_poCheatListStore->append()); + + row[m_oRecordModel.iIndex] = i; + row[m_oRecordModel.bEnabled] = gbCheatList[i].enabled; + row[m_oRecordModel.uDesc] = gbCheatList[i].cheatDesc; + } +} + +} // namespace VBA diff --git a/src/gtk/gameboycheatlist.h b/src/gtk/gameboycheatlist.h new file mode 100644 index 0000000..5f9a9a9 --- /dev/null +++ b/src/gtk/gameboycheatlist.h @@ -0,0 +1,46 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 2008 VBA-M development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef __VBA_GAMEBOYCHEATLIST_H__ +#define __VBA_GAMEBOYCHEATLIST_H__ + +#include "../gb/gbCheats.h" +#include "cheatlist.h" + +namespace VBA +{ + +class GameBoyCheatListDialog : public CheatListDialog +{ +public: + GameBoyCheatListDialog(GtkDialog* _pstDialog, const Glib::RefPtr& refBuilder); + +protected: + void vAddCheat(Glib::ustring sDesc, ECheatType type, Glib::RefPtr buffer); + bool vCheatListOpen(const char *file); + void vCheatListSave(const char *file); + void vRemoveCheat(int index); + void vRemoveAllCheats(); + void vToggleCheat(int index, bool enable); + void vUpdateList(int previous = 0); +}; + +} // namespace VBA + + +#endif diff --git a/src/gtk/gameboyconfig.cpp b/src/gtk/gameboyconfig.cpp new file mode 100644 index 0000000..57bc667 --- /dev/null +++ b/src/gtk/gameboyconfig.cpp @@ -0,0 +1,114 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 2008 VBA-M development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "gameboyconfig.h" + +#include +#include +#include + +#include "intl.h" + +namespace VBA +{ + +static const VBA::Window::EEmulatorType aEmulatorType[] = +{ + VBA::Window::EmulatorAuto, + VBA::Window::EmulatorCGB, + VBA::Window::EmulatorSGB, + VBA::Window::EmulatorGB, + VBA::Window::EmulatorGBA, + VBA::Window::EmulatorSGB2 +}; + +GameBoyConfigDialog::GameBoyConfigDialog(GtkDialog* _pstDialog, const Glib::RefPtr& refBuilder) : + Gtk::Dialog(_pstDialog), + m_poConfig(0) +{ + refBuilder->get_widget("SystemComboBox", m_poSystemComboBox); + refBuilder->get_widget("BorderCheckButton", m_poBorderCheckButton); + refBuilder->get_widget("PrinterCheckButton", m_poPrinterCheckButton); + refBuilder->get_widget("BootRomCheckButton", m_poBootRomCheckButton); + refBuilder->get_widget("BootRomFileChooserButton", m_poBootRomFileChooserButton); + + m_poSystemComboBox->signal_changed().connect(sigc::mem_fun(*this, &GameBoyConfigDialog::vOnSystemChanged)); + m_poBorderCheckButton->signal_toggled().connect(sigc::mem_fun(*this, &GameBoyConfigDialog::vOnBorderChanged)); + m_poPrinterCheckButton->signal_toggled().connect(sigc::mem_fun(*this, &GameBoyConfigDialog::vOnPrinterChanged)); + m_poBootRomCheckButton->signal_toggled().connect(sigc::mem_fun(*this, &GameBoyConfigDialog::vOnUseBootRomChanged)); + m_poBootRomFileChooserButton->signal_selection_changed().connect(sigc::mem_fun(*this, &GameBoyConfigDialog::vOnBootRomSelectionChanged)); +} + +void GameBoyConfigDialog::vSetConfig(Config::Section * _poConfig, VBA::Window * _poWindow) +{ + m_poConfig = _poConfig; + m_poWindow = _poWindow; + + VBA::Window::EEmulatorType eDefaultEmulatorType = (VBA::Window::EEmulatorType)m_poConfig->oGetKey("emulator_type"); + m_poSystemComboBox->set_active(aEmulatorType[eDefaultEmulatorType]); + + bool bBorder = m_poConfig->oGetKey("gb_border"); + m_poBorderCheckButton->set_active(bBorder); + + bool bPrinter = m_poConfig->oGetKey("gb_printer"); + m_poPrinterCheckButton->set_active(bPrinter); + + bool bUseBootRom = m_poConfig->oGetKey("gb_use_bios_file"); + m_poBootRomCheckButton->set_active(bUseBootRom); + m_poBootRomFileChooserButton->set_sensitive(bUseBootRom); + + std::string sBootRom = m_poConfig->oGetKey("gb_bios_file"); + m_poBootRomFileChooserButton->set_filename(sBootRom); +} + +void GameBoyConfigDialog::vOnSystemChanged() +{ + int iSystem = m_poSystemComboBox->get_active_row_number(); + m_poConfig->vSetKey("emulator_type", aEmulatorType[iSystem]); + + m_poWindow->vApplyConfigGBSystem(); +} + +void GameBoyConfigDialog::vOnBorderChanged() +{ + bool bBorder = m_poBorderCheckButton->get_active(); + m_poConfig->vSetKey("gb_border", bBorder); + m_poWindow->vApplyConfigGBBorder(); +} + +void GameBoyConfigDialog::vOnPrinterChanged() +{ + bool bPrinter = m_poPrinterCheckButton->get_active(); + m_poConfig->vSetKey("gb_printer", bPrinter); + m_poWindow->vApplyConfigGBPrinter(); +} + +void GameBoyConfigDialog::vOnUseBootRomChanged() +{ + bool bUseBootRom = m_poBootRomCheckButton->get_active(); + m_poConfig->vSetKey("gb_use_bios_file", bUseBootRom); + m_poBootRomFileChooserButton->set_sensitive(bUseBootRom); +} + +void GameBoyConfigDialog::vOnBootRomSelectionChanged() +{ + std::string sBootRom = m_poBootRomFileChooserButton->get_filename(); + m_poConfig->vSetKey("gb_bios_file", sBootRom); +} + +} // namespace VBA diff --git a/src/gtk/gameboyconfig.h b/src/gtk/gameboyconfig.h new file mode 100644 index 0000000..d5a2b77 --- /dev/null +++ b/src/gtk/gameboyconfig.h @@ -0,0 +1,58 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 2008 VBA-M development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef __VBA_GAMEBOYCONFIG_H__ +#define __VBA_GAMEBOYCONFIG_H__ + +#include +#include + +#include "configfile.h" +#include "window.h" + +namespace VBA +{ + +class GameBoyConfigDialog : public Gtk::Dialog +{ +public: + GameBoyConfigDialog(GtkDialog* _pstDialog, const Glib::RefPtr& refBuilder); + + void vSetConfig(Config::Section * _poConfig, VBA::Window * _poWindow); + +private: + void vOnSystemChanged(); + void vOnBorderChanged(); + void vOnPrinterChanged(); + void vOnUseBootRomChanged(); + void vOnBootRomSelectionChanged(); + + VBA::Window * m_poWindow; + + Config::Section * m_poConfig; + Gtk::ComboBox * m_poSystemComboBox; + Gtk::CheckButton * m_poBorderCheckButton; + Gtk::CheckButton * m_poPrinterCheckButton; + Gtk::CheckButton * m_poBootRomCheckButton; + Gtk::FileChooserButton * m_poBootRomFileChooserButton; +}; + +} // namespace VBA + + +#endif // __VBA_GAMEBOYCONFIG_H__ diff --git a/src/gtk/generalconfig.cpp b/src/gtk/generalconfig.cpp new file mode 100644 index 0000000..fc01e79 --- /dev/null +++ b/src/gtk/generalconfig.cpp @@ -0,0 +1,104 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 2011 VBA-M development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "generalconfig.h" + +#include +#include +#include + +#include "intl.h" + +namespace VBA +{ + +PreferencesDialog::PreferencesDialog(GtkDialog* _pstDialog, const Glib::RefPtr& refBuilder) : + Gtk::Dialog(_pstDialog), + m_poConfig(0) +{ + refBuilder->get_widget("PauseWhenInactiveCheckButton", m_poPauseWhenInactiveCheckButton); + refBuilder->get_widget("FrameSkipAutomaticCheckButton", m_poFrameSkipAutomaticCheckButton); + refBuilder->get_widget("FrameSkipLevelSpinButton", m_poFrameSkipLevelSpinButton); + refBuilder->get_widget("SpeedIndicatorComboBox", m_poSpeedIndicatorComboBox); + + m_poPauseWhenInactiveCheckButton->signal_toggled().connect(sigc::mem_fun(*this, &PreferencesDialog::vOnPauseWhenInactiveChanged)); + m_poFrameSkipAutomaticCheckButton->signal_toggled().connect(sigc::mem_fun(*this, &PreferencesDialog::vOnFrameskipChanged)); + m_poFrameSkipLevelSpinButton->signal_changed().connect(sigc::mem_fun(*this, &PreferencesDialog::vOnFrameskipChanged)); + m_poSpeedIndicatorComboBox->signal_changed().connect(sigc::mem_fun(*this, &PreferencesDialog::vOnSpeedIndicatorChanged)); + +} + +void PreferencesDialog::vSetConfig(Config::Section * _poConfig, VBA::Window * _poWindow) +{ + m_poConfig = _poConfig; + m_poWindow = _poWindow; + + bool bPauseWhenInactive = m_poConfig->oGetKey("pause_when_inactive"); + m_poPauseWhenInactiveCheckButton->set_active(bPauseWhenInactive); + + std::string sFrameskip = m_poConfig->oGetKey("frameskip"); + int iFrameskip = 0; + bool bAutoFrameskip = false; + + if (sFrameskip == "auto") + bAutoFrameskip = true; + else + iFrameskip = m_poConfig->oGetKey("frameskip"); + + m_poFrameSkipAutomaticCheckButton->set_active(bAutoFrameskip); + m_poFrameSkipLevelSpinButton->set_sensitive(!bAutoFrameskip); + m_poFrameSkipLevelSpinButton->set_value(iFrameskip); + + int iShowSpeed = m_poConfig->oGetKey("show_speed"); + m_poSpeedIndicatorComboBox->set_active(iShowSpeed); +} + +void PreferencesDialog::vOnPauseWhenInactiveChanged() +{ + bool bPauseWhenInactive = m_poPauseWhenInactiveCheckButton->get_active(); + m_poConfig->vSetKey("pause_when_inactive", bPauseWhenInactive); +} + +void PreferencesDialog::vOnFrameskipChanged() +{ + bool bAutoFrameskip = m_poFrameSkipAutomaticCheckButton->get_active(); + + if (bAutoFrameskip) + { + m_poConfig->vSetKey("frameskip", "auto"); + } + else + { + int iFrameskip = m_poFrameSkipLevelSpinButton->get_value(); + m_poConfig->vSetKey("frameskip", iFrameskip); + } + + m_poFrameSkipLevelSpinButton->set_sensitive(!bAutoFrameskip); + + m_poWindow->vApplyConfigFrameskip(); +} + +void PreferencesDialog::vOnSpeedIndicatorChanged() +{ + int iShowSpeed = m_poSpeedIndicatorComboBox->get_active_row_number(); + m_poConfig->vSetKey("show_speed", iShowSpeed); + + m_poWindow->vApplyConfigShowSpeed(); +} + +} // namespace VBA diff --git a/src/gtk/generalconfig.h b/src/gtk/generalconfig.h new file mode 100644 index 0000000..e34afca --- /dev/null +++ b/src/gtk/generalconfig.h @@ -0,0 +1,56 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 2008 VBA-M development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef __VBA_GENERALCONFIG_H__ +#define __VBA_GENERALCONFIG_H__ + +#include + +#include "configfile.h" +#include "window.h" + +namespace VBA +{ + +class PreferencesDialog : public Gtk::Dialog +{ +public: + PreferencesDialog(GtkDialog* _pstDialog, const Glib::RefPtr& refBuilder); + + void vSetConfig(Config::Section * _poConfig, VBA::Window * _poWindow); + +private: + void vOnPauseWhenInactiveChanged(); + void vOnFrameskipChanged(); + void vOnSpeedIndicatorChanged(); + + + VBA::Window * m_poWindow; + + Config::Section * m_poConfig; + Gtk::CheckButton * m_poPauseWhenInactiveCheckButton; + Gtk::CheckButton * m_poFrameSkipAutomaticCheckButton; + Gtk::SpinButton * m_poFrameSkipLevelSpinButton; + Gtk::ComboBox * m_poSpeedIndicatorComboBox; + +}; + +} // namespace VBA + + +#endif // __VBA_GENERALCONFIG_H__ diff --git a/src/gtk/gvbam.desktop b/src/gtk/gvbam.desktop new file mode 100644 index 0000000..e3d050f --- /dev/null +++ b/src/gtk/gvbam.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Version=1.0 +Type=Application +Name=VBA-M +GenericName=GameBoy Advance Emulator +Comment=Nindendo GameBoy Advance Emulator +Exec=gvbam %f +Icon=vbam +Categories=Game;Emulator;GTK; +MimeType=application/x-gameboy-rom;application/x-gameboy-advance-rom;application/x-dmg-rom;application/x-agb-rom;application/x-gb-rom;application/x-gba-rom; diff --git a/src/gtk/icons/16x16/apps/vbam.png b/src/gtk/icons/16x16/apps/vbam.png new file mode 100644 index 0000000..1390add Binary files /dev/null and b/src/gtk/icons/16x16/apps/vbam.png differ diff --git a/src/gtk/icons/22x22/apps/vbam.png b/src/gtk/icons/22x22/apps/vbam.png new file mode 100644 index 0000000..10c5cba Binary files /dev/null and b/src/gtk/icons/22x22/apps/vbam.png differ diff --git a/src/gtk/icons/24x24/apps/vbam.png b/src/gtk/icons/24x24/apps/vbam.png new file mode 100644 index 0000000..b19688f Binary files /dev/null and b/src/gtk/icons/24x24/apps/vbam.png differ diff --git a/src/gtk/icons/32x32/apps/vbam.png b/src/gtk/icons/32x32/apps/vbam.png new file mode 100644 index 0000000..6395293 Binary files /dev/null and b/src/gtk/icons/32x32/apps/vbam.png differ diff --git a/src/gtk/icons/scalable/apps/vbam.svg b/src/gtk/icons/scalable/apps/vbam.svg new file mode 100644 index 0000000..ebebf75 --- /dev/null +++ b/src/gtk/icons/scalable/apps/vbam.svg @@ -0,0 +1,836 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Mintendo GameJoy Advance + 2006-11-25 + + + Matteo Drera + + + + + Nintendo Gameboy Advance ;) + + + + + + nintendo + mintendo + gameboy + gamejoy + advance + visualboy + emulator + gba + gb + rom + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/gtk/intl.h b/src/gtk/intl.h new file mode 100644 index 0000000..039261b --- /dev/null +++ b/src/gtk/intl.h @@ -0,0 +1,36 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef __VBA_INTL_H__ +#define __VBA_INTL_H__ + +#ifdef ENABLE_NLS +# include +#else +# define _(String) (String) +# define N_(String) (String) +# define textdomain(String) (String) +# define gettext(String) (String) +# define dgettext(Domain,String) (String) +# define dcgettext(Domain,String,Type) (String) +# define bindtextdomain(Domain,Directory) (Domain) +#endif + + +#endif // __VBA_INTL_H__ diff --git a/src/gtk/joypadconfig.cpp b/src/gtk/joypadconfig.cpp new file mode 100644 index 0000000..b12e212 --- /dev/null +++ b/src/gtk/joypadconfig.cpp @@ -0,0 +1,319 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "joypadconfig.h" + +#include + +#include "intl.h" + +namespace VBA +{ + +const JoypadConfigDialog::SJoypadKey JoypadConfigDialog::m_astKeys[] = +{ + { KEY_UP, N_("Up :") }, + { KEY_DOWN, N_("Down :") }, + { KEY_LEFT, N_("Left :") }, + { KEY_RIGHT, N_("Right :") }, + { KEY_BUTTON_A, N_("Button A :") }, + { KEY_BUTTON_B, N_("Button B :") }, + { KEY_BUTTON_L, N_("Button L :") }, + { KEY_BUTTON_R, N_("Button R :") }, + { KEY_BUTTON_SELECT, N_("Select :") }, + { KEY_BUTTON_START, N_("Start :") }, + { KEY_BUTTON_SPEED, N_("Speed :") }, + { KEY_BUTTON_CAPTURE, N_("Capture :") }, + { KEY_BUTTON_AUTO_A, N_("Autofire A :") }, + { KEY_BUTTON_AUTO_B, N_("Autofire B :") } +}; + +JoypadConfigDialog::JoypadConfigDialog(Config::Section * _poConfig) : + Gtk::Dialog(_("Joypad config"), true, true), + m_oTitleHBox(false, 5), + m_oTitleLabel(_("Joypad :"), Gtk::ALIGN_RIGHT), + m_oDefaultJoypad(_("Default joypad")), + m_oTable(G_N_ELEMENTS(m_astKeys), 2, false), + m_iCurrentEntry(-1), + m_bUpdating(false), + m_ePad(PAD_MAIN), + m_poConfig(_poConfig) +{ + // Joypad selection + m_oTitleCombo.append_text("1"); + m_oTitleCombo.append_text("2"); + m_oTitleCombo.append_text("3"); + m_oTitleCombo.append_text("4"); + + m_oTitleHBox.pack_start(m_oTitleLabel, Gtk::PACK_SHRINK); + m_oTitleHBox.pack_start(m_oTitleCombo); + + // Joypad buttons + for (guint i = 0; i < G_N_ELEMENTS(m_astKeys); i++) + { + Gtk::Label * poLabel = Gtk::manage( new Gtk::Label(gettext(m_astKeys[i].m_csKeyName), Gtk::ALIGN_RIGHT) ); + Gtk::Entry * poEntry = Gtk::manage( new Gtk::Entry() ); + m_oTable.attach(* poLabel, 0, 1, i, i + 1); + m_oTable.attach(* poEntry, 1, 2, i, i + 1); + m_oEntries.push_back(poEntry); + + poEntry->signal_focus_in_event().connect(sigc::bind( + sigc::mem_fun(*this, &JoypadConfigDialog::bOnEntryFocusIn), i)); + poEntry->signal_focus_out_event().connect(sigc::mem_fun(*this, &JoypadConfigDialog::bOnEntryFocusOut)); + } + + // Dialog validation button + m_poOkButton = add_button(Gtk::Stock::CLOSE, Gtk::RESPONSE_CLOSE); + + // Layout + m_oTitleHBox.set_border_width(5); + m_oTable.set_border_width(5); + m_oTable.set_spacings(5); + get_vbox()->set_spacing(5); + get_vbox()->pack_start(m_oTitleHBox); + get_vbox()->pack_start(m_oSeparator); + get_vbox()->pack_start(m_oDefaultJoypad); + get_vbox()->pack_start(m_oTable); + + // Signals and default values + m_oConfigSig = Glib::signal_timeout().connect(sigc::mem_fun(*this, &JoypadConfigDialog::bOnConfigIdle), + 50); + m_oDefaultJoypad.signal_toggled().connect(sigc::mem_fun(*this, + &JoypadConfigDialog::vOnDefaultJoypadSelect) ); + m_oTitleCombo.signal_changed().connect(sigc::mem_fun(*this, + &JoypadConfigDialog::vOnJoypadSelect) ); + + m_oTitleCombo.set_active_text("1"); + + show_all_children(); +} + +JoypadConfigDialog::~JoypadConfigDialog() +{ + m_oConfigSig.disconnect(); +} + +void JoypadConfigDialog::vUpdateEntries() +{ + m_bUpdating = true; + m_oDefaultJoypad.set_active(inputGetDefaultJoypad() == m_ePad); + + for (guint i = 0; i < m_oEntries.size(); i++) + { + std::string csName; + + guint uiKeyval = inputGetKeymap(m_ePad, m_astKeys[i].m_eKeyFlag); + int dev = uiKeyval >> 16; + if (dev == 0) + { + csName = gdk_keyval_name(uiKeyval); + } + else + { + int what = uiKeyval & 0xffff; + std::stringstream os; + os << _("Joy ") << dev; + + if(what >= 128) + { + // joystick button + int button = what - 128; + os << _(" Button ") << button; + } + else if (what < 0x20) + { + // joystick axis + int dir = what & 1; + what >>= 1; + os << _(" Axis ") << what << (dir?'-':'+'); + } + else if (what < 0x30) + { + // joystick hat + int dir = (what & 3); + what = (what & 15); + what >>= 2; + os << _(" Hat ") << what << " "; + switch (dir) + { + case 0: os << _("Up"); break; + case 1: os << _("Down"); break; + case 2: os << _("Right"); break; + case 3: os << _("Left"); break; + } + } + + csName = os.str(); + } + + if (csName.empty()) + { + m_oEntries[i]->set_text(_("")); + } + else + { + m_oEntries[i]->set_text(csName); + } + } + + m_bUpdating = false; +} + +bool JoypadConfigDialog::bOnEntryFocusIn(GdkEventFocus * _pstEvent, + guint _uiEntry) +{ + m_iCurrentEntry = _uiEntry; + + return false; +} + +bool JoypadConfigDialog::bOnEntryFocusOut(GdkEventFocus * _pstEvent) +{ + m_iCurrentEntry = -1; + + return false; +} + +bool JoypadConfigDialog::on_key_press_event(GdkEventKey * _pstEvent) +{ + if (m_iCurrentEntry < 0) + { + return Gtk::Window::on_key_press_event(_pstEvent); + } + + // Forward the keyboard event by faking a SDL event + SDL_Event event; + event.type = SDL_KEYDOWN; + event.key.keysym.sym = (SDLKey)_pstEvent->keyval; + vOnInputEvent(event); + + return true; +} + +void JoypadConfigDialog::on_response(int response_id) +{ + m_poConfig->vSetKey("active_joypad", inputGetDefaultJoypad()); +} + +void JoypadConfigDialog::vOnInputEvent(const SDL_Event &event) +{ + if (m_iCurrentEntry < 0) + { + return; + } + + int code = inputGetEventCode(event); + if (!code) return; + inputSetKeymap(m_ePad, m_astKeys[m_iCurrentEntry].m_eKeyFlag, code); + vUpdateEntries(); + + if (m_iCurrentEntry + 1 < (gint)m_oEntries.size()) + { + m_oEntries[m_iCurrentEntry + 1]->grab_focus(); + } + else + { + m_poOkButton->grab_focus(); + } +} + +bool JoypadConfigDialog::bOnConfigIdle() +{ + SDL_Event event; + while(SDL_PollEvent(&event)) + { + switch(event.type) + { + case SDL_JOYAXISMOTION: + if (abs(event.jaxis.value) < 16384) continue; + if (event.jaxis.which != m_oPreviousEvent.jaxis.which + || event.jaxis.axis != m_oPreviousEvent.jaxis.axis + || (event.jaxis.value > 0 && m_oPreviousEvent.jaxis.value < 0) + || (event.jaxis.value < 0 && m_oPreviousEvent.jaxis.value > 0)) + { + vOnInputEvent(event); + m_oPreviousEvent = event; + } + vEmptyEventQueue(); + break; + case SDL_JOYBUTTONUP: + vOnInputEvent(event); + vEmptyEventQueue(); + break; + case SDL_JOYHATMOTION: + if (event.jhat.value) + { + vOnInputEvent(event); + vEmptyEventQueue(); + } + break; + } + } + + return true; +} + +void JoypadConfigDialog::vEmptyEventQueue() +{ + // Empty the SDL event queue + SDL_Event event; + while(SDL_PollEvent(&event)); +} + +void JoypadConfigDialog::vOnJoypadSelect() +{ + std::string oText = m_oTitleCombo.get_active_text(); + + if (oText == "1") + { + m_ePad = PAD_1; + } + else if (oText == "2") + { + m_ePad = PAD_2; + } + else if (oText == "3") + { + m_ePad = PAD_3; + } + else if (oText == "4") + { + m_ePad = PAD_4; + } + + vEmptyEventQueue(); + memset(&m_oPreviousEvent, 0, sizeof(m_oPreviousEvent)); + + vUpdateEntries(); +} + +void JoypadConfigDialog::vOnDefaultJoypadSelect() +{ + if (m_bUpdating) return; + + if (m_oDefaultJoypad.get_active()) + { + inputSetDefaultJoypad(m_ePad); + } + else + { + inputSetDefaultJoypad(PAD_MAIN); + } +} + +} // namespace VBA diff --git a/src/gtk/joypadconfig.h b/src/gtk/joypadconfig.h new file mode 100644 index 0000000..7b62160 --- /dev/null +++ b/src/gtk/joypadconfig.h @@ -0,0 +1,86 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef __VBA_JOYPADCONFIG_H__ +#define __VBA_JOYPADCONFIG_H__ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "../sdl/inputSDL.h" +#include "configfile.h" + +namespace VBA +{ + +class JoypadConfigDialog : public Gtk::Dialog +{ +public: + JoypadConfigDialog(Config::Section * _poConfig); + virtual ~JoypadConfigDialog(); + +protected: + bool bOnEntryFocusIn(GdkEventFocus * _pstEvent, guint _uiEntry); + bool bOnEntryFocusOut(GdkEventFocus * _pstEvent); + + void vOnInputEvent(const SDL_Event &event); + bool on_key_press_event(GdkEventKey * _pstEvent); + void on_response(int response_id); + +private: + struct SJoypadKey + { + const EKey m_eKeyFlag; + const char * m_csKeyName; + }; + + Gtk::HBox m_oTitleHBox; + Gtk::Label m_oTitleLabel; + Gtk::ComboBoxText m_oTitleCombo; + Gtk::HSeparator m_oSeparator; + Gtk::CheckButton m_oDefaultJoypad; + Gtk::Table m_oTable; + Gtk::Button * m_poOkButton; + std::vector m_oEntries; + gint m_iCurrentEntry; + bool m_bUpdating; + static const SJoypadKey m_astKeys[]; + sigc::connection m_oConfigSig; + SDL_Event m_oPreviousEvent; + EPad m_ePad; + Config::Section * m_poConfig; + + bool bOnConfigIdle(); + void vOnJoypadSelect(); + void vOnDefaultJoypadSelect(); + void vUpdateEntries(); + void vEmptyEventQueue(); +}; + +} // namespace VBA + + +#endif // __VBA_JOYPADCONFIG_H__ diff --git a/src/gtk/main.cpp b/src/gtk/main.cpp new file mode 100644 index 0000000..cb078e3 --- /dev/null +++ b/src/gtk/main.cpp @@ -0,0 +1,125 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include +#include +#include + +#ifdef USE_OPENGL +#include +#endif // USE_OPENGL + +#include "window.h" +#include "intl.h" + +int main(int argc, char * argv[]) +{ + bool bShowVersion = false; + Glib::OptionGroup::vecustrings listRemaining; + +#ifdef ENABLE_NLS + setlocale(LC_ALL, ""); + bindtextdomain("gvbam", LOCALEDIR); + textdomain("gvbam"); +#endif // ENABLE_NLS + + Glib::set_application_name(_("VBA-M")); + + Gtk::Main oKit(argc, argv); + +#ifdef USE_OPENGL + Gtk::GL::init(argc, argv); +#endif // USE_OPENGL + + Glib::OptionContext oContext; + Glib::OptionGroup oGroup("main_group", _("Main VBA-M options")); + + Glib::OptionEntry oVersion; + oVersion.set_long_name("version"); + oVersion.set_short_name('v'); + oVersion.set_description(_("Output version information.")); + oGroup.add_entry(oVersion, bShowVersion); + + Glib::OptionEntry oFileName; + oFileName.set_long_name(G_OPTION_REMAINING); + oFileName.set_description(G_OPTION_REMAINING); + oGroup.add_entry(oFileName, listRemaining); + + oContext.set_main_group(oGroup); + + try + { + oContext.parse(argc, argv); + } + catch (const Glib::Error& e) + { + Gtk::MessageDialog oDialog(e.what(), + false, + Gtk::MESSAGE_ERROR, + Gtk::BUTTONS_OK); + oDialog.run(); + return 1; + } + + if (bShowVersion) + { + g_print(_("VisualBoyAdvance version %s [GTK+]\n"), VERSION); + exit(0); + } + + Gtk::Window::set_default_icon_name("vbam"); + + std::string sGtkBuilderFile = VBA::Window::sGetUiFilePath("vbam.ui"); + + Glib::RefPtr poXml; + try + { + poXml = Gtk::Builder::create(); + poXml->add_from_file(sGtkBuilderFile, "accelgroup1"); + poXml->add_from_file(sGtkBuilderFile, "MainWindow"); + } + catch (const Gtk::BuilderError & e) + { + Gtk::MessageDialog oDialog(e.what(), + false, + Gtk::MESSAGE_ERROR, + Gtk::BUTTONS_OK); + oDialog.run(); + return 1; + } + + VBA::Window * poWindow = NULL; + poXml->get_widget_derived("MainWindow", poWindow); + + if (listRemaining.size() == 1) + { + // Display the window before loading the file + poWindow->show(); + while (Gtk::Main::events_pending()) + { + Gtk::Main::iteration(); + } + + poWindow->bLoadROM(listRemaining[0]); + } + + Gtk::Main::run(*poWindow); + delete poWindow; + + return 0; +} diff --git a/src/gtk/screenarea-cairo.cpp b/src/gtk/screenarea-cairo.cpp new file mode 100644 index 0000000..25a81ac --- /dev/null +++ b/src/gtk/screenarea-cairo.cpp @@ -0,0 +1,90 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "screenarea-cairo.h" + +#include + +namespace VBA +{ + +template T min( T x, T y ) { return x < y ? x : y; } +template T max( T x, T y ) { return x > y ? x : y; } + +ScreenAreaCairo::ScreenAreaCairo(int _iWidth, int _iHeight, int _iScale) : + ScreenArea(_iWidth, _iHeight, _iScale) +{ + vUpdateSize(); +} + +void ScreenAreaCairo::vDrawPixels(u8 * _puiData) +{ + ScreenArea::vDrawPixels(_puiData); + + queue_draw(); +} + +bool ScreenAreaCairo::on_expose_event(GdkEventExpose * _pstEvent) +{ + DrawingArea::on_expose_event(_pstEvent); + Cairo::RefPtr< Cairo::ImageSurface > poImage; + Cairo::RefPtr< Cairo::SurfacePattern > poPattern; + Cairo::RefPtr< Cairo::Context > poContext; + Cairo::Matrix oMatrix; + const int iScaledPitch = (m_iScaledWidth + 1) * sizeof(u32); + + poContext = get_window()->create_cairo_context(); + + poContext->set_identity_matrix(); + poContext->scale(m_dScaleFactor, m_dScaleFactor); + + poImage = Cairo::ImageSurface::create((u8 *)m_puiPixels, Cairo::FORMAT_RGB24, + m_iScaledWidth, m_iScaledHeight, iScaledPitch); + + cairo_matrix_init_translate(&oMatrix, -m_iAreaLeft, -m_iAreaTop); + poPattern = Cairo::SurfacePattern::create(poImage); + poPattern->set_filter(Cairo::FILTER_NEAREST); + poPattern->set_matrix (oMatrix); + poContext->set_source_rgb(0.0, 0.0, 0.0); + poContext->paint(); + + poContext->set_source(poPattern); + poContext->paint(); + + return true; +} + +void ScreenAreaCairo::vDrawBlackScreen() +{ + if (m_puiPixels && is_realized()) + { + memset(m_puiPixels, 0, m_iHeight * (m_iWidth + 1) * sizeof(u32)); + queue_draw_area(0, 0, get_width(), get_height()); + } +} + +void ScreenAreaCairo::vOnWidgetResize() +{ + m_dScaleFactor = min(get_height() / (double)m_iHeight, get_width() / (double)m_iWidth); + m_dScaleFactor /= m_iFilterScale; + + m_iAreaTop = (get_height() / m_dScaleFactor - m_iHeight * m_iFilterScale) / 2; + m_iAreaLeft = (get_width() / m_dScaleFactor - m_iWidth * m_iFilterScale) / 2; +} + +} // namespace VBA diff --git a/src/gtk/screenarea-cairo.h b/src/gtk/screenarea-cairo.h new file mode 100644 index 0000000..5a5be90 --- /dev/null +++ b/src/gtk/screenarea-cairo.h @@ -0,0 +1,48 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef __VBA_SCREENAREA_CAIRO_H__ +#define __VBA_SCREENAREA_CAIRO_H__ + +#include "screenarea.h" + +namespace VBA +{ + +class ScreenAreaCairo : public ScreenArea +{ +public: + ScreenAreaCairo(int _iWidth, int _iHeight, int _iScale = 1); + void vDrawPixels(u8 * _puiData); + void vDrawBlackScreen(); + +protected: + bool on_expose_event(GdkEventExpose * _pstEvent); + void vOnWidgetResize(); + +private: + double m_dScaleFactor; + int m_iAreaTop; + int m_iAreaLeft; +}; + +} // namespace VBA + + +#endif // __VBA_SCREENAREA_CAIRO_H__ diff --git a/src/gtk/screenarea-opengl.cpp b/src/gtk/screenarea-opengl.cpp new file mode 100644 index 0000000..924eb29 --- /dev/null +++ b/src/gtk/screenarea-opengl.cpp @@ -0,0 +1,192 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "screenarea-opengl.h" +#include "intl.h" + +#include + +namespace VBA +{ + +template T min( T x, T y ) { return x < y ? x : y; } +template T max( T x, T y ) { return x > y ? x : y; } + +ScreenAreaGl::ScreenAreaGl(int _iWidth, int _iHeight, int _iScale) : + ScreenArea(_iWidth, _iHeight, _iScale), + m_uiScreenTexture(0), + m_iTextureSize(0) +{ + Glib::RefPtr glconfig; + + glconfig = Gdk::GL::Config::create(Gdk::GL::MODE_RGB | + Gdk::GL::MODE_DOUBLE); + if (!glconfig) + { + fprintf(stderr, _("*** OpenGL : Cannot open display.\n")); + throw std::exception(); + } + + set_gl_capability(glconfig); + + vUpdateSize(); +} + +void ScreenAreaGl::vUpdateTexture() +{ + // Calculate the new texture size as a the smallest working power of two + // TODO: Support the ARB_texture_rectangle extension + int iExpX = 0, iExpY = 0; + for (int i = m_iScaledWidth; i; i >>= 1, ++iExpX); + for (int i = m_iScaledHeight; i; i >>= 1, ++iExpY); + int iNewTextureSize = 1 << max(iExpX, iExpY); + + // Notify the system if the texture size changed + if (iNewTextureSize != m_iTextureSize) { + m_iTextureSize = iNewTextureSize; + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, iNewTextureSize, iNewTextureSize, + 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); + } +} + +void ScreenAreaGl::on_realize() +{ + Gtk::DrawingArea::on_realize(); + + Glib::RefPtr glwindow = get_gl_window(); + if (!glwindow->gl_begin(get_gl_context())) + return; + + glDisable(GL_CULL_FACE); + glEnable(GL_TEXTURE_2D); + + if (glIsTexture(m_uiScreenTexture)) + glDeleteTextures(1, &m_uiScreenTexture); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + glOrtho(0.0, 1.0, 1.0, 0.0, 0.0, 1.0); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glGenTextures(1, &m_uiScreenTexture); + glBindTexture(GL_TEXTURE_2D, m_uiScreenTexture); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + vUpdateTexture(); + + glClearColor(0.0, 0.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + glwindow->gl_end(); +} + +void ScreenAreaGl::vDrawPixels(u8 * _puiData) +{ + ScreenArea::vDrawPixels(_puiData); + + queue_draw_area(0, 0, get_width(), get_height()); +} + +void ScreenAreaGl::vDrawBlackScreen() +{ + if (m_puiPixels && is_realized()) + { + memset(m_puiPixels, 0, m_iHeight * (m_iWidth + 1) * sizeof(u32)); + queue_draw_area(0, 0, get_width(), get_height()); + } +} + +void ScreenAreaGl::vOnWidgetResize() +{ + Glib::RefPtr glwindow = get_gl_window(); + + int iWidth = get_width(); + int iHeight = get_height(); + + float fScreenAspect = (float) m_iScaledWidth / m_iScaledHeight, + fWindowAspect = (float) iWidth / iHeight; + + if (!glwindow->gl_begin(get_gl_context())) + return; + + if (fWindowAspect == fScreenAspect) + glViewport(0, 0, iWidth, iHeight); + else if (fWindowAspect < fScreenAspect) { + int iAspectHeight = (int)(iWidth / fScreenAspect); + glViewport(0, (iHeight - iAspectHeight) / 2, iWidth, iAspectHeight); + } else { + int iAspectWidth = (int)(iHeight * fScreenAspect); + glViewport((iWidth - iAspectWidth) / 2, 0, iAspectWidth, iHeight); + } + + glwindow->gl_end(); +} + +bool ScreenAreaGl::on_expose_event(GdkEventExpose * _pstEvent) +{ + if (!m_bEnableRender) + return true; + + Glib::RefPtr glwindow = get_gl_window(); + if (!glwindow->gl_begin(get_gl_context())) + return false; + + glClear( GL_COLOR_BUFFER_BIT ); + glPixelStorei(GL_UNPACK_ROW_LENGTH, m_iScaledWidth + 1); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_iScaledWidth + 1, m_iScaledHeight, + GL_RGBA, GL_UNSIGNED_BYTE, m_puiPixels); + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2f(0.0f, 0.0f); + glVertex3i(0, 0, 0); + glTexCoord2f(m_iScaledWidth / (GLfloat) m_iTextureSize, 0.0f); + glVertex3i(1, 0, 0); + glTexCoord2f(0.0f, m_iScaledHeight / (GLfloat) m_iTextureSize); + glVertex3i(0, 1, 0); + glTexCoord2f(m_iScaledWidth / (GLfloat) m_iTextureSize, + m_iScaledHeight / (GLfloat) m_iTextureSize); + glVertex3i(1, 1, 0); + glEnd(); + + glwindow->swap_buffers(); + + glwindow->gl_end(); + + return true; +} + +void ScreenAreaGl::vOnSizeUpdated() +{ + if (!is_realized()) + return; + + Glib::RefPtr glwindow = get_gl_window(); + if (!glwindow->gl_begin(get_gl_context())) + return; + + vUpdateTexture(); + + glwindow->gl_end(); +} + +} // namespace VBA diff --git a/src/gtk/screenarea-opengl.h b/src/gtk/screenarea-opengl.h new file mode 100644 index 0000000..acb0b01 --- /dev/null +++ b/src/gtk/screenarea-opengl.h @@ -0,0 +1,53 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef __VBA_SCREENAREA_OPENGL_H__ +#define __VBA_SCREENAREA_OPENGL_H__ + +#include "screenarea.h" +#include + +namespace VBA +{ + +class ScreenAreaGl : public ScreenArea, + public Gtk::GL::Widget +{ +public: + ScreenAreaGl(int _iWidth, int _iHeight, int _iScale = 1); + void vDrawPixels(u8 * _puiData); + void vDrawBlackScreen(); + +protected: + void on_realize(); + bool on_expose_event(GdkEventExpose * _pstEvent); + void vOnWidgetResize(); + +private: + GLuint m_uiScreenTexture; + int m_iTextureSize; + + void vUpdateTexture(); + void vOnSizeUpdated(); +}; + +} // namespace VBA + + +#endif // __VBA_SCREENAREA_OPENGL_H__ diff --git a/src/gtk/screenarea.cpp b/src/gtk/screenarea.cpp new file mode 100644 index 0000000..cb0e181 --- /dev/null +++ b/src/gtk/screenarea.cpp @@ -0,0 +1,244 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "screenarea.h" + +#include + +namespace VBA +{ + +ScreenArea::ScreenArea(int _iWidth, int _iHeight, int _iScale) : + m_iFilterScale(1), + m_vFilter2x(NULL), + m_vFilterIB(NULL), + m_puiPixels(NULL), + m_puiDelta(NULL), + m_iScaledWidth(_iWidth), + m_iScaledHeight(_iHeight), + m_bEnableRender(true), + m_bShowCursor(true) +{ + g_assert(_iWidth >= 1 && _iHeight >= 1 && _iScale >= 1); + + m_iWidth = _iWidth; + m_iHeight = _iHeight; + m_iScale = _iScale; + + set_events(Gdk::EXPOSURE_MASK + | Gdk::POINTER_MOTION_MASK + | Gdk::ENTER_NOTIFY_MASK + | Gdk::LEAVE_NOTIFY_MASK); + + char aiEmptyData[8]; + memset(aiEmptyData, 0, sizeof(aiEmptyData)); + Glib::RefPtr poSource = Gdk::Bitmap::create(aiEmptyData, 8, 8); + Glib::RefPtr poMask = Gdk::Bitmap::create(aiEmptyData, 8, 8); + Gdk::Color oFg; + Gdk::Color oBg; + oFg.set_rgb(0, 0, 0); + oBg.set_rgb(0, 0, 0); + + m_poEmptyCursor = new Gdk::Cursor(poSource, poMask, oFg, oBg, 0, 0); +} + +ScreenArea::~ScreenArea() +{ + if (m_puiPixels) + { + delete[] m_puiPixels; + } + + if (m_puiDelta) + { + delete[] m_puiDelta; + } + + if (m_poEmptyCursor != NULL) + { + delete m_poEmptyCursor; + } +} + +void ScreenArea::vSetSize(int _iWidth, int _iHeight) +{ + g_return_if_fail(_iWidth >= 1 && _iHeight >= 1); + + if (_iWidth != m_iWidth || _iHeight != m_iHeight) + { + m_iWidth = _iWidth; + m_iHeight = _iHeight; + vUpdateSize(); + } +} + +void ScreenArea::vSetScale(int _iScale) +{ + g_return_if_fail(_iScale >= 1); + + if (_iScale == 1) + { + vSetFilter(FilterNone); + } + + m_iScale = _iScale; + vUpdateSize(); +} + +void ScreenArea::vSetFilter(EFilter _eFilter) +{ + m_vFilter2x = pvGetFilter(_eFilter, FilterDepth32); + + m_iFilterScale = 1; + if (m_vFilter2x != NULL) + { + m_iFilterScale = 2; + } + + vUpdateSize(); +} + +void ScreenArea::vSetFilterIB(EFilterIB _eFilterIB) +{ + m_vFilterIB = pvGetFilterIB(_eFilterIB, FilterDepth32); +} + +void ScreenArea::vStartCursorTimeout() +{ + m_oCursorSig.disconnect(); + m_oCursorSig = Glib::signal_timeout().connect( + sigc::mem_fun(*this, &ScreenArea::bOnCursorTimeout), + 2000); +} + +void ScreenArea::vStopCursorTimeout() +{ + m_oCursorSig.disconnect(); +} + +void ScreenArea::vHideCursor() +{ + get_window()->set_cursor(*m_poEmptyCursor); + m_bShowCursor = false; +} + +void ScreenArea::vShowCursor() +{ + get_window()->set_cursor(); + m_bShowCursor = true; +} + +bool ScreenArea::on_motion_notify_event(GdkEventMotion * _pstEvent) +{ + if (! m_bShowCursor) + { + vShowCursor(); + } + vStartCursorTimeout(); + return false; +} + +bool ScreenArea::on_enter_notify_event(GdkEventCrossing * _pstEvent) +{ + vStartCursorTimeout(); + return false; +} + +bool ScreenArea::on_leave_notify_event(GdkEventCrossing * _pstEvent) +{ + vStopCursorTimeout(); + if (! m_bShowCursor) + { + vShowCursor(); + } + return false; +} + +bool ScreenArea::bOnCursorTimeout() +{ + vHideCursor(); + return false; +} + +void ScreenArea::vDrawPixels(u8 * _puiData) +{ + const int iSrcPitch = (m_iWidth + 1) * sizeof(u32); + const int iScaledPitch = (m_iScaledWidth + 1) * sizeof(u32); + + if (m_vFilterIB != NULL) + { + m_vFilterIB(_puiData + iSrcPitch, + iSrcPitch, + m_iWidth, + m_iHeight); + } + + if (m_vFilter2x != NULL) + { + m_vFilter2x(_puiData + iSrcPitch, + iSrcPitch, + m_puiDelta, + (u8 *)m_puiPixels, + iScaledPitch, + m_iWidth, + m_iHeight); + } + else + { + memcpy(m_puiPixels, _puiData + iSrcPitch, m_iHeight * iSrcPitch); + } +} + +void ScreenArea::vUpdateSize() +{ + if (m_puiPixels) + { + delete[] m_puiPixels; + } + + if (m_puiDelta) + { + delete[] m_puiDelta; + } + + m_iScaledWidth = m_iFilterScale * m_iWidth; + m_iScaledHeight = m_iFilterScale * m_iHeight; + + m_puiPixels = new u32[(m_iScaledWidth + 1) * m_iScaledHeight]; + m_puiDelta = new u8[(m_iWidth + 2) * (m_iHeight + 2) * sizeof(u32)]; + memset(m_puiPixels, 0, (m_iScaledWidth + 1) * m_iScaledHeight * sizeof(u32)); + memset(m_puiDelta, 255, (m_iWidth + 2) * (m_iHeight + 2) * sizeof(u32)); + + vOnSizeUpdated(); + + set_size_request(m_iScale * m_iWidth, m_iScale * m_iHeight); +} + +bool ScreenArea::on_configure_event(GdkEventConfigure * event) +{ + vOnWidgetResize(); + + return true; +} + +void ScreenArea::vSetEnableRender(bool _bEnable) +{ + m_bEnableRender = _bEnable; +} + +} // namespace VBA diff --git a/src/gtk/screenarea.h b/src/gtk/screenarea.h new file mode 100644 index 0000000..65b6019 --- /dev/null +++ b/src/gtk/screenarea.h @@ -0,0 +1,82 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef __VBA_SCREENAREA_H__ +#define __VBA_SCREENAREA_H__ + +#include +#include + +#include "filters.h" + +namespace VBA +{ + +class ScreenArea : public Gtk::DrawingArea +{ +public: + ScreenArea(int _iWidth, int _iHeight, int _iScale = 1); + virtual ~ScreenArea(); + + void vSetSize(int _iWidth, int _iHeight); + void vSetScale(int _iScale); + void vSetFilter(EFilter _eFilter); + void vSetFilterIB(EFilterIB _eFilterIB); + void vSetEnableRender(bool _bEnable); + virtual void vDrawPixels(u8 * _puiData); + virtual void vDrawBlackScreen() = 0; + +protected: + virtual bool on_motion_notify_event(GdkEventMotion * _pstEvent); + virtual bool on_enter_notify_event(GdkEventCrossing * _pstEvent); + virtual bool on_leave_notify_event(GdkEventCrossing * _pstEvent); + virtual bool on_configure_event(GdkEventConfigure * event); + virtual bool bOnCursorTimeout(); + virtual void vOnSizeUpdated() {} + + int m_iWidth; + int m_iHeight; + int m_iScale; + int m_iFilterScale; + int m_iAreaWidth; + int m_iAreaHeight; + Filter m_vFilter2x; + FilterIB m_vFilterIB; + u32 * m_puiPixels; + u8 * m_puiDelta; + int m_iScaledWidth; + int m_iScaledHeight; + bool m_bEnableRender; + + bool m_bShowCursor; + Gdk::Cursor * m_poEmptyCursor; + sigc::connection m_oCursorSig; + + void vUpdateSize(); + virtual void vOnWidgetResize() = 0; + void vStartCursorTimeout(); + void vStopCursorTimeout(); + void vHideCursor(); + void vShowCursor(); +}; + +} // namespace VBA + + +#endif // __VBA_SCREENAREA_H__ diff --git a/src/gtk/soundconfig.cpp b/src/gtk/soundconfig.cpp new file mode 100644 index 0000000..36f48c0 --- /dev/null +++ b/src/gtk/soundconfig.cpp @@ -0,0 +1,134 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 2008 VBA-M development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "soundconfig.h" + +#include +#include +#include + +#include "intl.h" + +namespace VBA +{ + +SoundConfigDialog::SoundConfigDialog(GtkDialog* _pstDialog, const Glib::RefPtr& refBuilder) : + Gtk::Dialog(_pstDialog), + m_poConfig(0) +{ + refBuilder->get_widget("VolumeComboBox", m_poVolumeComboBox); + refBuilder->get_widget("RateComboBox", m_poRateComboBox); + + m_poVolumeComboBox->signal_changed().connect(sigc::mem_fun(*this, &SoundConfigDialog::vOnVolumeChanged)); + m_poRateComboBox->signal_changed().connect(sigc::mem_fun(*this, &SoundConfigDialog::vOnRateChanged)); +} + +void SoundConfigDialog::vSetConfig(Config::Section * _poConfig, VBA::Window * _poWindow) +{ + m_poConfig = _poConfig; + m_poWindow = _poWindow; + + bool bMute = m_poConfig->oGetKey("mute"); + float fSoundVolume = m_poConfig->oGetKey("volume"); + + if (bMute) + m_poVolumeComboBox->set_active(0); + else if (0.0f <= fSoundVolume && fSoundVolume <= 0.25f) + m_poVolumeComboBox->set_active(1); + else if (0.25f < fSoundVolume && fSoundVolume <= 0.50f) + m_poVolumeComboBox->set_active(2); + else if (1.0f < fSoundVolume && fSoundVolume <= 2.0f) + m_poVolumeComboBox->set_active(4); + else + m_poVolumeComboBox->set_active(3); + + long iSoundSampleRate = m_poConfig->oGetKey("sample_rate"); + switch (iSoundSampleRate) + { + case 11025: + m_poRateComboBox->set_active(0); + break; + case 22050: + m_poRateComboBox->set_active(1); + break; + default: + case 44100: + m_poRateComboBox->set_active(2); + break; + case 48000: + m_poRateComboBox->set_active(3); + break; + } +} + +void SoundConfigDialog::vOnVolumeChanged() +{ + int iVolume = m_poVolumeComboBox->get_active_row_number(); + switch (iVolume) + { + case 0: // Mute + m_poConfig->vSetKey("mute", true); + m_poConfig->vSetKey("volume", 1.0f); + break; + case 1: // 25 % + m_poConfig->vSetKey("mute", false); + m_poConfig->vSetKey("volume", 0.25f); + break; + case 2: // 50 % + m_poConfig->vSetKey("mute", false); + m_poConfig->vSetKey("volume", 0.50f); + break; + case 4: // 200 % + m_poConfig->vSetKey("mute", false); + m_poConfig->vSetKey("volume", 2.00f); + break; + case 3: // 100 % + default: + m_poConfig->vSetKey("mute", false); + m_poConfig->vSetKey("volume", 1.00f); + break; + } + + m_poWindow->vApplyConfigMute(); + m_poWindow->vApplyConfigVolume(); +} + +void SoundConfigDialog::vOnRateChanged() +{ + int iRate = m_poRateComboBox->get_active_row_number(); + switch (iRate) + { + case 0: // 11 KHz + m_poConfig->vSetKey("sample_rate", 11025); + break; + case 1: // 22 KHz + m_poConfig->vSetKey("sample_rate", 22050); + break; + case 2: // 44 KHz + default: + m_poConfig->vSetKey("sample_rate", 44100); + break; + case 3: // 48 KHz + m_poConfig->vSetKey("sample_rate", 48000); + break; + } + + m_poWindow->vApplyConfigSoundSampleRate(); +} + +} // namespace VBA diff --git a/src/gtk/soundconfig.h b/src/gtk/soundconfig.h new file mode 100644 index 0000000..e4445a9 --- /dev/null +++ b/src/gtk/soundconfig.h @@ -0,0 +1,53 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 2008 VBA-M development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef __VBA_SOUNDCONFIG_H__ +#define __VBA_SOUNDCONFIG_H__ + +#include +#include +#include + +#include "configfile.h" +#include "window.h" + +namespace VBA +{ + +class SoundConfigDialog : public Gtk::Dialog +{ +public: + SoundConfigDialog(GtkDialog* _pstDialog, const Glib::RefPtr& refBuilder); + + void vSetConfig(Config::Section * _poConfig, VBA::Window * _poWindow); + +private: + void vOnVolumeChanged(); + void vOnRateChanged(); + + VBA::Window * m_poWindow; + + Config::Section * m_poConfig; + Gtk::ComboBox * m_poVolumeComboBox; + Gtk::ComboBox * m_poRateComboBox; +}; + +} // namespace VBA + + +#endif // __VBA_SOUNDCONFIG_H__ diff --git a/src/gtk/system.cpp b/src/gtk/system.cpp new file mode 100644 index 0000000..9f37ced --- /dev/null +++ b/src/gtk/system.cpp @@ -0,0 +1,192 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "../sdl/inputSDL.h" +#include "../gba/Sound.h" +#include "../common/SoundSDL.h" + +#include "window.h" +#include "intl.h" + +// Required vars, used by the emulator core +// +int systemRedShift; +int systemGreenShift; +int systemBlueShift; +int systemColorDepth; +int systemVerbose; +int systemSaveUpdateCounter; +int systemFrameSkip; +u32 systemColorMap32[0x10000]; +u16 systemColorMap16[0x10000]; +u16 systemGbPalette[24]; + +int emulating; +int RGB_LOW_BITS_MASK; + +inline VBA::Window * GUI() +{ + return VBA::Window::poGetInstance(); +} + +void systemMessage(int _iId, const char * _csFormat, ...) +{ + va_list args; + va_start(args, _csFormat); + + GUI()->vPopupErrorV(_(_csFormat), args); + + va_end(args); +} + +void systemDrawScreen() +{ + GUI()->vDrawScreen(); +} + +bool systemReadJoypads() +{ + return true; +} + +u32 systemReadJoypad(int joy) +{ + return inputReadJoypad(joy); +} + +void systemShowSpeed(int _iSpeed) +{ + GUI()->vShowSpeed(_iSpeed); +} + +void system10Frames(int _iRate) +{ + GUI()->vComputeFrameskip(_iRate); +} + +void systemFrame() +{ +} + +void systemSetTitle(const char * _csTitle) +{ + GUI()->set_title(_csTitle); +} + +void systemScreenCapture(int _iNum) +{ + GUI()->vCaptureScreen(_iNum); +} + +u32 systemGetClock() +{ + Glib::TimeVal time; + time.assign_current_time(); + return time.as_double() * 1000; +} + +void systemUpdateMotionSensor() +{ +} + +int systemGetSensorX() +{ + return 0; +} + +int systemGetSensorY() +{ + return 0; +} + +void systemGbPrint(u8 * _puiData, + int _iLen, + int _iPages, + int _iFeed, + int _iPalette, + int _iContrast) +{ +} + +void systemScreenMessage(const char * _csMsg) +{ +} + +bool systemCanChangeSoundQuality() +{ + return true; +} + +bool systemPauseOnFrame() +{ + return false; +} + +void systemGbBorderOn() +{ +} + +SoundDriver * systemSoundInit() +{ + soundShutdown(); + + return new SoundSDL(); +} + +void systemOnSoundShutdown() +{ +} + +void systemOnWriteDataToSoundBuffer(const u16 * finalWave, int length) +{ +} + +void debuggerMain() +{ +} + +void debuggerSignal(int, int) +{ +} + +void debuggerOutput(const char *, u32) +{ +} + +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); +} diff --git a/src/gtk/tools.cpp b/src/gtk/tools.cpp new file mode 100644 index 0000000..8e1ced8 --- /dev/null +++ b/src/gtk/tools.cpp @@ -0,0 +1,85 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "tools.h" + +namespace VBA +{ + +std::string sCutSuffix(const std::string & _rsString, + const std::string & _rsSep) +{ + return _rsString.substr(0, _rsString.find_last_of(_rsSep)); +} + +Glib::ustring sCutSuffix(const Glib::ustring & _rsString, + const Glib::ustring & _rsSep) +{ + return _rsString.substr(0, _rsString.find_last_of(_rsSep)); +} + +bool bHasSuffix(const Glib::ustring & _rsString, + const Glib::ustring & _rsSuffix, + bool _bCaseSensitive) +{ + if (_rsSuffix.size() > _rsString.size()) + { + return false; + } + + Glib::ustring sEnd = _rsString.substr(_rsString.size() - _rsSuffix.size()); + + if (_bCaseSensitive) + { + if (_rsSuffix == sEnd) + { + return true; + } + } + else + { + if (_rsSuffix.lowercase() == sEnd.lowercase()) + { + return true; + } + } + + return false; +} + +void vTokenize(Glib::ustring source, std::vector& tokens) +{ + Glib::ustring delimiters = " \t\n\r"; + + // Skip delimiters at beginning. + Glib::ustring::size_type lastPos = source.find_first_not_of(delimiters, 0); + // Find first "non-delimiter". + Glib::ustring::size_type pos = source.find_first_of(delimiters, lastPos); + + while (Glib::ustring::npos != pos || std:: string::npos != lastPos) + { + // Found a token, add it to the vector. + tokens.push_back(source.substr(lastPos, pos - lastPos)); + // Skip delimiters. Note the "not_of" + lastPos = source.find_first_not_of(delimiters, pos); + // Find next "non-delimiter" + pos = source.find_first_of(delimiters, lastPos); + } +} + +} // namespace VBA diff --git a/src/gtk/tools.h b/src/gtk/tools.h new file mode 100644 index 0000000..ab29269 --- /dev/null +++ b/src/gtk/tools.h @@ -0,0 +1,45 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef __VBA_TOOLS_H__ +#define __VBA_TOOLS_H__ + +#include +#include +#include + +namespace VBA +{ + +std::string sCutSuffix(const std::string & _rsString, + const std::string & _rsSep = "."); + +Glib::ustring sCutSuffix(const Glib::ustring & _rsString, + const Glib::ustring & _rsSep = "."); + +bool bHasSuffix(const Glib::ustring & _rsString, + const Glib::ustring & _rsSuffix, + bool _bCaseSensitive = true); + +void vTokenize(Glib::ustring source, std::vector& tokens); + +} + + +#endif // __VBA_TOOLS_H__ diff --git a/src/gtk/ui/cheatedit.ui b/src/gtk/ui/cheatedit.ui new file mode 100644 index 0000000..64bfdc5 --- /dev/null +++ b/src/gtk/ui/cheatedit.ui @@ -0,0 +1,179 @@ + + + + + + + + + + + + 5 + Edit cheat + center-on-parent + dialog + + + True + + + True + + + True + 0 + none + + + True + 12 + + + True + True + 31 + ◠+ + + + + + + True + <b>Description</b> + True + + + + + False + 0 + + + + + True + 0 + none + + + True + 12 + + + True + model1 + + + + 0 + + + + + + + + + True + <b>Type</b> + True + + + + + False + 1 + + + + + True + 0 + none + + + True + 12 + + + True + True + never + automatic + + + True + True + + + + + + + + + True + <b>Codes</b> + True + + + + + end + 2 + + + + + 1 + + + + + True + end + + + gtk-apply + True + True + True + True + + + False + False + 0 + + + + + gtk-close + True + True + True + True + + + False + False + 1 + + + + + False + end + 0 + + + + + + CheatApplyButton + CheatCancelButton + + + diff --git a/src/gtk/ui/cheatlist.ui b/src/gtk/ui/cheatlist.ui new file mode 100644 index 0000000..59df081 --- /dev/null +++ b/src/gtk/ui/cheatlist.ui @@ -0,0 +1,180 @@ + + + + + + 320 + 260 + 5 + Cheat list + center-on-parent + dialog + + + True + + + True + + + True + Open cheat list + gtk-open + True + document-open + + + False + True + + + + + True + Save cheat list + gtk-save + True + document-save + + + False + True + + + + + True + + + False + True + + + + + True + Add new cheat + gtk-add + True + list-add + + + False + True + + + + + True + Delete selected cheat + gtk-delete + True + list-remove + + + False + True + + + + + True + Delete all cheats + True + gtk-clear + + + False + True + + + + + True + + + False + True + + + + + True + Toggle all Cheats + gtk-markall + True + edit-select-all + + + False + True + + + + + False + 0 + + + + + True + True + + + True + True + never + automatic + + + True + True + + + + + end + 0 + + + + + 1 + + + + + True + end + + + + + + gtk-close + True + True + True + True + + + False + False + 1 + + + + + False + end + 2 + + + + + + button1 + + + diff --git a/src/gtk/ui/display.ui b/src/gtk/ui/display.ui new file mode 100644 index 0000000..54d73bb --- /dev/null +++ b/src/gtk/ui/display.ui @@ -0,0 +1,278 @@ + + + + 600 + 100 + 10 + 100 + 10 + 100 + + + + + + + + + + + + + + + + + + 1x + + + 2x + + + 3x + + + 4x + + + 5x + + + 6x + + + + + 5 + GTK_WIN_POS_CENTER_ON_PARENT + GDK_WINDOW_TYPE_HINT_DIALOG + False + + + True + 2 + + + True + 0 + GTK_SHADOW_NONE + + + True + 12 + + + True + + + True + True + OpenGL + True + True + + + + + True + True + Cairo + True + True + OutputOpenGL + + + 1 + + + + + + + + + True + <b>Output module</b> + True + + + + + False + 1 + + + + + True + 0 + GTK_SHADOW_NONE + + + True + 12 + + + True + + + True + 1 + Default scale : + GTK_JUSTIFY_RIGHT + + + False + + + + + True + ScalingListStore + + + + 0 + + + + + 1 + + + + + + + + + True + <b>Zoom</b> + True + + + + + False + 2 + + + + + True + 0 + GTK_SHADOW_NONE + + + True + 12 + + + True + 2 + 2 + + + True + 1 + Interframe blending : + + + 1 + 2 + GTK_FILL + + + + + True + 1 + Fullscreen filter : + + + GTK_FILL + + + + + True + IBFiltersListStore + + + + 0 + + + + + 1 + 2 + 1 + 2 + + + + + True + FiltersListStore + + + + 0 + + + + + 1 + 2 + + + + + + + + + True + <b>Filters</b> + True + + + + + 3 + + + + + True + GTK_BUTTONBOX_END + + + + + + True + True + True + gtk-close + True + + + 1 + + + + + False + GTK_PACK_END + + + + + + button1 + + + diff --git a/src/gtk/ui/gameboy.ui b/src/gtk/ui/gameboy.ui new file mode 100644 index 0000000..743f11c --- /dev/null +++ b/src/gtk/ui/gameboy.ui @@ -0,0 +1,206 @@ + + + + + + + + + + Automatic + + + Game Boy Advance + + + Game Boy Color + + + Super Game Boy + + + Super Game Boy 2 + + + Game Boy + + + + + 5 + GameBoy settings + GTK_WIN_POS_CENTER_ON_PARENT + GDK_WINDOW_TYPE_HINT_DIALOG + False + + + True + + + True + 0 + GTK_SHADOW_NONE + + + True + 12 + + + True + + + True + + + True + Emulated system : + + + + + True + model1 + + + + 0 + + + + + 1 + + + + + + + True + True + Display Super Game Boy borders + True + + + 1 + + + + + True + True + Emulate a Game Boy Printer + True + + + 2 + + + + + + + + + True + <b>System and peripherals</b> + True + + + + + 1 + + + + + True + 0 + GTK_SHADOW_NONE + + + True + 12 + + + True + + + True + True + Use a Game Boy boot ROM file + True + + + + + True + + + True + Boot ROM file : + + + False + + + + + True + + + 1 + + + + + 1 + + + + + + + + + True + <b>Boot ROM</b> + True + + + + + 2 + + + + + True + GTK_BUTTONBOX_END + + + + + + True + True + True + gtk-close + True + + + 1 + + + + + False + GTK_PACK_END + + + + + + button1 + + + diff --git a/src/gtk/ui/gameboyadvance.ui b/src/gtk/ui/gameboyadvance.ui new file mode 100644 index 0000000..47161e7 --- /dev/null +++ b/src/gtk/ui/gameboyadvance.ui @@ -0,0 +1,265 @@ + + + + + + + + + + 64K + + + 128K + + + + + + + + + + Automatic + + + EEPROM + + + SRAM + + + Flash + + + EEPROM + Sensor + + + None + + + + + 5 + Game Boy Advance settings + GTK_WIN_POS_CENTER_ON_PARENT + GDK_WINDOW_TYPE_HINT_DIALOG + False + + + True + + + True + 0 + GTK_SHADOW_NONE + + + True + 12 + + + True + 2 + 2 + + + True + model1 + + + + 0 + + + + + 1 + 2 + 1 + 2 + + + + + True + model2 + + + + 0 + + + + + 1 + 2 + + + + + True + Flash size : + + + 1 + 2 + GTK_FILL + GTK_FILL + + + + + True + Save type : + + + GTK_FILL + GTK_FILL + + + + + + + + + True + <b>Cartridge</b> + True + + + + + 1 + + + + + True + 0 + GTK_SHADOW_NONE + + + True + 12 + + + True + + + True + True + Use a bios file + True + + + + + True + + + True + Bios file : + + + False + + + + + True + + + 1 + + + + + 1 + + + + + + + + + True + <b>Bios</b> + True + + + + + 2 + + + + + True + 0 + GTK_SHADOW_NONE + + + True + 12 + + + True + + + True + True + Enable real-time clock + True + + + + + + + + + True + <b>Real-Time Clock</b> + True + + + + + 3 + + + + + True + GTK_BUTTONBOX_END + + + + + + True + True + True + gtk-close + True + + + 1 + + + + + False + GTK_PACK_END + + + + + + button1 + + + diff --git a/src/gtk/ui/preferences.ui b/src/gtk/ui/preferences.ui new file mode 100644 index 0000000..74b20b0 --- /dev/null +++ b/src/gtk/ui/preferences.ui @@ -0,0 +1,226 @@ + + + + + + 5 + Preferences + normal + False + + + True + 2 + + + True + 0 + none + + + True + 12 + + + Pause when inactive + True + True + False + True + + + + + + + True + <b>General</b> + True + + + + + False + 1 + + + + + True + 0 + none + + + True + 12 + + + True + 2 + + + Enable automatic frame skipping + True + True + False + True + + + 0 + + + + + True + + + True + 1 + Frameskip level : + + + False + 0 + + + + + True + False + FrameSkipAdjustment + True + + + 1 + + + + + 1 + + + + + + + + + True + <b>Frameskip</b> + True + + + + + False + 2 + + + + + True + 0 + none + + + True + 12 + + + True + + + True + Speed indicator : + + + False + 0 + + + + + True + SpeedIndicatorListStore + + + + 0 + + + + + 1 + + + + + + + + + True + <b>Appearance</b> + True + + + + + False + 3 + + + + + True + end + + + gtk-close + True + True + True + True + + + False + False + 0 + + + + + False + end + 0 + + + + + + button2 + + + + + + + + + + None + + + Percentage + + + Detailed + + + + + 9 + 1 + 1 + + diff --git a/src/gtk/ui/sound.ui b/src/gtk/ui/sound.ui new file mode 100644 index 0000000..9c693ec --- /dev/null +++ b/src/gtk/ui/sound.ui @@ -0,0 +1,154 @@ + + + + + + + + + + Mute + + + 25 % + + + 50 % + + + 100 % + + + 200 % + + + + + + + + + + 11 KHz + + + 22 KHz + + + 44.1 KHz + + + 48 KHz + + + + + 5 + GTK_WIN_POS_CENTER_ON_PARENT + GDK_WINDOW_TYPE_HINT_DIALOG + False + + + True + 2 + + + True + 10 + 2 + 2 + 10 + 10 + + + True + model1 + + + + 0 + + + + + 1 + 2 + + + + + True + 1 + Volume : + + + GTK_FILL + + + + + True + 1 + Sample rate : + + + 1 + 2 + GTK_FILL + + + + + True + model2 + + + + 0 + + + + + 1 + 2 + 1 + 2 + + + + + 1 + + + + + True + GTK_BUTTONBOX_END + + + + + + True + True + True + gtk-close + True + + + 1 + + + + + False + GTK_PACK_END + + + + + + button1 + + + diff --git a/src/gtk/ui/vbam.ui b/src/gtk/ui/vbam.ui new file mode 100644 index 0000000..849bf54 --- /dev/null +++ b/src/gtk/ui/vbam.ui @@ -0,0 +1,596 @@ + + + + + + False + VBA + + + + + + True + False + + + True + False + + + True + False + False + _File + True + + + False + + + gtk-open + True + False + False + True + True + accelgroup1 + + + + + True + False + False + Open rece_nt + True + + + + + True + False + + + + + True + False + False + Screen capt_ure... + True + + + + + True + False + + + + + gtk-close + True + False + False + True + True + accelgroup1 + + + + + gtk-quit + True + False + False + True + True + accelgroup1 + + + + + + + + + + True + False + False + _Emulation + True + + + False + + + True + False + False + Pause + True + + + + + + True + False + False + _Reset + True + + + + + + gtk-fullscreen + True + False + False + True + True + accelgroup1 + + + + + + True + False + + + + + True + False + False + Loa_d state + True + + + False + + + True + False + False + Most recent + True + + + + + True + False + False + Auto load most recent + True + + + + + True + False + + + + + True + False + False + Slot1 + True + + + + + + True + False + False + Slot2 + True + + + + + + True + False + False + Slot3 + True + + + + + + True + False + False + Slot4 + True + + + + + + True + False + False + Slot5 + True + + + + + + True + False + False + Slot6 + True + + + + + + True + False + False + Slot7 + True + + + + + + True + False + False + Slot8 + True + + + + + + True + False + False + Slot9 + True + + + + + + True + False + False + Slot10 + True + + + + + + True + False + False + From _File ... + True + + + + + + + + + + True + False + False + S_ave state + True + + + False + + + True + False + False + Oldest slot + True + + + + + True + False + + + + + True + False + False + Slot1 + True + + + + + + True + False + False + Slot2 + True + + + + + + True + False + False + Slot3 + True + + + + + + True + False + False + Slot4 + True + + + + + + True + False + False + Slot5 + True + + + + + + True + False + False + Slot6 + True + + + + + + True + False + False + Slot7 + True + + + + + + True + False + False + Slot8 + True + + + + + + True + False + False + Slot9 + True + + + + + + True + False + False + Slot10 + True + + + + + + True + False + + + + + True + False + False + To _File ... + True + + + + + + + + + + True + False + + + + + True + False + False + _List cheats ... + True + + + + + + True + False + False + _Disable cheats + True + + + + + + + + + True + False + False + _Options + True + + + False + + + True + False + False + _Preferences ... + True + + + + + True + False + False + _Game Boy ... + True + + + + + True + False + False + Game Boy _Advance ... + True + + + + + True + False + False + _Display ... + True + + + + + True + False + False + _Sound ... + True + + + + + True + False + False + D_irectories ... + True + + + + + True + False + False + _Joypads ... + True + + + + + + + + + True + False + False + _Help + True + + + False + + + gtk-about + True + False + False + True + True + accelgroup1 + + + + + + + + + False + True + 0 + + + + + True + False + 0 + 0 + + + + + + True + True + 1 + + + + + + + diff --git a/src/gtk/window.cpp b/src/gtk/window.cpp new file mode 100644 index 0000000..b76baf6 --- /dev/null +++ b/src/gtk/window.cpp @@ -0,0 +1,1551 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "window.h" + +#include +#include +#include +#include + +#include + +#include + +#include "../gba/GBA.h" +#include "../gba/RTC.h" +#include "../gba/Sound.h" +#include "../gb/gb.h" +#include "../gb/gbGlobals.h" +#include "../gb/gbCheats.h" +#include "../gb/gbSound.h" +#include "../gb/gbPrinter.h" +#include "../Util.h" + +#include "tools.h" +#include "intl.h" +#include "screenarea-cairo.h" + +#ifdef USE_OPENGL +#include "screenarea-opengl.h" +#endif // USE_OPENGL + +extern int RGB_LOW_BITS_MASK; + + +namespace VBA +{ + +Window * Window::m_poInstance = NULL; + +const Window::SJoypadKey Window::m_astJoypad[] = +{ + { "left", KEY_LEFT }, + { "right", KEY_RIGHT }, + { "up", KEY_UP }, + { "down", KEY_DOWN }, + { "A", KEY_BUTTON_A }, + { "B", KEY_BUTTON_B }, + { "select", KEY_BUTTON_SELECT }, + { "start", KEY_BUTTON_START }, + { "L", KEY_BUTTON_L }, + { "R", KEY_BUTTON_R }, + { "speed", KEY_BUTTON_SPEED }, + { "capture", KEY_BUTTON_CAPTURE }, + { "speed", KEY_BUTTON_SPEED }, + { "capture", KEY_BUTTON_CAPTURE }, + { "autoA", KEY_BUTTON_AUTO_A }, + { "autoB", KEY_BUTTON_AUTO_B } +}; + +Window::Window(GtkWindow * _pstWindow, const Glib::RefPtr & _poXml) : + Gtk::Window (_pstWindow), + m_iGBScreenWidth (160), + m_iGBScreenHeight (144), + m_iSGBScreenWidth (256), + m_iSGBScreenHeight(224), + m_iGBAScreenWidth (240), + m_iGBAScreenHeight(160), + m_iFrameskipMin (0), + m_iFrameskipMax (9), + m_iScaleMin (1), + m_iScaleMax (6), + m_iShowSpeedMin (ShowNone), + m_iShowSpeedMax (ShowDetailed), + m_iSaveTypeMin (SaveAuto), + m_iSaveTypeMax (SaveNone), + m_iSoundSampleRateMin(11025), + m_iSoundSampleRateMax(48000), + m_fSoundVolumeMin (0.50f), + m_fSoundVolumeMax (2.00f), + m_iEmulatorTypeMin(EmulatorAuto), + m_iEmulatorTypeMax(EmulatorSGB2), + m_iFilter2xMin (FirstFilter), + m_iFilter2xMax (LastFilter), + m_iFilterIBMin (FirstFilterIB), + m_iFilterIBMax (LastFilterIB), + m_iJoypadMin (PAD_1), + m_iJoypadMax (PAD_4), + m_iVideoOutputMin (OutputCairo), + m_iVideoOutputMax (OutputOpenGL), + m_bFullscreen (false) +{ + m_poXml = _poXml; + m_poFileOpenDialog = NULL; + m_iScreenWidth = m_iGBAScreenWidth; + m_iScreenHeight = m_iGBAScreenHeight; + m_eCartridge = CartridgeNone; + + vInitSDL(); + vInitSystem(); + + vSetDefaultTitle(); + + // Get config + // + m_sUserDataDir = Glib::get_user_config_dir() + "/gvbam"; + m_sConfigFile = m_sUserDataDir + "/config"; + + vInitConfig(); + + if (! Glib::file_test(m_sUserDataDir, Glib::FILE_TEST_EXISTS)) + { + mkdir(m_sUserDataDir.c_str(), 0777); + } + if (Glib::file_test(m_sConfigFile, Glib::FILE_TEST_EXISTS)) + { + vLoadConfig(m_sConfigFile); + vCheckConfig(); + } + else + { + vSaveConfig(m_sConfigFile); + } + + vCreateFileOpenDialog(); + vApplyConfigJoypads(); + vApplyConfigScreenArea(); + vApplyConfigFilter(); + vApplyConfigFilterIB(); + vApplyConfigMute(); + vApplyConfigVolume(); + vApplyConfigGBSystem(); + vApplyConfigGBBorder(); + vApplyConfigGBPrinter(); + vApplyConfigGBASaveType(); + vApplyConfigGBAFlashSize(); + vApplyConfigGBARTC(); + vApplyConfigFrameskip(); + vApplyConfigShowSpeed(); + + Gtk::MenuItem * poMI; + Gtk::CheckMenuItem * poCMI; + + // Menu bar + _poXml->get_widget("MenuBar", m_poMenuBar); + m_poMenuBar->signal_deactivate().connect(sigc::mem_fun(*this, &Window::vOnMenuExit)); + + _poXml->get_widget("FileMenu", poMI); + poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnMenuEnter)); + _poXml->get_widget("EmulationMenu", poMI); + poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnMenuEnter)); + _poXml->get_widget("OptionsMenu", poMI); + poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnMenuEnter)); + _poXml->get_widget("HelpMenu", poMI); + poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnMenuEnter)); + + // File menu + // + _poXml->get_widget("FileOpen", poMI); + poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnFileOpen)); + + _poXml->get_widget("FileLoad", poMI); + poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnFileLoad)); + m_listSensitiveWhenPlaying.push_back(poMI); + + _poXml->get_widget("FileSave", poMI); + poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnFileSave)); + m_listSensitiveWhenPlaying.push_back(poMI); + + for (int i = 0; i < 10; i++) + { + char csName[20]; + snprintf(csName, 20, "LoadGameSlot%d", i + 1); + _poXml->get_widget(csName, m_apoLoadGameItem[i]); + snprintf(csName, 20, "SaveGameSlot%d", i + 1); + _poXml->get_widget(csName, m_apoSaveGameItem[i]); + + m_apoLoadGameItem[i]->signal_activate().connect(sigc::bind( + sigc::mem_fun(*this, &Window::vOnLoadGame), + i + 1)); + m_apoSaveGameItem[i]->signal_activate().connect(sigc::bind( + sigc::mem_fun(*this, &Window::vOnSaveGame), + i + 1)); + } + vUpdateGameSlots(); + + _poXml->get_widget("LoadGameMostRecent", poMI); + poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnLoadGameMostRecent)); + m_listSensitiveWhenPlaying.push_back(poMI); + + _poXml->get_widget("LoadGameAuto", poCMI); + poCMI->set_active(m_poCoreConfig->oGetKey("load_game_auto")); + vOnLoadGameAutoToggled(poCMI); + poCMI->signal_toggled().connect(sigc::bind( + sigc::mem_fun(*this, &Window::vOnLoadGameAutoToggled), + poCMI)); + + _poXml->get_widget("SaveGameOldest", poMI); + poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnSaveGameOldest)); + m_listSensitiveWhenPlaying.push_back(poMI); + + _poXml->get_widget("FilePause", m_poFilePauseItem); + m_poFilePauseItem->set_active(false); + vOnFilePauseToggled(m_poFilePauseItem); + m_poFilePauseItem->signal_toggled().connect(sigc::bind( + sigc::mem_fun(*this, &Window::vOnFilePauseToggled), + m_poFilePauseItem)); + m_listSensitiveWhenPlaying.push_back(m_poFilePauseItem); + + _poXml->get_widget("FileReset", poMI); + poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnFileReset)); + m_listSensitiveWhenPlaying.push_back(poMI); + + _poXml->get_widget("FileScreenCapture", poMI); + poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnFileScreenCapture)); + m_listSensitiveWhenPlaying.push_back(poMI); + + _poXml->get_widget("FileClose", poMI); + poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnFileClose)); + m_listSensitiveWhenPlaying.push_back(poMI); + + _poXml->get_widget("FileExit", poMI); + poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnFileExit)); + + // Recent menu + // + m_poRecentManager = Gtk::RecentManager::get_default(); + + Gtk::RecentFilter oRecentFilter; + oRecentFilter.add_application( Glib::get_application_name() ); + + m_poRecentChooserMenu = Gtk::manage( new Gtk::RecentChooserMenu(m_poRecentManager) ); + m_poRecentChooserMenu->set_show_numbers(); + m_poRecentChooserMenu->set_show_tips(); + m_poRecentChooserMenu->set_local_only(); + m_poRecentChooserMenu->add_filter(oRecentFilter); + m_poRecentChooserMenu->signal_item_activated().connect( + sigc::mem_fun(*this, &Window::vOnRecentFile)); + + + _poXml->get_widget("RecentMenu", m_poRecentMenu); + m_poRecentMenu->set_submenu(static_cast(*m_poRecentChooserMenu)); + + // Emulator menu + // + _poXml->get_widget("DirectoriesConfigure", poMI); + poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnDirectories)); + + // Preferences + _poXml->get_widget("GeneralConfigure", poMI); + poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnGeneralConfigure)); + + // Game Boy menu + _poXml->get_widget("GameBoyConfigure", poMI); + poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnGameBoyConfigure)); + + // Game Boy Advance menu + _poXml->get_widget("GameBoyAdvanceConfigure", poMI); + poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnGameBoyAdvanceConfigure)); + + // Cheat list menu + _poXml->get_widget("CheatList", poMI); + poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnCheatList)); + m_listSensitiveWhenPlaying.push_back(poMI); + + // Cheat disable item + _poXml->get_widget("CheatDisable", poCMI); + poCMI->set_active(!cheatsEnabled); + poCMI->signal_toggled().connect(sigc::bind( + sigc::mem_fun(*this, &Window::vOnCheatDisableToggled), + poCMI)); + m_listSensitiveWhenPlaying.push_back(poCMI); + + // Display menu + _poXml->get_widget("DisplayConfigure", poMI); + poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnDisplayConfigure)); + + // Sound menu + _poXml->get_widget("SoundConfigure", poMI); + poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnSoundConfigure)); + + // Joypad menu + // + _poXml->get_widget("JoypadConfigure", poMI); + poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnJoypadConfigure)); + + EPad eDefaultJoypad = (EPad)m_poInputConfig->oGetKey("active_joypad"); + inputSetDefaultJoypad(eDefaultJoypad); + + // Fullscreen menu + // + _poXml->get_widget("VideoFullscreen", poMI); + poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnVideoFullscreen)); + + // Help menu + // + _poXml->get_widget("HelpAbout", poMI); + poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnHelpAbout)); + + // Init widgets sensitivity + for (std::list::iterator it = m_listSensitiveWhenPlaying.begin(); + it != m_listSensitiveWhenPlaying.end(); + it++) + { + (*it)->set_sensitive(false); + } + + if (m_poInstance == NULL) + { + m_poInstance = this; + } + else + { + abort(); + } +} + +Window::~Window() +{ + vOnFileClose(); + vUnInitSystem(); + vSaveJoypadsToConfig(); + vSaveConfig(m_sConfigFile); + + if (m_poFileOpenDialog != NULL) + { + delete m_poFileOpenDialog; + } + + m_poInstance = NULL; +} + +void Window::vInitColors(EColorFormat _eColorFormat) +{ + switch (_eColorFormat) + { + case ColorFormatBGR: +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + systemRedShift = 3; + systemGreenShift = 11; + systemBlueShift = 19; + RGB_LOW_BITS_MASK = 0x00010101; +#else + systemRedShift = 27; + systemGreenShift = 19; + systemBlueShift = 11; + RGB_LOW_BITS_MASK = 0x01010100; +#endif + break; + default: +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + systemRedShift = 19; + systemGreenShift = 11; + systemBlueShift = 3; + RGB_LOW_BITS_MASK = 0x00010101; +#else + systemRedShift = 11; + systemGreenShift = 19; + systemBlueShift = 27; + RGB_LOW_BITS_MASK = 0x01010100; +#endif + break; + } + + for (int i = 0; i < 0x10000; i++) + { + systemColorMap32[i] = (((i & 0x1f) << systemRedShift) + | (((i & 0x3e0) >> 5) << systemGreenShift) + | (((i & 0x7c00) >> 10) << systemBlueShift)); + } + + for (int i = 0; i < 24; ) + { + systemGbPalette[i++] = (0x1f) | (0x1f << 5) | (0x1f << 10); + systemGbPalette[i++] = (0x15) | (0x15 << 5) | (0x15 << 10); + systemGbPalette[i++] = (0x0c) | (0x0c << 5) | (0x0c << 10); + systemGbPalette[i++] = 0; + } + + Init_2xSaI(32); +} + +void Window::vApplyConfigScreenArea() +{ + EVideoOutput eVideoOutput = (EVideoOutput)m_poDisplayConfig->oGetKey("output"); + + Gtk::Alignment * poC; + + m_poXml->get_widget("ScreenContainer", poC); + poC->remove(); + poC->set(Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER, 1.0, 1.0); + + try + { + switch (eVideoOutput) + { +#ifdef USE_OPENGL + case OutputOpenGL: + vInitColors(ColorFormatBGR); + m_poScreenArea = Gtk::manage(new ScreenAreaGl(m_iScreenWidth, m_iScreenHeight)); + break; +#endif // USE_OPENGL + case OutputCairo: + default: + vInitColors(ColorFormatRGB); + m_poScreenArea = Gtk::manage(new ScreenAreaCairo(m_iScreenWidth, m_iScreenHeight)); + break; + } + } + catch (std::exception e) + { + fprintf(stderr, _("Unable to initialize output, falling back to Cairo\n")); + m_poScreenArea = Gtk::manage(new ScreenAreaCairo(m_iScreenWidth, m_iScreenHeight)); + } + + poC->add(*m_poScreenArea); + vDrawDefaultScreen(); + m_poScreenArea->show(); +} + +void Window::vInitSystem() +{ + systemColorDepth = 32; + systemVerbose = 0; + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + systemFrameSkip = 2; + + emulating = 0; + + gbFrameSkip = 0; + + m_iFrameCount = 0; + + soundInit(); +} + +void Window::vUnInitSystem() +{ + soundShutdown(); +} + +void Window::vInitSDL() +{ + static bool bDone = false; + + if (bDone) + return; + + int iFlags = (SDL_INIT_EVERYTHING | SDL_INIT_NOPARACHUTE); + + if (SDL_Init(iFlags) < 0) + { + fprintf(stderr, _("Failed to init SDL: %s"), SDL_GetError()); + abort(); + } + + inputSetKeymap(PAD_DEFAULT, KEY_LEFT, GDK_Left); + inputSetKeymap(PAD_DEFAULT, KEY_RIGHT, GDK_Right); + inputSetKeymap(PAD_DEFAULT, KEY_UP, GDK_Up); + inputSetKeymap(PAD_DEFAULT, KEY_DOWN, GDK_Down); + inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_A, GDK_z); + inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_B, GDK_x); + inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_START, GDK_Return); + inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_SELECT, GDK_BackSpace); + inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_L, GDK_a); + inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_R, GDK_s); + inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_SPEED, GDK_space); + inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_CAPTURE, GDK_F12); + inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_AUTO_A, GDK_q); + inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_AUTO_B, GDK_w); + + // TODO : remove + int sdlNumDevices = SDL_NumJoysticks(); + for (int i = 0; i < sdlNumDevices; i++) + SDL_JoystickOpen(i); + + inputInitJoysticks(); + + bDone = true; +} + +void Window::vInitConfig() +{ + m_oConfig.vClear(); + + // Directories section + // + m_poDirConfig = m_oConfig.poAddSection("Directories"); + m_poDirConfig->vSetKey("gb_roms", Glib::get_home_dir()); + m_poDirConfig->vSetKey("gba_roms", Glib::get_home_dir()); + m_poDirConfig->vSetKey("batteries", m_sUserDataDir); + m_poDirConfig->vSetKey("cheats", m_sUserDataDir); + m_poDirConfig->vSetKey("saves", m_sUserDataDir); + m_poDirConfig->vSetKey("captures", m_sUserDataDir); + + // Core section + // + m_poCoreConfig = m_oConfig.poAddSection("Core"); + m_poCoreConfig->vSetKey("load_game_auto", false ); + m_poCoreConfig->vSetKey("frameskip", "auto" ); + m_poCoreConfig->vSetKey("use_bios_file", false ); + m_poCoreConfig->vSetKey("bios_file", "" ); + m_poCoreConfig->vSetKey("enable_rtc", false ); + m_poCoreConfig->vSetKey("save_type", SaveAuto ); + m_poCoreConfig->vSetKey("flash_size", 64 ); + m_poCoreConfig->vSetKey("gb_border", false ); + m_poCoreConfig->vSetKey("gb_printer", false ); + m_poCoreConfig->vSetKey("gb_use_bios_file", false ); + m_poCoreConfig->vSetKey("gb_bios_file", "" ); + m_poCoreConfig->vSetKey("emulator_type", EmulatorAuto ); + m_poCoreConfig->vSetKey("pause_when_inactive", true ); + m_poCoreConfig->vSetKey("show_speed", ShowPercentage ); + + // Display section + // + m_poDisplayConfig = m_oConfig.poAddSection("Display"); + m_poDisplayConfig->vSetKey("scale", 1 ); + m_poDisplayConfig->vSetKey("filter2x", FilterNone ); + m_poDisplayConfig->vSetKey("filterIB", FilterIBNone ); +#ifdef USE_OPENGL + m_poDisplayConfig->vSetKey("output", OutputOpenGL ); +#else + m_poDisplayConfig->vSetKey("output", OutputCairo ); +#endif // USE_OPENGL + + + // Sound section + // + m_poSoundConfig = m_oConfig.poAddSection("Sound"); + m_poSoundConfig->vSetKey("mute", false ); + m_poSoundConfig->vSetKey("sample_rate", 44100 ); + m_poSoundConfig->vSetKey("volume", 1.00f ); + + // Input section + // + m_poInputConfig = m_oConfig.poAddSection("Input"); + m_poInputConfig->vSetKey("active_joypad", m_iJoypadMin ); + for (int i = m_iJoypadMin; i <= m_iJoypadMax; i++) + { + char csPrefix[20]; + snprintf(csPrefix, sizeof(csPrefix), "joypadSDL%d_", i); + std::string sPrefix(csPrefix); + + for (guint j = 0; j < G_N_ELEMENTS(m_astJoypad); j++) + { + m_poInputConfig->vSetKey(sPrefix + m_astJoypad[j].m_csKey, + inputGetKeymap(PAD_DEFAULT, m_astJoypad[j].m_eKeyFlag)); + } + } +} + +void Window::vCheckConfig() +{ + int iValue; + int iAdjusted; + float fValue; + float fAdjusted; + std::string sValue; + + // Directories section + // + sValue = m_poDirConfig->sGetKey("gb_roms"); + if (sValue != "" && ! Glib::file_test(sValue, Glib::FILE_TEST_IS_DIR)) + { + m_poDirConfig->vSetKey("gb_roms", Glib::get_home_dir()); + } + sValue = m_poDirConfig->sGetKey("gba_roms"); + if (sValue != "" && ! Glib::file_test(sValue, Glib::FILE_TEST_IS_DIR)) + { + m_poDirConfig->vSetKey("gba_roms", Glib::get_home_dir()); + } + sValue = m_poDirConfig->sGetKey("batteries"); + if (sValue != "" && ! Glib::file_test(sValue, Glib::FILE_TEST_IS_DIR)) + { + m_poDirConfig->vSetKey("batteries", m_sUserDataDir); + } + sValue = m_poDirConfig->sGetKey("cheats"); + if (sValue != "" && ! Glib::file_test(sValue, Glib::FILE_TEST_IS_DIR)) + { + m_poDirConfig->vSetKey("cheats", m_sUserDataDir); + } + sValue = m_poDirConfig->sGetKey("saves"); + if (sValue != "" && ! Glib::file_test(sValue, Glib::FILE_TEST_IS_DIR)) + { + m_poDirConfig->vSetKey("saves", m_sUserDataDir); + } + sValue = m_poDirConfig->sGetKey("captures"); + if (sValue != "" && ! Glib::file_test(sValue, Glib::FILE_TEST_IS_DIR)) + { + m_poDirConfig->vSetKey("captures", m_sUserDataDir); + } + + // Core section + // + if (m_poCoreConfig->sGetKey("frameskip") != "auto") + { + iValue = m_poCoreConfig->oGetKey("frameskip"); + iAdjusted = CLAMP(iValue, m_iFrameskipMin, m_iFrameskipMax); + if (iValue != iAdjusted) + { + m_poCoreConfig->vSetKey("frameskip", iAdjusted); + } + } + + sValue = m_poCoreConfig->sGetKey("bios_file"); + if (sValue != "" && ! Glib::file_test(sValue, Glib::FILE_TEST_IS_REGULAR)) + { + m_poCoreConfig->vSetKey("bios_file", ""); + } + if (m_poCoreConfig->sGetKey("bios_file") == "") + { + m_poCoreConfig->vSetKey("use_bios_file", false); + } + if (m_poCoreConfig->sGetKey("enable_rtc") == "") + { + m_poCoreConfig->vSetKey("enable_rtc", false); + } + + sValue = m_poCoreConfig->sGetKey("gb_bios_file"); + if (sValue != "" && ! Glib::file_test(sValue, Glib::FILE_TEST_IS_REGULAR)) + { + m_poCoreConfig->vSetKey("gb_bios_file", ""); + } + if (m_poCoreConfig->sGetKey("gb_bios_file") == "") + { + m_poCoreConfig->vSetKey("gb_use_bios_file", false); + } + + iValue = m_poCoreConfig->oGetKey("save_type"); + if (iValue != 0) + { + iAdjusted = CLAMP(iValue, m_iSaveTypeMin, m_iSaveTypeMax); + if (iValue != iAdjusted) + { + m_poCoreConfig->vSetKey("save_type", iAdjusted); + } + } + + iValue = m_poCoreConfig->oGetKey("flash_size"); + if (iValue != 64 && iValue != 128) + { + m_poCoreConfig->vSetKey("flash_size", 64); + } + + iValue = m_poCoreConfig->oGetKey("emulator_type"); + iAdjusted = CLAMP(iValue, m_iEmulatorTypeMin, m_iEmulatorTypeMax); + if (iValue != iAdjusted) + { + m_poCoreConfig->vSetKey("emulator_type", iAdjusted); + } + + // Display section + // + iValue = m_poDisplayConfig->oGetKey("scale"); + iAdjusted = CLAMP(iValue, m_iScaleMin, m_iScaleMax); + if (iValue != iAdjusted) + { + m_poDisplayConfig->vSetKey("scale", iAdjusted); + } + + iValue = m_poCoreConfig->oGetKey("show_speed"); + iAdjusted = CLAMP(iValue, m_iShowSpeedMin, m_iShowSpeedMax); + if (iValue != iAdjusted) + { + m_poCoreConfig->vSetKey("show_speed", iAdjusted); + } + + iValue = m_poDisplayConfig->oGetKey("filter2x"); + iAdjusted = CLAMP(iValue, m_iFilter2xMin, m_iFilter2xMax); + if (iValue != iAdjusted) + { + m_poDisplayConfig->vSetKey("filter2x", iAdjusted); + } + + iValue = m_poDisplayConfig->oGetKey("filterIB"); + iAdjusted = CLAMP(iValue, m_iFilterIBMin, m_iFilterIBMax); + if (iValue != iAdjusted) + { + m_poDisplayConfig->vSetKey("filterIB", iAdjusted); + } + + iValue = m_poDisplayConfig->oGetKey("output"); + iAdjusted = CLAMP(iValue, m_iVideoOutputMin, m_iVideoOutputMax); + if (iValue != iAdjusted) + { + m_poDisplayConfig->vSetKey("output", iAdjusted); + } + + // Sound section + // + iValue = m_poSoundConfig->oGetKey("sample_rate"); + iAdjusted = CLAMP(iValue, m_iSoundSampleRateMin, m_iSoundSampleRateMax); + if (iValue != iAdjusted) + { + m_poSoundConfig->vSetKey("sample_rate", iAdjusted); + } + + fValue = m_poSoundConfig->oGetKey("volume"); + fAdjusted = CLAMP(fValue, m_fSoundVolumeMin, m_fSoundVolumeMax); + if (fValue != fAdjusted) + { + m_poSoundConfig->vSetKey("volume", fAdjusted); + } + + // Input section + // + iValue = m_poInputConfig->oGetKey("active_joypad"); + iAdjusted = CLAMP(iValue, m_iJoypadMin, m_iJoypadMax); + if (iValue != iAdjusted) + { + m_poInputConfig->vSetKey("active_joypad", iAdjusted); + } +} + +void Window::vLoadConfig(const std::string & _rsFile) +{ + try + { + m_oConfig.vLoad(_rsFile, false, false); + } + catch (const Glib::Error & e) + { + vPopupError(e.what().c_str()); + } +} + +void Window::vSaveConfig(const std::string & _rsFile) +{ + try + { + m_oConfig.vSave(_rsFile); + } + catch (const Glib::Error & e) + { + vPopupError(e.what().c_str()); + } +} + +void Window::vApplyConfigFilter() +{ + int iFilter = m_poDisplayConfig->oGetKey("filter2x"); + m_poScreenArea->vSetFilter((EFilter)iFilter); + if (emulating) + { + vDrawScreen(); + } +} + +void Window::vApplyConfigFilterIB() +{ + int iFilter = m_poDisplayConfig->oGetKey("filterIB"); + m_poScreenArea->vSetFilterIB((EFilterIB)iFilter); + if (emulating) + { + vDrawScreen(); + } +} + +void Window::vApplyConfigMute() +{ + bool bMute = m_poSoundConfig->oGetKey("mute"); + if (bMute) + { + soundSetEnable(0x000); + } + else + { + soundSetEnable(0x30f); + } +} + +void Window::vApplyConfigVolume() +{ + float fSoundVolume = m_poSoundConfig->oGetKey("volume"); + soundSetVolume(fSoundVolume); +} + +void Window::vApplyConfigSoundSampleRate() +{ + long iSoundSampleRate = m_poSoundConfig->oGetKey("sample_rate"); + if (m_eCartridge == CartridgeGBA) + { + soundSetSampleRate(iSoundSampleRate); + } + else if (m_eCartridge == CartridgeGB) + { + gbSoundSetSampleRate(iSoundSampleRate); + } +} + +void Window::vApplyConfigGBSystem() +{ + gbEmulatorType = m_poCoreConfig->oGetKey("emulator_type"); +} + +void Window::vApplyConfigGBBorder() +{ + gbBorderOn = m_poCoreConfig->oGetKey("gb_border"); + if (emulating && m_eCartridge == CartridgeGB && gbBorderOn) + { + gbSgbRenderBorder(); + } + vUpdateScreen(); +} + +void Window::vApplyConfigGBPrinter() +{ + bool bPrinter = m_poCoreConfig->oGetKey("gb_printer"); + if (bPrinter) + { + gbSerialFunction = gbPrinterSend; + } + else + { + gbSerialFunction = NULL; + } +} + +void Window::vApplyConfigGBASaveType() +{ + int iSaveType = m_poCoreConfig->oGetKey("save_type"); + cpuSaveType = iSaveType; +} + +void Window::vApplyConfigGBAFlashSize() +{ + int iFlashSize = m_poCoreConfig->oGetKey("flash_size"); + if (iFlashSize == 64) + { + flashSetSize(0x10000); + } + else + { + flashSetSize(0x20000); + } +} + +void Window::vApplyConfigGBARTC() +{ + bool iRTC = m_poCoreConfig->oGetKey("enable_rtc"); + rtcEnable(iRTC); +} + +void Window::vHistoryAdd(const std::string & _rsFile) +{ + std::string sURL = "file://" + _rsFile; + + m_poRecentManager->add_item(sURL); +} + +void Window::vApplyConfigJoypads() +{ + for (int i = m_iJoypadMin; i <= m_iJoypadMax; i++) + { + char csPrefix[20]; + snprintf(csPrefix, sizeof(csPrefix), "joypadSDL%d_", i); + std::string sPrefix(csPrefix); + + for (guint j = 0; j < G_N_ELEMENTS(m_astJoypad); j++) + { + inputSetKeymap((EPad)i, m_astJoypad[j].m_eKeyFlag, + m_poInputConfig->oGetKey(sPrefix + m_astJoypad[j].m_csKey)); + } + } +} + +void Window::vApplyConfigFrameskip() +{ + std::string sFrameskip = m_poCoreConfig->oGetKey("frameskip"); + + if (sFrameskip == "auto") + { + m_bAutoFrameskip = true; + gbFrameSkip = 0; + systemFrameSkip = 0; + } + else + { + m_bAutoFrameskip = false; + int iFrameskip = m_poCoreConfig->oGetKey("frameskip"); + gbFrameSkip = iFrameskip; + systemFrameSkip = iFrameskip; + } +} + +void Window::vApplyConfigShowSpeed() +{ + m_eShowSpeed = (EShowSpeed)m_poCoreConfig->oGetKey("show_speed"); + if (m_eShowSpeed == ShowNone) + vSetDefaultTitle(); +} + +void Window::vApplyPerGameConfig() +{ + std::string sRDBFile = PKGDATADIR "/vba-over.ini"; + if (!Glib::file_test(sRDBFile, Glib::FILE_TEST_EXISTS)) + return; + + char csGameID[5]; + csGameID[0] = rom[0xac]; + csGameID[1] = rom[0xad]; + csGameID[2] = rom[0xae]; + csGameID[3] = rom[0xaf]; + csGameID[4] = '\0'; + + Glib::KeyFile oKeyFile; + oKeyFile.load_from_file(sRDBFile); + + if (!oKeyFile.has_group(csGameID)) + return; + + if (oKeyFile.has_key(csGameID, "rtcEnabled")) + { + bool bRTCEnabled = oKeyFile.get_boolean(csGameID, "rtcEnabled"); + rtcEnable(bRTCEnabled); + } + + if (oKeyFile.has_key(csGameID, "flashSize")) + { + Glib::ustring sFlashSize = oKeyFile.get_string(csGameID, "flashSize"); + int iFlashSize = atoi(sFlashSize.c_str()); + if (iFlashSize == 0x10000 || iFlashSize == 0x20000) + flashSetSize(iFlashSize); + } + + if (oKeyFile.has_key(csGameID, "saveType")) + { + int iSaveType = oKeyFile.get_integer(csGameID, "saveType"); + if(iSaveType >= 0 && iSaveType <= 5) + cpuSaveType = iSaveType; + } + + if (oKeyFile.has_key(csGameID, "mirroringEnabled")) + { + mirroringEnable = oKeyFile.get_boolean(csGameID, "mirroringEnabled"); + } +} + +void Window::vSaveJoypadsToConfig() +{ + for (int i = m_iJoypadMin; i <= m_iJoypadMax; i++) + { + char csPrefix[20]; + snprintf(csPrefix, sizeof(csPrefix), "joypadSDL%d_", i); + std::string sPrefix(csPrefix); + + for (guint j = 0; j < G_N_ELEMENTS(m_astJoypad); j++) + { + m_poInputConfig->vSetKey(sPrefix + m_astJoypad[j].m_csKey, + inputGetKeymap((EPad)i, m_astJoypad[j].m_eKeyFlag)); + } + } +} + +void Window::vUpdateScreen() +{ + if (m_eCartridge == CartridgeGB) + { + if (gbBorderOn) + { + m_iScreenWidth = m_iSGBScreenWidth; + m_iScreenHeight = m_iSGBScreenHeight; + gbBorderLineSkip = m_iSGBScreenWidth; + gbBorderColumnSkip = (m_iSGBScreenWidth - m_iGBScreenWidth) / 2; + gbBorderRowSkip = (m_iSGBScreenHeight - m_iGBScreenHeight) / 2; + } + else + { + m_iScreenWidth = m_iGBScreenWidth; + m_iScreenHeight = m_iGBScreenHeight; + gbBorderLineSkip = m_iGBScreenWidth; + gbBorderColumnSkip = 0; + gbBorderRowSkip = 0; + } + } + else if (m_eCartridge == CartridgeGBA) + { + m_iScreenWidth = m_iGBAScreenWidth; + m_iScreenHeight = m_iGBAScreenHeight; + } + + g_return_if_fail(m_iScreenWidth >= 1 && m_iScreenHeight >= 1); + + m_poScreenArea->vSetSize(m_iScreenWidth, m_iScreenHeight); + m_poScreenArea->vSetScale(m_poDisplayConfig->oGetKey("scale")); + + resize(1, 1); + + if (emulating) + { + vDrawScreen(); + } + else + { + vDrawDefaultScreen(); + } +} + +bool Window::bLoadROM(const std::string & _rsFile) +{ + vOnFileClose(); + + // clear cheat list + cheatsDeleteAll(false); + gbCheatRemoveAll(); + + m_sRomFile = _rsFile; + const char * csFile = _rsFile.c_str(); + + IMAGE_TYPE eType = utilFindType(csFile); + if (eType == IMAGE_UNKNOWN) + { + vPopupError(_("Unknown file type %s"), csFile); + return false; + } + + bool bLoaded = false; + if (eType == IMAGE_GB) + { + bLoaded = gbLoadRom(csFile); + if (bLoaded) + { + m_eCartridge = CartridgeGB; + m_stEmulator = GBSystem; + + useBios = m_poCoreConfig->oGetKey("gb_use_bios_file"); + gbGetHardwareType(); + + if (gbHardware & 5) + { + gbCPUInit(m_poCoreConfig->sGetKey("gb_bios_file").c_str(), useBios); + } + + // If the bios file was rejected by gbCPUInit + if (m_poCoreConfig->oGetKey("gb_use_bios_file") && ! useBios) + { + m_poCoreConfig->vSetKey("gb_bios_file", ""); + } + + gbReset(); + } + } + else if (eType == IMAGE_GBA) + { + int iSize = CPULoadRom(csFile); + bLoaded = (iSize > 0); + if (bLoaded) + { + m_eCartridge = CartridgeGBA; + m_stEmulator = GBASystem; + + vApplyPerGameConfig(); + + useBios = m_poCoreConfig->oGetKey("use_bios_file"); + CPUInit(m_poCoreConfig->sGetKey("bios_file").c_str(), useBios); + CPUReset(); + + // If the bios file was rejected by CPUInit + if (m_poCoreConfig->oGetKey("use_bios_file") && ! useBios) + { + m_poCoreConfig->vSetKey("bios_file", ""); + } + } + } + + if (! bLoaded) + { + return false; + } + + vLoadBattery(); + vLoadCheats(); + vUpdateScreen(); + + emulating = 1; + m_bWasEmulating = false; + + vApplyConfigSoundSampleRate(); + + vUpdateGameSlots(); + vHistoryAdd(_rsFile); + + for (std::list::iterator it = m_listSensitiveWhenPlaying.begin(); + it != m_listSensitiveWhenPlaying.end(); + it++) + { + (*it)->set_sensitive(); + } + + if (m_poCoreConfig->oGetKey("load_game_auto")) + { + vOnLoadGameMostRecent(); + } + + vStartEmu(); + + return true; +} + +void Window::vPopupError(const char * _csFormat, ...) +{ + va_list args; + va_start(args, _csFormat); + char * csMsg = g_strdup_vprintf(_csFormat, args); + va_end(args); + + Gtk::MessageDialog oDialog(*this, + csMsg, + false, + Gtk::MESSAGE_ERROR, + Gtk::BUTTONS_OK); + oDialog.run(); + g_free(csMsg); +} + +void Window::vPopupErrorV(const char * _csFormat, va_list _args) +{ + char * csMsg = g_strdup_vprintf(_csFormat, _args); + + Gtk::MessageDialog oDialog(*this, + csMsg, + false, + Gtk::MESSAGE_ERROR, + Gtk::BUTTONS_OK); + oDialog.run(); + g_free(csMsg); +} + +void Window::vDrawScreen() +{ + m_poScreenArea->vDrawPixels(pix); + m_iFrameCount++; +} + +void Window::vDrawDefaultScreen() +{ + m_poScreenArea->vDrawBlackScreen(); +} + +void Window::vSetDefaultTitle() +{ + set_title("VBA-M"); +} + +void Window::vShowSpeed(int _iSpeed) +{ + char csTitle[50]; + + if (m_eShowSpeed == ShowPercentage) + { + snprintf(csTitle, 50, _("VBA-M - %d%%"), _iSpeed); + set_title(csTitle); + } + else if (m_eShowSpeed == ShowDetailed) + { + snprintf(csTitle, 50, _("VBA-M - %d%% (%d, %d fps)"), + _iSpeed, systemFrameSkip, m_iFrameCount); + set_title(csTitle); + } + + m_iFrameCount = 0; +} + +void Window::vComputeFrameskip(int _iRate) +{ + static Glib::TimeVal uiLastTime; + static int iFrameskipAdjust = 0; + + Glib::TimeVal uiTime; + uiTime.assign_current_time(); + + if (m_bWasEmulating) + { + if (m_bAutoFrameskip) + { + Glib::TimeVal uiDiff = uiTime - uiLastTime; + const int iWantedSpeed = 100; + int iSpeed = iWantedSpeed; + + if (uiDiff != Glib::TimeVal(0, 0)) + { + iSpeed = (1000000 / _iRate) / (uiDiff.as_double() * 1000); + } + + if (iSpeed >= iWantedSpeed - 2) + { + iFrameskipAdjust++; + if (iFrameskipAdjust >= 3) + { + iFrameskipAdjust = 0; + if (systemFrameSkip > 0) + { + systemFrameSkip--; + } + } + } + else + { + if (iSpeed < iWantedSpeed - 20) + { + iFrameskipAdjust -= ((iWantedSpeed - 10) - iSpeed) / 5; + } + else if (systemFrameSkip < 9) + { + iFrameskipAdjust--; + } + + if (iFrameskipAdjust <= -4) + { + iFrameskipAdjust = 0; + if (systemFrameSkip < 9) + { + systemFrameSkip++; + } + } + } + } + } + else + { + m_bWasEmulating = true; + } + + uiLastTime = uiTime; +} + +void Window::vCaptureScreen(int _iNum) +{ + std::string sBaseName; + std::string sDir = m_poDirConfig->sGetKey("captures"); + if (sDir == "") + { + sDir = m_sUserDataDir; + } + + sBaseName = sDir + "/" + sCutSuffix(Glib::path_get_basename(m_sRomFile)); + + + char * csFile = g_strdup_printf("%s_%02d.png", + sBaseName.c_str(), + _iNum); + + m_stEmulator.emuWritePNG(csFile); + + g_free(csFile); +} + +void Window::vCreateFileOpenDialog() +{ + if (m_poFileOpenDialog != NULL) + { + return; + } + + std::string sGBDir = m_poDirConfig->sGetKey("gb_roms"); + std::string sGBADir = m_poDirConfig->sGetKey("gba_roms"); + + Gtk::FileChooserDialog * poDialog = new Gtk::FileChooserDialog(*this, _("Open")); + poDialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + poDialog->add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK); + + try + { + if (sGBDir != "") + { + poDialog->add_shortcut_folder(sGBDir); + poDialog->set_current_folder(sGBDir); + } + + if (sGBADir != "" && sGBADir != sGBDir) + { + poDialog->add_shortcut_folder(sGBADir); + poDialog->set_current_folder(sGBADir); + } + } + catch (const Gtk::FileChooserError& e) + { + // Most likely the shortcut already exists, so do nothing + } + + const char * acsPattern[] = + { + // GBA + "*.[bB][iI][nN]", "*.[aA][gG][bB]", "*.[gG][bB][aA]", + // GB + "*.[gG][bB]", "*.[sS][gG][bB]", "*.[cC][gG][bB]", "*.[gG][bB][cC]", + // Both + "*.[mM][bB]", "*.[eE][lL][fF]", "*.[zZ][iI][pP]", "*.[zZ]", "*.[gG][zZ]", + "*.7[zZ]", + }; + + Gtk::FileFilter oAllGBAFilter; + oAllGBAFilter.set_name(_("All Gameboy Advance files")); + for (guint i = 0; i < G_N_ELEMENTS(acsPattern); i++) + { + oAllGBAFilter.add_pattern(acsPattern[i]); + } + + Gtk::FileFilter oGBAFilter; + oGBAFilter.set_name(_("Gameboy Advance files")); + for (int i = 0; i < 3; i++) + { + oGBAFilter.add_pattern(acsPattern[i]); + } + + Gtk::FileFilter oGBFilter; + oGBFilter.set_name(_("Gameboy files")); + for (int i = 3; i < 7; i++) + { + oGBFilter.add_pattern(acsPattern[i]); + } + + poDialog->add_filter(oAllGBAFilter); + poDialog->add_filter(oGBAFilter); + poDialog->add_filter(oGBFilter); + + m_poFileOpenDialog = poDialog; +} + +void Window::vLoadBattery() +{ + std::string sBattery; + std::string sDir = m_poDirConfig->sGetKey("batteries"); + if (sDir == "") + { + sDir = m_sUserDataDir; + } + + sBattery = sDir + "/" + sCutSuffix(Glib::path_get_basename(m_sRomFile)) + ".sav"; + + if (m_stEmulator.emuReadBattery(sBattery.c_str())) + { + systemScreenMessage(_("Loaded battery")); + } +} + +void Window::vLoadCheats() +{ + std::string sCheats; + std::string sDir = m_poDirConfig->sGetKey("cheats"); + if (sDir == "") + { + sDir = m_sUserDataDir; + } + + sCheats = sDir + "/" + sCutSuffix(Glib::path_get_basename(m_sRomFile)) + ".clt"; + + if (Glib::file_test(sCheats, Glib::FILE_TEST_EXISTS)) + { + if (m_eCartridge == CartridgeGB) + { + gbCheatsLoadCheatList(sCheats.c_str()); + } + else if (m_eCartridge == CartridgeGBA) + { + cheatsLoadCheatList(sCheats.c_str()); + } + } +} + +void Window::vSaveBattery() +{ + std::string sBattery; + std::string sDir = m_poDirConfig->sGetKey("batteries"); + if (sDir == "") + { + sDir = m_sUserDataDir; + } + + sBattery = sDir + "/" + sCutSuffix(Glib::path_get_basename(m_sRomFile)) + ".sav"; + + if (m_stEmulator.emuWriteBattery(sBattery.c_str())) + { + systemScreenMessage(_("Saved battery")); + } +} + +void Window::vSaveCheats() +{ + std::string sCheats; + std::string sDir = m_poDirConfig->sGetKey("cheats"); + if (sDir == "") + { + sDir = m_sUserDataDir; + } + + sCheats = sDir + "/" + sCutSuffix(Glib::path_get_basename(m_sRomFile)) + ".clt"; + + if (m_eCartridge == CartridgeGB) + { + gbCheatsSaveCheatList(sCheats.c_str()); + } + else if (m_eCartridge == CartridgeGBA) + { + cheatsSaveCheatList(sCheats.c_str()); + } +} + +void Window::vStartEmu() +{ + if (m_oEmuSig.connected()) + { + return; + } + + m_oEmuSig = Glib::signal_idle().connect(sigc::mem_fun(*this, &Window::bOnEmuIdle), + Glib::PRIORITY_HIGH_IDLE + 30); +} + +void Window::vStopEmu() +{ + m_oEmuSig.disconnect(); + m_bWasEmulating = false; +} + +void Window::vUpdateGameSlots() +{ + if (m_eCartridge == CartridgeNone) + { + std::string sDateTime = _("----/--/-- --:--:--"); + + for (int i = 0; i < 10; i++) + { + char csPrefix[10]; + snprintf(csPrefix, sizeof(csPrefix), "%2d ", i + 1); + + Gtk::Label * poLabel; + poLabel = dynamic_cast(m_apoLoadGameItem[i]->get_child()); + poLabel->set_text(csPrefix + sDateTime); + m_apoLoadGameItem[i]->set_sensitive(false); + + poLabel = dynamic_cast(m_apoSaveGameItem[i]->get_child()); + poLabel->set_text(csPrefix + sDateTime); + m_apoSaveGameItem[i]->set_sensitive(false); + + m_astGameSlot[i].m_bEmpty = true; + } + } + else + { + std::string sFileBase; + std::string sDir = m_poDirConfig->sGetKey("saves"); + if (sDir == "") + { + sDir = m_sUserDataDir; + } + + sFileBase = sDir + "/" + sCutSuffix(Glib::path_get_basename(m_sRomFile)); + + const char * csDateFormat = _("%Y/%m/%d %H:%M:%S"); + + for (int i = 0; i < 10; i++) + { + char csPrefix[10]; + snprintf(csPrefix, sizeof(csPrefix), "%2d ", i + 1); + + char csSlot[10]; + snprintf(csSlot, sizeof(csSlot), "%d", i + 1); + m_astGameSlot[i].m_sFile = sFileBase + csSlot + ".sgm"; + + std::string sDateTime; + struct stat stStat; + if (stat(m_astGameSlot[i].m_sFile.c_str(), &stStat) == -1) + { + sDateTime = _("----/--/-- --:--:--"); + m_astGameSlot[i].m_bEmpty = true; + } + else + { + char csDateTime[30]; + strftime(csDateTime, sizeof(csDateTime), csDateFormat, + localtime(&stStat.st_mtime)); + sDateTime = csDateTime; + m_astGameSlot[i].m_bEmpty = false; + m_astGameSlot[i].m_uiTime = stStat.st_mtime; + } + + Gtk::Label * poLabel; + poLabel = dynamic_cast(m_apoLoadGameItem[i]->get_child()); + poLabel->set_text(csPrefix + sDateTime); + m_apoLoadGameItem[i]->set_sensitive(! m_astGameSlot[i].m_bEmpty); + + poLabel = dynamic_cast(m_apoSaveGameItem[i]->get_child()); + poLabel->set_text(csPrefix + sDateTime); + m_apoSaveGameItem[i]->set_sensitive(); + } + } +} + +void Window::vToggleFullscreen() +{ + if(!m_bFullscreen) + { + fullscreen(); + m_poMenuBar->hide(); + } + else + { + unfullscreen(); + m_poMenuBar->show(); + } +} + +void Window::vSDLPollEvents() +{ + SDL_Event event; + while(SDL_PollEvent(&event)) + { + switch(event.type) + { + case SDL_JOYHATMOTION: + case SDL_JOYBUTTONDOWN: + case SDL_JOYBUTTONUP: + case SDL_JOYAXISMOTION: + case SDL_KEYDOWN: + case SDL_KEYUP: + inputProcessSDLEvent(event); + break; + } + } +} + +std::string Window::sGetUiFilePath(const std::string &_sFileName) +{ + // Use the ui file from the source folder if it exists + // to make gvbam runnable without installation + std::string sUiFile = "src/gtk/ui/" + _sFileName; + if (!Glib::file_test(sUiFile, Glib::FILE_TEST_EXISTS)) + { + sUiFile = "../src/gtk/ui/" + _sFileName; + } + + return sUiFile; +} + +} // VBA namespace diff --git a/src/gtk/window.h b/src/gtk/window.h new file mode 100644 index 0000000..7765249 --- /dev/null +++ b/src/gtk/window.h @@ -0,0 +1,288 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef __VBA_WINDOW_H__ +#define __VBA_WINDOW_H__ + +#include +#include +#include +#include +#include +#include + +#include "../System.h" +#include "../sdl/inputSDL.h" + +#include "configfile.h" +#include "screenarea.h" +#include "filters.h" + +namespace VBA +{ + +class Window : public Gtk::Window +{ + friend class Gtk::Builder; + +public: + virtual ~Window(); + + inline static Window * poGetInstance() { return m_poInstance; } + static std::string sGetUiFilePath(const std::string &_sFileName); + + enum ECartridge + { + CartridgeNone, + CartridgeGB, + CartridgeGBA + }; + + enum EVideoOutput + { + OutputCairo, + OutputOpenGL + }; + + enum EEmulatorType + { + EmulatorAuto, + EmulatorCGB, + EmulatorSGB, + EmulatorGB, + EmulatorGBA, + EmulatorSGB2 + }; + + enum ESaveType + { + SaveAuto, + SaveEEPROM, + SaveSRAM, + SaveFlash, + SaveEEPROMSensor, + SaveNone + }; + + // GB/GBA screen sizes + const int m_iGBScreenWidth; + const int m_iGBScreenHeight; + const int m_iSGBScreenWidth; + const int m_iSGBScreenHeight; + const int m_iGBAScreenWidth; + const int m_iGBAScreenHeight; + + bool bLoadROM(const std::string & _rsFile); + void vPopupError(const char * _csFormat, ...); + void vPopupErrorV(const char * _csFormat, va_list _args); + void vDrawScreen(); + void vComputeFrameskip(int _iRate); + void vShowSpeed(int _iSpeed); + void vCaptureScreen(int _iNum); + void vApplyConfigFilter(); + void vApplyConfigFilterIB(); + void vApplyConfigScreenArea(); + void vApplyConfigMute(); + void vApplyConfigVolume(); + void vApplyConfigSoundSampleRate(); + void vApplyConfigGBSystem(); + void vApplyConfigGBBorder(); + void vApplyConfigGBPrinter(); + void vApplyConfigGBASaveType(); + void vApplyConfigGBAFlashSize(); + void vApplyConfigGBARTC(); + void vApplyConfigFrameskip(); + void vApplyConfigShowSpeed(); + void vApplyPerGameConfig(); + void vUpdateScreen(); + + inline ECartridge eGetCartridge() const { return m_eCartridge; } + +protected: + Window(GtkWindow * _pstWindow, + const Glib::RefPtr & _poXml); + + enum EShowSpeed + { + ShowNone, + ShowPercentage, + ShowDetailed + }; + + enum ESoundStatus + { + SoundOff, + SoundMute, + SoundOn + }; + + enum EColorFormat + { + ColorFormatRGB, + ColorFormatBGR + }; + + virtual void vOnMenuEnter(); + virtual void vOnMenuExit(); + virtual void vOnFileOpen(); + virtual void vOnFileLoad(); + virtual void vOnFileSave(); + virtual void vOnLoadGameMostRecent(); + virtual void vOnLoadGameAutoToggled(Gtk::CheckMenuItem * _poCMI); + void vOnLoadGame(int _iSlot); + virtual void vOnSaveGameOldest(); + void vOnSaveGame(int _iSlot); + virtual void vOnFilePauseToggled(Gtk::CheckMenuItem * _poCMI); + virtual void vOnFileReset(); + virtual void vOnRecentFile(); + virtual void vOnFileScreenCapture(); + virtual void vOnFileClose(); + virtual void vOnFileExit(); + virtual void vOnVideoFullscreen(); + virtual void vOnDirectories(); + virtual void vOnGeneralConfigure(); + virtual void vOnJoypadConfigure(); + virtual void vOnDisplayConfigure(); + virtual void vOnSoundConfigure(); + virtual void vOnGameBoyConfigure(); + virtual void vOnGameBoyAdvanceConfigure(); + virtual void vOnCheatList(); + virtual void vOnCheatDisableToggled(Gtk::CheckMenuItem * _poCMI); + virtual void vOnHelpAbout(); + virtual bool bOnEmuIdle(); + + virtual bool on_focus_in_event(GdkEventFocus * _pstEvent); + virtual bool on_focus_out_event(GdkEventFocus * _pstEvent); + virtual bool on_key_press_event(GdkEventKey * _pstEvent); + virtual bool on_key_release_event(GdkEventKey * _pstEvent); + virtual bool on_window_state_event(GdkEventWindowState* _pstEvent); + +private: + // Config limits + const int m_iFrameskipMin; + const int m_iFrameskipMax; + const int m_iScaleMin; + const int m_iScaleMax; + const int m_iShowSpeedMin; + const int m_iShowSpeedMax; + const int m_iSaveTypeMin; + const int m_iSaveTypeMax; + const int m_iSoundSampleRateMin; + const int m_iSoundSampleRateMax; + const float m_fSoundVolumeMin; + const float m_fSoundVolumeMax; + const int m_iEmulatorTypeMin; + const int m_iEmulatorTypeMax; + const int m_iFilter2xMin; + const int m_iFilter2xMax; + const int m_iFilterIBMin; + const int m_iFilterIBMax; + const EPad m_iJoypadMin; + const EPad m_iJoypadMax; + const int m_iVideoOutputMin; + const int m_iVideoOutputMax; + + static Window * m_poInstance; + + Glib::RefPtr m_poXml; + + std::string m_sUserDataDir; + std::string m_sConfigFile; + Config::File m_oConfig; + Config::Section * m_poDirConfig; + Config::Section * m_poCoreConfig; + Config::Section * m_poDisplayConfig; + Config::Section * m_poSoundConfig; + Config::Section * m_poInputConfig; + + Gtk::FileChooserDialog * m_poFileOpenDialog; + + ScreenArea * m_poScreenArea; + Gtk::CheckMenuItem * m_poFilePauseItem; + Gtk::MenuBar * m_poMenuBar; + + struct SGameSlot + { + bool m_bEmpty; + std::string m_sFile; + time_t m_uiTime; + }; + + struct SJoypadKey + { + const char * m_csKey; + const EKey m_eKeyFlag; + }; + + static const SJoypadKey m_astJoypad[]; + + Gtk::MenuItem * m_apoLoadGameItem[10]; + Gtk::MenuItem * m_apoSaveGameItem[10]; + SGameSlot m_astGameSlot[10]; + + Glib::RefPtr m_poRecentManager; + Gtk::MenuItem * m_poRecentMenu; + Gtk::RecentChooserMenu * m_poRecentChooserMenu; + + std::list m_listSensitiveWhenPlaying; + + sigc::connection m_oEmuSig; + + int m_bFullscreen; + int m_iScreenWidth; + int m_iScreenHeight; + int m_iFrameCount; + + std::string m_sRomFile; + ECartridge m_eCartridge; + EmulatedSystem m_stEmulator; + bool m_bPaused; + bool m_bWasEmulating; + bool m_bAutoFrameskip; + EShowSpeed m_eShowSpeed; + + void vInitSystem(); + void vUnInitSystem(); + void vInitSDL(); + void vInitConfig(); + void vCheckConfig(); + void vInitColors(EColorFormat _eColorFormat); + void vLoadConfig(const std::string & _rsFile); + void vSaveConfig(const std::string & _rsFile); + void vHistoryAdd(const std::string & _rsFile); + void vApplyConfigJoypads(); + void vSaveJoypadsToConfig(); + void vDrawDefaultScreen(); + void vSetDefaultTitle(); + void vCreateFileOpenDialog(); + void vLoadBattery(); + void vLoadCheats(); + void vSaveBattery(); + void vSaveCheats(); + void vStartEmu(); + void vStopEmu(); + void vUpdateGameSlots(); + void vToggleFullscreen(); + void vSDLPollEvents(); +}; + +} // namespace VBA + + +#endif // __VBA_WINDOW_H__ diff --git a/src/gtk/windowcallbacks.cpp b/src/gtk/windowcallbacks.cpp new file mode 100644 index 0000000..9396b99 --- /dev/null +++ b/src/gtk/windowcallbacks.cpp @@ -0,0 +1,606 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "window.h" + +#include +#include +#include +#include + +#include + +#include "../gba/GBA.h" +#include "../gba/Sound.h" +#include "../gb/gb.h" +#include "../gb/gbGlobals.h" +#include "../Util.h" +#include "../sdl/inputSDL.h" + +#include "tools.h" +#include "intl.h" +#include "joypadconfig.h" +#include "directoriesconfig.h" +#include "displayconfig.h" +#include "soundconfig.h" +#include "gameboyconfig.h" +#include "gameboyadvanceconfig.h" +#include "generalconfig.h" +#include "gameboyadvancecheatlist.h" +#include "gameboycheatlist.h" + +namespace VBA +{ + +void Window::vOnMenuEnter() +{ + if (emulating && ! m_bPaused) + { + vStopEmu(); + soundPause(); + } +} + +void Window::vOnMenuExit() +{ + if (emulating && ! m_bPaused) + { + vStartEmu(); + soundResume(); + } +} + +void Window::vOnFileOpen() +{ + while (m_poFileOpenDialog->run() == Gtk::RESPONSE_OK) + { + if (bLoadROM(m_poFileOpenDialog->get_filename())) + { + break; + } + } + m_poFileOpenDialog->hide(); +} + +void Window::vOnFileLoad() +{ + std::string sSaveDir = m_poDirConfig->sGetKey("saves"); + + Gtk::FileChooserDialog oDialog(*this, _("Load game")); + oDialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + oDialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK); + + if (sSaveDir == "") + { + oDialog.set_current_folder(Glib::path_get_dirname(m_sRomFile)); + } + else + { + oDialog.set_current_folder(sSaveDir); + oDialog.add_shortcut_folder(sSaveDir); + } + + Gtk::FileFilter oSaveFilter; + oSaveFilter.set_name(_("VisualBoyAdvance save game")); + oSaveFilter.add_pattern("*.[sS][gG][mM]"); + + oDialog.add_filter(oSaveFilter); + + while (oDialog.run() == Gtk::RESPONSE_OK) + { + if (m_stEmulator.emuReadState(oDialog.get_filename().c_str())) + { + break; + } + } +} + +void Window::vOnFileSave() +{ + Glib::ustring sSaveDir = m_poDirConfig->sGetKey("saves"); + + Gtk::FileChooserDialog oDialog(*this, _("Save game"), + Gtk::FILE_CHOOSER_ACTION_SAVE); + oDialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + oDialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK); + + if (sSaveDir == "") + { + oDialog.set_current_folder(Glib::path_get_dirname(m_sRomFile)); + } + else + { + oDialog.set_current_folder(sSaveDir); + oDialog.add_shortcut_folder(sSaveDir); + } + oDialog.set_current_name(sCutSuffix(Glib::path_get_basename(m_sRomFile))); + + Gtk::FileFilter oSaveFilter; + oSaveFilter.set_name(_("VisualBoyAdvance save game")); + oSaveFilter.add_pattern("*.[sS][gG][mM]"); + + oDialog.add_filter(oSaveFilter); + + while (oDialog.run() == Gtk::RESPONSE_OK) + { + Glib::ustring sFile = oDialog.get_filename(); + if (! bHasSuffix(sFile, ".sgm", false)) + { + sFile += ".sgm"; + } + + if (Glib::file_test(sFile, Glib::FILE_TEST_EXISTS)) + { + Gtk::MessageDialog oConfirmDialog(*this, + _("File already exists. Overwrite it?"), + false, + Gtk::MESSAGE_QUESTION, + Gtk::BUTTONS_YES_NO); + if (oConfirmDialog.run() != Gtk::RESPONSE_YES) + { + continue; + } + } + + if (m_stEmulator.emuWriteState(sFile.c_str())) + { + break; + } + } +} + +void Window::vOnLoadGameMostRecent() +{ + int iMostRecent = -1; + time_t uiTimeMax = 0; + + for (int i = 0; i < 10; i++) + { + if (! m_astGameSlot[i].m_bEmpty + && (iMostRecent < 0 || m_astGameSlot[i].m_uiTime > uiTimeMax)) + { + iMostRecent = i; + uiTimeMax = m_astGameSlot[i].m_uiTime; + } + } + + if (iMostRecent >= 0) + { + vOnLoadGame(iMostRecent + 1); + } +} + +void Window::vOnLoadGameAutoToggled(Gtk::CheckMenuItem * _poCMI) +{ + m_poCoreConfig->vSetKey("load_game_auto", _poCMI->get_active()); +} + +void Window::vOnLoadGame(int _iSlot) +{ + int i = _iSlot - 1; + if (! m_astGameSlot[i].m_bEmpty) + { + m_stEmulator.emuReadState(m_astGameSlot[i].m_sFile.c_str()); + m_poFilePauseItem->set_active(false); + } +} + +void Window::vOnSaveGameOldest() +{ + int iOldest = -1; + time_t uiTimeMin = 0; + + for (int i = 0; i < 10; i++) + { + if (! m_astGameSlot[i].m_bEmpty + && (iOldest < 0 || m_astGameSlot[i].m_uiTime < uiTimeMin)) + { + iOldest = i; + uiTimeMin = m_astGameSlot[i].m_uiTime; + } + } + + if (iOldest >= 0) + { + vOnSaveGame(iOldest + 1); + } + else + { + vOnSaveGame(1); + } +} + +void Window::vOnSaveGame(int _iSlot) +{ + int i = _iSlot - 1; + m_stEmulator.emuWriteState(m_astGameSlot[i].m_sFile.c_str()); + vUpdateGameSlots(); +} + +void Window::vOnFilePauseToggled(Gtk::CheckMenuItem * _poCMI) +{ + m_bPaused = _poCMI->get_active(); + if (emulating) + { + if (m_bPaused) + { + vStopEmu(); + soundPause(); + } + else + { + vStartEmu(); + soundResume(); + } + } +} + +void Window::vOnFileReset() +{ + if (emulating) + { + m_stEmulator.emuReset(); + m_poFilePauseItem->set_active(false); + } +} + +void Window::vOnRecentFile() +{ + Glib::ustring sURI = m_poRecentChooserMenu->get_current_uri(); + + if (!sURI.empty()) + { + std::string sFileName = Glib::filename_from_uri(sURI); + bLoadROM(sFileName); + } +} + +void Window::vOnFileScreenCapture() +{ + std::string sCaptureDir = m_poDirConfig->sGetKey("captures"); + + Gtk::FileChooserDialog oDialog(*this, _("Save screenshot"), + Gtk::FILE_CHOOSER_ACTION_SAVE); + oDialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + oDialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK); + + if (sCaptureDir == "") + { + oDialog.set_current_folder(Glib::path_get_dirname(m_sRomFile)); + } + else + { + oDialog.set_current_folder(sCaptureDir); + oDialog.add_shortcut_folder(sCaptureDir); + } + oDialog.set_current_name(sCutSuffix(Glib::path_get_basename(m_sRomFile))); + + Gtk::FileFilter oPngFilter; + oPngFilter.set_name(_("PNG image")); + oPngFilter.add_pattern("*.[pP][nN][gG]"); + + oDialog.add_filter(oPngFilter); + + while (oDialog.run() == Gtk::RESPONSE_OK) + { + Glib::ustring sFile = oDialog.get_filename(); + Glib::ustring sExt = ".png"; + + if (! bHasSuffix(sFile, sExt, false)) + { + sFile += sExt; + } + + if (Glib::file_test(sFile, Glib::FILE_TEST_EXISTS)) + { + Gtk::MessageDialog oConfirmDialog(*this, + _("File already exists. Overwrite it?"), + false, + Gtk::MESSAGE_QUESTION, + Gtk::BUTTONS_YES_NO); + if (oConfirmDialog.run() != Gtk::RESPONSE_YES) + { + continue; + } + } + + if (m_stEmulator.emuWritePNG(sFile.c_str())) + { + break; + } + } +} + +void Window::vOnFileClose() +{ + if (m_eCartridge != CartridgeNone) + { + soundPause(); + vStopEmu(); + vSetDefaultTitle(); + vDrawDefaultScreen(); + vSaveBattery(); + vSaveCheats(); + m_stEmulator.emuCleanUp(); + m_eCartridge = CartridgeNone; + emulating = 0; + + vUpdateGameSlots(); + + for (std::list::iterator it = m_listSensitiveWhenPlaying.begin(); + it != m_listSensitiveWhenPlaying.end(); + it++) + { + (*it)->set_sensitive(false); + } + + m_poFilePauseItem->set_active(false); + } +} + +void Window::vOnFileExit() +{ + hide(); +} + +void Window::vOnVideoFullscreen() +{ + vToggleFullscreen(); +} + +void Window::vOnDirectories() +{ + DirectoriesConfigDialog oDialog(m_poDirConfig); + oDialog.set_transient_for(*this); + oDialog.run(); + + // Needed if saves dir changed + vUpdateGameSlots(); +} + +void Window::vOnJoypadConfigure() +{ + JoypadConfigDialog oDialog(m_poInputConfig); + oDialog.set_transient_for(*this); + oDialog.run(); +} + +void Window::vOnDisplayConfigure() +{ + std::string sUiFile = sGetUiFilePath("display.ui"); + Glib::RefPtr poBuilder = Gtk::Builder::create_from_file(sUiFile); + + DisplayConfigDialog * poDialog = 0; + poBuilder->get_widget_derived("DisplayConfigDialog", poDialog); + poDialog->vSetConfig(m_poDisplayConfig, this); + poDialog->set_transient_for(*this); + poDialog->run(); + poDialog->hide(); +} + +void Window::vOnSoundConfigure() +{ + std::string sUiFile = sGetUiFilePath("sound.ui"); + Glib::RefPtr poBuilder = Gtk::Builder::create_from_file(sUiFile); + + SoundConfigDialog * poDialog = 0; + poBuilder->get_widget_derived("SoundConfigDialog", poDialog); + poDialog->vSetConfig(m_poSoundConfig, this); + poDialog->set_transient_for(*this); + poDialog->run(); + poDialog->hide(); +} + +void Window::vOnGameBoyConfigure() +{ + std::string sUiFile = sGetUiFilePath("gameboy.ui"); + Glib::RefPtr poBuilder = Gtk::Builder::create_from_file(sUiFile); + + GameBoyConfigDialog * poDialog = 0; + poBuilder->get_widget_derived("GameBoyConfigDialog", poDialog); + poDialog->vSetConfig(m_poCoreConfig, this); + poDialog->set_transient_for(*this); + poDialog->run(); + poDialog->hide(); +} + +void Window::vOnGameBoyAdvanceConfigure() +{ + std::string sUiFile = sGetUiFilePath("gameboyadvance.ui"); + Glib::RefPtr poBuilder = Gtk::Builder::create_from_file(sUiFile); + + GameBoyAdvanceConfigDialog * poDialog = 0; + poBuilder->get_widget_derived("GameBoyAdvanceConfigDialog", poDialog); + poDialog->vSetConfig(m_poCoreConfig, this); + poDialog->set_transient_for(*this); + poDialog->run(); + poDialog->hide(); +} + +void Window::vOnGeneralConfigure() +{ + std::string sUiFile = sGetUiFilePath("preferences.ui"); + Glib::RefPtr poBuilder = Gtk::Builder::create_from_file(sUiFile); + + PreferencesDialog * poDialog = 0; + poBuilder->get_widget_derived("PreferencesDialog", poDialog); + poDialog->vSetConfig(m_poCoreConfig, this); + poDialog->set_transient_for(*this); + poDialog->run(); + poDialog->hide(); +} + +void Window::vOnCheatList() +{ + if (m_eCartridge == CartridgeGBA) + { + std::string sUiFile = sGetUiFilePath("cheatlist.ui"); + Glib::RefPtr poBuilder = Gtk::Builder::create_from_file(sUiFile); + + GameBoyAdvanceCheatListDialog * poDialog = 0; + poBuilder->get_widget_derived("CheatListDialog", poDialog); + poDialog->set_transient_for(*this); + poDialog->vSetWindow(this); + poDialog->run(); + poDialog->hide(); + } + else if (m_eCartridge == CartridgeGB) + { + std::string sUiFile = sGetUiFilePath("cheatlist.ui"); + Glib::RefPtr poBuilder = Gtk::Builder::create_from_file(sUiFile); + + GameBoyCheatListDialog * poDialog = 0; + poBuilder->get_widget_derived("CheatListDialog", poDialog); + poDialog->set_transient_for(*this); + poDialog->vSetWindow(this); + poDialog->run(); + poDialog->hide(); + } +} + +void Window::vOnCheatDisableToggled(Gtk::CheckMenuItem * _poCMI) +{ + if (m_eCartridge == CartridgeGB) { + cheatsEnabled = !cheatsEnabled; + } + else if (m_eCartridge == CartridgeGBA) { + cheatsEnabled = !cheatsEnabled; + } + + _poCMI->set_active(!cheatsEnabled); +} + +void Window::vOnHelpAbout() +{ + Gtk::AboutDialog oAboutDialog; + const char csGPLHeader[] = "This program is free software: you can redistribute it and/or modify\n" + "it under the terms of the GNU General Public License as published by\n" + "the Free Software Foundation, either version 2 of the License, or\n" + "(at your option) any later version.\n\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program. If not, see ."; + const char csCopyright[] = "Copyright (C) 1999-2003 Forgotten\n" + "Copyright (C) 2004-2006 VBA development team\n" + "Copyright (C) 2007-2011 VBA-M development team"; + + oAboutDialog.set_transient_for(*this); + + oAboutDialog.set_name(_("VBA-M")); + oAboutDialog.set_version(VERSION); + oAboutDialog.set_comments(_("Nintendo GameBoy Advance emulator.")); + oAboutDialog.set_license(csGPLHeader); + oAboutDialog.set_copyright(csCopyright); + oAboutDialog.set_logo_icon_name("vbam"); + + oAboutDialog.set_website("http://www.vba-m.com/"); + + std::list list_authors; + list_authors.push_back("Forgotten"); + list_authors.push_back("kxu"); + list_authors.push_back("Pokemonhacker"); + list_authors.push_back("Spacy51"); + list_authors.push_back("mudlord"); + list_authors.push_back("Nach"); + list_authors.push_back("jbo_85"); + list_authors.push_back("bgK"); + oAboutDialog.set_authors(list_authors); + + std::list list_artists; + list_artists.push_back("Matteo Drera"); + list_artists.push_back("Jakub Steiner"); + list_artists.push_back("Jones Lee"); + oAboutDialog.set_artists(list_artists); + + oAboutDialog.run(); +} + +bool Window::bOnEmuIdle() +{ + vSDLPollEvents(); + + m_stEmulator.emuMain(m_stEmulator.emuCount); + return true; +} + +bool Window::on_focus_in_event(GdkEventFocus * _pstEvent) +{ + if (emulating && !m_bPaused) + { + vStartEmu(); + soundResume(); + } + return false; +} + +bool Window::on_focus_out_event(GdkEventFocus * _pstEvent) +{ + if (emulating + && !m_bPaused + && m_poCoreConfig->oGetKey("pause_when_inactive")) + { + vStopEmu(); + soundPause(); + } + return false; +} + +bool Window::on_key_press_event(GdkEventKey * _pstEvent) +{ + // The menu accelerators are disabled when it is hidden + if (_pstEvent->keyval == GDK_F11 && !m_poMenuBar->is_visible()) + { + vToggleFullscreen(); + return true; + } + + // Forward the keyboard event to the input module by faking a SDL event + SDL_Event event; + event.type = SDL_KEYDOWN; + event.key.keysym.sym = (SDLKey)_pstEvent->keyval; + inputProcessSDLEvent(event); + + return Gtk::Window::on_key_press_event(_pstEvent); +} + +bool Window::on_key_release_event(GdkEventKey * _pstEvent) +{ + // Forward the keyboard event to the input module by faking a SDL event + SDL_Event event; + event.type = SDL_KEYUP; + event.key.keysym.sym = (SDLKey)_pstEvent->keyval; + inputProcessSDLEvent(event); + + + return Gtk::Window::on_key_release_event(_pstEvent); +} + +bool Window::on_window_state_event(GdkEventWindowState* _pstEvent) +{ + if (_pstEvent->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) + { + m_bFullscreen = _pstEvent->new_window_state & GDK_WINDOW_STATE_FULLSCREEN; + } + + return true; +} + +} // namespace VBA diff --git a/src/libretro/Makefile b/src/libretro/Makefile new file mode 100644 index 0000000..ca26df2 --- /dev/null +++ b/src/libretro/Makefile @@ -0,0 +1,77 @@ +TILED_RENDERING = 0 + +ifeq ($(platform),) +platform = unix +ifeq ($(shell uname -a),) + platform = win +else ifneq ($(findstring MINGW,$(shell uname -a)),) + platform = win +else ifneq ($(findstring win,$(shell uname -a)),) + platform = win +else ifneq ($(findstring Darwin,$(shell uname -a)),) + platform = osx +endif +endif + +TARGET_NAME = vbam + +ifeq ($(platform), unix) + TARGET := $(TARGET_NAME)_libretro.so + fpic := -fPIC + SHARED := -shared + TILED_RENDERING = 1 +else ifeq ($(platform), osx) + TARGET := $(TARGET_NAME)_libretro.dylib + fpic := -fPIC + SHARED := -dynamiclib +else + TARGET := $(TARGET_NAME)_libretro.dll + LDFLAGS += -Wl,-no-undefined -Wl,--version-script=link.T + CC = gcc + CXX = g++ + SHARED := -shared -static-libgcc -static-libstdc++ +endif + +ifeq ($(TILED_RENDERING), 1) +VBA_DEFINES += -DTILED_RENDERING +endif + +VBA_DIR := ../ + +VBA_SRC_DIRS := $(VBA_DIR)/gba $(VBA_DIR)/apu + +VBA_CXXSRCS := $(foreach dir,$(VBA_SRC_DIRS),$(wildcard $(dir)/*.cpp)) +VBA_CXXOBJ := $(VBA_CXXSRCS:.cpp=.o) ../common/Patch.o +VBA_CSRCS := $(foreach dir,$(VBA_SRC_DIRS),$(wildcard $(dir)/*.c)) +VBA_COBJ := $(VBA_CSRCS:.c=.o) +UTIL_SOURCES := $(wildcard ../common/utils/zlib/*.c) +UTIL_OBJS := $(UTIL_SOURCES:.c=.o) + +OBJS := $(VBA_COBJ) $(VBA_CXXOBJ) $(UTIL_OBJS) libretro.o UtilRetro.o SoundRetro.o scrc32.o + +VBA_DEFINES += -D__LIBRETRO__ -DFINAL_VERSION -DC_CORE -DUSE_GBA_ONLY -DNO_LINK +VBA_DEFINES += -DFRONTEND_SUPPORTS_RGB565 + +CFLAGS += -O3 -std=gnu99 $(fpic) $(VBA_DEFINES) -I../common/utils/zlib +CXXFLAGS += -O3 $(fpic) $(VBA_DEFINES) -I../common/utils/zlib + + +INCDIRS := -I$(VBA_DIR) +LIBS := + +$(TARGET): $(OBJS) + $(CXX) -o $@ $(SHARED) $(OBJS) $(LDFLAGS) $(LIBS) + +%.o: %.cpp + $(CXX) -c -o $@ $< $(CXXFLAGS) $(INCDIRS) + +%.o: %.c + $(CC) -c -o $@ $< $(CFLAGS) $(INCDIRS) + +clean: + rm -f $(OBJS) + rm -f $(TARGET) + +.PHONY: clean + + diff --git a/src/libretro/SoundRetro.cpp b/src/libretro/SoundRetro.cpp new file mode 100644 index 0000000..c604b91 --- /dev/null +++ b/src/libretro/SoundRetro.cpp @@ -0,0 +1,58 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 2008 VBA-M development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "libretro.h" +#include "SoundRetro.h" +unsigned g_audio_frames; +extern retro_audio_sample_batch_t audio_batch_cb; + +SoundRetro::SoundRetro() +{ +} + +void SoundRetro::write(u16 * finalWave, int length) +{ + const int16_t* wave = (const int16_t*)finalWave; + int frames = length >> 1; + audio_batch_cb(wave, frames); + + g_audio_frames += frames; +} + + +bool SoundRetro::init(long sampleRate) +{ + g_audio_frames = 0; + + return true; +} + +SoundRetro::~SoundRetro() +{ +} + +void SoundRetro::pause() +{ +} + +void SoundRetro::resume() +{ +} + +void SoundRetro::reset() +{ +} diff --git a/src/libretro/SoundRetro.h b/src/libretro/SoundRetro.h new file mode 100644 index 0000000..3aa3774 --- /dev/null +++ b/src/libretro/SoundRetro.h @@ -0,0 +1,36 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 2008 VBA-M development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef __VBA_SOUND_RETRO_H__ +#define __VBA_SOUND_RETRO_H__ + +#include "../common/SoundDriver.h" + +class SoundRetro: public SoundDriver +{ +public: + SoundRetro(); + virtual ~SoundRetro(); + + virtual bool init(long sampleRate); + virtual void pause(); + virtual void reset(); + virtual void resume(); + virtual void write(u16 * finalWave, int length); +}; + +#endif // __VBA_SOUND_RETRO_H__ diff --git a/src/libretro/UtilRetro.cpp b/src/libretro/UtilRetro.cpp new file mode 100644 index 0000000..5b8f5dc --- /dev/null +++ b/src/libretro/UtilRetro.cpp @@ -0,0 +1,308 @@ +#include +#include +#include +#include + +#include "System.h" +#include "NLS.h" +#include "Util.h" +#include "gba/Flash.h" +#include "gba/GBA.h" +#include "gba/Globals.h" +#include "gba/RTC.h" +#include "common/Port.h" + +#include "gba/gbafilter.h" +#include "gb/gbGlobals.h" + +#ifndef _MSC_VER +#define _stricmp strcasecmp +#endif // ! _MSC_VER + +extern int systemColorDepth; +extern int systemRedShift; +extern int systemGreenShift; +extern int systemBlueShift; + +extern uint16_t systemColorMap16[0x10000]; +extern uint32_t systemColorMap32[0x10000]; + +bool utilWritePNGFile(const char *fileName, int w, int h, uint8_t *pix) +{ + return false; +} + +void utilPutDword(u8 *p, u32 value) +{ + *p++ = value & 255; + *p++ = (value >> 8) & 255; + *p++ = (value >> 16) & 255; + *p = (value >> 24) & 255; +} + +void utilPutWord(uint8_t *p, uint16_t value) +{ + *p++ = value & 255; + *p = (value >> 8) & 255; +} + +bool utilWriteBMPFile(const char *fileName, int w, int h, uint8_t *pix) +{ + return false; +} + +extern bool cpuIsMultiBoot; + +bool utilIsGBAImage(const char * file) +{ + cpuIsMultiBoot = false; + if(strlen(file) > 4) { + const char * p = strrchr(file,'.'); + + if(p != NULL) { + if((_stricmp(p, ".agb") == 0) || + (_stricmp(p, ".gba") == 0) || + (_stricmp(p, ".bin") == 0) || + (_stricmp(p, ".elf") == 0)) + return true; + if(_stricmp(p, ".mb") == 0) { + cpuIsMultiBoot = true; + return true; + } + } + } + + return false; +} + +bool utilIsGBImage(const char * file) +{ + if(strlen(file) > 4) { + const char * p = strrchr(file,'.'); + + if(p != NULL) { + if((_stricmp(p, ".dmg") == 0) || + (_stricmp(p, ".gb") == 0) || + (_stricmp(p, ".gbc") == 0) || + (_stricmp(p, ".cgb") == 0) || + (_stricmp(p, ".sgb") == 0)) + return true; + } + } + + return false; +} + +bool utilIsGzipFile(const char *file) +{ + if(strlen(file) > 3) { + const char * p = strrchr(file,'.'); + + if(p != NULL) { + if(_stricmp(p, ".gz") == 0) + return true; + if(_stricmp(p, ".z") == 0) + return true; + } + } + + return false; +} + +// strip .gz or .z off end +void utilStripDoubleExtension(const char *file, char *buffer) +{ + if(buffer != file) // allows conversion in place + strcpy(buffer, file); + + if(utilIsGzipFile(file)) { + char *p = strrchr(buffer, '.'); + + if(p) + *p = 0; + } +} + +static bool utilIsImage(const char *file) +{ + return utilIsGBAImage(file) || utilIsGBImage(file); +} + +IMAGE_TYPE utilFindType(const char *file) +{ + char buffer [2048]; + if ( !utilIsImage( file ) ) // TODO: utilIsArchive() instead? + { + return IMAGE_UNKNOWN; + } + return utilIsGBAImage(file) ? IMAGE_GBA : IMAGE_GB; +} + +static int utilGetSize(int size) +{ + int res = 1; + while(res < size) + res <<= 1; + return res; +} + +uint8_t *utilLoad(const char *file, bool (*accept)(const char *), uint8_t *data, int &size) +{ + FILE *fp = NULL; + char *buf = NULL; + + fp = fopen(file,"rb"); + fseek(fp, 0, SEEK_END); //go to end + size = ftell(fp); // get position at end (length) + rewind(fp); + + uint8_t *image = data; + if(image == NULL) + { + //allocate buffer memory if none was passed to the function + image = (uint8_t *)malloc(utilGetSize(size)); + if(image == NULL) + { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "data"); + return NULL; + } + } + + fread(image, 1, size, fp); // read into buffer + fclose(fp); + return image; +} + +void utilGBAFindSave(const uint8_t *data, const int size) +{ + uint32_t *p = (uint32_t *)data; + uint32_t *end = (uint32_t *)(data + size); + int saveType = 0; + int flashSize = 0x10000; + bool rtcFound = false; + + while(p < end) { + uint32_t d = READ32LE(p); + + if(d == 0x52504545) { + if(memcmp(p, "EEPROM_", 7) == 0) { + if(saveType == 0) + saveType = 3; + } + } else if (d == 0x4D415253) { + if(memcmp(p, "SRAM_", 5) == 0) { + if(saveType == 0) + saveType = 1; + } + } else if (d == 0x53414C46) { + if(memcmp(p, "FLASH1M_", 8) == 0) { + if(saveType == 0) { + saveType = 2; + flashSize = 0x20000; + } + } else if(memcmp(p, "FLASH", 5) == 0) { + if(saveType == 0) { + saveType = 2; + flashSize = 0x10000; + } + } + } else if (d == 0x52494953) { + if(memcmp(p, "SIIRTC_V", 8) == 0) + rtcFound = true; + } + p++; + } + // if no matches found, then set it to NONE + if(saveType == 0) { + saveType = 5; + } + + rtcEnable(rtcFound); + cpuSaveType = saveType; + flashSetSize(flashSize); +} + +void utilUpdateSystemColorMaps(bool lcd) +{ + switch(systemColorDepth) { + case 16: + { + for(int i = 0; i < 0x10000; i++) { + systemColorMap16[i] = ((i & 0x1f) << systemRedShift) | + (((i & 0x3e0) >> 5) << systemGreenShift) | + (((i & 0x7c00) >> 10) << systemBlueShift); + } + if (lcd) gbafilter_pal(systemColorMap16, 0x10000); + } + break; + case 24: + case 32: + { + for(int i = 0; i < 0x10000; i++) { + systemColorMap32[i] = ((i & 0x1f) << systemRedShift) | + (((i & 0x3e0) >> 5) << systemGreenShift) | + (((i & 0x7c00) >> 10) << systemBlueShift); + } + if (lcd) gbafilter_pal32(systemColorMap32, 0x10000); + } + break; + } +} + +// Check for existence of file. +bool utilFileExists( const char *filename ) +{ + FILE *f = fopen( filename, "r" ); + if( f == NULL ) { + return false; + } else { + fclose( f ); + return true; + } +} + +// Not endian safe, but VBA itself doesn't seem to care, so hey <_< +void utilWriteIntMem(uint8_t *& data, int val) +{ + memcpy(data, &val, sizeof(int)); + data += sizeof(int); +} + +void utilWriteMem(uint8_t *& data, const void *in_data, unsigned size) +{ + memcpy(data, in_data, size); + data += size; +} + +void utilWriteDataMem(uint8_t *& data, variable_desc *desc) +{ + while (desc->address) + { + utilWriteMem(data, desc->address, desc->size); + desc++; + } +} + +int utilReadIntMem(const uint8_t *& data) +{ + int res; + memcpy(&res, data, sizeof(int)); + data += sizeof(int); + return res; +} + +void utilReadMem(void *buf, const uint8_t *& data, unsigned size) +{ + memcpy(buf, data, size); + data += size; +} + +void utilReadDataMem(const uint8_t *& data, variable_desc *desc) +{ + while (desc->address) + { + utilReadMem(desc->address, data, desc->size); + desc++; + } +} diff --git a/src/libretro/gbaconv/gbaconv.c b/src/libretro/gbaconv/gbaconv.c new file mode 100644 index 0000000..ae73a6c --- /dev/null +++ b/src/libretro/gbaconv/gbaconv.c @@ -0,0 +1,216 @@ +#include +#include +#ifndef __CELLOS_LV2__ +#include +#endif +#include +#include +#include +#include + +enum save_type +{ + EEPROM_512B, + EEPROM_8K, + FLASH_64K, + FLASH_128K, + SAVE_UNKNOWN +}; + +static const char *save_type_to_string(enum save_type type) +{ + switch (type) + { + case EEPROM_512B: + return "EEPROM 4kbit"; + case EEPROM_8K: + return "EEPROM 64kbit"; + case FLASH_64K: + return "FLASH 512kbit"; + case FLASH_128K: + return "FLASH 1MBit"; + + default: + return "Unknown type"; + } +} + +static bool scan_section(const uint8_t *data, unsigned size) +{ + for (unsigned i = 0; i < size; i++) + { + if (data[i] != 0xff) + return true; + } + + return false; +} + +static enum save_type detect_save_type(const uint8_t *data, unsigned size) +{ + if (size == 512) + return EEPROM_512B; + if (size == 0x2000) + return EEPROM_8K; + if (size == 0x10000) + return FLASH_64K; + if (size == 0x20000) + return FLASH_128K; + + if (size == (0x20000 + 0x2000)) + { + if (scan_section(data, 0x10000) && !scan_section(data + 0x10000, 0x10000)) + return FLASH_64K; + if (scan_section(data, 0x20000)) + return FLASH_128K; + + if (scan_section(data + 0x20000, 512) && !scan_section(data + 0x20000 + 512, 0x20000 - 512)) + return EEPROM_512B; + if (scan_section(data + 0x20000, 0x2000)) + return EEPROM_8K; + } + + return SAVE_UNKNOWN; +} + +static void dump_srm(FILE *file, const uint8_t *data, enum save_type type) +{ + void *buf = malloc(0x20000 + 0x2000); + memset(buf, 0xff, 0x20000 + 0x2000); + + switch (type) + { + case EEPROM_512B: + fwrite(buf, 1, 0x20000, file); + fwrite(data, 1, 512, file); + fwrite(buf, 1, 0x2000 - 512, file); + break; + + case EEPROM_8K: + fwrite(buf, 1, 0x20000, file); + fwrite(data, 1, 0x2000, file); + break; + + case FLASH_64K: + fwrite(data, 1, 0x10000, file); + fwrite(buf, 1, 0x20000 + 0x2000 - 0x10000, file); + break; + + case FLASH_128K: + fwrite(data, 1, 0x20000, file); + fwrite(buf, 1, 0x2000, file); + break; + + default: + break; + } + + free(buf); +} + +static void dump_sav(FILE *file, const uint8_t *data, enum save_type type) +{ + switch (type) + { + case EEPROM_512B: + fwrite(data + 0x20000, 1, 512, file); + break; + + case EEPROM_8K: + fwrite(data + 0x20000, 1, 0x2000, file); + break; + + case FLASH_64K: + fwrite(data, 1, 0x10000, file); + break; + + case FLASH_128K: + fwrite(data, 1, 0x20000, file); + break; + + default: + break; + } +} + +// One shot cowboy code :) + +int main(int argc, char *argv[]) +{ + if (argc != 2) + { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + FILE *file = fopen(argv[1], "rb"); + if (!file) + { + fprintf(stderr, "Failed to open file \"%s\"\n", argv[1]); + goto error; + } + + fseek(file, 0, SEEK_END); + long len = ftell(file); + rewind(file); + + uint8_t *buffer = malloc(len); + if (!buffer) + { + fprintf(stderr, "Failed to allocate memory!\n"); + goto error; + } + fread(buffer, 1, len, file); + fclose(file); + file = NULL; + + char *out_path = strdup(argv[1]); + char *split = strrchr(out_path, '.'); + const char *ext = NULL; + + if (split) + { + *split = '\0'; + ext = split + 1; + + if (strcasecmp(ext, "srm") == 0) + strcat(out_path, ".sav"); + else if (strlen(ext) >= 3) + strcat(out_path, ".srm"); + else + ext = NULL; + } + + if (!ext) + { + fprintf(stderr, "Cannot detect extension!\n"); + goto error; + } + + enum save_type type = detect_save_type(buffer, len); + printf("Detected save type: %s\n", save_type_to_string(type)); + + if (type == SAVE_UNKNOWN) + { + fprintf(stderr, "Cannot infer save type ...\n"); + goto error; + } + + file = fopen(out_path, "wb"); + if (!file) + goto error; + + if (len == (0x20000 + 0x2000)) + dump_sav(file, buffer, type); + else + dump_srm(file, buffer, type); + fclose(file); + + return 0; + +error: + if (file) + fclose(file); + return 1; +} + diff --git a/src/libretro/jni/Android.mk b/src/libretro/jni/Android.mk new file mode 100644 index 0000000..b4dc961 --- /dev/null +++ b/src/libretro/jni/Android.mk @@ -0,0 +1,61 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +ifeq ($(TARGET_ARCH),arm) +LOCAL_CFLAGS += -DANDROID_ARM +LOCAL_ARM_MODE := arm +endif + +ifeq ($(TARGET_ARCH),x86) +LOCAL_CFLAGS += -DANDROID_X86 +endif + +ifeq ($(TARGET_ARCH),mips) +LOCAL_CFLAGS += -DANDROID_MIPS +endif + +VBADIR = ../../ + +LOCAL_MODULE := libretro +LOCAL_SRC_FILES = $(VBADIR)/gba/agbprint.cpp \ + $(VBADIR)/gba/armdis.cpp \ + $(VBADIR)/gba/bios.cpp \ + $(VBADIR)/gba/Cheats.cpp \ + $(VBADIR)/gba/CheatSearch.cpp \ + $(VBADIR)/gba/EEprom.cpp \ + $(VBADIR)/gba/elf.cpp \ + $(VBADIR)/gba/Flash.cpp \ + $(VBADIR)/gba/GBA-arm.cpp \ + $(VBADIR)/gba/GBA.cpp \ + $(VBADIR)/gba/gbafilter.cpp \ + $(VBADIR)/gba/GBAGfx.cpp \ + $(VBADIR)/gba/GBALink.cpp \ + $(VBADIR)/gba/GBASockClient.cpp \ + $(VBADIR)/gba/GBA-thumb.cpp \ + $(VBADIR)/gba/Globals.cpp \ + $(VBADIR)/gba/Mode0.cpp \ + $(VBADIR)/gba/Mode1.cpp \ + $(VBADIR)/gba/Mode2.cpp \ + $(VBADIR)/gba/Mode3.cpp \ + $(VBADIR)/gba/Mode4.cpp \ + $(VBADIR)/gba/Mode5.cpp \ + $(VBADIR)/gba/remote.cpp \ + $(VBADIR)/gba/RTC.cpp \ + $(VBADIR)/gba/Sound.cpp \ + $(VBADIR)/gba/Sram.cpp \ + $(VBADIR)/apu/Blip_Buffer.cpp \ + $(VBADIR)/apu/Effects_Buffer.cpp \ + $(VBADIR)/apu/Gb_Apu.cpp \ + $(VBADIR)/apu/Gb_Apu_State.cpp \ + $(VBADIR)/apu/Gb_Oscs.cpp \ + $(VBADIR)/apu/Multi_Buffer.cpp \ + $(VBADIR)/libretro/libretro.cpp \ + $(VBADIR)/libretro/UtilRetro.cpp \ + $(VBADIR)/libretro/SoundRetro.cpp \ + $(VBADIR)/libretro/scrc32.cpp + +LOCAL_CFLAGS = -O3 -DINLINE=inline -DHAVE_STDINT_H -DHAVE_INTTYPES_H -DLSB_FIRST -D__LIBRETRO__ -DFINAL_VERSION -DC_CORE -DUSE_GBA_ONLY -DNO_LINK -DFRONTEND_SUPPORTS_RGB565 -DTILED_RENDERING +LOCAL_C_INCLUDES = $(VBADIR) + +include $(BUILD_SHARED_LIBRARY) diff --git a/src/libretro/jni/Application.mk b/src/libretro/jni/Application.mk new file mode 100644 index 0000000..a252a72 --- /dev/null +++ b/src/libretro/jni/Application.mk @@ -0,0 +1 @@ +APP_ABI := all diff --git a/src/libretro/libretro.cpp b/src/libretro/libretro.cpp new file mode 100644 index 0000000..4e8e03c --- /dev/null +++ b/src/libretro/libretro.cpp @@ -0,0 +1,627 @@ +#include +#include +#include + +#include "libretro.h" +#include "SoundRetro.h" + +#include "../Util.h" +#include "../System.h" +#include "../common/Port.h" +#include "../common/Types.h" +#include "../gba/RTC.h" +#include "../gba/GBAGfx.h" +#include "../gba/bios.h" +#include "../gba/Flash.h" +#include "../gba/EEprom.h" +#include "../gba/Sound.h" +#include "../apu/Blip_Buffer.h" +#include "../apu/Gb_Oscs.h" +#include "../apu/Gb_Apu.h" +#include "../gba/Globals.h" + +static retro_video_refresh_t video_cb; +static retro_input_poll_t poll_cb; +static retro_input_state_t input_cb; +retro_audio_sample_batch_t audio_batch_cb; +static retro_environment_t environ_cb; + +bool enableRtc; +extern uint64_t joy; +static bool can_dupe; +unsigned device_type = 0; +int emulating = 0; + +uint8_t libretro_save_buf[0x20000 + 0x2000]; /* Workaround for broken-by-design GBA save semantics. */ + +static unsigned libretro_save_size = sizeof(libretro_save_buf); + +int RGB_LOW_BITS_MASK = 0; + +u16 systemColorMap16[0x10000]; +u32 systemColorMap32[0x10000]; +u16 systemGbPalette[24]; +int systemRedShift = 0; +int systemBlueShift = 0; +int systemGreenShift = 0; +int systemColorDepth = 32; +int systemDebug = 0; +int systemVerbose = 0; +int systemFrameSkip = 0; +int systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; +int systemSpeed = 0; + +u64 startTime = 0; +u32 renderedFrames = 0; + +void (*dbgOutput)(const char *s, u32 addr); +void (*dbgSignal)(int sig, int number); + +void *retro_get_memory_data(unsigned id) +{ + if (id != RETRO_MEMORY_SAVE_RAM) + return 0; + + return libretro_save_buf; +} + +size_t retro_get_memory_size(unsigned id) +{ + if (id != RETRO_MEMORY_SAVE_RAM) + return 0; + + return libretro_save_size; +} + +static bool scan_area(const uint8_t *data, unsigned size) +{ + for (unsigned i = 0; i < size; i++) + if (data[i] != 0xff) + return true; + + return false; +} + +static void adjust_save_ram() +{ + if (scan_area(libretro_save_buf, 512) && + !scan_area(libretro_save_buf + 512, sizeof(libretro_save_buf) - 512)) + { + libretro_save_size = 512; + fprintf(stderr, "Detecting EEprom 8kbit\n"); + } + else if (scan_area(libretro_save_buf, 0x2000) && + !scan_area(libretro_save_buf + 0x2000, sizeof(libretro_save_buf) - 0x2000)) + { + libretro_save_size = 0x2000; + fprintf(stderr, "Detecting EEprom 64kbit\n"); + } + + else if (scan_area(libretro_save_buf, 0x10000) && + !scan_area(libretro_save_buf + 0x10000, sizeof(libretro_save_buf) - 0x10000)) + { + libretro_save_size = 0x10000; + fprintf(stderr, "Detecting Flash 512kbit\n"); + } + else if (scan_area(libretro_save_buf, 0x20000) && + !scan_area(libretro_save_buf + 0x20000, sizeof(libretro_save_buf) - 0x20000)) + { + libretro_save_size = 0x20000; + fprintf(stderr, "Detecting Flash 1Mbit\n"); + } + else + fprintf(stderr, "Did not detect any particular SRAM type.\n"); + + if (libretro_save_size == 512 || libretro_save_size == 0x2000) + eepromData = libretro_save_buf; + else if (libretro_save_size == 0x10000 || libretro_save_size == 0x20000) + flashSaveMemory = libretro_save_buf; +} + + +unsigned retro_api_version(void) +{ + return RETRO_API_VERSION; +} + +void retro_set_video_refresh(retro_video_refresh_t cb) +{ + video_cb = cb; +} + +void retro_set_audio_sample(retro_audio_sample_t cb) +{ } + +void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) +{ + audio_batch_cb = cb; +} + +void retro_set_input_poll(retro_input_poll_t cb) +{ + poll_cb = cb; +} + +void retro_set_input_state(retro_input_state_t cb) +{ + input_cb = cb; +} + +void retro_set_controller_port_device(unsigned port, unsigned device) +{ } + +void retro_set_environment(retro_environment_t cb) +{ + environ_cb = cb; + + struct retro_variable variables[] = { + { "vbam-next-gamepad", + "Button layout; original|reversed" }, + { NULL, NULL }, + }; + + cb(RETRO_ENVIRONMENT_SET_VARIABLES, variables); +} + +void retro_get_system_info(struct retro_system_info *info) +{ + info->need_fullpath = true; + info->valid_extensions = "gba"; + info->library_version = "v1.0.2"; + info->library_name = "VBA-M"; + info->block_extract = false; +} + +void retro_get_system_av_info(struct retro_system_av_info *info) +{ + info->geometry.base_width = 240; + info->geometry.base_height = 160; + info->geometry.max_width = 240; + info->geometry.max_height = 160; + info->timing.fps = 16777216.0 / 280896.0; + info->timing.sample_rate = 32000.0; +} + +void retro_init(void) +{ + memset(libretro_save_buf, 0xff, sizeof(libretro_save_buf)); + adjust_save_ram(); + environ_cb(RETRO_ENVIRONMENT_GET_CAN_DUPE, &can_dupe); + +#ifdef FRONTEND_SUPPORTS_RGB565 + enum retro_pixel_format rgb565 = RETRO_PIXEL_FORMAT_RGB565; + if(environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &rgb565)) + fprintf(stderr, "Frontend supports RGB565 - will use that instead of XRGB1555.\n"); +#else + enum retro_pixel_format rgb8888 = RETRO_PIXEL_FORMAT_XRGB8888; + if(environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &rgb8888)) + fprintf(stderr, "Frontend supports XRGB8888 - will use that instead of XRGB1555.\n"); +#endif +} + +static unsigned serialize_size = 0; + +typedef struct { + char romtitle[256]; + char romid[5]; + int flashSize; + int saveType; + int rtcEnabled; + int mirroringEnabled; + int useBios; +} ini_t; + +static const ini_t gbaover[256] = { + //romtitle, romid flash save rtc mirror bios + {"2 Games in 1 - Dragon Ball Z - The Legacy of Goku I & II (USA)", "BLFE", 0, 1, 0, 0, 0}, + {"2 Games in 1 - Dragon Ball Z - Buu's Fury + Dragon Ball GT - Transformation (USA)", "BUFE", 0, 1, 0, 0, 0}, + {"Boktai - The Sun Is in Your Hand (Europe)(En,Fr,De,Es,It)", "U3IP", 0, 0, 1, 0, 0}, + {"Boktai - The Sun Is in Your Hand (USA)", "U3IE", 0, 0, 1, 0, 0}, + {"Boktai 2 - Solar Boy Django (USA)", "U32E", 0, 0, 1, 0, 0}, + {"Boktai 2 - Solar Boy Django (Europe)(En,Fr,De,Es,It)", "U32P", 0, 0, 1, 0, 0}, + {"Bokura no Taiyou - Taiyou Action RPG (Japan)", "U3IJ", 0, 0, 1, 0, 0}, + {"Card e-Reader+ (Japan)", "PSAJ", 131072, 0, 0, 0, 0}, + {"Classic NES Series - Bomberman (USA, Europe)", "FBME", 0, 1, 0, 1, 0}, + {"Classic NES Series - Castlevania (USA, Europe)", "FADE", 0, 1, 0, 1, 0}, + {"Classic NES Series - Donkey Kong (USA, Europe)", "FDKE", 0, 1, 0, 1, 0}, + {"Classic NES Series - Dr. Mario (USA, Europe)", "FDME", 0, 1, 0, 1, 0}, + {"Classic NES Series - Excitebike (USA, Europe)", "FEBE", 0, 1, 0, 1, 0}, + {"Classic NES Series - Legend of Zelda (USA, Europe)", "FZLE", 0, 1, 0, 1, 0}, + {"Classic NES Series - Ice Climber (USA, Europe)", "FICE", 0, 1, 0, 1, 0}, + {"Classic NES Series - Metroid (USA, Europe)", "FMRE", 0, 1, 0, 1, 0}, + {"Classic NES Series - Pac-Man (USA, Europe)", "FP7E", 0, 1, 0, 1, 0}, + {"Classic NES Series - Super Mario Bros. (USA, Europe)", "FSME", 0, 1, 0, 1, 0}, + {"Classic NES Series - Xevious (USA, Europe)", "FXVE", 0, 1, 0, 1, 0}, + {"Classic NES Series - Zelda II - The Adventure of Link (USA, Europe)", "FLBE", 0, 1, 0, 1, 0}, + {"Digi Communication 2 - Datou! Black Gemagema Dan (Japan)", "BDKJ", 0, 1, 0, 0, 0}, + {"e-Reader (USA)", "PSAE", 131072, 0, 0, 0, 0}, + {"Dragon Ball GT - Transformation (USA)", "BT4E", 0, 1, 0, 0, 0}, + {"Dragon Ball Z - Buu's Fury (USA)", "BG3E", 0, 1, 0, 0, 0}, + {"Dragon Ball Z - Taiketsu (Europe)(En,Fr,De,Es,It)", "BDBP", 0, 1, 0, 0, 0}, + {"Dragon Ball Z - Taiketsu (USA)", "BDBE", 0, 1, 0, 0, 0}, + {"Dragon Ball Z - The Legacy of Goku II International (Japan)", "ALFJ", 0, 1, 0, 0, 0}, + {"Dragon Ball Z - The Legacy of Goku II (Europe)(En,Fr,De,Es,It)", "ALFP", 0, 1, 0, 0, 0}, + {"Dragon Ball Z - The Legacy of Goku II (USA)", "ALFE", 0, 1, 0, 0, 0}, + {"Dragon Ball Z - The Legacy Of Goku (Europe)(En,Fr,De,Es,It)", "ALGP", 0, 1, 0, 0, 0}, + {"Dragon Ball Z - The Legacy of Goku (USA)", "ALGE", 131072, 1, 0, 0, 0}, + {"F-Zero - Climax (Japan)", "BFTJ", 131072, 0, 0, 0, 0}, + {"Famicom Mini Vol. 01 - Super Mario Bros. (Japan)", "FMBJ", 0, 1, 0, 1, 0}, + {"Famicom Mini Vol. 12 - Clu Clu Land (Japan)", "FCLJ", 0, 1, 0, 1, 0}, + {"Famicom Mini Vol. 13 - Balloon Fight (Japan)", "FBFJ", 0, 1, 0, 1, 0}, + {"Famicom Mini Vol. 14 - Wrecking Crew (Japan)", "FWCJ", 0, 1, 0, 1, 0}, + {"Famicom Mini Vol. 15 - Dr. Mario (Japan)", "FDMJ", 0, 1, 0, 1, 0}, + {"Famicom Mini Vol. 16 - Dig Dug (Japan)", "FTBJ", 0, 1, 0, 1, 0}, + {"Famicom Mini Vol. 17 - Takahashi Meijin no Boukenjima (Japan)", "FTBJ", 0, 1, 0, 1, 0}, + {"Famicom Mini Vol. 18 - Makaimura (Japan)", "FMKJ", 0, 1, 0, 1, 0}, + {"Famicom Mini Vol. 19 - Twin Bee (Japan)", "FTWJ", 0, 1, 0, 1, 0}, + {"Famicom Mini Vol. 20 - Ganbare Goemon! Karakuri Douchuu (Japan)", "FGGJ", 0, 1, 0, 1, 0}, + {"Famicom Mini Vol. 21 - Super Mario Bros. 2 (Japan)", "FM2J", 0, 1, 0, 1, 0}, + {"Famicom Mini Vol. 22 - Nazo no Murasame Jou (Japan)", "FNMJ", 0, 1, 0, 1, 0}, + {"Famicom Mini Vol. 23 - Metroid (Japan)", "FMRJ", 0, 1, 0, 1, 0}, + {"Famicom Mini Vol. 24 - Hikari Shinwa - Palthena no Kagami (Japan)", "FPTJ", 0, 1, 0, 1, 0}, + {"Famicom Mini Vol. 25 - The Legend of Zelda 2 - Link no Bouken (Japan)","FLBJ",0, 1, 0, 1, 0}, + {"Famicom Mini Vol. 26 - Famicom Mukashi Banashi - Shin Onigashima - Zen Kou Hen (Japan)","FFMJ",0,1,0, 1, 0}, + {"Famicom Mini Vol. 27 - Famicom Tantei Club - Kieta Koukeisha - Zen Kou Hen (Japan)","FTKJ",0,1,0, 1, 0}, + {"Famicom Mini Vol. 28 - Famicom Tantei Club Part II - Ushiro ni Tatsu Shoujo - Zen Kou Hen (Japan)","FTUJ",0,1,0,1,0}, + {"Famicom Mini Vol. 29 - Akumajou Dracula (Japan)", "FADJ", 0, 1, 0, 1, 0}, + {"Famicom Mini Vol. 30 - SD Gundam World - Gachapon Senshi Scramble Wars (Japan)","FSDJ",0,1, 0, 1, 0}, + {"Game Boy Wars Advance 1+2 (Japan)", "BGWJ", 131072, 0, 0, 0, 0}, + {"Golden Sun - The Lost Age (USA)", "AGFE", 65536, 0, 0, 1, 0}, + {"Golden Sun (USA)", "AGSE", 65536, 0, 0, 1, 0}, + {"Koro Koro Puzzle - Happy Panechu! (Japan)", "KHPJ", 0, 4, 0, 0, 0}, + {"Mario vs. Donkey Kong (Europe)", "BM5P", 0, 3, 0, 0, 0}, + {"Pocket Monsters - Emerald (Japan)", "BPEJ", 131072, 0, 1, 0, 0}, + {"Pocket Monsters - Fire Red (Japan)", "BPRJ", 131072, 0, 0, 0, 0}, + {"Pocket Monsters - Leaf Green (Japan)", "BPGJ", 131072, 0, 0, 0, 0}, + {"Pocket Monsters - Ruby (Japan)", "AXVJ", 131072, 0, 1, 0, 0}, + {"Pocket Monsters - Sapphire (Japan)", "AXPJ", 131072, 0, 1, 0, 0}, + {"Pokemon Mystery Dungeon - Red Rescue Team (USA, Australia)", "B24E", 131072, 0, 0, 0, 0}, + {"Pokemon Mystery Dungeon - Red Rescue Team (En,Fr,De,Es,It)", "B24P", 131072, 0, 0, 0, 0}, + {"Pokemon - Blattgruene Edition (Germany)", "BPGD", 131072, 0, 0, 0, 0}, + {"Pokemon - Edicion Rubi (Spain)", "AXVS", 131072, 0, 1, 0, 0}, + {"Pokemon - Edicion Esmeralda (Spain)", "BPES", 131072, 0, 1, 0, 0}, + {"Pokemon - Edicion Rojo Fuego (Spain)", "BPRS", 131072, 1, 0, 0, 0}, + {"Pokemon - Edicion Verde Hoja (Spain)", "BPGS", 131072, 1, 0, 0, 0}, + {"Pokemon - Eidicion Zafiro (Spain)", "AXPS", 131072, 0, 1, 0, 0}, + {"Pokemon - Emerald Version (USA, Europe)", "BPEE", 131072, 0, 1, 0, 0}, + {"Pokemon - Feuerrote Edition (Germany)", "BPRD", 131072, 0, 0, 0, 0}, + {"Pokemon - Fire Red Version (USA, Europe)", "BPRE", 131072, 0, 0, 0, 0}, + {"Pokemon - Leaf Green Version (USA, Europe)", "BPGE", 131072, 0, 0, 0, 0}, + {"Pokemon - Rubin Edition (Germany)", "AXVD", 131072, 0, 1, 0, 0}, + {"Pokemon - Ruby Version (USA, Europe)", "AXVE", 131072, 0, 1, 0, 0}, + {"Pokemon - Sapphire Version (USA, Europe)", "AXPE", 131072, 0, 1, 0, 0}, + {"Pokemon - Saphir Edition (Germany)", "AXPD", 131072, 0, 1, 0, 0}, + {"Pokemon - Smaragd Edition (Germany)", "BPED", 131072, 0, 1, 0, 0}, + {"Pokemon - Version Emeraude (France)", "BPEF", 131072, 0, 1, 0, 0}, + {"Pokemon - Version Rouge Feu (France)", "BPRF", 131072, 0, 0, 0, 0}, + {"Pokemon - Version Rubis (France)", "AXVF", 131072, 0, 1, 0, 0}, + {"Pokemon - Version Saphir (France)", "AXPF", 131072, 0, 1, 0, 0}, + {"Pokemon - Version Vert Feuille (France)", "BPGF", 131072, 0, 0, 0, 0}, + {"Pokemon - Versione Rubino (Italy)", "AXVI", 131072, 0, 1, 0, 0}, + {"Pokemon - Versione Rosso Fuoco (Italy)", "BPRI", 131072, 0, 0, 0, 0}, + {"Pokemon - Versione Smeraldo (Italy)", "BPEI", 131072, 0, 1, 0, 0}, + {"Pokemon - Versione Verde Foglia (Italy)", "BPGI", 131072, 0, 0, 0, 0}, + {"Pokemon - Versione Zaffiro (Italy)", "AXPI", 131072, 0, 1, 0, 0}, + {"Rockman EXE 4.5 - Real Operation (Japan)", "BR4J", 0, 0, 1, 0, 0}, + {"Rocky (Europe)(En,Fr,De,Es,It)", "AROP", 0, 1, 0, 0, 0}, + {"Sennen Kazoku (Japan)", "BKAJ", 131072, 0, 1, 0, 0}, + {"Shin Bokura no Taiyou - Gyakushuu no Sabata (Japan)", "U33J", 0, 1, 1, 0, 0}, + {"Super Mario Advance 4 (Japan)", "AX4J", 131072, 0, 0, 0, 0}, + {"Super Mario Advance 4 - Super Mario Bros. 3 (Europe)(En,Fr,De,Es,It)","AX4P", 131072, 0, 0, 0, 0}, + {"Super Mario Advance 4 - Super Mario Bros 3 - Super Mario Advance 4 v1.1 (USA)","AX4E",131072,0,0,0,0}, + {"Top Gun - Combat Zones (USA)(En,Fr,De,Es,It)", "A2YE", 0, 5, 0, 0, 0}, + {"Yoshi no Banyuuinryoku (Japan)", "KYGJ", 0, 4, 0, 0, 0}, + {"Yoshi - Topsy-Turvy (USA)", "KYGE", 0, 1, 0, 0, 0}, + {"Yu-Gi-Oh! GX - Duel Academy (USA)", "BYGE", 0, 2, 0, 0, 1}, + {"Yu-Gi-Oh! - Ultimate Masters - 2006 (Europe)(En,Jp,Fr,De,Es,It)", "BY6P", 0, 2, 0, 0, 0}, + {"Zoku Bokura no Taiyou - Taiyou Shounen Django (Japan)", "U32J", 0, 0, 1, 0, 0} +}; + +static void load_image_preferences (void) +{ + char buffer[5]; + buffer[0] = rom[0xac]; + buffer[1] = rom[0xad]; + buffer[2] = rom[0xae]; + buffer[3] = rom[0xaf]; + buffer[4] = 0; + fprintf(stderr, "GameID in ROM is: %s\n", buffer); + + bool found = false; + int found_no = 0; + + for(int i = 0; i < 256; i++) + { + if(!strcmp(gbaover[i].romid, buffer)) + { + found = true; + found_no = i; + break; + } + } + + if(found) + { + fprintf(stderr, "Found ROM in vba-over list.\n"); + + enableRtc = gbaover[found_no].rtcEnabled; + + if(gbaover[found_no].flashSize != 0) + flashSize = gbaover[found_no].flashSize; + else + flashSize = 65536; + + cpuSaveType = gbaover[found_no].saveType; + + mirroringEnable = gbaover[found_no].mirroringEnabled; + } + + fprintf(stderr, "RTC = %d.\n", enableRtc); + fprintf(stderr, "flashSize = %d.\n", flashSize); + fprintf(stderr, "cpuSaveType = %d.\n", cpuSaveType); + fprintf(stderr, "mirroringEnable = %d.\n", mirroringEnable); +} + +static void gba_init(void) +{ + cpuSaveType = 0; + flashSize = 0x10000; + enableRtc = false; + mirroringEnable = false; +#ifdef FRONTEND_SUPPORTS_RGB565 + systemColorDepth = 16; + systemRedShift = 11; + systemGreenShift = 6; + systemBlueShift = 0; +#else + systemColorDepth = 32; + systemRedShift = 19; + systemGreenShift = 11; + systemBlueShift = 3; +#endif + + + utilUpdateSystemColorMaps(false); + + load_image_preferences(); + + if(flashSize == 0x10000 || flashSize == 0x20000) + flashSetSize(flashSize); + + if(enableRtc) + rtcEnable(enableRtc); + + doMirroring(mirroringEnable); + + soundInit(); + soundSetSampleRate(32000); + + CPUInit(0, false); + CPUReset(); + + soundReset(); + + uint8_t * state_buf = (uint8_t*)malloc(2000000); + serialize_size = CPUWriteState(state_buf, 2000000); + free(state_buf); + + emulating = 1; +} + +void retro_deinit(void) +{ + emulating = 0; +} + +void retro_reset(void) +{ + CPUReset(); +} + +static const unsigned binds[] = { + RETRO_DEVICE_ID_JOYPAD_A, + RETRO_DEVICE_ID_JOYPAD_B, + RETRO_DEVICE_ID_JOYPAD_SELECT, + RETRO_DEVICE_ID_JOYPAD_START, + RETRO_DEVICE_ID_JOYPAD_RIGHT, + RETRO_DEVICE_ID_JOYPAD_LEFT, + RETRO_DEVICE_ID_JOYPAD_UP, + RETRO_DEVICE_ID_JOYPAD_DOWN, + RETRO_DEVICE_ID_JOYPAD_R, + RETRO_DEVICE_ID_JOYPAD_L +}; + +static const unsigned binds2[] = { + RETRO_DEVICE_ID_JOYPAD_B, + RETRO_DEVICE_ID_JOYPAD_A, + RETRO_DEVICE_ID_JOYPAD_SELECT, + RETRO_DEVICE_ID_JOYPAD_START, + RETRO_DEVICE_ID_JOYPAD_RIGHT, + RETRO_DEVICE_ID_JOYPAD_LEFT, + RETRO_DEVICE_ID_JOYPAD_UP, + RETRO_DEVICE_ID_JOYPAD_DOWN, + RETRO_DEVICE_ID_JOYPAD_R, + RETRO_DEVICE_ID_JOYPAD_L +}; + +static unsigned has_frame; + +static void update_variables(void) +{ + struct retro_variable var; + + var.key = "vbam-next-gamepad"; + var.value = NULL; + + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var)) + { + if (strcmp(var.value, "original") == 0) + device_type = 0; + else if (strcmp(var.value, "reversed") == 0) + device_type = 1; + } +} + +#ifdef FINAL_VERSION +#define TICKS 250000 +#else +#define TICKS 5000 +#endif + +void retro_run(void) +{ + bool updated = false; + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated) + update_variables(); + + poll_cb(); + + + has_frame = 0; + + do{ + CPULoop(TICKS); + }while(!has_frame); +} + +size_t retro_serialize_size(void) +{ + return serialize_size; +} + +bool retro_serialize(void *data, size_t size) +{ + return CPUWriteState((uint8_t*)data, size); +} + +bool retro_unserialize(const void *data, size_t size) +{ + return CPUReadState((uint8_t*)data, size); +} + +void retro_cheat_reset(void) +{} + +void retro_cheat_set(unsigned, bool, const char*) +{} + +bool retro_load_game(const struct retro_game_info *game) +{ + update_variables(); + + bool ret = CPULoadRom(game->path); + + gba_init(); + + return ret; +} + +bool retro_load_game_special( + unsigned game_type, + const struct retro_game_info *info, size_t num_info +) +{ return false; } + +extern unsigned g_audio_frames; +static unsigned g_video_frames; + +void retro_unload_game(void) +{ + fprintf(stderr, "[VBA] Sync stats: Audio frames: %u, Video frames: %u, AF/VF: %.2f\n", + g_audio_frames, g_video_frames, (float)g_audio_frames / g_video_frames); + g_audio_frames = 0; + g_video_frames = 0; +} + +unsigned retro_get_region(void) +{ + return RETRO_REGION_NTSC; +} + +void systemOnWriteDataToSoundBuffer(const u16 *finalWave, int length) +{ +} + +void systemOnSoundShutdown() {} +bool systemCanChangeSoundQuality() { return true; } + +#ifdef FRONTEND_SUPPORTS_RGB565 +#define GBA_PITCH 484 +#else +#define GBA_PITCH 964 +#endif + +void systemDrawScreen() +{ + video_cb(pix, 240, 160, GBA_PITCH); + g_video_frames++; + has_frame = 1; +} + +void systemFrame() {} + +void systemMessage(int, const char* str, ...) +{ + fprintf(stderr, "%s", str); +} + +void systemMessage(const char* str, ...) +{ + fprintf(stderr, "%s", str); +} + +int systemGetSensorX(void) +{ + return 0; +} + +int systemGetSensorY(void) +{ + return 0; +} + +u32 systemReadJoypad(int which) +{ + if (which == -1) + which = 0; + + u32 J = 0; + + for (unsigned i = 0; i < 10; i++) + J |= input_cb(which, RETRO_DEVICE_JOYPAD, 0, device_type ? binds2[i] : binds[i]) << i; + + return J; +} + +bool systemReadJoypads() { return true; } + +void systemUpdateMotionSensor() {} + +bool systemPauseOnFrame() { return false; } +void systemGbPrint(u8 *data,int pages, int feed, int palette, int contrast) {} +void systemScreenCapture(int a) {} +void systemScreenMessage(const char*msg) +{ + fprintf(stderr, "DEBUG: %s\n", msg); +} + +void systemSetTitle(const char *title) {} +void systemShowSpeed(int speed) {} +void system10Frames(int rate) {} + +u32 systemGetClock() +{ + return 0; +} + +int cheatsCheckKeys(u32 keys, u32 extended) +{ + return 0; +} + +SoundDriver *systemSoundInit() +{ + soundShutdown(); + + return new SoundRetro(); +} diff --git a/src/libretro/libretro.h b/src/libretro/libretro.h new file mode 100755 index 0000000..b02855a --- /dev/null +++ b/src/libretro/libretro.h @@ -0,0 +1,758 @@ +#ifndef LIBRETRO_H__ +#define LIBRETRO_H__ + +#include +#include +#include + +// Hack applied for MSVC when compiling in C89 mode as it isn't C99 compliant. +#ifdef __cplusplus +extern "C" { +#else +#if defined(_MSC_VER) && !defined(SN_TARGET_PS3) && !defined(__cplusplus) +#define bool unsigned char +#define true 1 +#define false 0 +#else +#include +#endif +#endif + +// Used for checking API/ABI mismatches that can break libretro implementations. +// It is not incremented for compatible changes to the API. +#define RETRO_API_VERSION 1 + +// Libretro's fundamental device abstractions. +#define RETRO_DEVICE_MASK 0xff +#define RETRO_DEVICE_NONE 0 + +// The JOYPAD is called RetroPad. It is essentially a Super Nintendo controller, +// but with additional L2/R2/L3/R3 buttons, similar to a PS1 DualShock. +#define RETRO_DEVICE_JOYPAD 1 + +// The mouse is a simple mouse, similar to Super Nintendo's mouse. +// X and Y coordinates are reported relatively to last poll (poll callback). +// It is up to the libretro implementation to keep track of where the mouse pointer is supposed to be on the screen. +// The frontend must make sure not to interfere with its own hardware mouse pointer. +#define RETRO_DEVICE_MOUSE 2 + +// KEYBOARD device lets one poll for raw key pressed. +// It is poll based, so input callback will return with the current pressed state. +#define RETRO_DEVICE_KEYBOARD 3 + +// Lightgun X/Y coordinates are reported relatively to last poll, similar to mouse. +#define RETRO_DEVICE_LIGHTGUN 4 + +// The ANALOG device is an extension to JOYPAD (RetroPad). +// Similar to DualShock it adds two analog sticks. +// This is treated as a separate device type as it returns values in the full analog range +// of [-0x8000, 0x7fff]. Positive X axis is right. Positive Y axis is down. +// Only use ANALOG type when polling for analog values of the axes. +#define RETRO_DEVICE_ANALOG 5 + +// Abstracts the concept of a pointing mechanism, e.g. touch. +// This allows libretro to query in absolute coordinates where on the screen a mouse (or something similar) is being placed. +// For a touch centric device, coordinates reported are the coordinates of the press. +// +// Coordinates in X and Y are reported as: +// [-0x7fff, 0x7fff]: -0x7fff corresponds to the far left/top of the screen, +// and 0x7fff corresponds to the far right/bottom of the screen. +// The "screen" is here defined as area that is passed to the frontend and later displayed on the monitor. +// The frontend is free to scale/resize this screen as it sees fit, however, +// (X, Y) = (-0x7fff, -0x7fff) will correspond to the top-left pixel of the game image, etc. +// +// To check if the pointer coordinates are valid (e.g. a touch display actually being touched), +// PRESSED returns 1 or 0. +// If using a mouse, PRESSED will usually correspond to the left mouse button. +// PRESSED will only return 1 if the pointer is inside the game screen. +// +// For multi-touch, the index variable can be used to successively query more presses. +// If index = 0 returns true for _PRESSED, coordinates can be extracted +// with _X, _Y for index = 0. One can then query _PRESSED, _X, _Y with index = 1, and so on. +// Eventually _PRESSED will return false for an index. No further presses are registered at this point. +#define RETRO_DEVICE_POINTER 6 + +// These device types are specializations of the base types above. +// They should only be used in retro_set_controller_type() to inform libretro implementations +// about use of a very specific device type. +// +// In input state callback, however, only the base type should be used in the 'device' field. +#define RETRO_DEVICE_JOYPAD_MULTITAP ((1 << 8) | RETRO_DEVICE_JOYPAD) +#define RETRO_DEVICE_LIGHTGUN_SUPER_SCOPE ((1 << 8) | RETRO_DEVICE_LIGHTGUN) +#define RETRO_DEVICE_LIGHTGUN_JUSTIFIER ((2 << 8) | RETRO_DEVICE_LIGHTGUN) +#define RETRO_DEVICE_LIGHTGUN_JUSTIFIERS ((3 << 8) | RETRO_DEVICE_LIGHTGUN) + +// Buttons for the RetroPad (JOYPAD). +// The placement of these is equivalent to placements on the Super Nintendo controller. +// L2/R2/L3/R3 buttons correspond to the PS1 DualShock. +#define RETRO_DEVICE_ID_JOYPAD_B 0 +#define RETRO_DEVICE_ID_JOYPAD_Y 1 +#define RETRO_DEVICE_ID_JOYPAD_SELECT 2 +#define RETRO_DEVICE_ID_JOYPAD_START 3 +#define RETRO_DEVICE_ID_JOYPAD_UP 4 +#define RETRO_DEVICE_ID_JOYPAD_DOWN 5 +#define RETRO_DEVICE_ID_JOYPAD_LEFT 6 +#define RETRO_DEVICE_ID_JOYPAD_RIGHT 7 +#define RETRO_DEVICE_ID_JOYPAD_A 8 +#define RETRO_DEVICE_ID_JOYPAD_X 9 +#define RETRO_DEVICE_ID_JOYPAD_L 10 +#define RETRO_DEVICE_ID_JOYPAD_R 11 +#define RETRO_DEVICE_ID_JOYPAD_L2 12 +#define RETRO_DEVICE_ID_JOYPAD_R2 13 +#define RETRO_DEVICE_ID_JOYPAD_L3 14 +#define RETRO_DEVICE_ID_JOYPAD_R3 15 + +// Index / Id values for ANALOG device. +#define RETRO_DEVICE_INDEX_ANALOG_LEFT 0 +#define RETRO_DEVICE_INDEX_ANALOG_RIGHT 1 +#define RETRO_DEVICE_ID_ANALOG_X 0 +#define RETRO_DEVICE_ID_ANALOG_Y 1 + +// Id values for MOUSE. +#define RETRO_DEVICE_ID_MOUSE_X 0 +#define RETRO_DEVICE_ID_MOUSE_Y 1 +#define RETRO_DEVICE_ID_MOUSE_LEFT 2 +#define RETRO_DEVICE_ID_MOUSE_RIGHT 3 + +// Id values for LIGHTGUN types. +#define RETRO_DEVICE_ID_LIGHTGUN_X 0 +#define RETRO_DEVICE_ID_LIGHTGUN_Y 1 +#define RETRO_DEVICE_ID_LIGHTGUN_TRIGGER 2 +#define RETRO_DEVICE_ID_LIGHTGUN_CURSOR 3 +#define RETRO_DEVICE_ID_LIGHTGUN_TURBO 4 +#define RETRO_DEVICE_ID_LIGHTGUN_PAUSE 5 +#define RETRO_DEVICE_ID_LIGHTGUN_START 6 + +// Id values for POINTER. +#define RETRO_DEVICE_ID_POINTER_X 0 +#define RETRO_DEVICE_ID_POINTER_Y 1 +#define RETRO_DEVICE_ID_POINTER_PRESSED 2 + +// Returned from retro_get_region(). +#define RETRO_REGION_NTSC 0 +#define RETRO_REGION_PAL 1 + +// Passed to retro_get_memory_data/size(). +// If the memory type doesn't apply to the implementation NULL/0 can be returned. +#define RETRO_MEMORY_MASK 0xff + +// Regular save ram. This ram is usually found on a game cartridge, backed up by a battery. +// If save game data is too complex for a single memory buffer, +// the SYSTEM_DIRECTORY environment callback can be used. +#define RETRO_MEMORY_SAVE_RAM 0 + +// Some games have a built-in clock to keep track of time. +// This memory is usually just a couple of bytes to keep track of time. +#define RETRO_MEMORY_RTC 1 + +// System ram lets a frontend peek into a game systems main RAM. +#define RETRO_MEMORY_SYSTEM_RAM 2 + +// Video ram lets a frontend peek into a game systems video RAM (VRAM). +#define RETRO_MEMORY_VIDEO_RAM 3 + +// Special memory types. +#define RETRO_MEMORY_SNES_BSX_RAM ((1 << 8) | RETRO_MEMORY_SAVE_RAM) +#define RETRO_MEMORY_SNES_BSX_PRAM ((2 << 8) | RETRO_MEMORY_SAVE_RAM) +#define RETRO_MEMORY_SNES_SUFAMI_TURBO_A_RAM ((3 << 8) | RETRO_MEMORY_SAVE_RAM) +#define RETRO_MEMORY_SNES_SUFAMI_TURBO_B_RAM ((4 << 8) | RETRO_MEMORY_SAVE_RAM) +#define RETRO_MEMORY_SNES_GAME_BOY_RAM ((5 << 8) | RETRO_MEMORY_SAVE_RAM) +#define RETRO_MEMORY_SNES_GAME_BOY_RTC ((6 << 8) | RETRO_MEMORY_RTC) + +// Special game types passed into retro_load_game_special(). +// Only used when multiple ROMs are required. +#define RETRO_GAME_TYPE_BSX 0x101 +#define RETRO_GAME_TYPE_BSX_SLOTTED 0x102 +#define RETRO_GAME_TYPE_SUFAMI_TURBO 0x103 +#define RETRO_GAME_TYPE_SUPER_GAME_BOY 0x104 + +// Keysyms used for ID in input state callback when polling RETRO_KEYBOARD. +enum retro_key +{ + RETROK_UNKNOWN = 0, + RETROK_FIRST = 0, + RETROK_BACKSPACE = 8, + RETROK_TAB = 9, + RETROK_CLEAR = 12, + RETROK_RETURN = 13, + RETROK_PAUSE = 19, + RETROK_ESCAPE = 27, + RETROK_SPACE = 32, + RETROK_EXCLAIM = 33, + RETROK_QUOTEDBL = 34, + RETROK_HASH = 35, + RETROK_DOLLAR = 36, + RETROK_AMPERSAND = 38, + RETROK_QUOTE = 39, + RETROK_LEFTPAREN = 40, + RETROK_RIGHTPAREN = 41, + RETROK_ASTERISK = 42, + RETROK_PLUS = 43, + RETROK_COMMA = 44, + RETROK_MINUS = 45, + RETROK_PERIOD = 46, + RETROK_SLASH = 47, + RETROK_0 = 48, + RETROK_1 = 49, + RETROK_2 = 50, + RETROK_3 = 51, + RETROK_4 = 52, + RETROK_5 = 53, + RETROK_6 = 54, + RETROK_7 = 55, + RETROK_8 = 56, + RETROK_9 = 57, + RETROK_COLON = 58, + RETROK_SEMICOLON = 59, + RETROK_LESS = 60, + RETROK_EQUALS = 61, + RETROK_GREATER = 62, + RETROK_QUESTION = 63, + RETROK_AT = 64, + RETROK_LEFTBRACKET = 91, + RETROK_BACKSLASH = 92, + RETROK_RIGHTBRACKET = 93, + RETROK_CARET = 94, + RETROK_UNDERSCORE = 95, + RETROK_BACKQUOTE = 96, + RETROK_a = 97, + RETROK_b = 98, + RETROK_c = 99, + RETROK_d = 100, + RETROK_e = 101, + RETROK_f = 102, + RETROK_g = 103, + RETROK_h = 104, + RETROK_i = 105, + RETROK_j = 106, + RETROK_k = 107, + RETROK_l = 108, + RETROK_m = 109, + RETROK_n = 110, + RETROK_o = 111, + RETROK_p = 112, + RETROK_q = 113, + RETROK_r = 114, + RETROK_s = 115, + RETROK_t = 116, + RETROK_u = 117, + RETROK_v = 118, + RETROK_w = 119, + RETROK_x = 120, + RETROK_y = 121, + RETROK_z = 122, + RETROK_DELETE = 127, + + RETROK_KP0 = 256, + RETROK_KP1 = 257, + RETROK_KP2 = 258, + RETROK_KP3 = 259, + RETROK_KP4 = 260, + RETROK_KP5 = 261, + RETROK_KP6 = 262, + RETROK_KP7 = 263, + RETROK_KP8 = 264, + RETROK_KP9 = 265, + RETROK_KP_PERIOD = 266, + RETROK_KP_DIVIDE = 267, + RETROK_KP_MULTIPLY = 268, + RETROK_KP_MINUS = 269, + RETROK_KP_PLUS = 270, + RETROK_KP_ENTER = 271, + RETROK_KP_EQUALS = 272, + + RETROK_UP = 273, + RETROK_DOWN = 274, + RETROK_RIGHT = 275, + RETROK_LEFT = 276, + RETROK_INSERT = 277, + RETROK_HOME = 278, + RETROK_END = 279, + RETROK_PAGEUP = 280, + RETROK_PAGEDOWN = 281, + + RETROK_F1 = 282, + RETROK_F2 = 283, + RETROK_F3 = 284, + RETROK_F4 = 285, + RETROK_F5 = 286, + RETROK_F6 = 287, + RETROK_F7 = 288, + RETROK_F8 = 289, + RETROK_F9 = 290, + RETROK_F10 = 291, + RETROK_F11 = 292, + RETROK_F12 = 293, + RETROK_F13 = 294, + RETROK_F14 = 295, + RETROK_F15 = 296, + + RETROK_NUMLOCK = 300, + RETROK_CAPSLOCK = 301, + RETROK_SCROLLOCK = 302, + RETROK_RSHIFT = 303, + RETROK_LSHIFT = 304, + RETROK_RCTRL = 305, + RETROK_LCTRL = 306, + RETROK_RALT = 307, + RETROK_LALT = 308, + RETROK_RMETA = 309, + RETROK_LMETA = 310, + RETROK_LSUPER = 311, + RETROK_RSUPER = 312, + RETROK_MODE = 313, + RETROK_COMPOSE = 314, + + RETROK_HELP = 315, + RETROK_PRINT = 316, + RETROK_SYSREQ = 317, + RETROK_BREAK = 318, + RETROK_MENU = 319, + RETROK_POWER = 320, + RETROK_EURO = 321, + RETROK_UNDO = 322, + + RETROK_LAST, + + RETROK_DUMMY = INT_MAX // Ensure sizeof(enum) == sizeof(int) +}; + +enum retro_mod +{ + RETROKMOD_NONE = 0x0000, + + RETROKMOD_SHIFT = 0x01, + RETROKMOD_CTRL = 0x02, + RETROKMOD_ALT = 0x04, + RETROKMOD_META = 0x08, + + RETROKMOD_NUMLOCK = 0x10, + RETROKMOD_CAPSLOCK = 0x20, + RETROKMOD_SCROLLOCK = 0x40, + + RETROKMOD_DUMMY = INT_MAX // Ensure sizeof(enum) == sizeof(int) +}; + +// If set, this call is not part of the public libretro API yet. It can change or be removed at any time. +#define RETRO_ENVIRONMENT_EXPERIMENTAL 0x10000 + +// Environment commands. +#define RETRO_ENVIRONMENT_SET_ROTATION 1 // const unsigned * -- + // Sets screen rotation of graphics. + // Is only implemented if rotation can be accelerated by hardware. + // Valid values are 0, 1, 2, 3, which rotates screen by 0, 90, 180, 270 degrees + // counter-clockwise respectively. + // +#define RETRO_ENVIRONMENT_GET_OVERSCAN 2 // bool * -- + // Boolean value whether or not the implementation should use overscan, or crop away overscan. + // +#define RETRO_ENVIRONMENT_GET_CAN_DUPE 3 // bool * -- + // Boolean value whether or not frontend supports frame duping, + // passing NULL to video frame callback. + // +// Environ 4, 5 are no longer supported (GET_VARIABLE / SET_VARIABLES), and reserved to avoid possible ABI clash. +#define RETRO_ENVIRONMENT_SET_MESSAGE 6 // const struct retro_message * -- + // Sets a message to be displayed in implementation-specific manner for a certain amount of 'frames'. + // Should not be used for trivial messages, which should simply be logged to stderr. +#define RETRO_ENVIRONMENT_SHUTDOWN 7 // N/A (NULL) -- + // Requests the frontend to shutdown. + // Should only be used if game has a specific + // way to shutdown the game from a menu item or similar. + // +#define RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL 8 + // const unsigned * -- + // Gives a hint to the frontend how demanding this implementation + // is on a system. E.g. reporting a level of 2 means + // this implementation should run decently on all frontends + // of level 2 and up. + // + // It can be used by the frontend to potentially warn + // about too demanding implementations. + // + // The levels are "floating", but roughly defined as: + // 0: Low-powered embedded devices such as Raspberry Pi + // 1: 6th generation consoles, such as Wii/Xbox 1, and phones, tablets, etc. + // 2: 7th generation consoles, such as PS3/360, with sub-par CPUs. + // 3: Modern desktop/laptops with reasonably powerful CPUs. + // 4: High-end desktops with very powerful CPUs. + // + // This function can be called on a per-game basis, + // as certain games an implementation can play might be + // particularily demanding. + // If called, it should be called in retro_load_game(). + // +#define RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY 9 + // const char ** -- + // Returns the "system" directory of the frontend. + // This directory can be used to store system specific ROMs such as BIOSes, configuration data, etc. + // The returned value can be NULL. + // If so, no such directory is defined, + // and it's up to the implementation to find a suitable directory. + // +#define RETRO_ENVIRONMENT_SET_PIXEL_FORMAT 10 + // const enum retro_pixel_format * -- + // Sets the internal pixel format used by the implementation. + // The default pixel format is RETRO_PIXEL_FORMAT_0RGB1555. + // This pixel format however, is deprecated (see enum retro_pixel_format). + // If the call returns false, the frontend does not support this pixel format. + // This function should be called inside retro_load_game() or retro_get_system_av_info(). + // +#define RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS 11 + // const struct retro_input_descriptor * -- + // Sets an array of retro_input_descriptors. + // It is up to the frontend to present this in a usable way. + // The array is terminated by retro_input_descriptor::description being set to NULL. + // This function can be called at any time, but it is recommended to call it as early as possible. +#define RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK 12 + // const struct retro_keyboard_callback * -- + // Sets a callback function used to notify core about keyboard events. + // +#define RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE 13 + // const struct retro_disk_control_callback * -- + // Sets an interface which frontend can use to eject and insert disk images. + // This is used for games which consist of multiple images and must be manually + // swapped out by the user (e.g. PSX). +#define RETRO_ENVIRONMENT_SET_HW_RENDER (14 | RETRO_ENVIRONMENT_EXPERIMENTAL) + // struct retro_hw_render_callback * -- + // NOTE: This call is currently very experimental, and should not be considered part of the public API. + // The interface could be changed or removed at any time. + // Sets an interface to let a libretro core render with hardware acceleration. + // Should be called in retro_load_game(). + // If successful, libretro cores will be able to render to a frontend-provided framebuffer. + // The size of this framebuffer will be at least as large as max_width/max_height provided in get_av_info(). + // If HW rendering is used, pass only RETRO_HW_FRAME_BUFFER_VALID or NULL to retro_video_refresh_t. +#define RETRO_ENVIRONMENT_GET_VARIABLE 15 + // struct retro_variable * -- + // Interface to aquire user-defined information from environment + // that cannot feasibly be supported in a multi-system way. + // 'key' should be set to a key which has already been set by SET_VARIABLES. + // 'data' will be set to a value or NULL. + // +#define RETRO_ENVIRONMENT_SET_VARIABLES 16 + // const struct retro_variable * -- + // Allows an implementation to signal the environment + // which variables it might want to check for later using GET_VARIABLE. + // This allows the frontend to present these variables to a user dynamically. + // This should be called as early as possible (ideally in retro_set_environment). + // + // 'data' points to an array of retro_variable structs terminated by a { NULL, NULL } element. + // retro_variable::key should be namespaced to not collide with other implementations' keys. E.g. A core called 'foo' should use keys named as 'foo_option'. + // retro_variable::value should contain a human readable description of the key as well as a '|' delimited list of expected values. + // The number of possible options should be very limited, i.e. it should be feasible to cycle through options without a keyboard. + // First entry should be treated as a default. + // + // Example entry: + // { "foo_option", "Speed hack coprocessor X; false|true" } + // + // Text before first ';' is description. This ';' must be followed by a space, and followed by a list of possible values split up with '|'. + // Only strings are operated on. The possible values will generally be displayed and stored as-is by the frontend. + // +#define RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE 17 + // bool * -- + // Result is set to true if some variables are updated by + // frontend since last call to RETRO_ENVIRONMENT_GET_VARIABLE. + // Variables should be queried with GET_VARIABLE. + +// Pass this to retro_video_refresh_t if rendering to hardware. +// Passing NULL to retro_video_refresh_t is still a frame dupe as normal. +#define RETRO_HW_FRAME_BUFFER_VALID ((void*)-1) + +// Invalidates the current HW context. +// If called, all GPU resources must be reinitialized. +// Usually called when frontend reinits video driver. +// Also called first time video driver is initialized, allowing libretro core to init resources. +typedef void (*retro_hw_context_reset_t)(void); +// Gets current framebuffer which is to be rendered to. Could change every frame potentially. +typedef uintptr_t (*retro_hw_get_current_framebuffer_t)(void); + +// Get a symbol from HW context. +typedef void (*retro_proc_address_t)(void); +typedef retro_proc_address_t (*retro_hw_get_proc_address_t)(const char *sym); + +enum retro_hw_context_type +{ + RETRO_HW_CONTEXT_NONE = 0, + RETRO_HW_CONTEXT_OPENGL, // OpenGL 2.x. Latest version available before 3.x+. + RETRO_HW_CONTEXT_OPENGLES2, // GLES 2.0 + + RETRO_HW_CONTEXT_DUMMY = INT_MAX +}; + +struct retro_hw_render_callback +{ + enum retro_hw_context_type context_type; // Which API to use. Set by libretro core. + retro_hw_context_reset_t context_reset; // Set by libretro core. + retro_hw_get_current_framebuffer_t get_current_framebuffer; // Set by frontend. + retro_hw_get_proc_address_t get_proc_address; // Set by frontend. + bool depth; // Set if render buffers should have depth component attached. +}; + +// Callback type passed in RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK. Called by the frontend in response to keyboard events. +// down is set if the key is being pressed, or false if it is being released. +// keycode is the RETROK value of the char. +// character is the text character of the pressed key. (UTF-32). +// key_modifiers is a set of RETROKMOD values or'ed together. +typedef void (*retro_keyboard_event_t)(bool down, unsigned keycode, uint32_t character, uint16_t key_modifiers); + +struct retro_keyboard_callback +{ + retro_keyboard_event_t callback; +}; + +// Callbacks for RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE. +// Should be set for implementations which can swap out multiple disk images in runtime. +// If the implementation can do this automatically, it should strive to do so. +// However, there are cases where the user must manually do so. +// +// Overview: To swap a disk image, eject the disk image with set_eject_state(true). +// Set the disk index with set_image_index(index). Insert the disk again with set_eject_state(false). + +// If ejected is true, "ejects" the virtual disk tray. +// When ejected, the disk image index can be set. +typedef bool (*retro_set_eject_state_t)(bool ejected); +// Gets current eject state. The initial state is 'not ejected'. +typedef bool (*retro_get_eject_state_t)(void); +// Gets current disk index. First disk is index 0. +// If return value is >= get_num_images(), no disk is currently inserted. +typedef unsigned (*retro_get_image_index_t)(void); +// Sets image index. Can only be called when disk is ejected. +// The implementation supports setting "no disk" by using an index >= get_num_images(). +typedef bool (*retro_set_image_index_t)(unsigned index); +// Gets total number of images which are available to use. +typedef unsigned (*retro_get_num_images_t)(void); +// +// Replaces the disk image associated with index. +// Arguments to pass in info have same requirements as retro_load_game(). +// Virtual disk tray must be ejected when calling this. +// Replacing a disk image with info = NULL will remove the disk image from the internal list. +// As a result, calls to get_image_index() can change. +// +// E.g. replace_image_index(1, NULL), and previous get_image_index() returned 4 before. +// Index 1 will be removed, and the new index is 3. +struct retro_game_info; +typedef bool (*retro_replace_image_index_t)(unsigned index, const struct retro_game_info *info); +// Adds a new valid index (get_num_images()) to the internal disk list. +// This will increment subsequent return values from get_num_images() by 1. +// This image index cannot be used until a disk image has been set with replace_image_index. +typedef bool (*retro_add_image_index_t)(void); + +struct retro_disk_control_callback +{ + retro_set_eject_state_t set_eject_state; + retro_get_eject_state_t get_eject_state; + + retro_get_image_index_t get_image_index; + retro_set_image_index_t set_image_index; + retro_get_num_images_t get_num_images; + + retro_replace_image_index_t replace_image_index; + retro_add_image_index_t add_image_index; +}; + +enum retro_pixel_format +{ + // 0RGB1555, native endian. 0 bit must be set to 0. + // This pixel format is default for compatibility concerns only. + // If a 15/16-bit pixel format is desired, consider using RGB565. + RETRO_PIXEL_FORMAT_0RGB1555 = 0, + + // XRGB8888, native endian. X bits are ignored. + RETRO_PIXEL_FORMAT_XRGB8888 = 1, + + // RGB565, native endian. This pixel format is the recommended format to use if a 15/16-bit format is desired + // as it is the pixel format that is typically available on a wide range of low-power devices. + // It is also natively supported in APIs like OpenGL ES. + RETRO_PIXEL_FORMAT_RGB565 = 2, + + // Ensure sizeof() == sizeof(int). + RETRO_PIXEL_FORMAT_UNKNOWN = INT_MAX +}; + +struct retro_message +{ + const char *msg; // Message to be displayed. + unsigned frames; // Duration in frames of message. +}; + +// Describes how the libretro implementation maps a libretro input bind +// to its internal input system through a human readable string. +// This string can be used to better let a user configure input. +struct retro_input_descriptor +{ + // Associates given parameters with a description. + unsigned port; + unsigned device; + unsigned index; + unsigned id; + + const char *description; // Human readable description for parameters. + // The pointer must remain valid until retro_unload_game() is called. +}; + +struct retro_system_info +{ + // All pointers are owned by libretro implementation, and pointers must remain valid until retro_deinit() is called. + + const char *library_name; // Descriptive name of library. Should not contain any version numbers, etc. + const char *library_version; // Descriptive version of core. + + const char *valid_extensions; // A string listing probably rom extensions the core will be able to load, separated with pipe. + // I.e. "bin|rom|iso". + // Typically used for a GUI to filter out extensions. + + bool need_fullpath; // If true, retro_load_game() is guaranteed to provide a valid pathname in retro_game_info::path. + // ::data and ::size are both invalid. + // If false, ::data and ::size are guaranteed to be valid, but ::path might not be valid. + // This is typically set to true for libretro implementations that must load from file. + // Implementations should strive for setting this to false, as it allows the frontend to perform patching, etc. + + bool block_extract; // If true, the frontend is not allowed to extract any archives before loading the real ROM. + // Necessary for certain libretro implementations that load games from zipped archives. +}; + +struct retro_game_geometry +{ + unsigned base_width; // Nominal video width of game. + unsigned base_height; // Nominal video height of game. + unsigned max_width; // Maximum possible width of game. + unsigned max_height; // Maximum possible height of game. + + float aspect_ratio; // Nominal aspect ratio of game. If aspect_ratio is <= 0.0, + // an aspect ratio of base_width / base_height is assumed. + // A frontend could override this setting if desired. +}; + +struct retro_system_timing +{ + double fps; // FPS of video content. + double sample_rate; // Sampling rate of audio. +}; + +struct retro_system_av_info +{ + struct retro_game_geometry geometry; + struct retro_system_timing timing; +}; + +struct retro_variable +{ + const char *key; // Variable to query in RETRO_ENVIRONMENT_GET_VARIABLE. + // If NULL, obtains the complete environment string if more complex parsing is necessary. + // The environment string is formatted as key-value pairs delimited by semicolons as so: + // "key1=value1;key2=value2;..." + const char *value; // Value to be obtained. If key does not exist, it is set to NULL. +}; + +struct retro_game_info +{ + const char *path; // Path to game, UTF-8 encoded. Usually used as a reference. + // May be NULL if rom was loaded from stdin or similar. + // retro_system_info::need_fullpath guaranteed that this path is valid. + const void *data; // Memory buffer of loaded game. Will be NULL if need_fullpath was set. + size_t size; // Size of memory buffer. + const char *meta; // String of implementation specific meta-data. +}; + +// Callbacks +// +// Environment callback. Gives implementations a way of performing uncommon tasks. Extensible. +typedef bool (*retro_environment_t)(unsigned cmd, void *data); + +// Render a frame. Pixel format is 15-bit 0RGB1555 native endian unless changed (see RETRO_ENVIRONMENT_SET_PIXEL_FORMAT). +// Width and height specify dimensions of buffer. +// Pitch specifices length in bytes between two lines in buffer. +// For performance reasons, it is highly recommended to have a frame that is packed in memory, i.e. pitch == width * byte_per_pixel. +// Certain graphic APIs, such as OpenGL ES, do not like textures that are not packed in memory. +typedef void (*retro_video_refresh_t)(const void *data, unsigned width, unsigned height, size_t pitch); + +// Renders a single audio frame. Should only be used if implementation generates a single sample at a time. +// Format is signed 16-bit native endian. +typedef void (*retro_audio_sample_t)(int16_t left, int16_t right); +// Renders multiple audio frames in one go. One frame is defined as a sample of left and right channels, interleaved. +// I.e. int16_t buf[4] = { l, r, l, r }; would be 2 frames. +// Only one of the audio callbacks must ever be used. +typedef size_t (*retro_audio_sample_batch_t)(const int16_t *data, size_t frames); + +// Polls input. +typedef void (*retro_input_poll_t)(void); +// Queries for input for player 'port'. device will be masked with RETRO_DEVICE_MASK. +// Specialization of devices such as RETRO_DEVICE_JOYPAD_MULTITAP that have been set with retro_set_controller_port_device() +// will still use the higher level RETRO_DEVICE_JOYPAD to request input. +typedef int16_t (*retro_input_state_t)(unsigned port, unsigned device, unsigned index, unsigned id); + +// Sets callbacks. retro_set_environment() is guaranteed to be called before retro_init(). +// The rest of the set_* functions are guaranteed to have been called before the first call to retro_run() is made. +void retro_set_environment(retro_environment_t); +void retro_set_video_refresh(retro_video_refresh_t); +void retro_set_audio_sample(retro_audio_sample_t); +void retro_set_audio_sample_batch(retro_audio_sample_batch_t); +void retro_set_input_poll(retro_input_poll_t); +void retro_set_input_state(retro_input_state_t); + +// Library global initialization/deinitialization. +void retro_init(void); +void retro_deinit(void); + +// Must return RETRO_API_VERSION. Used to validate ABI compatibility when the API is revised. +unsigned retro_api_version(void); + +// Gets statically known system info. Pointers provided in *info must be statically allocated. +// Can be called at any time, even before retro_init(). +void retro_get_system_info(struct retro_system_info *info); + +// Gets information about system audio/video timings and geometry. +// Can be called only after retro_load_game() has successfully completed. +// NOTE: The implementation of this function might not initialize every variable if needed. +// E.g. geom.aspect_ratio might not be initialized if core doesn't desire a particular aspect ratio. +void retro_get_system_av_info(struct retro_system_av_info *info); + +// Sets device to be used for player 'port'. +void retro_set_controller_port_device(unsigned port, unsigned device); + +// Resets the current game. +void retro_reset(void); + +// Runs the game for one video frame. +// During retro_run(), input_poll callback must be called at least once. +// +// If a frame is not rendered for reasons where a game "dropped" a frame, +// this still counts as a frame, and retro_run() should explicitly dupe a frame if GET_CAN_DUPE returns true. +// In this case, the video callback can take a NULL argument for data. +void retro_run(void); + +// Returns the amount of data the implementation requires to serialize internal state (save states). +// Beetween calls to retro_load_game() and retro_unload_game(), the returned size is never allowed to be larger than a previous returned value, to +// ensure that the frontend can allocate a save state buffer once. +size_t retro_serialize_size(void); + +// Serializes internal state. If failed, or size is lower than retro_serialize_size(), it should return false, true otherwise. +bool retro_serialize(void *data, size_t size); +bool retro_unserialize(const void *data, size_t size); + +void retro_cheat_reset(void); +void retro_cheat_set(unsigned index, bool enabled, const char *code); + +// Loads a game. +bool retro_load_game(const struct retro_game_info *game); + +// Loads a "special" kind of game. Should not be used except in extreme cases. +bool retro_load_game_special( + unsigned game_type, + const struct retro_game_info *info, size_t num_info +); + +// Unloads a currently loaded game. +void retro_unload_game(void); + +// Gets region of game. +unsigned retro_get_region(void); + +// Gets region of memory. +void *retro_get_memory_data(unsigned id); +size_t retro_get_memory_size(unsigned id); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libretro/link.T b/src/libretro/link.T new file mode 100644 index 0000000..9e82b5d --- /dev/null +++ b/src/libretro/link.T @@ -0,0 +1,4 @@ +{ + global: retro_*; + local: *; +}; diff --git a/src/libretro/msvc/msvc-2003-xbox1.bat b/src/libretro/msvc/msvc-2003-xbox1.bat new file mode 100644 index 0000000..44db300 --- /dev/null +++ b/src/libretro/msvc/msvc-2003-xbox1.bat @@ -0,0 +1,47 @@ +@SET VSINSTALLDIR=C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\IDE +@SET VCINSTALLDIR=C:\Program Files\Microsoft Visual Studio .NET 2003 +@SET FrameworkDir=C:\WINDOWS\Microsoft.NET\Framework +@SET FrameworkVersion=v1.1.4322 +@SET FrameworkSDKDir=C:\Program Files\Microsoft Visual Studio .NET 2003\SDK\v1.1 +@rem Root of Visual Studio common files. + +@if "%VSINSTALLDIR%"=="" goto Usage +@if "%VCINSTALLDIR%"=="" set VCINSTALLDIR=%VSINSTALLDIR% + +@rem +@rem Root of Visual Studio ide installed files. +@rem +@set DevEnvDir=%VSINSTALLDIR% + +@rem +@rem Root of Visual C++ installed files. +@rem +@set MSVCDir=%VCINSTALLDIR%\VC7 + +@rem +@echo Setting environment for using Microsoft Visual Studio .NET 2003 tools. +@echo (If you have another version of Visual Studio or Visual C++ installed and wish +@echo to use its tools from the command line, run vcvars32.bat for that version.) +@rem + +@REM %VCINSTALLDIR%\Common7\Tools dir is added only for real setup. + +@set PATH=%DevEnvDir%;%MSVCDir%\BIN;%VCINSTALLDIR%\Common7\Tools;%VCINSTALLDIR%\Common7\Tools\bin\prerelease;%VCINSTALLDIR%\Common7\Tools\bin;%FrameworkSDKDir%\bin;%FrameworkDir%\%FrameworkVersion%;%PATH%; +@set INCLUDE=%MSVCDir%\ATLMFC\INCLUDE;%MSVCDir%\INCLUDE;%FrameworkSDKDir%\include;%INCLUDE%;%XDK%\xbox\include +@set LIB=%MSVCDir%\ATLMFC\LIB;%MSVCDir%\LIB;%MSVCDir%\PlatformSDK\lib;%XDK%\lib;%XDK%\xbox\lib;%LIB% + +@goto end + +:Usage + +@echo. VSINSTALLDIR variable is not set. +@echo. +@echo SYNTAX: %0 + +@goto end + +:end + +devenv /clean Release msvc-2003-xbox1.sln +devenv /build Release msvc-2003-xbox1.sln +exit diff --git a/src/libretro/msvc/msvc-2003-xbox1.sln b/src/libretro/msvc/msvc-2003-xbox1.sln new file mode 100644 index 0000000..f52e31b --- /dev/null +++ b/src/libretro/msvc/msvc-2003-xbox1.sln @@ -0,0 +1,30 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vbanext-msvc2003-xbox1", "msvc-2003-xbox1/msvc-2003-xbox1.vcproj", "{BDCB70E0-EEBB-404A-9874-9A2E22A4A2B3}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Profile = Profile + Profile_FastCap = Profile_FastCap + Release = Release + Release_LTCG = Release_LTCG + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {BDCB70E0-EEBB-404A-9874-9A2E22A4A2B3}.Debug.ActiveCfg = Debug|Xbox + {BDCB70E0-EEBB-404A-9874-9A2E22A4A2B3}.Debug.Build.0 = Debug|Xbox + {BDCB70E0-EEBB-404A-9874-9A2E22A4A2B3}.Profile.ActiveCfg = Profile|Xbox + {BDCB70E0-EEBB-404A-9874-9A2E22A4A2B3}.Profile.Build.0 = Profile|Xbox + {BDCB70E0-EEBB-404A-9874-9A2E22A4A2B3}.Profile_FastCap.ActiveCfg = Profile_FastCap|Xbox + {BDCB70E0-EEBB-404A-9874-9A2E22A4A2B3}.Profile_FastCap.Build.0 = Profile_FastCap|Xbox + {BDCB70E0-EEBB-404A-9874-9A2E22A4A2B3}.Release.ActiveCfg = Release|Xbox + {BDCB70E0-EEBB-404A-9874-9A2E22A4A2B3}.Release.Build.0 = Release|Xbox + {BDCB70E0-EEBB-404A-9874-9A2E22A4A2B3}.Release_LTCG.ActiveCfg = Release_LTCG|Xbox + {BDCB70E0-EEBB-404A-9874-9A2E22A4A2B3}.Release_LTCG.Build.0 = Release_LTCG|Xbox + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/src/libretro/msvc/msvc-2003-xbox1/msvc-2003-xbox1.vcproj b/src/libretro/msvc/msvc-2003-xbox1/msvc-2003-xbox1.vcproj new file mode 100644 index 0000000..ecc0de8 --- /dev/null +++ b/src/libretro/msvc/msvc-2003-xbox1/msvc-2003-xbox1.vcproj @@ -0,0 +1,234 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/libretro/msvc/msvc-2003-xbox1/stdint.h b/src/libretro/msvc/msvc-2003-xbox1/stdint.h new file mode 100644 index 0000000..9d8fe7b --- /dev/null +++ b/src/libretro/msvc/msvc-2003-xbox1/stdint.h @@ -0,0 +1,249 @@ +// ISO C9x compliant stdint.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2008 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. The name of the author may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// +#ifndef __RARCH_STDINT_H +#define __RARCH_STDINT_H + +#if _MSC_VER && (_MSC_VER < 1600) +//pre-MSVC 2010 needs an implementation of stdint.h + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include + +// For Visual Studio 6 in C++ mode and for many Visual Studio versions when +// compiling for ARM we should wrap include with 'extern "C++" {}' +// or compiler give many errors like this: +// error C2733: second C linkage of overloaded function 'wmemchr' not allowed +#ifdef __cplusplus +extern "C" { +#endif +# include +#ifdef __cplusplus +} +#endif + +// Define _W64 macros to mark types changing their size, like intptr_t. +#ifndef _W64 +# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 +# define _W64 __w64 +# else +# define _W64 +# endif +#endif + + +// 7.18.1 Integer types + +// 7.18.1.1 Exact-width integer types + +// Visual Studio 6 and Embedded Visual C++ 4 doesn't +// realize that, e.g. char has the same size as __int8 +// so we give up on __intX for them. +#if (_MSC_VER < 1300) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; +#else + typedef signed __int8 int8_t; + typedef signed __int16 int16_t; + typedef signed __int32 int32_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; +#endif +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; + + +// 7.18.1.2 Minimum-width integer types +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +// 7.18.1.3 Fastest minimum-width integer types +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint64_t uint_fast64_t; + +// 7.18.1.4 Integer types capable of holding object pointers +#ifdef _WIN64 // [ + typedef signed __int64 intptr_t; + typedef unsigned __int64 uintptr_t; +#else // _WIN64 ][ + typedef _W64 signed int intptr_t; + typedef _W64 unsigned int uintptr_t; +#endif // _WIN64 ] + +// 7.18.1.5 Greatest-width integer types +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + + +// 7.18.2 Limits of specified-width integer types + +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 + +// 7.18.2.1 Limits of exact-width integer types +#define INT8_MIN ((int8_t)_I8_MIN) +#define INT8_MAX _I8_MAX +#define INT16_MIN ((int16_t)_I16_MIN) +#define INT16_MAX _I16_MAX +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX _I32_MAX +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX _I64_MAX +#define UINT8_MAX _UI8_MAX +#define UINT16_MAX _UI16_MAX +#define UINT32_MAX _UI32_MAX +#define UINT64_MAX _UI64_MAX + +// 7.18.2.2 Limits of minimum-width integer types +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +// 7.18.2.3 Limits of fastest minimum-width integer types +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST16_MAX INT16_MAX +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT16_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +// 7.18.2.4 Limits of integer types capable of holding object pointers +#ifdef _WIN64 // [ +# define INTPTR_MIN INT64_MIN +# define INTPTR_MAX INT64_MAX +# define UINTPTR_MAX UINT64_MAX +#else // _WIN64 ][ +# define INTPTR_MIN INT32_MIN +# define INTPTR_MAX INT32_MAX +# define UINTPTR_MAX UINT32_MAX +#endif // _WIN64 ] + +// 7.18.2.5 Limits of greatest-width integer types +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +// 7.18.3 Limits of other integer types + +#ifdef _WIN64 // [ +# define PTRDIFF_MIN _I64_MIN +# define PTRDIFF_MAX _I64_MAX +#else // _WIN64 ][ +# define PTRDIFF_MIN _I32_MIN +# define PTRDIFF_MAX _I32_MAX +#endif // _WIN64 ] + +#define SIG_ATOMIC_MIN INT_MIN +#define SIG_ATOMIC_MAX INT_MAX + +#ifndef SIZE_MAX // [ +# ifdef _WIN64 // [ +# define SIZE_MAX _UI64_MAX +# else // _WIN64 ][ +# define SIZE_MAX _UI32_MAX +# endif // _WIN64 ] +#endif // SIZE_MAX ] + +// WCHAR_MIN and WCHAR_MAX are also defined in +#ifndef WCHAR_MIN // [ +# define WCHAR_MIN 0 +#endif // WCHAR_MIN ] +#ifndef WCHAR_MAX // [ +# define WCHAR_MAX _UI16_MAX +#endif // WCHAR_MAX ] + +#define WINT_MIN 0 +#define WINT_MAX _UI16_MAX + +#endif // __STDC_LIMIT_MACROS ] + + +// 7.18.4 Limits of other integer types + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +#define INTMAX_C INT64_C +#define UINTMAX_C UINT64_C + +#endif // __STDC_CONSTANT_MACROS ] + +#else +//sanity for everything else +#include +#endif + +#endif diff --git a/src/libretro/msvc/msvc-2010-360.bat b/src/libretro/msvc/msvc-2010-360.bat new file mode 100644 index 0000000..0e5fde5 --- /dev/null +++ b/src/libretro/msvc/msvc-2010-360.bat @@ -0,0 +1,124 @@ +@echo off + +@echo Setting environment for using Microsoft Visual Studio 2010 x86 tools. + +@call :GetVSCommonToolsDir +@if "%VS100COMNTOOLS%"=="" goto error_no_VS100COMNTOOLSDIR + +@call "%VS100COMNTOOLS%VCVarsQueryRegistry.bat" 32bit No64bit + +@if "%VSINSTALLDIR%"=="" goto error_no_VSINSTALLDIR +@if "%FrameworkDir32%"=="" goto error_no_FrameworkDIR32 +@if "%FrameworkVersion32%"=="" goto error_no_FrameworkVer32 +@if "%Framework35Version%"=="" goto error_no_Framework35Version + +@set FrameworkDir=%FrameworkDir32% +@set FrameworkVersion=%FrameworkVersion32% + +@if not "%WindowsSdkDir%" == "" ( + @set "PATH=%WindowsSdkDir%bin\NETFX 4.0 Tools;%WindowsSdkDir%bin;%PATH%" + @set "INCLUDE=%WindowsSdkDir%include;%INCLUDE%" + @set "LIB=%WindowsSdkDir%lib;%LIB%" +) + +@rem +@rem Root of Visual Studio IDE installed files. +@rem +@set DevEnvDir=%VSINSTALLDIR%Common7\IDE\ + +@rem PATH +@rem ---- +@if exist "%VSINSTALLDIR%Team Tools\Performance Tools" ( + @set "PATH=%VSINSTALLDIR%Team Tools\Performance Tools;%PATH%" +) +@if exist "%ProgramFiles%\HTML Help Workshop" set PATH=%ProgramFiles%\HTML Help Workshop;%PATH% +@if exist "%ProgramFiles(x86)%\HTML Help Workshop" set PATH=%ProgramFiles(x86)%\HTML Help Workshop;%PATH% +@if exist "%VCINSTALLDIR%VCPackages" set PATH=%VCINSTALLDIR%VCPackages;%PATH% +@set PATH=%FrameworkDir%%Framework35Version%;%PATH% +@set PATH=%FrameworkDir%%FrameworkVersion%;%PATH% +@set PATH=%VSINSTALLDIR%Common7\Tools;%PATH% +@if exist "%VCINSTALLDIR%BIN" set PATH=%VCINSTALLDIR%BIN;%PATH% +@set PATH=%DevEnvDir%;%PATH% + +@if exist "%VSINSTALLDIR%VSTSDB\Deploy" ( + @set "PATH=%VSINSTALLDIR%VSTSDB\Deploy;%PATH%" +) + +@if not "%FSHARPINSTALLDIR%" == "" ( + @set "PATH=%FSHARPINSTALLDIR%;%PATH%" +) + +@rem INCLUDE +@rem ------- +@if exist "%VCINSTALLDIR%ATLMFC\INCLUDE" set INCLUDE=%VCINSTALLDIR%ATLMFC\INCLUDE;%INCLUDE% +@if exist "%VCINSTALLDIR%INCLUDE" set INCLUDE=%VCINSTALLDIR%INCLUDE;%INCLUDE% + +@rem LIB +@rem --- +@if exist "%VCINSTALLDIR%ATLMFC\LIB" set LIB=%VCINSTALLDIR%ATLMFC\LIB;%LIB% +@if exist "%VCINSTALLDIR%LIB" set LIB=%VCINSTALLDIR%LIB;%LIB% + +@rem LIBPATH +@rem ------- +@if exist "%VCINSTALLDIR%ATLMFC\LIB" set LIBPATH=%VCINSTALLDIR%ATLMFC\LIB;%LIBPATH% +@if exist "%VCINSTALLDIR%LIB" set LIBPATH=%VCINSTALLDIR%LIB;%LIBPATH% +@set LIBPATH=%FrameworkDir%%Framework35Version%;%LIBPATH% +@set LIBPATH=%FrameworkDir%%FrameworkVersion%;%LIBPATH% + +@goto end + +@REM ----------------------------------------------------------------------- +:GetVSCommonToolsDir +@set VS100COMNTOOLS= +@call :GetVSCommonToolsDirHelper32 HKLM > nul 2>&1 +@if errorlevel 1 call :GetVSCommonToolsDirHelper32 HKCU > nul 2>&1 +@if errorlevel 1 call :GetVSCommonToolsDirHelper64 HKLM > nul 2>&1 +@if errorlevel 1 call :GetVSCommonToolsDirHelper64 HKCU > nul 2>&1 +@exit /B 0 + +:GetVSCommonToolsDirHelper32 +@for /F "tokens=1,2*" %%i in ('reg query "%1\SOFTWARE\Microsoft\VisualStudio\SxS\VS7" /v "10.0"') DO ( + @if "%%i"=="10.0" ( + @SET "VS100COMNTOOLS=%%k" + ) +) +@if "%VS100COMNTOOLS%"=="" exit /B 1 +@SET "VS100COMNTOOLS=%VS100COMNTOOLS%Common7\Tools\" +@exit /B 0 + +:GetVSCommonToolsDirHelper64 +@for /F "tokens=1,2*" %%i in ('reg query "%1\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\SxS\VS7" /v "10.0"') DO ( + @if "%%i"=="10.0" ( + @SET "VS100COMNTOOLS=%%k" + ) +) +@if "%VS100COMNTOOLS%"=="" exit /B 1 +@SET "VS100COMNTOOLS=%VS100COMNTOOLS%Common7\Tools\" +@exit /B 0 + +@REM ----------------------------------------------------------------------- +:error_no_VS100COMNTOOLSDIR +@echo ERROR: Cannot determine the location of the VS Common Tools folder. +@goto end + +:error_no_VSINSTALLDIR +@echo ERROR: Cannot determine the location of the VS installation. +@goto end + +:error_no_FrameworkDIR32 +@echo ERROR: Cannot determine the location of the .NET Framework 32bit installation. +@goto end + +:error_no_FrameworkVer32 +@echo ERROR: Cannot determine the version of the .NET Framework 32bit installation. +@goto end + +:error_no_Framework35Version +@echo ERROR: Cannot determine the .NET Framework 3.5 version. +@goto end + +:end + +msbuild msvc-2010-360.sln /p:Configuration=Release /target:clean +msbuild msvc-2010-360.sln /p:Configuration=Release +exit diff --git a/src/libretro/msvc/msvc-2010-360.sln b/src/libretro/msvc/msvc-2010-360.sln new file mode 100644 index 0000000..9978fb3 --- /dev/null +++ b/src/libretro/msvc/msvc-2010-360.sln @@ -0,0 +1,32 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mvc-2010-360", "msvc-2010-360/msvc-2010-360.vcxproj", "{DB17AF79-EB79-4062-ACA8-F82864712DA0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + CodeAnalysis|Xbox 360 = CodeAnalysis|Xbox 360 + Debug|Xbox 360 = Debug|Xbox 360 + Profile_FastCap|Xbox 360 = Profile_FastCap|Xbox 360 + Profile|Xbox 360 = Profile|Xbox 360 + Release_LTCG|Xbox 360 = Release_LTCG|Xbox 360 + Release|Xbox 360 = Release|Xbox 360 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DB17AF79-EB79-4062-ACA8-F82864712DA0}.CodeAnalysis|Xbox 360.ActiveCfg = CodeAnalysis|Xbox 360 + {DB17AF79-EB79-4062-ACA8-F82864712DA0}.CodeAnalysis|Xbox 360.Build.0 = CodeAnalysis|Xbox 360 + {DB17AF79-EB79-4062-ACA8-F82864712DA0}.Debug|Xbox 360.ActiveCfg = Debug|Xbox 360 + {DB17AF79-EB79-4062-ACA8-F82864712DA0}.Debug|Xbox 360.Build.0 = Debug|Xbox 360 + {DB17AF79-EB79-4062-ACA8-F82864712DA0}.Profile_FastCap|Xbox 360.ActiveCfg = Profile_FastCap|Xbox 360 + {DB17AF79-EB79-4062-ACA8-F82864712DA0}.Profile_FastCap|Xbox 360.Build.0 = Profile_FastCap|Xbox 360 + {DB17AF79-EB79-4062-ACA8-F82864712DA0}.Profile|Xbox 360.ActiveCfg = Profile|Xbox 360 + {DB17AF79-EB79-4062-ACA8-F82864712DA0}.Profile|Xbox 360.Build.0 = Profile|Xbox 360 + {DB17AF79-EB79-4062-ACA8-F82864712DA0}.Release_LTCG|Xbox 360.ActiveCfg = Release_LTCG|Xbox 360 + {DB17AF79-EB79-4062-ACA8-F82864712DA0}.Release_LTCG|Xbox 360.Build.0 = Release_LTCG|Xbox 360 + {DB17AF79-EB79-4062-ACA8-F82864712DA0}.Release|Xbox 360.ActiveCfg = Release|Xbox 360 + {DB17AF79-EB79-4062-ACA8-F82864712DA0}.Release|Xbox 360.Build.0 = Release|Xbox 360 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/libretro/msvc/msvc-2010-360/msvc-2010-360.vcxproj b/src/libretro/msvc/msvc-2010-360/msvc-2010-360.vcxproj new file mode 100644 index 0000000..08c128e --- /dev/null +++ b/src/libretro/msvc/msvc-2010-360/msvc-2010-360.vcxproj @@ -0,0 +1,254 @@ + + + + + CodeAnalysis + Xbox 360 + + + Debug + Xbox 360 + + + Profile + Xbox 360 + + + Profile_FastCap + Xbox 360 + + + Release + Xbox 360 + + + Release_LTCG + Xbox 360 + + + + + + + + + + + + + + + + + + + + + {DB17AF79-EB79-4062-ACA8-F82864712DA0} + Xbox360Proj + vba-next-libretro-360 + + + + StaticLibrary + MultiByte + + + StaticLibrary + MultiByte + + + StaticLibrary + MultiByte + + + StaticLibrary + MultiByte + + + StaticLibrary + MultiByte + true + + + StaticLibrary + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + + + $(OutDir)vba_next_libretro_xdk360$(TargetExt) + + + $(OutDir)vba_next_libretro_xdk360$(TargetExt) + + + $(OutDir)vba_next_libretro_xdk360$(TargetExt) + + + $(OutDir)vba_next_libretro_xdk360$(TargetExt) + + + $(OutDir)vba_next_libretro_xdk360$(TargetExt) + + + $(OutDir)vba_next_libretro_xdk360$(TargetExt) + + + + NotUsing + Level3 + ProgramDatabase + Disabled + false + true + false + $(OutDir)$(ProjectName).pch + MultiThreadedDebug + _DEBUG;_XBOX;_LIB;;__LIBRETRO__;SPEEDHAX;__POWERPC__;__ppc__;WORDS_BIGENDIAN;BLARGG_BIG_ENDIAN;INLINE=_inline;%(PreprocessorDefinitions);_XBOX360;FRONTEND_SUPPORTS_RGB565 + Callcap + $(SolutionDir)\..\libretro;$(SolutionDir)\..\src;$(SolutionDir)\..\src\common;$(SolutionDir)\..\src\gb;$(SolutionDir)\..\src\gba;$(SolutionDir)\..\utils\zlib;%(AdditionalIncludeDirectories) + + + true + + + + + NotUsing + Level4 + ProgramDatabase + Disabled + false + true + AnalyzeOnly + false + $(OutDir)$(ProjectName).pch + MultiThreadedDebug + _DEBUG;_XBOX;_LIB;%(PreprocessorDefinitions);_XBOX360;FRONTEND_SUPPORTS_RGB565 + Callcap + $(SolutionDir)\..\libretro;$(SolutionDir)\..\src;$(SolutionDir)\..\src\common;$(SolutionDir)\..\src\gb;$(SolutionDir)\..\src\gba;$(SolutionDir)\..\utils\zlib;%(AdditionalIncludeDirectories) + + + true + + + + + Level3 + NotUsing + Full + true + false + true + ProgramDatabase + Size + false + $(OutDir)$(ProjectName).pch + MultiThreaded + NDEBUG;_XBOX;PROFILE;_LIB;%(PreprocessorDefinitions);_XBOX360;FRONTEND_SUPPORTS_RGB565 + Callcap + $(SolutionDir)\..\libretro;$(SolutionDir)\..\src;$(SolutionDir)\..\src\common;$(SolutionDir)\..\src\gb;$(SolutionDir)\..\src\gba;$(SolutionDir)\..\utils\zlib;%(AdditionalIncludeDirectories) + + + true + false + xapilib.lib;%(IgnoreSpecificDefaultLibraries) + true + + + + + Level3 + NotUsing + Full + true + false + true + ProgramDatabase + Fastcap + Size + false + $(OutDir)$(ProjectName).pch + MultiThreaded + NDEBUG;_XBOX;PROFILE;FASTCAP;_LIB;%(PreprocessorDefinitions);_XBOX360;FRONTEND_SUPPORTS_RGB565 + $(SolutionDir)\..\libretro;$(SolutionDir)\..\src;$(SolutionDir)\..\src\common;$(SolutionDir)\..\src\gb;$(SolutionDir)\..\src\gba;$(SolutionDir)\..\utils\zlib;%(AdditionalIncludeDirectories) + + + true + false + true + + + + + Level3 + NotUsing + Full + true + true + ProgramDatabase + Speed + false + false + $(OutDir)$(ProjectName).pch + MultiThreaded + NDEBUG;_XBOX;_LIB;__LIBRETRO__;SPEEDHAX;__POWERPC__;__ppc__;WORDS_BIGENDIAN;BLARGG_BIG_ENDIAN;INLINE=_inline;USE_CACHE_PREFETCH;BRANCHLESS_GBA_GFX;%(PreprocessorDefinitions);_XBOX360;FRONTEND_SUPPORTS_RGB565 + $(SolutionDir)\..\libretro;$(SolutionDir)\..\src;$(SolutionDir)\..\src\common;$(SolutionDir)\..\src\gb;$(SolutionDir)\..\src\gba;$(SolutionDir)\..\utils\zlib;%(AdditionalIncludeDirectories) + true + true + + + true + true + true + + + + + Level3 + NotUsing + Full + true + true + ProgramDatabase + Size + false + false + $(OutDir)$(ProjectName).pch + MultiThreaded + NDEBUG;_XBOX;LTCG;_LIB;__LIBRETRO__;USE_CACHE_PREFETCH;BRANCHLESS_GBA_GFX;SPEEDHAX;__POWERPC__;__ppc__;WORDS_BIGENDIAN;BLARGG_BIG_ENDIAN;INLINE=_inline;%(PreprocessorDefinitions);_XBOX360;FRONTEND_SUPPORTS_RGB565 + $(SolutionDir)\..\libretro;$(SolutionDir)\..\src;$(SolutionDir)\..\src\common;$(SolutionDir)\..\src\gb;$(SolutionDir)\..\src\gba;$(SolutionDir)\..\utils\zlib;%(AdditionalIncludeDirectories) + true + + + true + true + true + + + + + + diff --git a/src/libretro/msvc/msvc-2010-360/msvc-2010-360.vcxproj.filters b/src/libretro/msvc/msvc-2010-360/msvc-2010-360.vcxproj.filters new file mode 100644 index 0000000..c93fc42 --- /dev/null +++ b/src/libretro/msvc/msvc-2010-360/msvc-2010-360.vcxproj.filters @@ -0,0 +1,62 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {d19de43b-0337-4673-9b9e-1892d348647d} + + + {becd444c-3df5-4a9e-9885-8a181058095d} + + + + + Header Files\libretro + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files\libretro + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/src/libretro/msvc/msvc-2010.sln b/src/libretro/msvc/msvc-2010.sln new file mode 100644 index 0000000..d7ef2d0 --- /dev/null +++ b/src/libretro/msvc/msvc-2010.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "msvc-2010", "msvc-2010/msvc-2010.vcxproj", "{C461207F-8F4F-4BFB-A90D-25298F37B47D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C461207F-8F4F-4BFB-A90D-25298F37B47D}.Debug|Win32.ActiveCfg = Debug|Win32 + {C461207F-8F4F-4BFB-A90D-25298F37B47D}.Debug|Win32.Build.0 = Debug|Win32 + {C461207F-8F4F-4BFB-A90D-25298F37B47D}.Release|Win32.ActiveCfg = Release|Win32 + {C461207F-8F4F-4BFB-A90D-25298F37B47D}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/libretro/msvc/msvc-2010/libretro.def b/src/libretro/msvc/msvc-2010/libretro.def new file mode 100644 index 0000000..2a71c09 --- /dev/null +++ b/src/libretro/msvc/msvc-2010/libretro.def @@ -0,0 +1,27 @@ +LIBRARY "libretro-prboom msvc2010" +EXPORTS +retro_set_environment +retro_set_video_refresh +retro_set_audio_sample +retro_set_audio_sample_batch +retro_set_input_poll +retro_set_input_state +retro_init +retro_deinit +retro_api_version +retro_get_system_info +retro_get_system_av_info +retro_set_controller_port_device +retro_reset +retro_run +retro_serialize_size +retro_serialize +retro_unserialize +retro_cheat_reset +retro_cheat_set +retro_load_game +retro_load_game_special +retro_unload_game +retro_get_region +retro_get_memory_data +retro_get_memory_size diff --git a/src/libretro/msvc/msvc-2010/msvc-2010.vcxproj b/src/libretro/msvc/msvc-2010/msvc-2010.vcxproj new file mode 100644 index 0000000..828f898 --- /dev/null +++ b/src/libretro/msvc/msvc-2010/msvc-2010.vcxproj @@ -0,0 +1,105 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {C461207F-8F4F-4BFB-A90D-25298F37B47D} + Win32Proj + msvc2010 + vba-next + + + + DynamicLibrary + true + Unicode + + + DynamicLibrary + false + true + Unicode + + + + + + + + + + + + + true + libretro + + + false + libretro + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;MSVC2010_EXPORTS;_CRT_SECURE_NO_WARNINGS;LSB_FIRST;SPEEDHAX=1;__LIBRETRO__;INLINE=_inline;%(PreprocessorDefinitions);FRONTEND_SUPPORTS_RGB565 + $(SolutionDir)\..\src\common;$(SolutionDir)\..\src\gb;$(SolutionDir)\..\src\gba;$(SolutionDir)\..\src\;$(SolutionDir)\..\libretro;$(SolutionDir)\..\utils\zlib;%(AdditionalIncludeDirectories) + + + Windows + true + libretro.def + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;MSVC2010_EXPORTS;_CRT_SECURE_NO_WARNINGS;LSB_FIRST;SPEEDHAX=1;__LIBRETRO__;INLINE=_inline;%(PreprocessorDefinitions);FRONTEND_SUPPORTS_RGB565 + $(SolutionDir)\..\src\common;$(SolutionDir)\..\src\gb;$(SolutionDir)\..\src\gba;$(SolutionDir)\..\src\;$(SolutionDir)\..\libretro;$(SolutionDir)\..\utils\zlib;%(AdditionalIncludeDirectories) + + + Windows + true + true + true + libretro.def + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/libretro/msvc/msvc-2010/msvc-2010.vcxproj.filters b/src/libretro/msvc/msvc-2010/msvc-2010.vcxproj.filters new file mode 100644 index 0000000..543d7d2 --- /dev/null +++ b/src/libretro/msvc/msvc-2010/msvc-2010.vcxproj.filters @@ -0,0 +1,78 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {3ed03eb4-3d22-42ca-b2ab-1058e771db27} + + + {01290b17-a062-4772-8a90-1fb347398419} + + + {9553246f-4fb1-41ce-807d-bd3c3dc6f564} + + + {d5a23baf-0d6e-4d49-a81e-f1320c3186cc} + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files\gba + + + Header Files\gba + + + Header Files\gba + + + Header Files\gba + + + Header Files\libretro + + + Header Files\gba + + + Header Files\common + + + Header Files\common + + + + + Source Files\libretro + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/src/libretro/qnx/playbook/.cproject b/src/libretro/qnx/playbook/.cproject new file mode 100644 index 0000000..9c4fde6 --- /dev/null +++ b/src/libretro/qnx/playbook/.cproject @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/libretro/qnx/playbook/.project b/src/libretro/qnx/playbook/.project new file mode 100644 index 0000000..dad9adc --- /dev/null +++ b/src/libretro/qnx/playbook/.project @@ -0,0 +1,85 @@ + + + vba-next + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + ?name? + + + + org.eclipse.cdt.make.core.append_environment + true + + + org.eclipse.cdt.make.core.autoBuildTarget + all + + + org.eclipse.cdt.make.core.buildArguments + -C../../.. -f Makefile.libretro platform=qnx + + + org.eclipse.cdt.make.core.buildCommand + make + + + org.eclipse.cdt.make.core.cleanBuildTarget + clean + + + org.eclipse.cdt.make.core.contents + org.eclipse.cdt.make.core.activeConfigSettings + + + org.eclipse.cdt.make.core.enableAutoBuild + false + + + org.eclipse.cdt.make.core.enableCleanBuild + true + + + org.eclipse.cdt.make.core.enableFullBuild + true + + + org.eclipse.cdt.make.core.fullBuildTarget + all + + + org.eclipse.cdt.make.core.stopOnError + true + + + org.eclipse.cdt.make.core.useDefaultBuildCmd + false + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + com.qnx.tools.bbt.xml.core.bbtXMLValidationBuilder + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + com.qnx.tools.ide.bbt.core.bbtnature + org.eclipse.cdt.core.ccnature + + diff --git a/src/libretro/scrc32.cpp b/src/libretro/scrc32.cpp new file mode 100644 index 0000000..6c30a9c --- /dev/null +++ b/src/libretro/scrc32.cpp @@ -0,0 +1,88 @@ +#ifndef _S_CRC32_H +#define _S_CRC32_H + +#ifdef __cplusplus +extern "C" { +#endif + +static const unsigned long crc_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; + +#define DO1_CRC32(buf) crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8); +#define DO2_CRC32(buf) DO1_CRC32(buf); DO1_CRC32(buf); +#define DO4_CRC32(buf) DO2_CRC32(buf); DO2_CRC32(buf); +#define DO8_CRC32(buf) DO4_CRC32(buf); DO4_CRC32(buf); + +unsigned long crc32(unsigned long crc, const unsigned char *buf, unsigned int len) +{ + if (buf == 0) return 0L; + crc = crc ^ 0xffffffffL; + while (len >= 8) + { + DO8_CRC32(buf); + len -= 8; + } + if (len) do { + DO1_CRC32(buf); + } while (--len); + return crc ^ 0xffffffffL; +} + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/qt/Config.cpp b/src/qt/Config.cpp new file mode 100644 index 0000000..fc2e3a9 --- /dev/null +++ b/src/qt/Config.cpp @@ -0,0 +1,8 @@ +#include "Config.h" +bool Config::mute() { + return m_mute; +} +void Config::setMute(bool v) { + m_mute = v; + emit muteChanged(); +} diff --git a/src/qt/Config.h b/src/qt/Config.h new file mode 100644 index 0000000..eb4b601 --- /dev/null +++ b/src/qt/Config.h @@ -0,0 +1,17 @@ +#ifndef QVBAM_QT_CONFIG +#define QVBAM_QT_CONFIG + +#include + +class Config : public QObject { + Q_OBJECT + Q_PROPERTY(bool mute READ mute WRITE setMute NOTIFY muteChanged) +public: + bool mute(); + void setMute(bool); +signals: + void muteChanged(); +private: + bool m_mute; +}; +#endif diff --git a/src/qt/CustomQQuickView.cpp b/src/qt/CustomQQuickView.cpp new file mode 100644 index 0000000..67f6006 --- /dev/null +++ b/src/qt/CustomQQuickView.cpp @@ -0,0 +1,10 @@ +#include "CustomQQuickView.h" + +extern Window * w; +bool CustomQQuickView::event(QEvent *e) { + if (e->type() == QEvent::Close) { + qDebug() << "Close event"; + delete w; + } + return QQuickView::event(e); +} diff --git a/src/qt/CustomQQuickView.h b/src/qt/CustomQQuickView.h new file mode 100644 index 0000000..43b572c --- /dev/null +++ b/src/qt/CustomQQuickView.h @@ -0,0 +1,7 @@ +#include +#include +#include "window.h" +class CustomQQuickView : public QQuickView { +public: + bool event(QEvent * e); +}; diff --git a/src/qt/FilesModel.cpp b/src/qt/FilesModel.cpp new file mode 100644 index 0000000..03b365f --- /dev/null +++ b/src/qt/FilesModel.cpp @@ -0,0 +1,44 @@ +#include "FilesModel.h" + +#define ROM_PATH "/.local/share/emanuelesorce.qvbam/roms" + +QStringList FilesModel::files(){ + QDir qdir(QDir::homePath() + ROM_PATH); + if (!qdir.exists()) { + qdir.mkpath(qdir.absolutePath()); + return QStringList(); + } else { + QStringList qsl = qdir.entryList(); + int i = 0; + while (i < qsl.length()) { + if (qsl[i] == "." || qsl[i] == "..") { + qsl.removeAt(i); + } else { + i++; + } + } + qDebug() << "return " << qsl; + return qsl; + } + +} + + +void FilesModel::importFiles(QString fullPath) { + QFile file(fullPath); + QFileInfo fileInfo(file.fileName()); + QString target = QDir::homePath() + ROM_PATH + "/" + fileInfo.fileName(); + if (file.exists()) { + file.copy(target); + } + emit filesChanged(); +} + +void FilesModel::removeFile(QString fileName) { + QFile f(QDir::homePath() + ROM_PATH + "/" + fileName); + qDebug() << "remove " << fileName << " " << f.exists(); + if (f.exists()) { + f.remove(); + emit filesChanged(); + } +} diff --git a/src/qt/FilesModel.h b/src/qt/FilesModel.h new file mode 100644 index 0000000..aec27f5 --- /dev/null +++ b/src/qt/FilesModel.h @@ -0,0 +1,15 @@ +#include +#include +#include +#include +#include +class FilesModel : public QObject{ + Q_OBJECT + Q_PROPERTY(QStringList files READ files NOTIFY filesChanged) +public: + QStringList files(); + Q_INVOKABLE void importFiles(QString fullPath); + Q_INVOKABLE void removeFile(QString fileName); +signals: + void filesChanged(); +}; diff --git a/src/qt/QGameSlot.cpp b/src/qt/QGameSlot.cpp new file mode 100644 index 0000000..137a6b4 --- /dev/null +++ b/src/qt/QGameSlot.cpp @@ -0,0 +1,19 @@ +#include "QGameSlot.h" + +bool QGameSlot::isEmpty() { + return m_isEmpty; +} + +void QGameSlot::setIsEmpty(bool v) { + m_isEmpty = v; + emit isEmptyChanged(); +} + +QString QGameSlot::time() { + return m_time; +} + +void QGameSlot::setTime(QString t) { + m_time = t; + emit timeChanged(); +} diff --git a/src/qt/QGameSlot.h b/src/qt/QGameSlot.h new file mode 100644 index 0000000..a4a9407 --- /dev/null +++ b/src/qt/QGameSlot.h @@ -0,0 +1,21 @@ +#include + +#ifndef QGAMESLOT +#define QGAMESLOT +class QGameSlot : public QObject{ + Q_OBJECT + Q_PROPERTY(bool isEmpty READ isEmpty WRITE setIsEmpty NOTIFY isEmptyChanged) + Q_PROPERTY(QString time READ time WRITE setTime NOTIFY timeChanged) +public: + bool isEmpty(); + void setIsEmpty(bool); + QString time(); + void setTime(QString); +signals: + void isEmptyChanged(); + void timeChanged(); +private: + bool m_isEmpty; + QString m_time; +}; +#endif diff --git a/src/qt/ScreenArea.cpp b/src/qt/ScreenArea.cpp new file mode 100644 index 0000000..9af077f --- /dev/null +++ b/src/qt/ScreenArea.cpp @@ -0,0 +1,19 @@ +#include "ScreenArea.h" + +extern u8 *pix; +extern Window * w; + +ScreenArea::ScreenArea() { + m_image = new QImage(pix, 241, 160, QImage::Format_RGB32); + m_sourceTarget = new QRectF (0, 0, 241, 160); +} + +ScreenArea::~ScreenArea() { + delete m_image; + delete m_sourceTarget; +} + +void ScreenArea::paint(QPainter *painter) { + QRectF target(0, 0, this->width(), this->height()); + painter->drawImage(target, *m_image, *m_sourceTarget); +} diff --git a/src/qt/ScreenArea.h b/src/qt/ScreenArea.h new file mode 100644 index 0000000..a4f9745 --- /dev/null +++ b/src/qt/ScreenArea.h @@ -0,0 +1,16 @@ +#include +#include +#include +#include "../common/Types.h" +#include "window.h" +class ScreenArea : public QQuickPaintedItem { + Q_OBJECT +public: + ScreenArea(); + ~ScreenArea(); + void paint(QPainter *painter); + +private: + QImage * m_image; + QRectF * m_sourceTarget; +}; diff --git a/src/qt/ScreenAreaOpenGL.cpp b/src/qt/ScreenAreaOpenGL.cpp new file mode 100644 index 0000000..8b43e44 --- /dev/null +++ b/src/qt/ScreenAreaOpenGL.cpp @@ -0,0 +1,26 @@ +#include "ScreenAreaOpenGL.h" +#include +#include +#include +#include "../common/Types.h" +#include "CustomQQuickView.h" +#define NOISE_SIZE 160 +extern CustomQQuickView * view; +extern u8 * pix; +extern Window * w; +Squircle::Squircle() +{ + setFlag(ItemHasContents, true); +} + +QSGNode * Squircle::updatePaintNode(QSGNode * oldnode, UpdatePaintNodeData *){ + QSGSimpleTextureNode *node= static_cast(oldnode); + if(!node) { + node = new QSGSimpleTextureNode(); + node->setRect(boundingRect()); + } + QImage image(pix, 241, 160, QImage::Format_RGB32); + QSGTexture * t = view->createTextureFromImage(image); + node->setTexture(t); + return node; +} diff --git a/src/qt/ScreenAreaOpenGL.h b/src/qt/ScreenAreaOpenGL.h new file mode 100644 index 0000000..8e3a126 --- /dev/null +++ b/src/qt/ScreenAreaOpenGL.h @@ -0,0 +1,18 @@ +#ifndef SQUIRCLE_H +#define SQUIRCLE_H + +#include +#include +#include +#include +class Squircle : public QQuickItem +{ + Q_OBJECT + +public: + Squircle(); + virtual QSGNode * updatePaintNode(QSGNode *node, UpdatePaintNodeData *); + +}; + +#endif // SQUIRCLE_H diff --git a/src/qt/SoundBuffer.cpp b/src/qt/SoundBuffer.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/qt/SoundQt.cpp b/src/qt/SoundQt.cpp new file mode 100644 index 0000000..ab38c32 --- /dev/null +++ b/src/qt/SoundQt.cpp @@ -0,0 +1,59 @@ +#include "SoundQt.h" + +extern int emulating; +extern bool speedup; +// Hold up to 100 ms of data in the ring buffer + +SoundBuffer::SoundBuffer(int samples) { + _rbuf.reset(samples * 2 * _delay); +} + +qint64 SoundBuffer::readData(char *data, qint64 length) { + u16 * stream = (u16 *)data; + int readLength = std::min(static_cast(length) / 2, _rbuf.used()); + _rbuf.read(stream, readLength); + return readLength * 2; +} + +qint64 SoundBuffer::writeData(const char* data, qint64 length) { + u16 *finalWave = (u16 *)data; + int writeLength = std::min(static_cast(length) / 2, _rbuf.avail()); + _rbuf.write(finalWave, writeLength); + return writeLength * 2; +} + +bool SoundQt::init(long sampleRate) { + buffer = new SoundBuffer(sampleRate); + buffer->open(QBuffer::ReadWrite); + + QAudioFormat format; + format.setSampleRate(sampleRate); + format.setCodec("audio/pcm"); + format.setChannelCount(2); + format.setSampleSize(16); + format.setByteOrder(QAudioFormat::LittleEndian); + format.setSampleType(QAudioFormat::SignedInt); + QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice()); + if (!info.isFormatSupported(format)) { + qWarning() << "Raw audio format not supported by backend, cannot play audio."; + return false; + } + audio = new QAudioOutput(format); + audio->start(buffer); + return true; +} + +void SoundQt::pause() { + audio->suspend(); +} + +void SoundQt::reset() { +} + +void SoundQt::resume() { + audio->resume(); +} + +void SoundQt::write(u16 * finalWave, int length) { + buffer->write((char *)finalWave, length); +} diff --git a/src/qt/SoundQt.h b/src/qt/SoundQt.h new file mode 100644 index 0000000..5510fa1 --- /dev/null +++ b/src/qt/SoundQt.h @@ -0,0 +1,35 @@ +#include "../common/SoundDriver.h" +#include +#include +#include +#include +#include +#include +#include +#include "../common/RingBuffer.h" +class SoundBuffer : public QIODevice { +public: + SoundBuffer(int samples); + virtual qint64 readData(char*, qint64); + virtual qint64 writeData(const char*, qint64); +private: + const float _delay = 0.11f; + + RingBuffer _rbuf; +}; + +class SoundQt : public QObject, public SoundDriver { + Q_OBJECT +public: + virtual bool init(long sampleRate); + virtual void pause(); + virtual void reset(); + virtual void resume(); + virtual void write(u16 * finalWave, int length); + +//public slots: +// void handleStateChanged(QAudio::State); +private: + QAudioOutput * audio; + SoundBuffer * buffer; +}; diff --git a/src/qt/main.cpp b/src/qt/main.cpp new file mode 100644 index 0000000..0247b3f --- /dev/null +++ b/src/qt/main.cpp @@ -0,0 +1,42 @@ +#include +#include +#include +#include +#include +#include +#include +#include "ScreenArea.h" +#include "window.h" +#include "CustomQQuickView.h" +#include "ScreenAreaOpenGL.h" +#include "FilesModel.h" +#include "QGameSlot.h" +Window * w; +CustomQQuickView * view; + +void sig_handler(int sig) { + qDebug() << "SIGINT CAUGHT"; + w->vOnFileClose(); + exit(0); +} +int main(int argc, char ** argv) { + signal(SIGINT, sig_handler); + signal(SIGKILL, sig_handler); + signal(SIGTERM, sig_handler); + QGuiApplication a(argc, argv); + view = new CustomQQuickView(); + + w = new Window(); + FilesModel romsModel; + view->setResizeMode(QQuickView::SizeRootObjectToView); + view->engine()->rootContext()->setContextProperty("romsModel", &romsModel); + view->engine()->rootContext()->setContextProperty("iwindow", w); + qmlRegisterType("QVBA", 0, 1, "ScreenArea"); + qmlRegisterUncreatableType("QVBA", 0, 1, "WINDOW", "hehe"); + qmlRegisterUncreatableType("QVBA", 0, 1, "QGameSlot", "hehe"); + + + view->setSource(QUrl::fromLocalFile("qml/main.qml")); + view->show(); + return a.exec(); +} diff --git a/src/qt/qml/ContentPickerDialog.qml b/src/qt/qml/ContentPickerDialog.qml new file mode 100644 index 0000000..5092d0b --- /dev/null +++ b/src/qt/qml/ContentPickerDialog.qml @@ -0,0 +1,84 @@ +/* + * Copied from webbrowser app + * Copyright 2014 Canonical Ltd. + * + * This file is part of webbrowser-app. + * + * webbrowser-app is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 3. + * + * webbrowser-app is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ + +import QtQuick 2.4 +import Ubuntu.Components 1.3 +import Ubuntu.Components.Popups 1.3 as Popups +import Ubuntu.Content 1.3 + +Component { + Popups.PopupBase { + id: picker + property var activeTransfer + property var selectedItems + + property color oldColor + + Rectangle { + anchors.fill: parent + + ContentTransferHint { + anchors.fill: parent + activeTransfer: picker.activeTransfer + } + + ContentPeerPicker { + id: peerPicker + anchors.fill: parent + visible: true + contentType: ContentType.All + handler: ContentHandler.Source + onPeerSelected: { + peer.contentType = ContentType.All + peer.selectionType = ContentTransfer.Single + picker.activeTransfer = peer.request() + stateChangeConnection.target = picker.activeTransfer + } + + onCancelPressed: { + mainView.backgroundColor = picker.oldColor + PopupUtils.close(picker) + } + } + } + + Connections { + id: stateChangeConnection + onStateChanged: { + if (picker.activeTransfer.state === ContentTransfer.Charged) { + romsModel.importFiles(String(picker.activeTransfer.items[0].url).replace("file://", "")) + closeTimer.start() + } + } + } + Timer { + id: closeTimer + interval: 1000 + repeat: false + onTriggered: { + mainView.backgroundColor = picker.oldColor + PopupUtils.close(picker) + } + } + Component.onCompleted: { + picker.oldColor = mainView.backgroundColor; + mainView.backgroundColor = "white" + } + } +} diff --git a/src/qt/qml/PlayPage.qml b/src/qt/qml/PlayPage.qml new file mode 100644 index 0000000..bc1a3fd --- /dev/null +++ b/src/qt/qml/PlayPage.qml @@ -0,0 +1,293 @@ +import QtQuick 2.4 +import QVBA 0.1 +import Ubuntu.Components 1.3 +import Ubuntu.Components.ListItems 1.3 as ListItems +import Ubuntu.Components.Popups 1.3 +Page { + property string slotAction + property bool showPad: { + var screenheight = Math.min(width / 240, height / 160) * 160 + if (height - screenheight > buttonBackground.height) { + return true; + } else { + return false; + } + } + Rectangle { + id: screenContainer + anchors { + top: parent.top + bottom: showPad ? buttonBackground.top : parent.bottom + } + width: parent.width + color: "black" + ScreenArea { + anchors.centerIn: parent + property int scale : settings.displayScale == 0 ? Math.min(parent.width / 240, parent.height / 160): settings.displayScale + width: 241 * scale + height: 160 * scale + id: sa + Connections { + target: iwindow + onSDrawScreen: { + sa.update() + } + } + } + Label { + id: speedLabel + anchors { + right: parent.right + top: parent.top + } + visible: settings.configShowSpeed + text: "Speed: " + iwindow.speed + "%" + } + Label { + anchors { + right: parent.right + top: speedLabel.bottom + } + visible: settings.configShowFrameSkip + text: "Frame Skip: " + iwindow.frameSkip + } + } + Rectangle { + id: buttonBackground + width: parent.width + height: units.gu(28) + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + color: showPad ? "#4E2865" : "transparent" + opacity: showPad ? 1.0 : 0.5 + //[startx, starty, endx, endy, key] + //position in absolute value + //keyname, x,y,width, height left or right, key, image + property var buttonsPositions: [ + ["L", 2.25, 21.25, 11, 5, "left", Qt.Key_A, "./img/LButton.png"], + ["R", 2.25, 21.25, 11, 5, "right", Qt.Key_S, "./img/RButton.png"], + ["A", 2.5, 5.5, 6.5, 6.5, "right", Qt.Key_Z, "./img/AButton.png"], + ["B", 9, 3, 6.5, 6.5, "right", Qt.Key_X, "./img/BButton.png"], + ["Start", 19, 3, 3.5, 3.5, "right", Qt.Key_Return, "./img/circleButton.png"], + ["Select", 24.5, 3, 3.5, 3.5, "right", Qt.Key_Backspace, "./img/circleButton.png"], + ["Direct", 3.5, 5, 12, 12, "left", 0, "./img/directionButtons.png"] + ] + property var directRatio: 0.31; + property var lastKeyStatus: [false, false, false, false, false, false, false, false, false, false] + property var buttonKeys: [Qt.Key_A, Qt.Key_S, Qt.Key_Z, Qt.Key_X, Qt.Key_Return, Qt.Key_Backspace, Qt.Key_Up, Qt.Key_Left, Qt.Key_Down, Qt.Key_Right] + + MultiPointTouchArea { + anchors.fill: parent + onTouchUpdated: { + for (var i in buttonBackground.lastKeyStatus) { + var pressed = false; + var buttonKey = buttonBackground.buttonKeys[i]; + for (var j in touchPoints) { + if (i < 6) { + if (buttonBackground.buttonsPositions[i][5] == "left") { + if (touchPoints[j].x > units.gu(buttonBackground.buttonsPositions[i][1]) + && touchPoints[j].x < units.gu(buttonBackground.buttonsPositions[i][1]) + units.gu(buttonBackground.buttonsPositions[i][3]) + && touchPoints[j].y > height - units.gu(buttonBackground.buttonsPositions[i][2]) - units.gu(buttonBackground.buttonsPositions[i][4]) + && touchPoints[j].y < height - units.gu(buttonBackground.buttonsPositions[i][2])) { + pressed = true + } + } else { + //should be right + if (touchPoints[j].x > width - units.gu(buttonBackground.buttonsPositions[i][1]) - units.gu(buttonBackground.buttonsPositions[i][3]) + && touchPoints[j].x < width - units.gu(buttonBackground.buttonsPositions[i][1]) + && touchPoints[j].y > height - units.gu(buttonBackground.buttonsPositions[i][2]) - units.gu(buttonBackground.buttonsPositions[i][4]) + && touchPoints[j].y < height - units.gu(buttonBackground.buttonsPositions[i][2])) { + pressed = true; + } + } + } else { + //Direction Button + var directButtonWidth = units.gu(buttonBackground.buttonsPositions[6][3]); + var directButtonHeight = units.gu(buttonBackground.buttonsPositions[6][4]); + var directButtonX = units.gu(buttonBackground.buttonsPositions[6][1]); + var directButtonY = height - units.gu(buttonBackground.buttonsPositions[6][1]) - directButtonHeight; + var directButtonPosition = [ + [directButtonX + buttonBackground.directRatio * directButtonWidth, + directButtonX + (1 - buttonBackground.directRatio) * directButtonWidth, + directButtonY, + directButtonY + buttonBackground.directRatio * directButtonHeight, + Qt.Key_Up], + [directButtonX, + directButtonX + buttonBackground.directRatio * directButtonWidth, + directButtonY + buttonBackground.directRatio * directButtonHeight, + directButtonY + (1 - buttonBackground.directRatio) * directButtonHeight, + Qt.Key_Left], + [directButtonX + buttonBackground.directRatio * directButtonWidth, + directButtonX + (1 - buttonBackground.directRatio) * directButtonWidth, + directButtonY + (1 - buttonBackground.directRatio) * directButtonHeight, + directButtonY + directButtonHeight, + Qt.Key_Down], + [directButtonX + (1 - buttonBackground.directRatio) * directButtonWidth, + directButtonX + directButtonWidth, + directButtonY + buttonBackground.directRatio * directButtonHeight, + directButtonY + (1 - buttonBackground.directRatio) * directButtonHeight, + Qt.Key_Right] + ] + if (touchPoints[j].x > directButtonPosition[i - 6][0] + && touchPoints[j].x < directButtonPosition[i - 6][1] + && touchPoints[j].y > directButtonPosition[i - 6][2] + && touchPoints[j].y < directButtonPosition[i - 6][3]) { + pressed = true; + } + } + } + if (pressed != buttonBackground.lastKeyStatus[i]) { + if (pressed) { + iwindow.on_key_press_event(buttonKey); + } else { + iwindow.on_key_release_event(buttonKey); + } + buttonBackground.lastKeyStatus[i] = pressed; + } + } + } + + // MouseArea { + // anchors.fill: parent + // onClicked: { + // console.log(mouse.x / units.gu(1) + " " + (width - mouse.x) / units.gu(1) + " " + (height - mouse.y) / units.gu(1)) + // } + // } + } + + Repeater { + model: buttonBackground.buttonsPositions + delegate: Image { + source: model.modelData[7] + anchors { + bottom: parent.bottom + bottomMargin: units.gu(model.modelData[2]) + left: model.modelData[5] == "left" ? parent.left : undefined + right: model.modelData[5] == "right" ? parent.right : undefined + leftMargin: model.modelData[5] == "left" ? units.gu(model.modelData[1]) : undefined + rightMargin: model.modelData[5] == "right" ? units.gu(model.modelData[1]) : undefined + } + width: units.gu(model.modelData[3]) + height: units.gu(model.modelData[4]) + } + } + } + + MouseArea { + id: toolbarMask + anchors { + top: parent.top + left: parent.left + right: parent.right + bottom: tools.top + } + enabled: tools.opened + onClicked: tools.close() + } + + Icon { + name: "contextual-menu" + opacity: 0.5 + width: units.gu(5) + height: width + x: units.gu(2) + y: units.gu(2) + MouseArea { + anchors.fill: parent + onClicked: tools.open() + } + } + + Panel { + id: tools + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + height: units.gu(8) + Component.onCompleted: { + tools.open(); + closeToolbarTimer.start(); + } + Rectangle { + anchors.fill: parent + color: "white" + } + + Timer { + id: closeToolbarTimer + interval: 2000 + repeat: false + onTriggered: tools.close() + } + + ToolbarItems { + back: ToolbarButton { + action: Action { + text: "Close" + iconSource: "./img/close.svg" + onTriggered: { + pageStack.pop(); + iwindow.vOnFileClose(); + } + } + } + ToolbarButton { + action: Action { + text: "Settings" + iconSource: "./img/settings.svg" + onTriggered: { + pageStack.push(Qt.resolvedUrl("SettingPage.qml")); + } + } + } + + ToolbarButton { + action: Action { + text: "Save Slot" + iconSource: "./img/save.svg" + onTriggered: { + slotAction = "save"; + PopupUtils.open(slotSheet) + } + } + } + ToolbarButton { + action: Action { + text: "Load Slot" + iconSource: "./img/keyboard-caps.svg" + onTriggered: { + slotAction = "load"; + PopupUtils.open(slotSheet) + } + } + } + } + } + Component { + id: slotSheet + DefaultSheet { + id: sheet + title: slotAction + doneButton: false + ListView { + anchors.fill: parent + model: iwindow.gameSlot + clip: true + delegate: ListItems.Standard { + text: model.isEmpty ? model.time : "" + model.time + "" + onClicked: { + if (slotAction === "save") { + iwindow.vOnSaveGame(model.index + 1); + } else if (slotAction === "load") { + iwindow.vOnLoadGame(model.index + 1); + } + PopupUtils.close(sheet); + tools.close(); + } + } + } + } + } +} diff --git a/src/qt/qml/SettingPage.qml b/src/qt/qml/SettingPage.qml new file mode 100644 index 0000000..defed5d --- /dev/null +++ b/src/qt/qml/SettingPage.qml @@ -0,0 +1,54 @@ +import QtQuick 2.4 +import Ubuntu.Components 1.3 +import Ubuntu.Components.ListItems 1.3 as ListItem +Page { + title: "Settings" + + Flickable { + id: flickable2 + anchors.fill: parent + contentHeight: column.height + Column { + id: column + width: parent.width + ListItem.Standard { + text: "Enable Sound" + control: Switch { + id: enableSoundSwitch + checked: settings.enableSound + onCheckedChanged: { + settings.enableSound = checked; + settings.save(); + } + } + } + ListItem.Standard { + text: "Show speed" + control: Switch { + checked: settings.configShowSpeed + onCheckedChanged: { + settings.configShowSpeed = checked; + settings.save(); + } + } + } + ListItem.Standard { + text: "Show frame skip" + control: Switch { + checked: settings.configShowFrameSkip + onCheckedChanged: { + settings.configShowFrameSkip = checked; + settings.save(); + } + } + } + ListItem.ItemSelector { + text: i18n.tr("Scale") + model: ["auto", "x1", "x2", "x3", "x4", "x5", "x6"] + selectedIndex: settings.displayScale; + onSelectedIndexChanged: settings.displayScale = selectedIndex; + } + } + } + flickable: flickable2 +} diff --git a/src/qt/qml/Settings.qml b/src/qt/qml/Settings.qml new file mode 100644 index 0000000..5d36b92 --- /dev/null +++ b/src/qt/qml/Settings.qml @@ -0,0 +1,46 @@ +import QtQuick 2.4 +import U1db 1.0 as U1db +Item { + property bool enableSound: true + property bool configShowSpeed: true; + property bool configShowFrameSkip: true; + property int displayScale: 0; //0 for automatic + Component.onCompleted: load(); + onEnableSoundChanged: { + iwindow.config.mute = !enableSound + } + onDisplayScaleChanged: save(); + U1db.Database { + id: aDatabase + path: "aU1DbDatabase" + } + + U1db.Document { + id: aDocument + database: aDatabase + docId: 'settings' + create: true + defaults: { + enableSound: true + configShowSpeed: true + configShowFrameSkip: true + } + } + function load() { + var settings = aDocument.contents; + if (settings.enableSound !== undefined) { + enableSound = settings.enableSound; + configShowSpeed = settings.configShowSpeed; + configShowFrameSkip = settings.configShowFrameSkip; + displayScale = settings.displayScale; + } + } + function save() { + var settings = aDocument.contents; + settings.enableSound = enableSound; + settings.configShowSpeed = configShowSpeed; + settings.configShowFrameSkip = configShowFrameSkip; + settings.displayScale = displayScale; + aDocument.contents = settings; + } +} diff --git a/src/qt/qml/TopPage.qml b/src/qt/qml/TopPage.qml new file mode 100644 index 0000000..8c4904c --- /dev/null +++ b/src/qt/qml/TopPage.qml @@ -0,0 +1,48 @@ +import QtQuick 2.4 +import Ubuntu.Components 1.3 +import Ubuntu.Components.ListItems 1.3 as ListItem +import Ubuntu.Components.Popups 1.3 +Tabs { + id: tabs + Tab { + id: romTab + title: "Load Roms" + page: Page { + head.actions: [ + Action { + iconName: "add" + text: i18n.tr("Import") + onTriggered: PopupUtils.open(contentPickerDialog) + } + ] + ListView { + anchors.fill: parent + model: romsModel.files + delegate: ListItem.Standard { + text: model.modelData + removable: true; + confirmRemoval: true; + onClicked: { + iwindow.bLoadRomInQML(model.modelData) + pageStack.push(Qt.resolvedUrl("PlayPage.qml")) + } + onItemRemoved: { + romsModel.removeFile(model.modelData) + } + } + } + } + } + + Tab { + id: settingTab + title: "Settings" + page: SettingPage { + + } + } + + ContentPickerDialog { + id: contentPickerDialog + } +} diff --git a/src/qt/qml/img/AButton.png b/src/qt/qml/img/AButton.png new file mode 100644 index 0000000..8a19974 Binary files /dev/null and b/src/qt/qml/img/AButton.png differ diff --git a/src/qt/qml/img/BButton.png b/src/qt/qml/img/BButton.png new file mode 100644 index 0000000..431d0a9 Binary files /dev/null and b/src/qt/qml/img/BButton.png differ diff --git a/src/qt/qml/img/LButton.png b/src/qt/qml/img/LButton.png new file mode 100644 index 0000000..75c026c Binary files /dev/null and b/src/qt/qml/img/LButton.png differ diff --git a/src/qt/qml/img/RButton.png b/src/qt/qml/img/RButton.png new file mode 100644 index 0000000..274dde9 Binary files /dev/null and b/src/qt/qml/img/RButton.png differ diff --git a/src/qt/qml/img/circleButton.png b/src/qt/qml/img/circleButton.png new file mode 100644 index 0000000..0def29a Binary files /dev/null and b/src/qt/qml/img/circleButton.png differ diff --git a/src/qt/qml/img/close.svg b/src/qt/qml/img/close.svg new file mode 100644 index 0000000..fc8bb09 --- /dev/null +++ b/src/qt/qml/img/close.svg @@ -0,0 +1,19 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/src/qt/qml/img/directionButtons.png b/src/qt/qml/img/directionButtons.png new file mode 100644 index 0000000..85b8c6f Binary files /dev/null and b/src/qt/qml/img/directionButtons.png differ diff --git a/src/qt/qml/img/keyboard-caps.svg b/src/qt/qml/img/keyboard-caps.svg new file mode 100644 index 0000000..4e5011b --- /dev/null +++ b/src/qt/qml/img/keyboard-caps.svg @@ -0,0 +1,114 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/src/qt/qml/img/media-playback-pause.svg b/src/qt/qml/img/media-playback-pause.svg new file mode 100644 index 0000000..3d8bf7f --- /dev/null +++ b/src/qt/qml/img/media-playback-pause.svg @@ -0,0 +1,20 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/src/qt/qml/img/media-playback-start.svg b/src/qt/qml/img/media-playback-start.svg new file mode 100644 index 0000000..cc0fe71 --- /dev/null +++ b/src/qt/qml/img/media-playback-start.svg @@ -0,0 +1,98 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/src/qt/qml/img/pad.png b/src/qt/qml/img/pad.png new file mode 100644 index 0000000..0f17013 Binary files /dev/null and b/src/qt/qml/img/pad.png differ diff --git a/src/qt/qml/img/save.svg b/src/qt/qml/img/save.svg new file mode 100644 index 0000000..4fb31c1 --- /dev/null +++ b/src/qt/qml/img/save.svg @@ -0,0 +1,110 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/src/qt/qml/img/settings.svg b/src/qt/qml/img/settings.svg new file mode 100644 index 0000000..fb738ba --- /dev/null +++ b/src/qt/qml/img/settings.svg @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + diff --git a/src/qt/qml/main.qml b/src/qt/qml/main.qml new file mode 100644 index 0000000..416383a --- /dev/null +++ b/src/qt/qml/main.qml @@ -0,0 +1,44 @@ +import QtQuick 2.4 +import QVBA 0.1 +import Ubuntu.Components 1.3 + +MainView { + id: mainView + width: units.gu(48) + height: units.gu(70) + backgroundColor: UbuntuColors.porcelain + automaticOrientation: true + applicationName: "emanuelesorce.qvbam" + PageStack { + id: pageStack + Component.onCompleted: { + push(Qt.resolvedUrl("TopPage.qml")) + } + } + + Settings { + id: settings + } + + PlayPage { + id: playPage + visible: false + } + focus: true + Keys.onPressed: { + iwindow.on_key_press_event(event.key); + } + Keys.onReleased: { + iwindow.on_key_release_event(event.key); + } + Connections { + target: Qt.application + onActiveChanged: { + if (Qt.application.active) { + iwindow.paused = false; + } else { + iwindow.paused = true; + } + } + } +} diff --git a/src/qt/system.cpp b/src/qt/system.cpp new file mode 100644 index 0000000..560a377 --- /dev/null +++ b/src/qt/system.cpp @@ -0,0 +1,172 @@ +#include "system.h" + +int systemRedShift; +int systemGreenShift; +int systemBlueShift; +int systemColorDepth; +int systemVerbose; +int systemSaveUpdateCounter; +int systemFrameSkip; +u32 systemColorMap32[0x10000]; +u16 systemColorMap16[0x10000]; +u16 systemGbPalette[24]; + + +int emulating; +int RGB_LOW_BITS_MASK; +extern Window * w; +Window * GUI() { + return w; +} + +void systemMessage(int _iId, const char * _csFormat, ...) +{ + qDebug() << "TODO:: systemMessage" << _csFormat; + // va_list args; + // va_start(args, _csFormat); + + // GUI()->vPopupErrorV(_(_csFormat), args); + + // va_end(args); +} + +void debuggerBreakOnWrite(u32 address, u32 oldvalue, u32 value, int size, int t) +{ +} + +void log(const char *defaultMsg, ...) +{ + qDebug() << "TODO:: log"; + + // 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); +} + +int systemGetSensorX() +{ + return 0; +} + +int systemGetSensorY() +{ + return 0; +} + +void debuggerOutput(const char *, u32) +{ +} + +void debuggerSignal(int, int) +{ +} + +void (*dbgOutput)(const char *, u32) = debuggerOutput; +void (*dbgSignal)(int, int) = debuggerSignal; + +bool systemCanChangeSoundQuality() +{ + return true; +} + +SoundDriver * systemSoundInit() +{ + soundShutdown(); + + return new SoundSDL(); + // I tried to use QAudioOutput. It still has problem in confinement. There is a pluseaudio server that might solve the problem. Waiting for it to publish. + // Anyway, currently just not have sound in this version. +// return new SoundQt(); +} + +void systemOnSoundShutdown() +{ +} + +//int missingSound = 0; +//char * emptySound = 0; +//int emptySoundLength = 0; +//extern SoundDriver * soundDriver; +void systemOnWriteDataToSoundBuffer(const u16 * finalWave, int length) +{ +// if (emptySoundLength != 0 && emptySoundLength != length) { +// delete emptySound; +// emptySound = 0; +// } +// if (emptySound == 0) { +// emptySound = new char[length]; +// emptySoundLength = length; +// memset(emptySound, 0, emptySoundLength); +// } +// while (missingSound > 0) { +// soundDriver->write((u16 *)emptySound, emptySoundLength); +// missingSound --; +// } +} + +bool systemReadJoypads() +{ + return true; +} + +u32 systemReadJoypad(int joy) +{ + return inputReadJoypad(joy); +} + +u32 systemGetClock() +{ + return QTime::currentTime().msecsSinceStartOfDay(); +} + +bool systemPauseOnFrame() +{ + return false; +} + +void systemFrame() +{ +} + +void system10Frames(int _iRate) +{ + GUI()->vComputeFrameskip(_iRate); +// missingSound += systemFrameSkip; +} + +void systemShowSpeed(int _iSpeed) +{ + GUI()->setspeed(_iSpeed); +} + +void systemUpdateMotionSensor() +{ +} + +void systemScreenCapture(int _iNum) +{ + qDebug() << "TODO::systemScreenCapture"; + // GUI()->vCaptureScreen(_iNum); +} + +void systemDrawScreen() +{ + GUI()->vDrawScreen(); +} + +void systemGbBorderOn() +{ +} + + +void systemScreenMessage(const char *msg) { + qDebug() << "TODO:: systemScreenMessage " << msg; +} diff --git a/src/qt/system.h b/src/qt/system.h new file mode 100644 index 0000000..3370707 --- /dev/null +++ b/src/qt/system.h @@ -0,0 +1,7 @@ +#include +#include "../common/Types.h" +#include "../gba/Sound.h" +#include "../common/SoundSDL.h" +#include "../sdl/inputSDL.h" +#include "window.h" +#include "SoundQt.h" diff --git a/src/qt/window.cpp b/src/qt/window.cpp new file mode 100644 index 0000000..a6098a1 --- /dev/null +++ b/src/qt/window.cpp @@ -0,0 +1,647 @@ +#include "window.h" + +#include + +extern int RGB_LOW_BITS_MASK; + +int Init_2xSaI(u32); + +Window::Window() : + m_iGBAScreenWidth (240), + m_iGBAScreenHeight(160), + m_eCartridge(CartridgeNone), + m_bPaused(false) { + m_config = new Config(); + for (int i = 0; i < 10; i++) + m_qGameSlotList.push_back(new QGameSlot()); + connect(m_config, SIGNAL(muteChanged()), this, SLOT(vApplyConfigMute())); + //SDLdoesn't work on TOUCH yet + vInitSDL(); + vInitSystem(); + + // Get config + // + struct passwd *pw = getpwuid(getuid()); + const char *homedir = pw->pw_dir; + m_sUserDataDir = (QString(homedir) + + QString("/.local/share/emanuelesorce.qvbam/") + ).toStdString(); + QDir qDir(QString::fromStdString(m_sUserDataDir)); + if (!qDir.exists()) { + qDir.mkpath(QString::fromStdString(m_sUserDataDir)); + } + + // m_sConfigFile = m_sUserDataDir + "/config"; + + // vInitConfig(); + + // if (Glib::file_test(m_sConfigFile, Glib::FILE_TEST_EXISTS)) + // { + // vLoadConfig(m_sConfigFile); + // vCheckConfig(); + // } + // else + // { + // vSaveConfig(m_sConfigFile); + // } + + + vApplyConfigScreenArea(); + vApplyConfigMute(); + vApplyConfigFrameskip(); +} + +Window::~Window() { + qDebug() << "~Windows called"; + vOnFileClose(); + // vUnInitSystem(); + // vSaveJoypadsToConfig(); + // vSaveConfig(m_sConfigFile); +} + +bool Window::bLoadROM(const std::string &_rsFile) { + vOnFileClose(); + + // clear cheat list + // cheatsDeleteAll(false); + // gbCheatRemoveAll(); + + m_sRomFile = _rsFile; + const char * csFile = _rsFile.c_str(); + + IMAGE_TYPE eType = utilFindType(csFile); + if (eType == IMAGE_UNKNOWN) + { + qDebug () << "Unknown file type" << csFile; + return false; + } + + bool bLoaded = false; + // if (eType == IMAGE_GB) + // { + // bLoaded = gbLoadRom(csFile); + // if (bLoaded) + // { + // m_eCartridge = CartridgeGB; + // m_stEmulator = GBSystem; + + // useBios = m_poCoreConfig->oGetKey("gb_use_bios_file"); + // gbGetHardwareType(); + + // if (gbHardware & 5) + // { + // gbCPUInit(m_poCoreConfig->sGetKey("gb_bios_file").c_str(), useBios); + // } + + // // If the bios file was rejected by gbCPUInit + // if (m_poCoreConfig->oGetKey("gb_use_bios_file") && ! useBios) + // { + // m_poCoreConfig->vSetKey("gb_bios_file", ""); + // } + + // gbReset(); + // } + // } + // else + if (eType == IMAGE_GBA) + { + int iSize = CPULoadRom(csFile); + bLoaded = (iSize > 0); + if (bLoaded) + { + m_eCartridge = CartridgeGBA; + m_stEmulator = GBASystem; + + vApplyPerGameConfig(); + + // useBios = m_poCoreConfig->oGetKey("use_bios_file"); + useBios = false; + // CPUInit(m_poCoreConfig->sGetKey("bios_file").c_str(), useBios); + CPUInit("", useBios); + CPUReset(); + + // If the bios file was rejected by CPUInit + // if (m_poCoreConfig->oGetKey("use_bios_file") && ! useBios) + // { + // m_poCoreConfig->vSetKey("bios_file", ""); + // } + } + } + + if (! bLoaded) + { + return false; + } + + vLoadBattery(); + // vLoadCheats(); + vUpdateScreen(); + + emulating = 1; + m_bWasEmulating = false; + + vApplyConfigSoundSampleRate(); + + vUpdateGameSlots(); + // vHistoryAdd(_rsFile); + + // for (std::list::iterator it = m_listSensitiveWhenPlaying.begin(); + // it != m_listSensitiveWhenPlaying.end(); + // it++) + // { + // (*it)->set_sensitive(); + // } + + // if (m_poCoreConfig->oGetKey("load_game_auto")) + // { + // vOnLoadGameMostRecent(); + // } + + vStartEmu(); + + return true; +} + +void Window::vOnFileClose() { + if (m_eCartridge != CartridgeNone) + { + soundPause(); + vStopEmu(); + // vSetDefaultTitle(); + // vDrawDefaultScreen(); + vSaveBattery(); + // vSaveCheats(); + m_stEmulator.emuCleanUp(); + m_eCartridge = CartridgeNone; + emulating = 0; + + // vUpdateGameSlots(); + + // for (std::list::iterator it = m_listSensitiveWhenPlaying.begin(); + // it != m_listSensitiveWhenPlaying.end(); + // it++) + // { + // (*it)->set_sensitive(false); + // } + setPaused(false); + } +} + +void Window::vStopEmu() { + m_idleTimerRunning = false; + m_bWasEmulating = false; +} + +void Window::vApplyPerGameConfig() { + qDebug() << "TODO:: vApplyPerGameConfig" << endl; +} + +void Window::vLoadBattery() { + std::string sBattery; + std::string sDir; + sDir = m_sUserDataDir; + sBattery = sDir + "/" + QFileInfo(QString::fromStdString(m_sRomFile)).baseName().toStdString() + ".sav"; + if (m_stEmulator.emuReadBattery(sBattery.c_str())) + { + systemScreenMessage("Loaded battery"); + } +} + +void Window::vUpdateScreen() { + // if (m_eCartridge == CartridgeGB) + // { + // if (gbBorderOn) + // { + // m_iScreenWidth = m_iSGBScreenWidth; + // m_iScreenHeight = m_iSGBScreenHeight; + // gbBorderLineSkip = m_iSGBScreenWidth; + // gbBorderColumnSkip = (m_iSGBScreenWidth - m_iGBScreenWidth) / 2; + // gbBorderRowSkip = (m_iSGBScreenHeight - m_iGBScreenHeight) / 2; + // } + // else + // { + // m_iScreenWidth = m_iGBScreenWidth; + // m_iScreenHeight = m_iGBScreenHeight; + // gbBorderLineSkip = m_iGBScreenWidth; + // gbBorderColumnSkip = 0; + // gbBorderRowSkip = 0; + // } + // } + // else + if (m_eCartridge == CartridgeGBA) + { + m_iScreenWidth = m_iGBAScreenWidth; + m_iScreenHeight = m_iGBAScreenHeight; + } + + Q_ASSERT(m_iScreenWidth >= 1 && m_iScreenHeight >= 1); + + // m_poScreenArea->vSetSize(m_iScreenWidth, m_iScreenHeight); + // m_poScreenArea->vSetScale(m_poDisplayConfig->oGetKey("scale")); + + // resize(1, 1); + + // if (emulating) + // { + // vDrawScreen(); + // } + // else + // { + // vDrawDefaultScreen(); + // } +} + +void Window::vStartEmu() { + m_idleTimerRunning = true; + idleTimer.singleShot(1, this, SLOT(bOnEmuIdle())); +} + +void Window::vInitSystem() +{ + systemColorDepth = 32; + systemVerbose = 0; + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + systemFrameSkip = 2; + + emulating = 0; + + gbFrameSkip = 0; + + soundInit(); +} + +bool Window::bOnEmuIdle() +{ + // vSDLPollEvents(); + if (m_idleTimerRunning) { + QTime t; + t.start(); + m_stEmulator.emuMain(m_stEmulator.emuCount); + // qDebug("Time elapsed: %d ms", t.elapsed()); + int elapsed = t.elapsed(); + // idleTimer.singleShot(std::max(10 - elapsed, 0), this, SLOT(bOnEmuIdle())); + idleTimer.singleShot(0, this, SLOT(bOnEmuIdle())); + } + return true; +} + +void Window::vSDLPollEvents() +{ + SDL_Event event; + qDebug() << "Window::vSDLPollEvents"; + while(SDL_PollEvent(&event)) + { + switch(event.type) + { + case SDL_JOYHATMOTION: + case SDL_JOYBUTTONDOWN: + case SDL_JOYBUTTONUP: + case SDL_JOYAXISMOTION: + case SDL_KEYDOWN: + case SDL_KEYUP: + qDebug() << "input recieved"; + inputProcessSDLEvent(event); + break; + } + } +} + +void Window::vDrawScreen() +{ + m_iFrameCount++; + sDrawScreen(); +} + +bool Window::on_key_press_event(Qt::Key key) { + SDL_Event event; + event.type = SDL_KEYDOWN; + event.key.keysym.sym = (SDLKey)key; + inputProcessSDLEvent(event); + + return true; +} + +bool Window::on_key_release_event(Qt::Key key) { + SDL_Event event; + event.type = SDL_KEYUP; + event.key.keysym.sym = (SDLKey)key; + inputProcessSDLEvent(event); + return true; +} + +void Window::vApplyConfigScreenArea() { + vInitColors(ColorFormatRGB); +} + +void Window::vInitColors(EColorFormat _eColorFormat) { + switch (_eColorFormat) + { + case ColorFormatBGR: +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + systemRedShift = 3; + systemGreenShift = 11; + systemBlueShift = 19; + RGB_LOW_BITS_MASK = 0x00010101; +#else + systemRedShift = 27; + systemGreenShift = 19; + systemBlueShift = 11; + RGB_LOW_BITS_MASK = 0x01010100; +#endif + break; + default: +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + systemRedShift = 19; + systemGreenShift = 11; + systemBlueShift = 3; + RGB_LOW_BITS_MASK = 0x00010101; +#else + systemRedShift = 11; + systemGreenShift = 19; + systemBlueShift = 27; + RGB_LOW_BITS_MASK = 0x01010100; +#endif + break; + } + + for (int i = 0; i < 0x10000; i++) + { + systemColorMap32[i] = (((i & 0x1f) << systemRedShift) + | (((i & 0x3e0) >> 5) << systemGreenShift) + | (((i & 0x7c00) >> 10) << systemBlueShift)); + } + + for (int i = 0; i < 24; ) + { + systemGbPalette[i++] = (0x1f) | (0x1f << 5) | (0x1f << 10); + systemGbPalette[i++] = (0x15) | (0x15 << 5) | (0x15 << 10); + systemGbPalette[i++] = (0x0c) | (0x0c << 5) | (0x0c << 10); + systemGbPalette[i++] = 0; + } + + Init_2xSaI(32); +} + +void Window::vInitSDL() +{ + static bool bDone = false; + + if (bDone) + return; + + // int iFlags = (SDL_INIT_EVERYTHING | SDL_INIT_NOPARACHUTE); + + //Currently we don't need to init, or not with everything. + // if (SDL_Init(iFlags) < 0) + // { + // qDebug() << "Failed to init SDL: " << SDL_GetError(); + // abort(); + // } + + inputSetKeymap(PAD_DEFAULT, KEY_LEFT, Qt::Key_Left); + inputSetKeymap(PAD_DEFAULT, KEY_RIGHT, Qt::Key_Right); + inputSetKeymap(PAD_DEFAULT, KEY_UP, Qt::Key_Up); + inputSetKeymap(PAD_DEFAULT, KEY_DOWN, Qt::Key_Down); + inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_A, Qt::Key_Z); + inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_B, Qt::Key_X); + inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_START, Qt::Key_Return); + inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_SELECT, Qt::Key_Backspace); + inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_L, Qt::Key_A); + inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_R, Qt::Key_S); + inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_SPEED, Qt::Key_Space); + inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_CAPTURE, Qt::Key_F12); + inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_AUTO_A, Qt::Key_Q); + inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_AUTO_B, Qt::Key_W); + + int sdlNumDevices = SDL_NumJoysticks(); + for (int i = 0; i < sdlNumDevices; i++) + SDL_JoystickOpen(i); + + inputInitJoysticks(); + + bDone = true; +} + +void Window::vSaveBattery() { + std::string sBattery; + std::string sDir; + sDir = m_sUserDataDir; + + sBattery = sDir + "/" + QFileInfo(QString::fromStdString(m_sRomFile)).baseName().toStdString() + ".sav"; + + if (m_stEmulator.emuWriteBattery(sBattery.c_str())) + { + systemScreenMessage("Saved battery"); + } +} + +QObject * Window::config() { + return m_config; +} +int Window::speed() { + return m_speed; +} +void Window::setspeed(int s) { + if (s != m_speed) { + m_speed = s; + emit speedChanged(); + } +} +bool Window::paused() { + return m_bPaused; +} +void Window::setPaused(bool p) { + if (p != m_bPaused) { + m_bPaused = p; + vOnFilePauseToggled(); + emit pausedChanged(); + } +} + +int Window::frameSkip() { + return systemFrameSkip; +} + +QQmlListProperty Window::gameSlot() { + return QQmlListProperty(this, m_qGameSlotList); +} + +void Window::vComputeFrameskip(int _iRate) { + static QDateTime uiLastTime; + static int iFrameskipAdjust = 0; + + QDateTime uiTime = QDateTime::currentDateTime(); + + if (m_bWasEmulating) + { + if (m_bAutoFrameskip) + { + int uiDiff = uiTime.toMSecsSinceEpoch() - uiLastTime.toMSecsSinceEpoch(); + const int iWantedSpeed = 100; + int iSpeed = iWantedSpeed; + + if (uiDiff != 0) + { + iSpeed = (1000000 / _iRate) / (uiDiff); + } + + if (iSpeed >= iWantedSpeed - 2) + { + iFrameskipAdjust++; + if (iFrameskipAdjust >= 3) + { + iFrameskipAdjust = 0; + if (systemFrameSkip > 0) + { + systemFrameSkip--; + emit frameSkipChanged(); + } + } + } + else + { + if (iSpeed < iWantedSpeed - 20) + { + iFrameskipAdjust -= ((iWantedSpeed - 10) - iSpeed) / 5; + } + else if (systemFrameSkip < 9) + { + iFrameskipAdjust--; + } + + if (iFrameskipAdjust <= -4) + { + iFrameskipAdjust = 0; + if (systemFrameSkip < 9) + { + systemFrameSkip++; + emit frameSkipChanged(); + } + } + } + } + } + else + { + m_bWasEmulating = true; + } + // qDebug() << "systemFrameSkip is " << systemFrameSkip << endl; + + uiLastTime = uiTime; +} + +void Window::vApplyConfigFrameskip() { + // std::string sFrameskip = m_poCoreConfig->oGetKey("frameskip"); + std::string sFrameskip = "auto"; + if (sFrameskip == "auto") + { + m_bAutoFrameskip = true; + gbFrameSkip = 0; + systemFrameSkip = 0; + } + else + { + m_bAutoFrameskip = false; + int iFrameskip = 0; + // int iFrameskip = m_poCoreConfig->oGetKey("frameskip"); + gbFrameSkip = iFrameskip; + systemFrameSkip = iFrameskip; + } +} + +void Window::vApplyConfigMute() +{ + qDebug() << "Window::vApplyConfigMute" ; + bool bMute = m_config->mute(); + if (bMute) + { + soundSetEnable(0x000); + } + else + { + soundSetEnable(0x30f); + } +} + +bool Window::bLoadRomInQML(QString fileName){ + return bLoadROM(m_sUserDataDir + "/roms/" + fileName.toStdString()); +} + +void Window::vOnFilePauseToggled() +{ + if (emulating) + { + if (m_bPaused) + { + vStopEmu(); + soundPause(); + } + else + { + vStartEmu(); + soundResume(); + } + } +} + +void Window::vUpdateGameSlots() { + std::string sFileBase; + std::string sDir = m_sUserDataDir; + + sFileBase = sDir + "/" + QFileInfo(QString::fromStdString(m_sRomFile)).baseName().toStdString(); + + const char * csDateFormat = "%Y/%m/%d %H:%M:%S"; + + for (int i = 0; i < 10; i++) + { + char csPrefix[10]; + snprintf(csPrefix, sizeof(csPrefix), "%2d ", i + 1); + + char csSlot[10]; + snprintf(csSlot, sizeof(csSlot), "%d", i + 1); + m_astGameSlot[i].m_sFile = sFileBase + csSlot + ".sgm"; + + std::string sDateTime; + struct stat stStat; + if (stat(m_astGameSlot[i].m_sFile.c_str(), &stStat) == -1) + { + sDateTime = "----/--/-- --:--:--"; + m_astGameSlot[i].m_bEmpty = true; + } + else + { + char csDateTime[30]; + strftime(csDateTime, sizeof(csDateTime), csDateFormat, + localtime(&stStat.st_mtime)); + sDateTime = csDateTime; + m_astGameSlot[i].m_bEmpty = false; + m_astGameSlot[i].m_uiTime = stStat.st_mtime; + } + m_qGameSlotList[i]->setIsEmpty(m_astGameSlot[i].m_bEmpty); + m_qGameSlotList[i]->setTime(QString::fromStdString(csPrefix + sDateTime)); + } +} + +void Window::vOnSaveGame(int _iSlot) +{ + int i = _iSlot - 1; + m_stEmulator.emuWriteState(m_astGameSlot[i].m_sFile.c_str()); + vUpdateGameSlots(); +} + +void Window::vOnLoadGame(int _iSlot) +{ + int i = _iSlot - 1; + if (! m_astGameSlot[i].m_bEmpty) + { + m_stEmulator.emuReadState(m_astGameSlot[i].m_sFile.c_str()); + setPaused(false); + } +} + +void Window::vApplyConfigSoundSampleRate() { + //TODO configure this + long iSoundSampleRate = 11025; + soundSetSampleRate(iSoundSampleRate); +} + diff --git a/src/qt/window.h b/src/qt/window.h new file mode 100644 index 0000000..600dd15 --- /dev/null +++ b/src/qt/window.h @@ -0,0 +1,130 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../gba/Sound.h" +#include "../Util.h" +#include "../gba/GBA.h" +#include "../gb/gbGlobals.h" +#include "../sdl/inputSDL.h" +#include +#include +#include "Config.h" +#include "QGameSlot.h" + +#ifndef WINDOW +#define WINDOW +class Window : public QObject { + Q_OBJECT + Q_PROPERTY(QObject * config READ config) + Q_PROPERTY(int speed READ speed NOTIFY speedChanged) + Q_PROPERTY(int frameSkip READ frameSkip NOTIFY frameSkipChanged) + Q_PROPERTY(bool paused READ paused WRITE setPaused NOTIFY pausedChanged) + Q_PROPERTY(QQmlListProperty gameSlot READ gameSlot NOTIFY gameSlotChanged) + struct SGameSlot + { + bool m_bEmpty; + std::string m_sFile; + time_t m_uiTime; + }; + +public: + + int m_iScreenWidth; + int m_iScreenHeight; + + enum ECartridge + { + CartridgeNone, + CartridgeGB, + CartridgeGBA + }; + + enum EColorFormat + { + ColorFormatRGB, + ColorFormatBGR + }; + + Window(); + ~Window(); + bool bLoadROM(const std::string & _rsFile); + void vDrawScreen(); + + Q_INVOKABLE bool bLoadRomInQML(QString fileName); + Q_INVOKABLE bool on_key_press_event(Qt::Key key); + Q_INVOKABLE bool on_key_release_event(Qt::Key key); + void vComputeFrameskip(int); + Q_INVOKABLE void vOnFileClose(); + Q_INVOKABLE void vOnSaveGame(int _iSlot); + Q_INVOKABLE void vOnLoadGame(int _iSlot); + + //qml get set function + QObject * config(); + int speed(); + void setspeed(int); + bool paused(); + void setPaused(bool); + int frameSkip(); + QQmlListProperty gameSlot(); + + +public slots: + bool bOnEmuIdle(); + void vApplyConfigMute(); + +signals: + void sDrawScreen(); + + //qmlsignals + void speedChanged(); + void pausedChanged(); + void frameSkipChanged(); + void gameSlotChanged(); + +private: + int m_iFrameCount; + + const int m_iGBAScreenWidth; + const int m_iGBAScreenHeight; + + std::string m_sUserDataDir; + + std::string m_sRomFile; + ECartridge m_eCartridge; + EmulatedSystem m_stEmulator; + bool m_bWasEmulating; + bool m_bAutoFrameskip; + SGameSlot m_astGameSlot[10]; + + QTimer idleTimer; + Config * m_config; + int m_speed; + bool m_idleTimerRunning; + bool m_bPaused; + QList m_qGameSlotList; + + void vStopEmu(); + void vApplyPerGameConfig(); + void vLoadBattery(); + void vUpdateScreen(); + void vStartEmu(); + void vInitSystem(); + void vInitSDL(); + void vSDLPollEvents(); + void vApplyConfigScreenArea(); + void vInitColors(EColorFormat _eColorFormat); + void vSaveBattery(); + void vApplyConfigFrameskip(); + void vOnFilePauseToggled(); + void vUpdateGameSlots(); + void vApplyConfigSoundSampleRate(); + +}; +#endif diff --git a/src/sdl/SDL.cpp b/src/sdl/SDL.cpp new file mode 100644 index 0000000..910e93e --- /dev/null +++ b/src/sdl/SDL.cpp @@ -0,0 +1,2747 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2005-2006 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include +#include +#include +#include +#include +#include +#include +#ifdef __APPLE__ + #include + #include +#else + #include + #include +#endif + +#include + +#include "../AutoBuild.h" + +#include + +#include "../common/Patch.h" +#include "../gba/GBA.h" +#include "../gba/agbprint.h" +#include "../gba/Flash.h" +#include "../gba/Cheats.h" +#include "../gba/RTC.h" +#include "../gba/Sound.h" +#include "../gb/gb.h" +#include "../gb/gbGlobals.h" +#include "../gb/gbCheats.h" +#include "../gb/gbSound.h" +#include "../Util.h" + +#include "debugger.h" +#include "filters.h" +#include "text.h" +#include "inputSDL.h" +#include "../common/SoundSDL.h" + +#ifndef _WIN32 +# include +# define GETCWD getcwd +#else // _WIN32 +# include +# define GETCWD _getcwd +#endif // _WIN32 + +#ifndef __GNUC__ +# define HAVE_DECL_GETOPT 0 +# define __STDC__ 1 +# include "getopt.h" +#else // ! __GNUC__ +# define HAVE_DECL_GETOPT 1 +# include +#endif // ! __GNUC__ + +#if WITH_LIRC +#include +#include +#endif + +extern void remoteInit(); +extern void remoteCleanUp(); +extern void remoteStubMain(); +extern void remoteStubSignal(int,int); +extern void remoteOutput(const char *, u32); +extern void remoteSetProtocol(int); +extern void remoteSetPort(int); + +struct EmulatedSystem emulator = { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + false, + 0 +}; + +SDL_Surface *surface = NULL; + +int systemSpeed = 0; +int systemRedShift = 0; +int systemBlueShift = 0; +int systemGreenShift = 0; +int systemColorDepth = 0; +int systemDebug = 0; +int systemVerbose = 0; +int systemFrameSkip = 0; +int systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + +int srcPitch = 0; +int srcWidth = 0; +int srcHeight = 0; +int destWidth = 0; +int destHeight = 0; +int desktopWidth = 0; +int desktopHeight = 0; + +Filter filter = kStretch2x; +u8 *delta = NULL; +static const int delta_size = 322*242*4; + +int filter_enlarge = 2; + +int sdlPrintUsage = 0; + +int cartridgeType = 3; +int captureFormat = 0; + +int openGL = 1; +int textureSize = 256; +GLuint screenTexture = 0; +u8 *filterPix = 0; + +int pauseWhenInactive = 0; +int active = 1; +int emulating = 0; +int RGB_LOW_BITS_MASK=0x821; +u32 systemColorMap32[0x10000]; +u16 systemColorMap16[0x10000]; +u16 systemGbPalette[24]; +FilterFunc filterFunction = 0; +IFBFilterFunc ifbFunction = 0; +IFBFilter ifbType = kIFBNone; +char filename[2048]; +char biosFileName[2048]; +char gbBiosFileName[2048]; +char captureDir[2048]; +char saveDir[2048]; +char batteryDir[2048]; +char* homeDir = NULL; + +// Directory within homedir to use for default save location. +#define DOT_DIR ".vbam" + +static char *rewindMemory = NULL; +static int *rewindSerials = NULL; +static int rewindPos = 0; +static int rewindSerial = 0; +static int rewindTopPos = 0; +static int rewindCounter = 0; +static int rewindCount = 0; +static bool rewindSaveNeeded = false; +static int rewindTimer = 0; + +static int sdlSaveKeysSwitch = 0; +// if 0, then SHIFT+F# saves, F# loads (old VBA, ...) +// if 1, then SHIFT+F# loads, F# saves (linux snes9x, ...) +// if 2, then F5 decreases slot number, F6 increases, F7 saves, F8 loads + +static int saveSlotPosition = 0; // default is the slot from normal F1 +// internal slot number for undoing the last load +#define SLOT_POS_LOAD_BACKUP 8 +// internal slot number for undoing the last save +#define SLOT_POS_SAVE_BACKUP 9 + +static int sdlOpenglScale = 1; +// will scale window on init by this much +static int sdlSoundToggledOff = 0; +// allow up to 100 IPS/UPS/PPF patches given on commandline +#define PATCH_MAX_NUM 100 +int sdl_patch_num = 0; +char * (sdl_patch_names[PATCH_MAX_NUM]) = { NULL }; // and so on + +extern int autoFireMaxCount; + +#define REWIND_NUM 8 +#define REWIND_SIZE 400000 +#define SYSMSG_BUFFER_SIZE 1024 + +#define _stricmp strcasecmp + +bool wasPaused = false; +int autoFrameSkip = 0; +int frameskipadjust = 0; +int showRenderedFrames = 0; +int renderedFrames = 0; + +u32 throttleLastTime = 0; +u32 autoFrameSkipLastTime = 0; + +int showSpeed = 1; +int showSpeedTransparent = 1; +bool disableStatusMessages = false; +bool paused = false; +bool pauseNextFrame = false; +bool debugger = false; +bool debuggerStub = false; +int fullscreen = 0; +int sdlFlashSize = 0; +int sdlAutoPatch = 1; +int sdlRtcEnable = 0; +int sdlAgbPrint = 0; +int sdlMirroringEnable = 0; + +static int ignore_first_resize_event = 0; + +/* forward */ +void systemConsoleMessage(const char*); + +void (*dbgMain)() = debuggerMain; +void (*dbgSignal)(int,int) = debuggerSignal; +void (*dbgOutput)(const char *, u32) = debuggerOutput; + +int mouseCounter = 0; + +bool screenMessage = false; +char screenMessageBuffer[21]; +u32 screenMessageTime = 0; + +char *arg0; + +int sdlPreparedCheats = 0; +#define MAX_CHEATS 100 +const char * sdlPreparedCheatCodes[MAX_CHEATS]; + +#define SDL_SOUND_MAX_VOLUME 2.0 +#define SDL_SOUND_ECHO 0.2 +#define SDL_SOUND_STEREO 0.15 + +struct option sdlOptions[] = { + { "agb-print", no_argument, &sdlAgbPrint, 1 }, + { "auto-frameskip", no_argument, &autoFrameSkip, 1 }, + { "bios", required_argument, 0, 'b' }, + { "config", required_argument, 0, 'c' }, + { "debug", no_argument, 0, 'd' }, + { "filter", required_argument, 0, 'f' }, + { "ifb-filter", required_argument, 0, 'I' }, + { "flash-size", required_argument, 0, 'S' }, + { "flash-64k", no_argument, &sdlFlashSize, 0 }, + { "flash-128k", no_argument, &sdlFlashSize, 1 }, + { "frameskip", required_argument, 0, 's' }, + { "fullscreen", no_argument, &fullscreen, 1 }, + { "gdb", required_argument, 0, 'G' }, + { "help", no_argument, &sdlPrintUsage, 1 }, + { "patch", required_argument, 0, 'i' }, + { "no-agb-print", no_argument, &sdlAgbPrint, 0 }, + { "no-auto-frameskip", no_argument, &autoFrameSkip, 0 }, + { "no-debug", no_argument, 0, 'N' }, + { "no-patch", no_argument, &sdlAutoPatch, 0 }, + { "no-opengl", no_argument, &openGL, 0 }, + { "no-pause-when-inactive", no_argument, &pauseWhenInactive, 0 }, + { "no-rtc", no_argument, &sdlRtcEnable, 0 }, + { "no-show-speed", no_argument, &showSpeed, 0 }, + { "opengl", required_argument, 0, 'O' }, + { "opengl-nearest", no_argument, &openGL, 1 }, + { "opengl-bilinear", no_argument, &openGL, 2 }, + { "pause-when-inactive", no_argument, &pauseWhenInactive, 1 }, + { "profile", optional_argument, 0, 'p' }, + { "rtc", no_argument, &sdlRtcEnable, 1 }, + { "save-type", required_argument, 0, 't' }, + { "save-auto", no_argument, &cpuSaveType, 0 }, + { "save-eeprom", no_argument, &cpuSaveType, 1 }, + { "save-sram", no_argument, &cpuSaveType, 2 }, + { "save-flash", no_argument, &cpuSaveType, 3 }, + { "save-sensor", no_argument, &cpuSaveType, 4 }, + { "save-none", no_argument, &cpuSaveType, 5 }, + { "show-speed-normal", no_argument, &showSpeed, 1 }, + { "show-speed-detailed", no_argument, &showSpeed, 2 }, + { "throttle", required_argument, 0, 'T' }, + { "verbose", required_argument, 0, 'v' }, + { "cheat", required_argument, 0, 1000 }, + { "autofire", required_argument, 0, 1001 }, + { NULL, no_argument, NULL, 0 } +}; + +static void sdlChangeVolume(float d) +{ + float oldVolume = soundGetVolume(); + float newVolume = oldVolume + d; + + if (newVolume < 0.0) newVolume = 0.0; + if (newVolume > SDL_SOUND_MAX_VOLUME) newVolume = SDL_SOUND_MAX_VOLUME; + + if (fabs(newVolume - oldVolume) > 0.001) { + char tmp[32]; + sprintf(tmp, "Volume: %i%%", (int)(newVolume*100.0+0.5)); + systemScreenMessage(tmp); + soundSetVolume(newVolume); + } +} + +#if WITH_LIRC +//LIRC code +bool LIRCEnabled = false; +int LIRCfd = 0; +static struct lirc_config *LIRCConfigInfo; + +void StartLirc(void) +{ + fprintf(stdout, "Trying to start LIRC: "); + //init LIRC and Record output + LIRCfd = lirc_init( "vbam",1 ); + if( LIRCfd == -1 ) { + //it failed + fprintf(stdout, "Failed\n"); + } else { + fprintf(stdout, "Success\n"); + //read the config file + char LIRCConfigLoc[2048]; + sprintf(LIRCConfigLoc, "%s/%s/%s", homeDir, DOT_DIR, "lircrc"); + fprintf(stdout, "LIRC Config file:"); + if( lirc_readconfig(LIRCConfigLoc,&LIRCConfigInfo,NULL) == 0 ) { + //check vbam dir for lircrc + fprintf(stdout, "Loaded (%s)\n", LIRCConfigLoc ); + } else if( lirc_readconfig(NULL,&LIRCConfigInfo,NULL) == 0 ) { + //check default lircrc location + fprintf(stdout, "Loaded\n"); + } else { + //it all failed + fprintf(stdout, "Failed\n"); + LIRCEnabled = false; + } + LIRCEnabled = true; + } +} + +void StopLirc(void) +{ + //did we actually get lirc working at the start + if(LIRCEnabled) { + //if so free the config and deinit lirc + fprintf(stdout, "Shuting down LIRC\n"); + lirc_freeconfig(LIRCConfigInfo); + lirc_deinit(); + //set lirc enabled to false + LIRCEnabled = false; + } +} +#endif + + +u32 sdlFromHex(char *s) +{ + u32 value; + sscanf(s, "%x", &value); + return value; +} + +u32 sdlFromDec(char *s) +{ + u32 value = 0; + sscanf(s, "%u", &value); + return value; +} + +#ifdef __MSC__ +#define stat _stat +#define S_IFDIR _S_IFDIR +#endif + +void sdlCheckDirectory(char *dir) +{ + struct stat buf; + + int len = strlen(dir); + + char *p = dir + len - 1; + + if(*p == '/' || + *p == '\\') + *p = 0; + + if(stat(dir, &buf) == 0) { + if(!(buf.st_mode & S_IFDIR)) { + fprintf(stderr, "Error: %s is not a directory\n", dir); + dir[0] = 0; + } + } else { + fprintf(stderr, "Error: %s does not exist\n", dir); + dir[0] = 0; + } +} + +char *sdlGetFilename(char *name) +{ + static char filebuffer[2048]; + + int len = strlen(name); + + char *p = name + len - 1; + + while(true) { + if(*p == '/' || + *p == '\\') { + p++; + break; + } + len--; + p--; + if(len == 0) + break; + } + + if(len == 0) + strcpy(filebuffer, name); + else + strcpy(filebuffer, p); + return filebuffer; +} + +FILE *sdlFindFile(const char *name) +{ + char buffer[4096]; + char path[2048]; + +#ifdef _WIN32 +#define PATH_SEP ";" +#define FILE_SEP '\\' +#define EXE_NAME "vbam.exe" +#else // ! _WIN32 +#define PATH_SEP ":" +#define FILE_SEP '/' +#define EXE_NAME "vbam" +#endif // ! _WIN32 + + fprintf(stdout, "Searching for file %s\n", name); + + if(GETCWD(buffer, 2048)) { + fprintf(stdout, "Searching current directory: %s\n", buffer); + } + + FILE *f = fopen(name, "r"); + if(f != NULL) { + return f; + } + + if(homeDir) { + fprintf(stdout, "Searching home directory: %s%c%s\n", homeDir, FILE_SEP, DOT_DIR); + sprintf(path, "%s%c%s%c%s", homeDir, FILE_SEP, DOT_DIR, FILE_SEP, name); + f = fopen(path, "r"); + if(f != NULL) + return f; + } + +#ifdef _WIN32 + char *home = getenv("USERPROFILE"); + if(home != NULL) { + fprintf(stdout, "Searching user profile directory: %s\n", home); + sprintf(path, "%s%c%s", home, FILE_SEP, name); + f = fopen(path, "r"); + if(f != NULL) + return f; + } + + if(!strchr(arg0, '/') && + !strchr(arg0, '\\')) { + char *path = getenv("PATH"); + + if(path != NULL) { + fprintf(stdout, "Searching PATH\n"); + strncpy(buffer, path, 4096); + buffer[4095] = 0; + char *tok = strtok(buffer, PATH_SEP); + + while(tok) { + sprintf(path, "%s%c%s", tok, FILE_SEP, EXE_NAME); + f = fopen(path, "r"); + if(f != NULL) { + char path2[2048]; + fclose(f); + sprintf(path2, "%s%c%s", tok, FILE_SEP, name); + f = fopen(path2, "r"); + if(f != NULL) { + fprintf(stdout, "Found at %s\n", path2); + return f; + } + } + tok = strtok(NULL, PATH_SEP); + } + } + } else { + // executable is relative to some directory + fprintf(stdout, "Searching executable directory\n"); + strcpy(buffer, arg0); + char *p = strrchr(buffer, FILE_SEP); + if(p) { + *p = 0; + sprintf(path, "%s%c%s", buffer, FILE_SEP, name); + f = fopen(path, "r"); + if(f != NULL) + return f; + } + } +#else // ! _WIN32 + fprintf(stdout, "Searching data directory: %s\n", PKGDATADIR); + sprintf(path, "%s%c%s", PKGDATADIR, FILE_SEP, name); + f = fopen(path, "r"); + if(f != NULL) + return f; + + fprintf(stdout, "Searching system config directory: %s\n", SYSCONFDIR); + sprintf(path, "%s%c%s", SYSCONFDIR, FILE_SEP, name); + f = fopen(path, "r"); + if(f != NULL) + return f; +#endif // ! _WIN32 + + return NULL; +} + +void sdlReadPreferences(FILE *f) +{ + char buffer[2048]; + + while(1) { + char *s = fgets(buffer, 2048, f); + + if(s == NULL) + break; + + char *p = strchr(s, '#'); + + if(p) + *p = 0; + + char *token = strtok(s, " \t\n\r="); + + if(!token) + continue; + + if(strlen(token) == 0) + continue; + + char *key = token; + char *value = strtok(NULL, "\t\n\r"); + + if(value == NULL) { + fprintf(stdout, "Empty value for key %s\n", key); + continue; + } + + if(!strcmp(key,"Joy0_Left")) { + inputSetKeymap(PAD_1, KEY_LEFT, sdlFromHex(value)); + } else if(!strcmp(key, "Joy0_Right")) { + inputSetKeymap(PAD_1, KEY_RIGHT, sdlFromHex(value)); + } else if(!strcmp(key, "Joy0_Up")) { + inputSetKeymap(PAD_1, KEY_UP, sdlFromHex(value)); + } else if(!strcmp(key, "Joy0_Down")) { + inputSetKeymap(PAD_1, KEY_DOWN, sdlFromHex(value)); + } else if(!strcmp(key, "Joy0_A")) { + inputSetKeymap(PAD_1, KEY_BUTTON_A, sdlFromHex(value)); + } else if(!strcmp(key, "Joy0_B")) { + inputSetKeymap(PAD_1, KEY_BUTTON_B, sdlFromHex(value)); + } else if(!strcmp(key, "Joy0_L")) { + inputSetKeymap(PAD_1, KEY_BUTTON_L, sdlFromHex(value)); + } else if(!strcmp(key, "Joy0_R")) { + inputSetKeymap(PAD_1, KEY_BUTTON_R, sdlFromHex(value)); + } else if(!strcmp(key, "Joy0_Start")) { + inputSetKeymap(PAD_1, KEY_BUTTON_START, sdlFromHex(value)); + } else if(!strcmp(key, "Joy0_Select")) { + inputSetKeymap(PAD_1, KEY_BUTTON_SELECT, sdlFromHex(value)); + } else if(!strcmp(key, "Joy0_Speed")) { + inputSetKeymap(PAD_1, KEY_BUTTON_SPEED, sdlFromHex(value)); + } else if(!strcmp(key, "Joy0_Capture")) { + inputSetKeymap(PAD_1, KEY_BUTTON_CAPTURE, sdlFromHex(value)); + } else if(!strcmp(key,"Joy1_Left")) { + inputSetKeymap(PAD_2, KEY_LEFT, sdlFromHex(value)); + } else if(!strcmp(key, "Joy1_Right")) { + inputSetKeymap(PAD_2, KEY_RIGHT, sdlFromHex(value)); + } else if(!strcmp(key, "Joy1_Up")) { + inputSetKeymap(PAD_2, KEY_UP, sdlFromHex(value)); + } else if(!strcmp(key, "Joy1_Down")) { + inputSetKeymap(PAD_2, KEY_DOWN, sdlFromHex(value)); + } else if(!strcmp(key, "Joy1_A")) { + inputSetKeymap(PAD_2, KEY_BUTTON_A, sdlFromHex(value)); + } else if(!strcmp(key, "Joy1_B")) { + inputSetKeymap(PAD_2, KEY_BUTTON_B, sdlFromHex(value)); + } else if(!strcmp(key, "Joy1_L")) { + inputSetKeymap(PAD_2, KEY_BUTTON_L, sdlFromHex(value)); + } else if(!strcmp(key, "Joy1_R")) { + inputSetKeymap(PAD_2, KEY_BUTTON_R, sdlFromHex(value)); + } else if(!strcmp(key, "Joy1_Start")) { + inputSetKeymap(PAD_2, KEY_BUTTON_START, sdlFromHex(value)); + } else if(!strcmp(key, "Joy1_Select")) { + inputSetKeymap(PAD_2, KEY_BUTTON_SELECT, sdlFromHex(value)); + } else if(!strcmp(key, "Joy1_Speed")) { + inputSetKeymap(PAD_2, KEY_BUTTON_SPEED, sdlFromHex(value)); + } else if(!strcmp(key, "Joy1_Capture")) { + inputSetKeymap(PAD_2, KEY_BUTTON_CAPTURE, sdlFromHex(value)); + } else if(!strcmp(key,"Joy2_Left")) { + inputSetKeymap(PAD_3, KEY_LEFT, sdlFromHex(value)); + } else if(!strcmp(key, "Joy2_Right")) { + inputSetKeymap(PAD_3, KEY_RIGHT, sdlFromHex(value)); + } else if(!strcmp(key, "Joy2_Up")) { + inputSetKeymap(PAD_3, KEY_UP, sdlFromHex(value)); + } else if(!strcmp(key, "Joy2_Down")) { + inputSetKeymap(PAD_3, KEY_DOWN, sdlFromHex(value)); + } else if(!strcmp(key, "Joy2_A")) { + inputSetKeymap(PAD_3, KEY_BUTTON_A, sdlFromHex(value)); + } else if(!strcmp(key, "Joy2_B")) { + inputSetKeymap(PAD_3, KEY_BUTTON_B, sdlFromHex(value)); + } else if(!strcmp(key, "Joy2_L")) { + inputSetKeymap(PAD_3, KEY_BUTTON_L, sdlFromHex(value)); + } else if(!strcmp(key, "Joy2_R")) { + inputSetKeymap(PAD_3, KEY_BUTTON_R, sdlFromHex(value)); + } else if(!strcmp(key, "Joy2_Start")) { + inputSetKeymap(PAD_3, KEY_BUTTON_START, sdlFromHex(value)); + } else if(!strcmp(key, "Joy2_Select")) { + inputSetKeymap(PAD_3, KEY_BUTTON_SELECT, sdlFromHex(value)); + } else if(!strcmp(key, "Joy2_Speed")) { + inputSetKeymap(PAD_3, KEY_BUTTON_SPEED, sdlFromHex(value)); + } else if(!strcmp(key, "Joy2_Capture")) { + inputSetKeymap(PAD_3, KEY_BUTTON_CAPTURE, sdlFromHex(value)); + } else if(!strcmp(key,"Joy3_Left")) { + inputSetKeymap(PAD_4, KEY_LEFT, sdlFromHex(value)); + } else if(!strcmp(key, "Joy3_Right")) { + inputSetKeymap(PAD_4, KEY_RIGHT, sdlFromHex(value)); + } else if(!strcmp(key, "Joy3_Up")) { + inputSetKeymap(PAD_4, KEY_UP, sdlFromHex(value)); + } else if(!strcmp(key, "Joy3_Down")) { + inputSetKeymap(PAD_4, KEY_DOWN, sdlFromHex(value)); + } else if(!strcmp(key, "Joy3_A")) { + inputSetKeymap(PAD_4, KEY_BUTTON_A, sdlFromHex(value)); + } else if(!strcmp(key, "Joy3_B")) { + inputSetKeymap(PAD_4, KEY_BUTTON_B, sdlFromHex(value)); + } else if(!strcmp(key, "Joy3_L")) { + inputSetKeymap(PAD_4, KEY_BUTTON_L, sdlFromHex(value)); + } else if(!strcmp(key, "Joy3_R")) { + inputSetKeymap(PAD_4, KEY_BUTTON_R, sdlFromHex(value)); + } else if(!strcmp(key, "Joy3_Start")) { + inputSetKeymap(PAD_4, KEY_BUTTON_START, sdlFromHex(value)); + } else if(!strcmp(key, "Joy3_Select")) { + inputSetKeymap(PAD_4, KEY_BUTTON_SELECT, sdlFromHex(value)); + } else if(!strcmp(key, "Joy3_Speed")) { + inputSetKeymap(PAD_4, KEY_BUTTON_SPEED, sdlFromHex(value)); + } else if(!strcmp(key, "Joy3_Capture")) { + inputSetKeymap(PAD_4, KEY_BUTTON_CAPTURE, sdlFromHex(value)); + } else if(!strcmp(key, "Joy0_AutoA")) { + inputSetKeymap(PAD_1, KEY_BUTTON_AUTO_A, sdlFromHex(value)); + } else if(!strcmp(key, "Joy0_AutoB")) { + inputSetKeymap(PAD_1, KEY_BUTTON_AUTO_B, sdlFromHex(value)); + } else if(!strcmp(key, "Joy1_AutoA")) { + inputSetKeymap(PAD_2, KEY_BUTTON_AUTO_A, sdlFromHex(value)); + } else if(!strcmp(key, "Joy1_AutoB")) { + inputSetKeymap(PAD_2, KEY_BUTTON_AUTO_B, sdlFromHex(value)); + } else if(!strcmp(key, "Joy2_AutoA")) { + inputSetKeymap(PAD_3, KEY_BUTTON_AUTO_A, sdlFromHex(value)); + } else if(!strcmp(key, "Joy2_AutoB")) { + inputSetKeymap(PAD_3, KEY_BUTTON_AUTO_B, sdlFromHex(value)); + } else if(!strcmp(key, "Joy3_AutoA")) { + inputSetKeymap(PAD_4, KEY_BUTTON_AUTO_A, sdlFromHex(value)); + } else if(!strcmp(key, "Joy3_AutoB")) { + inputSetKeymap(PAD_4, KEY_BUTTON_AUTO_B, sdlFromHex(value)); + } else if(!strcmp(key, "openGL")) { + openGL = sdlFromHex(value); + } else if(!strcmp(key, "Motion_Left")) { + inputSetMotionKeymap(KEY_LEFT, sdlFromHex(value)); + } else if(!strcmp(key, "Motion_Right")) { + inputSetMotionKeymap(KEY_RIGHT, sdlFromHex(value)); + } else if(!strcmp(key, "Motion_Up")) { + inputSetMotionKeymap(KEY_UP, sdlFromHex(value)); + } else if(!strcmp(key, "Motion_Down")) { + inputSetMotionKeymap(KEY_DOWN, sdlFromHex(value)); + } else if(!strcmp(key, "frameSkip")) { + frameSkip = sdlFromHex(value); + if(frameSkip < 0 || frameSkip > 9) + frameSkip = 2; + } else if(!strcmp(key, "gbFrameSkip")) { + gbFrameSkip = sdlFromHex(value); + if(gbFrameSkip < 0 || gbFrameSkip > 9) + gbFrameSkip = 0; + } else if(!strcmp(key, "fullScreen")) { + fullscreen = sdlFromHex(value) ? 1 : 0; + } else if(!strcmp(key, "useBios")) { + useBios = sdlFromHex(value) ? true : false; + } else if(!strcmp(key, "skipBios")) { + skipBios = sdlFromHex(value) ? true : false; + } else if(!strcmp(key, "biosFile")) { + strcpy(biosFileName, value); + } else if(!strcmp(key, "gbBiosFile")) { + strcpy(gbBiosFileName, value); + } else if(!strcmp(key, "filter")) { + filter = (Filter)sdlFromDec(value); + if(filter < kStretch1x || filter >= kInvalidFilter) + filter = kStretch2x; + } else if(!strcmp(key, "disableStatus")) { + disableStatusMessages = sdlFromHex(value) ? true : false; + } else if(!strcmp(key, "borderOn")) { + gbBorderOn = sdlFromHex(value) ? true : false; + } else if(!strcmp(key, "borderAutomatic")) { + gbBorderAutomatic = sdlFromHex(value) ? true : false; + } else if(!strcmp(key, "emulatorType")) { + gbEmulatorType = sdlFromHex(value); + if(gbEmulatorType < 0 || gbEmulatorType > 5) + gbEmulatorType = 1; + } else if(!strcmp(key, "colorOption")) { + gbColorOption = sdlFromHex(value) ? true : false; + } else if(!strcmp(key, "captureDir")) { + sdlCheckDirectory(value); + strcpy(captureDir, value); + } else if(!strcmp(key, "saveDir")) { + sdlCheckDirectory(value); + strcpy(saveDir, value); + } else if(!strcmp(key, "batteryDir")) { + sdlCheckDirectory(value); + strcpy(batteryDir, value); + } else if(!strcmp(key, "captureFormat")) { + captureFormat = sdlFromHex(value); + } else if(!strcmp(key, "soundQuality")) { + int soundQuality = sdlFromHex(value); + switch(soundQuality) { + case 1: + case 2: + case 4: + break; + default: + fprintf(stdout, "Unknown sound quality %d. Defaulting to 22Khz\n", + soundQuality); + soundQuality = 2; + break; + } + soundSetSampleRate(44100 / soundQuality); + } else if(!strcmp(key, "soundEnable")) { + int res = sdlFromHex(value) & 0x30f; + soundSetEnable(res); + } else if(!strcmp(key, "soundStereo")) { + if (sdlFromHex(value)) { + gb_effects_config.stereo = SDL_SOUND_STEREO; + gb_effects_config.enabled = true; + } + } else if(!strcmp(key, "soundEcho")) { + if (sdlFromHex(value)) { + gb_effects_config.echo = SDL_SOUND_ECHO; + gb_effects_config.enabled = true; + } + } else if(!strcmp(key, "soundSurround")) { + if (sdlFromHex(value)) { + gb_effects_config.surround = true; + gb_effects_config.enabled = true; + } + } else if(!strcmp(key, "declicking")) { + gbSoundSetDeclicking(sdlFromHex(value) != 0); + } else if(!strcmp(key, "soundVolume")) { + float volume = sdlFromDec(value) / 100.0; + if (volume < 0.0 || volume > SDL_SOUND_MAX_VOLUME) + volume = 1.0; + soundSetVolume(volume); + } else if(!strcmp(key, "saveType")) { + cpuSaveType = sdlFromHex(value); + if(cpuSaveType < 0 || cpuSaveType > 5) + cpuSaveType = 0; + } else if(!strcmp(key, "flashSize")) { + sdlFlashSize = sdlFromHex(value); + if(sdlFlashSize != 0 && sdlFlashSize != 1) + sdlFlashSize = 0; + } else if(!strcmp(key, "ifbType")) { + ifbType = (IFBFilter)sdlFromHex(value); + if(ifbType < kIFBNone || ifbType >= kInvalidIFBFilter) + ifbType = kIFBNone; + } else if(!strcmp(key, "showSpeed")) { + showSpeed = sdlFromHex(value); + if(showSpeed < 0 || showSpeed > 2) + showSpeed = 1; + } else if(!strcmp(key, "showSpeedTransparent")) { + showSpeedTransparent = sdlFromHex(value); + } else if(!strcmp(key, "autoFrameSkip")) { + autoFrameSkip = sdlFromHex(value); + } else if(!strcmp(key, "pauseWhenInactive")) { + pauseWhenInactive = sdlFromHex(value) ? true : false; + } else if(!strcmp(key, "agbPrint")) { + sdlAgbPrint = sdlFromHex(value); + } else if(!strcmp(key, "rtcEnabled")) { + sdlRtcEnable = sdlFromHex(value); + } else if(!strcmp(key, "rewindTimer")) { + rewindTimer = sdlFromHex(value); + if(rewindTimer < 0 || rewindTimer > 600) + rewindTimer = 0; + rewindTimer *= 6; // convert value to 10 frames multiple + } else if(!strcmp(key, "saveKeysSwitch")) { + sdlSaveKeysSwitch = sdlFromHex(value); + } else if(!strcmp(key, "openGLscale")) { + sdlOpenglScale = sdlFromHex(value); + } else if(!strcmp(key, "autoFireMaxCount")) { + autoFireMaxCount = sdlFromDec(value); + if(autoFireMaxCount < 1) + autoFireMaxCount = 1; + } else { + fprintf(stderr, "Unknown configuration key %s\n", key); + } + } +} + +void sdlOpenGLInit(int w, int h) +{ + float screenAspect = (float) srcWidth / srcHeight, + windowAspect = (float) w / h; + + if(glIsTexture(screenTexture)) + glDeleteTextures(1, &screenTexture); + + glDisable(GL_CULL_FACE); + glEnable(GL_TEXTURE_2D); + + if(windowAspect == screenAspect) + glViewport(0, 0, w, h); + else if (windowAspect < screenAspect) { + int height = (int)(w / screenAspect); + glViewport(0, (h - height) / 2, w, height); + } else { + int width = (int)(h * screenAspect); + glViewport((w - width) / 2, 0, width, h); + } + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + glOrtho(0.0, 1.0, 1.0, 0.0, 0.0, 1.0); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glGenTextures(1, &screenTexture); + glBindTexture(GL_TEXTURE_2D, screenTexture); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, + openGL == 2 ? GL_LINEAR : GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + openGL == 2 ? GL_LINEAR : GL_NEAREST); + + // Calculate texture size as a the smallest working power of two + float n1 = log10((float)destWidth ) / log10( 2.0f); + float n2 = log10((float)destHeight ) / log10( 2.0f); + float n = (n1 > n2)? n1 : n2; + + // round up + if (((float)((int)n)) != n) + n = ((float)((int)n)) + 1.0f; + + textureSize = (int)pow(2.0f, n); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureSize, textureSize, 0, + GL_BGRA, GL_UNSIGNED_BYTE, NULL); + + glClearColor(0.0,0.0,0.0,1.0); + glClear( GL_COLOR_BUFFER_BIT ); +} + +void sdlReadPreferences() +{ + FILE *f = sdlFindFile("vbam.cfg"); + + if(f == NULL) { + fprintf(stdout, "Configuration file NOT FOUND (using defaults)\n"); + return; + } else + fprintf(stdout, "Reading configuration file.\n"); + + sdlReadPreferences(f); + + fclose(f); +} + +static void sdlApplyPerImagePreferences() +{ + FILE *f = sdlFindFile("vba-over.ini"); + if(!f) { + fprintf(stdout, "vba-over.ini NOT FOUND (using emulator settings)\n"); + return; + } else + fprintf(stdout, "Reading vba-over.ini\n"); + + char buffer[7]; + buffer[0] = '['; + buffer[1] = rom[0xac]; + buffer[2] = rom[0xad]; + buffer[3] = rom[0xae]; + buffer[4] = rom[0xaf]; + buffer[5] = ']'; + buffer[6] = 0; + + char readBuffer[2048]; + + bool found = false; + + while(1) { + char *s = fgets(readBuffer, 2048, f); + + if(s == NULL) + break; + + char *p = strchr(s, ';'); + + if(p) + *p = 0; + + char *token = strtok(s, " \t\n\r="); + + if(!token) + continue; + if(strlen(token) == 0) + continue; + + if(!strcmp(token, buffer)) { + found = true; + break; + } + } + + if(found) { + while(1) { + char *s = fgets(readBuffer, 2048, f); + + if(s == NULL) + break; + + char *p = strchr(s, ';'); + if(p) + *p = 0; + + char *token = strtok(s, " \t\n\r="); + if(!token) + continue; + if(strlen(token) == 0) + continue; + + if(token[0] == '[') // starting another image settings + break; + char *value = strtok(NULL, "\t\n\r="); + if(value == NULL) + continue; + + if(!strcmp(token, "rtcEnabled")) + rtcEnable(atoi(value) == 0 ? false : true); + else if(!strcmp(token, "flashSize")) { + int size = atoi(value); + if(size == 0x10000 || size == 0x20000) + flashSetSize(size); + } else if(!strcmp(token, "saveType")) { + int save = atoi(value); + if(save >= 0 && save <= 5) + cpuSaveType = save; + } else if(!strcmp(token, "mirroringEnabled")) { + mirroringEnable = (atoi(value) == 0 ? false : true); + } + } + } + fclose(f); +} + +static int sdlCalculateShift(u32 mask) +{ + int m = 0; + + while(mask) { + m++; + mask >>= 1; + } + + return m-5; +} + +/* returns filename of savestate num, in static buffer (not reentrant, no need to free, + * but value won't survive much - so if you want to remember it, dup it) + * You may use the buffer for something else though - until you call sdlStateName again + */ +static char * sdlStateName(int num) +{ + static char stateName[2048]; + + if(saveDir[0]) + sprintf(stateName, "%s/%s%d.sgm", saveDir, sdlGetFilename(filename), + num+1); + else if (homeDir) + sprintf(stateName, "%s/%s/%s%d.sgm", homeDir, DOT_DIR, sdlGetFilename(filename), num + 1); + else + sprintf(stateName,"%s%d.sgm", filename, num+1); + + return stateName; +} + +void sdlWriteState(int num) +{ + char * stateName; + + stateName = sdlStateName(num); + + if(emulator.emuWriteState) + emulator.emuWriteState(stateName); + + // now we reuse the stateName buffer - 2048 bytes fit in a lot + if (num == SLOT_POS_LOAD_BACKUP) + { + sprintf(stateName, "Current state backed up to %d", num+1); + systemScreenMessage(stateName); + } + else if (num>=0) + { + sprintf(stateName, "Wrote state %d", num+1); + systemScreenMessage(stateName); + } + + systemDrawScreen(); +} + +void sdlReadState(int num) +{ + char * stateName; + + stateName = sdlStateName(num); + if(emulator.emuReadState) + emulator.emuReadState(stateName); + + if (num == SLOT_POS_LOAD_BACKUP) + { + sprintf(stateName, "Last load UNDONE"); + } else + if (num == SLOT_POS_SAVE_BACKUP) + { + sprintf(stateName, "Last save UNDONE"); + } + else + { + sprintf(stateName, "Loaded state %d", num+1); + } + systemScreenMessage(stateName); + + systemDrawScreen(); +} + +/* + * perform savestate exchange + * - put the savestate in slot "to" to slot "backup" (unless backup == to) + * - put the savestate in slot "from" to slot "to" (unless from == to) + */ +void sdlWriteBackupStateExchange(int from, int to, int backup) +{ + char * dmp; + char * stateNameOrig = NULL; + char * stateNameDest = NULL; + char * stateNameBack = NULL; + + dmp = sdlStateName(from); + stateNameOrig = (char*)realloc(stateNameOrig, strlen(dmp) + 1); + strcpy(stateNameOrig, dmp); + dmp = sdlStateName(to); + stateNameDest = (char*)realloc(stateNameDest, strlen(dmp) + 1); + strcpy(stateNameDest, dmp); + dmp = sdlStateName(backup); + stateNameBack = (char*)realloc(stateNameBack, strlen(dmp) + 1); + strcpy(stateNameBack, dmp); + + /* on POSIX, rename would not do anything anyway for identical names, but let's check it ourselves anyway */ + if (to != backup) { + if (-1 == rename(stateNameDest, stateNameBack)) { + fprintf(stdout, "savestate backup: can't backup old state %s to %s", stateNameDest, stateNameBack ); + perror(": "); + } + } + if (to != from) { + if (-1 == rename(stateNameOrig, stateNameDest)) { + fprintf(stdout, "savestate backup: can't move new state %s to %s", stateNameOrig, stateNameDest ); + perror(": "); + } + } + + systemConsoleMessage("Savestate store and backup committed"); // with timestamp and newline + fprintf(stdout, "to slot %d, backup in %d, using temporary slot %d\n", to+1, backup+1, from+1); + + free(stateNameOrig); + free(stateNameDest); + free(stateNameBack); +} + +void sdlWriteBattery() +{ + char buffer[1048]; + + if(batteryDir[0]) + sprintf(buffer, "%s/%s.sav", batteryDir, sdlGetFilename(filename)); + else if (homeDir) + sprintf(buffer, "%s/%s/%s.sav", homeDir, DOT_DIR, sdlGetFilename(filename)); + else + sprintf(buffer, "%s.sav", filename); + + emulator.emuWriteBattery(buffer); + + systemScreenMessage("Wrote battery"); +} + +void sdlReadBattery() +{ + char buffer[1048]; + + if(batteryDir[0]) + sprintf(buffer, "%s/%s.sav", batteryDir, sdlGetFilename(filename)); + else if (homeDir) + sprintf(buffer, "%s/%s/%s.sav", homeDir, DOT_DIR, sdlGetFilename(filename)); + else + sprintf(buffer, "%s.sav", filename); + + bool res = false; + + res = emulator.emuReadBattery(buffer); + + if(res) + systemScreenMessage("Loaded battery"); +} + +void sdlReadDesktopVideoMode() { + const SDL_VideoInfo* vInfo = SDL_GetVideoInfo(); + desktopWidth = vInfo->current_w; + desktopHeight = vInfo->current_h; +} + +void sdlInitVideo() { + int flags; + int screenWidth; + int screenHeight; + + filter_enlarge = getFilterEnlargeFactor(filter); + + destWidth = filter_enlarge * srcWidth; + destHeight = filter_enlarge * srcHeight; + + flags = SDL_ANYFORMAT | (fullscreen ? SDL_FULLSCREEN : 0); + if(openGL) { + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + flags |= SDL_OPENGL | SDL_RESIZABLE; + } else + flags |= SDL_HWSURFACE | SDL_DOUBLEBUF; + + if (fullscreen && openGL) { + screenWidth = desktopWidth; + screenHeight = desktopHeight; + } else { + screenWidth = destWidth; + screenHeight = destHeight; + } + + surface = SDL_SetVideoMode(screenWidth, screenHeight, 0, flags); + + if(surface == NULL) { + systemMessage(0, "Failed to set video mode"); + SDL_Quit(); + exit(-1); + } + + u32 rmask, gmask, bmask; + + if(openGL) { + #if SDL_BYTEORDER == SDL_LIL_ENDIAN /* OpenGL RGBA masks */ + rmask = 0x000000FF; + gmask = 0x0000FF00; + bmask = 0x00FF0000; + #else + rmask = 0xFF000000; + gmask = 0x00FF0000; + bmask = 0x0000FF00; + #endif + } else { + rmask = surface->format->Rmask; + gmask = surface->format->Gmask; + bmask = surface->format->Bmask; + } + + systemRedShift = sdlCalculateShift(rmask); + systemGreenShift = sdlCalculateShift(gmask); + systemBlueShift = sdlCalculateShift(bmask); + + if(openGL) { + // Align to BGRA instead of ABGR + systemRedShift += 8; + systemGreenShift += 8; + systemBlueShift += 8; + } + + systemColorDepth = surface->format->BitsPerPixel; + + if(systemColorDepth == 16) { + srcPitch = srcWidth*2 + 4; + } else { + if(systemColorDepth == 32) + srcPitch = srcWidth*4 + 4; + else + srcPitch = srcWidth*3; + } + + if(openGL) { + int scaledWidth = screenWidth * sdlOpenglScale; + int scaledHeight = screenHeight * sdlOpenglScale; + + free(filterPix); + filterPix = (u8 *)calloc(1, (systemColorDepth >> 3) * destWidth * destHeight); + sdlOpenGLInit(screenWidth, screenHeight); + + if ( (!fullscreen) + && sdlOpenglScale > 1 + && scaledWidth < desktopWidth + && scaledHeight < desktopHeight + ) { + SDL_SetVideoMode(scaledWidth, scaledHeight, 0, + SDL_OPENGL | SDL_RESIZABLE | + (fullscreen ? SDL_FULLSCREEN : 0)); + sdlOpenGLInit(scaledWidth, scaledHeight); + /* xKiv: it would seem that SDL_RESIZABLE causes the *previous* dimensions to be immediately + * reported back via the SDL_VIDEORESIZE event + */ + ignore_first_resize_event = 1; + } + } + +} + +#define MOD_KEYS (KMOD_CTRL|KMOD_SHIFT|KMOD_ALT|KMOD_META) +#define MOD_NOCTRL (KMOD_SHIFT|KMOD_ALT|KMOD_META) +#define MOD_NOALT (KMOD_CTRL|KMOD_SHIFT|KMOD_META) +#define MOD_NOSHIFT (KMOD_CTRL|KMOD_ALT|KMOD_META) + + +/* + * 04.02.2008 (xKiv): factored out from sdlPollEvents + * + */ +void change_rewind(int howmuch) +{ + if( emulating && emulator.emuReadMemState && rewindMemory + && rewindCount + ) { + rewindPos = (rewindPos + rewindCount + howmuch) % rewindCount; + emulator.emuReadMemState( + &rewindMemory[REWIND_SIZE*rewindPos], + REWIND_SIZE + ); + rewindCounter = 0; + { + char rewindMsgBuffer[50]; + snprintf(rewindMsgBuffer, 50, "Rewind to %1d [%d]", rewindPos+1, rewindSerials[rewindPos]); + rewindMsgBuffer[49] = 0; + systemConsoleMessage(rewindMsgBuffer); + } + } +} + +/* + * handle the F* keys (for savestates) + * given the slot number and state of the SHIFT modifier, save or restore + * (in savemode 3, saveslot is stored in saveSlotPosition and num means: + * 4 .. F5: decrease slot number (down to 0) + * 5 .. F6: increase slot number (up to 7, because 8 and 9 are reserved for backups) + * 6 .. F7: save state + * 7 .. F8: load state + * (these *should* be configurable) + * other keys are ignored + * ) + */ +static void sdlHandleSavestateKey(int num, int shifted) +{ + int action = -1; + // 0: load + // 1: save + int backuping = 1; // controls whether we are doing savestate backups + + if ( sdlSaveKeysSwitch == 2 ) + { + // ignore "shifted" + switch (num) + { + // nb.: saveSlotPosition is base 0, but to the user, we show base 1 indexes (F## numbers)! + case 4: + if (saveSlotPosition > 0) + { + saveSlotPosition--; + fprintf(stdout, "Changed savestate slot to %d.\n", saveSlotPosition + 1); + } else + fprintf(stderr, "Can't decrease slotnumber below 1.\n"); + return; // handled + case 5: + if (saveSlotPosition < 7) + { + saveSlotPosition++; + fprintf(stdout, "Changed savestate slot to %d.\n", saveSlotPosition + 1); + } else + fprintf(stderr, "Can't increase slotnumber above 8.\n"); + return; // handled + case 6: + action = 1; // save + break; + case 7: + action = 0; // load + break; + default: + // explicitly ignore + return; // handled + } + } + + if (sdlSaveKeysSwitch == 0 ) /* "classic" VBA: shifted is save */ + { + if (shifted) + action = 1; // save + else action = 0; // load + saveSlotPosition = num; + } + if (sdlSaveKeysSwitch == 1 ) /* "xKiv" VBA: shifted is load */ + { + if (!shifted) + action = 1; // save + else action = 0; // load + saveSlotPosition = num; + } + + if (action < 0 || action > 1) + { + fprintf( + stderr, + "sdlHandleSavestateKey(%d,%d), mode %d: unexpected action %d.\n", + num, + shifted, + sdlSaveKeysSwitch, + action + ); + } + + if (action) + { /* save */ + if (backuping) + { + sdlWriteState(-1); // save to a special slot + sdlWriteBackupStateExchange(-1, saveSlotPosition, SLOT_POS_SAVE_BACKUP); // F10 + } else { + sdlWriteState(saveSlotPosition); + } + } else { /* load */ + if (backuping) + { + /* first back up where we are now */ + sdlWriteState(SLOT_POS_LOAD_BACKUP); // F9 + } + sdlReadState(saveSlotPosition); + } + +} // sdlHandleSavestateKey + +void sdlPollEvents() +{ + SDL_Event event; + while(SDL_PollEvent(&event)) { + switch(event.type) { + case SDL_QUIT: + emulating = 0; + break; + case SDL_VIDEORESIZE: + if (ignore_first_resize_event) + { + ignore_first_resize_event = 0; + break; + } + if (openGL) + { + SDL_SetVideoMode(event.resize.w, event.resize.h, 0, + SDL_OPENGL | SDL_RESIZABLE | + (fullscreen ? SDL_FULLSCREEN : 0)); + sdlOpenGLInit(event.resize.w, event.resize.h); + } + break; + case SDL_ACTIVEEVENT: + if(pauseWhenInactive && (event.active.state & SDL_APPINPUTFOCUS)) { + active = event.active.gain; + if(active) { + if(!paused) { + if(emulating) + soundResume(); + } + } else { + wasPaused = true; + if(pauseWhenInactive) { + if(emulating) + soundPause(); + } + + memset(delta,255,delta_size); + } + } + break; + case SDL_MOUSEMOTION: + case SDL_MOUSEBUTTONUP: + case SDL_MOUSEBUTTONDOWN: + if(fullscreen) { + SDL_ShowCursor(SDL_ENABLE); + mouseCounter = 120; + } + break; + case SDL_JOYHATMOTION: + case SDL_JOYBUTTONDOWN: + case SDL_JOYBUTTONUP: + case SDL_JOYAXISMOTION: + case SDL_KEYDOWN: + inputProcessSDLEvent(event); + break; + case SDL_KEYUP: + switch(event.key.keysym.sym) { + case SDLK_r: + if(!(event.key.keysym.mod & MOD_NOCTRL) && + (event.key.keysym.mod & KMOD_CTRL)) { + if(emulating) { + emulator.emuReset(); + + systemScreenMessage("Reset"); + } + } + break; + case SDLK_b: + if(!(event.key.keysym.mod & MOD_NOCTRL) && + (event.key.keysym.mod & KMOD_CTRL)) + change_rewind(-1); + break; + case SDLK_v: + if(!(event.key.keysym.mod & MOD_NOCTRL) && + (event.key.keysym.mod & KMOD_CTRL)) + change_rewind(+1); + break; + case SDLK_h: + if(!(event.key.keysym.mod & MOD_NOCTRL) && + (event.key.keysym.mod & KMOD_CTRL)) + change_rewind(0); + break; + case SDLK_j: + if(!(event.key.keysym.mod & MOD_NOCTRL) && + (event.key.keysym.mod & KMOD_CTRL)) + change_rewind( (rewindTopPos - rewindPos) * ((rewindTopPos>rewindPos) ? +1:-1) ); + break; + case SDLK_e: + if(!(event.key.keysym.mod & MOD_NOCTRL) && + (event.key.keysym.mod & KMOD_CTRL)) { + cheatsEnabled = !cheatsEnabled; + systemConsoleMessage(cheatsEnabled?"Cheats on":"Cheats off"); + } + break; + + case SDLK_s: + if(!(event.key.keysym.mod & MOD_NOCTRL) && + (event.key.keysym.mod & KMOD_CTRL) + ) { + if (sdlSoundToggledOff) { // was off + // restore saved state + soundSetEnable( sdlSoundToggledOff ); + sdlSoundToggledOff = 0; + systemConsoleMessage("Sound toggled on"); + } else { // was on + sdlSoundToggledOff = soundGetEnable(); + soundSetEnable( 0 ); + systemConsoleMessage("Sound toggled off"); + if (!sdlSoundToggledOff) { + sdlSoundToggledOff = 0x3ff; + } + } + } + break; + case SDLK_KP_DIVIDE: + sdlChangeVolume(-0.1); + break; + case SDLK_KP_MULTIPLY: + sdlChangeVolume(0.1); + break; + case SDLK_KP_MINUS: + if (gb_effects_config.stereo > 0.0) { + gb_effects_config.stereo = 0.0; + if (gb_effects_config.echo == 0.0 && !gb_effects_config.surround) { + gb_effects_config.enabled = 0; + } + systemScreenMessage("Stereo off"); + } else { + gb_effects_config.stereo = SDL_SOUND_STEREO; + gb_effects_config.enabled = true; + systemScreenMessage("Stereo on"); + } + break; + case SDLK_KP_PLUS: + if (gb_effects_config.echo > 0.0) { + gb_effects_config.echo = 0.0; + if (gb_effects_config.stereo == 0.0 && !gb_effects_config.surround) { + gb_effects_config.enabled = false; + } + systemScreenMessage("Echo off"); + } else { + gb_effects_config.echo = SDL_SOUND_ECHO; + gb_effects_config.enabled = true; + systemScreenMessage("Echo on"); + } + break; + case SDLK_KP_ENTER: + if (gb_effects_config.surround) { + gb_effects_config.surround = false; + if (gb_effects_config.stereo == 0.0 && gb_effects_config.echo == 0.0) { + gb_effects_config.enabled = false; + } + systemScreenMessage("Surround off"); + } else { + gb_effects_config.surround =true; + gb_effects_config.enabled = true; + systemScreenMessage("Surround on"); + } + break; + + case SDLK_p: + if(!(event.key.keysym.mod & MOD_NOCTRL) && + (event.key.keysym.mod & KMOD_CTRL)) { + paused = !paused; + SDL_PauseAudio(paused); + if(paused) + wasPaused = true; + systemConsoleMessage(paused?"Pause on":"Pause off"); + } + break; + case SDLK_ESCAPE: + emulating = 0; + break; + case SDLK_f: + if(!(event.key.keysym.mod & MOD_NOCTRL) && + (event.key.keysym.mod & KMOD_CTRL)) { + fullscreen = !fullscreen; + sdlInitVideo(); + } + break; + case SDLK_g: + if(!(event.key.keysym.mod & MOD_NOCTRL) && + (event.key.keysym.mod & KMOD_CTRL)) { + filterFunction = 0; + while (!filterFunction) + { + filter = (Filter)((filter + 1) % kInvalidFilter); + filterFunction = initFilter(filter, systemColorDepth, srcWidth); + } + if (getFilterEnlargeFactor(filter) != filter_enlarge) + sdlInitVideo(); + systemScreenMessage(getFilterName(filter)); + } + break; + case SDLK_F11: + if(dbgMain != debuggerMain) { + if(armState) { + armNextPC -= 4; + reg[15].I -= 4; + } else { + armNextPC -= 2; + reg[15].I -= 2; + } + } + debugger = true; + break; + case SDLK_F1: + case SDLK_F2: + case SDLK_F3: + case SDLK_F4: + case SDLK_F5: + case SDLK_F6: + case SDLK_F7: + case SDLK_F8: + if(!(event.key.keysym.mod & MOD_NOSHIFT) && + (event.key.keysym.mod & KMOD_SHIFT)) { + sdlHandleSavestateKey( event.key.keysym.sym - SDLK_F1, 1); // with SHIFT + } else if(!(event.key.keysym.mod & MOD_KEYS)) { + sdlHandleSavestateKey( event.key.keysym.sym - SDLK_F1, 0); // without SHIFT + } + break; + /* backups - only load */ + case SDLK_F9: + /* F9 is "load backup" - saved state from *just before* the last restore */ + if ( ! (event.key.keysym.mod & MOD_NOSHIFT) ) /* must work with or without shift, but only without other modifiers*/ + { + sdlReadState(SLOT_POS_LOAD_BACKUP); + } + break; + case SDLK_F10: + /* F10 is "save backup" - what was in the last overwritten savestate before we overwrote it*/ + if ( ! (event.key.keysym.mod & MOD_NOSHIFT) ) /* must work with or without shift, but only without other modifiers*/ + { + sdlReadState(SLOT_POS_SAVE_BACKUP); + } + break; + case SDLK_1: + case SDLK_2: + case SDLK_3: + case SDLK_4: + if(!(event.key.keysym.mod & MOD_NOALT) && + (event.key.keysym.mod & KMOD_ALT)) { + const char *disableMessages[4] = + { "autofire A disabled", + "autofire B disabled", + "autofire R disabled", + "autofire L disabled"}; + const char *enableMessages[4] = + { "autofire A", + "autofire B", + "autofire R", + "autofire L"}; + + EKey k = KEY_BUTTON_A; + if (event.key.keysym.sym == SDLK_1) + k = KEY_BUTTON_A; + else if (event.key.keysym.sym == SDLK_2) + k = KEY_BUTTON_B; + else if (event.key.keysym.sym == SDLK_3) + k = KEY_BUTTON_R; + else if (event.key.keysym.sym == SDLK_4) + k = KEY_BUTTON_L; + + if(inputToggleAutoFire(k)) { + systemScreenMessage(enableMessages[event.key.keysym.sym - SDLK_1]); + } else { + systemScreenMessage(disableMessages[event.key.keysym.sym - SDLK_1]); + } + } else if(!(event.key.keysym.mod & MOD_NOCTRL) && + (event.key.keysym.mod & KMOD_CTRL)) { + int mask = 0x0100 << (event.key.keysym.sym - SDLK_1); + layerSettings ^= mask; + layerEnable = DISPCNT & layerSettings; + CPUUpdateRenderBuffers(false); + } + break; + case SDLK_5: + case SDLK_6: + case SDLK_7: + case SDLK_8: + if(!(event.key.keysym.mod & MOD_NOCTRL) && + (event.key.keysym.mod & KMOD_CTRL)) { + int mask = 0x0100 << (event.key.keysym.sym - SDLK_1); + layerSettings ^= mask; + layerEnable = DISPCNT & layerSettings; + } + break; + case SDLK_n: + if(!(event.key.keysym.mod & MOD_NOCTRL) && + (event.key.keysym.mod & KMOD_CTRL)) { + if(paused) + paused = false; + pauseNextFrame = true; + } + break; + default: + break; + } + inputProcessSDLEvent(event); + break; + } + } +} + +#if WITH_LIRC +void lircCheckInput(void) +{ + if(LIRCEnabled) { + //setup a poll (poll.h) + struct pollfd pollLIRC; + //values fd is the pointer gotten from lircinit and events is what way + pollLIRC.fd = LIRCfd; + pollLIRC.events = POLLIN; + //run the poll + if( poll( &pollLIRC, 1, 0 ) > 0 ) { + //poll retrieved something + char *CodeLIRC; + char *CmdLIRC; + int ret; //dunno??? + if( lirc_nextcode(&CodeLIRC) == 0 && CodeLIRC != NULL ) { + //retrieve the commands + while( ( ret = lirc_code2char( LIRCConfigInfo, CodeLIRC, &CmdLIRC ) ) == 0 && CmdLIRC != NULL ) { + //change the text to uppercase + char *CmdLIRC_Pointer = CmdLIRC; + while(*CmdLIRC_Pointer != '\0') { + *CmdLIRC_Pointer = toupper(*CmdLIRC_Pointer); + CmdLIRC_Pointer++; + } + + if( strcmp( CmdLIRC, "QUIT" ) == 0 ) { + emulating = 0; + } else if( strcmp( CmdLIRC, "PAUSE" ) == 0 ) { + paused = !paused; + SDL_PauseAudio(paused); + if(paused) wasPaused = true; + systemConsoleMessage( paused?"Pause on":"Pause off" ); + systemScreenMessage( paused?"Pause on":"Pause off" ); + } else if( strcmp( CmdLIRC, "RESET" ) == 0 ) { + if(emulating) { + emulator.emuReset(); + systemScreenMessage("Reset"); + } + } else if( strcmp( CmdLIRC, "MUTE" ) == 0 ) { + if (sdlSoundToggledOff) { // was off + // restore saved state + soundSetEnable( sdlSoundToggledOff ); + sdlSoundToggledOff = 0; + systemConsoleMessage("Sound toggled on"); + } else { // was on + sdlSoundToggledOff = soundGetEnable(); + soundSetEnable( 0 ); + systemConsoleMessage("Sound toggled off"); + if (!sdlSoundToggledOff) { + sdlSoundToggledOff = 0x3ff; + } + } + } else if( strcmp( CmdLIRC, "VOLUP" ) == 0 ) { + sdlChangeVolume(0.1); + } else if( strcmp( CmdLIRC, "VOLDOWN" ) == 0 ) { + sdlChangeVolume(-0.1); + } else if( strcmp( CmdLIRC, "LOADSTATE" ) == 0 ) { + sdlReadState(saveSlotPosition); + } else if( strcmp( CmdLIRC, "SAVESTATE" ) == 0 ) { + sdlWriteState(saveSlotPosition); + } else if( strcmp( CmdLIRC, "1" ) == 0 ) { + saveSlotPosition = 0; + systemScreenMessage("Selected State 1"); + } else if( strcmp( CmdLIRC, "2" ) == 0 ) { + saveSlotPosition = 1; + systemScreenMessage("Selected State 2"); + } else if( strcmp( CmdLIRC, "3" ) == 0 ) { + saveSlotPosition = 2; + systemScreenMessage("Selected State 3"); + } else if( strcmp( CmdLIRC, "4" ) == 0 ) { + saveSlotPosition = 3; + systemScreenMessage("Selected State 4"); + } else if( strcmp( CmdLIRC, "5" ) == 0 ) { + saveSlotPosition = 4; + systemScreenMessage("Selected State 5"); + } else if( strcmp( CmdLIRC, "6" ) == 0 ) { + saveSlotPosition = 5; + systemScreenMessage("Selected State 6"); + } else if( strcmp( CmdLIRC, "7" ) == 0 ) { + saveSlotPosition = 6; + systemScreenMessage("Selected State 7"); + } else if( strcmp( CmdLIRC, "8" ) == 0 ) { + saveSlotPosition = 7; + systemScreenMessage("Selected State 8"); + } else { + //do nothing + } + } + //we dont need this code nomore + free(CodeLIRC); + } + } + } +} +#endif + +void usage(char *cmd) +{ + printf("%s [option ...] file\n", cmd); + printf("\ +\n\ +Options:\n\ + -O, --opengl=MODE Set OpenGL texture filter\n\ + --no-opengl 0 - Disable OpenGL\n\ + --opengl-nearest 1 - No filtering\n\ + --opengl-bilinear 2 - Bilinear filtering\n\ + -F, --fullscreen Full screen\n\ + -G, --gdb=PROTOCOL GNU Remote Stub mode:\n\ + tcp - use TCP at port 55555\n\ + tcp:PORT - use TCP at port PORT\n\ + pipe - use pipe transport\n\ + -I, --ifb-filter=FILTER Select interframe blending filter:\n\ +"); + for (int i = 0; i < (int)kInvalidIFBFilter; i++) + printf(" %d - %s\n", i, getIFBFilterName((IFBFilter)i)); + printf("\ + -N, --no-debug Don't parse debug information\n\ + -S, --flash-size=SIZE Set the Flash size\n\ + --flash-64k 0 - 64K Flash\n\ + --flash-128k 1 - 128K Flash\n\ + -T, --throttle=THROTTLE Set the desired throttle (5...1000)\n\ + -b, --bios=BIOS Use given bios file\n\ + -c, --config=FILE Read the given configuration file\n\ + -d, --debug Enter debugger\n\ + -f, --filter=FILTER Select filter:\n\ +"); + for (int i = 0; i < (int)kInvalidFilter; i++) + printf(" %d - %s\n", i, getFilterName((Filter)i)); + printf("\ + -h, --help Print this help\n\ + -i, --patch=PATCH Apply given patch\n\ + -p, --profile=[HERTZ] Enable profiling\n\ + -s, --frameskip=FRAMESKIP Set frame skip (0...9)\n\ + -t, --save-type=TYPE Set the available save type\n\ + --save-auto 0 - Automatic (EEPROM, SRAM, FLASH)\n\ + --save-eeprom 1 - EEPROM\n\ + --save-sram 2 - SRAM\n\ + --save-flash 3 - FLASH\n\ + --save-sensor 4 - EEPROM+Sensor\n\ + --save-none 5 - NONE\n\ + -v, --verbose=VERBOSE Set verbose logging (trace.log)\n\ + 1 - SWI\n\ + 2 - Unaligned memory access\n\ + 4 - Illegal memory write\n\ + 8 - Illegal memory read\n\ + 16 - DMA 0\n\ + 32 - DMA 1\n\ + 64 - DMA 2\n\ + 128 - DMA 3\n\ + 256 - Undefined instruction\n\ + 512 - AGBPrint messages\n\ +\n\ +Long options only:\n\ + --agb-print Enable AGBPrint support\n\ + --auto-frameskip Enable auto frameskipping\n\ + --no-agb-print Disable AGBPrint support\n\ + --no-auto-frameskip Disable auto frameskipping\n\ + --no-patch Do not automatically apply patch\n\ + --no-pause-when-inactive Don't pause when inactive\n\ + --no-rtc Disable RTC support\n\ + --no-show-speed Don't show emulation speed\n\ + --no-throttle Disable throttle\n\ + --pause-when-inactive Pause when inactive\n\ + --rtc Enable RTC support\n\ + --show-speed-normal Show emulation speed\n\ + --show-speed-detailed Show detailed speed data\n\ + --cheat 'CHEAT' Add a cheat\n\ +"); +} + +/* + * 04.02.2008 (xKiv) factored out, reformatted, more usefuler rewinds browsing scheme + */ +void handleRewinds() +{ + int curSavePos; // where we are saving today [1] + + rewindCount++; // how many rewinds will be stored after this store + if(rewindCount > REWIND_NUM) + rewindCount = REWIND_NUM; + + curSavePos = (rewindTopPos + 1) % rewindCount; // [1] depends on previous + + if( + emulator.emuWriteMemState + && + emulator.emuWriteMemState( + &rewindMemory[curSavePos*REWIND_SIZE], + REWIND_SIZE + ) + ) { + char rewMsgBuf[100]; + snprintf(rewMsgBuf, 100, "Remembered rewind %1d (of %1d), serial %d.", curSavePos+1, rewindCount, rewindSerial); + rewMsgBuf[99] = 0; + systemConsoleMessage(rewMsgBuf); + rewindSerials[curSavePos] = rewindSerial; + + // set up next rewind save + // - don't clobber the current rewind position, unless it is the original top + if (rewindPos == rewindTopPos) { + rewindPos = curSavePos; + } + // - new identification and top + rewindSerial++; + rewindTopPos = curSavePos; + // for the rest of the code, rewindTopPos will be where the newest rewind got stored + } +} + +int main(int argc, char **argv) +{ + fprintf(stdout, "VBA-M version %s [SDL]\n", VERSION); + + arg0 = argv[0]; + + captureDir[0] = 0; + saveDir[0] = 0; + batteryDir[0] = 0; + + int op = -1; + + frameSkip = 2; + gbBorderOn = 0; + + parseDebug = true; + + gb_effects_config.stereo = 0.0; + gb_effects_config.echo = 0.0; + gb_effects_config.surround = false; + gb_effects_config.enabled = false; + + char buf[1024]; + struct stat s; + +#ifndef _WIN32 + // Get home dir + homeDir = getenv("HOME"); + snprintf(buf, 1024, "%s/%s", homeDir, DOT_DIR); + // Make dot dir if not existent + if (stat(buf, &s) == -1 || !S_ISDIR(s.st_mode)) + mkdir(buf, 0755); +#else + homeDir = 0; +#endif + + sdlReadPreferences(); + + sdlPrintUsage = 0; + + while((op = getopt_long(argc, + argv, + "FNO:T:Y:G:I:D:b:c:df:hi:p::s:t:v:", + sdlOptions, + NULL)) != -1) { + switch(op) { + case 0: + // long option already processed by getopt_long + break; + case 1000: + // --cheat + if (sdlPreparedCheats >= MAX_CHEATS) { + fprintf(stderr, "Warning: cannot add more than %d cheats.\n", MAX_CHEATS); + break; + } + { + char * cpy; + cpy = (char *)malloc(1 + strlen(optarg)); + strcpy(cpy, optarg); + sdlPreparedCheatCodes[sdlPreparedCheats++] = cpy; + } + break; + case 1001: + // --autofire + autoFireMaxCount = sdlFromDec(optarg); + if (autoFireMaxCount < 1) + autoFireMaxCount = 1; + break; + case 'b': + useBios = true; + if(optarg == NULL) { + fprintf(stderr, "Missing BIOS file name\n"); + exit(-1); + } + strcpy(biosFileName, optarg); + break; + case 'c': + { + if(optarg == NULL) { + fprintf(stderr, "Missing config file name\n"); + exit(-1); + } + FILE *f = fopen(optarg, "r"); + if(f == NULL) { + fprintf(stderr, "File not found %s\n", optarg); + exit(-1); + } + sdlReadPreferences(f); + fclose(f); + } + break; + case 'd': + debugger = true; + break; + case 'h': + sdlPrintUsage = 1; + break; + case 'i': + if(optarg == NULL) { + fprintf(stderr, "Missing patch name\n"); + exit(-1); + } + if (sdl_patch_num >= PATCH_MAX_NUM) { + fprintf(stderr, "Too many patches given at %s (max is %d). Ignoring.\n", optarg, PATCH_MAX_NUM); + } else { + sdl_patch_names[sdl_patch_num] = (char *)malloc(1 + strlen(optarg)); + strcpy(sdl_patch_names[sdl_patch_num], optarg); + sdl_patch_num++; + } + break; + case 'G': + dbgMain = remoteStubMain; + dbgSignal = remoteStubSignal; + dbgOutput = remoteOutput; + debugger = true; + debuggerStub = true; + if(optarg) { + char *s = optarg; + if(strncmp(s,"tcp:", 4) == 0) { + s+=4; + int port = atoi(s); + remoteSetProtocol(0); + remoteSetPort(port); + } else if(strcmp(s,"tcp") == 0) { + remoteSetProtocol(0); + } else if(strcmp(s, "pipe") == 0) { + remoteSetProtocol(1); + } else { + fprintf(stderr, "Unknown protocol %s\n", s); + exit(-1); + } + } else { + remoteSetProtocol(0); + } + break; + case 'N': + parseDebug = false; + break; + case 'D': + if(optarg) { + systemDebug = atoi(optarg); + } else { + systemDebug = 1; + } + break; + case 'F': + fullscreen = 1; + mouseCounter = 120; + break; + case 'f': + if(optarg) { + filter = (Filter)atoi(optarg); + } else { + filter = kStretch2x; + } + break; + case 'I': + if(optarg) { + ifbType = (IFBFilter)atoi(optarg); + } else { + ifbType = kIFBNone; + } + break; + case 'p': +#ifdef PROFILING + if(optarg) { + cpuEnableProfiling(atoi(optarg)); + } else + cpuEnableProfiling(100); +#endif + break; + case 'S': + sdlFlashSize = atoi(optarg); + if(sdlFlashSize < 0 || sdlFlashSize > 1) + sdlFlashSize = 0; + break; + case 's': + if(optarg) { + int a = atoi(optarg); + if(a >= 0 && a <= 9) { + gbFrameSkip = a; + frameSkip = a; + } + } else { + frameSkip = 2; + gbFrameSkip = 0; + } + break; + case 't': + if(optarg) { + int a = atoi(optarg); + if(a < 0 || a > 5) + a = 0; + cpuSaveType = a; + } + break; + case 'v': + if(optarg) { + systemVerbose = atoi(optarg); + } else + systemVerbose = 0; + break; + case '?': + sdlPrintUsage = 1; + break; + case 'O': + if(optarg) { + openGL = atoi(optarg); + if (openGL < 0 || openGL > 2) + openGL = 1; + } else + openGL = 0; + break; + + } + } + + if(sdlPrintUsage) { + usage(argv[0]); + exit(-1); + } + + if(rewindTimer) { + rewindMemory = (char *)malloc(REWIND_NUM*REWIND_SIZE); + rewindSerials = (int *)calloc(REWIND_NUM, sizeof(int)); // init to zeroes + } + + if(sdlFlashSize == 0) + flashSetSize(0x10000); + else + flashSetSize(0x20000); + + rtcEnable(sdlRtcEnable ? true : false); + agbPrintEnable(sdlAgbPrint ? true : false); + + if(!debuggerStub) { + if(optind >= argc) { + systemMessage(0,"Missing image name"); + usage(argv[0]); + exit(-1); + } + } + + for(int i = 0; i < 24;) { + systemGbPalette[i++] = (0x1f) | (0x1f << 5) | (0x1f << 10); + systemGbPalette[i++] = (0x15) | (0x15 << 5) | (0x15 << 10); + systemGbPalette[i++] = (0x0c) | (0x0c << 5) | (0x0c << 10); + systemGbPalette[i++] = 0; + } + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + if(optind < argc) { + char *szFile = argv[optind]; + u32 len = strlen(szFile); + if (len > SYSMSG_BUFFER_SIZE) + { + fprintf(stderr,"%s :%s: File name too long\n",argv[0],szFile); + exit(-1); + } + + utilStripDoubleExtension(szFile, filename); + char *p = strrchr(filename, '.'); + + if(p) + *p = 0; + + if (sdlAutoPatch && sdl_patch_num == 0) + { + char * tmp; + // no patch given yet - look for ROMBASENAME.ips + tmp = (char *)malloc(strlen(filename) + 4 + 1); + sprintf(tmp, "%s.ips", filename); + sdl_patch_names[sdl_patch_num] = tmp; + sdl_patch_num++; + + // no patch given yet - look for ROMBASENAME.ups + tmp = (char *)malloc(strlen(filename) + 4 + 1); + sprintf(tmp, "%s.ups", filename); + sdl_patch_names[sdl_patch_num] = tmp; + sdl_patch_num++; + + // no patch given yet - look for ROMBASENAME.ppf + tmp = (char *)malloc(strlen(filename) + 4 + 1); + sprintf(tmp, "%s.ppf", filename); + sdl_patch_names[sdl_patch_num] = tmp; + sdl_patch_num++; + } + + soundInit(); + + bool failed = false; + + IMAGE_TYPE type = utilFindType(szFile); + + if(type == IMAGE_UNKNOWN) { + systemMessage(0, "Unknown file type %s", szFile); + exit(-1); + } + cartridgeType = (int)type; + + if(type == IMAGE_GB) { + failed = !gbLoadRom(szFile); + if(!failed) { + gbGetHardwareType(); + + // used for the handling of the gb Boot Rom + if (gbHardware & 5) + gbCPUInit(gbBiosFileName, useBios); + + cartridgeType = IMAGE_GB; + emulator = GBSystem; + int size = gbRomSize, patchnum; + for (patchnum = 0; patchnum < sdl_patch_num; patchnum++) { + fprintf(stdout, "Trying patch %s%s\n", sdl_patch_names[patchnum], + applyPatch(sdl_patch_names[patchnum], &gbRom, &size) ? " [success]" : ""); + } + if(size != gbRomSize) { + extern bool gbUpdateSizes(); + gbUpdateSizes(); + gbReset(); + } + gbReset(); + } + } else if(type == IMAGE_GBA) { + int size = CPULoadRom(szFile); + failed = (size == 0); + if(!failed) { + sdlApplyPerImagePreferences(); + + doMirroring(mirroringEnable); + + cartridgeType = 0; + emulator = GBASystem; + + CPUInit(biosFileName, useBios); + int patchnum; + for (patchnum = 0; patchnum < sdl_patch_num; patchnum++) { + fprintf(stdout, "Trying patch %s%s\n", sdl_patch_names[patchnum], + applyPatch(sdl_patch_names[patchnum], &rom, &size) ? " [success]" : ""); + } + CPUReset(); + } + } + + if(failed) { + systemMessage(0, "Failed to load file %s", szFile); + exit(-1); + } + } else { + soundInit(); + cartridgeType = 0; + strcpy(filename, "gnu_stub"); + rom = (u8 *)malloc(0x2000000); + workRAM = (u8 *)calloc(1, 0x40000); + bios = (u8 *)calloc(1,0x4000); + internalRAM = (u8 *)calloc(1,0x8000); + paletteRAM = (u8 *)calloc(1,0x400); + vram = (u8 *)calloc(1, 0x20000); + oam = (u8 *)calloc(1, 0x400); + pix = (u8 *)calloc(1, 4 * 241 * 162); + ioMem = (u8 *)calloc(1, 0x400); + + emulator = GBASystem; + + CPUInit(biosFileName, useBios); + CPUReset(); + } + + sdlReadBattery(); + + if(debuggerStub) + remoteInit(); + + int flags = SDL_INIT_VIDEO|SDL_INIT_AUDIO| + SDL_INIT_TIMER|SDL_INIT_NOPARACHUTE; + + if(SDL_Init(flags)) { + systemMessage(0, "Failed to init SDL: %s", SDL_GetError()); + exit(-1); + } + + if(SDL_InitSubSystem(SDL_INIT_JOYSTICK)) { + systemMessage(0, "Failed to init joystick support: %s", SDL_GetError()); + } + +#if WITH_LIRC + StartLirc(); +#endif + inputInitJoysticks(); + + if(cartridgeType == 0) { + srcWidth = 240; + srcHeight = 160; + systemFrameSkip = frameSkip; + } else if (cartridgeType == 1) { + if(gbBorderOn) { + srcWidth = 256; + srcHeight = 224; + gbBorderLineSkip = 256; + gbBorderColumnSkip = 48; + gbBorderRowSkip = 40; + } else { + srcWidth = 160; + srcHeight = 144; + gbBorderLineSkip = 160; + gbBorderColumnSkip = 0; + gbBorderRowSkip = 0; + } + systemFrameSkip = gbFrameSkip; + } else { + srcWidth = 320; + srcHeight = 240; + } + + sdlReadDesktopVideoMode(); + + sdlInitVideo(); + + filterFunction = initFilter(filter, systemColorDepth, srcWidth); + if (!filterFunction) { + fprintf(stderr,"Unable to init filter '%s'\n", getFilterName(filter)); + exit(-1); + } + + if(systemColorDepth == 15) + systemColorDepth = 16; + + if(systemColorDepth != 16 && systemColorDepth != 24 && + systemColorDepth != 32) { + fprintf(stderr,"Unsupported color depth '%d'.\nOnly 16, 24 and 32 bit color depths are supported\n", systemColorDepth); + exit(-1); + } + + fprintf(stdout,"Color depth: %d\n", systemColorDepth); + + utilUpdateSystemColorMaps(); + + if(delta == NULL) { + delta = (u8*)malloc(delta_size); + memset(delta, 255, delta_size); + } + + ifbFunction = initIFBFilter(ifbType, systemColorDepth); + + emulating = 1; + renderedFrames = 0; + + autoFrameSkipLastTime = throttleLastTime = systemGetClock(); + + SDL_WM_SetCaption("VBA-M", NULL); + + // now we can enable cheats? + { + int i; + for (i=0; i> 3); + u8 *screen; + + renderedFrames++; + + if (openGL) + screen = filterPix; + else { + screen = (u8*)surface->pixels; + SDL_LockSurface(surface); + } + + if (ifbFunction) + ifbFunction(pix + srcPitch, srcPitch, srcWidth, srcHeight); + + filterFunction(pix + srcPitch, srcPitch, delta, screen, + destPitch, srcWidth, srcHeight); + + drawScreenMessage(screen, destPitch, 10, destHeight - 20, 3000); + + if (showSpeed && fullscreen) + drawSpeed(screen, destPitch, 10, 20); + + if (openGL) { + glClear( GL_COLOR_BUFFER_BIT ); + glPixelStorei(GL_UNPACK_ROW_LENGTH, destWidth); + if (systemColorDepth == 16) + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, destWidth, destHeight, + GL_RGB, GL_UNSIGNED_SHORT_5_6_5, screen); + else + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, destWidth, destHeight, + GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, screen); + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2f(0.0f, 0.0f); + glVertex3i(0, 0, 0); + glTexCoord2f(destWidth / (GLfloat) textureSize, 0.0f); + glVertex3i(1, 0, 0); + glTexCoord2f(0.0f, destHeight / (GLfloat) textureSize); + glVertex3i(0, 1, 0); + glTexCoord2f(destWidth / (GLfloat) textureSize, + destHeight / (GLfloat) textureSize); + glVertex3i(1, 1, 0); + glEnd(); + + SDL_GL_SwapBuffers(); + } else { + SDL_UnlockSurface(surface); + SDL_Flip(surface); + } + +} + +void systemSetTitle(const char *title) +{ + SDL_WM_SetCaption(title, NULL); +} + +void systemShowSpeed(int speed) +{ + systemSpeed = speed; + + showRenderedFrames = renderedFrames; + renderedFrames = 0; + + if(!fullscreen && showSpeed) { + char buffer[80]; + if(showSpeed == 1) + sprintf(buffer, "VBA-M - %d%%", systemSpeed); + else + sprintf(buffer, "VBA-M - %d%%(%d, %d fps)", systemSpeed, + systemFrameSkip, + showRenderedFrames); + + systemSetTitle(buffer); + } +} + +void systemFrame() +{ +} + +void system10Frames(int rate) +{ + u32 time = systemGetClock(); + if(!wasPaused && autoFrameSkip) { + u32 diff = time - autoFrameSkipLastTime; + int speed = 100; + + if(diff) + speed = (1000000/rate)/diff; + + if(speed >= 98) { + frameskipadjust++; + + if(frameskipadjust >= 3) { + frameskipadjust=0; + if(systemFrameSkip > 0) + systemFrameSkip--; + } + } else { + if(speed < 80) + frameskipadjust -= (90 - speed)/5; + else if(systemFrameSkip < 9) + frameskipadjust--; + + if(frameskipadjust <= -2) { + frameskipadjust += 2; + if(systemFrameSkip < 9) + systemFrameSkip++; + } + } + } + if(rewindMemory) { + if(++rewindCounter >= rewindTimer) { + rewindSaveNeeded = true; + rewindCounter = 0; + } + } + + if(systemSaveUpdateCounter) { + if(--systemSaveUpdateCounter <= SYSTEM_SAVE_NOT_UPDATED) { + sdlWriteBattery(); + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + } + } + + wasPaused = false; + autoFrameSkipLastTime = time; +} + +void systemScreenCapture(int a) +{ + char buffer[2048]; + + if(captureFormat) { + if(captureDir[0]) + sprintf(buffer, "%s/%s%02d.bmp", captureDir, sdlGetFilename(filename), a); + else if (homeDir) + sprintf(buffer, "%s/%s/%s%02d.bmp", homeDir, DOT_DIR, sdlGetFilename(filename), a); + else + sprintf(buffer, "%s%02d.bmp", filename, a); + + emulator.emuWriteBMP(buffer); + } else { + if(captureDir[0]) + sprintf(buffer, "%s/%s%02d.png", captureDir, sdlGetFilename(filename), a); + else if (homeDir) + sprintf(buffer, "%s/%s/%s%02d.png", homeDir, DOT_DIR, sdlGetFilename(filename), a); + else + sprintf(buffer, "%s%02d.png", filename, a); + emulator.emuWritePNG(buffer); + } + + systemScreenMessage("Screen capture"); +} + +u32 systemGetClock() +{ + return SDL_GetTicks(); +} + +void systemGbPrint(u8 *data,int len,int pages,int feed,int palette, int contrast) +{ +} + +/* xKiv: added timestamp */ +void systemConsoleMessage(const char *msg) +{ + time_t now_time; + struct tm now_time_broken; + + now_time = time(NULL); + now_time_broken = *(localtime( &now_time )); + fprintf( + stdout, + "%02d:%02d:%02d %02d.%02d.%4d: %s\n", + now_time_broken.tm_hour, + now_time_broken.tm_min, + now_time_broken.tm_sec, + now_time_broken.tm_mday, + now_time_broken.tm_mon + 1, + now_time_broken.tm_year + 1900, + msg + ); +} + +void systemScreenMessage(const char *msg) +{ + + screenMessage = true; + screenMessageTime = systemGetClock(); + if(strlen(msg) > 20) { + strncpy(screenMessageBuffer, msg, 20); + screenMessageBuffer[20] = 0; + } else + strcpy(screenMessageBuffer, msg); + + systemConsoleMessage(msg); +} + +bool systemCanChangeSoundQuality() +{ + return false; +} + +bool systemPauseOnFrame() +{ + if(pauseNextFrame) { + paused = true; + pauseNextFrame = false; + return true; + } + return false; +} + +void systemGbBorderOn() +{ + srcWidth = 256; + srcHeight = 224; + gbBorderLineSkip = 256; + gbBorderColumnSkip = 48; + gbBorderRowSkip = 40; + + sdlInitVideo(); + + filterFunction = initFilter(filter, systemColorDepth, srcWidth); +} + +bool systemReadJoypads() +{ + return true; +} + +u32 systemReadJoypad(int which) +{ + return inputReadJoypad(which); +} + +void systemUpdateMotionSensor() +{ + inputUpdateMotionSensor(); +} + +int systemGetSensorX() +{ + return inputGetSensorX(); +} + +int systemGetSensorY() +{ + return inputGetSensorY(); +} + +SoundDriver * systemSoundInit() +{ + soundShutdown(); + + return new SoundSDL(); +} + +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); +} diff --git a/src/sdl/debugger.cpp b/src/sdl/debugger.cpp new file mode 100644 index 0000000..8b77111 --- /dev/null +++ b/src/sdl/debugger.cpp @@ -0,0 +1,2683 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2005 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +// Parts adapted from VBA-H (VBA for Hackers) by LabMaster + +#include +#include +#include +#include + +#include "../gba/GBA.h" +#include "../gba/Sound.h" +#include "../gba/armdis.h" +#include "../gba/elf.h" +#include "../common/Port.h" +#include "exprNode.h" + +extern bool debugger; +extern int emulating; +extern void sdlWriteState(int num); +extern void sdlReadState(int num); + +extern struct EmulatedSystem emulator; + +#define debuggerReadMemory(addr) \ + READ32LE((&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])) + +#define debuggerReadHalfWord(addr) \ + READ16LE((&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])) + +#define debuggerReadByte(addr) \ + map[(addr)>>24].address[(addr) & map[(addr)>>24].mask] + +#define debuggerWriteMemory(addr, value) \ + WRITE32LE(&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask], value) + +#define debuggerWriteHalfWord(addr, value) \ + WRITE16LE(&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask], value) + +#define debuggerWriteByte(addr, value) \ + map[(addr)>>24].address[(addr) & map[(addr)>>24].mask] = (value) + +struct breakpointInfo { + u32 address; + u32 value; + int size; + + u32 cond_address; + char cond_rel; + u32 cond_value; + int cond_size; + bool ia1; + bool ia2; +}; + +struct DebuggerCommand { + const char *name; + void (*function)(int,char **); + const char *help; + const char *syntax; +}; + +unsigned int SearchStart = 0xFFFFFFFF; +unsigned int SearchMaxMatches = 5; +u8 SearchData [64]; // It doesn't make much sense to search for more than 64 bytes +unsigned int SearchLength = 0; +unsigned int SearchResults; + +static void debuggerContinueAfterBreakpoint(); +void debuggerDoSearch(); +unsigned int AddressToGBA(u8* mem); + +static void debuggerHelp(int,char **); +static void debuggerNext(int,char **); +static void debuggerContinue(int, char **); +static void debuggerRegisters(int, char **); +static void debuggerBreak(int, char **); +static void debuggerBreakDelete(int, char **); +static void debuggerBreakList(int, char **); +static void debuggerBreakArm(int, char **); +static void debuggerBreakThumb(int, char **); +static void debuggerBreakChange(int, char **); +static void debuggerBreakChangeClear(int, char **); +static void debuggerBreakWriteClear(int, char **); +static void debuggerBreakWrite(int, char **); +#ifndef FINAL_VERSION +static void debuggerDebug(int, char **); +#endif +static void debuggerDisassemble(int, char **); +static void debuggerDisassembleArm(int, char **); +static void debuggerDisassembleThumb(int, char **); +static void debuggerEditByte(int, char **); +static void debuggerEditHalfWord(int, char **); +static void debuggerEditRegister(int, char **); +static void debuggerEdit(int, char **); +static void debuggerFileDisassemble(int, char **); +static void debuggerFileDisassembleArm(int, char **); +static void debuggerFileDisassembleThumb(int, char **); +static void debuggerFindText(int, char **); +static void debuggerFindHex(int, char **); +static void debuggerFindResume(int, char **); +static void debuggerIo(int, char **); +static void debuggerLast(int, char **); +static void debuggerLocals(int, char **); +static void debuggerMemoryByte(int, char **); +static void debuggerMemoryHalfWord(int, char **); +static void debuggerMemory(int, char **); +static void debuggerPrint(int, char **); +static void debuggerQuit(int, char **); +static void debuggerSetRadix(int, char **); +static void debuggerSymbols(int, char **); +#ifdef GBA_LOGGING +static void debuggerVerbose(int, char **); +#endif +static void debuggerWhere(int, char **); + +static void debuggerReadState(int, char **); +static void debuggerWriteState(int, char **); +static void debuggerDumpLoad(int, char**); +static void debuggerDumpSave(int, char**); +static void debuggerCondValidate(int n, char **args, int start); +static bool debuggerCondEvaluate(int num); +static void debuggerCondBreakThumb(int, char **); +static void debuggerCondBreakArm(int, char **); + +static DebuggerCommand debuggerCommands[] = { + { "?", debuggerHelp, "Show this help information. Type ? for command help", "[]" }, + { "ba", debuggerBreakArm, "Add an ARM breakpoint", "
" }, + { "bd", debuggerBreakDelete,"Delete a breakpoint", "" }, + { "bl", debuggerBreakList, "List breakpoints" }, + { "bpc", debuggerBreakChange, "Break on change", "
" }, + { "bpcc", debuggerBreakChangeClear, "Clear break on change", "[
]" }, + { "bpw", debuggerBreakWrite, "Break on write", "
" }, + { "bpwc", debuggerBreakWriteClear, "Clear break on write", "[
]" }, + { "break", debuggerBreak, "Add a breakpoint on the given function", "||" }, + { "bt", debuggerBreakThumb, "Add a THUMB breakpoint", "
" }, + { "c", debuggerContinue, "Continue execution" , NULL }, + { "cba", debuggerCondBreakArm, "Add a conditional ARM breakpoint", "
$
|R []\n either ==, !=, <, >, <=, >=\n either b, h, w" }, + { "cbt", debuggerCondBreakThumb, "Add a conditional THUMB breakpoint", "
$
|R []\n either ==, !=, <, >, <=, >=\n either b, h, w" }, + { "d", debuggerDisassemble, "Disassemble instructions", "[
[]]" }, + { "da", debuggerDisassembleArm, "Disassemble ARM instructions", "[
[]]" }, + { "dload",debuggerDumpLoad, "Load raw data dump from file","
"}, + { "dsave",debuggerDumpSave, "Dump raw data to file","
"}, + { "dt", debuggerDisassembleThumb, "Disassemble THUMB instructions", "[
[]]" }, + { "eb", debuggerEditByte, "Modify memory location (byte)", "
" }, + { "eh", debuggerEditHalfWord,"Modify memory location (half-word)","
" }, + { "er", debuggerEditRegister, "Modify register", " " }, + { "ew", debuggerEdit, "Modify memory location (word)", "
" }, + { "fd", debuggerFileDisassemble, "Disassemble instructions to file", " [
[]]" }, + { "fda", debuggerFileDisassembleArm, "Disassemble ARM instructions to file", " [
[]]" }, + { "fdt", debuggerFileDisassembleThumb, "Disassemble THUMB instructions to file", " [
[]]" }, + { "ft", debuggerFindText, "Search memory for ASCII-string.", " [] " }, + { "fh", debuggerFindHex, "Search memory for hex-string.", " [] " }, + { "fr", debuggerFindResume, "Resume current search.", "[]" }, + { "h", debuggerHelp, "Show this help information. Type h for command help", "[]" }, + { "io", debuggerIo, "Show I/O registers status", "[video|video2|dma|timer|misc]" }, + { "last", debuggerLast, "Trigger the display of the last registers states", NULL }, + { "load", debuggerReadState, "Load a savegame", "" }, + { "locals", debuggerLocals, "Show local variables", NULL }, + { "mb", debuggerMemoryByte, "Show memory contents (bytes)", "
" }, + { "mh", debuggerMemoryHalfWord, "Show memory contents (half-words)", "
"}, + { "mw", debuggerMemory, "Show memory contents (words)", "
" }, + { "n", debuggerNext, "Execute the next instruction", "[]" }, + { "print", debuggerPrint, "Print the value of a expression (if known)", "[/x|/o|/d] " }, + { "q", debuggerQuit, "Quit the emulator", NULL }, + { "r", debuggerRegisters, "Show ARM registers", NULL }, + { "radix", debuggerSetRadix, "Set the print radix", "" }, + { "save", debuggerWriteState, "Create a savegame", "" }, + { "symbols", debuggerSymbols, "List symbols", "[]" }, +#ifndef FINAL_VERSION + { "trace", debuggerDebug, "Set the trace level", "" }, +#endif +#ifdef GBA_LOGGING + { "verbose", debuggerVerbose, "Change verbose setting", "" }, +#endif + { "where", debuggerWhere, "Show the call chain (if available)", NULL }, + { NULL, NULL, NULL, NULL} // end marker +}; + +breakpointInfo debuggerBreakpointList[100]; + +int debuggerNumOfBreakpoints = 0; +bool debuggerAtBreakpoint = false; +int debuggerBreakpointNumber = 0; +int debuggerRadix = 0; + +extern u32 cpuPrefetch[2]; + +#define ARM_PREFETCH \ + {\ + cpuPrefetch[0] = debuggerReadMemory(armNextPC);\ + cpuPrefetch[1] = debuggerReadMemory(armNextPC+4);\ + } + +#define THUMB_PREFETCH \ + {\ + cpuPrefetch[0] = debuggerReadHalfWord(armNextPC);\ + cpuPrefetch[1] = debuggerReadHalfWord(armNextPC+2);\ + } + +static void debuggerPrefetch() +{ + if(armState) { + ARM_PREFETCH; + } else { + THUMB_PREFETCH; + } +} + +static void debuggerApplyBreakpoint(u32 address, int num, int size) +{ + if(size) + debuggerWriteMemory(address, (u32)(0xe1200070 | + (num & 0xf) | + ((num<<4)&0xf0))); + else + debuggerWriteHalfWord(address, + (u16)(0xbe00 | num)); +} + +static void debuggerDisableBreakpoints() +{ + for(int i = 0; i < debuggerNumOfBreakpoints; i++) { + if(debuggerBreakpointList[i].size) + debuggerWriteMemory(debuggerBreakpointList[i].address, + debuggerBreakpointList[i].value); + else + debuggerWriteHalfWord(debuggerBreakpointList[i].address, + debuggerBreakpointList[i].value); + } +} + +static void debuggerEnableBreakpoints(bool skipPC) +{ + for(int i = 0; i < debuggerNumOfBreakpoints; i++) { + if(debuggerBreakpointList[i].address == armNextPC && skipPC) + continue; + + debuggerApplyBreakpoint(debuggerBreakpointList[i].address, + i, + debuggerBreakpointList[i].size); + } +} + +static void debuggerUsage(const char *cmd) +{ + for(int i = 0; ; i++) { + if(debuggerCommands[i].name) { + if(!strcmp(debuggerCommands[i].name, cmd)) { + printf("%s %s\n\n%s\n", + debuggerCommands[i].name, + debuggerCommands[i].syntax ? debuggerCommands[i].syntax : "", + debuggerCommands[i].help); + break; + } + } else { + printf("Unrecognized command '%s'.", cmd); + break; + } + } +} + +static void debuggerPrintBaseType(Type *t, u32 value, u32 location, + LocationType type, + int bitSize, int bitOffset) +{ + if(bitSize) { + if(bitOffset) + value >>= ((t->size*8)-bitOffset-bitSize); + value &= (1 << bitSize)-1; + } else { + if(t->size == 2) + value &= 0xFFFF; + else if(t->size == 1) + value &= 0xFF; + } + + if(t->size == 8) { + u64 value = 0; + if(type == LOCATION_memory) { + value = debuggerReadMemory(location) | + ((u64)debuggerReadMemory(location+4)<<32); + } else if(type == LOCATION_register) { + value = reg[location].I | ((u64)reg[location+1].I << 32); + } + switch(t->encoding) { + case DW_ATE_signed: + switch(debuggerRadix) { + case 0: + printf("%lld", (long long)value); + break; + case 1: + printf("0x%llx", (long long)value); + break; + case 2: + printf("0%llo", (long long)value); + break; + } + break; + case DW_ATE_unsigned: + switch(debuggerRadix) { + case 0: + printf("%llu", (unsigned long long)value); + break; + case 1: + printf("0x%llx", (unsigned long long)value); + break; + case 2: + printf("0%llo", (unsigned long long)value); + break; + } + break; + default: + printf("Unknowing 64-bit encoding\n"); + } + return; + } + + switch(t->encoding) { + case DW_ATE_boolean: + if(value) + printf("true"); + else + printf("false"); + break; + case DW_ATE_signed: + switch(debuggerRadix) { + case 0: + printf("%d", value); + break; + case 1: + printf("0x%x", value); + break; + case 2: + printf("0%o", value); + break; + } + break; + case DW_ATE_unsigned: + case DW_ATE_unsigned_char: + switch(debuggerRadix) { + case 0: + printf("%u", value); + break; + case 1: + printf("0x%x", value); + break; + case 2: + printf("0%o", value); + break; + } + break; + default: + printf("UNKNOWN BASE %d %08x", t->encoding, value); + } +} + +static const char *debuggerPrintType(Type *t) +{ + char buffer[1024]; + static char buffer2[1024]; + + if(t->type == TYPE_pointer) { + if(t->pointer) + strcpy(buffer, debuggerPrintType(t->pointer)); + else + strcpy(buffer, "void"); + sprintf(buffer2, "%s *", buffer); + return buffer2; + } else if(t->type == TYPE_reference) { + strcpy(buffer, debuggerPrintType(t->pointer)); + sprintf(buffer2, "%s &", buffer); + return buffer2; + } + return t->name; +} + +static void debuggerPrintValueInternal(Function *, Type *, ELFBlock *, int, int, u32); +static void debuggerPrintValueInternal(Function *f, Type *t, + int bitSize, int bitOffset, + u32 objLocation, LocationType type); + +static u32 debuggerGetValue(u32 location, LocationType type) +{ + switch(type) { + case LOCATION_memory: + return debuggerReadMemory(location); + case LOCATION_register: + return reg[location].I; + case LOCATION_value: + return location; + } + return 0; +} + +static void debuggerPrintPointer(Type *t, u32 value) +{ + printf("(%s)0x%08x", debuggerPrintType(t), value); +} + +static void debuggerPrintReference(Type *t, u32 value) +{ + printf("(%s)0x%08x", debuggerPrintType(t), value); +} + +static void debuggerPrintFunction(Type *t, u32 value) +{ + printf("(%s)0x%08x", debuggerPrintType(t), value); +} + +static void debuggerPrintArray(Type *t, u32 value) +{ + // todo + printf("(%s[])0x%08x", debuggerPrintType(t->array->type), value); +} + +static void debuggerPrintMember(Function *f, + Member *m, + u32 objLocation, + u32 location) +{ + int bitSize = m->bitSize; + if(bitSize) { + u32 value = 0; + int off = m->bitOffset; + int size = m->byteSize; + u32 v = 0; + switch(size){ + case 1: + v = debuggerReadByte(location); + break; + case 2: + v = debuggerReadHalfWord(location); + break; + default: + v = debuggerReadMemory(location); + break; + } + + while(bitSize) { + int top = size*8 - off; + int bot = top - bitSize; + top--; + if(bot >= 0) { + value = (v >> (size*8 - bitSize - off)) & ((1 << bitSize)-1); + bitSize = 0; + } else { + value |= (v & ((1 << top)-1)) << (bitSize - top); + bitSize -= (top+1); + location -= size; + off = 0; + switch(size){ + case 1: + v = debuggerReadByte(location); + break; + case 2: + v = debuggerReadHalfWord(location); + break; + default: + v = debuggerReadMemory(location); + break; + } + } + } + debuggerPrintBaseType(m->type, value, location, LOCATION_memory, + bitSize, 0); + } else { + debuggerPrintValueInternal(f, m->type, m->location, m->bitSize, + m->bitOffset, objLocation); + } +} + +static void debuggerPrintStructure(Function *f, Type *t, u32 objLocation) +{ + printf("{"); + int count = t->structure->memberCount; + int i = 0; + while(i < count) { + Member *m = &t->structure->members[i]; + printf("%s=", m->name); + LocationType type; + u32 location = elfDecodeLocation(f, m->location, &type, objLocation); + debuggerPrintMember(f, m, objLocation, location); + i++; + if(i < count) + printf(","); + } + printf("}"); +} + +static void debuggerPrintUnion(Function *f, Type *t, u32 objLocation) +{ + // todo + printf("{"); + int count = t->structure->memberCount; + int i = 0; + while(i < count) { + Member *m = &t->structure->members[i]; + printf("%s=", m->name); + debuggerPrintMember(f, m, objLocation, 0); + i++; + if(i < count) + printf(","); + } + printf("}"); +} + +static void debuggerPrintEnum(Type *t, u32 value) +{ + int i; + for(i = 0; i < t->enumeration->count; i++) { + EnumMember *m = (EnumMember *)&t->enumeration->members[i]; + if(value == m->value) { + printf("%s", m->name); + return; + } + } + printf("(UNKNOWN VALUE) %d", value); +} + +static void debuggerPrintValueInternal(Function *f, Type *t, + int bitSize, int bitOffset, + u32 objLocation, LocationType type) +{ + u32 value = debuggerGetValue(objLocation, type); + if(!t) { + printf("void"); + return; + } + switch(t->type) { + case TYPE_base: + debuggerPrintBaseType(t, value, objLocation, type, bitSize, bitOffset); + break; + case TYPE_pointer: + debuggerPrintPointer(t, value); + break; + case TYPE_reference: + debuggerPrintReference(t, value); + break; + case TYPE_function: + debuggerPrintFunction(t, value); + break; + case TYPE_array: + debuggerPrintArray(t, objLocation); + break; + case TYPE_struct: + debuggerPrintStructure(f, t, objLocation); + break; + case TYPE_union: + debuggerPrintUnion(f, t, objLocation); + break; + case TYPE_enum: + debuggerPrintEnum(t, value); + break; + default: + printf("%08x", value); + break; + } +} + +static void debuggerPrintValueInternal(Function *f, Type *t, ELFBlock *loc, + int bitSize, int bitOffset, + u32 objLocation) +{ + LocationType type; + u32 location; + if(loc) { + if(objLocation) + location = elfDecodeLocation(f, loc, &type, objLocation); + else + location = elfDecodeLocation(f, loc,&type); + } else { + location = objLocation; + type = LOCATION_memory; + } + + debuggerPrintValueInternal(f, t, bitSize, bitOffset, location, type); +} + +static void debuggerPrintValue(Function *f, Object *o) +{ + debuggerPrintValueInternal(f, o->type, o->location, 0, 0, 0); + + printf("\n"); +} + +static void debuggerSymbols(int argc, char **argv) +{ + int i = 0; + u32 value; + u32 size; + int type; + bool match = false; + int matchSize = 0; + const char *matchStr = NULL; + + if(argc == 2) { + match = true; + matchSize = strlen(argv[1]); + matchStr = argv[1]; + } + printf("Symbol Value Size Type \n"); + printf("-------------------- ------- -------- -------\n"); + const char *s = NULL; + while((s = elfGetSymbol(i, &value, &size, &type))) { + if(*s) { + if(match) { + if(strncmp(s, matchStr, matchSize) != 0) { + i++; + continue; + } + } + const char *ts = "?"; + switch(type) { + case 2: + ts = "ARM"; + break; + case 0x0d: + ts = "THUMB"; + break; + case 1: + ts = "DATA"; + break; + } + printf("%-20s %08x %08x %-7s\n", + s, value, size, ts); + } + i++; + } +} + +static void debuggerSetRadix(int argc, char **argv) +{ + if(argc != 2) + debuggerUsage(argv[0]); + else { + int r = atoi(argv[1]); + + bool error = false; + switch(r) { + case 10: + debuggerRadix = 0; + break; + case 8: + debuggerRadix = 2; + break; + case 16: + debuggerRadix = 1; + break; + default: + error = true; + printf("Unknown radix %d. Valid values are 8, 10 and 16.\n", r); + break; + } + if(!error) + printf("Radix set to %d\n", r); + } +} + +static void debuggerPrint(int argc, char **argv) +{ + if(argc != 2 && argc != 3) { + debuggerUsage(argv[0]); + } else { + u32 pc = armNextPC; + Function *f = NULL; + CompileUnit *u = NULL; + + elfGetCurrentFunction(pc, + &f, &u); + + int oldRadix = debuggerRadix; + if(argc == 3) { + if(argv[1][0] == '/') { + if(argv[1][1] == 'x') + debuggerRadix = 1; + else if(argv[1][1] == 'o') + debuggerRadix = 2; + else if(argv[1][1] == 'd') + debuggerRadix = 0; + else { + printf("Unknown format %c\n", argv[1][1]); + return; + } + } else { + printf("Unknown option %s\n", argv[1]); + return; + } + } + + const char *s = argc == 2 ? argv[1] : argv[2]; + + extern const char *exprString; + extern int exprCol; + extern int yyparse(); + exprString = s; + exprCol = 0; + if(!yyparse()) { + extern Node *result; + if(result->resolve(result, f, u)) { + if(result->member) + debuggerPrintMember(f, + result->member, + result->objLocation, + result->location); + else + debuggerPrintValueInternal(f, result->type, 0, 0, + result->location, + result->locType); + printf("\n"); + } else { + printf("Error resolving expression\n"); + } + } else { + printf("Error parsing expression:\n"); + printf("%s\n", s); + exprCol--; + for(int i = 0; i < exprCol; i++) + printf(" "); + printf("^\n"); + } + extern void exprCleanBuffer(); + exprCleanBuffer(); + exprNodeCleanUp(); + debuggerRadix = oldRadix; + } +} + +static void debuggerHelp(int n, char **args) +{ + if(n == 2) { + debuggerUsage(args[1]); + } else { + for(int i = 0; ; i++) { + if(debuggerCommands[i].name) { + printf("%s\t%s\n", debuggerCommands[i].name, debuggerCommands[i].help); + } else + break; + } + } +} + +#ifndef FINAL_VERSION +static void debuggerDebug(int n, char **args) +{ + if(n == 2) { + int v = 0; + sscanf(args[1], "%d", &v); + systemDebug = v; + printf("Debug level set to %d\n", systemDebug); + } else + debuggerUsage("trace"); +} +#endif + +#ifdef GBA_LOGGING +static void debuggerVerbose(int n, char **args) +{ + if(n == 2) { + int v = 0; + sscanf(args[1], "%d", &v); + systemVerbose = v; + printf("Verbose level set to %d\n", systemVerbose); + } else + debuggerUsage("verbose"); +} +#endif + +static void debuggerWhere(int n, char **args) +{ + void elfPrintCallChain(u32); + elfPrintCallChain(armNextPC); +} + +static void debuggerLocals(int n, char **args) +{ + Function *f = NULL; + CompileUnit *u = NULL; + u32 pc = armNextPC; + if(elfGetCurrentFunction(pc, + &f, &u)) { + Object *o = f->parameters; + while(o) { + printf("%s=", o->name); + debuggerPrintValue(f, o); + o = o->next; + } + + o = f->variables; + while(o) { + bool visible = o->startScope ? pc>=o->startScope : true; + if(visible) + visible = o->endScope ? pc < o->endScope : true; + if(visible) { + printf("%s=", o->name); + debuggerPrintValue(f, o); + } + o = o->next; + } + } else { + printf("No information for current address\n"); + } +} + +static void debuggerNext(int n, char **args) +{ + int count = 1; + if(n == 2) { + sscanf(args[1], "%d", &count); + } + for(int i = 0; i < count; i++) { + if(debuggerAtBreakpoint) { + debuggerContinueAfterBreakpoint(); + debuggerEnableBreakpoints(false); + } else { + debuggerPrefetch(); + emulator.emuMain(1); + } + } + debuggerDisableBreakpoints(); + Function *f = NULL; + CompileUnit *u = NULL; + u32 a = armNextPC; + if(elfGetCurrentFunction(a, &f, &u)) { + const char *file; + int line = elfFindLine(u, f, a, &file); + + printf("File %s, function %s, line %d\n", file, f->name, + line); + } + debuggerRegisters(0, NULL); +} + +static void debuggerContinue(int n, char **args) +{ + if(debuggerAtBreakpoint) + debuggerContinueAfterBreakpoint(); + debuggerEnableBreakpoints(false); + debugger = false; + debuggerPrefetch(); +} + +/*extern*/ void debuggerSignal(int sig,int number) +{ + switch(sig) { + case 4: + { + printf("Illegal instruction at %08x\n", armNextPC); + debugger = true; + } + break; + case 5: + { + bool cond = debuggerCondEvaluate(number & 255); + if(cond) { + printf("Breakpoint %d reached\n", number); + debugger = true; + } else { + debuggerDisableBreakpoints(); + debuggerPrefetch(); + emulator.emuMain(1); + debuggerEnableBreakpoints(false); + return; + } + debuggerAtBreakpoint = true; + debuggerBreakpointNumber = number; + debuggerDisableBreakpoints(); + + Function *f = NULL; + CompileUnit *u = NULL; + + if(elfGetCurrentFunction(armNextPC, &f, &u)) { + const char *file; + int line = elfFindLine(u,f,armNextPC,&file); + printf("File %s, function %s, line %d\n", file, f->name, + line); + } + } + break; + default: + printf("Unknown signal %d\n", sig); + break; + } +} + +static void debuggerBreakList(int, char **) +{ + printf("Num Address Type Symbol\n"); + printf("--- -------- ----- ------\n"); + for(int i = 0; i < debuggerNumOfBreakpoints; i++) { + printf("%3d %08x %s %s\n",i, debuggerBreakpointList[i].address, + debuggerBreakpointList[i].size ? "ARM" : "THUMB", + elfGetAddressSymbol(debuggerBreakpointList[i].address)); + } +} + +static void debuggerBreakDelete(int n, char **args) +{ + if(n == 2) { + int n = 0; + sscanf(args[1], "%d", &n); + if(n >= 0 && n < debuggerNumOfBreakpoints) { + printf("Deleting breakpoint %d (%d)\n", n, debuggerNumOfBreakpoints); + n++; + if(n < debuggerNumOfBreakpoints) { + for(int i = n; i < debuggerNumOfBreakpoints; i++) { + debuggerBreakpointList[i-1].address = + debuggerBreakpointList[i].address; + debuggerBreakpointList[i-1].value = + debuggerBreakpointList[i].value; + debuggerBreakpointList[i-1].size = + debuggerBreakpointList[i].size; + } + } + debuggerNumOfBreakpoints--; + } + else + printf("No breakpoints are set\n"); + } else + debuggerUsage("bd"); +} + +static void debuggerBreak(int n, char **args) +{ + if(n == 2) { + u32 address = 0; + u32 value = 0; + int type = 0; + const char *s = args[1]; + char c = *s; + if(strchr(s, ':')) { + const char *name = s; + char *l = (char *)strchr(s, ':'); + *l++ = 0; + int line = atoi(l); + + u32 addr; + Function *f; + CompileUnit *u; + + if(elfFindLineInModule(&addr, name, line)) { + if(elfGetCurrentFunction(addr, &f, &u)) { + u32 addr2; + if(elfGetSymbolAddress(f->name, &addr2, &value, &type)) { + address = addr; + } else { + printf("Unable to get function symbol data\n"); + return; + } + } else { + printf("Unable to find function for address\n"); + return; + } + } else { + printf("Unable to find module or line\n"); + return; + } + } else if(c >= '0' && c <= '9') { + int line = atoi(s); + Function *f; + CompileUnit *u; + u32 addr; + + if(elfGetCurrentFunction(armNextPC, &f, &u)) { + if(elfFindLineInUnit(&addr, u, line)) { + if(elfGetCurrentFunction(addr, &f, &u)) { + u32 addr2; + if(elfGetSymbolAddress(f->name, &addr2, &value, &type)) { + address = addr; + } else { + printf("Unable to get function symbol data\n"); + return; + } + } else { + printf("Unable to find function for address\n"); + return; + } + } else { + printf("Unable to find line\n"); + return; + } + } else { + printf("Cannot find current function\n"); + return; + } + } else { + if(!elfGetSymbolAddress(s, &address, &value, &type)) { + printf("Function %s not found\n", args[1]); + return; + } + } + if(type == 0x02 || type == 0x0d) { + int i = debuggerNumOfBreakpoints; + int size = 0; + if(type == 2) + size = 1; + debuggerBreakpointList[i].address = address; + debuggerBreakpointList[i].value = type == 0x02 ? + debuggerReadMemory(address) : debuggerReadHalfWord(address); + debuggerBreakpointList[i].size = size; + // debuggerApplyBreakpoint(address, i, size); + debuggerNumOfBreakpoints++; + if(size) + printf("Added ARM breakpoint at %08x\n", address); + else + printf("Added THUMB breakpoint at %08x\n", address); + } else { + printf("%s is not a function symbol\n", args[1]); + } + } else + debuggerUsage("break"); +} + +static void debuggerBreakThumb(int n, char **args) +{ + if(n == 2) { + u32 address = 0; + sscanf(args[1],"%x", &address); + int i = debuggerNumOfBreakpoints; + debuggerBreakpointList[i].address = address; + debuggerBreakpointList[i].value = debuggerReadHalfWord(address); + debuggerBreakpointList[i].size = 0; + // debuggerApplyBreakpoint(address, i, 0); + debuggerNumOfBreakpoints++; + printf("Added THUMB breakpoint at %08x\n", address); + } else + debuggerUsage("bt"); +} + +static void debuggerBreakArm(int n, char **args) +{ + if(n == 2) { + u32 address = 0; + sscanf(args[1],"%x", &address); + int i = debuggerNumOfBreakpoints; + debuggerBreakpointList[i].address = address; + debuggerBreakpointList[i].value = debuggerReadMemory(address); + debuggerBreakpointList[i].size = 1; + // debuggerApplyBreakpoint(address, i, 1); + debuggerNumOfBreakpoints++; + printf("Added ARM breakpoint at %08x\n", address); + } else + debuggerUsage("ba"); +} + +/*extern*/ void debuggerBreakOnWrite(u32 address, u32 oldvalue, u32 value, + int size, int t) +{ + const char *type = "write"; + if(t == 2) + type = "change"; + + if(size == 2) + printf("Breakpoint (on %s) address %08x old:%08x new:%08x\n", + type, address, oldvalue, value); + else if(size == 1) + printf("Breakpoint (on %s) address %08x old:%04x new:%04x\n", + type, address, (u16)oldvalue,(u16)value); + else + printf("Breakpoint (on %s) address %08x old:%02x new:%02x\n", + type, address, (u8)oldvalue, (u8)value); + debugger = true; +} + +static void debuggerBreakWriteClear(int n, char **args) +{ + if(n == 3) { + u32 address = 0; + sscanf(args[1], "%x", &address); + int n = 0; + sscanf(args[2], "%d", &n); + + if (! ((address >= 0x02000000 && address < 0x02040000) || + (address >= 0x03000000 && address < 0x03008000) || + (address >= 0x05000000 && address < 0x05000400) || + (address >= 0x06000000 && address < 0x06018000) || + (address >= 0x07000000 && address < 0x07000400))) { + printf("Invalid address: %08x\n", address); + return; + } + + u32 final = address + n; + switch(address >> 24) { + case 2: + { + address &= 0x3ffff; + final &= 0x3ffff; + for(u32 i = address; i < final; i++) + if(freezeWorkRAM[i] == 1) + freezeWorkRAM[i] = 0; + printf("Cleared break on write from %08x to %08x\n", + 0x2000000+address, 0x2000000+final); + } + break; + case 3: + { + address &= 0x7fff; + final &= 0x7fff; + for(u32 i = address; i < final; i++) + if(freezeInternalRAM[i] == 1) + freezeInternalRAM[i] = 0; + printf("Cleared break on write from %08x to %08x\n", + 0x3000000+address, 0x3000000+final); + } + break; + case 5: + { + address &= 0x3ff; + final &= 0x3ff; + for(u32 i = address; i < final; i++) + if(freezePRAM[i] == 1) + freezePRAM[i] = 0; + printf("Cleared break on write from %08x to %08x\n", + 0x5000000+address, 0x5000000+final); + } + break; + case 6: + { + if (address > 0x6010000) { + address &= 0x17fff; + final &= 0x17fff; + } else { + address &= 0xffff; + final &= 0xffff; + } + + for (u32 i = address; i < final; i++) + if(freezeVRAM[i] == 1) + freezeVRAM[i] = 0; + printf("Cleared break on write from %08x to %08x\n", + 0x06000000+address, 0x06000000+final); + } + break; + case 7: + { + address &= 0x3ff; + final &= 0x3ff; + for(u32 i = address; i < final; i++) + if(freezeOAM[i] == 1) + freezeOAM[i] = 0; + printf("Cleared break on write from %08x to %08x\n", + 0x7000000+address, 0x7000000+final); + } + break; + } + } else if(n == 1) { + int i; + for(i = 0; i < 0x40000; i++) + if(freezeWorkRAM[i] == 1) + freezeWorkRAM[i] = 0; + for(i = 0; i < 0x8000; i++) + if(freezeInternalRAM[i] == 1) + freezeInternalRAM[i] = 0; + for(i = 0; i < 0x400; i++) + if(freezePRAM[i] == 1) + freezePRAM[i] = 0; + for(i = 0; i< 0x18000; i++) + if(freezeVRAM[i] == 1) + freezeVRAM[i] = 0; + for(i = 0; i < 0x400; i++) + if(freezeOAM[i] == 1) + freezeOAM[i] = 0; + + printf("Cleared all break on write\n"); + } else + debuggerUsage("bpwc"); +} + +static void debuggerBreakWrite(int n, char **args) +{ + if(n == 3) { + if(cheatsNumber != 0) { + printf("Cheats are enabled. Cannot continue.\n"); + return; + } + u32 address = 0; + sscanf(args[1], "%x", &address); + int n = 0; + sscanf(args[2], "%d", &n); + + if (! ((address >= 0x02000000 && address < 0x02040000) || + (address >= 0x03000000 && address < 0x03008000) || + (address >= 0x05000000 && address < 0x05000400) || + (address >= 0x06000000 && address < 0x06018000) || + (address >= 0x07000000 && address < 0x07000400))) { + printf("Invalid address: %08x\n", address); + return; + } + + u32 final = address + n; + + if(address < 0x2040000 && final > 0x2040000) { + printf("Invalid byte count: %d\n", n); + return; + } else if(address < 0x3008000 && final > 0x3008000) { + printf("Invalid byte count: %d\n", n); + return; + } else if(address < 0x05000400 && final > 0x05000400) { + printf("Invalid byte count: %d\n", n); + return; + } else if(address < 0x06018000 && final > 0x06018000) { + printf("Invalid byte count: %d\n", n); + return; + } else if(address < 0x07000400 && final > 0x07000400) { + printf("Invalid byte count: %d\n", n); + return; + } + + printf("Added break on write at %08x for %d bytes\n", address, n); + + switch(address >> 24) { + case 2: + for (int i = 0; i < n; i++) + freezeWorkRAM[(address + i) & 0x3ffff] = 1; + break; + case 3: + for (int i = 0; i < n; i++) + freezeInternalRAM[(address + i) & 0x7fff] = 1; + break; + case 5: + for (int i = 0; i < n; i++) + freezePRAM[(address + i) & 0x3ff] = 1; + break; + case 6: + // address/range must be valid, so we can use a lazy mask + for (int i = 0; i < n; i++) + freezeVRAM[(address + i) & 0x1ffff] = 1; + break; + case 7: + for (int i = 0; i < n; i++) + freezeOAM[(address + i) & 0x3ff] = 1; + break; + } + + } else + debuggerUsage("bpw"); +} + +static void debuggerBreakChangeClear(int n, char **args) +{ + if(n == 3) { + u32 address = 0; + sscanf(args[1], "%x", &address); + int n = 0; + sscanf(args[2], "%d", &n); + + if (! ((address >= 0x02000000 && address < 0x02040000) || + (address >= 0x03000000 && address < 0x03008000) || + (address >= 0x05000000 && address < 0x05000400) || + (address >= 0x06000000 && address < 0x06018000) || + (address >= 0x07000000 && address < 0x07000400))) { + printf("Invalid address: %08x\n", address); + return; + } + + u32 final = address + n; + switch(address >> 24) { + case 2: + { + address &= 0x3ffff; + final &= 0x3ffff; + for(u32 i = address; i < final; i++) + if(freezeWorkRAM[i] == 2) + freezeWorkRAM[i] = 0; + printf("Cleared break on change from %08x to %08x\n", + 0x2000000+address, 0x2000000+final); + } + break; + case 3: + { + address &= 0x7fff; + final &= 0x7fff; + for(u32 i = address; i < final; i++) + if(freezeInternalRAM[i] == 2) + freezeInternalRAM[i] = 0; + printf("Cleared break on change from %08x to %08x\n", + 0x3000000+address, 0x3000000+final); + } + break; + case 5: + { + address &= 0x3ff; + final &= 0x3ff; + for(u32 i = address; i < final; i++) + if(freezePRAM[i] == 2) + freezePRAM[i] = 0; + printf("Cleared break on change from %08x to %08x\n", + 0x5000000+address, 0x5000000+final); + } + break; + case 6: + { + if (address > 0x6010000) { + address &= 0x17fff; + final &= 0x17fff; + } else { + address &= 0xffff; + final &= 0xffff; + } + for(u32 i = address; i < final; i++) + if(freezeVRAM[i] == 2) + freezeVRAM[i] = 0; + printf("Cleared break on change from %08x to %08x\n", + 0x3000000+address, 0x3000000+final); + } + break; + case 7: + { + address &= 0x3ff; + final &= 0x3ff; + for(u32 i = address; i < final; i++) + if(freezeOAM[i] == 2) + freezeOAM[i] = 0; + printf("Cleared break on change from %08x to %08x\n", + 0x7000000+address, 0x7000000+final); + } + break; + } + } else if(n == 1) { + int i; + for(i = 0; i < 0x40000; i++) + if(freezeWorkRAM[i] == 2) + freezeWorkRAM[i] = 0; + for(i = 0; i < 0x8000; i++) + if(freezeInternalRAM[i] == 2) + freezeInternalRAM[i] = 0; + for(i = 0; i < 0x400; i++) + if(freezePRAM[i] == 2) + freezePRAM[i] = 0; + for(i = 0; i < 0x18000; i++) + if(freezeVRAM[i] == 2) + freezeVRAM[i] = 0; + for(i = 0; i < 0x400; i++) + if(freezeOAM[i] == 2) + freezeOAM[i] = 0; + + printf("Cleared all break on change\n"); + } else + debuggerUsage("bpcc"); +} + +static void debuggerBreakChange(int n, char **args) +{ + if(n == 3) { + if(cheatsNumber != 0) { + printf("Cheats are enabled. Cannot continue.\n"); + return; + } + u32 address = 0; + sscanf(args[1], "%x", &address); + int n = 0; + sscanf(args[2], "%d", &n); + + if (! ((address >= 0x02000000 && address < 0x02040000) || + (address >= 0x03000000 && address < 0x03008000) || + (address >= 0x05000000 && address < 0x05000400) || + (address >= 0x06000000 && address < 0x06018000) || + (address >= 0x07000000 && address < 0x07000400))) { + printf("Invalid address: %08x\n", address); + return; + } + + u32 final = address + n; + + if(address < 0x2040000 && final > 0x2040000) { + printf("Invalid byte count: %d\n", n); + return; + } else if(address < 0x3008000 && final > 0x3008000) { + printf("Invalid byte count: %d\n", n); + return; + } else if(address < 0x6018000 && final > 0x6018000) { + printf("Invalid byte count: %d\n", n); + return; + } else if(address < 0x7000400 && final > 0x7000400) { + printf("Invalid byte count: %d\n", n); + return; + } + printf("Added break on change at %08x for %d bytes\n", address, n); + + switch(address >> 24) { + case 2: + for (int i = 0; i < n; i++) + freezeWorkRAM[(address + i) & 0x3ffff] = 2; + break; + case 3: + for (int i = 0; i < n; i++) + freezeInternalRAM[(address + i) & 0x7fff] = 2; + break; + case 5: + for (int i = 0; i < n; i++) + freezePRAM[(address + i) & 0x3ff] = 2; + break; + case 6: + // address/range must be valid, so we can use a lazy mask + for (int i = 0; i < n; i++) + freezeVRAM[(address + i) & 0x1ffff] = 2; + break; + case 7: + for (int i = 0; i < n; i++) + freezeOAM[(address + i) & 0x3ff] = 2; + break; + } + + } else + debuggerUsage("bpc"); +} + +static void debuggerDisassembleArm(FILE *f, u32 pc, int count) +{ + char buffer[80]; + int i = 0; + u32 len = 0; + char format[30]; + for(i = 0; i < count; i++) { + size_t l = strlen(elfGetAddressSymbol(pc+4*i)); + if(l > len) + len = l; + } + sprintf(format, "%%08x %%-%ds %%s\n", len); + for(i = 0; i < count; i++) { + u32 addr = pc; + pc += disArm(pc, buffer, 2); + fprintf(f, format, addr, elfGetAddressSymbol(addr), buffer); + } +} + +static void debuggerDisassembleThumb(FILE *f, u32 pc, int count) +{ + char buffer[80]; + int i = 0; + u32 len = 0; + char format[30]; + for(i = 0; i < count; i++) { + size_t l = strlen(elfGetAddressSymbol(pc+2*i)); + if(l > len) + len = l; + } + sprintf(format, "%%08x %%-%ds %%s\n", len); + + for(i = 0; i < count; i++) { + u32 addr = pc; + pc += disThumb(pc, buffer, 2); + fprintf(f, format, addr, elfGetAddressSymbol(addr), buffer); + } +} + +static void debuggerDisassembleArm(int n, char **args) +{ + u32 pc = reg[15].I; + pc -= 4; + int count = 20; + if(n >= 2) { + sscanf(args[1], "%x", &pc); + } + if(pc & 3) { + printf("Misaligned address %08x\n", pc); + pc &= 0xfffffffc; + } + if(n >= 3) { + sscanf(args[2], "%d", &count); + } + debuggerDisassembleArm(stdout, pc, count); +} + +static void debuggerDisassembleThumb(int n, char **args) +{ + u32 pc = reg[15].I; + pc -= 2; + int count = 20; + if(n >= 2) { + sscanf(args[1], "%x", &pc); + } + if(pc & 1) { + printf("Misaligned address %08x\n", pc); + pc &= 0xfffffffe; + } + if(n >= 3) { + sscanf(args[2], "%d", &count); + } + debuggerDisassembleThumb(stdout, pc, count); +} + +static void debuggerDisassemble(int n, char **args) +{ + if(armState) + debuggerDisassembleArm(n, args); + else + debuggerDisassembleThumb(n, args); +} + +static void debuggerFileDisassembleArm(int n, char **args) +{ + u32 pc = reg[15].I; + pc -= 4; + int count = 20; + if(n < 2) { + debuggerUsage("fda"); + return; + } + FILE *f = fopen(args[1], "w+"); + if(!f) { + printf("Error: cannot open file %s\n", args[1]); + return; + } + if(n >= 3) { + sscanf(args[2], "%x", &pc); + } + if(pc & 3) { + printf("Misaligned address %08x\n", pc); + pc &= 0xfffffffc; + } + if(n >= 4) { + sscanf(args[3], "%d", &count); + } + debuggerDisassembleArm(f, pc, count); + fclose(f); +} + +static void debuggerFileDisassembleThumb(int n, char **args) +{ + u32 pc = reg[15].I; + pc -= 2; + int count = 20; + if(n < 2) { + debuggerUsage("fdt"); + return; + } + FILE *f = fopen(args[1], "w+"); + if(!f) { + printf("Error: cannot open file %s\n", args[1]); + return; + } + + if(n >= 3) { + sscanf(args[2], "%x", &pc); + } + if(pc & 1) { + printf("Misaligned address %08x\n", pc); + pc &= 0xfffffffe; + } + if(n >= 4) { + sscanf(args[3], "%d", &count); + } + debuggerDisassembleThumb(f, pc, count); + fclose(f); +} + +void debuggerFindText(int n, char **args) +{ + if ((n == 4) || (n == 3)) + { + SearchResults = 0; + sscanf(args[1], "%x", &SearchStart); + + if (n == 4) + { + sscanf(args[2], "%u", &SearchMaxMatches); + strncpy((char*) SearchData, args[3], 64); + SearchLength = strlen(args[3]); + } + else if (n == 3) + { + strncpy((char*) SearchData, args[2], 64); + SearchLength = strlen(args[2]); + }; + + if (SearchLength > 64) + { + printf ("Entered string (length: %d) is longer than 64 bytes and was cut.\n", SearchLength); + SearchLength = 64; + }; + + debuggerDoSearch (); + + } else + debuggerUsage("ft"); +}; + +void debuggerFindHex(int n, char **args) +{ + if ((n == 4) || (n == 3)) + { + SearchResults = 0; + sscanf(args[1], "%x", &SearchStart); + + char SearchHex [128]; + if (n == 4) + { + sscanf(args[2], "%u", &SearchMaxMatches); + strncpy(SearchHex, args[3], 128); + SearchLength = strlen(args[3]); + } + else if (n == 3) + { + strncpy(SearchHex, args[2], 128); + SearchLength = strlen(args[2]); + }; + + if (SearchLength & 1) + printf ("Unaligned bytecount: %d,5. Last digit (%c) cut.\n", SearchLength / 2, SearchHex [SearchLength - 1]); + + SearchLength /= 2; + + if (SearchLength > 64) + { + printf ("Entered string (length: %d) is longer than 64 bytes and was cut.\n", SearchLength); + SearchLength = 64; + }; + + for (unsigned int i = 0; i < SearchLength; i++) + { + unsigned int cbuf = 0; + sscanf (&SearchHex [i << 1], "%02x", &cbuf); + SearchData [i] = cbuf; + }; + + debuggerDoSearch (); + + } else + debuggerUsage("fh"); +}; + +void debuggerFindResume(int n, char **args) +{ + if ((n == 1) || (n == 2)) + { + if (SearchLength == 0) + { + printf("Error: No search in progress. Start a search with ft or fh.\n"); + debuggerUsage("fr"); + return; + }; + + if (n == 2) + sscanf(args[1], "%u", &SearchMaxMatches); + + debuggerDoSearch(); + + } else + debuggerUsage("fr"); +}; + +void debuggerDoSearch() +{ + unsigned int count = 0; + + while (true) + { + unsigned int final = SearchStart + SearchLength - 1; + u8* end; + u8* start; + + switch (SearchStart >> 24) + { + case 0: + if (final > 0x00003FFF) { SearchStart = 0x02000000; continue; } + else { start = bios + (SearchStart & 0x3FFF); end = bios + 0x3FFF; break; }; + case 2: + if (final > 0x0203FFFF) { SearchStart = 0x03000000; continue; } + else { start = workRAM + (SearchStart & 0x3FFFF); end = workRAM + 0x3FFFF; break; }; + case 3: + if (final > 0x03007FFF) { SearchStart = 0x04000000; continue; } + else { start = internalRAM + (SearchStart & 0x7FFF); end = internalRAM + 0x7FFF; break; }; + case 4: + if (final > 0x040003FF) { SearchStart = 0x05000000; continue; } + else { start = ioMem + (SearchStart & 0x3FF); end = ioMem + 0x3FF; break; }; + case 5: + if (final > 0x050003FF) { SearchStart = 0x06000000; continue; } + else { start = paletteRAM + (SearchStart & 0x3FF); end = paletteRAM + 0x3FF; break; }; + case 6: + if (final > 0x0601FFFF) { SearchStart = 0x07000000; continue; } + else { start = vram + (SearchStart & 0x1FFFF); end = vram + 0x1FFFF; break; }; + case 7: + if (final > 0x070003FF) { SearchStart = 0x08000000; continue; } + else { start = oam + (SearchStart & 0x3FF); end = oam + 0x3FF; break; }; + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + if (final <= 0x09FFFFFF) + { start = rom + (SearchStart & 0x01FFFFFF); end = rom + 0x01FFFFFF; break; }; + default: + printf ("Search completed.\n"); + SearchLength = 0; + return; + }; + + end -= SearchLength - 1; + u8 firstbyte = SearchData [0]; + while (start <= end) + { + while ((start <= end) && (*start != firstbyte)) + start++; + + if (start > end) + break; + + unsigned int p = 1; + while ((start [p] == SearchData [p]) && (p < SearchLength)) + p++; + + if (p == SearchLength) + { + printf ("Search result (%d): %08x\n", count + SearchResults, AddressToGBA (start)); + count++; + if (count == SearchMaxMatches) + { + SearchStart = AddressToGBA (start + p); + SearchResults += count; + return; + }; + + start += p; // assume areas don't overlap; alternative: start++; + } else + start++; + }; + + SearchStart = AddressToGBA (end + SearchLength - 1) + 1; + }; +}; + +unsigned int AddressToGBA(u8* mem) +{ + if(mem >= &bios[0] && mem <= &bios[0x3fff]) + return 0x00000000 + (mem - &bios[0]); + else if(mem >= &workRAM[0] && mem <= &workRAM[0x3ffff]) + return 0x02000000 + (mem - &workRAM[0]); + else if(mem >= &internalRAM[0] && mem <= &internalRAM[0x7fff]) + return 0x03000000 + (mem - &internalRAM[0]); + else if(mem >= &ioMem[0] && mem <= &ioMem[0x3ff]) + return 0x04000000 + (mem - &ioMem[0]); + else if(mem >= &paletteRAM[0] && mem <= &paletteRAM[0x3ff]) + return 0x05000000 + (mem - &paletteRAM[0]); + else if(mem >= &vram[0] && mem <= &vram[0x1ffff]) + return 0x06000000 + (mem - &vram[0]); + else if(mem >= &oam[0] && mem <= &oam[0x3ff]) + return 0x07000000 + (mem - &oam[0]); + else if(mem >= &rom[0] && mem <= &rom[0x1ffffff]) + return 0x08000000 + (mem - &rom[0]); + else + return 0xFFFFFFFF; +}; + +static void debuggerFileDisassemble(int n, char **args) +{ + if(n < 2) { + debuggerUsage("fd"); + } else { + if(armState) + debuggerFileDisassembleArm(n, args); + else + debuggerFileDisassembleThumb(n, args); + } +} + +static void debuggerContinueAfterBreakpoint() +{ + printf("Continuing after breakpoint\n"); + debuggerEnableBreakpoints(true); + debuggerPrefetch(); + emulator.emuMain(1); + debuggerAtBreakpoint = false; +} + +static void debuggerRegisters(int, char **) +{ + char m[] = { 'm', 0 }; + char one[] = { '1', 0 }; + + char *command[3] = { m , 0, one }; + char buffer[10]; + +#ifdef BKPT_SUPPORT + if (debugger_last) + { + printf("R00=%08x R04=%08x R08=%08x R12=%08x\n", + oldreg[0], oldreg[4], oldreg[8], oldreg[12]); + printf("R01=%08x R05=%08x R09=%08x R13=%08x\n", + oldreg[1], oldreg[5], oldreg[9], oldreg[13]); + printf("R02=%08x R06=%08x R10=%08x R14=%08x\n", + oldreg[2], oldreg[6], oldreg[10], oldreg[14]); + printf("R03=%08x R07=%08x R11=%08x R15=%08x\n", + oldreg[3], oldreg[7], oldreg[11], oldreg[15]); + command[1]=oldbuffer; + debuggerDisassemble(3, command); + printf("\n"); + } +#endif + + printf("R00=%08x R04=%08x R08=%08x R12=%08x\n", + reg[0].I, reg[4].I, reg[8].I, reg[12].I); + printf("R01=%08x R05=%08x R09=%08x R13=%08x\n", + reg[1].I, reg[5].I, reg[9].I, reg[13].I); + printf("R02=%08x R06=%08x R10=%08x R14=%08x\n", + reg[2].I, reg[6].I, reg[10].I, reg[14].I); + printf("R03=%08x R07=%08x R11=%08x R15=%08x\n", + reg[3].I, reg[7].I, reg[11].I, reg[15].I); + printf("CPSR=%08x (%c%c%c%c%c%c%c Mode: %02x)\n", + reg[16].I, + (N_FLAG ? 'N' : '.'), + (Z_FLAG ? 'Z' : '.'), + (C_FLAG ? 'C' : '.'), + (V_FLAG ? 'V' : '.'), + (armIrqEnable ? '.' : 'I'), + ((!(reg[16].I & 0x40)) ? '.' : 'F'), + (armState ? '.' : 'T'), + armMode); + sprintf(buffer,"%08x", armState ? reg[15].I - 4 : reg[15].I - 2); + command[1]=buffer; + debuggerDisassemble(3, command); +} + +static void debuggerIoVideo() +{ + printf("DISPCNT = %04x\n", DISPCNT); + printf("DISPSTAT = %04x\n", DISPSTAT); + printf("VCOUNT = %04x\n", VCOUNT); + printf("BG0CNT = %04x\n", BG0CNT); + printf("BG1CNT = %04x\n", BG1CNT); + printf("BG2CNT = %04x\n", BG2CNT); + printf("BG3CNT = %04x\n", BG3CNT); + printf("WIN0H = %04x\n", WIN0H); + printf("WIN0V = %04x\n", WIN0V); + printf("WIN1H = %04x\n", WIN1H); + printf("WIN1V = %04x\n", WIN1V); + printf("WININ = %04x\n", WININ); + printf("WINOUT = %04x\n", WINOUT); + printf("MOSAIC = %04x\n", MOSAIC); + printf("BLDMOD = %04x\n", BLDMOD); + printf("COLEV = %04x\n", COLEV); + printf("COLY = %04x\n", COLY); +} + +static void debuggerIoVideo2() +{ + printf("BG0HOFS = %04x\n", BG0HOFS); + printf("BG0VOFS = %04x\n", BG0VOFS); + printf("BG1HOFS = %04x\n", BG1HOFS); + printf("BG1VOFS = %04x\n", BG1VOFS); + printf("BG2HOFS = %04x\n", BG2HOFS); + printf("BG2VOFS = %04x\n", BG2VOFS); + printf("BG3HOFS = %04x\n", BG3HOFS); + printf("BG3VOFS = %04x\n", BG3VOFS); + printf("BG2PA = %04x\n", BG2PA); + printf("BG2PB = %04x\n", BG2PB); + printf("BG2PC = %04x\n", BG2PC); + printf("BG2PD = %04x\n", BG2PD); + printf("BG2X = %08x\n", (BG2X_H<<16)|BG2X_L); + printf("BG2Y = %08x\n", (BG2Y_H<<16)|BG2Y_L); + printf("BG3PA = %04x\n", BG3PA); + printf("BG3PB = %04x\n", BG3PB); + printf("BG3PC = %04x\n", BG3PC); + printf("BG3PD = %04x\n", BG3PD); + printf("BG3X = %08x\n", (BG3X_H<<16)|BG3X_L); + printf("BG3Y = %08x\n", (BG3Y_H<<16)|BG3Y_L); +} + +static void debuggerIoDMA() +{ + printf("DM0SAD = %08x\n", (DM0SAD_H<<16)|DM0SAD_L); + printf("DM0DAD = %08x\n", (DM0DAD_H<<16)|DM0DAD_L); + printf("DM0CNT = %08x\n", (DM0CNT_H<<16)|DM0CNT_L); + printf("DM1SAD = %08x\n", (DM1SAD_H<<16)|DM1SAD_L); + printf("DM1DAD = %08x\n", (DM1DAD_H<<16)|DM1DAD_L); + printf("DM1CNT = %08x\n", (DM1CNT_H<<16)|DM1CNT_L); + printf("DM2SAD = %08x\n", (DM2SAD_H<<16)|DM2SAD_L); + printf("DM2DAD = %08x\n", (DM2DAD_H<<16)|DM2DAD_L); + printf("DM2CNT = %08x\n", (DM2CNT_H<<16)|DM2CNT_L); + printf("DM3SAD = %08x\n", (DM3SAD_H<<16)|DM3SAD_L); + printf("DM3DAD = %08x\n", (DM3DAD_H<<16)|DM3DAD_L); + printf("DM3CNT = %08x\n", (DM3CNT_H<<16)|DM3CNT_L); +} + +static void debuggerIoTimer() +{ + printf("TM0D = %04x\n", TM0D); + printf("TM0CNT = %04x\n", TM0CNT); + printf("TM1D = %04x\n", TM1D); + printf("TM1CNT = %04x\n", TM1CNT); + printf("TM2D = %04x\n", TM2D); + printf("TM2CNT = %04x\n", TM2CNT); + printf("TM3D = %04x\n", TM3D); + printf("TM3CNT = %04x\n", TM3CNT); +} + +static void debuggerIoMisc() +{ + printf("P1 = %04x\n", P1); + printf("IE = %04x\n", IE); + printf("IF = %04x\n", IF); + printf("IME = %04x\n", IME); +} + +static void debuggerIo(int n, char **args) +{ + if(n == 1) { + debuggerIoVideo(); + return; + } + if(!strcmp(args[1], "video")) + debuggerIoVideo(); + else if(!strcmp(args[1], "video2")) + debuggerIoVideo2(); + else if(!strcmp(args[1], "dma")) + debuggerIoDMA(); + else if(!strcmp(args[1], "timer")) + debuggerIoTimer(); + else if(!strcmp(args[1], "misc")) + debuggerIoMisc(); + else printf("Unrecognized option %s\n", args[1]); +} + +static void debuggerEditByte(int n, char **args) +{ + if(n == 3) { + u32 address = 0x10000; + u32 byte = 0; + sscanf(args[1], "%x", &address); + sscanf(args[2], "%x", &byte); + debuggerWriteByte(address, (u8)byte); + } else + debuggerUsage("eb"); +} + +static void debuggerEditHalfWord(int n, char **args) +{ + if(n == 3) { + u32 address = 0x10000; + u32 HalfWord = 0; + sscanf(args[1], "%x", &address); + if(address & 1) { + printf("Error: address must be half-word aligned\n"); + return; + } + sscanf(args[2], "%x", &HalfWord); + debuggerWriteHalfWord(address, (u16)HalfWord); + } else + debuggerUsage("eh"); +} + +static void debuggerEditRegister(int n, char **args) +{ + if(n == 3) { + int r = 15; + u32 val; + sscanf(args[1], "%d", &r); + if(r > 16 || r == 15) { + // don't allow PC to change + printf("Error: Register must be valid (0-14,16)\n"); + return; + } + sscanf(args[2], "%x", &val); + reg[r].I=val; + printf("Register changed.\n"); + } else + debuggerUsage("er"); +} + +static void debuggerEdit(int n, char **args) +{ + if(n == 3) { + u32 address; + u32 byte; + sscanf(args[1], "%x", &address); + if(address & 3) { + printf("Error: address must be word aligned\n"); + return; + } + sscanf(args[2], "%x", &byte); + debuggerWriteMemory(address, (u32)byte); + } else + debuggerUsage("ew"); +} + + +#define ASCII(c) (c) < 32 ? '.' : (c) > 127 ? '.' : (c) + +static void debuggerMemoryByte(int n, char **args) +{ + if(n == 2) { + u32 addr = 0; + sscanf(args[1], "%x", &addr); + for(int _i = 0; _i < 16; _i++) { + int a = debuggerReadByte(addr); + int b = debuggerReadByte(addr+1); + int c = debuggerReadByte(addr+2); + int d = debuggerReadByte(addr+3); + int e = debuggerReadByte(addr+4); + int f = debuggerReadByte(addr+5); + int g = debuggerReadByte(addr+6); + int h = debuggerReadByte(addr+7); + int i = debuggerReadByte(addr+8); + int j = debuggerReadByte(addr+9); + int k = debuggerReadByte(addr+10); + int l = debuggerReadByte(addr+11); + int m = debuggerReadByte(addr+12); + int n = debuggerReadByte(addr+13); + int o = debuggerReadByte(addr+14); + int p = debuggerReadByte(addr+15); + + printf("%08x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n", + addr,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p, + ASCII(a),ASCII(b),ASCII(c),ASCII(d), + ASCII(e),ASCII(f),ASCII(g),ASCII(h), + ASCII(i),ASCII(j),ASCII(k),ASCII(l), + ASCII(m),ASCII(n),ASCII(o),ASCII(p)); + addr += 16; + } + } else + debuggerUsage("mb"); +} + +static void debuggerMemoryHalfWord(int n, char **args) +{ + if(n == 2) { + u32 addr = 0; + sscanf(args[1], "%x", &addr); + addr = addr & 0xfffffffe; + for(int _i = 0; _i < 16; _i++) { + int a = debuggerReadByte(addr); + int b = debuggerReadByte(addr+1); + int c = debuggerReadByte(addr+2); + int d = debuggerReadByte(addr+3); + int e = debuggerReadByte(addr+4); + int f = debuggerReadByte(addr+5); + int g = debuggerReadByte(addr+6); + int h = debuggerReadByte(addr+7); + int i = debuggerReadByte(addr+8); + int j = debuggerReadByte(addr+9); + int k = debuggerReadByte(addr+10); + int l = debuggerReadByte(addr+11); + int m = debuggerReadByte(addr+12); + int n = debuggerReadByte(addr+13); + int o = debuggerReadByte(addr+14); + int p = debuggerReadByte(addr+15); + + printf("%08x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n", + addr,b,a,d,c,f,e,h,g,j,i,l,k,n,m,p,o, + ASCII(a),ASCII(b),ASCII(c),ASCII(d), + ASCII(e),ASCII(f),ASCII(g),ASCII(h), + ASCII(i),ASCII(j),ASCII(k),ASCII(l), + ASCII(m),ASCII(n),ASCII(o),ASCII(p)); + addr += 16; + } + } else + debuggerUsage("mh"); +} + +static void debuggerMemory(int n, char **args) +{ + if(n == 2) { + u32 addr = 0; + sscanf(args[1], "%x", &addr); + addr = addr & 0xfffffffc; + for(int _i = 0; _i < 16; _i++) { + int a = debuggerReadByte(addr); + int b = debuggerReadByte(addr+1); + int c = debuggerReadByte(addr+2); + int d = debuggerReadByte(addr+3); + + int e = debuggerReadByte(addr+4); + int f = debuggerReadByte(addr+5); + int g = debuggerReadByte(addr+6); + int h = debuggerReadByte(addr+7); + + int i = debuggerReadByte(addr+8); + int j = debuggerReadByte(addr+9); + int k = debuggerReadByte(addr+10); + int l = debuggerReadByte(addr+11); + + int m = debuggerReadByte(addr+12); + int n = debuggerReadByte(addr+13); + int o = debuggerReadByte(addr+14); + int p = debuggerReadByte(addr+15); + + printf("%08x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n", + addr,d,c,b,a,h,g,f,e,l,k,j,i,p,o,n,m, + ASCII(a),ASCII(b),ASCII(c),ASCII(d), + ASCII(e),ASCII(f),ASCII(g),ASCII(h), + ASCII(i),ASCII(j),ASCII(k),ASCII(l), + ASCII(m),ASCII(n),ASCII(o),ASCII(p)); + addr += 16; + } + } else + debuggerUsage("mw"); +} + +static void debuggerQuit(int, char **) +{ + char buffer[10]; + printf("Are you sure you want to quit (y/n)? "); + if (!fgets(buffer, sizeof(buffer), stdin)) + return; + + if(buffer[0] == 'y' || buffer[0] == 'Y') { + debugger = false; + emulating = false; + } +} + +static void debuggerWriteState(int n, char **args) +{ + int num = 12; + + if(n == 2) { + sscanf(args[1],"%d",&num); + if(num > 0 && num < 11) + sdlWriteState(num-1); + else + printf("Savestate number must be in the 1-10 range"); + } + else + debuggerUsage("save"); +} + +static void debuggerReadState(int n, char **args) +{ + int num = 12; + + if(n == 2) { + sscanf(args[1],"%d",&num); + if(num > 0 && num < 11) + sdlReadState(num-1); + else + printf("Savestate number must be in the 1-10 range"); + } + else + debuggerUsage("load"); +} + +static void debuggerDumpLoad(int n, char** args) +{ + u32 address = 0; + const char *file; + FILE *f; + int c; + + if(n==3) { + file=args[1]; + + sscanf(args[2],"%x",&address); + + f=fopen(file,"rb"); + if(!f) { + printf("Error opening file.\n"); + return; + } + + fseek(f,0,SEEK_END); + int size=ftell(f); + fseek(f,0,SEEK_SET); + + for(int i=0;i 4) { //conditional args handled separately + int i = debuggerNumOfBreakpoints; + + u32 address = 0; + sscanf(args[1],"%x", &address); + + debuggerBreakpointList[i].address = address; + debuggerBreakpointList[i].value = debuggerReadHalfWord(address); + debuggerBreakpointList[i].size = 0; + + debuggerCondValidate(n, args,2); + } else + debuggerUsage("cbt"); + +} + +static void debuggerCondBreakArm(int n, char **args) +{ + if(n > 4) { //conditional args handled separately + + int i = debuggerNumOfBreakpoints; + u32 address = 0; + + sscanf(args[1],"%x", &address); + debuggerBreakpointList[i].address = address; + debuggerBreakpointList[i].value = debuggerReadMemory(address); + debuggerBreakpointList[i].size = 1; + debuggerCondValidate(n, args,2); + } else + debuggerUsage("cba"); +} + +static void debuggerCondValidate(int n, char **args,int start) +{ + /* + 0: address/register + 1: op + 2: value + 3: size + */ + + int i=debuggerNumOfBreakpoints; + + char *address=args[start]; + const char *op=args[start+1]; + char *value=args[start+2]; + const char *tsize,*taddress,*tvalue; + + int rel=0; + + u32 value1=0; + u32 value2=0; + + char size=0; + int j=1; + + if(n==6) { + size = args[start+3][0]; + if(size != 'b' && size != 'h' && size != 'w') { + printf("Invalid size.\n"); + return; + } + + switch(size) { + case 'b': + debuggerBreakpointList[i].cond_size=1; + tsize="byte"; + break; + case 'h': + debuggerBreakpointList[i].cond_size=2; + tsize="halfword"; + break; + case 'w': + debuggerBreakpointList[i].cond_size=4; + tsize="word"; + break; + } + } + + switch(toupper(address[0])) { + case '$': //is address + while (address[j]) { + address[j-1]=address[j]; + j++; + } + address[j-1]=0; + + sscanf(address,"%x",&value1); + switch(size) { + case 'h': + if(value1 & 1) { + printf("Misaligned Conditional Address.\n"); + return; + } + break; + case 'w': + if(value1 & 3) { + printf("Misaligned Conditional Address.\n"); + return; + } + case 'b': + break; + default: + printf("Erroneous Condition\n"); + debuggerUsage((char *)((toupper(args[0][2])=='T') ? "cbt" : "cba")); + return; + } + debuggerBreakpointList[i].ia1=true; + taddress="$"; + break; + case 'R': //is register + while(address[j]) { + address[j-1]=address[j]; + j++; + } + address[j-1]=0; + sscanf(address,"%d",&value1); + + if(value1 > 16) { + printf("Invalid Register.\n"); + return; + } + if(size) + size=0; + debuggerBreakpointList[i].ia1=true; + taddress="r"; + break; + default: //immediate; + printf("First Comparison Parameter should not be Immediate\n"); + return; + } + + debuggerBreakpointList[i].cond_address = value1; + + // Check op + switch(op[0]) { + case '=': // 1 + if (op[1] == '=' && op[2]==0) + rel=1; + else + goto error; + break; + case '!': //2 + if (op[1]=='=' && op[2]==0) + rel=2; + else + goto error; + break; + case '<': //3 + if(op[1]=='=') + rel=5; + else if (op[1]==0) + rel=3; + else + goto error; + break; + case '>': //4 + if (op[1]=='=') + rel=6; + else if (op[1]==0) + rel=4; + else + goto error; + break; + default: + error: + printf("Invalid comparison operator.\n"); + return; + } + + if(op==0) { + printf("Invalid comparison operator.\n"); + return; + } + debuggerBreakpointList[i].cond_rel=rel; + + switch(toupper(value[0])) { + case '$': //is address + while(value[j]) { + value[j-1]=value[j]; + j++; + } + value[j-1]=0; + + sscanf(value,"%x",&value2); + debuggerBreakpointList[i].ia2=true; + tvalue="$"; + switch(size) { + case 'h': + if(value2 & 1) { + printf("Misaligned Conditional Address.\n"); + return; + } + break; + case 'w': + if(value2 & 3) { + printf("Misaligned Conditional Address.\n"); + return; + } + case 'b': + break; + default: + printf("Erroneous Condition\n"); + debuggerUsage((char *)((toupper(args[0][2])=='T') ? "cbt" : "cba")); + return; + } + break; + case 'R': //is register + while(value[j]) { + value[j-1]=value[j]; + j++; + } + value[j-1]=0; + sscanf(value,"%d",&value2); + + if(value2 > 16) { + printf("Invalid Register.\n"); + return; + } + debuggerBreakpointList[i].ia2=true; + tvalue="r"; + break; + default: //immediate; + sscanf(value, "%x",&value2); + debuggerBreakpointList[i].ia2=false; + tvalue="0x"; + + switch(size) { + case 'b': + value2 &=0xFF; + break; + case 'h': + value2 &=0xFFFF; + break; + default: + case 'w': + value2 &=0xFFFFFFFF; + break; + } + break; + } + + debuggerBreakpointList[i].cond_value = value2; + debuggerNumOfBreakpoints++; + + // At here, everything's set. Display message. + switch(size) { + case 0: + printf("Added breakpoint on %08X if R%02d %s %08X\n", + debuggerBreakpointList[i].address, + debuggerBreakpointList[i].cond_address, + op, + debuggerBreakpointList[i].cond_value); + break; + case 'b': + printf("Added breakpoint on %08X if %s%08X %s %s%02X\n", + debuggerBreakpointList[i].address, + taddress, + debuggerBreakpointList[i].cond_address, + op,tvalue, + debuggerBreakpointList[i].cond_value); + break; + case 'h': + printf("Added breakpoint on %08X if %s%08X %s %s%04X\n", + debuggerBreakpointList[i].address, + taddress, + debuggerBreakpointList[i].cond_address, + op, + tvalue, + debuggerBreakpointList[i].cond_value); + break; + case 'w': + printf("Added breakpoint on %08X if %s%08X %s %s%08X\n", + debuggerBreakpointList[i].address, + taddress, + debuggerBreakpointList[i].cond_address, + op,tvalue, + debuggerBreakpointList[i].cond_value); + break; + } +} + +static bool debuggerCondEvaluate(int num) +{ + // check if there is a condition + if(debuggerBreakpointList[num].cond_rel == 0) + return true; + + u32 address=debuggerBreakpointList[num].cond_address; + char size=debuggerBreakpointList[num].cond_size; + u32 value=debuggerBreakpointList[num].cond_value; + u32 value1=0; + u32 value2=0; + + if(address<17) + value1=reg[address].I; + else { + switch(size) { + case 1: + value1=debuggerReadByte(address); + break; + case 2: + value1=debuggerReadHalfWord(address); + break; + default: + value1=debuggerReadMemory(address); + break; + } + } + + //value2 + if(debuggerBreakpointList[num].ia2) { //is address or register + if(value<17) + value2=reg[address].I; + else { + switch(size) { + case 'b': + value2=debuggerReadByte(value); + break; + case 'h': + value2=debuggerReadHalfWord(value); + break; + default: + value2=debuggerReadMemory(value); + break; + } + } + } else + value2=value; + + switch(debuggerBreakpointList[num].cond_rel) { + case 1: // == + return (value1 == value2); + case 2: // != + return (value1 != value2); + case 3: // < + return (value1 < value2); + case 4: // > + return (value1 > value2); + case 5: // <= + return (value1 <= value2); + case 6: // >= + return (value1 >= value2); + default: + return false; //should never happen + } +} + +/*extern*/ void debuggerOutput(const char *s, u32 addr) +{ + if(s) + printf("%s", s); + else { + char c; + + c = debuggerReadByte(addr); + addr++; + while(c) { + putchar(c); + c = debuggerReadByte(addr); + addr++; + } + } +} + +char* strqtok (char* string, const char* ctrl) +{ // quoted tokens + static char* nexttoken = NULL; + char* str; + + if (string != NULL) + str = string; + else { + if (nexttoken == NULL) + return NULL; + str = nexttoken; + }; + + char deli [32]; + memset (deli, 0, 32 * sizeof (char)); + while (*ctrl) + { + deli [*ctrl >> 3] |= (1 << (*ctrl & 7)); + ctrl++; + }; + // can't allow to be set + deli ['"' >> 3] &= ~(1 << ('"' & 7)); + + // jump over leading delimiters + while ((deli [*str >> 3] & (1 << (*str & 7))) && *str) + str++; + + if (*str == '"') + { + string = ++str; + + // only break if another quote or end of string is found + while ((*str != '"') && *str) + str++; + } else { + string = str; + + // break on delimiter + while (!(deli [*str >> 3] & (1 << (*str & 7))) && *str) + str++; + }; + + if (string == str) + { + nexttoken = NULL; + return NULL; + } else { + if (*str) + { + *str = 0; + nexttoken = str + 1; + } else + nexttoken = NULL; + + return string; + }; +}; + +/*extern*/ void debuggerMain() +{ + char buffer[1024]; + char *commands[10]; + int commandCount = 0; + + if(emulator.emuUpdateCPSR) + emulator.emuUpdateCPSR(); + debuggerRegisters(0, NULL); + + while(debugger) { + soundPause(); + debuggerDisableBreakpoints(); + printf("debugger> "); + commandCount = 0; + char *s = fgets(buffer, 1024, stdin); + + commands[0] = strqtok(s, " \t\n"); + if(commands[0] == NULL) + continue; + commandCount++; + while((s = strqtok(NULL, " \t\n"))) { + commands[commandCount++] = s; + if(commandCount == 10) + break; + } + + for(int j = 0; ; j++) { + if(debuggerCommands[j].name == NULL) { + printf("Unrecognized command %s. Type h for help.\n", commands[0]); + break; + } + if(!strcmp(commands[0], debuggerCommands[j].name)) { + debuggerCommands[j].function(commandCount, commands); + break; + } + } + } +} + +void debuggerLast(int n, char **args) +{ +debugger_last =!debugger_last; +if (debugger_last == true) +printf ("Last registers will be shown\n"); +else +printf ("Last registers wont be shown\n"); +} diff --git a/src/sdl/debugger.h b/src/sdl/debugger.h new file mode 100644 index 0000000..ec4ceab --- /dev/null +++ b/src/sdl/debugger.h @@ -0,0 +1,22 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +extern void debuggerMain(); +extern void debuggerOutput(const char *, u32); +extern void debuggerSignal(int,int); diff --git a/src/sdl/expr-lex.cpp b/src/sdl/expr-lex.cpp new file mode 100644 index 0000000..877072d --- /dev/null +++ b/src/sdl/expr-lex.cpp @@ -0,0 +1,1787 @@ +#line 2 "expr-lex.cpp" + +#line 4 "expr-lex.cpp" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 35 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +#ifdef __cplusplus + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +/* C99 requires __STDC__ to be defined as 1. */ +#if defined (__STDC__) + +#define YY_USE_CONST + +#endif /* defined (__STDC__) */ +#endif /* ! __cplusplus */ + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN (yy_start) = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START (((yy_start) - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart(yyin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#define YY_BUF_SIZE 16384 +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +extern int yyleng; + +extern FILE *yyin, *yyout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = (yy_hold_char); \ + YY_RESTORE_YY_MORE_OFFSET \ + (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, (yytext_ptr) ) + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* Stack of input buffers. */ +static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ +static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ +static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ + ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ + : NULL) + +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] + +/* yy_hold_char holds the character lost when yytext is formed. */ +static char yy_hold_char; +static int yy_n_chars; /* number of characters read into yy_ch_buf */ +int yyleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = (char *) 0; +static int yy_init = 0; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void yyrestart (FILE *input_file ); +void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ); +YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ); +void yy_delete_buffer (YY_BUFFER_STATE b ); +void yy_flush_buffer (YY_BUFFER_STATE b ); +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ); +void yypop_buffer_state (void ); + +static void yyensure_buffer_stack (void ); +static void yy_load_buffer_state (void ); +static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file ); + +#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER ) + +YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ); +YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ); +YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len ); + +void *yyalloc (yy_size_t ); +void *yyrealloc (void *,yy_size_t ); +void yyfree (void * ); + +#define yy_new_buffer yy_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer(yyin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer(yyin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +#define yywrap(n) 1 +#define YY_SKIP_YYWRAP + +typedef unsigned char YY_CHAR; + +FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; + +typedef int yy_state_type; + +extern int yylineno; + +int yylineno = 1; + +extern char *yytext; +#define yytext_ptr yytext + +static yy_state_type yy_get_previous_state (void ); +static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); +static int yy_get_next_buffer (void ); +static void yy_fatal_error (yyconst char msg[] ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + (yytext_ptr) = yy_bp; \ + yyleng = (size_t) (yy_cp - yy_bp); \ + (yy_hold_char) = *yy_cp; \ + *yy_cp = '\0'; \ + (yy_c_buf_p) = yy_cp; + +#define YY_NUM_RULES 10 +#define YY_END_OF_BUFFER 11 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static yyconst flex_int16_t yy_accept[24] = + { 0, + 0, 0, 11, 9, 8, 8, 6, 7, 9, 4, + 3, 2, 2, 8, 5, 3, 2, 2, 2, 2, + 2, 1, 0 + } ; + +static yyconst flex_int32_t yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 1, 1, 1, 1, 4, 1, 1, + 1, 5, 1, 1, 6, 7, 1, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 1, 1, 1, + 1, 9, 1, 1, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 1, 1, 1, 1, 10, 1, 10, 10, 10, 10, + + 11, 12, 10, 10, 13, 10, 10, 10, 10, 10, + 14, 10, 10, 10, 15, 10, 10, 10, 10, 10, + 10, 16, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static yyconst flex_int32_t yy_meta[17] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, + 2, 2, 2, 2, 2, 2 + } ; + +static yyconst flex_int16_t yy_base[25] = + { 0, + 0, 0, 32, 33, 15, 17, 33, 33, 22, 33, + 22, 0, 16, 19, 33, 20, 0, 11, 15, 11, + 12, 0, 33, 21 + } ; + +static yyconst flex_int16_t yy_def[25] = + { 0, + 23, 1, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 24, 24, 23, 23, 23, 24, 24, 24, 24, + 24, 24, 0, 23 + } ; + +static yyconst flex_int16_t yy_nxt[50] = + { 0, + 4, 5, 6, 7, 8, 9, 10, 11, 4, 12, + 12, 12, 12, 12, 13, 12, 14, 14, 14, 14, + 14, 14, 17, 22, 21, 20, 19, 16, 18, 16, + 15, 23, 3, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23 + } ; + +static yyconst flex_int16_t yy_chk[50] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 5, 5, 6, 6, + 14, 14, 24, 21, 20, 19, 18, 16, 13, 11, + 9, 3, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23 + } ; + +static yy_state_type yy_last_accepting_state; +static char *yy_last_accepting_cpos; + +extern int yy_flex_debug; +int yy_flex_debug = 0; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *yytext; +#line 1 "expr.l" +#line 2 "expr.l" +#include "expr.cpp.h" + +#ifndef __GNUC__ +#include +#define isatty _isatty +#define fileno _fileno +#endif + +char *exprString; +int exprCol; + +#define YY_INPUT(buf,result,max_size) \ + { \ + int c = *exprString++; \ + exprCol++;\ + result = (c == 0) ? YY_NULL : (buf[0] = c, 1); \ + } +#line 487 "expr-lex.cpp" + +#define INITIAL 0 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +static int yy_init_globals (void ); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy (void ); + +int yyget_debug (void ); + +void yyset_debug (int debug_flag ); + +YY_EXTRA_TYPE yyget_extra (void ); + +void yyset_extra (YY_EXTRA_TYPE user_defined ); + +FILE *yyget_in (void ); + +void yyset_in (FILE * in_str ); + +FILE *yyget_out (void ); + +void yyset_out (FILE * out_str ); + +int yyget_leng (void ); + +char *yyget_text (void ); + +int yyget_lineno (void ); + +void yyset_lineno (int line_number ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap (void ); +#else +extern int yywrap (void ); +#endif +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ); +#endif + +#ifndef YY_NO_INPUT + +#ifdef __cplusplus +static int yyinput (void ); +#else +static int input (void ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + size_t n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex (void); + +#define YY_DECL int yylex (void) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + +#line 33 "expr.l" + + +#line 670 "expr-lex.cpp" + + if ( !(yy_init) ) + { + (yy_init) = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! (yy_start) ) + (yy_start) = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer(yyin,YY_BUF_SIZE ); + } + + yy_load_buffer_state( ); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = (yy_c_buf_p); + + /* Support of yytext. */ + *yy_cp = (yy_hold_char); + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = (yy_start); +yy_match: + do + { + register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 24 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 33 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = (yy_hold_char); + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + goto yy_find_action; + +case 1: +YY_RULE_SETUP +#line 35 "expr.l" +{ + return TOKEN_SIZEOF; +} + YY_BREAK +case 2: +YY_RULE_SETUP +#line 39 "expr.l" +{ + return TOKEN_IDENTIFIER; +} + YY_BREAK +case 3: +YY_RULE_SETUP +#line 43 "expr.l" +{ + return TOKEN_NUMBER; +} + YY_BREAK +case 4: +YY_RULE_SETUP +#line 47 "expr.l" +{ + return TOKEN_DOT; +} + YY_BREAK +case 5: +YY_RULE_SETUP +#line 51 "expr.l" +{ + return TOKEN_ARROW; +} + YY_BREAK +case 6: +YY_RULE_SETUP +#line 55 "expr.l" +{ + return TOKEN_ADDR; +} + YY_BREAK +case 7: +YY_RULE_SETUP +#line 59 "expr.l" +{ + return TOKEN_STAR; +} + YY_BREAK +case 8: +/* rule 8 can match eol */ +YY_RULE_SETUP +#line 63 "expr.l" + + YY_BREAK +case 9: +YY_RULE_SETUP +#line 65 "expr.l" +return *yytext; + YY_BREAK +case 10: +YY_RULE_SETUP +#line 67 "expr.l" +ECHO; + YY_BREAK +#line 818 "expr-lex.cpp" +case YY_STATE_EOF(INITIAL): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = (yy_hold_char); + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++(yy_c_buf_p); + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = (yy_c_buf_p); + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_END_OF_FILE: + { + (yy_did_buffer_switch_on_eof) = 0; + + if ( yywrap( ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = + (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + (yy_c_buf_p) = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ +} /* end of yylex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (void) +{ + register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + register char *source = (yytext_ptr); + register int number_to_move, i; + int ret_val; + + if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER; + + int yy_c_buf_p_offset = + (int) ((yy_c_buf_p) - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + (yy_n_chars), (size_t) num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + if ( (yy_n_chars) == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart(yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + } + + (yy_n_chars) += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; + + (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (void) +{ + register yy_state_type yy_current_state; + register char *yy_cp; + + yy_current_state = (yy_start); + + for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) + { + register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 24 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) +{ + register int yy_is_jam; + register char *yy_cp = (yy_c_buf_p); + + register YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 24 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_is_jam = (yy_current_state == 23); + + return yy_is_jam ? 0 : yy_current_state; +} + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (void) +#else + static int input (void) +#endif + +{ + int c; + + *(yy_c_buf_p) = (yy_hold_char); + + if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + /* This was really a NUL. */ + *(yy_c_buf_p) = '\0'; + + else + { /* need more input */ + int offset = (yy_c_buf_p) - (yytext_ptr); + ++(yy_c_buf_p); + + switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart(yyin ); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap( ) ) + return EOF; + + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = (yytext_ptr) + offset; + break; + } + } + } + + c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ + *(yy_c_buf_p) = '\0'; /* preserve yytext */ + (yy_hold_char) = *++(yy_c_buf_p); + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * + * @note This function does not reset the start condition to @c INITIAL . + */ + void yyrestart (FILE * input_file ) +{ + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer(yyin,YY_BUF_SIZE ); + } + + yy_init_buffer(YY_CURRENT_BUFFER,input_file ); + yy_load_buffer_state( ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * + */ + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) +{ + + /* TODO. We should be able to replace this entire function body + * with + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + yyensure_buffer_stack (); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + yy_load_buffer_state( ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + (yy_did_buffer_switch_on_eof) = 1; +} + +static void yy_load_buffer_state (void) +{ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + (yy_hold_char) = *(yy_c_buf_p); +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * + * @return the allocated buffer state. + */ + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size ) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2 ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer(b,file ); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * + */ + void yy_delete_buffer (YY_BUFFER_STATE b ) +{ + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yyfree((void *) b->yy_ch_buf ); + + yyfree((void *) b ); +} + +#ifndef __cplusplus +extern int isatty (int ); +#endif /* __cplusplus */ + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file ) + +{ + int oerrno = errno; + + yy_flush_buffer(b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * + */ + void yy_flush_buffer (YY_BUFFER_STATE b ) +{ + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + yy_load_buffer_state( ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * + */ +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ) +{ + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(); + + /* This block is copied from yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + (yy_buffer_stack_top)++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * + */ +void yypop_buffer_state (void) +{ + if (!YY_CURRENT_BUFFER) + return; + + yy_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + if ((yy_buffer_stack_top) > 0) + --(yy_buffer_stack_top); + + if (YY_CURRENT_BUFFER) { + yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void yyensure_buffer_stack (void) +{ + int num_to_alloc; + + if (!(yy_buffer_stack)) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; + (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + (yy_buffer_stack_max) = num_to_alloc; + (yy_buffer_stack_top) = 0; + return; + } + + if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + int grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = (yy_buffer_stack_max) + grow_size; + (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc + ((yy_buffer_stack), + num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); + (yy_buffer_stack_max) = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size ) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer(b ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to yylex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (yyconst char * yystr ) +{ + + return yy_scan_bytes(yystr,strlen(yystr) ); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param bytes the byte buffer to scan + * @param len the number of bytes in the buffer pointed to by @a bytes. + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, int _yybytes_len ) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = _yybytes_len + 2; + buf = (char *) yyalloc(n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer(buf,n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yy_fatal_error (yyconst char* msg ) +{ + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = (yy_hold_char); \ + (yy_c_buf_p) = yytext + yyless_macro_arg; \ + (yy_hold_char) = *(yy_c_buf_p); \ + *(yy_c_buf_p) = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the current line number. + * + */ +int yyget_lineno (void) +{ + + return yylineno; +} + +/** Get the input stream. + * + */ +FILE *yyget_in (void) +{ + return yyin; +} + +/** Get the output stream. + * + */ +FILE *yyget_out (void) +{ + return yyout; +} + +/** Get the length of the current token. + * + */ +int yyget_leng (void) +{ + return yyleng; +} + +/** Get the current token. + * + */ + +char *yyget_text (void) +{ + return yytext; +} + +/** Set the current line number. + * @param line_number + * + */ +void yyset_lineno (int line_number ) +{ + + yylineno = line_number; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param in_str A readable stream. + * + * @see yy_switch_to_buffer + */ +void yyset_in (FILE * in_str ) +{ + yyin = in_str ; +} + +void yyset_out (FILE * out_str ) +{ + yyout = out_str ; +} + +int yyget_debug (void) +{ + return yy_flex_debug; +} + +void yyset_debug (int bdebug ) +{ + yy_flex_debug = bdebug ; +} + +static int yy_init_globals (void) +{ + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. + */ + + (yy_buffer_stack) = 0; + (yy_buffer_stack_top) = 0; + (yy_buffer_stack_max) = 0; + (yy_c_buf_p) = (char *) 0; + (yy_init) = 0; + (yy_start) = 0; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = (FILE *) 0; + yyout = (FILE *) 0; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (void) +{ + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(); + } + + /* Destroy the stack itself. */ + yyfree((yy_buffer_stack) ); + (yy_buffer_stack) = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * yylex() is called, initialization will occur. */ + yy_init_globals( ); + + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) +{ + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * s ) +{ + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size ) +{ + return (void *) malloc( size ); +} + +void *yyrealloc (void * ptr, yy_size_t size ) +{ + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); +} + +void yyfree (void * ptr ) +{ + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 67 "expr.l" + + + +void exprCleanBuffer() +{ + yy_delete_buffer(YY_CURRENT_BUFFER); + yy_init = 1; +} + diff --git a/src/sdl/expr.cpp b/src/sdl/expr.cpp new file mode 100644 index 0000000..32cc83c --- /dev/null +++ b/src/sdl/expr.cpp @@ -0,0 +1,1645 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* Skeleton implementation for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.3" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Using locations. */ +#define YYLSP_NEEDED 0 + + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + TOKEN_IDENTIFIER = 258, + TOKEN_DOT = 259, + TOKEN_STAR = 260, + TOKEN_ARROW = 261, + TOKEN_ADDR = 262, + TOKEN_SIZEOF = 263, + TOKEN_NUMBER = 264 + }; +#endif +/* Tokens. */ +#define TOKEN_IDENTIFIER 258 +#define TOKEN_DOT 259 +#define TOKEN_STAR 260 +#define TOKEN_ARROW 261 +#define TOKEN_ADDR 262 +#define TOKEN_SIZEOF 263 +#define TOKEN_NUMBER 264 + + + + +/* Copy the first part of user declarations. */ +#line 1 "expr.ypp" + +namespace std { +#include +#include +#include +#include +} + +using namespace std; + +#include "../System.h" +#include "../gba/elf.h" +#include "exprNode.h" + +extern int yyerror(const char *); +extern int yylex(); +extern char *yytext; + + +//#define YYERROR_VERBOSE 1 +//#define YYDEBUG 1 + + Node *result = NULL; + + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef int YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + + + +/* Copy the second part of user declarations. */ + + +/* Line 216 of yacc.c. */ +#line 149 "expr.tab.cpp" + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#elif (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +typedef signed char yytype_int8; +#else +typedef short int yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif +# endif +# ifndef YY_ +# define YY_(msgid) msgid +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(e) ((void) (e)) +#else +# define YYUSE(e) /* empty */ +#endif + +/* Identity function, used to suppress warnings about constant conditions. */ +#ifndef lint +# define YYID(n) (n) +#else +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static int +YYID (int i) +#else +static int +YYID (i) + int i; +#endif +{ + return i; +} +#endif + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined _STDLIB_H \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss; + YYSTYPE yyvs; + }; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (YYID (0)) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack, Stack, yysize); \ + Stack = &yyptr->Stack; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 14 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 27 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 14 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 6 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 13 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 26 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 264 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 11, 12, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 10, 2, 13, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const yytype_uint8 yyprhs[] = +{ + 0, 0, 3, 5, 7, 11, 15, 19, 24, 26, + 29, 32, 37, 39 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yytype_int8 yyrhs[] = +{ + 15, 0, -1, 16, -1, 17, -1, 11, 16, 12, + -1, 16, 4, 19, -1, 16, 6, 19, -1, 16, + 10, 18, 13, -1, 19, -1, 5, 16, -1, 7, + 16, -1, 8, 11, 16, 12, -1, 9, -1, 3, + -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const yytype_uint8 yyrline[] = +{ + 0, 32, 32, 36, 37, 38, 39, 40, 43, 44, + 45, 46, 50, 54 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "TOKEN_IDENTIFIER", "TOKEN_DOT", + "TOKEN_STAR", "TOKEN_ARROW", "TOKEN_ADDR", "TOKEN_SIZEOF", + "TOKEN_NUMBER", "'['", "'('", "')'", "']'", "$accept", "final", + "expression", "simple_expression", "number", "ident", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 91, 40, 41, 93 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 14, 15, 16, 16, 16, 16, 16, 17, 17, + 17, 17, 18, 19 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 1, 1, 3, 3, 3, 4, 1, 2, + 2, 4, 1, 1 +}; + +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 0, 13, 0, 0, 0, 0, 0, 2, 3, 8, + 9, 10, 0, 0, 1, 0, 0, 0, 0, 4, + 5, 6, 12, 0, 11, 7 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + -1, 6, 7, 8, 23, 9 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -10 +static const yytype_int8 yypact[] = +{ + 1, -10, 1, 1, -9, 1, 5, 17, -10, -10, + 17, 17, 1, 7, -10, 4, 4, 6, 10, -10, + -10, -10, -10, 13, -10, -10 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -10, -10, -2, -10, -10, 9 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -1 +static const yytype_uint8 yytable[] = +{ + 10, 11, 12, 13, 1, 14, 2, 1, 3, 4, + 18, 15, 5, 16, 15, 22, 16, 17, 0, 19, + 17, 15, 24, 16, 20, 21, 25, 17 +}; + +static const yytype_int8 yycheck[] = +{ + 2, 3, 11, 5, 3, 0, 5, 3, 7, 8, + 12, 4, 11, 6, 4, 9, 6, 10, -1, 12, + 10, 4, 12, 6, 15, 16, 13, 10 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 3, 5, 7, 8, 11, 15, 16, 17, 19, + 16, 16, 11, 16, 0, 4, 6, 10, 16, 12, + 19, 19, 9, 18, 12, 13 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ + +#define YYFAIL goto yyerrlab + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ + YYPOPSTACK (1); \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (YYID (0)) + + +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (YYID (N)) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (YYID (0)) +#endif + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if YYLTYPE_IS_TRIVIAL +# define YY_LOCATION_PRINT(File, Loc) \ + fprintf (File, "%d.%d-%d.%d", \ + (Loc).first_line, (Loc).first_column, \ + (Loc).last_line, (Loc).last_column) +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (YYLEX_PARAM) +#else +# define YYLEX yylex () +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (YYID (0)) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (YYID (0)) + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_value_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# else + YYUSE (yyoutput); +# endif + switch (yytype) + { + default: + break; + } +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + yy_symbol_value_print (yyoutput, yytype, yyvaluep); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) +#else +static void +yy_stack_print (bottom, top) + yytype_int16 *bottom; + yytype_int16 *top; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (; bottom <= top; ++bottom) + YYFPRINTF (stderr, " %d", *bottom); + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (YYID (0)) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_reduce_print (YYSTYPE *yyvsp, int yyrule) +#else +static void +yy_reduce_print (yyvsp, yyrule) + YYSTYPE *yyvsp; + int yyrule; +#endif +{ + int yynrhs = yyr2[yyrule]; + int yyi; + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + fprintf (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + ); + fprintf (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyvsp, Rule); \ +} while (YYID (0)) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static YYSIZE_T +yystrlen (const char *yystr) +#else +static YYSIZE_T +yystrlen (yystr) + const char *yystr; +#endif +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static char * +yystpcpy (char *yydest, const char *yysrc) +#else +static char * +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +#endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into YYRESULT an error message about the unexpected token + YYCHAR while in state YYSTATE. Return the number of bytes copied, + including the terminating null byte. If YYRESULT is null, do not + copy anything; just return the number of bytes that would be + copied. As a special case, return 0 if an ordinary "syntax error" + message will do. Return YYSIZE_MAXIMUM if overflow occurs during + size calculation. */ +static YYSIZE_T +yysyntax_error (char *yyresult, int yystate, int yychar) +{ + int yyn = yypact[yystate]; + + if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) + return 0; + else + { + int yytype = YYTRANSLATE (yychar); + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + int yysize_overflow = 0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + int yyx; + +# if 0 + /* This is so xgettext sees the translatable formats that are + constructed on the fly. */ + YY_("syntax error, unexpected %s"); + YY_("syntax error, unexpected %s, expecting %s"); + YY_("syntax error, unexpected %s, expecting %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); +# endif + char *yyfmt; + char const *yyf; + static char const yyunexpected[] = "syntax error, unexpected %s"; + static char const yyexpecting[] = ", expecting %s"; + static char const yyor[] = " or %s"; + char yyformat[sizeof yyunexpected + + sizeof yyexpecting - 1 + + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) + * (sizeof yyor - 1))]; + char const *yyprefix = yyexpecting; + + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yycount = 1; + + yyarg[0] = yytname[yytype]; + yyfmt = yystpcpy (yyformat, yyunexpected); + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + yyformat[sizeof yyunexpected - 1] = '\0'; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + yyfmt = yystpcpy (yyfmt, yyprefix); + yyprefix = yyor; + } + + yyf = YY_(yyformat); + yysize1 = yysize + yystrlen (yyf); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + + if (yysize_overflow) + return YYSIZE_MAXIMUM; + + if (yyresult) + { + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + char *yyp = yyresult; + int yyi = 0; + while ((*yyp = *yyf) != '\0') + { + if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyf += 2; + } + else + { + yyp++; + yyf++; + } + } + } + return yysize; + } +} +#endif /* YYERROR_VERBOSE */ + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) +#else +static void +yydestruct (yymsg, yytype, yyvaluep) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + YYUSE (yyvaluep); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + + default: + break; + } +} + + +/* Prevent warnings from -Wmissing-prototypes. */ + +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + + +/* The look-ahead symbol. */ +int yychar; + +/* The semantic value of the look-ahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; + + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *YYPARSE_PARAM) +#else +int +yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +#endif +#else /* ! YYPARSE_PARAM */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void) +#else +int +yyparse () + +#endif +#endif +{ + + int yystate; + int yyn; + int yyresult; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + /* Look-ahead token as an internal (translated) token number. */ + int yytoken = 0; +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + + /* Three stacks and their tools: + `yyss': related to states, + `yyvs': related to semantic values, + `yyls': related to locations. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss = yyssa; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp; + + + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + YYSIZE_T yystacksize = YYINITDEPTH; + + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + yyssp = yyss; + yyvsp = yyvs; + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + look-ahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to look-ahead token. */ + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a look-ahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + if (yyn == YYFINAL) + YYACCEPT; + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the look-ahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + yystate = yyn; + *++yyvsp = yylval; + + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: +#line 32 "expr.ypp" + { result = (yyvsp[(1) - (1)]); ;} + break; + + case 3: +#line 36 "expr.ypp" + { (yyval) = (yyvsp[(1) - (1)]); ;} + break; + + case 4: +#line 37 "expr.ypp" + { (yyval) = (yyvsp[(2) - (3)]); ;} + break; + + case 5: +#line 38 "expr.ypp" + { (yyval) = exprNodeDot((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); ;} + break; + + case 6: +#line 39 "expr.ypp" + { (yyval) = exprNodeArrow((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); ;} + break; + + case 7: +#line 40 "expr.ypp" + { (yyval) = exprNodeArray((yyvsp[(1) - (4)]), (yyvsp[(3) - (4)])); ;} + break; + + case 8: +#line 43 "expr.ypp" + { (yyval) = (yyvsp[(1) - (1)]); ;} + break; + + case 9: +#line 44 "expr.ypp" + { (yyval) = exprNodeStar((yyvsp[(2) - (2)])); ;} + break; + + case 10: +#line 45 "expr.ypp" + { (yyval) = exprNodeAddr((yyvsp[(2) - (2)])); ;} + break; + + case 11: +#line 46 "expr.ypp" + { (yyval) = exprNodeSizeof((yyvsp[(3) - (4)])); ;} + break; + + case 12: +#line 50 "expr.ypp" + { (yyval) = exprNodeNumber(); ;} + break; + + case 13: +#line 54 "expr.ypp" + {(yyval) = exprNodeIdentifier(); ;} + break; + + +/* Line 1267 of yacc.c. */ +#line 1410 "expr.tab.cpp" + default: break; + } + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (YY_("syntax error")); +#else + { + YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); + if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) + { + YYSIZE_T yyalloc = 2 * yysize; + if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) + yyalloc = YYSTACK_ALLOC_MAXIMUM; + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yyalloc); + if (yymsg) + yymsg_alloc = yyalloc; + else + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + } + } + + if (0 < yysize && yysize <= yymsg_alloc) + { + (void) yysyntax_error (yymsg, yystate, yychar); + yyerror (yymsg); + } + else + { + yyerror (YY_("syntax error")); + if (yysize != 0) + goto yyexhaustedlab; + } + } +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse look-ahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse look-ahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + if (yyn == YYFINAL) + YYACCEPT; + + *++yyvsp = yylval; + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#ifndef yyoverflow +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEOF && yychar != YYEMPTY) + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + /* Do not reclaim the symbols of the rule which action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); +} + + +#line 57 "expr.ypp" + + +int yyerror(const char *s) +{ + return 0; +} + +#ifndef SDL +extern FILE *yyin; +int main(int argc, char **argv) +{ + // yydebug = 1; + ++argv, --argc; + if(argc > 0) + yyin = fopen(argv[0], "r"); + else + yyin = stdin; + if(!yyparse()) + result->print(); +} +#endif + diff --git a/src/sdl/expr.cpp.h b/src/sdl/expr.cpp.h new file mode 100644 index 0000000..ea22694 --- /dev/null +++ b/src/sdl/expr.cpp.h @@ -0,0 +1,71 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* Skeleton interface for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + TOKEN_IDENTIFIER = 258, + TOKEN_DOT = 259, + TOKEN_STAR = 260, + TOKEN_ARROW = 261, + TOKEN_ADDR = 262, + TOKEN_SIZEOF = 263, + TOKEN_NUMBER = 264 + }; +#endif +/* Tokens. */ +#define TOKEN_IDENTIFIER 258 +#define TOKEN_DOT 259 +#define TOKEN_STAR 260 +#define TOKEN_ARROW 261 +#define TOKEN_ADDR 262 +#define TOKEN_SIZEOF 263 +#define TOKEN_NUMBER 264 + + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef int YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + +extern YYSTYPE yylval; + diff --git a/src/sdl/expr.l b/src/sdl/expr.l new file mode 100644 index 0000000..0ed070a --- /dev/null +++ b/src/sdl/expr.l @@ -0,0 +1,73 @@ +%{ +#include "expr.cpp.h" + +#ifndef __GNUC__ +#include +#define isatty _isatty +#define fileno _fileno +#endif + +char *exprString; +int exprCol; + +#define YY_INPUT(buf,result,max_size) \ + { \ + int c = *exprString++; \ + exprCol++;\ + result = (c == 0) ? YY_NULL : (buf[0] = c, 1); \ + } +%} + +%option nomain +%option noyywrap +%option nounput + +SIZEOF "sizeof" +ID [a-zA-Z_][a-zA-Z0-9_]* +NUM [0-9]+ +DOT "." +ARROW "->" +STAR "*" +ADDR "&" + +%% + +{SIZEOF} { + return TOKEN_SIZEOF; +} + +{ID} { + return TOKEN_IDENTIFIER; +} + +{NUM} { + return TOKEN_NUMBER; +} + +{DOT} { + return TOKEN_DOT; +} + +{ARROW} { + return TOKEN_ARROW; +} + +{ADDR} { + return TOKEN_ADDR; +} + +{STAR} { + return TOKEN_STAR; +} + +[ \t\n]+ + +. return *yytext; + +%% + +void exprCleanBuffer() +{ + yy_delete_buffer(YY_CURRENT_BUFFER); + yy_init = 1; +} diff --git a/src/sdl/expr.ypp b/src/sdl/expr.ypp new file mode 100644 index 0000000..843d09d --- /dev/null +++ b/src/sdl/expr.ypp @@ -0,0 +1,77 @@ +%{ +namespace std { +#include +#include +#include +#include +} + +using namespace std; + +#include "../System.h" +#include "../gba/elf.h" +#include "exprNode.h" + +extern int yyerror(const char *); +extern int yylex(); +extern char *yytext; + + +//#define YYERROR_VERBOSE 1 +//#define YYDEBUG 1 + + Node *result = NULL; +%} + +%token TOKEN_IDENTIFIER TOKEN_DOT TOKEN_STAR TOKEN_ARROW TOKEN_ADDR +%token TOKEN_SIZEOF TOKEN_NUMBER +%left TOKEN_DOT TOKEN_ARROW '[' +%expect 6 +%% + +final: expression { result = $1; } +; + +expression: + simple_expression { $$ = $1; } | + '(' expression ')' { $$ = $2; } | + expression TOKEN_DOT ident { $$ = exprNodeDot($1, $3); } | + expression TOKEN_ARROW ident { $$ = exprNodeArrow($1, $3); } | + expression '[' number ']' { $$ = exprNodeArray($1, $3); } +; +simple_expression: + ident { $$ = $1; } | + TOKEN_STAR expression { $$ = exprNodeStar($2); } | + TOKEN_ADDR expression { $$ = exprNodeAddr($2); } | + TOKEN_SIZEOF '(' expression ')' { $$ = exprNodeSizeof($3); } +; + +number: + TOKEN_NUMBER { $$ = exprNodeNumber(); } +; + +ident: + TOKEN_IDENTIFIER {$$ = exprNodeIdentifier(); } +; + +%% + +int yyerror(const char *s) +{ + return 0; +} + +#ifndef SDL +extern FILE *yyin; +int main(int argc, char **argv) +{ + // yydebug = 1; + ++argv, --argc; + if(argc > 0) + yyin = fopen(argv[0], "r"); + else + yyin = stdin; + if(!yyparse()) + result->print(); +} +#endif diff --git a/src/sdl/exprNode.cpp b/src/sdl/exprNode.cpp new file mode 100644 index 0000000..ac19889 --- /dev/null +++ b/src/sdl/exprNode.cpp @@ -0,0 +1,415 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include +#include +#include + +#include "../gba/GBA.h" +#include "../gba/elf.h" +#include "../common/Port.h" +#include "exprNode.h" + +#ifndef __GNUC__ +#define strdup _strdup +#endif + +extern char *yytext; + +#define debuggerReadMemory(addr) \ + READ32LE((&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])) + +const void *exprNodeCleanUpList[100]; +int exprNodeCleanUpCount = 0; +Type exprNodeType = { 0, TYPE_base, "int", DW_ATE_signed, 4, 0, {0}, 0 }; + +void exprNodeClean(const void *m) +{ + exprNodeCleanUpList[exprNodeCleanUpCount++] = m; +} + +void exprNodeCleanUp() +{ + for(int i = 0; i < exprNodeCleanUpCount; i++) { + free((void *)exprNodeCleanUpList[i]); + } + exprNodeCleanUpCount = 0; +} + +Node *exprNodeIdentifier() +{ + Node *n = (Node *)calloc(1, sizeof(Node)); + n->name = strdup(yytext); + + exprNodeClean(n->name); + exprNodeClean(n); + + n->print = exprNodeIdentifierPrint; + n->resolve = exprNodeIdentifierResolve; + return n; +} + +bool exprNodeIdentifierResolve(Node *n, Function *f, CompileUnit *u) +{ + Object *o; + if(elfGetObject(n->name, f, u, &o)) { + n->type = o->type; + n->location = elfDecodeLocation(f, o->location, &n->locType); + return true; + } else { + printf("Object %s not found\n", n->name); + } + return false; +} + +void exprNodeIdentifierPrint(Node *n) +{ + printf("%s", n->name); +} + +Node *exprNodeNumber() +{ + Node *n = (Node *)calloc(1, sizeof(Node)); + + exprNodeClean(n); + n->location = atoi(yytext); + n->type = &exprNodeType; + n->locType = LOCATION_value; + n->print = exprNodeNumberPrint; + n->resolve = exprNodeNumberResolve; + return n; +} + +bool exprNodeNumberResolve(Node *n, Function *f, CompileUnit *u) +{ + return true; +} + +void exprNodeNumberPrint(Node *n) +{ + printf("%d", n->location); +} + +Node *exprNodeStar(Node *exp) +{ + Node *n = (Node *)calloc(1, sizeof(Node)); + exprNodeClean(n); + + n->expression = exp; + + n->print = exprNodeStarPrint; + n->resolve = exprNodeStarResolve; + return n; +} + +bool exprNodeStarResolve(Node *n, Function *f, CompileUnit *u) +{ + if(n->expression->resolve(n->expression, f, u)) { + if(n->expression->type->type == TYPE_pointer) { + n->location = n->expression->location; + if(n->expression->locType == LOCATION_memory) { + n->location = debuggerReadMemory(n->location); + } else if(n->expression->locType == LOCATION_register) { + n->location = reg[n->expression->location].I; + } else { + n->location = n->expression->location; + } + n->type = n->expression->type->pointer; + n->locType = LOCATION_memory; + return true; + } else { + printf("Object is not of pointer type\n"); + } + } + return false; +} + +void exprNodeStarPrint(Node *n) +{ + printf("*"); + n->expression->print(n->expression); +} + +Node *exprNodeDot(Node *exp, Node *ident) +{ + Node *n = (Node *)calloc(1, sizeof(Node)); + exprNodeClean(n); + + n->expression = exp; + n->name = ident->name; + + n->print = exprNodeDotPrint; + n->resolve = exprNodeDotResolve; + return n; +} + +bool exprNodeDotResolve(Node *n, Function *f, CompileUnit *u) +{ + if(n->expression->resolve(n->expression, f, u)) { + TypeEnum tt = n->expression->type->type; + + if(tt == TYPE_struct || + tt == TYPE_union) { + u32 loc = n->expression->location; + Type *t = n->expression->type; + int count = t->structure->memberCount; + int i = 0; + while(i < count) { + Member *m = &t->structure->members[i]; + if(strcmp(m->name, n->name) == 0) { + // found member + n->type = m->type; + if(tt == TYPE_struct) { + n->location = elfDecodeLocation(f, m->location, &n->locType, + loc); + n->objLocation = loc; + } else { + n->location = loc; + n->locType = n->expression->locType; + n->objLocation = loc; + } + n->member = m; + return true; + } + i++; + } + printf("Member %s not found\n", n->name); + } else { + printf("Object is not of structure type\n"); + } + } + return false; +} + +void exprNodeDotPrint(Node *n) +{ + n->expression->print(n->expression); + printf(".%s", n->name); +} + +Node *exprNodeArrow(Node *exp, Node *ident) +{ + Node *n = (Node *)calloc(1, sizeof(Node)); + exprNodeClean(n); + + n->expression = exp; + n->name = ident->name; + + n->print = exprNodeArrowPrint; + n->resolve = exprNodeArrowResolve; + return n; +} + +bool exprNodeArrowResolve(Node *n, Function *f, CompileUnit *u) +{ + if(n->expression->resolve(n->expression, f, u)) { + TypeEnum tt = n->expression->type->type; + if(tt != TYPE_pointer) { + printf("Object not of pointer type\n"); + return false; + } + tt = n->expression->type->pointer->type; + + if(tt == TYPE_struct || + tt == TYPE_union) { + u32 loc = debuggerReadMemory(n->expression->location); + Type *t = n->expression->type->pointer; + int count = t->structure->memberCount; + int i = 0; + while(i < count) { + Member *m = &t->structure->members[i]; + if(strcmp(m->name, n->name) == 0) { + // found member + n->type = m->type; + if(tt == TYPE_struct) { + n->location = elfDecodeLocation(f, m->location, &n->locType, + loc); + n->objLocation = loc; + } else { + n->location = loc; + n->objLocation = loc; + } + n->locType = LOCATION_memory; + n->member = m; + return true; + } + i++; + } + printf("Member %s not found\n", n->name); + } else { + printf("Object is not of structure type\n"); + } + } + return false; +} + +void exprNodeArrowPrint(Node *n) +{ + n->expression->print(n->expression); + printf("->%s", n->name); +} + +Node *exprNodeAddr(Node *exp) +{ + Node *n = (Node *)calloc(1, sizeof(Node)); + exprNodeClean(n); + + n->expression = exp; + + n->print = exprNodeAddrPrint; + n->resolve = exprNodeAddrResolve; + return n; +} + +bool exprNodeAddrResolve(Node *n, Function *f, CompileUnit *u) +{ + if(n->expression->resolve(n->expression, f, u)) { + if(n->expression->locType == LOCATION_memory) { + n->location = n->expression->location; + n->locType = LOCATION_value; + n->type = &exprNodeType; + } else if(n->expression->locType == LOCATION_register) { + printf("Value is in register %d\n", n->expression->location); + } else { + printf("Direct value is %d\n", n->location); + } + return true; + } + return false; +} + +void exprNodeAddrPrint(Node *n) +{ + printf("*"); + n->expression->print(n->expression); +} + +Node *exprNodeSizeof(Node *exp) +{ + Node *n = (Node *)calloc(1, sizeof(Node)); + exprNodeClean(n); + + n->expression = exp; + + n->print = exprNodeSizeofPrint; + n->resolve = exprNodeSizeofResolve; + return n; +} + +bool exprNodeSizeofResolve(Node *n, Function *f, CompileUnit *u) +{ + if(n->expression->resolve(n->expression, f, u)) { + n->location = n->expression->type->size; + n->locType = LOCATION_value; + n->type = &exprNodeType; + return true; + } + return false; +} + +void exprNodeSizeofPrint(Node *n) +{ + printf("sizeof("); + n->expression->print(n->expression); + printf(")"); +} + +Node *exprNodeArray(Node *exp, Node *number) +{ + Node *n = (Node *)calloc(1, sizeof(Node)); + exprNodeClean(n); + + n->expression = exp; + n->value = number->location; + + n->print = exprNodeArrayPrint; + n->resolve = exprNodeArrayResolve; + return n; +} + +int exprNodeGetSize(Array *a, int index) +{ + index++; + if(index == a->maxBounds) { + return a->type->size; + } else { + int size = a->bounds[a->maxBounds-1] * a->type->size; + + for(int i = index; i < a->maxBounds-1; i++) { + size *= a->bounds[i]; + } + return size; + } +} + +bool exprNodeArrayResolve(Node *n, Function *f, CompileUnit *u) +{ + if(n->expression->resolve(n->expression, f, u)) { + TypeEnum tt = n->expression->type->type; + if(tt != TYPE_array && + tt != TYPE_pointer) { + printf("Object not of array or pointer type\n"); + return false; + } + + if(tt == TYPE_array) { + Array *a = n->expression->type->array; + + u32 loc = n->expression->location; + Type *t = a->type; + if(a->maxBounds > 1) { + int index = n->expression->index; + + if(index == a->maxBounds) { + printf("Too many indices for array\n"); + return false; + } + + if((index+1) < a->maxBounds) { + n->type = n->expression->type; + n->index = index+1; + n->locType = LOCATION_memory; + n->location = n->expression->location + + n->value * exprNodeGetSize(a, index); + return true; + } + } + n->type = t; + n->location = loc + n->value * t->size; + n->locType = LOCATION_memory; + } else { + Type *t = n->expression->type->pointer; + u32 loc = n->expression->location; + if(n->expression->locType == LOCATION_register) + loc = reg[loc].I; + else + loc = debuggerReadMemory(loc); + n->type = t; + n->location = loc + n->value * t->size; + n->locType = LOCATION_memory; + } + return true; + } + return false; +} + +void exprNodeArrayPrint(Node *n) +{ + n->expression->print(n->expression); + printf("[%d]", n->value); +} diff --git a/src/sdl/exprNode.h b/src/sdl/exprNode.h new file mode 100644 index 0000000..5b505d7 --- /dev/null +++ b/src/sdl/exprNode.h @@ -0,0 +1,68 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +struct Node { + Type *type; + u32 location; + u32 objLocation; + LocationType locType; + int value; + int index; + const char *name; + Node *expression; + Member *member; + void (*print)(Node *); + bool (*resolve)(Node *, Function *f, CompileUnit *u); +}; + +extern void exprNodeCleanUp(); + +extern Node *exprNodeIdentifier(); +extern void exprNodeIdentifierPrint(Node *); +extern bool exprNodeIdentifierResolve(Node *, Function *, CompileUnit *); + +extern Node *exprNodeNumber(); +extern void exprNodeNumberPrint(Node *); +extern bool exprNodeNumberResolve(Node *, Function *, CompileUnit *); + +extern Node *exprNodeStar(Node *); +extern void exprNodeStarPrint(Node *); +extern bool exprNodeStarResolve(Node *, Function *, CompileUnit *); + +extern Node *exprNodeDot(Node *, Node *); +extern void exprNodeDotPrint(Node *); +extern bool exprNodeDotResolve(Node *, Function *, CompileUnit *); + +extern Node *exprNodeArrow(Node *, Node *); +extern void exprNodeArrowPrint(Node *); +extern bool exprNodeArrowResolve(Node *, Function *, CompileUnit *); + +extern Node *exprNodeAddr(Node *); +extern void exprNodeAddrPrint(Node *); +extern bool exprNodeAddrResolve(Node *, Function *, CompileUnit *); + +extern Node *exprNodeSizeof(Node *); +extern void exprNodeSizeofPrint(Node *); +extern bool exprNodeSizeofResolve(Node *, Function *, CompileUnit *); + +extern Node *exprNodeArray(Node *, Node *); +extern void exprNodeArrayPrint(Node *); +extern bool exprNodeArrayResolve(Node *, Function *, CompileUnit *); + +#define YYSTYPE struct Node * diff --git a/src/sdl/filters.cpp b/src/sdl/filters.cpp new file mode 100644 index 0000000..503f8b0 --- /dev/null +++ b/src/sdl/filters.cpp @@ -0,0 +1,678 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004-2008 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "filters.h" + + // + // Screen filters + // + +extern int Init_2xSaI(u32); +extern void hq2x_init(unsigned); +extern bool sdlStretchInit(int colorDepth, int sizeMultiplier, int srcWidth); + +extern void sdlStretch1x(u8*,u32,u8*,u8*,u32,int,int); +extern void sdlStretch2x(u8*,u32,u8*,u8*,u32,int,int); +extern void sdlStretch3x(u8*,u32,u8*,u8*,u32,int,int); +extern void sdlStretch4x(u8*,u32,u8*,u8*,u32,int,int); +extern void _2xSaI(u8*,u32,u8*,u8*,u32,int,int); +extern void _2xSaI32(u8*,u32,u8*,u8*,u32,int,int); +extern void Super2xSaI(u8*,u32,u8*,u8*,u32,int,int); +extern void Super2xSaI32(u8*,u32,u8*,u8*,u32,int,int); +extern void SuperEagle(u8*,u32,u8*,u8*,u32,int,int); +extern void SuperEagle32(u8*,u32,u8*,u8*,u32,int,int); +extern void Pixelate(u8*,u32,u8*,u8*,u32,int,int); +extern void Pixelate32(u8*,u32,u8*,u8*,u32,int,int); +extern void AdMame2x(u8*,u32,u8*,u8*,u32,int,int); +extern void AdMame2x32(u8*,u32,u8*,u8*,u32,int,int); +extern void Bilinear(u8*,u32,u8*,u8*,u32,int,int); +extern void Bilinear32(u8*,u32,u8*,u8*,u32,int,int); +extern void BilinearPlus(u8*,u32,u8*,u8*,u32,int,int); +extern void BilinearPlus32(u8*,u32,u8*,u8*,u32,int,int); +extern void Scanlines(u8*,u32,u8*,u8*,u32,int,int); +extern void Scanlines32(u8*,u32,u8*,u8*,u32,int,int); +extern void ScanlinesTV(u8*,u32,u8*,u8*,u32,int,int); +extern void ScanlinesTV32(u8*,u32,u8*,u8*,u32,int,int); +extern void hq2x(u8*,u32,u8*,u8*,u32,int,int); +extern void hq2x32(u8*,u32,u8*,u8*,u32,int,int); +extern void lq2x(u8*,u32,u8*,u8*,u32,int,int); +extern void lq2x32(u8*,u32,u8*,u8*,u32,int,int); +extern void hq3x16(u8*,u32,u8*,u8*,u32,int,int); +extern void hq4x16(u8*,u32,u8*,u8*,u32,int,int); +extern void hq3x32_32(u8*,u32,u8*,u8*,u32,int,int); +extern void hq4x32_32(u8*,u32,u8*,u8*,u32,int,int); + +struct FilterDesc { + char name[30]; + int enlargeFactor; + FilterFunc func16; + FilterFunc func24; + FilterFunc func32; +}; + +const FilterDesc Filters[] = { + { "Stretch 1x", 1, sdlStretch1x, sdlStretch1x, sdlStretch1x }, + { "Stretch 2x", 2, sdlStretch2x, sdlStretch2x, sdlStretch2x }, + { "2xSaI", 2, _2xSaI, 0, _2xSaI32 }, + { "Super 2xSaI", 2, Super2xSaI, 0, Super2xSaI32 }, + { "Super Eagle", 2, SuperEagle, 0, SuperEagle32 }, + { "Pixelate", 2, Pixelate, 0, Pixelate32 }, + { "AdvanceMAME Scale2x", 2, AdMame2x, 0, AdMame2x32 }, + { "Bilinear", 2, Bilinear, 0, Bilinear32 }, + { "Bilinear Plus", 2, BilinearPlus, 0, BilinearPlus32 }, + { "Scanlines", 2, Scanlines, 0, Scanlines32 }, + { "TV Mode", 2, ScanlinesTV, 0, ScanlinesTV32 }, + { "lq2x", 2, lq2x, 0, lq2x32 }, + { "hq2x", 2, hq2x, 0, hq2x32 }, + { "Stretch 3x", 3, sdlStretch3x, sdlStretch3x, sdlStretch3x }, + { "hq3x", 3, hq3x16, 0, hq3x32_32 }, + { "Stretch 4x", 4, sdlStretch4x, sdlStretch4x, sdlStretch4x }, + { "hq4x", 4, hq4x16, 0, hq4x32_32 } +}; + +int getFilterEnlargeFactor(const Filter f) +{ + return Filters[f].enlargeFactor; +} + +char* getFilterName(const Filter f) +{ + return (char*)Filters[f].name; +} + +FilterFunc initFilter(const Filter f, const int colorDepth, const int srcWidth) +{ + FilterFunc func; + + switch (colorDepth) { + case 15: + case 16: + func = Filters[f].func16; + break; + case 24: + func = Filters[f].func24; + break; + case 32: + func = Filters[f].func32; + break; + default: + func = 0; + break; + } + + if (func) + switch (f) { + case kStretch1x: + sdlStretchInit(colorDepth, 0, srcWidth); + break; + case kStretch2x: + sdlStretchInit(colorDepth, 1, srcWidth); + break; + case kStretch3x: + sdlStretchInit(colorDepth, 2, srcWidth); + break; + case kStretch4x: + sdlStretchInit(colorDepth, 3, srcWidth); + break; + case k2xSaI: + case kSuper2xSaI: + case kSuperEagle: + if (colorDepth == 15) Init_2xSaI(555); + else if (colorDepth == 16) Init_2xSaI(565); + else Init_2xSaI(colorDepth); + break; + case khq2x: + case klq2x: + hq2x_init(colorDepth); + break; + default: + break; + } + + return func; +} + + // + // Interframe blending filters + // + +extern void SmartIB(u8*,u32,int,int); +extern void SmartIB32(u8*,u32,int,int); +extern void MotionBlurIB(u8*,u32,int,int); +extern void MotionBlurIB32(u8*,u32,int,int); + +struct IFBFilterDesc { + char name[30]; + IFBFilterFunc func16; + IFBFilterFunc func32; +}; + +const IFBFilterDesc IFBFilters[] = { + { "No interframe blending", 0, 0 }, + { "Interframe motion blur", MotionBlurIB, MotionBlurIB32 }, + { "Smart interframe blending", SmartIB, SmartIB32 } +}; + +IFBFilterFunc initIFBFilter(const IFBFilter f, const int colorDepth) +{ + IFBFilterFunc func; + + switch (colorDepth) { + case 15: + case 16: + func = IFBFilters[f].func16; + break; + case 32: + func = IFBFilters[f].func32; + break; + case 24: + default: + func = 0; + break; + } + + return func; +} + +char* getIFBFilterName(const IFBFilter f) +{ + return (char*)IFBFilters[f].name; +} + + // + // Optimized stretchers implementation + // + +#ifndef C_CORE +u8 sdlStretcher[16384]; + +#ifdef _MSC_VER +#define SDL_CALL_STRETCHER \ + {\ + __asm mov eax, stretcher\ + __asm mov edi, destPtr\ + __asm mov esi, srcPtr\ + __asm call eax\ + } +#else +#define SDL_CALL_STRETCHER \ + asm volatile("call *%%eax"::"a" (stretcher),"S" (srcPtr),"D" (dstPtr)) +#endif + +#define SDL_LONG(val) \ + *((u32 *)&sdlStretcher[sdlStretcherPos]) = val;\ + sdlStretcherPos+=4; + +#define SDL_AND_EAX(val) \ + sdlStretcher[sdlStretcherPos++] = 0x25;\ + SDL_LONG(val); + +#define SDL_AND_EBX(val) \ + sdlStretcher[sdlStretcherPos++] = 0x81;\ + sdlStretcher[sdlStretcherPos++] = 0xe3;\ + SDL_LONG(val); + +#define SDL_OR_EAX_EBX \ + sdlStretcher[sdlStretcherPos++] = 0x09;\ + sdlStretcher[sdlStretcherPos++] = 0xd8; + +#define SDL_LOADL_EBX \ + sdlStretcher[sdlStretcherPos++] = 0x8b;\ + sdlStretcher[sdlStretcherPos++] = 0x1f; + +#define SDL_LOADW \ + sdlStretcher[sdlStretcherPos++] = 0x66;\ + sdlStretcher[sdlStretcherPos++] = 0x8b;\ + sdlStretcher[sdlStretcherPos++] = 0x06;\ + sdlStretcher[sdlStretcherPos++] = 0x83;\ + sdlStretcher[sdlStretcherPos++] = 0xc6;\ + sdlStretcher[sdlStretcherPos++] = 0x02; + +#define SDL_LOADL \ + sdlStretcher[sdlStretcherPos++] = 0x8b;\ + sdlStretcher[sdlStretcherPos++] = 0x06;\ + sdlStretcher[sdlStretcherPos++] = 0x83;\ + sdlStretcher[sdlStretcherPos++] = 0xc6;\ + sdlStretcher[sdlStretcherPos++] = 0x04; + +#define SDL_LOADL2 \ + sdlStretcher[sdlStretcherPos++] = 0x8b;\ + sdlStretcher[sdlStretcherPos++] = 0x06;\ + sdlStretcher[sdlStretcherPos++] = 0x83;\ + sdlStretcher[sdlStretcherPos++] = 0xc6;\ + sdlStretcher[sdlStretcherPos++] = 0x03; + +#define SDL_STOREW \ + sdlStretcher[sdlStretcherPos++] = 0x66;\ + sdlStretcher[sdlStretcherPos++] = 0x89;\ + sdlStretcher[sdlStretcherPos++] = 0x07;\ + sdlStretcher[sdlStretcherPos++] = 0x83;\ + sdlStretcher[sdlStretcherPos++] = 0xc7;\ + sdlStretcher[sdlStretcherPos++] = 0x02; + +#define SDL_STOREL \ + sdlStretcher[sdlStretcherPos++] = 0x89;\ + sdlStretcher[sdlStretcherPos++] = 0x07;\ + sdlStretcher[sdlStretcherPos++] = 0x83;\ + sdlStretcher[sdlStretcherPos++] = 0xc7;\ + sdlStretcher[sdlStretcherPos++] = 0x04; + +#define SDL_STOREL2 \ + sdlStretcher[sdlStretcherPos++] = 0x89;\ + sdlStretcher[sdlStretcherPos++] = 0x07;\ + sdlStretcher[sdlStretcherPos++] = 0x83;\ + sdlStretcher[sdlStretcherPos++] = 0xc7;\ + sdlStretcher[sdlStretcherPos++] = 0x03; + +#define SDL_RET \ + sdlStretcher[sdlStretcherPos++] = 0xc3; + +#define SDL_PUSH_EAX \ + sdlStretcher[sdlStretcherPos++] = 0x50; + +#define SDL_PUSH_ECX \ + sdlStretcher[sdlStretcherPos++] = 0x51; + +#define SDL_PUSH_EBX \ + sdlStretcher[sdlStretcherPos++] = 0x53; + +#define SDL_PUSH_ESI \ + sdlStretcher[sdlStretcherPos++] = 0x56; + +#define SDL_PUSH_EDI \ + sdlStretcher[sdlStretcherPos++] = 0x57; + +#define SDL_POP_EAX \ + sdlStretcher[sdlStretcherPos++] = 0x58; + +#define SDL_POP_ECX \ + sdlStretcher[sdlStretcherPos++] = 0x59; + +#define SDL_POP_EBX \ + sdlStretcher[sdlStretcherPos++] = 0x5b; + +#define SDL_POP_ESI \ + sdlStretcher[sdlStretcherPos++] = 0x5e; + +#define SDL_POP_EDI \ + sdlStretcher[sdlStretcherPos++] = 0x5f; + +#define SDL_MOV_ECX(val) \ + sdlStretcher[sdlStretcherPos++] = 0xb9;\ + SDL_LONG(val); + +#define SDL_REP_MOVSB \ + sdlStretcher[sdlStretcherPos++] = 0xf3;\ + sdlStretcher[sdlStretcherPos++] = 0xa4; + +#define SDL_REP_MOVSW \ + sdlStretcher[sdlStretcherPos++] = 0xf3;\ + sdlStretcher[sdlStretcherPos++] = 0x66;\ + sdlStretcher[sdlStretcherPos++] = 0xa5; + +#define SDL_REP_MOVSL \ + sdlStretcher[sdlStretcherPos++] = 0xf3;\ + sdlStretcher[sdlStretcherPos++] = 0xa5; + +void sdlMakeStretcher(int width, int sizeOption) +{ + int sdlStretcherPos; + sdlStretcherPos = 0; + switch(systemColorDepth) { + case 16: + if(sizeOption) { + SDL_PUSH_EAX; + SDL_PUSH_ESI; + SDL_PUSH_EDI; + for(int i = 0; i < width; i++) { + SDL_LOADW; + SDL_STOREW; + SDL_STOREW; + if(sizeOption > 1) { + SDL_STOREW; + } + if(sizeOption > 2) { + SDL_STOREW; + } + } + SDL_POP_EDI; + SDL_POP_ESI; + SDL_POP_EAX; + SDL_RET; + } else { + SDL_PUSH_ESI; + SDL_PUSH_EDI; + SDL_PUSH_ECX; + SDL_MOV_ECX(width); + SDL_REP_MOVSW; + SDL_POP_ECX; + SDL_POP_EDI; + SDL_POP_ESI; + SDL_RET; + } + break; + case 24: + if(sizeOption) { + SDL_PUSH_EAX; + SDL_PUSH_ESI; + SDL_PUSH_EDI; + int w = width - 1; + for(int i = 0; i < w; i++) { + SDL_LOADL2; + SDL_STOREL2; + SDL_STOREL2; + if(sizeOption > 1) { + SDL_STOREL2; + } + if(sizeOption > 2) { + SDL_STOREL2; + } + } + // need to write the last one + SDL_LOADL2; + SDL_STOREL2; + if(sizeOption > 1) { + SDL_STOREL2; + } + if(sizeOption > 2) { + SDL_STOREL2; + } + SDL_AND_EAX(0x00ffffff); + SDL_PUSH_EBX; + SDL_LOADL_EBX; + SDL_AND_EBX(0xff000000); + SDL_OR_EAX_EBX; + SDL_POP_EBX; + SDL_STOREL2; + SDL_POP_EDI; + SDL_POP_ESI; + SDL_POP_EAX; + SDL_RET; + } else { + SDL_PUSH_ESI; + SDL_PUSH_EDI; + SDL_PUSH_ECX; + SDL_MOV_ECX(3*width); + SDL_REP_MOVSB; + SDL_POP_ECX; + SDL_POP_EDI; + SDL_POP_ESI; + SDL_RET; + } + break; + case 32: + if(sizeOption) { + SDL_PUSH_EAX; + SDL_PUSH_ESI; + SDL_PUSH_EDI; + for(int i = 0; i < width; i++) { + SDL_LOADL; + SDL_STOREL; + SDL_STOREL; + if(sizeOption > 1) { + SDL_STOREL; + } + if(sizeOption > 2) { + SDL_STOREL; + } + } + SDL_POP_EDI; + SDL_POP_ESI; + SDL_POP_EAX; + SDL_RET; + } else { + SDL_PUSH_ESI; + SDL_PUSH_EDI; + SDL_PUSH_ECX; + SDL_MOV_ECX(width); + SDL_REP_MOVSL; + SDL_POP_ECX; + SDL_POP_EDI; + SDL_POP_ESI; + SDL_RET; + } + break; + } +} + +#else // C_CORE + +void (*sdlStretcher)(u8 *, u8*, int) = 0; + +#define SDL_CALL_STRETCHER \ + sdlStretcher(srcPtr, dstPtr, width) + +template +void sdlStretchx1(u8 *src, u8 *dest, int width) +{ + T *s = (T *)src; + T *d = (T *)dest; + for(int i = 0; i < width; i++) + *d++ = *s++; +} + +template +void sdlStretchx2(u8 *src, u8 *dest, int width) +{ + T *s = (T *)src; + T *d = (T *)dest; + for(int i = 0; i < width; i++) { + *d++ = *s; + *d++ = *s++; + } +} + +template +void sdlStretchx3(u8 *src, u8 *dest, int width) +{ + T *s = (T *)src; + T *d = (T *)dest; + for(int i = 0; i < width; i++) { + *d++ = *s; + *d++ = *s; + *d++ = *s++; + } +} + +template +void sdlStretchx4(u8 *src, u8 *dest, int width) +{ + T *s = (T *)src; + T *d = (T *)dest; + for(int i = 0; i < width; i++) { + *d++ = *s; + *d++ = *s; + *d++ = *s; + *d++ = *s++; + } +} + +void (*sdlStretcher16[4])(u8 *, u8 *, int) = { + sdlStretchx1, + sdlStretchx2, + sdlStretchx3, + sdlStretchx4 +}; + +void (*sdlStretcher32[4])(u8 *, u8 *, int) = { + sdlStretchx1, + sdlStretchx2, + sdlStretchx3, + sdlStretchx4 +}; + +void sdlStretch24x1(u8 *src, u8 *dest, int width) +{ + u8 *s = src; + u8 *d = dest; + for(int i = 0; i < width; i++) { + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + } +} + +void sdlStretch24x2(u8 *src, u8 *dest, int width) +{ + u8 *s = (u8 *)src; + u8 *d = (u8 *)dest; + for(int i = 0; i < width; i++) { + *d++ = *s; + *d++ = *(s+1); + *d++ = *(s+2); + s += 3; + *d++ = *s; + *d++ = *(s+1); + *d++ = *(s+2); + s += 3; + } +} + +void sdlStretch24x3(u8 *src, u8 *dest, int width) +{ + u8 *s = (u8 *)src; + u8 *d = (u8 *)dest; + for(int i = 0; i < width; i++) { + *d++ = *s; + *d++ = *(s+1); + *d++ = *(s+2); + s += 3; + *d++ = *s; + *d++ = *(s+1); + *d++ = *(s+2); + s += 3; + *d++ = *s; + *d++ = *(s+1); + *d++ = *(s+2); + s += 3; + } +} + +void sdlStretch24x4(u8 *src, u8 *dest, int width) +{ + u8 *s = (u8 *)src; + u8 *d = (u8 *)dest; + for(int i = 0; i < width; i++) { + *d++ = *s; + *d++ = *(s+1); + *d++ = *(s+2); + s += 3; + *d++ = *s; + *d++ = *(s+1); + *d++ = *(s+2); + s += 3; + *d++ = *s; + *d++ = *(s+1); + *d++ = *(s+2); + s += 3; + *d++ = *s; + *d++ = *(s+1); + *d++ = *(s+2); + s += 3; + } +} + +void (*sdlStretcher24[4])(u8 *, u8 *, int) = { + sdlStretch24x1, + sdlStretch24x2, + sdlStretch24x3, + sdlStretch24x4 +}; + +#endif // C_CORE + +bool sdlStretchInit(int colorDepth, int sizeMultiplier, int srcWidth) +{ +#ifndef C_CORE + sdlMakeStretcher(srcWidth, sizeMultiplier); +#else + switch(colorDepth) { + case 16: + sdlStretcher = sdlStretcher16[sizeMultiplier]; + break; + case 24: + sdlStretcher = sdlStretcher24[sizeMultiplier]; + break; + case 32: + sdlStretcher = sdlStretcher32[sizeMultiplier]; + break; + default: + return false; + } +#endif + return true; +} + +void sdlStretch1x(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, u8 *dstPtr, u32 dstPitch, int width, int height) { + int i; +#ifndef C_CORE + u32 *stretcher = (u32 *)sdlStretcher; +#endif + for(i = 0; i < height; i++) { + SDL_CALL_STRETCHER; + srcPtr += srcPitch; + dstPtr += dstPitch; + } +} +void sdlStretch2x(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, u8 *dstPtr, u32 dstPitch, int width, int height) { + int i; +#ifndef C_CORE + u32 *stretcher = (u32 *)sdlStretcher; +#endif + for(i = 0; i < height; i++) { + SDL_CALL_STRETCHER; + dstPtr += dstPitch; + SDL_CALL_STRETCHER; + srcPtr += srcPitch; + dstPtr += dstPitch; + } +} +void sdlStretch3x(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, u8 *dstPtr, u32 dstPitch, int width, int height) { + int i; +#ifndef C_CORE + u32 *stretcher = (u32 *)sdlStretcher; +#endif + for(i = 0; i < height; i++) { + SDL_CALL_STRETCHER; + dstPtr += dstPitch; + SDL_CALL_STRETCHER; + dstPtr += dstPitch; + SDL_CALL_STRETCHER; + srcPtr += srcPitch; + dstPtr += dstPitch; + } +} +void sdlStretch4x(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, u8 *dstPtr, u32 dstPitch, int width, int height) { + int i; +#ifndef C_CORE + u32 *stretcher = (u32 *)sdlStretcher; +#endif + for(i = 0; i < height; i++) { + SDL_CALL_STRETCHER; + dstPtr += dstPitch; + SDL_CALL_STRETCHER; + dstPtr += dstPitch; + SDL_CALL_STRETCHER; + dstPtr += dstPitch; + SDL_CALL_STRETCHER; + srcPtr += srcPitch; + dstPtr += dstPitch; + } +} + + diff --git a/src/sdl/filters.h b/src/sdl/filters.h new file mode 100644 index 0000000..9733e45 --- /dev/null +++ b/src/sdl/filters.h @@ -0,0 +1,61 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004-2008 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef VBA_SDL_FILTERS_H +#define VBA_SDL_FILTERS_H + +#include "../System.h" + + // + // Screen filters + // + +// List of available filters +enum Filter { kStretch1x, kStretch2x, k2xSaI, kSuper2xSaI, kSuperEagle, kPixelate, + kAdMame2x, kBilinear, kBilinearPlus, kScanlines, kScanlinesTV, + klq2x, khq2x, kStretch3x, khq3x, kStretch4x, khq4x, kInvalidFilter }; + +// Function pointer type for a filter function +typedef void(*FilterFunc)(u8*, u32, u8*, u8*, u32, int, int); + +// Initialize a filter and get the corresponding filter function pointer +FilterFunc initFilter(const Filter f, const int colorDepth, const int srcWidth); + +// Get the enlarge factor of a filter +int getFilterEnlargeFactor(const Filter f); + +// Get the display name for a filter +char* getFilterName(const Filter f); + + // + // Interframe filters + // + +// List of available IFB filters +enum IFBFilter { kIFBNone, kIBMotionBlur, kIBSmart, kInvalidIFBFilter }; + +// Function pointer type for an IFB filter function +typedef void(*IFBFilterFunc)(u8*, u32, int, int); + +// Initialize an IFB filter and get the corresponding filter function pointer +IFBFilterFunc initIFBFilter(const IFBFilter f, const int colorDepth); + +// Get the display name for an IFB filter +char* getIFBFilterName(const IFBFilter f); + +#endif // VBA_SDL_FILTERS_H diff --git a/src/sdl/getopt.c b/src/sdl/getopt.c new file mode 100644 index 0000000..3d3cfe9 --- /dev/null +++ b/src/sdl/getopt.c @@ -0,0 +1,1060 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to drepper@gnu.org + before changing it! + + Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98 + Free Software Foundation, Inc. + + NOTE: This source is derived from an old version taken from the GNU C + Library (glibc). + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. */ + +/* This tells Alpha OSF/1 not to define a getopt prototype in . + Ditto for AIX 3.2 and . */ +#ifndef _NO_PROTO +# define _NO_PROTO +#endif + +#ifdef HAVE_CONFIG_H +# include +#endif + +#if !defined __STDC__ || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +# ifndef const +# define const +# endif +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 +# include +# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +# define ELIDE_CODE +# endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +# include +# include +#endif /* GNU C library. */ + +#ifdef VMS +# include +# if HAVE_STRING_H - 0 +# include +# endif +#endif + +#ifndef _ +/* This is for other GNU distributions with internationalized messages. + When compiling libc, the _ macro is predefined. */ +# ifdef HAVE_LIBINTL_H +# include +# define _(msgid) gettext (msgid) +# else +# define _(msgid) (msgid) +# endif +#endif + +#ifdef _WIN32 +#include +#endif + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = NULL; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* 1003.2 says this must be 1 before any call. */ +int optind = 1; + +/* Formerly, initialization of getopt depended on optind==0, which + causes problems with re-calling getopt as programs generally don't + know that. */ + +int __getopt_initialized = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return -1 with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* Value of POSIXLY_CORRECT environment variable. */ +static char *posixly_correct; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +# include +# define my_index strchr +#else + +# if HAVE_STRING_H +# include +# else +# if HAVE_STRINGS_H +# include +# endif +# endif + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +#ifndef getenv +extern char *getenv (); +#endif + +static char * +my_index (str, chr) + const char *str; + int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +/* If using GCC, we can safely declare strlen this way. + If not using GCC, it is ok not to declare it. */ +#ifdef __GNUC__ +/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. + That was relevant to code that was here before. */ +# if (!defined __STDC__ || !__STDC__) && !defined strlen +/* gcc with -traditional declares the built-in strlen to return int, + and has done so at least since version 2.4.5. -- rms. */ +extern int strlen (const char *); +# endif /* not __STDC__ */ +#endif /* __GNUC__ */ + +#endif /* not __GNU_LIBRARY__ */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +#ifdef _LIBC +/* Bash 2.0 gives us an environment variable containing flags + indicating ARGV elements that should not be considered arguments. */ + +/* Defined in getopt_init.c */ +extern char *__getopt_nonoption_flags; + +static int nonoption_flags_max_len; +static int nonoption_flags_len; + +static int original_argc; +static char *const *original_argv; + +/* Make sure the environment variable bash 2.0 puts in the environment + is valid for the getopt call we must make sure that the ARGV passed + to getopt is that one passed to the process. */ +static void +__attribute__ ((unused)) +store_args_and_env (int argc, char *const *argv) +{ + /* XXX This is no good solution. We should rather copy the args so + that we can compare them later. But we must not use malloc(3). */ + original_argc = argc; + original_argv = argv; +} +# ifdef text_set_element +text_set_element (__libc_subinit, store_args_and_env); +# endif /* text_set_element */ + +# define SWAP_FLAGS(ch1, ch2) \ + if (nonoption_flags_len > 0) \ + { \ + char __tmp = __getopt_nonoption_flags[ch1]; \ + __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ + __getopt_nonoption_flags[ch2] = __tmp; \ + } +#else /* !_LIBC */ +# define SWAP_FLAGS(ch1, ch2) +#endif /* _LIBC */ + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +#if defined __STDC__ && __STDC__ +static void exchange (char **); +#endif + +static void +exchange (argv) + char **argv; +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + +#ifdef _LIBC + /* First make sure the handling of the `__getopt_nonoption_flags' + string can work normally. Our top argument must be in the range + of the string. */ + if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) + { + /* We must extend the array. The user plays games with us and + presents new arguments. */ + char *new_str = malloc (top + 1); + if (new_str == NULL) + nonoption_flags_len = nonoption_flags_max_len = 0; + else + { + memset (__mempcpy (new_str, __getopt_nonoption_flags, + nonoption_flags_max_len), + '\0', top + 1 - nonoption_flags_max_len); + nonoption_flags_max_len = top + 1; + __getopt_nonoption_flags = new_str; + } + } +#endif + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + SWAP_FLAGS (bottom + i, middle + i); + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Initialize the internal data when the first call is made. */ + +#if defined __STDC__ && __STDC__ +static const char *_getopt_initialize (int, char *const *, const char *); +#endif +static const char * +_getopt_initialize (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = optind; + + nextchar = NULL; + + posixly_correct = getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + +#ifdef _LIBC + if (posixly_correct == NULL + && argc == original_argc && argv == original_argv) + { + if (nonoption_flags_max_len == 0) + { + if (__getopt_nonoption_flags == NULL + || __getopt_nonoption_flags[0] == '\0') + nonoption_flags_max_len = -1; + else + { + const char *orig_str = __getopt_nonoption_flags; + int len = nonoption_flags_max_len = strlen (orig_str); + if (nonoption_flags_max_len < argc) + nonoption_flags_max_len = argc; + __getopt_nonoption_flags = + (char *) malloc (nonoption_flags_max_len); + if (__getopt_nonoption_flags == NULL) + nonoption_flags_max_len = -1; + else + memset (__mempcpy (__getopt_nonoption_flags, orig_str, len), + '\0', nonoption_flags_max_len - len); + } + } + nonoption_flags_len = nonoption_flags_max_len; + } + else + nonoption_flags_len = 0; +#endif + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns -1. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + optarg = NULL; + + if (optind == 0 || !__getopt_initialized) + { + if (optind == 0) + optind = 1; /* Don't scan ARGV[0], the program name. */ + optstring = _getopt_initialize (argc, argv, optstring); + __getopt_initialized = 1; + } + + /* Test whether ARGV[optind] points to a non-option argument. + Either it does not have option syntax, or there is an environment flag + from the shell indicating it is not an option. The later information + is only used when the used in the GNU libc. */ +#ifdef _LIBC +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ + || (optind < nonoption_flags_len \ + && __getopt_nonoption_flags[optind] == '1')) +#else +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') +#endif + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been + moved back by the user (who may also have changed the arguments). */ + if (last_nonopt > optind) + last_nonopt = optind; + if (first_nonopt > optind) + first_nonopt = optind; + + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc && NONOPTION_P) + optind++; + last_nonopt = optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return -1; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if (NONOPTION_P) + { + if (ordering == REQUIRE_ORDER) + return -1; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[optind][1] == '-' + || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) + == (unsigned int) strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + optopt = 0; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + _("%s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + _("%s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[optind - 1][0], pfound->name); + + nextchar += strlen (nextchar); + + optopt = pfound->val; + return '?'; + } + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, _("%s: unrecognized option `--%s'\n"), + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + optopt = 0; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { + if (posixly_correct) + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: illegal option -- %c\n"), + argv[0], c); + else + fprintf (stderr, _("%s: invalid option -- %c\n"), + argv[0], c); + } + optopt = c; + return '?'; + } + /* Convenience. Treat POSIX -W foo same as long option --foo */ + if (temp[0] == 'W' && temp[1] == ';') + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; + + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + + /* optarg is now the argument, see if it's in the + table of longopts. */ + + for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + if (pfound != NULL) + { + option_index = indfound; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + fprintf (stderr, _("\ +%s: option `-W %s' doesn't allow an argument\n"), + argv[0], pfound->name); + + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + nextchar = NULL; + return 'W'; /* Let the application handle it. */ + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, + _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == -1) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/src/sdl/getopt.h b/src/sdl/getopt.h new file mode 100644 index 0000000..cb5feba --- /dev/null +++ b/src/sdl/getopt.h @@ -0,0 +1,141 @@ +/* Declarations for getopt. + Copyright 1989, 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 2000 + Free Software Foundation, Inc. + + NOTE: The canonical source of this file is maintained with the GNU C Library. + Bugs can be reported to bug-glibc@gnu.org. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. */ + +#ifndef _GETOPT_H +#define _GETOPT_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ +#if defined (__STDC__) && __STDC__ + const char *name; +#else + char *name; +#endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +#if defined (__STDC__) && __STDC__ +/* HAVE_DECL_* is a three-state macro: undefined, 0 or 1. If it is + undefined, we haven't run the autoconf check so provide the + declaration without arguments. If it is 0, we checked and failed + to find the declaration so provide a fully prototyped one. If it + is 1, we found it so don't provide any declaration at all. */ +#if defined (__GNU_LIBRARY__) || (defined (HAVE_DECL_GETOPT) && !HAVE_DECL_GETOPT) +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ +extern int getopt (int argc, char *const *argv, const char *shortopts); +#else /* not __GNU_LIBRARY__ */ +# if !defined (HAVE_DECL_GETOPT) +extern int getopt (); +# endif +#endif /* __GNU_LIBRARY__ */ +extern int getopt_long (int argc, char *const *argv, const char *shortopts, + const struct option *longopts, int *longind); +extern int getopt_long_only (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind); + +/* Internal only. Users should not call this directly. */ +extern int _getopt_internal (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind, + int long_only); +#else /* not __STDC__ */ +extern int getopt (); +extern int getopt_long (); +extern int getopt_long_only (); + +extern int _getopt_internal (); +#endif /* __STDC__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* getopt.h */ diff --git a/src/sdl/getopt1.c b/src/sdl/getopt1.c new file mode 100644 index 0000000..a3637c2 --- /dev/null +++ b/src/sdl/getopt1.c @@ -0,0 +1,190 @@ +/* getopt_long and getopt_long_only entry points for GNU getopt. + Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98 + Free Software Foundation, Inc. + + NOTE: This source is derived from an old version taken from the GNU C + Library (glibc). + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "getopt.h" + +#if !defined __STDC__ || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 +#include +#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +#define ELIDE_CODE +#endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +#include +#endif + +#ifndef NULL +#define NULL 0 +#endif + +int +getopt_long (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +/* Like getopt_long, but '-' as well as '--' can indicate a long option. + If an option that starts with '-' (not '--') doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ + +int +getopt_long_only (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 1); +} + + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +#include + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = + { + {"add", 1, 0, 0}, + {"append", 0, 0, 0}, + {"delete", 1, 0, 0}, + {"verbose", 0, 0, 0}, + {"create", 0, 0, 0}, + {"file", 1, 0, 0}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, "abc:d:0123456789", + long_options, &option_index); + if (c == -1) + break; + + switch (c) + { + case 0: + printf ("option %s", long_options[option_index].name); + if (optarg) + printf (" with arg %s", optarg); + printf ("\n"); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case 'd': + printf ("option d with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/src/sdl/inputSDL.cpp b/src/sdl/inputSDL.cpp new file mode 100644 index 0000000..489fc9d --- /dev/null +++ b/src/sdl/inputSDL.cpp @@ -0,0 +1,621 @@ +// VBA-M, A Nintendo Handheld Console Emulator +// Copyright (C) 2008 VBA-M development team +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "inputSDL.h" + +#define SDLBUTTONS_NUM 14 + +static void sdlUpdateKey(uint32_t key, bool down); +static void sdlUpdateJoyButton(int which, int button, bool pressed); +static void sdlUpdateJoyHat(int which, int hat, int value); +static void sdlUpdateJoyAxis(int which, int axis, int value); +static bool sdlCheckJoyKey(int key); + +static bool sdlButtons[4][SDLBUTTONS_NUM] = { + { false, false, false, false, false, false, + false, false, false, false, false, false, + false, false + }, + { false, false, false, false, false, false, + false, false, false, false, false, false, + false, false + }, + { false, false, false, false, false, false, + false, false, false, false, false, false, + false, false + }, + { false, false, false, false, false, false, + false, false, false, false, false, false, + false, false + } +}; + +static bool sdlMotionButtons[4] = { false, false, false, false }; + +static int sdlNumDevices = 0; +static SDL_Joystick **sdlDevices = NULL; + +static EPad sdlDefaultJoypad = PAD_MAIN; + +static int autoFire = 0; +static bool autoFireToggle = false; +static int autoFireCountdown = 0; +int autoFireMaxCount = 1; + +static uint32_t joypad[5][SDLBUTTONS_NUM] = { + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { SDLK_LEFT, SDLK_RIGHT, + SDLK_UP, SDLK_DOWN, + SDLK_z, SDLK_x, + SDLK_RETURN,SDLK_BACKSPACE, + SDLK_a, SDLK_s, + SDLK_SPACE, SDLK_F12, + SDLK_q, SDLK_w, + } +}; + +static uint32_t motion[4] = { + SDLK_KP4, SDLK_KP6, SDLK_KP8, SDLK_KP2 +}; + +static uint32_t defaultMotion[4] = { + SDLK_KP4, SDLK_KP6, SDLK_KP8, SDLK_KP2 +}; + +static int sensorX = 2047; +static int sensorY = 2047; + +static uint32_t sdlGetHatCode(const SDL_Event &event) +{ + if (!event.jhat.value) return 0; + + return ( + ((event.jhat.which + 1) << 16) | + 32 | + (event.jhat.hat << 2) | + ( + event.jhat.value & SDL_HAT_UP ? 0 : + event.jhat.value & SDL_HAT_DOWN ? 1 : + event.jhat.value & SDL_HAT_RIGHT ? 2 : + event.jhat.value & SDL_HAT_LEFT ? 3 : 0 + ) + ); +} + +static uint32_t sdlGetButtonCode(const SDL_Event &event) +{ + return ( + ((event.jbutton.which + 1) << 16) | + (event.jbutton.button + 0x80) + ); +} + +static uint32_t sdlGetAxisCode(const SDL_Event &event) +{ + if (event.jaxis.value >= -16384 && event.jaxis.value <= 16384) return 0; + + return ( + ((event.jaxis.which + 1) << 16) | + (event.jaxis.axis << 1) | + ( + event.jaxis.value > 16384 ? 1 : + event.jaxis.value < -16384 ? 0 : 0 + ) + ); +} + +uint32_t inputGetEventCode(const SDL_Event &event) +{ + switch(event.type) + { + case SDL_KEYDOWN: + case SDL_KEYUP: + return event.key.keysym.sym; + break; + case SDL_JOYHATMOTION: + return sdlGetHatCode(event); + break; + case SDL_JOYBUTTONDOWN: + case SDL_JOYBUTTONUP: + return sdlGetButtonCode(event); + break; + case SDL_JOYAXISMOTION: + return sdlGetAxisCode(event); + break; + default: + return 0; + break; + } +} + +uint32_t inputGetKeymap(EPad pad, EKey key) +{ + return joypad[pad][key]; +} + +void inputSetKeymap(EPad pad, EKey key, uint32_t code) +{ + joypad[pad][key] = code; +} + +void inputSetMotionKeymap(EKey key, uint32_t code) +{ + motion[key] = code; +} + +bool inputGetAutoFire(EKey key) +{ + int mask = 0; + + switch (key) + { + case KEY_BUTTON_A: + mask = 1 << 0; + break; + case KEY_BUTTON_B: + mask = 1 << 1; + break; + case KEY_BUTTON_R: + mask = 1 << 8; + break; + case KEY_BUTTON_L: + mask = 1 << 9; + break; + default: + break; + } + + return !(autoFire & mask); +} + +bool inputToggleAutoFire(EKey key) +{ + int mask = 0; + + switch (key) + { + case KEY_BUTTON_A: + mask = 1 << 0; + break; + case KEY_BUTTON_B: + mask = 1 << 1; + break; + case KEY_BUTTON_R: + mask = 1 << 8; + break; + case KEY_BUTTON_L: + mask = 1 << 9; + break; + default: + break; + } + + if(autoFire & mask) + { + autoFire &= ~mask; + return false; + } + else + { + autoFire |= mask; + return true; + } +} + +static void sdlUpdateKey(uint32_t key, bool down) +{ + int i; + for(int j = 0; j < 4; j++) { + for(i = 0 ; i < SDLBUTTONS_NUM; i++) { +// if((joypad[j][i] & 0xffff0000) == 0) { + if(key == joypad[j][i]) + sdlButtons[j][i] = down; +// } + } + } + for(i = 0 ; i < 4; i++) { + if((motion[i] & 0xffff0000) == 0) { + if(key == motion[i]) + sdlMotionButtons[i] = down; + } + } +} + +static void sdlUpdateJoyButton(int which, + int button, + bool pressed) +{ + int i; + for(int j = 0; j < 4; j++) { + for(i = 0; i < SDLBUTTONS_NUM; i++) { + int dev = (joypad[j][i] >> 16); + int b = joypad[j][i] & 0xffff; + if(dev) { + dev--; + + if((dev == which) && (b >= 128) && (b == (button+128))) { + sdlButtons[j][i] = pressed; + } + } + } + } + for(i = 0; i < 4; i++) { + int dev = (motion[i] >> 16); + int b = motion[i] & 0xffff; + if(dev) { + dev--; + + if((dev == which) && (b >= 128) && (b == (button+128))) { + sdlMotionButtons[i] = pressed; + } + } + } +} + +static void sdlUpdateJoyHat(int which, + int hat, + int value) +{ + int i; + for(int j = 0; j < 4; j++) { + for(i = 0; i < SDLBUTTONS_NUM; i++) { + int dev = (joypad[j][i] >> 16); + int a = joypad[j][i] & 0xffff; + if(dev) { + dev--; + + if((dev == which) && (a>=32) && (a < 48) && (((a&15)>>2) == hat)) { + int dir = a & 3; + int v = 0; + switch(dir) { + case 0: + v = value & SDL_HAT_UP; + break; + case 1: + v = value & SDL_HAT_DOWN; + break; + case 2: + v = value & SDL_HAT_RIGHT; + break; + case 3: + v = value & SDL_HAT_LEFT; + break; + } + sdlButtons[j][i] = (v ? true : false); + } + } + } + } + for(i = 0; i < 4; i++) { + int dev = (motion[i] >> 16); + int a = motion[i] & 0xffff; + if(dev) { + dev--; + + if((dev == which) && (a>=32) && (a < 48) && (((a&15)>>2) == hat)) { + int dir = a & 3; + int v = 0; + switch(dir) { + case 0: + v = value & SDL_HAT_UP; + break; + case 1: + v = value & SDL_HAT_DOWN; + break; + case 2: + v = value & SDL_HAT_RIGHT; + break; + case 3: + v = value & SDL_HAT_LEFT; + break; + } + sdlMotionButtons[i] = (v ? true : false); + } + } + } +} + +static void sdlUpdateJoyAxis(int which, + int axis, + int value) +{ + int i; + for(int j = 0; j < 4; j++) { + for(i = 0; i < SDLBUTTONS_NUM; i++) { + int dev = (joypad[j][i] >> 16); + int a = joypad[j][i] & 0xffff; + if(dev) { + dev--; + + if((dev == which) && (a < 32) && ((a>>1) == axis)) { + sdlButtons[j][i] = (a & 1) ? (value > 16384) : (value < -16384); + } + } + } + } + for(i = 0; i < 4; i++) { + int dev = (motion[i] >> 16); + int a = motion[i] & 0xffff; + if(dev) { + dev--; + + if((dev == which) && (a < 32) && ((a>>1) == axis)) { + sdlMotionButtons[i] = (a & 1) ? (value > 16384) : (value < -16384); + } + } + } +} + +static bool sdlCheckJoyKey(int key) +{ + int dev = (key >> 16) - 1; + int what = key & 0xffff; + + if(what >= 128) { + // joystick button + int button = what - 128; + + if(button >= SDL_JoystickNumButtons(sdlDevices[dev])) + return false; + } else if (what < 0x20) { + // joystick axis + what >>= 1; + if(what >= SDL_JoystickNumAxes(sdlDevices[dev])) + return false; + } else if (what < 0x30) { + // joystick hat + what = (what & 15); + what >>= 2; + if(what >= SDL_JoystickNumHats(sdlDevices[dev])) + return false; + } + + // no problem found + return true; +} + +void inputInitJoysticks() +{ + // The main joypad has to be entirely defined + for(int i = 0; i < SDLBUTTONS_NUM; i++) { + if (!joypad[PAD_MAIN][i]) + joypad[PAD_MAIN][i] = joypad[PAD_DEFAULT][i]; + } + + sdlNumDevices = SDL_NumJoysticks(); + + if(sdlNumDevices) + sdlDevices = (SDL_Joystick **)calloc(1,sdlNumDevices * + sizeof(SDL_Joystick **)); + bool usesJoy = false; + + for(int j = 0; j < 4; j++) { + for(int i = 0; i < SDLBUTTONS_NUM; i++) { + int dev = joypad[j][i] >> 16; + if(dev) { + dev--; + bool ok = false; + + if(sdlDevices) { + if(dev < sdlNumDevices) { + if(sdlDevices[dev] == NULL) { + sdlDevices[dev] = SDL_JoystickOpen(dev); + } + + ok = sdlCheckJoyKey(joypad[j][i]); + } else + ok = false; + } + + if(!ok) + joypad[j][i] = joypad[PAD_DEFAULT][i]; + else + usesJoy = true; + } + } + } + + for(int i = 0; i < 4; i++) { + int dev = motion[i] >> 16; + if(dev) { + dev--; + bool ok = false; + + if(sdlDevices) { + if(dev < sdlNumDevices) { + if(sdlDevices[dev] == NULL) { + sdlDevices[dev] = SDL_JoystickOpen(dev); + } + + ok = sdlCheckJoyKey(motion[i]); + } else + ok = false; + } + + if(!ok) + motion[i] = defaultMotion[i]; + else + usesJoy = true; + } + } + + if(usesJoy) + SDL_JoystickEventState(SDL_ENABLE); +} + +void inputProcessSDLEvent(const SDL_Event &event) +{ +// fprintf(stdout, "%x\n", inputGetEventCode(event)); + + switch(event.type) + { + case SDL_KEYDOWN: + sdlUpdateKey(event.key.keysym.sym, true); + break; + case SDL_KEYUP: + sdlUpdateKey(event.key.keysym.sym, false); + break; + case SDL_JOYHATMOTION: + sdlUpdateJoyHat(event.jhat.which, + event.jhat.hat, + event.jhat.value); + break; + case SDL_JOYBUTTONDOWN: + case SDL_JOYBUTTONUP: + sdlUpdateJoyButton(event.jbutton.which, + event.jbutton.button, + event.jbutton.state == SDL_PRESSED); + break; + case SDL_JOYAXISMOTION: + sdlUpdateJoyAxis(event.jaxis.which, + event.jaxis.axis, + event.jaxis.value); + break; + } +} + +uint32_t inputReadJoypad(int which) +{ + int realAutoFire = autoFire; + + if(which < 0 || which > 3) + which = sdlDefaultJoypad; + + uint32_t res = 0; + + if(sdlButtons[which][KEY_BUTTON_A]) + res |= 1; + if(sdlButtons[which][KEY_BUTTON_B]) + res |= 2; + if(sdlButtons[which][KEY_BUTTON_SELECT]) + res |= 4; + if(sdlButtons[which][KEY_BUTTON_START]) + res |= 8; + if(sdlButtons[which][KEY_RIGHT]) + res |= 16; + if(sdlButtons[which][KEY_LEFT]) + res |= 32; + if(sdlButtons[which][KEY_UP]) + res |= 64; + if(sdlButtons[which][KEY_DOWN]) + res |= 128; + if(sdlButtons[which][KEY_BUTTON_R]) + res |= 256; + if(sdlButtons[which][KEY_BUTTON_L]) + res |= 512; + if(sdlButtons[which][KEY_BUTTON_AUTO_A]) + realAutoFire ^= 1; + if(sdlButtons[which][KEY_BUTTON_AUTO_B]) + realAutoFire ^= 2; + + // disallow L+R or U+D of being pressed at the same time + if((res & 48) == 48) + res &= ~16; + if((res & 192) == 192) + res &= ~128; + + if(sdlButtons[which][KEY_BUTTON_SPEED]) + res |= 1024; + if(sdlButtons[which][KEY_BUTTON_CAPTURE]) + res |= 2048; + + if(realAutoFire) { + res &= (~realAutoFire); + if(autoFireToggle) + res |= realAutoFire; + autoFireCountdown--; // this needs decrementing even when autoFireToggle is toggled, + // so that autoFireMaxCount==1 (the default) will alternate at the maximum possible + // frequency (every time this code is reached). Which is what it did before + // introducing autoFireCountdown. + if (autoFireCountdown <= 0) { + autoFireToggle = !autoFireToggle; + autoFireCountdown = autoFireMaxCount; + } + } + + return res; +} + +void inputUpdateMotionSensor() +{ + if(sdlMotionButtons[KEY_LEFT]) { + sensorX += 3; + if(sensorX > 2197) + sensorX = 2197; + if(sensorX < 2047) + sensorX = 2057; + } else if(sdlMotionButtons[KEY_RIGHT]) { + sensorX -= 3; + if(sensorX < 1897) + sensorX = 1897; + if(sensorX > 2047) + sensorX = 2037; + } else if(sensorX > 2047) { + sensorX -= 2; + if(sensorX < 2047) + sensorX = 2047; + } else { + sensorX += 2; + if(sensorX > 2047) + sensorX = 2047; + } + + if(sdlMotionButtons[KEY_UP]) { + sensorY += 3; + if(sensorY > 2197) + sensorY = 2197; + if(sensorY < 2047) + sensorY = 2057; + } else if(sdlMotionButtons[KEY_DOWN]) { + sensorY -= 3; + if(sensorY < 1897) + sensorY = 1897; + if(sensorY > 2047) + sensorY = 2037; + } else if(sensorY > 2047) { + sensorY -= 2; + if(sensorY < 2047) + sensorY = 2047; + } else { + sensorY += 2; + if(sensorY > 2047) + sensorY = 2047; + } +} + +int inputGetSensorX() +{ + return sensorX; +} + +int inputGetSensorY() +{ + return sensorY; +} + +void inputSetDefaultJoypad(EPad pad) +{ + sdlDefaultJoypad = pad; +} + +EPad inputGetDefaultJoypad() +{ + return sdlDefaultJoypad; +} diff --git a/src/sdl/inputSDL.h b/src/sdl/inputSDL.h new file mode 100644 index 0000000..cf93072 --- /dev/null +++ b/src/sdl/inputSDL.h @@ -0,0 +1,141 @@ +// VBA-M, A Nintendo Handheld Console Emulator +// Copyright (C) 2008 VBA-M development team +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef VBAM_SDL_INPUT_H +#define VBAM_SDL_INPUT_H + +#include + +enum EKey { + KEY_LEFT, + KEY_RIGHT, + KEY_UP, + KEY_DOWN, + KEY_BUTTON_A, + KEY_BUTTON_B, + KEY_BUTTON_START, + KEY_BUTTON_SELECT, + KEY_BUTTON_L, + KEY_BUTTON_R, + KEY_BUTTON_SPEED, + KEY_BUTTON_CAPTURE, + KEY_BUTTON_AUTO_A, + KEY_BUTTON_AUTO_B +}; + +enum EPad { + PAD_MAIN, + PAD_1 = PAD_MAIN, + PAD_2, + PAD_3, + PAD_4, + PAD_DEFAULT +}; + +/** + * Init the joysticks needed by the keymap. Verify that the keymap is compatible + * with the joysticks. If it's not the case, revert to the default keymap. + */ +void inputInitJoysticks(); + +/** + * Define which key controls an emulated joypad button + * @param pad Emulated joypad index (there may be up to 4 joypads for the SGB) + * @param key Emulated joypad button + * @param code Code defining an actual joypad / keyboard button + */ +void inputSetKeymap(EPad pad, EKey key, uint32_t code); + +/** + * Get which key is associated to which emulated joypad button + * @param pad Emulated joypad index (there may be up to 4 joypads for the SGB) + * @param key Emulated joypad button + * @retunr Code defining an actual joypad / keyboard button + */ +uint32_t inputGetKeymap(EPad pad, EKey key); + +/** + * Define which keys control motion detection emulation + * @param key Emulated joypad button + * @param code Code defining an actual joypad / keyboard button + */ +void inputSetMotionKeymap(EKey key, uint32_t code); + +/** + * Toggle Auto fire for the specified button. Only A, B, R, L are supported. + * @param key Emulated joypad button + * @return Auto fire enabled + */ +bool inputToggleAutoFire(EKey key); + +/** + * Get Auto fire status for the specified button. Only A, B, R, L are supported. + * @param key Emulated joypad button + * @return Auto fire enabled + */ +bool inputGetAutoFire(EKey key); + +/** + * Update the emulated pads state with a SDL event + * @param SDL_Event An event that has just occured + */ +void inputProcessSDLEvent(const SDL_Event &event); + +/** + * Get the keymap code corresponding to a SDL event + * @param SDL_Event An event that has just occured + * @return Keymap code + */ +uint32_t inputGetEventCode(const SDL_Event &event); + +/** + * Read the state of an emulated joypad + * @param which Emulated joypad index + * @return Joypad state + */ +uint32_t inputReadJoypad(int which); + +/** + * Compute the motion sensor X and Y values + */ +void inputUpdateMotionSensor(); + +/** + * Get the motion sensor X value + * @return motion sensor X value + */ +int inputGetSensorX(); + +/** + * Get the motion sensor Y value + * @return motion sensor Y value + */ +int inputGetSensorY(); + +/** + * Set which joypad configuration use when the core doesn't ask for a specific + * @param pad Default pad + */ +void inputSetDefaultJoypad(EPad pad); + +/** + * Get the default joypad + * pad + */ +EPad inputGetDefaultJoypad(); + +#endif // VBAM_SDL_INPUT_H diff --git a/src/sdl/text.cpp b/src/sdl/text.cpp new file mode 100644 index 0000000..66368f1 --- /dev/null +++ b/src/sdl/text.cpp @@ -0,0 +1,148 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Code originally from fceu/drawing.h file, adapted by Forgotten + */ +#include "../System.h" + +extern int RGB_LOW_BITS_MASK; + +static const u8 fontdata2[2048] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7e,0x81,0xa5,0x81,0xbd,0x99,0x81,0x7e,0x7e,0xff,0xdb,0xff,0xc3,0xe7,0xff,0x7e,0x36,0x7f,0x7f,0x7f,0x3e,0x1c,0x08,0x00,0x08,0x1c,0x3e,0x7f,0x3e,0x1c,0x08,0x00,0x1c,0x3e,0x1c,0x7f,0x7f,0x3e,0x1c,0x3e,0x08,0x08,0x1c,0x3e,0x7f,0x3e,0x1c,0x3e,0x00,0x00,0x18,0x3c,0x3c,0x18,0x00,0x00,0xff,0xff,0xe7,0xc3,0xc3,0xe7,0xff,0xff,0x00,0x3c,0x66,0x42,0x42,0x66,0x3c,0x00,0xff,0xc3,0x99,0xbd,0xbd,0x99,0xc3,0xff,0xf0,0xe0,0xf0,0xbe,0x33,0x33,0x33,0x1e,0x3c,0x66,0x66,0x66,0x3c,0x18,0x7e,0x18,0xfc,0xcc,0xfc,0x0c,0x0c,0x0e,0x0f,0x07,0xfe,0xc6,0xfe,0xc6,0xc6,0xe6,0x67,0x03,0x99,0x5a,0x3c,0xe7,0xe7,0x3c,0x5a,0x99,0x01,0x07,0x1f,0x7f,0x1f,0x07,0x01,0x00,0x40,0x70,0x7c,0x7f,0x7c,0x70,0x40,0x00,0x18,0x3c,0x7e,0x18,0x18,0x7e,0x3c,0x18,0x66,0x66,0x66,0x66,0x66,0x00,0x66,0x00,0xfe,0xdb,0xdb,0xde,0xd8,0xd8,0xd8,0x00,0x7c,0xc6,0x1c,0x36,0x36,0x1c,0x33,0x1e,0x00,0x00,0x00,0x00,0x7e,0x7e,0x7e,0x00,0x18,0x3c,0x7e,0x18,0x7e,0x3c,0x18,0xff,0x18,0x3c,0x7e,0x18,0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x18,0x7e,0x3c,0x18,0x00,0x00,0x18,0x30,0x7f,0x30,0x18,0x00,0x00,0x00,0x0c,0x06,0x7f,0x06,0x0c,0x00,0x00,0x00,0x00,0x03,0x03,0x03,0x7f,0x00,0x00,0x00,0x24,0x66,0xff,0x66,0x24,0x00,0x00,0x00,0x18,0x3c,0x7e,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0x7e,0x3c,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x1e,0x1e,0x0c,0x0c,0x00,0x0c,0x00,0x36,0x36,0x36,0x00,0x00,0x00,0x00,0x00,0x36,0x36,0x7f,0x36,0x7f,0x36,0x36,0x00,0x0c,0x3e,0x03,0x1e,0x30,0x1f,0x0c,0x00,0x00,0x63,0x33,0x18,0x0c,0x66,0x63,0x00,0x1c,0x36,0x1c,0x6e,0x3b,0x33,0x6e,0x00,0x06,0x06,0x03,0x00,0x00,0x00,0x00,0x00,0x18,0x0c,0x06,0x06,0x06,0x0c,0x18,0x00,0x06,0x0c,0x18,0x18,0x18,0x0c,0x06,0x00,0x00,0x66,0x3c,0xff,0x3c,0x66,0x00,0x00,0x00,0x0c,0x0c,0x3f,0x0c,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x0c,0x06,0x00,0x00,0x00,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x0c,0x00,0x60,0x30,0x18,0x0c,0x06,0x03,0x01,0x00,0x3e,0x63,0x73,0x7b,0x6f,0x67,0x3e,0x00,0x0c,0x0e,0x0c,0x0c,0x0c,0x0c,0x3f,0x00,0x1e,0x33,0x30,0x1c,0x06,0x33,0x3f,0x00,0x1e,0x33,0x30,0x1c,0x30,0x33,0x1e,0x00,0x38,0x3c,0x36,0x33,0x7f,0x30,0x78,0x00,0x3f,0x03,0x1f,0x30,0x30,0x33,0x1e,0x00,0x1c,0x06,0x03,0x1f,0x33,0x33,0x1e,0x00,0x3f,0x33,0x30,0x18,0x0c,0x0c,0x0c,0x00,0x1e,0x33,0x33,0x1e,0x33,0x33,0x1e,0x00,0x1e,0x33,0x33,0x3e,0x30,0x18,0x0e,0x00,0x00,0x0c,0x0c,0x00,0x00,0x0c,0x0c,0x00,0x00,0x0c,0x0c,0x00,0x00,0x0c,0x0c,0x06,0x18,0x0c,0x06,0x03,0x06,0x0c,0x18,0x00,0x00,0x00,0x3f,0x00,0x00,0x3f,0x00,0x00,0x06,0x0c,0x18,0x30,0x18,0x0c,0x06,0x00,0x1e,0x33,0x30,0x18,0x0c,0x00,0x0c,0x00, + 0x3e,0x63,0x7b,0x7b,0x7b,0x03,0x1e,0x00,0x0c,0x1e,0x33,0x33,0x3f,0x33,0x33,0x00,0x3f,0x66,0x66,0x3e,0x66,0x66,0x3f,0x00,0x3c,0x66,0x03,0x03,0x03,0x66,0x3c,0x00,0x1f,0x36,0x66,0x66,0x66,0x36,0x1f,0x00,0x7f,0x46,0x16,0x1e,0x16,0x46,0x7f,0x00,0x7f,0x46,0x16,0x1e,0x16,0x06,0x0f,0x00,0x3c,0x66,0x03,0x03,0x73,0x66,0x7c,0x00,0x33,0x33,0x33,0x3f,0x33,0x33,0x33,0x00,0x1e,0x0c,0x0c,0x0c,0x0c,0x0c,0x1e,0x00,0x78,0x30,0x30,0x30,0x33,0x33,0x1e,0x00,0x67,0x66,0x36,0x1e,0x36,0x66,0x67,0x00,0x0f,0x06,0x06,0x06,0x46,0x66,0x7f,0x00,0x63,0x77,0x7f,0x7f,0x6b,0x63,0x63,0x00,0x63,0x67,0x6f,0x7b,0x73,0x63,0x63,0x00,0x1c,0x36,0x63,0x63,0x63,0x36,0x1c,0x00,0x3f,0x66,0x66,0x3e,0x06,0x06,0x0f,0x00,0x1e,0x33,0x33,0x33,0x3b,0x1e,0x38,0x00,0x3f,0x66,0x66,0x3e,0x36,0x66,0x67,0x00,0x1e,0x33,0x07,0x0e,0x38,0x33,0x1e,0x00,0x3f,0x2d,0x0c,0x0c,0x0c,0x0c,0x1e,0x00,0x33,0x33,0x33,0x33,0x33,0x33,0x3f,0x00,0x33,0x33,0x33,0x33,0x33,0x1e,0x0c,0x00,0x63,0x63,0x63,0x6b,0x7f,0x77,0x63,0x00,0x63,0x63,0x36,0x1c,0x1c,0x36,0x63,0x00,0x33,0x33,0x33,0x1e,0x0c,0x0c,0x1e,0x00,0x7f,0x63,0x31,0x18,0x4c,0x66,0x7f,0x00,0x1e,0x06,0x06,0x06,0x06,0x06,0x1e,0x00,0x03,0x06,0x0c,0x18,0x30,0x60,0x40,0x00,0x1e,0x18,0x18,0x18,0x18,0x18,0x1e,0x00,0x08,0x1c,0x36,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff, + 0x0c,0x0c,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1e,0x30,0x3e,0x33,0x6e,0x00,0x07,0x06,0x06,0x3e,0x66,0x66,0x3b,0x00,0x00,0x00,0x1e,0x33,0x03,0x33,0x1e,0x00,0x38,0x30,0x30,0x3e,0x33,0x33,0x6e,0x00,0x00,0x00,0x1e,0x33,0x3f,0x03,0x1e,0x00,0x1c,0x36,0x06,0x0f,0x06,0x06,0x0f,0x00,0x00,0x00,0x6e,0x33,0x33,0x3e,0x30,0x1f,0x07,0x06,0x36,0x6e,0x66,0x66,0x67,0x00,0x0c,0x00,0x0e,0x0c,0x0c,0x0c,0x1e,0x00,0x30,0x00,0x30,0x30,0x30,0x33,0x33,0x1e,0x07,0x06,0x66,0x36,0x1e,0x36,0x67,0x00,0x0e,0x0c,0x0c,0x0c,0x0c,0x0c,0x1e,0x00,0x00,0x00,0x33,0x7f,0x7f,0x6b,0x63,0x00,0x00,0x00,0x1f,0x33,0x33,0x33,0x33,0x00,0x00,0x00,0x1e,0x33,0x33,0x33,0x1e,0x00,0x00,0x00,0x3b,0x66,0x66,0x3e,0x06,0x0f,0x00,0x00,0x6e,0x33,0x33,0x3e,0x30,0x78,0x00,0x00,0x3b,0x6e,0x66,0x06,0x0f,0x00,0x00,0x00,0x3e,0x03,0x1e,0x30,0x1f,0x00,0x08,0x0c,0x3e,0x0c,0x0c,0x2c,0x18,0x00,0x00,0x00,0x33,0x33,0x33,0x33,0x6e,0x00,0x00,0x00,0x33,0x33,0x33,0x1e,0x0c,0x00,0x00,0x00,0x63,0x6b,0x7f,0x7f,0x36,0x00,0x00,0x00,0x63,0x36,0x1c,0x36,0x63,0x00,0x00,0x00,0x33,0x33,0x33,0x3e,0x30,0x1f,0x00,0x00,0x3f,0x19,0x0c,0x26,0x3f,0x00,0x38,0x0c,0x0c,0x07,0x0c,0x0c,0x38,0x00,0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x00,0x07,0x0c,0x0c,0x38,0x0c,0x0c,0x07,0x00,0x6e,0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x1c,0x36,0x63,0x63,0x7f,0x00, + 0x1e,0x33,0x03,0x33,0x1e,0x18,0x30,0x1e,0x00,0x33,0x00,0x33,0x33,0x33,0x7e,0x00,0x38,0x00,0x1e,0x33,0x3f,0x03,0x1e,0x00,0x7e,0xc3,0x3c,0x60,0x7c,0x66,0xfc,0x00,0x33,0x00,0x1e,0x30,0x3e,0x33,0x7e,0x00,0x07,0x00,0x1e,0x30,0x3e,0x33,0x7e,0x00,0x0c,0x0c,0x1e,0x30,0x3e,0x33,0x7e,0x00,0x00,0x00,0x1e,0x03,0x03,0x1e,0x30,0x1c,0x7e,0xc3,0x3c,0x66,0x7e,0x06,0x3c,0x00,0x33,0x00,0x1e,0x33,0x3f,0x03,0x1e,0x00,0x07,0x00,0x1e,0x33,0x3f,0x03,0x1e,0x00,0x33,0x00,0x0e,0x0c,0x0c,0x0c,0x1e,0x00,0x3e,0x63,0x1c,0x18,0x18,0x18,0x3c,0x00,0x07,0x00,0x0e,0x0c,0x0c,0x0c,0x1e,0x00,0x63,0x1c,0x36,0x63,0x7f,0x63,0x63,0x00,0x0c,0x0c,0x00,0x1e,0x33,0x3f,0x33,0x00,0x38,0x00,0x3f,0x06,0x1e,0x06,0x3f,0x00,0x00,0x00,0xfe,0x30,0xfe,0x33,0xfe,0x00,0x7c,0x36,0x33,0x7f,0x33,0x33,0x73,0x00,0x1e,0x33,0x00,0x1e,0x33,0x33,0x1e,0x00,0x00,0x33,0x00,0x1e,0x33,0x33,0x1e,0x00,0x00,0x07,0x00,0x1e,0x33,0x33,0x1e,0x00,0x1e,0x33,0x00,0x33,0x33,0x33,0x7e,0x00,0x00,0x07,0x00,0x33,0x33,0x33,0x7e,0x00,0x00,0x33,0x00,0x33,0x33,0x3e,0x30,0x1f,0xc3,0x18,0x3c,0x66,0x66,0x3c,0x18,0x00,0x33,0x00,0x33,0x33,0x33,0x33,0x1e,0x00,0x18,0x18,0x7e,0x03,0x03,0x7e,0x18,0x18,0x1c,0x36,0x26,0x0f,0x06,0x67,0x3f,0x00,0x33,0x33,0x1e,0x3f,0x0c,0x3f,0x0c,0x0c,0x1f,0x33,0x33,0x5f,0x63,0xf3,0x63,0xe3,0x70,0xd8,0x18,0x3c,0x18,0x18,0x1b,0x0e, + 0x38,0x00,0x1e,0x30,0x3e,0x33,0x7e,0x00,0x1c,0x00,0x0e,0x0c,0x0c,0x0c,0x1e,0x00,0x00,0x38,0x00,0x1e,0x33,0x33,0x1e,0x00,0x00,0x38,0x00,0x33,0x33,0x33,0x7e,0x00,0x00,0x1f,0x00,0x1f,0x33,0x33,0x33,0x00,0x3f,0x00,0x33,0x37,0x3f,0x3b,0x33,0x00,0x3c,0x36,0x36,0x7c,0x00,0x7e,0x00,0x00,0x1c,0x36,0x36,0x1c,0x00,0x3e,0x00,0x00,0x0c,0x00,0x0c,0x06,0x03,0x33,0x1e,0x00,0x00,0x00,0x00,0x3f,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x3f,0x30,0x30,0x00,0x00,0xc3,0x63,0x33,0x7b,0xcc,0x66,0x33,0xf0,0xc3,0x63,0x33,0xdb,0xec,0xf6,0xf3,0xc0,0x18,0x18,0x00,0x18,0x18,0x18,0x18,0x00,0x00,0xcc,0x66,0x33,0x66,0xcc,0x00,0x00,0x00,0x33,0x66,0xcc,0x66,0x33,0x00,0x00,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xdb,0xee,0xdb,0x77,0xdb,0xee,0xdb,0x77,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1f,0x18,0x18,0x18,0x18,0x18,0x1f,0x18,0x1f,0x18,0x18,0x18,0x6c,0x6c,0x6c,0x6c,0x6f,0x6c,0x6c,0x6c,0x00,0x00,0x00,0x00,0x7f,0x6c,0x6c,0x6c,0x00,0x00,0x1f,0x18,0x1f,0x18,0x18,0x18,0x6c,0x6c,0x6f,0x60,0x6f,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x00,0x00,0x7f,0x60,0x6f,0x6c,0x6c,0x6c,0x6c,0x6c,0x6f,0x60,0x7f,0x00,0x00,0x00,0x6c,0x6c,0x6c,0x6c,0x7f,0x00,0x00,0x00,0x18,0x18,0x1f,0x18,0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1f,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0xf8,0x00,0x00,0x00,0x18,0x18,0x18,0x18,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xf8,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x18,0x18,0x18,0x18,0xff,0x18,0x18,0x18,0x18,0x18,0xf8,0x18,0xf8,0x18,0x18,0x18,0x6c,0x6c,0x6c,0x6c,0xec,0x6c,0x6c,0x6c,0x6c,0x6c,0xec,0x0c,0xfc,0x00,0x00,0x00,0x00,0x00,0xfc,0x0c,0xec,0x6c,0x6c,0x6c,0x6c,0x6c,0xef,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xef,0x6c,0x6c,0x6c,0x6c,0x6c,0xec,0x0c,0xec,0x6c,0x6c,0x6c,0x00,0x00,0xff,0x00,0xff,0x00,0x00,0x00,0x6c,0x6c,0xef,0x00,0xef,0x6c,0x6c,0x6c,0x18,0x18,0xff,0x00,0xff,0x00,0x00,0x00,0x6c,0x6c,0x6c,0x6c,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0xff,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0xfc,0x00,0x00,0x00,0x18,0x18,0xf8,0x18,0xf8,0x00,0x00,0x00,0x00,0x00,0xf8,0x18,0xf8,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0xfc,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0xff,0x6c,0x6c,0x6c,0x18,0x18,0xff,0x18,0xff,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf8,0x18,0x18,0x18,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x00,0x00,0x6e,0x3b,0x13,0x3b,0x6e,0x00,0x00,0x1e,0x33,0x1f,0x33,0x1f,0x03,0x03,0x00,0x3f,0x33,0x03,0x03,0x03,0x03,0x00,0x00,0x7f,0x36,0x36,0x36,0x36,0x36,0x00,0x3f,0x33,0x06,0x0c,0x06,0x33,0x3f,0x00,0x00,0x00,0x7e,0x1b,0x1b,0x1b,0x0e,0x00,0x00,0x66,0x66,0x66,0x66,0x3e,0x06,0x03,0x00,0x6e,0x3b,0x18,0x18,0x18,0x18,0x00,0x3f,0x0c,0x1e,0x33,0x33,0x1e,0x0c,0x3f,0x1c,0x36,0x63,0x7f,0x63,0x36,0x1c,0x00,0x1c,0x36,0x63,0x63,0x36,0x36,0x77,0x00,0x38,0x0c,0x18,0x3e,0x33,0x33,0x1e,0x00,0x00,0x00,0x7e,0xdb,0xdb,0x7e,0x00,0x00,0x60,0x30,0x7e,0xdb,0xdb,0x7e,0x06,0x03,0x1c,0x06,0x03,0x1f,0x03,0x06,0x1c,0x00,0x1e,0x33,0x33,0x33,0x33,0x33,0x33,0x00,0x00,0x3f,0x00,0x3f,0x00,0x3f,0x00,0x00,0x0c,0x0c,0x3f,0x0c,0x0c,0x00,0x3f,0x00,0x06,0x0c,0x18,0x0c,0x06,0x00,0x3f,0x00,0x18,0x0c,0x06,0x0c,0x18,0x00,0x3f,0x00,0x70,0xd8,0xd8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1b,0x1b,0x0e,0x0c,0x0c,0x00,0x3f,0x00,0x0c,0x0c,0x00,0x00,0x6e,0x3b,0x00,0x6e,0x3b,0x00,0x00,0x1c,0x36,0x36,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0xf0,0x30,0x30,0x30,0x37,0x36,0x3c,0x38,0x1e,0x36,0x36,0x36,0x36,0x00,0x00,0x00,0x0e,0x18,0x0c,0x06,0x1e,0x00,0x00,0x00,0x00,0x00,0x3c,0x3c,0x3c,0x3c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +}; + +void drawText(u8 *screen, int pitch, int x, int y, + const char *string, bool trans) +{ + screen += y*pitch; + int inc = 2; + switch(systemColorDepth) { + case 24: + inc = 3; + break; + case 32: + inc = 4; + break; + } + screen += x*inc; + + switch(systemColorDepth) { + case 16: + { + while(*string) { + char c = *string++; + u8 *scr = screen; + + u16 mask = ~RGB_LOW_BITS_MASK; + int h, w; + u16 *s = (u16 *)scr; + for (h = 0; h < 8; h++) { + for (w = 0; w < 8; w++, s++) { + int on = (fontdata2[(c<<3)+h]>>w)&1; + + if(trans) { + if(on) + *s = ((0xf) << systemRedShift) + + ((*s & mask) >>1); + } else { + if(on) + *s = (0x1f) << systemRedShift; + } + } + scr += pitch; + s = (u16 *)scr; + } + screen += inc*8; + } + } + break; + case 24: + { + while(*string) { + char c = *string++; + u8 *scr = screen; + + int h, w; + u8 *s = (u8 *)scr; + for (h = 0; h < 8; h++) { + for (w = 0; w < 8; w++, s+=3) { + int on = (fontdata2[(c<<3)+h]>>w)&1; + + if(trans) { + if(on) { + u32 color = (0x1f) << systemRedShift; + *s = ((color & 255)>>1)+(*s>>1); + *(s+1) = (((color >> 8) & 255)>>1)+(*(s+1)>>1); + *(s+2) = (((color >> 16) & 255)>>1)+(*(s+2)>>1); + } + } else { + if(on) { + u32 color = (0x1f) << systemRedShift; + *s = (color & 255); + *(s+1) = (color >> 8) & 255; + *(s+2) = (color >> 16) & 255; + } + } + } + scr += pitch; + s = (u8 *)scr; + } + screen += inc*8; + } + } + break; + case 32: + { + while(*string) { + char c = *string++; + u8 *scr = screen; + + int h, w; + u32 mask = 0xfefefe; + u32 *s = (u32 *)scr; + for (h = 0; h < 8; h++) { + for (w = 0; w < 8; w++, s++) { + int on = (fontdata2[(c<<3)+h]>>w)&1; + + if(trans) { + if(on) + *s = ((0xf) << systemRedShift) + ((*s & mask)>>1); + } else { + if(on) + *s = (0x1f) << systemRedShift; + } + } + scr += pitch; + s = (u32 *)scr; + } + screen += inc*8; + } + } + break; + } +} + diff --git a/src/sdl/text.h b/src/sdl/text.h new file mode 100644 index 0000000..32edd33 --- /dev/null +++ b/src/sdl/text.h @@ -0,0 +1,20 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +extern void drawText(u8 *, int, int, int, const char *, bool); diff --git a/src/sdl/vbam.cfg-example b/src/sdl/vbam.cfg-example new file mode 100644 index 0000000..97c2053 --- /dev/null +++ b/src/sdl/vbam.cfg-example @@ -0,0 +1,222 @@ +# All numeric values are in hexadecimal +# Use TAB or EQUAL sign to separate name from value + +# +# Key configuration (all numbers are in hexadecimal!) +# +# Keys values are in the format YYYYXXXX where YYYY is the device number. +# 0 means keyboard and XXXX is the SDL define for the desired key +# (read SDL_keysym.h). +# +# If YYYY is greater than 0, it means joystick number YYYY-1 and it uses the +# following format for XXXX: +# +# - if XXXX < 20, XXXX is the axis number multiplied by 2. An even number means +# movement to the negative side (on the X axis, it means left). An odd +# number means movement to the positive side (on the X axis, it mean +# right). For the Y axis, negative means up and positive means down. +# X axis is usally axis number 0 and Y is axis number 1. +# - if 20 >= XXXX > 30, then XXXX is the HAT number multiplied by 4 plus the +# direction: 0 for up, 1 for down, 2 for right and 3 for left. Example: +# 0021 is HAT 0 down, 0026 is HAT 1 right. +# - if 80 >= XXXX > 100, XXXX is the joystick button number (XXXX-0080). +# +# Default key configuration is (value in parenthesis): +# +# Left Left Arrow (00000114) +# Right Right Arrow (00000113) +# Up Up Arrow (00000111) +# Down Down Arrow (00000112) +# A Z (0000007a) +# B X (00000078) +# L A (00000061) +# R S (00000073) +# Start ENTER (0000000d) +# Select BACKSPACE (00000008) +# Speed up SPACE (00000020) +# Capture F12 (00000125) +# Auto A Q (00000071) +# Auto B W (00000077) +# +Joy0_Left=0114 +Joy0_Right=0113 +Joy0_Up=0111 +Joy0_Down=0112 +Joy0_A=007a +Joy0_B=0078 +Joy0_L=0061 +Joy0_R=0073 +Joy0_Start=000d +Joy0_Select=0008 +Joy0_Speed=0020 +Joy0_Capture=0125 +Joy0_AutoA=0071 +Joy0_AutoB=0077 + +# Motion support keys. Same format as above +# +# Default keys are (value in parenthesis): +# +# Left Numeric Pad 4 (0104) +# Right Numeric Pad 6 (0106) +# Up Numeric Pad 8 (0108) +# Down Numeric Pad 2 (0102) +# +Motion_Left=0104 +Motion_Right=0106 +Motion_Up=0108 +Motion_Down=0102 + +# OpenGL mode: +# 0 = don't use OpenGL, 1 = no texture filtering, 2 = bilinear filtering +openGL=1 +# when using OpenGL scaling and 'no filter' (called the 1x filter, number 0), +# make the window this many times taller and wider: +openGLscale=1 + +# Frame skip setting. Allowed values are from 0 to 5 only. +frameSkip=0 + +# Gameboy Frame skip setting. Allowed values are from 0 to 5 only. +gbFrameSkip=0 + +# Use fullscreen mode. 0=false, any other value means true +fullScreen=0 + +# Use bios file. 0=false, any other value means true +useBios=0 + +# GBA bios file full path and name (ZIP not supported) +biosFile=none + +# GB bios file full path and name (ZIP not supported) +gbBiosFile=none + +# Skip bios code +# 0=disable, anything else skips BIOS code +skipBios=0 + +# Filter to use: +# 0 = Stretch 1x (no filter), 1 = Stretch 2x, 2 = 2xSaI, 3 = Super 2xSaI, +# 4 = Super Eagle, 5 = Pixelate, 6 = Motion Blur, 7 = AdvanceMAME Scale2x, +# 8 = Bilinear, 9 = Bilinear Plus, 10 = Scanlines, 11 = TV Mode, 12 = lq2x, +# 13 = hq2x, 14 = Stretch 3x, 15 = hq3x, 16 = Stretch 4x, 17 = hq4x +filter=1 + +# Disable status messages. 0=false, any other value means true +disableStatus=0 + +# Enable Gameboy border. 0=false, any other value means true +borderOn=0 + +# Controls automatic SGB border +# 0=disable, anything else enables automatic SGB border display +borderAutomatic=0 + +# Gameboy emulator type. 0=automatic, 1=CGB/GBC, 2=SGB, 3=GB, 4=GBA, 5=SGB2 +emulatorType=0 + +# Enable washed colors. 0=false, any other value means true +colorOption=1 + +# Directories. Not setting one them makes the file go the rom directory. + +# Save state directory +#saveDir= + +# Screen shot Capture directory +#captureDir= + +# Battery directory +#batteryDir= + +# Screen capture format +# 0=PNG, anything else for BMP +captureFormat=0 + +# Sound quality +# 1=44 Khz, 2=22Khz, 4=11Khz +soundQuality=2 + +# GB Sound Stereo +# 0=false, anything else for true +soundStereo=0 + +# GB Sound Echo +# 0=false, anything else for true +soundEcho=0 + +# GB Sound Surround +# 0=false, anything else for true +soundSurround=0 + +# GB Sound Declicking +# 0=false, anything else for true +declicking=1 + +# Save Type +# 0=automatic, 1=EEPROM, 2=SRAM, 3=Flash, 4=EEPROM+Sensor, 5=NONE +saveType=0 + +# Flash size +# 0=64K Flash, 1=128K Flash +flashSize=0 + +# Sound volume +# 0-200=0%-200% +soundVolume=100 + +# Interframe blending +# 0=none, 1=motion blur, 2=smart +ifbType=0 + +# Show emulation speed +# 0=none, 1=percentage, 2=detailed +showSpeed=1 + +# Show speed in transparent mode +# 0=normal, anything else for transparent +showSpeedTransparent=1 + +# Enable/Disable auto frameskip +# 0=disable, anything else to enable +autoFrameSkip=0 + +# Sets the desired throttle +# 0=disable, 5...1000 valid throttle speeds +throttle=0 + +# Pauses the emulator when the window is inactive +# 0=disable, anything else to enable +pauseWhenInactive=0 + +# Enables AGBPrint support +# 0=disable, anything else to enable +agbPrint=0 + +# Enables GBA RTC support +# 0=disable, anything else to enable +rtcEnabled=0 + +# Sound Enable +# Controls which channels are enabled: (add values) +# 1 - Channel 1 +# 2 - Channel 2 +# 4 - Channel 3 +# 8 - Channel 4 +# 100 - DirectSound A +# 200 - DirectSound B +# 30f=all enabled, 0=mute all +soundEnable=30f + +# The interval between the rewind saves +# Minimum of 0 seconds to disable rewind support, +# Maximum of 10 minutes (258). Value in seconds (hexadecimal numbers) +rewindTimer=0 + +# type of save/load keyboard control +# if 0, then SHIFT+F# saves, F# loads (old VBA, ...) +# if 1, then SHIFT+F# loads, F# saves (linux snes9x, ...) +# if 2, then F5 decreases slot number, F6 increases, F7 saves, F8 loads // not implemented +saveKeysSwitch=2 + diff --git a/src/vba-over.ini b/src/vba-over.ini new file mode 100644 index 0000000..8a417ce --- /dev/null +++ b/src/vba-over.ini @@ -0,0 +1,497 @@ +# February 2008 + +# ------------------- +# (Int) International / MultiLingual +# ------------------- + + +# Dragon Ball Z - The Legacy of Goku II (Europe)(En,Fr,De,Es,It) +[ALFP] +saveType=1 + +# Dragon Ball Z - The Legacy of Goku (Europe)(En,Fr,De,Es,It) +[ALGP] +saveType=1 + +# Rocky (Europe)(En,Fr,De,Es,It) +[AROP] +saveType=1 + +# Rocky (USA)(En,Fr,De,Es,It) +[AR8e] +saveType=1 + +# Pokemon - Ruby Version (USA, Europe) +[AXVE] +rtcEnabled=1 +flashSize=131072 + +# Pokemon - Sapphire Version (USA, Europe) +[AXPE] +rtcEnabled=1 +flashSize=131072 + +# Super Mario Advance 4 - Super Mario Bros. 3 (Europe)(En,Fr,De,Es,It) +[AX4P] +flashSize=131072 + +# Top Gun - Combat Zones (USA)(En,Fr,De,Es,It) +[A2YE] +saveType=5 + +# Dragon Ball Z - Taiketsu (Europe)(En,Fr,De,Es,It) +[BDBP] +saveType=1 + +# Mario vs. Donkey Kong (Europe) +[BM5P] +saveType=3 + +# Pokemon - Emerald Version (USA, Europe) +[BPEE] +rtcEnabled=1 +flashSize=131072 + +# Yu-Gi-Oh! - Ultimate Masters - World Championship Tournament 2006 (Europe)(En,Jp,Fr,De,Es,It) +[BY6P] +saveType=2 + +# Pokemon Mystery Dungeon - Red Rescue Team (USA, Australia) +[B24E] +flashSize=131072 + +# Classic NES Series - Castlevania (USA, Europe) +[FADE] +saveType=1 +mirroringEnabled=1 + +# Classic NES Series - Bomberman (USA, Europe) +[FBME] +saveType=1 +mirroringEnabled=1 + +# Classic NES Series - Donkey Kong (USA, Europe) +[FDKE] +saveType=1 +mirroringEnabled=1 + +# Classic NES Series - Dr. Mario (USA, Europe) +[FDME] +saveType=1 +mirroringEnabled=1 + +# Classic NES Series - Excitebike (USA, Europe) +[FEBE] +saveType=1 +mirroringEnabled=1 + +# Classic NES Series - Ice Climber (USA, Europe) +[FICE] +saveType=1 +mirroringEnabled=1 + +# Classic NES Series - Zelda II - The Adventure of Link (USA, Europe) +[FLBE] +saveType=1 +mirroringEnabled=1 + +# Classic NES Series - Metroid (USA, Europe) +[FMRE] +saveType=1 +mirroringEnabled=1 + +# Classic NES Series - Pac-Man (USA, Europe) +[FP7E] +saveType=1 +mirroringEnabled=1 + +# Classic NES Series - Super Mario Bros. (USA, Europe) +[FSME] +saveType=1 +mirroringEnabled=1 + +# Classic NES Series - Xevious (USA, Europe) +[FXVE] +saveType=1 +mirroringEnabled=1 + +# Classic NES Series - Legend of Zelda (USA, Europe) +[FZLE] +saveType=1 +mirroringEnabled=1 + +# Yoshi's Universal Gravitation (Europe)(En,Fr,De,Es,It) +[KYGP] +saveType=4 + +# Boktai - The Sun Is in Your Hand (Europe)(En,Fr,De,Es,It) +[U3IP] +rtcEnabled=1 + +# Boktai 2 - Solar Boy Django (Europe)(En,Fr,De,Es,It) +[U32P] +rtcEnabled=1 + + +# -------------------- +# (U) USA +# -------------------- + +# Golden Sun - The Lost Age (USA) +[AGFE] +rtcEnabled=1 +flashSize=0x10000 + +# Golden Sun (USA) +[AGSE] +rtcEnabled=1 +flashSize=0x10000 + +# Dragon Ball Z - The Legacy of Goku II (USA) +[ALFE] +saveType=1 + +# Dragon Ball Z - The Legacy of Goku (USA) +[ALGE] +saveType=1 + +# Super Mario Advance 4 - Super Mario Bros 3 - Super Mario Advance 4 v1.1 (USA) +[AX4E] +flashSize=131072 + +# Dragon Ball Z - Taiketsu (USA) +[BDBE] +saveType=1 + +# Dragon Ball Z - Buu's Fury (USA) +[BG3E] +saveType=1 + +# 2 Games in 1 - Dragon Ball Z - The Legacy of Goku I & II (USA) +[BLFE] +saveType=1 + +# Pokemon - Fire Red Version (USA, Europe) +[BPRE] +flashSize=131072 + +# Pokemon - Leaf Green Version (USA, Europe) +[BPGE] +flashSize=131072 + +# Dragon Ball GT - Transformation (USA) +[BT4E] +saveType=1 + +# 2 Games in 1 - Dragon Ball Z - Buu's Fury + Dragon Ball GT - Transformation (USA) +[BUFE] +saveType=1 + +# Yu-Gi-Oh! GX - Duel Academy (USA) +[BYGE] +saveType=2 +useBios=1 + +# Yoshi - Topsy-Turvy (USA) +[KYGE] +saveType=1 + +# e-Reader (USA) +[PSAE] +flashSize=131072 + +# Boktai - The Sun Is in Your Hand (USA) +[U3IE] +rtcEnabled=1 + +# Boktai 2 - Solar Boy Django (USA) +[U32E] +rtcEnabled=1 + +# -------------------- +# (J) Japan +# -------------------- + +# Dragon Ball Z - The Legacy of Goku II International (Japan) +[ALFJ] +saveType=1 + +# Pocket Monsters - Sapphire (Japan) +[AXPJ] +rtcEnabled=1 +flashSize=131072 + +# Pocket Monsters - Ruby (Japan) +[AXVJ] +rtcEnabled=1 +flashSize=131072 + +# Super Mario Advance 4 (Japan) +[AX4J] +flashSize=131072 + +# F-Zero - Climax (Japan) +[BFTJ] +flashSize=131072 + +# Game Boy Wars Advance 1+2 (Japan) +[BGWJ] +flashSize=131072 + +# Sennen Kazoku (Japan) +[BKAJ] +rtcEnabled=1 +flashSize=131072 + +# Pocket Monsters - Emerald (Japan) +[BPEJ] +rtcEnabled=1 +flashSize=131072 + +# Pocket Monsters - Leaf Green (Japan) +[BPGJ] +flashSize=131072 + +# Pocket Monsters - Fire Red (Japan) +[BPRJ] +flashSize=131072 + +# Digi Communication 2 - Datou! Black Gemagema Dan (Japan) +[BDKJ] +saveType=1 + +# Rockman EXE 4.5 - Real Operation (Japan) +[BR4J] +rtcEnabled=1 + +# Famicom Mini Vol. 01 - Super Mario Bros. (Japan) +[FMBJ] +saveType=1 +mirroringEnabled=1 + +# Famicom Mini Vol. 12 - Clu Clu Land (Japan) +[FCLJ] +saveType=1 +mirroringEnabled=1 + +# Famicom Mini Vol. 13 - Balloon Fight (Japan) +[FBFJ] +saveType=1 +mirroringEnabled=1 + +# Famicom Mini Vol. 14 - Wrecking Crew (Japan) +[FWCJ] +saveType=1 +mirroringEnabled=1 + +# Famicom Mini Vol. 15 - Dr. Mario (Japan) +[FDMJ] +saveType=1 +mirroringEnabled=1 + +# Famicom Mini Vol. 16 - Dig Dug (Japan) +[FDDJ] +saveType=1 +mirroringEnabled=1 + +# Famicom Mini Vol. 17 - Takahashi Meijin no Boukenjima (Japan) +[FTBJ] +saveType=1 +mirroringEnabled=1 + +# Famicom Mini Vol. 18 - Makaimura (Japan) +[FMKJ] +saveType=1 +mirroringEnabled=1 + +# Famicom Mini Vol. 19 - Twin Bee (Japan) +[FTWJ] +saveType=1 +mirroringEnabled=1 + +# Famicom Mini Vol. 20 - Ganbare Goemon! Karakuri Douchuu (Japan) +[FGGJ] +saveType=1 +mirroringEnabled=1 + +# Famicom Mini Vol. 21 - Super Mario Bros. 2 (Japan) +[FM2J] +saveType=1 +mirroringEnabled=1 + +# Famicom Mini Vol. 22 - Nazo no Murasame Jou (Japan) +[FNMJ] +saveType=1 +mirroringEnabled=1 + +# Famicom Mini Vol. 23 - Metroid (Japan) +[FMRJ] +saveType=1 +mirroringEnabled=1 + +# Famicom Mini Vol. 24 - Hikari Shinwa - Palthena no Kagami (Japan) +[FPTJ] +saveType=1 +mirroringEnabled=1 + +# Famicom Mini Vol. 25 - The Legend of Zelda 2 - Link no Bouken (Japan) +[FLBJ] +saveType=1 +mirroringEnabled=1 + +# Famicom Mini Vol. 26 - Famicom Mukashi Banashi - Shin Onigashima - Zen Kou Hen (Japan) +[FFMJ] +saveType=1 +mirroringEnabled=1 + +# Famicom Mini Vol. 27 - Famicom Tantei Club - Kieta Koukeisha - Zen Kou Hen (Japan) +[FTKJ] +saveType=1 +mirroringEnabled=1 + +# Famicom Mini Vol. 28 - Famicom Tantei Club Part II - Ushiro ni Tatsu Shoujo - Zen Kou Hen (Japan) +[FTUJ] +saveType=1 +mirroringEnabled=1 + +# Famicom Mini Vol. 29 - Akumajou Dracula (Japan) +[FADJ] +saveType=1 +mirroringEnabled=1 + +# Famicom Mini Vol. 30 - SD Gundam World - Gachapon Senshi Scramble Wars (Japan) +[FSDJ] +saveType=1 +mirroringEnabled=1 + +# Koro Koro Puzzle - Happy Panechu! (Japan) +[KHPJ] +saveType=4 + +# Yoshi no Banyuuinryoku (Japan) +[KYGJ] +saveType=4 + +# Card e-Reader+ (Japan) +[PSAJ] +flashSize=131072 + +# Bokura no Taiyou - Taiyou Action RPG (Japan) +[U3IJ] +rtcEnabled=1 + +# Zoku Bokura no Taiyou - Taiyou Shounen Django (Japan) +[U32J] +rtcEnabled=1 + +# Shin Bokura no Taiyou - Gyakushuu no Sabata (Japan) +[U33J] +rtcEnabled=1 + +# -------------------- +# (F) France +# -------------------- + +# Pokemon - Version Saphir (France) +[AXPF] +rtcEnabled=1 +flashSize=131072 + +# Pokemon - Version Rubis (France) +[AXVF] +rtcEnabled=1 +flashSize=131072 + +# Pokemon - Version Emeraude (France) +[BPEF] +rtcEnabled=1 +flashSize=131072 + +# Pokemon - Version Vert Feuille (France) +[BPGF] +flashSize=131072 + +# Pokemon - Version Rouge Feu (France) +[BPRF] +flashSize=131072 + +# -------------------- +# (I) Italy +# -------------------- + +# Pokemon - Versione Zaffiro (Italy) +[AXPI] +rtcEnabled=1 +flashSize=131072 + +# Pokemon - Versione Rubino (Italy) +[AXVI] +rtcEnabled=1 +flashSize=131072 + +# Pokemon - Versione Smeraldo (Italy) +[BPEI] +rtcEnabled=1 +flashSize=131072 + +# Pokemon - Versione Verde Foglia (Italy) +[BPGI] +flashSize=131072 + +# Pokemon - Versione Rosso Fuoco (Italy) +[BPRI] +flashSize=131072 + +# -------------------- +# (G) Germany +# -------------------- + +# Pokemon - Saphir-Edition (Germany) +[AXPD] +rtcEnabled=1 +flashSize=131072 + +# Pokemon - Rubin-Edition (Germany) +[AXVD] +rtcEnabled=1 +flashSize=131072 + +# Pokemon - Smaragd-Edition (Germany) +[BPED] +rtcEnabled=1 +flashSize=131072 + +# Pokemon - Blattgruene Edition (Germany) +[BPGD] +flashSize=131072 + +# Pokemon - Feuerrote Edition (Germany) +[BPRD] +flashSize=131072 + +# -------------------- +# (S) Spain +# -------------------- + +# Pokemon - Edicion Zafiro (Spain) +[AXPS] +rtcEnabled=1 +flashSize=131072 + +# Pokemon - Edicion Rubi (Spain) +[AXVS] +rtcEnabled=1 +flashSize=131072 + +# Pokemon - Edicion Esmeralda (Spain) +[BPES] +rtcEnabled=1 +flashSize=131072 + +# Pokemon - Edicion Verde Hoja (Spain) +[BPGS] +flashSize=131072 + +# Pokemon - Edicion Rojo Fuego (Spain) +[BPRS] +flashSize=131072 \ No newline at end of file diff --git a/src/version.h b/src/version.h new file mode 100644 index 0000000..c32a84c --- /dev/null +++ b/src/version.h @@ -0,0 +1,57 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#define VBA_NAME "VisualBoyAdvance-M" + +#ifdef WIN32 +#include "svnrev.h" +#else +#ifdef SVN_REV +#define SVN_REV_STR SVN_REV +#else +#define SVN_REV_STR "" +#endif +#endif + +#define VBA_FEATURE_STRING "" + +#ifdef DEBUG +#define VBA_SUBVERSION_STRING " debug" +#elif defined(PUBLIC_RELEASE) +#define VBA_SUBVERSION_STRING "" +#else +#define VBA_SUBVERSION_STRING " (SVN" SVN_REV_STR ")" +#endif + +#if defined(_MSC_VER) +#define VBA_COMPILER "" +#define VBA_COMPILER_DETAIL " msvc " _Py_STRINGIZE(_MSC_VER) +#define _Py_STRINGIZE(X) _Py_STRINGIZE1((X)) +#define _Py_STRINGIZE1(X) _Py_STRINGIZE2 ## X +#define _Py_STRINGIZE2(X) #X +//re: http://72.14.203.104/search?q=cache:HG-okth5NGkJ:mail.python.org/pipermail/python-checkins/2002-November/030704.html+_msc_ver+compiler+version+string&hl=en&gl=us&ct=clnk&cd=5 +#else +// TODO: make for others compilers +#define VBA_COMPILER "" +#define VBA_COMPILER_DETAIL "" +#endif + +#define VBA_VERSION_STRING " " "1.8.0" VBA_SUBVERSION_STRING VBA_FEATURE_STRING VBA_COMPILER +#define VBA_NAME_AND_VERSION " " VBA_NAME VBA_VERSION_STRING +#define VBA_NAME_AND_SUBVERSION " " VBA_NAME VBA_SUBVERSION_STRING diff --git a/src/win32/AVIWrite.cpp b/src/win32/AVIWrite.cpp new file mode 100644 index 0000000..f0dc90d --- /dev/null +++ b/src/win32/AVIWrite.cpp @@ -0,0 +1,284 @@ +#include "stdafx.h" + +#include "AVIWrite.h" +#pragma comment( lib, "Vfw32" ) + + +AVIWrite::AVIWrite() +{ + m_failed = false; + m_file = NULL; + m_videoStream = NULL; + m_audioStream = NULL; + ZeroMemory( &m_videoCompSettings, sizeof( m_videoCompSettings ) ); + ZeroMemory( &m_audioCompSettings, sizeof( m_audioCompSettings ) ); + m_videoCompressed = NULL; + m_audioCompressed = NULL; + m_frameRate = 0; + m_frameCounter = 0; + m_sampleCounter = 0; + m_videoFrameSize = 0; + m_audioFrameSize = 0; + m_audioBlockAlign = 0; + + AVIFileInit(); +} + + +AVIWrite::~AVIWrite() +{ + if( m_audioCompressed ) { + AVIStreamRelease( m_audioCompressed ); + } + + if( m_audioStream ) { + AVIStreamRelease( m_audioStream ); + } + + if( m_videoCompressed ) { + AVIStreamRelease( m_videoCompressed ); + } + + if( m_videoStream ) { + AVIStreamRelease( m_videoStream ); + } + + if( m_file ) { + AVIFileRelease( m_file ); + } + + AVIFileExit(); +} + + +bool AVIWrite::CreateAVIFile( LPCTSTR filename ) +{ + if( m_file || m_failed ) return false; + + HRESULT err = 0; + + // -- create the AVI file -- + err = AVIFileOpen( + &m_file, + filename, + OF_CREATE | OF_WRITE | OF_SHARE_EXCLUSIVE, + NULL + ); + + if( FAILED( err ) ) { + m_failed = true; + return false; + } + + return true; +} + + +// colorBits: 16, 24 or 32 +bool AVIWrite::CreateVideoStream( LONG imageWidth, LONG imageHeight, WORD colorBits, DWORD framesPerSecond, HWND parentWnd ) +{ + if( m_videoStream || m_failed ) return false; + + HRESULT err = 0; + AVISTREAMINFO videoInfo; + BITMAPINFOHEADER bitmapInfo; + AVICOMPRESSOPTIONS *settings[1]; + ZeroMemory( &videoInfo, sizeof( videoInfo ) ); + ZeroMemory( &bitmapInfo, sizeof( bitmapInfo ) ); + settings[0] = &m_videoCompSettings; + + // -- initialize the video stream information -- + videoInfo.fccType = streamtypeVIDEO; + videoInfo.fccHandler = 0; + videoInfo.dwScale = 1; + videoInfo.dwRate = framesPerSecond; + videoInfo.dwSuggestedBufferSize = imageWidth * imageHeight * ( colorBits >> 3 ); + + // -- create the video stream -- + err = AVIFileCreateStream( + m_file, + &m_videoStream, + &videoInfo + ); + + if( FAILED( err ) ) { + m_failed = true; + return false; + } + + + // -- ask for compression settings -- + if( AVISaveOptions( + parentWnd, + 0, + 1, + &m_videoStream, + settings ) ) + { + err = AVIMakeCompressedStream( + &m_videoCompressed, + m_videoStream, + settings[0], + NULL + ); + + AVISaveOptionsFree( 1, settings ); + if( FAILED( err ) ) { + m_failed = true; + return false; + } + } else { + AVISaveOptionsFree( 1, settings ); + m_failed = true; + return false; + } + + + // -- initialize the video stream format -- + bitmapInfo.biSize = sizeof( bitmapInfo ); + bitmapInfo.biWidth = imageWidth; + bitmapInfo.biHeight = imageHeight; + bitmapInfo.biBitCount = colorBits; + bitmapInfo.biPlanes = 1; + bitmapInfo.biCompression = BI_RGB; + bitmapInfo.biSizeImage = imageWidth * imageHeight * ( colorBits >> 3 ); + + // -- set the video stream format -- + err = AVIStreamSetFormat( + m_videoCompressed, + 0, + &bitmapInfo, + bitmapInfo.biSize + ( bitmapInfo.biClrUsed * sizeof( RGBQUAD ) ) + ); + + if( FAILED( err ) ) { + m_failed = true; + return false; + } + + + m_frameRate = framesPerSecond; + m_videoFrameSize = imageWidth * imageHeight * ( colorBits >> 3 ); + + return true; +} + + +// call AddVideoStream() first +// channelCount: max. 2 +// sampleBits: max. 16 +bool AVIWrite::CreateAudioStream( WORD channelCount, DWORD sampleRate, WORD sampleBits, HWND parentWnd ) +{ + if( m_audioStream || m_failed ) return false; + + HRESULT err = 0; + AVISTREAMINFO audioInfo; + WAVEFORMATEX waveInfo; + ZeroMemory( &audioInfo, sizeof( audioInfo ) ); + ZeroMemory( &waveInfo, sizeof( waveInfo ) ); + + // -- initialize the audio stream information -- + audioInfo.fccType = streamtypeAUDIO; + audioInfo.dwQuality = (DWORD)-1; + audioInfo.dwScale = channelCount * ( sampleBits >> 3 ); + audioInfo.dwRate = channelCount * ( sampleBits >> 3 ) * sampleRate; + audioInfo.dwInitialFrames = 1; + audioInfo.dwSampleSize = channelCount * ( sampleBits >> 3 ); + audioInfo.dwSuggestedBufferSize = 0; + + // -- create the audio stream -- + err = AVIFileCreateStream( + m_file, + &m_audioStream, + &audioInfo + ); + + if( FAILED( err ) ) { + m_failed = true; + return false; + } + + + // -- initialize the audio stream format -- + waveInfo.wFormatTag = WAVE_FORMAT_PCM; + waveInfo.nChannels = channelCount; + waveInfo.nSamplesPerSec = sampleRate; + waveInfo.nAvgBytesPerSec = channelCount * ( sampleBits >> 3 ) * sampleRate; + waveInfo.nBlockAlign = channelCount * ( sampleBits >> 3 ); + waveInfo.wBitsPerSample = sampleBits; + waveInfo.cbSize = 0; + + // -- set the audio stream format -- + err = AVIStreamSetFormat( + m_audioStream, + 0, + &waveInfo, + sizeof( waveInfo ) + ); + + if( FAILED( err ) ) { + m_failed = true; + return false; + } + + m_audioBlockAlign = channelCount * ( sampleBits >> 3 ); + m_audioFrameSize = channelCount * ( sampleBits >> 3 ) * ( sampleRate / m_frameRate ); + + return true; +} + + +bool AVIWrite::AddVideoFrame( LPVOID imageData ) +{ + if( !m_videoStream || m_failed ) return false; + + HRESULT err = 0; + + err = AVIStreamWrite( + m_videoCompressed, + m_frameCounter, + 1, + imageData, + m_videoFrameSize, + AVIIF_KEYFRAME, + NULL, + NULL + ); + + if( FAILED( err ) ) { + m_failed = true; + return false; + } + + m_frameCounter++; + + return true; +} + + +bool AVIWrite::AddAudioFrame( LPVOID soundData ) +{ + if( !m_audioStream || m_failed ) return false; + + HRESULT err = 0; + + err = AVIStreamWrite( + m_audioStream, + m_sampleCounter, + m_audioFrameSize / m_audioBlockAlign, + soundData, + m_audioFrameSize, + 0, + NULL, + NULL + ); + + if( FAILED( err ) ) { + m_failed = true; + return false; + } + + m_sampleCounter += m_audioFrameSize / m_audioBlockAlign; + + return true; +} diff --git a/src/win32/AVIWrite.h b/src/win32/AVIWrite.h new file mode 100644 index 0000000..5b7adb0 --- /dev/null +++ b/src/win32/AVIWrite.h @@ -0,0 +1,32 @@ +#include + + +// info: recreate the whole AVIWrite object if any method fails +class AVIWrite +{ +public: + AVIWrite(); + virtual ~AVIWrite(); + + bool CreateAVIFile( LPCTSTR filename ); + bool CreateVideoStream( LONG imageWidth, LONG imageHeight, WORD colorBits, DWORD framesPerSecond, HWND parentWnd ); + bool CreateAudioStream( WORD channelCount, DWORD sampleRate, WORD sampleBits, HWND parentWnd ); + bool AddVideoFrame( LPVOID imageData ); + bool AddAudioFrame( LPVOID soundData ); + +private: + bool m_failed; + PAVIFILE m_file; + PAVISTREAM m_videoStream; + PAVISTREAM m_audioStream; + AVICOMPRESSOPTIONS m_videoCompSettings; + AVICOMPRESSOPTIONS m_audioCompSettings; + PAVISTREAM m_videoCompressed; + PAVISTREAM m_audioCompressed; + DWORD m_frameRate; + LONG m_frameCounter; + LONG m_sampleCounter; + LONG m_videoFrameSize; + LONG m_audioFrameSize; + WORD m_audioBlockAlign; +}; diff --git a/src/win32/AboutDialog.cpp b/src/win32/AboutDialog.cpp new file mode 100644 index 0000000..76954ac --- /dev/null +++ b/src/win32/AboutDialog.cpp @@ -0,0 +1,55 @@ +#include "stdafx.h" +#include "AboutDialog.h" +#include "../AutoBuild.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// AboutDialog dialog + + +AboutDialog::AboutDialog(CWnd* pParent /*=NULL*/) + : CDialog(AboutDialog::IDD, pParent) +{ + m_version = _T(VBA_VERSION_STRING); + m_date = _T(__DATE__); +} + + +void AboutDialog::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(AboutDialog) + DDX_Text(pDX, IDC_VERSION, m_version); + DDX_Control(pDX, IDC_URL, m_link); + //}}AFX_DATA_MAP + DDX_Text(pDX, IDC_DATE, m_date); +} + + +BEGIN_MESSAGE_MAP(AboutDialog, CDialog) + //{{AFX_MSG_MAP(AboutDialog) + //}}AFX_MSG_MAP + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// AboutDialog message handlers + +BOOL AboutDialog::OnInitDialog() +{ + CDialog::OnInitDialog(); + + CWnd *p = GetDlgItem(IDC_TRANSLATOR_URL); + if(p) { + m_translator.SubclassDlgItem(IDC_TRANSLATOR_URL, this); + } + + m_link.SetWindowText("http://vba-m.com"); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} diff --git a/src/win32/AboutDialog.h b/src/win32/AboutDialog.h new file mode 100644 index 0000000..d09b08e --- /dev/null +++ b/src/win32/AboutDialog.h @@ -0,0 +1,24 @@ +#pragma once + +#include "stdafx.h" +#include "Hyperlink.h" +#include "resource.h" + + +class AboutDialog : public CDialog +{ +public: + AboutDialog(CWnd* pParent = NULL); + enum { IDD = IDD_ABOUT }; + +protected: + virtual void DoDataExchange(CDataExchange* pDX); + virtual BOOL OnInitDialog(); + DECLARE_MESSAGE_MAP() + +private: + Hyperlink m_link; + Hyperlink m_translator; + CString m_version; + CString m_date; +}; diff --git a/src/win32/AccelEditor.cpp b/src/win32/AccelEditor.cpp new file mode 100644 index 0000000..4caae8b --- /dev/null +++ b/src/win32/AccelEditor.cpp @@ -0,0 +1,269 @@ +#include "stdafx.h" +#include "vba.h" +#include "AccelEditor.h" +#include "CmdAccelOb.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// AccelEditor dialog + + +AccelEditor::AccelEditor(CWnd* pParent /*=NULL*/) + : ResizeDlg(AccelEditor::IDD, pParent) +{ + //{{AFX_DATA_INIT(AccelEditor) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT + mgr = theApp.winAccelMgr; +} + + +void AccelEditor::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(AccelEditor) + DDX_Control(pDX, IDC_CURRENTS, m_currents); + DDX_Control(pDX, IDC_ALREADY_AFFECTED, m_alreadyAffected); + DDX_Control(pDX, IDC_COMMANDS, m_commands); + DDX_Control(pDX, IDC_EDIT_KEY, m_key); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(AccelEditor, CDialog) + //{{AFX_MSG_MAP(AccelEditor) + ON_BN_CLICKED(ID_OK, OnOk) + ON_LBN_SELCHANGE(IDC_COMMANDS, OnSelchangeCommands) + ON_BN_CLICKED(IDC_RESET, OnReset) + ON_BN_CLICKED(IDC_ASSIGN, OnAssign) + ON_BN_CLICKED(ID_CANCEL, OnCancel) + ON_BN_CLICKED(IDC_REMOVE, OnRemove) + //}}AFX_MSG_MAP + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// AccelEditor message handlers + +BOOL AccelEditor::OnInitDialog() +{ + CDialog::OnInitDialog(); + + DIALOG_SIZER_START( sz ) + DIALOG_SIZER_ENTRY( IDC_STATIC1, DS_MoveX) + DIALOG_SIZER_ENTRY( IDC_STATIC2, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_STATIC3, DS_MoveX | DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_ALREADY_AFFECTED, DS_MoveY) + DIALOG_SIZER_ENTRY( ID_OK, DS_MoveX) + DIALOG_SIZER_ENTRY( ID_CANCEL, DS_MoveX) + DIALOG_SIZER_ENTRY( IDC_ASSIGN, DS_MoveX) + DIALOG_SIZER_ENTRY( IDC_REMOVE, DS_MoveX) + DIALOG_SIZER_ENTRY( IDC_RESET, DS_MoveX) + DIALOG_SIZER_ENTRY( IDC_CLOSE, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_COMMANDS, DS_SizeX | DS_SizeY) + DIALOG_SIZER_ENTRY( IDC_CURRENTS, DS_MoveX | DS_SizeY) + DIALOG_SIZER_ENTRY( IDC_EDIT_KEY, DS_MoveX | DS_MoveY) + DIALOG_SIZER_END() + + SetData(sz, + TRUE, + HKEY_CURRENT_USER, + "Software\\Emulators\\VisualBoyAdvance\\Viewer\\AccelEditor", + NULL); + + InitCommands(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void AccelEditor::InitCommands() +{ + m_commands.ResetContent(); + m_alreadyAffected.SetWindowText(""); + + POSITION pos = mgr.m_mapAccelString.GetStartPosition(); + + while(pos != NULL) { + CString command; + WORD wID; + mgr.m_mapAccelString.GetNextAssoc(pos, command, wID); + + int index = m_commands.AddString(command); + m_commands.SetItemData(index, wID); + } + + // Update the currents accels associated with the selected command + if (m_commands.SetCurSel(0) != LB_ERR) + OnSelchangeCommands(); +} + +void AccelEditor::OnCancel() +{ + EndDialog(FALSE); +} + +void AccelEditor::OnOk() +{ + EndDialog(TRUE); +} + +void AccelEditor::OnSelchangeCommands() +{ + // Check if some commands exist. + int index = m_commands.GetCurSel(); + if (index == LB_ERR) + return; + + WORD wIDCommand = LOWORD(m_commands.GetItemData(index)); + m_currents.ResetContent(); + + CCmdAccelOb* pCmdAccel; + + if (mgr.m_mapAccelTable.Lookup(wIDCommand, pCmdAccel)) { + CAccelsOb* pAccel; + CString szBuffer; + POSITION pos = pCmdAccel->m_Accels.GetHeadPosition(); + + // Add the keys to the 'currents keys' listbox. + while (pos != NULL) { + pAccel = pCmdAccel->m_Accels.GetNext(pos); + pAccel->GetString(szBuffer); + index = m_currents.AddString(szBuffer); + // and a pointer to the accel object. + m_currents.SetItemData(index, (DWORD_PTR)pAccel); + } + } + // Init the key editor + // m_pKey->ResetKey(); + +} + +void AccelEditor::OnReset() +{ + mgr.Default(); + InitCommands(); // update the listboxes. +} + +void AccelEditor::OnAssign() +{ + // Control if it's not already affected + CCmdAccelOb* pCmdAccel; + CAccelsOb* pAccel; + WORD wIDCommand; + POSITION pos; + + WORD wKey; + bool bCtrl, bAlt, bShift; + + if (!m_key.GetAccelKey(wKey, bCtrl, bAlt, bShift)) + return; // no valid key, abort + + int count = m_commands.GetCount(); + int index; + for (index = 0; index < count; index++) { + + wIDCommand = LOWORD(m_commands.GetItemData(index)); + mgr.m_mapAccelTable.Lookup(wIDCommand, pCmdAccel); + + pos = pCmdAccel->m_Accels.GetHeadPosition(); + while (pos != NULL) { + pAccel = pCmdAccel->m_Accels.GetNext(pos); + if (pAccel->IsEqual(wKey, bCtrl, bAlt, bShift)) { + // the key is already affected (in the same or other command) + m_alreadyAffected.SetWindowText(pCmdAccel->m_szCommand); + m_key.SetSel(0, -1); + return; // abort + } + } + } + + // OK, we can add the accel key in the currently selected group + index = m_commands.GetCurSel(); + if (index == LB_ERR) + return; + + // Get the object who manage the accels list, associated to the command. + wIDCommand = LOWORD(m_commands.GetItemData(index)); + + if (mgr.m_mapAccelTable.Lookup(wIDCommand, pCmdAccel) != TRUE) + return; + + BYTE cVirt = 0; + if (bCtrl) + cVirt |= FCONTROL; + if (bAlt) + cVirt |= FALT; + if (bShift) + cVirt |= FSHIFT; + + cVirt |= FVIRTKEY; + + // Create the new key... + pAccel = new CAccelsOb(cVirt, wKey, false); + ASSERT(pAccel != NULL); + // ...and add in the list. + pCmdAccel->m_Accels.AddTail(pAccel); + + // Update the listbox. + CString szBuffer; + pAccel->GetString(szBuffer); + + index = m_currents.AddString(szBuffer); + m_currents.SetItemData(index, (DWORD_PTR)pAccel); + + // Reset the key editor. + m_key.ResetKey(); +} + +void AccelEditor::OnRemove() +{ + // Some controls + int indexCurrent = m_currents.GetCurSel(); + if (indexCurrent == LB_ERR) + return; + + // 2nd part. + int indexCmd = m_commands.GetCurSel(); + if (indexCmd == LB_ERR) + return; + + // Ref to the ID command + WORD wIDCommand = LOWORD(m_commands.GetItemData(indexCmd)); + + // Run through the accels,and control if it can be deleted. + CCmdAccelOb* pCmdAccel; + if (mgr.m_mapAccelTable.Lookup(wIDCommand, pCmdAccel) == TRUE) { + CAccelsOb* pAccel; + CAccelsOb* pAccelCurrent = (CAccelsOb*)(m_currents.GetItemData(indexCurrent)); + CString szBuffer; + POSITION pos = pCmdAccel->m_Accels.GetHeadPosition(); + POSITION PrevPos; + while (pos != NULL) { + PrevPos = pos; + pAccel = pCmdAccel->m_Accels.GetNext(pos); + if (pAccel == pAccelCurrent) { + if (!pAccel->m_bLocked) { + // not locked, so we delete the key + pCmdAccel->m_Accels.RemoveAt(PrevPos); + delete pAccel; + // and update the listboxes/key editor/static text + m_currents.DeleteString(indexCurrent); + m_key.ResetKey(); + m_alreadyAffected.SetWindowText(""); + return; + } else { + systemMessage(0,"Unable to remove this\naccelerator (Locked)"); + return; + } + } + } + systemMessage(0,"internal error (CAccelDlgHelper::Remove : pAccel unavailable)"); + return; + } + systemMessage(0,"internal error (CAccelDlgHelper::Remove : Lookup failed)"); +} diff --git a/src/win32/AccelEditor.h b/src/win32/AccelEditor.h new file mode 100644 index 0000000..142148c --- /dev/null +++ b/src/win32/AccelEditor.h @@ -0,0 +1,60 @@ +#if !defined(AFX_ACCELEDITOR_H__66F5C854_E28E_40D1_B763_1850374B46A2__INCLUDED_) +#define AFX_ACCELEDITOR_H__66F5C854_E28E_40D1_B763_1850374B46A2__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// AccelEditor.h : header file +// +#include "AcceleratorManager.h" +#include "KeyboardEdit.h" +#include "ResizeDlg.h" + +///////////////////////////////////////////////////////////////////////////// +// AccelEditor dialog + +class AccelEditor : public ResizeDlg +{ + // Construction + public: + CAcceleratorManager mgr; + void InitCommands(); + AccelEditor(CWnd* pParent = NULL); // standard constructor + + // Dialog Data + //{{AFX_DATA(AccelEditor) + enum { IDD = IDD_ACCEL_EDITOR }; + CListBox m_currents; + CStatic m_alreadyAffected; + CListBox m_commands; + CKeyboardEdit m_key; + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(AccelEditor) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + + // Implementation + protected: + + // Generated message map functions + //{{AFX_MSG(AccelEditor) + virtual BOOL OnInitDialog(); + afx_msg void OnCancel(); + afx_msg void OnOk(); + afx_msg void OnSelchangeCommands(); + afx_msg void OnReset(); + afx_msg void OnAssign(); + afx_msg void OnRemove(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + + //{{AFX_INSERT_LOCATION}} + // Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_ACCELEDITOR_H__66F5C854_E28E_40D1_B763_1850374B46A2__INCLUDED_) diff --git a/src/win32/AcceleratorManager.cpp b/src/win32/AcceleratorManager.cpp new file mode 100644 index 0000000..a3ae0ce --- /dev/null +++ b/src/win32/AcceleratorManager.cpp @@ -0,0 +1,783 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 1998 by Thierry Maurel +// All rights reserved +// +// Distribute freely, except: don't remove my name from the source or +// documentation (don't take credit for my work), mark your changes (don't +// get me blamed for your possible bugs), don't alter or remove this +// notice. +// No warrantee of any kind, express or implied, is included with this +// software; use at your own risk, responsibility for damages (if any) to +// anyone resulting from the use of this software rests entirely with the +// user. +// +// Send bug reports, bug fixes, enhancements, requests, flames, etc., and +// I'll try to keep a version up to date. I can be reached as follows: +// tmaurel@caramail.com (or tmaurel@hol.fr) +// +//////////////////////////////////////////////////////////////////////////////// +// File : AcceleratorManager.cpp +// Project : AccelsEditor +//////////////////////////////////////////////////////////////////////////////// +// Version : 1.0 * Author : T.Maurel +// Date : 17.08.98 +// +// Remarks : implementation of the CAcceleratorManager class. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "VBA.h" + +#include "AcceleratorManager.h" +#include "Reg.h" + +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif + + + +////////////////////////////////////////////////////////////////////// +// Constructor/Destructor +////////////////////////////////////////////////////////////////////// +// +// +CAcceleratorManager::CAcceleratorManager() +{ + m_hRegKey = HKEY_CURRENT_USER; + m_szRegKey = ""; + m_bAutoSave = FALSE; + m_pWndConnected = NULL; + + m_bDefaultTable = false; +} + + +////////////////////////////////////////////////////////////////////// +// +// +CAcceleratorManager::~CAcceleratorManager() +{ + if ((m_bAutoSave == true) && (m_szRegKey.IsEmpty() != FALSE)) { + // bool bRet = Write(); + // if (!bRet) + // systemMessage(0, "CAcceleratorManager::~CAcceleratorManager\nError in CAcceleratorManager::Write..."); + } + + Reset(); +} + + +////////////////////////////////////////////////////////////////////// +// Internal fcts +////////////////////////////////////////////////////////////////////// +// +// +void CAcceleratorManager::Reset() +{ + CCmdAccelOb* pCmdAccel; + WORD wKey; + POSITION pos = m_mapAccelTable.GetStartPosition(); + while (pos != NULL) { + m_mapAccelTable.GetNextAssoc(pos, wKey, pCmdAccel); + delete pCmdAccel; + } + m_mapAccelTable.RemoveAll(); + m_mapAccelString.RemoveAll(); + + pos = m_mapAccelTableSaved.GetStartPosition(); + while (pos != NULL) { + m_mapAccelTableSaved.GetNextAssoc(pos, wKey, pCmdAccel); + delete pCmdAccel; + } + m_mapAccelTableSaved.RemoveAll(); +} + + +////////////////////////////////////////////////////////////////////// +// +// +bool CAcceleratorManager::AddAccel(BYTE cVirt, WORD wIDCommand, WORD wKey, LPCTSTR szCommand, bool bLocked) +{ + ASSERT(szCommand != NULL); + + WORD wIDCmd; + if (m_mapAccelString.Lookup(szCommand, wIDCmd) == TRUE) { + if (wIDCmd != wIDCommand) + return false; + } + + CCmdAccelOb* pCmdAccel = NULL; + if (m_mapAccelTable.Lookup(wIDCommand, pCmdAccel) == TRUE) { + if (pCmdAccel->m_szCommand != szCommand) { + return false; + } + CAccelsOb* pAccel; + POSITION pos = pCmdAccel->m_Accels.GetHeadPosition(); + while (pos != NULL) { + pAccel = pCmdAccel->m_Accels.GetNext(pos); + if (pAccel->m_cVirt == cVirt && + pAccel->m_wKey == wKey) + return FALSE; + } + // Adding the accelerator + pCmdAccel->Add(cVirt, wKey, bLocked); + + } else { + pCmdAccel = new CCmdAccelOb(cVirt, wIDCommand, wKey, szCommand, bLocked); + ASSERT(pCmdAccel != NULL); + m_mapAccelTable.SetAt(wIDCommand, pCmdAccel); + } + // 2nd table + m_mapAccelString.SetAt(szCommand, wIDCommand); + return true; +} + + +////////////////////////////////////////////////////////////////////// +// Debug fcts +////////////////////////////////////////////////////////////////////// +// +// +#ifdef _DEBUG +void CAcceleratorManager::AssertValid() const +{ +} + +////////////////////////////////////////////////////////////////////// +// +// +void CAcceleratorManager::Dump(CDumpContext& dc) const +{ + CCmdAccelOb* pCmdAccel; + WORD wKey; + dc << "CAcceleratorManager::Dump :\n"; + dc << "m_mapAccelTable :\n"; + POSITION pos = m_mapAccelTable.GetStartPosition(); + while (pos != NULL) { + m_mapAccelTable.GetNextAssoc(pos, wKey, pCmdAccel); + dc << "a CCmdAccelOb at 0x" << (void*)pCmdAccel << " = {\n"; + dc << pCmdAccel; + dc << "}\n"; + } + dc << "\nm_mapAccelTableSaved\n"; + pos = m_mapAccelTableSaved.GetStartPosition(); + while (pos != NULL) { + m_mapAccelTableSaved.GetNextAssoc(pos, wKey, pCmdAccel); + dc << "a CCmdAccelOb at 0x" << (void*)pCmdAccel << " = {\n"; + dc << pCmdAccel; + dc << "}\n"; + } +} +#endif + + +////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////// +// +// +void CAcceleratorManager::Connect(CWnd* pWnd, bool bAutoSave) +{ + ASSERT(m_pWndConnected == NULL); + m_pWndConnected = pWnd; + m_bAutoSave = bAutoSave; +} + + +////////////////////////////////////////////////////////////////////// +// +// +bool CAcceleratorManager::GetRegKey(HKEY& hRegKey, CString& szRegKey) +{ + if (m_szRegKey.IsEmpty()) + return false; + + hRegKey = m_hRegKey; + szRegKey = m_szRegKey; + return true; +} + + +////////////////////////////////////////////////////////////////////// +// +// +bool CAcceleratorManager::SetRegKey(HKEY hRegKey, LPCTSTR szRegKey) +{ + ASSERT(hRegKey != NULL); + ASSERT(szRegKey != NULL); + + m_szRegKey = szRegKey; + m_hRegKey = hRegKey; + return true; +} + + +////////////////////////////////////////////////////////////////////// +// Update the application's ACCELs table +////////////////////////////////////////////////////////////////////// +// +// +bool CAcceleratorManager::UpdateWndTable() +{ + int iLoop = 0; + CTypedPtrArray arrayACCEL; + + CCmdAccelOb* pCmdAccel; + WORD wKey; + LPACCEL pACCEL; + CAccelsOb* pAccelOb; + POSITION pos = m_mapAccelTable.GetStartPosition(); + while (pos != NULL) { + m_mapAccelTable.GetNextAssoc(pos, wKey, pCmdAccel); + POSITION pos = pCmdAccel->m_Accels.GetHeadPosition(); + while (pos != NULL) { + pAccelOb = pCmdAccel->m_Accels.GetNext(pos); + + pACCEL = new ACCEL; + ASSERT(pACCEL != NULL); + pACCEL->fVirt = pAccelOb->m_cVirt; + pACCEL->key = pAccelOb->m_wKey; + pACCEL->cmd = pCmdAccel->m_wIDCommand; + arrayACCEL.Add(pACCEL); + } + } + + INT_PTR nAccel = arrayACCEL.GetSize(); + LPACCEL lpAccel = (LPACCEL)LocalAlloc(LPTR, nAccel * sizeof(ACCEL)); + if (!lpAccel) { + for (iLoop = 0; iLoop < nAccel; iLoop++) + delete arrayACCEL.GetAt(iLoop); + arrayACCEL.RemoveAll(); + + return false; + } + + for (iLoop = 0; iLoop < nAccel; iLoop++) { + + pACCEL = arrayACCEL.GetAt(iLoop); + lpAccel[iLoop].fVirt = pACCEL->fVirt; + lpAccel[iLoop].key = pACCEL->key; + lpAccel[iLoop].cmd = pACCEL->cmd; + + delete pACCEL; + } + arrayACCEL.RemoveAll(); + + HACCEL hNewTable = CreateAcceleratorTable(lpAccel, (int)nAccel); + if (!hNewTable) { + ::LocalFree(lpAccel); + return false; + } + HACCEL hOldTable = theApp.hAccel; + if (!::DestroyAcceleratorTable(hOldTable)) { + ::LocalFree(lpAccel); + return false; + } + theApp.hAccel = hNewTable; + ::LocalFree(lpAccel); + + UpdateMenu(GetMenu(*AfxGetApp()->m_pMainWnd)); + + return true; +} + + +////////////////////////////////////////////////////////////////////// +// Create/Destroy accelerators +////////////////////////////////////////////////////////////////////// +// +// +bool CAcceleratorManager::DeleteAccel(BYTE cVirt, WORD wIDCommand, WORD wKey) +{ + CCmdAccelOb* pCmdAccel = NULL; + if (m_mapAccelTable.Lookup(wIDCommand, pCmdAccel) == TRUE) { + POSITION pos = pCmdAccel->m_Accels.GetHeadPosition(); + POSITION PrevPos; + CAccelsOb* pAccel = NULL; + while (pos != NULL) { + PrevPos = pos; + pAccel = pCmdAccel->m_Accels.GetNext(pos); + if (pAccel->m_bLocked == true) + return false; + + if (pAccel->m_cVirt == cVirt && pAccel->m_wKey == wKey) { + pCmdAccel->m_Accels.RemoveAt(PrevPos); + delete pAccel; + return true; + } + } + } + return false; +} + + +////////////////////////////////////////////////////////////////////// +// +// +bool CAcceleratorManager::DeleteEntry(WORD wIDCommand) +{ + CCmdAccelOb* pCmdAccel = NULL; + VERIFY(m_mapAccelTable.Lookup(wIDCommand, pCmdAccel) == TRUE); + + CAccelsOb* pAccel; + POSITION pos = pCmdAccel->m_Accels.GetHeadPosition(); + while (pos != NULL) { + pAccel = pCmdAccel->m_Accels.GetNext(pos); + if (pAccel->m_bLocked == true) + return false; + } + m_mapAccelString.RemoveKey(pCmdAccel->m_szCommand); + m_mapAccelTable.RemoveKey(wIDCommand); + delete pCmdAccel; + + return true; +} + + +////////////////////////////////////////////////////////////////////// +// +// +bool CAcceleratorManager::DeleteEntry(LPCTSTR szCommand) +{ + ASSERT(szCommand != NULL); + + WORD wIDCommand; + if (m_mapAccelString.Lookup(szCommand, wIDCommand) == TRUE) { + return DeleteEntry(wIDCommand); + } + return true; +} + + +////////////////////////////////////////////////////////////////////// +// +// +bool CAcceleratorManager::SetAccel(BYTE cVirt, WORD wIDCommand, WORD wKey, LPCTSTR szCommand, bool bLocked) +{ + ASSERT(szCommand != NULL); + + return AddAccel(cVirt, wIDCommand, wKey, szCommand, bLocked); +} + + +////////////////////////////////////////////////////////////////////// +// +// +bool CAcceleratorManager::AddCommandAccel(WORD wIDCommand, LPCTSTR szCommand, bool bLocked) +{ + ASSERT(szCommand != NULL); + + ASSERT(m_pWndConnected != NULL); + HACCEL hOriginalTable = theApp.hAccel; + + int nAccel = ::CopyAcceleratorTable(hOriginalTable, NULL, 0); + LPACCEL lpAccel = (LPACCEL)LocalAlloc(LPTR, (nAccel) * sizeof(ACCEL)); + if (!lpAccel) + return false; + ::CopyAcceleratorTable(hOriginalTable, lpAccel, nAccel); + + bool bRet = false; + for (int i = 0; i < nAccel; i++) { + if (lpAccel[i].cmd == wIDCommand) + bRet = AddAccel(lpAccel[i].fVirt, wIDCommand, lpAccel[i].key, szCommand, bLocked); + } + ::LocalFree(lpAccel); + return bRet; +} + + +////////////////////////////////////////////////////////////////////// +// +// +bool CAcceleratorManager::CreateEntry(WORD wIDCommand, LPCTSTR szCommand) +{ + ASSERT(szCommand != NULL); + + WORD wIDDummy; + if (m_mapAccelString.Lookup(szCommand, wIDDummy) == TRUE) + return false; + + CCmdAccelOb* pCmdAccel = new CCmdAccelOb(wIDCommand, szCommand); + ASSERT(pCmdAccel != NULL); + m_mapAccelTable.SetAt(wIDCommand, pCmdAccel); + m_mapAccelString.SetAt(szCommand, wIDCommand); + + return false; +} + + +////////////////////////////////////////////////////////////////////// +// Get a string from the ACCEL definition +////////////////////////////////////////////////////////////////////// +// +// +bool CAcceleratorManager::GetStringFromACCEL(ACCEL* pACCEL, CString& szAccel) +{ + ASSERT(pACCEL != NULL); + + CAccelsOb accel(pACCEL); + accel.GetString(szAccel); + + if (szAccel.IsEmpty()) + return false; + else + return true; +} + + +////////////////////////////////////////////////////////////////////// +// +// +bool CAcceleratorManager::GetStringFromACCEL(BYTE cVirt, WORD nCode, CString& szAccel) +{ + CAccelsOb accel(cVirt, nCode); + accel.GetString(szAccel); + + if (szAccel.IsEmpty()) + return false; + else + return true; +} + + +////////////////////////////////////////////////////////////////////// +// Copy function +// +CAcceleratorManager& CAcceleratorManager::operator=(const CAcceleratorManager& accelmgr) +{ + Reset(); + + CCmdAccelOb* pCmdAccel; + CCmdAccelOb* pNewCmdAccel; + WORD wKey; + // Copy the 2 tables : normal accel table... + POSITION pos = accelmgr.m_mapAccelTable.GetStartPosition(); + while (pos != NULL) { + accelmgr.m_mapAccelTable.GetNextAssoc(pos, wKey, pCmdAccel); + pNewCmdAccel = new CCmdAccelOb; + ASSERT(pNewCmdAccel != NULL); + *pNewCmdAccel = *pCmdAccel; + m_mapAccelTable.SetAt(wKey, pNewCmdAccel); + } + // ... and saved accel table. + pos = accelmgr.m_mapAccelTableSaved.GetStartPosition(); + while (pos != NULL) { + accelmgr.m_mapAccelTableSaved.GetNextAssoc(pos, wKey, pCmdAccel); + pNewCmdAccel = new CCmdAccelOb; + ASSERT(pNewCmdAccel != NULL); + *pNewCmdAccel = *pCmdAccel; + m_mapAccelTableSaved.SetAt(wKey, pNewCmdAccel); + } + + // The Strings-ID table + CString szKey; + pos = accelmgr.m_mapAccelString.GetStartPosition(); + while (pos != NULL) { + accelmgr.m_mapAccelString.GetNextAssoc(pos, szKey, wKey); + m_mapAccelString.SetAt(szKey, wKey); + } + m_bDefaultTable = accelmgr.m_bDefaultTable; + + return *this; +} + +void CAcceleratorManager::UpdateMenu(HMENU menu) +{ + int count = GetMenuItemCount(menu); + + OSVERSIONINFO info; + info.dwOSVersionInfoSize = sizeof(info); + GetVersionEx(&info); + + if(info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) { + MENUITEMINFO info; + char ss[128]; + ZeroMemory(&info, sizeof(info)); + info.cbSize = sizeof(info) - sizeof(HBITMAP); + info.fMask = MIIM_ID | MIIM_SUBMENU; + for(int i = 0; i < count; i++) { + GetMenuItemInfo(menu, i, TRUE, &info); + + if(info.hSubMenu != NULL) { + UpdateMenu(info.hSubMenu); + } else { + if(info.wID != (UINT)-1) { + MENUITEMINFO info2; + ZeroMemory(&info2, sizeof(info2)); + info2.cbSize = sizeof(info2) - sizeof(HBITMAP); + info2.fMask = MIIM_STRING; + info2.dwTypeData = ss; + info2.cch = 128; + GetMenuItemInfo(menu, i, MF_BYPOSITION, &info2); + CString str = ss; + int index = str.Find('\t'); + if(index != -1) + str = str.Left(index); + + WORD command = info.wID; + + CCmdAccelOb *o; + if(m_mapAccelTable.Lookup(command, o)) { + if(o->m_Accels.GetCount()) { + POSITION pos = o->m_Accels.GetHeadPosition(); + CAccelsOb *accel = o->m_Accels.GetNext(pos); + + CString s; + accel->GetString(s); + str += "\t"; + str += s; + } + } + if(str != ss) + ModifyMenu(menu, i, MF_BYPOSITION | MF_STRING, info.wID, str); + } + } + } + } else { + MENUITEMINFO info; + wchar_t ss[128]; + wchar_t str[512]; + + ZeroMemory(&info, sizeof(info)); + info.cbSize = sizeof(info); + info.fMask = MIIM_ID | MIIM_SUBMENU; + for(int i = 0; i < count; i++) { + GetMenuItemInfo(menu, i, TRUE, &info); + + if(info.hSubMenu != NULL) { + UpdateMenu(info.hSubMenu); + } else { + if(info.wID != (WORD)-1) { + MENUITEMINFOW info2; + ZeroMemory(&info2, sizeof(info2)); + info2.cbSize = sizeof(info2); + info2.fMask = MIIM_STRING; + info2.dwTypeData = ss; + info2.cch = 128; + GetMenuItemInfoW(menu, i, MF_BYPOSITION, &info2); + + wcscpy(str, ss); + + wchar_t *p = wcschr(str, '\t'); + if(p) + *p = 0; + + CCmdAccelOb *o; + WORD command = info.wID; + if(m_mapAccelTable.Lookup(command, o)) { + if(o->m_Accels.GetCount()) { + POSITION pos = o->m_Accels.GetHeadPosition(); + + CAccelsOb *accel = o->m_Accels.GetNext(pos); + + CString s; + accel->GetString(s); + + wchar_t temp[128]; + temp[0] = '\t'; + temp[1] = 0; + wcscat(str, temp); + p = temp; + for(const char *sp = s; *sp; sp++) + *p++ = *sp; + *p = 0; + wcscat(str, temp); + } + } + if(wcscmp(str,ss)) + ModifyMenuW(menu, i, MF_BYPOSITION | MF_STRING, info.wID, str); + } + } + } + } +} + +////////////////////////////////////////////////////////////////////// +// In/Out to the registry +////////////////////////////////////////////////////////////////////// +// +// +bool CAcceleratorManager::Load(HKEY hRegKey, LPCTSTR szRegKey) +{ + ASSERT(szRegKey != NULL); + + m_hRegKey = hRegKey; + m_szRegKey = szRegKey; + + DWORD data[2048/sizeof(DWORD)]; + + DWORD len = sizeof(data); + if(regQueryBinaryValue("keyboard", (char *)data, len)) { + int count = len/sizeof(DWORD); + + CCmdAccelOb* pCmdAccel; + CAccelsOb* pAccel; + DWORD dwIDAccelData, dwAccelData; + BOOL bExistID; + int iIndex = 0; + if(count) { + WORD wKey; + POSITION pos = m_mapAccelTable.GetStartPosition(); + + while(pos != NULL) { + m_mapAccelTable.GetNextAssoc(pos, wKey, pCmdAccel); + pCmdAccel->DeleteUserAccels(); + } + + while(iIndex < count) { + dwIDAccelData = data[iIndex++]; + + WORD wIDCommand = LOWORD(dwIDAccelData); + bExistID = m_mapAccelTable.Lookup(wIDCommand, pCmdAccel); + + if (bExistID) { + pCmdAccel->DeleteUserAccels(); + } + for (int j = 0; j < HIWORD(dwIDAccelData) && iIndex < count; j++) { + dwAccelData = data[iIndex++]; + if (bExistID) { + pAccel = new CAccelsOb; + ASSERT(pAccel != NULL); + pAccel->SetData(dwAccelData); + pCmdAccel->Add(pAccel); + } + } + } + } + UpdateWndTable(); + return true; + } + return false; +} + + +////////////////////////////////////////////////////////////////////// +// +// +bool CAcceleratorManager::Load() +{ + BOOL bRet = FALSE; + if (!m_szRegKey.IsEmpty()) + bRet = Load(m_hRegKey, m_szRegKey); + + if (bRet == TRUE) + return true; + else + return false; +} + + +////////////////////////////////////////////////////////////////////// +// +// +bool CAcceleratorManager::Write() +{ + CDWordArray AccelsDatasArray; + CDWordArray CmdDatasArray; + + int iCount = 0; + CCmdAccelOb* pCmdAccel; + CAccelsOb* pAccel; + DWORD dwAccelData; + + WORD wKey; + POSITION pos = m_mapAccelTable.GetStartPosition(); + while (pos != NULL) { + m_mapAccelTable.GetNextAssoc(pos, wKey, pCmdAccel); + CmdDatasArray.RemoveAll(); + + POSITION pos = pCmdAccel->m_Accels.GetHeadPosition(); + while (pos != NULL) { + pAccel = pCmdAccel->m_Accels.GetNext(pos); + // if (!pAccel->m_bLocked) { + dwAccelData = pAccel->GetData(); + CmdDatasArray.Add(dwAccelData); + // } + } + + if (CmdDatasArray.GetSize() > 0) { + CmdDatasArray.InsertAt(0, MAKELONG(pCmdAccel->m_wIDCommand, CmdDatasArray.GetSize())); + + AccelsDatasArray.Append(CmdDatasArray); + iCount++; + } + } + // AccelsDatasArray.InsertAt(0, MAKELONG(65535, iCount)); + + INT_PTR count = AccelsDatasArray.GetSize(); + DWORD *data = (DWORD *)malloc(count * sizeof(DWORD)); + ASSERT(data != NULL); + + for(int index = 0; index < count; index++) + data[index] = AccelsDatasArray[index]; + + regSetBinaryValue("keyboard", (char *)data, (int)(count*sizeof(DWORD))); + + AccelsDatasArray.RemoveAll(); + CmdDatasArray.RemoveAll(); + + free(data); + + return true; +} + + +////////////////////////////////////////////////////////////////////// +// Defaults values management. +////////////////////////////////////////////////////////////////////// +// +// +bool CAcceleratorManager::CreateDefaultTable() +{ + if (m_bDefaultTable) + return false; + + CCmdAccelOb* pCmdAccel; + CCmdAccelOb* pNewCmdAccel; + + CAccelsOb* pAccel; + CAccelsOb* pNewAccel; + + WORD wKey; + POSITION pos = m_mapAccelTable.GetStartPosition(); + while (pos != NULL) { + m_mapAccelTable.GetNextAssoc(pos, wKey, pCmdAccel); + pNewCmdAccel = new CCmdAccelOb; + ASSERT(pNewCmdAccel != NULL); + + POSITION pos = pCmdAccel->m_Accels.GetHeadPosition(); + while (pos != NULL) { + pAccel = pCmdAccel->m_Accels.GetNext(pos); + if (!pAccel->m_bLocked) { + pNewAccel = new CAccelsOb; + ASSERT(pNewAccel != NULL); + + *pNewAccel = *pAccel; + pNewCmdAccel->m_Accels.AddTail(pNewAccel); + } + } + if (pNewCmdAccel->m_Accels.GetCount() != 0) { + pNewCmdAccel->m_wIDCommand = pCmdAccel->m_wIDCommand; + pNewCmdAccel->m_szCommand = pCmdAccel->m_szCommand; + + m_mapAccelTableSaved.SetAt(wKey, pNewCmdAccel); + } else + delete pNewCmdAccel; + } + + m_bDefaultTable = true; + return true; +} + + +////////////////////////////////////////////////////////////////////// +// +// +bool CAcceleratorManager::Default() +{ + return true; +} diff --git a/src/win32/AcceleratorManager.h b/src/win32/AcceleratorManager.h new file mode 100644 index 0000000..30703a6 --- /dev/null +++ b/src/win32/AcceleratorManager.h @@ -0,0 +1,143 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 1998 by Thierry Maurel +// All rights reserved +// +// Distribute freely, except: don't remove my name from the source or +// documentation (don't take credit for my work), mark your changes (don't +// get me blamed for your possible bugs), don't alter or remove this +// notice. +// No warrantee of any kind, express or implied, is included with this +// software; use at your own risk, responsibility for damages (if any) to +// anyone resulting from the use of this software rests entirely with the +// user. +// +// Send bug reports, bug fixes, enhancements, requests, flames, etc., and +// I'll try to keep a version up to date. I can be reached as follows: +// tmaurel@caramail.com (or tmaurel@hol.fr) +// +//////////////////////////////////////////////////////////////////////////////// +// File : AcceleratorManager.h +// Project : AccelsEditor +//////////////////////////////////////////////////////////////////////////////// +// Version : 1.0 * Author : T.Maurel +// Date : 17.08.98 +// +// Remarks : interface for the CAcceleratorManager class. +// +//////////////////////////////////////////////////////////////////////////////// +#if !defined(AFX_ACCELERATORMANAGER_H__A6D76F4B_26C6_11D2_BE72_006097AC8D00__INCLUDED_) +#define AFX_ACCELERATORMANAGER_H__A6D76F4B_26C6_11D2_BE72_006097AC8D00__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + + + +#include "CmdAccelOb.h" + + +// Helper map +#include // MFC Templates extension +#ifndef CMapStringToWord +typedef CMap< CString, LPCSTR, WORD, WORD& > CMapStringToWord; +#endif + +#ifndef CMapWordToCCmdAccelOb +typedef CMap< WORD, WORD&, CCmdAccelOb*, CCmdAccelOb*& > CMapWordToCCmdAccelOb; +#endif + + +////////////////////////////////////////////////////////////////////// +// +// +class CAcceleratorManager : public CObject { + friend class AccelEditor; + public: + CAcceleratorManager(); + virtual ~CAcceleratorManager(); + + // Operations + public: + void UpdateMenu(HMENU menu); + void UpdateMenu(); + // Connection to the main application wnd + void Connect(CWnd *pWnd, bool bAutoSave = true); + // In/Out with the registry + bool Load(HKEY hRegKey, LPCTSTR szRegKey); + bool Load(); + bool Write(); + // Get the initials accels, not the user's + bool Default(); + // Save a copy in the 2 maps called xxxSaved, which are used in case + // of Default(), to reload the defaults accels. + bool CreateDefaultTable(); + bool IsDefaultTableAvailable() {return m_bDefaultTable;} + bool IsMapStringCommandsEmpty() { + if (m_mapAccelString.IsEmpty()) + return true; + else + return false; + } + + // Registry access configuration + bool GetRegKey(HKEY& hRegKey, CString &szRegKey); + bool SetRegKey(HKEY hRegKey, LPCTSTR szRegKey); + bool IsAutoSave() {return m_bAutoSave;} + void SetAutoSave(bool bAutoSave) {m_bAutoSave = bAutoSave;} + + // Helper fct, used for new menus strings + bool GetStringFromACCEL(ACCEL* pACCEL, CString& szAccel); + bool GetStringFromACCEL(BYTE cVirt, WORD nCode, CString& szAccel); + + // Update the ACCELS table in the application, from the specified + // datas in the manager. + bool UpdateWndTable(); + + // Modification helper fcts + bool SetAccel(BYTE cVirt, WORD wIDCommand, WORD wNewCaract, + LPCTSTR szCommand, bool bLocked = false); + bool AddCommandAccel(WORD wIDCommand, LPCTSTR szCommand, bool bLocked = true); + bool CreateEntry(WORD wIDCommand, LPCTSTR szCommand); + + bool DeleteEntry(LPCTSTR szCommand); + bool DeleteEntry(WORD wIDCommand); + bool DeleteAccel(BYTE cVirt, WORD wIDCommand, WORD wNewCaract); + + // Affectation operator + CAcceleratorManager& operator=(const CAcceleratorManager& accelmgr); + + public: +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + + protected: + // Erase all the datas + void Reset(); + // Internal affect fct. + bool AddAccel(BYTE cVirt, WORD wIDCommand, WORD wKey, + LPCTSTR szCommand, bool bLocked); + + // Attributes + protected: + CWnd *m_pWndConnected; + + // User datas + CMapStringToWord m_mapAccelString; + CMapWordToCCmdAccelOb m_mapAccelTable; + // Default datas + CMapWordToCCmdAccelOb m_mapAccelTableSaved; + bool m_bDefaultTable; + + // Where the users datas will be saved in the registry + HKEY m_hRegKey; + CString m_szRegKey; + // if true, there is an auto-save in the registry, when the destructor is called + bool m_bAutoSave; + +}; + + +#endif // !defined(AFX_ACCELERATORMANAGER_H__A6D76F4B_26C6_11D2_BE72_006097AC8D00__INCLUDED_) diff --git a/src/win32/Associate.cpp b/src/win32/Associate.cpp new file mode 100644 index 0000000..a7bc740 --- /dev/null +++ b/src/win32/Associate.cpp @@ -0,0 +1,112 @@ +#include "stdafx.h" +#include "vba.h" +#include "Associate.h" +#include "Reg.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// Associate dialog + + +Associate::Associate(CWnd* pParent /*=NULL*/) + : CDialog(Associate::IDD, pParent) +{ + //{{AFX_DATA_INIT(Associate) + m_agb = FALSE; + m_bin = FALSE; + m_cgb = FALSE; + m_dmg = FALSE; + m_gb = FALSE; + m_gba = FALSE; + m_gbc = FALSE; + m_sgb = FALSE; + //}}AFX_DATA_INIT +} + + +void Associate::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(Associate) + DDX_Check(pDX, IDC_AGB, m_agb); + DDX_Check(pDX, IDC_BIN, m_bin); + DDX_Check(pDX, IDC_CGB, m_cgb); + DDX_Check(pDX, IDC_DMG, m_dmg); + DDX_Check(pDX, IDC_GB, m_gb); + DDX_Check(pDX, IDC_GBA, m_gba); + DDX_Check(pDX, IDC_GBC, m_gbc); + DDX_Check(pDX, IDC_SGB, m_sgb); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(Associate, CDialog) + //{{AFX_MSG_MAP(Associate) + ON_BN_CLICKED(ID_CANCEL, OnCancel) + ON_BN_CLICKED(ID_OK, OnOk) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// Associate message handlers + +BOOL Associate::OnInitDialog() +{ + CDialog::OnInitDialog(); + + CenterWindow(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void Associate::OnCancel() +{ + EndDialog(FALSE); +} + +void Associate::OnOk() +{ + UpdateData(); + + int mask = 0; + if(m_gb) + mask |= 1; + if(m_dmg) + mask |= 2; + if(m_sgb) + mask |= 4; + if(m_cgb) + mask |= 8; + if(m_gbc) + mask |= 16; + if(m_gba) + mask |= 32; + if(m_agb) + mask |= 64; + if(m_bin) + mask |= 128; + if(mask) { + char applicationPath[2048]; + CString commandPath; + LPCTSTR types[] = { "*.dmg", ".gb", ".sgb", ".cgb", ".gbc", ".gba", ".agb", ".bin" }; + GetModuleFileName(NULL, applicationPath, 2048); + commandPath.Format("\"%s\" \"%%1\"", applicationPath); + regAssociateType("VisualBoyAdvance.Binary", + "Binary", + commandPath, + "%SystemRoot%\\system32\\SHELL32.dll,-13"); + + for(int i = 0; i < 8; i++) { + if(mask & (1< 1000 +#pragma once +#endif // _MSC_VER > 1000 +// Associate.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// Associate dialog + +class Associate : public CDialog +{ + // Construction + public: + Associate(CWnd* pParent = NULL); // standard constructor + + // Dialog Data + //{{AFX_DATA(Associate) + enum { IDD = IDD_ASSOCIATIONS }; + BOOL m_agb; + BOOL m_bin; + BOOL m_cgb; + BOOL m_dmg; + BOOL m_gb; + BOOL m_gba; + BOOL m_gbc; + BOOL m_sgb; + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(Associate) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + + // Implementation + protected: + + // Generated message map functions + //{{AFX_MSG(Associate) + virtual BOOL OnInitDialog(); + afx_msg void OnCancel(); + afx_msg void OnOk(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + + //{{AFX_INSERT_LOCATION}} + // Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_ASSOCIATE_H__3326525B_B405_40A7_82C4_B2594669A930__INCLUDED_) diff --git a/src/win32/AudioCoreSettingsDlg.cpp b/src/win32/AudioCoreSettingsDlg.cpp new file mode 100644 index 0000000..373c06b --- /dev/null +++ b/src/win32/AudioCoreSettingsDlg.cpp @@ -0,0 +1,191 @@ +#include "stdafx.h" +#include "VBA.h" + +#include "AudioCoreSettingsDlg.h" + +#define MIN_VOLUME 0.0f +#define MAX_VOLUME 4.0f + + +// AudioCoreSettingsDlg dialog + +IMPLEMENT_DYNAMIC(AudioCoreSettingsDlg, CDialog) + +AudioCoreSettingsDlg::AudioCoreSettingsDlg(CWnd* pParent /*=NULL*/) + : CDialog(AudioCoreSettingsDlg::IDD, pParent) + , m_enabled( false ) + , m_surround( false ) + , m_declicking( false ) + , m_sound_interpolation( false ) + , m_echo( 0.0f ) + , m_stereo( 0.0f ) + , m_volume( 0.0f ) + , m_sound_filtering( 0.0f ) + , m_sample_rate( 0 ) + , toolTip( NULL ) +{ +} + +AudioCoreSettingsDlg::~AudioCoreSettingsDlg() +{ + delete toolTip; +} + +void AudioCoreSettingsDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + + DDX_Control(pDX, IDC_ENHANCE_SOUND, enhance_sound); + DDX_Control(pDX, IDC_SURROUND, surround); + DDX_Control(pDX, IDC_ECHO, echo); + DDX_Control(pDX, IDC_STEREO, stereo); + DDX_Control(pDX, IDC_VOLUME, volume); + DDX_Control(pDX, IDC_DECLICKING, declicking); + DDX_Control(pDX, IDC_SOUND_INTERPOLATION, sound_interpolation); + DDX_Control(pDX, IDC_SOUND_FILTERING, sound_filtering); + DDX_Control(pDX, IDC_SAMPLE_RATE, sample_rate); + + if( pDX->m_bSaveAndValidate == TRUE ) { + m_enabled = BST_CHECKED == enhance_sound.GetCheck(); + m_surround = BST_CHECKED == surround.GetCheck(); + m_declicking = BST_CHECKED == declicking.GetCheck(); + m_sound_interpolation = BST_CHECKED == sound_interpolation.GetCheck(); + m_echo = (float)echo.GetPos() / 100.0f; + m_stereo = (float)stereo.GetPos() / 100.0f; + m_volume = (float)volume.GetPos() / 100.0f; + m_sound_filtering = (float)sound_filtering.GetPos() / 100.0f; + m_sample_rate = (unsigned int)sample_rate.GetItemData( sample_rate.GetCurSel() ); + } +} + +BOOL AudioCoreSettingsDlg::OnTtnNeedText(UINT id, NMHDR *pNMHDR, LRESULT *pResult) +{ + TOOLTIPTEXT *t3 = (TOOLTIPTEXT *)pNMHDR; // dirty Windows API + BOOL i_provided_tooltip_with_text = TRUE; + + if( !( t3->uFlags & TTF_IDISHWND ) ) { + return FALSE; + } + // even dirtier Windows API: + // t3->hdr.idFrom is actually a HWND, holy cow, why? + // The other case does not even occur. + int controlID = ::GetDlgCtrlID( (HWND)t3->hdr.idFrom ); + CString res; + TCHAR buf[0x400]; // Use own string buffer because szText has an 80 char limit. + // We can't use a dynamic buffer size because Windows does some shady things with + // t3->lpszText at the end of this function, so we have no chance to free the buffer + // before the end of this function. + + switch( controlID ) { + case IDC_VOLUME: + _stprintf_s( t3->szText, _countof( t3->szText ), _T( "%i%%" ), volume.GetPos() ); + break; + case IDC_ECHO: + _stprintf_s( t3->szText, _countof( t3->szText ), _T( "%i%%" ), echo.GetPos() ); + break; + case IDC_STEREO: + _stprintf_s( t3->szText, _countof( t3->szText ), _T( "%i%%" ), stereo.GetPos() ); + break; + case IDC_SOUND_FILTERING: + _stprintf_s( t3->szText, _countof( t3->szText ), _T( "%i%%" ), sound_filtering.GetPos() ); + break; + case IDC_DEFAULT_VOLUME: + res.LoadString( IDS_TOOLTIP_DEFAULT_VOLUME ); + _tcscpy_s( buf, _countof( buf ), res.GetString() ); + t3->lpszText = buf; + break; + case IDC_ENHANCE_SOUND: + res.LoadString( IDS_TOOLTIP_ENHANCE_SOUND ); + _tcscpy_s( buf, _countof( buf ), res.GetString() ); + t3->lpszText = buf; + break; + case IDC_SURROUND: + res.LoadString( IDS_TOOLTIP_SURROUND ); + _tcscpy_s( buf, _countof( buf ), res.GetString() ); + t3->lpszText = buf; + break; + case IDC_DECLICKING: + res.LoadString( IDS_TOOLTIP_DECLICKING ); + _tcscpy_s( buf, _countof( buf ), res.GetString() ); + t3->lpszText = buf; + break; + default: + i_provided_tooltip_with_text = FALSE; + break; + } + + return i_provided_tooltip_with_text; +} + + +BEGIN_MESSAGE_MAP(AudioCoreSettingsDlg, CDialog) + ON_NOTIFY_EX(TTN_NEEDTEXT, 0, &AudioCoreSettingsDlg::OnTtnNeedText) + ON_BN_CLICKED(IDC_DEFAULT_VOLUME, &AudioCoreSettingsDlg::OnBnClickedDefaultVolume) +END_MESSAGE_MAP() + + +// AudioCoreSettingsDlg message handlers + +BOOL AudioCoreSettingsDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + + // Set up tooltip control + toolTip = new CToolTipCtrl; + toolTip->Create( this ); + toolTip->AddTool( GetDlgItem( IDC_DEFAULT_VOLUME ) ); + toolTip->AddTool( GetDlgItem( IDC_ENHANCE_SOUND ) ); + toolTip->AddTool( GetDlgItem( IDC_SURROUND ) ); + toolTip->AddTool( GetDlgItem( IDC_DECLICKING ) ); + toolTip->Activate( TRUE ); + + enhance_sound.SetCheck( m_enabled ? BST_CHECKED : BST_UNCHECKED ); + + surround.SetCheck( m_surround ? BST_CHECKED : BST_UNCHECKED ); + + declicking.SetCheck( m_declicking ? BST_CHECKED : BST_UNCHECKED ); + + sound_interpolation.SetCheck( m_sound_interpolation ? BST_CHECKED : BST_UNCHECKED ); + + echo.SetRange( 0, 100 ); + echo.SetPos( (int)( m_echo * 100.0f ) ); + + stereo.SetRange( 0, 100 ); + stereo.SetPos( (int)( m_stereo * 100.0f ) ); + + sound_filtering.SetRange( 0, 100 ); + sound_filtering.SetPos( (int)( m_sound_filtering * 100.0f ) ); + + volume.SetRange( (int)( MIN_VOLUME * 100.0f ), (int)( MAX_VOLUME * 100.0f ) ); + volume.SetPos( (int)( m_volume * 100.0f ) ); + + unsigned int rate = 44100; + CString temp; + for( int i = 0 ; i <= 2 ; i++ ) { + temp.Format( _T("%u Hz"), rate ); + int id = sample_rate.AddString( temp.GetString() ); + sample_rate.SetItemData( id, rate ); + if( rate == m_sample_rate ) { + sample_rate.SetCurSel( id ); + } + rate /= 2; + } + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +BOOL AudioCoreSettingsDlg::PreTranslateMessage(MSG* pMsg) +{ + // Required for enabling ToolTips in a modal dialog box. + if( NULL != toolTip ) { + toolTip->RelayEvent( pMsg ); + } + + return CDialog::PreTranslateMessage(pMsg); +} + +void AudioCoreSettingsDlg::OnBnClickedDefaultVolume() +{ + volume.SetPos( 100 ); +} diff --git a/src/win32/AudioCoreSettingsDlg.h b/src/win32/AudioCoreSettingsDlg.h new file mode 100644 index 0000000..4ce69d2 --- /dev/null +++ b/src/win32/AudioCoreSettingsDlg.h @@ -0,0 +1,47 @@ +#pragma once + +// AudioCoreSettingsDlg dialog + +class AudioCoreSettingsDlg : public CDialog +{ + DECLARE_DYNAMIC(AudioCoreSettingsDlg) + +public: + bool m_enabled; + bool m_surround; + bool m_declicking; + bool m_sound_interpolation; + float m_echo; + float m_stereo; + float m_volume; + float m_sound_filtering; + unsigned int m_sample_rate; + + AudioCoreSettingsDlg(CWnd* pParent = NULL); // standard constructor + virtual ~AudioCoreSettingsDlg(); + + virtual BOOL OnInitDialog(); + virtual BOOL PreTranslateMessage(MSG* pMsg); + afx_msg void OnBnClickedDefaultVolume(); + +// Dialog Data + enum { IDD = IDD_AUDIO_CORE_SETTINGS }; + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + afx_msg BOOL OnTtnNeedText(UINT id, NMHDR *pNMHDR, LRESULT *pResult); // Retrieve text for ToolTip + + DECLARE_MESSAGE_MAP() + +private: + CButton enhance_sound; + CButton surround; + CButton declicking; + CButton sound_interpolation; + CSliderCtrl echo; + CSliderCtrl stereo; + CSliderCtrl volume; + CSliderCtrl sound_filtering; + CToolTipCtrl *toolTip; + CComboBox sample_rate; +}; diff --git a/src/win32/BIOSDialog.cpp b/src/win32/BIOSDialog.cpp new file mode 100644 index 0000000..169ba62 --- /dev/null +++ b/src/win32/BIOSDialog.cpp @@ -0,0 +1,138 @@ +#include "stdafx.h" +#include "VBA.h" + +#include "BIOSDialog.h" + + +// BIOSDialog dialog + +IMPLEMENT_DYNAMIC(BIOSDialog, CDialog) + +BIOSDialog::BIOSDialog(CWnd* pParent /*=NULL*/) + : CDialog(BIOSDialog::IDD, pParent) + , m_enableBIOS_GB(FALSE) + , m_enableBIOS_GBA(FALSE) + , m_skipLogo(FALSE) + , m_pathGB(_T("")) + , m_pathGBA(_T("")) +{ +} + +BIOSDialog::~BIOSDialog() +{ +} + +void BIOSDialog::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + DDX_Check(pDX, IDC_ENABLE_GB_BIOS, m_enableBIOS_GB); + DDX_Check(pDX, IDC_ENABLE_GBC_BIOS, m_enableBIOS_GBC); + DDX_Check(pDX, IDC_ENABLE_GBA_BIOS, m_enableBIOS_GBA); + DDX_Check(pDX, IDC_SKIP_BOOT_LOGO, m_skipLogo); + DDX_Text(pDX, IDC_GB_BIOS_PATH, m_pathGB); + DDX_Text(pDX, IDC_GBC_BIOS_PATH, m_pathGBC); + DDX_Text(pDX, IDC_GBA_BIOS_PATH, m_pathGBA); + DDX_Control(pDX, IDC_GB_BIOS_PATH, m_editGB); + DDX_Control(pDX, IDC_GBC_BIOS_PATH, m_editGBC); + DDX_Control(pDX, IDC_GBA_BIOS_PATH, m_editGBA); + + if( pDX->m_bSaveAndValidate == TRUE ) { + // disable BIOS usage when it does not exist + if( !fileExists( m_pathGBA ) ) { + m_enableBIOS_GBA = FALSE; + } + if( !fileExists( m_pathGBC ) ) { + m_enableBIOS_GBC = FALSE; + } + if( !fileExists( m_pathGB ) ) { + m_enableBIOS_GB = FALSE; + } + } +} + + +BEGIN_MESSAGE_MAP(BIOSDialog, CDialog) + ON_BN_CLICKED(IDC_SELECT_GB_BIOS_PATH, &BIOSDialog::OnBnClickedSelectGbBiosPath) + ON_BN_CLICKED(IDC_SELECT_GBC_BIOS_PATH, &BIOSDialog::OnBnClickedSelectGbcBiosPath) + ON_BN_CLICKED(IDC_SELECT_GBA_BIOS_PATH, &BIOSDialog::OnBnClickedSelectGbaBiosPath) +END_MESSAGE_MAP() + + +// BIOSDialog message handlers + +void BIOSDialog::OnBnClickedSelectGbBiosPath() +{ + CString current; + m_editGB.GetWindowText( current ); + if( !fileExists( current ) ) { + current = _T(""); + } + + CFileDialog dlg( + TRUE, + NULL, + current, + OFN_DONTADDTORECENT | OFN_FILEMUSTEXIST, + _T("BIOS Files (*.bin;*.rom)|*.bin;*.rom|All Files (*.*)|*.*||"), + this, + 0 ); + + if( IDOK == dlg.DoModal() ) { + m_editGB.SetWindowText( dlg.GetPathName() ); + } +} + +void BIOSDialog::OnBnClickedSelectGbcBiosPath() +{ + CString current; + m_editGBC.GetWindowText( current ); + if( !fileExists( current ) ) { + current = _T(""); + } + + CFileDialog dlg( + TRUE, + NULL, + current, + OFN_DONTADDTORECENT | OFN_FILEMUSTEXIST, + _T("BIOS Files (*.bin;*.rom)|*.bin;*.rom|All Files (*.*)|*.*||"), + this, + 0 ); + + if( IDOK == dlg.DoModal() ) { + m_editGBC.SetWindowText( dlg.GetPathName() ); + } +} + +void BIOSDialog::OnBnClickedSelectGbaBiosPath() +{ + CString current; + m_editGBA.GetWindowText( current ); + if( !fileExists( current ) ) { + current = _T(""); + } + + CFileDialog dlg( + TRUE, + NULL, + current, + OFN_DONTADDTORECENT | OFN_FILEMUSTEXIST, + _T("BIOS Files (*.bin;*.rom)|*.bin;*.rom|All Files (*.*)|*.*||"), + this, + 0 ); + + if( IDOK == dlg.DoModal() ) { + m_editGBA.SetWindowText( dlg.GetPathName() ); + } +} + +bool BIOSDialog::fileExists(CString& file) +{ + CFileStatus stat; + BOOL retVal = CFile::GetStatus( file, stat ); + bool noFile = false; + if( retVal == TRUE ) { + noFile |= ( stat.m_attribute & CFile::directory ) != 0; + } + return ( retVal == TRUE ) && !noFile; +} diff --git a/src/win32/BIOSDialog.h b/src/win32/BIOSDialog.h new file mode 100644 index 0000000..ea3ced5 --- /dev/null +++ b/src/win32/BIOSDialog.h @@ -0,0 +1,37 @@ +#pragma once + + +// BIOSDialog dialog + +class BIOSDialog : public CDialog +{ + DECLARE_DYNAMIC(BIOSDialog) + +public: + BIOSDialog(CWnd* pParent = NULL); // standard constructor + virtual ~BIOSDialog(); + +// Dialog Data + enum { IDD = IDD_BIOS }; + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + + DECLARE_MESSAGE_MAP() +private: + CEdit m_editGB; + CEdit m_editGBC; + CEdit m_editGBA; + bool fileExists(CString& file); + afx_msg void OnBnClickedSelectGbBiosPath(); + afx_msg void OnBnClickedSelectGbcBiosPath(); + afx_msg void OnBnClickedSelectGbaBiosPath(); +public: + BOOL m_enableBIOS_GB; + BOOL m_enableBIOS_GBC; + BOOL m_enableBIOS_GBA; + BOOL m_skipLogo; + CString m_pathGB; + CString m_pathGBC; + CString m_pathGBA; +}; diff --git a/src/win32/BitmapControl.cpp b/src/win32/BitmapControl.cpp new file mode 100644 index 0000000..5a13ae0 --- /dev/null +++ b/src/win32/BitmapControl.cpp @@ -0,0 +1,266 @@ +#include "stdafx.h" +#include "vba.h" +#include "BitmapControl.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +bool BitmapControl::isRegistered = false; + +///////////////////////////////////////////////////////////////////////////// +// BitmapControl + +IMPLEMENT_DYNCREATE(BitmapControl, CScrollView) + + BitmapControl::BitmapControl() +{ + w = 0; + h = 0; + data = NULL; + bmpInfo = NULL; + stretch = false; + registerClass(); + CSize sizeTotal; + sizeTotal.cx = sizeTotal.cy = 0; + SetScrollSizes(MM_TEXT, sizeTotal); +} + +BitmapControl::~BitmapControl() +{ +} + + +BEGIN_MESSAGE_MAP(BitmapControl, CScrollView) + //{{AFX_MSG_MAP(BitmapControl) + ON_WM_ERASEBKGND() + ON_WM_SIZE() + ON_WM_LBUTTONDOWN() + //}}AFX_MSG_MAP + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// BitmapControl drawing + +void BitmapControl::OnInitialUpdate() +{ + CScrollView::OnInitialUpdate(); + + CSize sizeTotal; + // TODO: calculate the total size of this view + sizeTotal.cx = sizeTotal.cy = 100; + SetScrollSizes(MM_TEXT, sizeTotal); +} + +void BitmapControl::OnDraw(CDC* dc) +{ + RECT r; + GetClientRect(&r); + int w1 = r.right - r.left; + int h1 = r.bottom - r.top; + CDC memDC; + memDC.CreateCompatibleDC(dc); + if(!stretch) { + if(w > w1) + w1 = w; + if(h > h1) + h1 = h; + } + CBitmap bitmap, *pOldBitmap; + bitmap.CreateCompatibleBitmap(dc, w1, h1); + pOldBitmap = memDC.SelectObject(&bitmap); + if(stretch) { + bmpInfo->bmiHeader.biWidth = w; + bmpInfo->bmiHeader.biHeight = -h; + + StretchDIBits(memDC.GetSafeHdc(), + 0, + 0, + w1, + h1, + 0, + 0, + w, + h, + data, + bmpInfo, + DIB_RGB_COLORS, + SRCCOPY); + } else { + FillOutsideRect(&memDC, CBrush::FromHandle(GetSysColorBrush(COLOR_BTNFACE))); + + bmpInfo->bmiHeader.biWidth = w; + bmpInfo->bmiHeader.biHeight = -h; + SetDIBitsToDevice(memDC.GetSafeHdc(), + 0, + 0, + w, + h, + 0, + 0, + 0, + h, + data, + bmpInfo, + DIB_RGB_COLORS); + } + + dc->BitBlt(0,0,w1,h1, + &memDC,0,0,SRCCOPY); + memDC.SelectObject(pOldBitmap); + + bitmap.DeleteObject(); + memDC.DeleteDC(); +} + +///////////////////////////////////////////////////////////////////////////// +// BitmapControl diagnostics + +#ifdef _DEBUG +void BitmapControl::AssertValid() const +{ + CScrollView::AssertValid(); +} + +void BitmapControl::Dump(CDumpContext& dc) const +{ + CScrollView::Dump(dc); +} +#endif //_DEBUG + +///////////////////////////////////////////////////////////////////////////// +// BitmapControl message handlers + +BOOL BitmapControl::OnEraseBkgnd(CDC* pDC) +{ + return TRUE; +} + +void BitmapControl::OnSize(UINT nType, int cx, int cy) +{ + if(!stretch) + CScrollView::OnSize(nType, cx, cy); +} + +void BitmapControl::OnLButtonDown(UINT nFlags, CPoint pt) +{ + if(!data) + return; + int x = pt.x; + int y = pt.y; + + WPARAM point; + + if(stretch) { + RECT rect; + GetClientRect(&rect); + + int height = rect.bottom - rect.top; + int width = rect.right - rect.left; + + int xx = (x * w) / width; + int yy = (y * h) / height; + + point = xx | (yy<<16); + + int xxx = xx / 8; + int yyy = yy / 8; + + for(int i = 0; i < 8; i++) { + memcpy(&colors[i*3*8], &data[xxx * 8 * 3 + + w * yyy * 8 * 3 + + i * w * 3], 8 * 3); + } + } else { + POINT p; + p.x = GetScrollPos(SB_HORZ); + p.y = GetScrollPos(SB_VERT); + + p.x += x; + p.y += y; + + if(p.x >= w || + p.y >= h) + return; + + point = p.x | (p.y<<16); + + int xxx = p.x / 8; + int yyy = p.y / 8; + + for(int i = 0; i < 8; i++) { + memcpy(&colors[i*3*8], &data[xxx * 8 * 3 + + w * yyy * 8 * 3 + + i * w * 3], 8 * 3); + } + } + + GetParent()->SendMessage(WM_MAPINFO, + point, + (LPARAM)colors); +} + +void BitmapControl::setBmpInfo(BITMAPINFO *info) +{ + bmpInfo = info; +} + +void BitmapControl::setData(u8 *d) +{ + data = d; +} + +void BitmapControl::setSize(int w1, int h1) +{ + if(w != w1 || h != h1) { + w = w1; + h = h1; + SIZE s; + s.cx = w; + s.cy = h; + SetScrollSizes(MM_TEXT, s); + } +} + +void BitmapControl::refresh() +{ + Invalidate(); +} + + +void BitmapControl::registerClass() +{ + if(!isRegistered) { + WNDCLASS wc; + ZeroMemory(&wc, sizeof(wc)); + wc.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS; + wc.lpfnWndProc = (WNDPROC)::DefWindowProc; + wc.hInstance = AfxGetInstanceHandle(); + wc.hIcon = NULL; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); + wc.lpszMenuName = NULL; + wc.lpszClassName = "VbaBitmapControl"; + AfxRegisterClass(&wc); + isRegistered = true; + } +} + +void BitmapControl::setStretch(bool b) +{ + if(b != stretch) { + stretch = b; + Invalidate(); + } +} + +bool BitmapControl::getStretch() +{ + return stretch; +} + +void BitmapControl::PostNcDestroy() +{ +} diff --git a/src/win32/BitmapControl.h b/src/win32/BitmapControl.h new file mode 100644 index 0000000..c20929e --- /dev/null +++ b/src/win32/BitmapControl.h @@ -0,0 +1,77 @@ +#if !defined(AFX_BITMAPCONTROL_H__2434AADB_B6A5_4E43_AA16_7B65B6F7FA26__INCLUDED_) +#define AFX_BITMAPCONTROL_H__2434AADB_B6A5_4E43_AA16_7B65B6F7FA26__INCLUDED_ + +#include "../System.h" // Added by ClassView +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// BitmapControl.h : header file +// +#ifndef WM_MAPINFO +#define WM_MAPINFO WM_APP+101 +#endif + +///////////////////////////////////////////////////////////////////////////// +// BitmapControl view + +class BitmapControl : public CScrollView +{ + public: + BitmapControl(); // protected constructor used by dynamic creation + protected: + DECLARE_DYNCREATE(BitmapControl) + + // Attributes + public: + + // Operations + public: + void setStretch(bool b); + void refresh(); + void setSize(int w1, int h1); + void setData(u8 *d); + void setBmpInfo(BITMAPINFO *info); + static bool isRegistered; + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(BitmapControl) + protected: + virtual void OnDraw(CDC* pDC); // overridden to draw this view + virtual void OnInitialUpdate(); // first time after construct + virtual void PostNcDestroy(); + //}}AFX_VIRTUAL + + // Implementation + public: + bool getStretch(); + virtual ~BitmapControl(); + protected: +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + + // Generated message map functions + //{{AFX_MSG(BitmapControl) + afx_msg BOOL OnEraseBkgnd(CDC* pDC); + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnLButtonDown(UINT nFlags, CPoint point); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + private: + void registerClass(); + bool stretch; + u8 colors[3*64]; + BITMAPINFO *bmpInfo; + u8 * data; + int h; + int w; +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_BITMAPCONTROL_H__2434AADB_B6A5_4E43_AA16_7B65B6F7FA26__INCLUDED_) diff --git a/src/win32/BugReport.cpp b/src/win32/BugReport.cpp new file mode 100644 index 0000000..6888216 --- /dev/null +++ b/src/win32/BugReport.cpp @@ -0,0 +1,245 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2005 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +// BugReport.cpp : implementation file +// + +#include "stdafx.h" +#include "vba.h" +#include "BugReport.h" + +#include "../agb/agbprint.h" +#include "../AutoBuild.h" +#include "../agb/GBA.h" +#include "../Globals.h" +#include "../Port.h" +#include "../RTC.h" +#include "../Sound.h" +#include "../dmg/gbCheats.h" +#include "../dmg/gbGlobals.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// BugReport dialog + + +BugReport::BugReport(CWnd* pParent /*=NULL*/) + : CDialog(BugReport::IDD, pParent) +{ + //{{AFX_DATA_INIT(BugReport) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT +} + + +void BugReport::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(BugReport) + DDX_Control(pDX, IDC_BUG_REPORT, m_report); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(BugReport, CDialog) + //{{AFX_MSG_MAP(BugReport) + ON_BN_CLICKED(IDC_COPY, OnCopy) + ON_BN_CLICKED(ID_OK, OnOk) + //}}AFX_MSG_MAP + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// BugReport message handlers + +void BugReport::OnCopy() +{ + OpenClipboard(); + + EmptyClipboard(); + CString report; + m_report.GetWindowText(report); + + HGLOBAL hglbCopy = GlobalAlloc(GMEM_MOVEABLE, + (report.GetLength() + 1) * sizeof(CHAR)); + if (hglbCopy == NULL) { + CloseClipboard(); + return; + } + + // Lock the handle and copy the text to the buffer. + + LPSTR lptstrCopy = (LPSTR)GlobalLock(hglbCopy); + memcpy(lptstrCopy, (const char *)report, + report.GetLength() * sizeof(CHAR)); + lptstrCopy[report.GetLength()] = (TCHAR) 0; // null character + GlobalUnlock(hglbCopy); + + // Place the handle on the clipboard. + + SetClipboardData(CF_TEXT, hglbCopy); + CloseClipboard(); + + systemMessage(IDS_BUG_REPORT, "Bug report has been copied to the Clipboard"); +} + +void BugReport::OnOk() +{ + EndDialog(TRUE); +} + +BOOL BugReport::OnInitDialog() +{ + CDialog::OnInitDialog(); + + CenterWindow(); + + CString report = createReport(); + + m_report.SetFont(CFont::FromHandle((HFONT)GetStockObject(SYSTEM_FIXED_FONT))); + + m_report.SetWindowText(report); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + + +static void AppendFormat(CString& report, const char *format, ...) +{ + CString buffer; + va_list valist; + + va_start(valist, format); + buffer.FormatV(format, valist); + va_end(valist); + report += buffer; +} + +CString BugReport::createReport() +{ + theApp.winCheckFullscreen(); + + CString report = ""; + AppendFormat(report, "Emu version : %s\r\n", VBA_VERSION_STRING); + AppendFormat(report, "Emu Type : %s\r\n", +#ifdef DEBUG + "Debug Version" +#else + "Release Version" +#endif + ); + + if(emulating) { + AppendFormat(report, "File : %s\r\n", theApp.szFile); + + char buffer[20]; + if(theApp.cartridgeType == 0) { + u32 check = 0; + for(int i = 0; i < 0x4000; i += 4) { + check += *((u32 *)&bios[i]); + } + AppendFormat(report, "BIOS Checksum: %08X\r\n", check); + + strncpy(buffer, (const char *)&rom[0xa0], 12); + buffer[12] = 0; + AppendFormat(report, "Internal name: %s\r\n", buffer); + + strncpy(buffer, (const char *)&rom[0xac], 4); + buffer[4] = 0; + AppendFormat(report, "Game code : %s\r\n", buffer); + + CString res = ""; + u32 *p = (u32 *)rom; + u32 *end = (u32 *)((char *)rom+theApp.romSize); + while(p < end) { + u32 d = READ32LE(p); + + if(d == 0x52504545) { + if(memcmp(p, "EEPROM_", 7) == 0) { + res += (const char *)p; + res += ' '; + } + } else if (d == 0x4D415253) { + if(memcmp(p, "SRAM_", 5) == 0) { + res += (const char *)p; + res += ' '; + } + } else if (d == 0x53414C46) { + if(memcmp(p, "FLASH1M_", 8) == 0) { + res += (const char *)p; + res += ' '; + } + } else if(memcmp(p, "FLASH", 5) == 0) { + res += (const char *)p; + res += ' '; + } else if (d == 0x52494953) { + if(memcmp(p, "SIIRTC_V", 8) == 0) { + res += (const char *)p; + res += ' '; + } + } + p++; + } + if(res.GetLength() > 0) + AppendFormat(report, "Cart Save : %s\r\n", res); + } else if(theApp.cartridgeType == 1) { + strncpy(buffer, (const char *)&gbRom[0x134], 15); + buffer[15] = 0; + AppendFormat(report, "Game title : %s\r\n", buffer); + } + } + + AppendFormat(report, "Using BIOS : %d\r\n", theApp.useBiosFile); + AppendFormat(report, "Skip BIOS : %d\r\n", theApp.skipBiosFile); + AppendFormat(report, "Disable SFX : %d\r\n", cpuDisableSfx); + AppendFormat(report, "Throttle : %d\r\n", theApp.throttle); + AppendFormat(report, "Rewind : %d\r\n", theApp.rewindTimer); + AppendFormat(report, "Auto frame : %d\r\n", theApp.autoFrameSkip); + AppendFormat(report, "Video option : %d\r\n", theApp.videoOption); + AppendFormat(report, "Render type : %d\r\n", theApp.renderMethod); + AppendFormat(report, "Color depth : %d\r\n", systemColorDepth); + AppendFormat(report, "Red shift : %08x\r\n", systemRedShift); + AppendFormat(report, "Green shift : %08x\r\n", systemGreenShift); + AppendFormat(report, "Blue shift : %08x\r\n", systemBlueShift); + AppendFormat(report, "Layer setting: %04X\r\n", layerSettings); + AppendFormat(report, "Mirroring : %d\r\n", mirroringEnable); + AppendFormat(report, "Save type : %d (%d)\r\n", + theApp.winSaveType, cpuSaveType); + AppendFormat(report, "Flash size : %08X (%08x)\r\n", + theApp.winFlashSize, flashSize); + AppendFormat(report, "RTC : %d (%d)\r\n", theApp.winRtcEnable, + rtcIsEnabled()); + AppendFormat(report, "AGBPrint : %d\r\n", agbPrintIsEnabled()); + AppendFormat(report, "Speed toggle : %d\r\n", theApp.speedupToggle); + AppendFormat(report, "Synchronize : %d\r\n", synchronize); + AppendFormat(report, "Sound OFF : %d\r\n", soundOffFlag); + AppendFormat(report, "Channels : %04x\r\n", soundGetEnable() & 0x30f); + AppendFormat(report, "Old Sync : %d\r\n", theApp.useOldSync); + AppendFormat(report, "Priority : %d\r\n", theApp.threadPriority); + AppendFormat(report, "Filters : %d (%d)\r\n", theApp.filterType, theApp.ifbType); + AppendFormat(report, "Cheats : %d\r\n", cheatsNumber); + AppendFormat(report, "GB Cheats : %d\r\n", gbCheatNumber); + AppendFormat(report, "GB Emu Type : %d\r\n", gbEmulatorType); + + return report; +} diff --git a/src/win32/BugReport.h b/src/win32/BugReport.h new file mode 100644 index 0000000..179df29 --- /dev/null +++ b/src/win32/BugReport.h @@ -0,0 +1,68 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#if !defined(AFX_BUGREPORT_H__DE7BC381_E45D_4200_910C_E5378E6364C9__INCLUDED_) +#define AFX_BUGREPORT_H__DE7BC381_E45D_4200_910C_E5378E6364C9__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// BugReport.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// BugReport dialog + +class BugReport : public CDialog +{ + // Construction + public: + BugReport(CWnd* pParent = NULL); // standard constructor + + // Dialog Data + //{{AFX_DATA(BugReport) + enum { IDD = IDD_BUG_REPORT }; + CEdit m_report; + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(BugReport) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + + // Implementation + protected: + CString createReport(); + + // Generated message map functions + //{{AFX_MSG(BugReport) + afx_msg void OnCopy(); + afx_msg void OnOk(); + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + + //{{AFX_INSERT_LOCATION}} + // Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_BUGREPORT_H__DE7BC381_E45D_4200_910C_E5378E6364C9__INCLUDED_) diff --git a/src/win32/CmdAccelOb.cpp b/src/win32/CmdAccelOb.cpp new file mode 100644 index 0000000..805425a --- /dev/null +++ b/src/win32/CmdAccelOb.cpp @@ -0,0 +1,527 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 1998 by Thierry Maurel +// All rights reserved +// +// Distribute freely, except: don't remove my name from the source or +// documentation (don't take credit for my work), mark your changes (don't +// get me blamed for your possible bugs), don't alter or remove this +// notice. +// No warrantee of any kind, express or implied, is included with this +// software; use at your own risk, responsibility for damages (if any) to +// anyone resulting from the use of this software rests entirely with the +// user. +// +// Send bug reports, bug fixes, enhancements, requests, flames, etc., and +// I'll try to keep a version up to date. I can be reached as follows: +// tmaurel@caramail.com (or tmaurel@hol.fr) +// +//////////////////////////////////////////////////////////////////////////////// +// File : CmdAccelOb.cpp +// Project : AccelsEditor +//////////////////////////////////////////////////////////////////////////////// +// Version : 1.0 * Author : T.Maurel +// Date : 17.08.98 +// +// Remarks : +// +//////////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "CmdAccelOb.h" + +//////////////////////////////////////////////////////////////////////// +// +// +MAPVIRTKEYS mapVirtKeys[] = { + {VK_LBUTTON, "VK_LBUTTON"}, + {VK_RBUTTON, "VK_RBUTTON"}, + {VK_CANCEL, "VK_CANCEL"}, + {VK_MBUTTON, "VK_MBUTTON"}, + {VK_BACK, "BACK"}, + {VK_TAB, "TAB"}, + {VK_CLEAR, "VK_CLEAR"}, + {VK_RETURN, "RETURN"}, + {VK_SHIFT, "SHIFT"}, + {VK_CONTROL, "CONTROL"}, + {VK_MENU, "MENU"}, + {VK_PAUSE, "PAUSE"}, + {VK_CAPITAL, "CAPITAL"}, + {VK_ESCAPE, "ESCAPE"}, + {VK_SPACE, "SPACE"}, + {VK_PRIOR, "PRIOR"}, + {VK_NEXT, "NEXT"}, + {VK_END, "END"}, + {VK_HOME, "HOME"}, + {VK_LEFT, "LEFT"}, + {VK_UP, "UP"}, + {VK_RIGHT, "RIGHT"}, + {VK_DOWN, "DOWN"}, + {VK_SELECT, "VK_SELECT"}, + {VK_PRINT, "PRINT"}, + {VK_EXECUTE, "EXECUTE"}, + {VK_SNAPSHOT, "SNAPSHOT"}, + {VK_INSERT, "INSERT"}, + {VK_DELETE, "DELETE"}, + {VK_HELP, "VK_HELP"}, + {WORD('0'), "0"}, + {WORD('1'), "1"}, + {WORD('2'), "2"}, + {WORD('3'), "3"}, + {WORD('4'), "4"}, + {WORD('5'), "5"}, + {WORD('6'), "6"}, + {WORD('7'), "7"}, + {WORD('8'), "8"}, + {WORD('9'), "9"}, + {WORD('A'), "A"}, + {WORD('B'), "B"}, + {WORD('C'), "C"}, + {WORD('D'), "D"}, + {WORD('E'), "E"}, + {WORD('F'), "F"}, + {WORD('G'), "G"}, + {WORD('H'), "H"}, + {WORD('I'), "I"}, + {WORD('J'), "J"}, + {WORD('K'), "K"}, + {WORD('L'), "L"}, + {WORD('M'), "M"}, + {WORD('N'), "N"}, + {WORD('O'), "O"}, + {WORD('P'), "P"}, + {WORD('Q'), "Q"}, + {WORD('R'), "R"}, + {WORD('S'), "S"}, + {WORD('T'), "T"}, + {WORD('U'), "U"}, + {WORD('V'), "V"}, + {WORD('W'), "W"}, + {WORD('X'), "X"}, + {WORD('Y'), "Y"}, + {WORD('Z'), "Z"}, + {VK_LWIN, "VK_LWIN"}, + {VK_RWIN, "VK_RWIN"}, + {VK_APPS, "VK_APPS"}, + {VK_NUMPAD0, "NUMPAD0"}, + {VK_NUMPAD1, "NUMPAD1"}, + {VK_NUMPAD2, "NUMPAD2"}, + {VK_NUMPAD3, "NUMPAD3"}, + {VK_NUMPAD4, "NUMPAD4"}, + {VK_NUMPAD5, "NUMPAD5"}, + {VK_NUMPAD6, "NUMPAD6"}, + {VK_NUMPAD7, "NUMPAD7"}, + {VK_NUMPAD8, "NUMPAD8"}, + {VK_NUMPAD9, "NUMPAD9"}, + {VK_MULTIPLY, "MULTIPLY"}, + {VK_ADD, "ADD"}, + {VK_SEPARATOR, "SEPARATOR"}, + {VK_SUBTRACT, "SUBTRACT"}, + {VK_DECIMAL, "DECIMAL"}, + {VK_DIVIDE, "DIVIDE"}, + {VK_F1, "F1"}, + {VK_F2, "F2"}, + {VK_F3, "F3"}, + {VK_F4, "F4"}, + {VK_F5, "F5"}, + {VK_F6, "F6"}, + {VK_F7, "F7"}, + {VK_F8, "F8"}, + {VK_F9, "F9"}, + {VK_F10, "F10"}, + {VK_F11, "F11"}, + {VK_F12, "F12"}, + {VK_F13, "F13"}, + {VK_F14, "F14"}, + {VK_F15, "F15"}, + {VK_F16, "F16"}, + {VK_F17, "F17"}, + {VK_F18, "F18"}, + {VK_F19, "F19"}, + {VK_F20, "F20"}, + {VK_F21, "F21"}, + {VK_F22, "F22"}, + {VK_F23, "F23"}, + {VK_F24, "F24"}, + {VK_NUMLOCK, "NUMLOCK"}, + {VK_SCROLL, "VK_SCROLL"}, + {VK_ATTN, "VK_ATTN"}, + {VK_CRSEL, "VK_CRSEL"}, + {VK_EXSEL, "VK_EXSEL"}, + {VK_EREOF, "VK_EREOF"}, + {VK_PLAY, "VK_PLAY"}, + {VK_ZOOM, "VK_ZOOM"}, + {VK_NONAME, "VK_NONAME"}, + {VK_PA1, "VK_PA1"}, + {VK_OEM_CLEAR, "VK_OEM_CLEAR"}, +}; + + +//////////////////////////////////////////////////////////////////////// +// +// +MAPVIRTKEYS mapVirtSysKeys[] = { + {FCONTROL, "Ctrl"}, + {FALT, "Alt"}, + {FSHIFT, "Shift"}, +}; + + +//////////////////////////////////////////////////////////////////////// +// helper fct for external access +//////////////////////////////////////////////////////////////////////// +// +// +TCHAR* mapVirtKeysStringFromWORD(WORD wKey) +{ + for (int index = 0; index < sizeof(mapVirtKeys)/sizeof(mapVirtKeys[0]); index++) { + if (mapVirtKeys[index].wKey == wKey) + return mapVirtKeys[index].szKey; + } + return NULL; +} + + + +//////////////////////////////////////////////////////////////////////// +// +#define DEFAULT_ACCEL 0x01 +#define USER_ACCEL 0x02 + + +//////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////// +// +// +CAccelsOb::CAccelsOb() +{ + m_cVirt = 0; + m_wKey = 0; + m_bLocked = false; +} + + +//////////////////////////////////////////////////////////////////////// +// +// +CAccelsOb::CAccelsOb(CAccelsOb* pFrom) +{ + ASSERT(pFrom != NULL); + + m_cVirt = pFrom->m_cVirt; + m_wKey = pFrom->m_wKey; + m_bLocked = pFrom->m_bLocked; +} + + +//////////////////////////////////////////////////////////////////////// +// +// +CAccelsOb::CAccelsOb(BYTE cVirt, WORD wKey, bool bLocked) +{ + m_cVirt = cVirt; + m_wKey = wKey; + m_bLocked = bLocked; +} + + +//////////////////////////////////////////////////////////////////////// +// +// +CAccelsOb::CAccelsOb(LPACCEL pACCEL) +{ + ASSERT(pACCEL != NULL); + + m_cVirt = pACCEL->fVirt; + m_wKey = pACCEL->key; + m_bLocked = false; +} + + +//////////////////////////////////////////////////////////////////////// +// +// +CAccelsOb& CAccelsOb::operator=(const CAccelsOb& from) +{ + m_cVirt = from.m_cVirt; + m_wKey = from.m_wKey; + m_bLocked = from.m_bLocked; + + return *this; +} + + +//////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////// +// +// +void CAccelsOb::GetString(CString& szBuffer) +{ + szBuffer = ""; + // in case of the object is not assigned, we avoid error messages + if (m_wKey == 0) + return; + + // modifiers part + int i; + for (i = 0; i < sizetable(mapVirtSysKeys); i++) { + if (m_cVirt & mapVirtSysKeys[i].wKey) { + szBuffer += mapVirtSysKeys[i].szKey; + szBuffer += "+"; + } + } + // and virtual key part + for (i = 0; i < sizetable(mapVirtKeys); i++) { + if (m_wKey == mapVirtKeys[i].wKey) { + szBuffer += mapVirtKeys[i].szKey; + return; + } + } + AfxMessageBox("Internal error : (CAccelsOb::GetString) m_wKey invalid"); +} + + +//////////////////////////////////////////////////////////////////////// +// +// +bool CAccelsOb::IsEqual(WORD wKey, bool bCtrl, bool bAlt, bool bShift) +{ + // CString szTemp; + // GetString(szTemp); + + + bool m_bCtrl = (m_cVirt & FCONTROL) ? true : false; + bool bRet = (bCtrl == m_bCtrl); + + bool m_bAlt = (m_cVirt & FALT) ? true : false; + bRet &= (bAlt == m_bAlt); + + bool m_bShift = (m_cVirt & FSHIFT) ? true : false; + bRet &= (bShift == m_bShift); + + bRet &= static_cast(m_wKey == wKey); + + return bRet; +} + + +//////////////////////////////////////////////////////////////////////// +// +// +DWORD CAccelsOb::GetData() +{ + BYTE cLocalCodes = 0; + if (m_bLocked) + cLocalCodes = DEFAULT_ACCEL; + else + cLocalCodes = USER_ACCEL; + + WORD bCodes = MAKEWORD(m_cVirt, cLocalCodes); + return MAKELONG(m_wKey, bCodes); +} + + +//////////////////////////////////////////////////////////////////////// +// +// +bool CAccelsOb::SetData(DWORD dwDatas) +{ + m_wKey = LOWORD(dwDatas); + + WORD bCodes = HIWORD(dwDatas); + m_cVirt = LOBYTE(bCodes); + + BYTE cLocalCodes = HIBYTE(bCodes); + m_bLocked = static_cast(cLocalCodes == DEFAULT_ACCEL); + return true; +} + +//////////////////////////////////////////////////////////////////////// +// +#ifdef _DEBUG +//////////////////////////////////////////////////////////////////////// +// +// +void CAccelsOb::AssertValid() const +{ + CObject::AssertValid(); +} + +//////////////////////////////////////////////////////////////////////// +// +// +void CAccelsOb::Dump(CDumpContext& dc) const +{ + dc << "\t\t"; + CObject::Dump(dc); + dc << "\t\tlocked=" << m_bLocked << ", cVirt=" << m_cVirt << ", wKey=" << m_wKey << "\n\n"; + +} +#endif + +//////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////// +// +// +CCmdAccelOb::CCmdAccelOb() +{ +} + + +//////////////////////////////////////////////////////////////////////// +// +// +CCmdAccelOb::CCmdAccelOb(WORD wIDCommand, LPCTSTR szCommand) +{ + ASSERT(szCommand != NULL); + + m_wIDCommand = wIDCommand; + m_szCommand = szCommand; +} + + +//////////////////////////////////////////////////////////////////////// +// +// +CCmdAccelOb::CCmdAccelOb(BYTE cVirt, WORD wIDCommand, WORD wKey, LPCTSTR szCommand, bool bLocked) +{ + ASSERT(szCommand != NULL); + + m_wIDCommand = wIDCommand; + m_szCommand = szCommand; + + CAccelsOb* pAccel = DEBUG_NEW CAccelsOb(cVirt, wKey, bLocked); + ASSERT(pAccel != NULL); + m_Accels.AddTail(pAccel); +} + + +//////////////////////////////////////////////////////////////////////// +// +// +CCmdAccelOb::~CCmdAccelOb() +{ + POSITION pos = m_Accels.GetHeadPosition(); + while (pos != NULL) + delete m_Accels.GetNext(pos); + m_Accels.RemoveAll(); +} + + +//////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////// +// +// +void CCmdAccelOb::Add(BYTE cVirt, WORD wKey, bool bLocked) +{ + CAccelsOb* pAccel = DEBUG_NEW CAccelsOb(cVirt, wKey, bLocked); + ASSERT(pAccel != NULL); + m_Accels.AddTail(pAccel); +} + + +//////////////////////////////////////////////////////////////////////// +// +// +void CCmdAccelOb::Add(CAccelsOb* pAccel) +{ + ASSERT(pAccel != NULL); + m_Accels.AddTail(pAccel); +} + + +//////////////////////////////////////////////////////////////////////// +// +// +CCmdAccelOb& CCmdAccelOb::operator=(const CCmdAccelOb& from) +{ + Reset(); + + m_wIDCommand = from.m_wIDCommand; + m_szCommand = from.m_szCommand; + + CAccelsOb* pAccel; + POSITION pos = from.m_Accels.GetHeadPosition(); + while (pos != NULL) { + pAccel = DEBUG_NEW CAccelsOb(from.m_Accels.GetNext(pos)); + ASSERT(pAccel != NULL); + m_Accels.AddTail(pAccel); + } + return *this; +} + + +//////////////////////////////////////////////////////////////////////// +// +// +void CCmdAccelOb::DeleteUserAccels() +{ + CAccelsOb* pAccel; + POSITION prevPos; + POSITION pos = m_Accels.GetHeadPosition(); + while (pos != NULL) { + prevPos = pos; + pAccel = m_Accels.GetNext(pos); + if (!pAccel->m_bLocked) { + delete pAccel; + m_Accels.RemoveAt(prevPos); + } + } +} + + +//////////////////////////////////////////////////////////////////////// +// +// +void CCmdAccelOb::Reset() +{ + m_wIDCommand = 0; + m_szCommand = "Empty command"; + + CAccelsOb* pAccel; + POSITION pos = m_Accels.GetHeadPosition(); + while (pos != NULL) { + pAccel = m_Accels.GetNext(pos); + delete pAccel; + } +} + +//////////////////////////////////////////////////////////////////////// +// +#ifdef _DEBUG +//////////////////////////////////////////////////////////////////////// +// +// +void CCmdAccelOb::AssertValid() const +{ + // call base class function first + CObject::AssertValid(); +} + + +//////////////////////////////////////////////////////////////////////// +// +// +void CCmdAccelOb::Dump( CDumpContext& dc ) const +{ + // call base class function first + dc << "\t"; + CObject::Dump( dc ); + + // now do the stuff for our specific class + dc << "\tIDCommand = " << m_wIDCommand; + dc << "\n\tszCommand = " << m_szCommand; + dc << "\n\tAccelerators = {\n"; + + CAccelsOb* pAccel; + POSITION pos = m_Accels.GetHeadPosition(); + while (pos != NULL) { + pAccel = m_Accels.GetNext(pos); + dc << pAccel; + } + dc << "\t}\n"; +} +#endif diff --git a/src/win32/CmdAccelOb.h b/src/win32/CmdAccelOb.h new file mode 100644 index 0000000..d473220 --- /dev/null +++ b/src/win32/CmdAccelOb.h @@ -0,0 +1,113 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 1998 by Thierry Maurel +// All rights reserved +// +// Distribute freely, except: don't remove my name from the source or +// documentation (don't take credit for my work), mark your changes (don't +// get me blamed for your possible bugs), don't alter or remove this +// notice. +// No warrantee of any kind, express or implied, is included with this +// software; use at your own risk, responsibility for damages (if any) to +// anyone resulting from the use of this software rests entirely with the +// user. +// +// Send bug reports, bug fixes, enhancements, requests, flames, etc., and +// I'll try to keep a version up to date. I can be reached as follows: +// tmaurel@caramail.com (or tmaurel@hol.fr) +// +//////////////////////////////////////////////////////////////////////////////// +// File : CmdAccelOb.h +// Project : AccelsEditor +//////////////////////////////////////////////////////////////////////////////// +// Version : 1.0 * Author : T.Maurel +// Date : 17.08.98 +// +// Remarks : +// +//////////////////////////////////////////////////////////////////////////////// +#ifndef __CMDACCEL_OB_INCLUDE +#define __CMDACCEL_OB_INCLUDE + +#include // MFC Templates extension + +//////////////////////////////////////////////////////////////////////// +// +// +typedef struct tagMAPVIRTKEYS { + WORD wKey; + TCHAR szKey[15]; +} MAPVIRTKEYS, *PMAPVIRTKEYS; + + +//////////////////////////////////////////////////////////////////////// +// +// +#define sizetable(table) (sizeof(table)/sizeof(table[0])) + + +//////////////////////////////////////////////////////////////////////// +// +// +class CAccelsOb : public CObject { + public: + CAccelsOb(); + CAccelsOb(CAccelsOb* pFrom); + CAccelsOb(BYTE cVirt, WORD wKey, bool bLocked = false); + CAccelsOb(LPACCEL pACCEL); + + public: + CAccelsOb& operator=(const CAccelsOb& from); + + void GetString(CString& szBuffer); + bool IsEqual(WORD wKey, bool bCtrl, bool bAlt, bool bShift); + DWORD GetData(); + bool SetData(DWORD dwDatas); + + public: +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + + public: + BYTE m_cVirt; + WORD m_wKey; + bool m_bLocked; +}; + + +////////////////////////////////////////////////////////////////////// +// +// +class CCmdAccelOb : public CObject { + public: + CCmdAccelOb(); + CCmdAccelOb(WORD wIDCommand, LPCTSTR szCommand); + CCmdAccelOb(BYTE cVirt, WORD wIDCommand, WORD wKey, LPCTSTR szCommand, bool bLocked = false); + ~CCmdAccelOb(); + + public: + void Add(CAccelsOb* pAccel); + void Add(BYTE cVirt, WORD wKey, bool bLocked = false); + void Reset(); + void DeleteUserAccels(); + + CCmdAccelOb& operator=(const CCmdAccelOb& from); + public: +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + + public: + WORD m_wIDCommand; + CString m_szCommand; + + CList< CAccelsOb*, CAccelsOb*& > m_Accels; +}; + + +//////////////////////////////////////////////////////////////////////// +#endif // __CMDACCEL_OB_INCLUDE + + diff --git a/src/win32/ColorButton.cpp b/src/win32/ColorButton.cpp new file mode 100644 index 0000000..4032bc2 --- /dev/null +++ b/src/win32/ColorButton.cpp @@ -0,0 +1,99 @@ +#include "stdafx.h" +#include "vba.h" +#include "ColorButton.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +bool ColorButton::isRegistered = false; + +///////////////////////////////////////////////////////////////////////////// +// ColorButton + +ColorButton::ColorButton() +{ + color = 0; + registerClass(); +} + +ColorButton::~ColorButton() +{ +} + + +BEGIN_MESSAGE_MAP(ColorButton, CButton) + //{{AFX_MSG_MAP(ColorButton) + // NOTE - the ClassWizard will add and remove mapping macros here. + //}}AFX_MSG_MAP + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// ColorButton message handlers + +void ColorButton::PreSubclassWindow() +{ + SetWindowLong(m_hWnd, GWL_STYLE, GetStyle() | BS_OWNERDRAW); + CWnd::PreSubclassWindow(); +} + +void ColorButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) +{ + ASSERT(lpDrawItemStruct); + + int r = (color & 0x1f) << 3; + int g = (color & 0x3e0) >> 2; + int b = (color & 0x7c00) >> 7; + + HDC dc = lpDrawItemStruct->hDC; + UINT state = lpDrawItemStruct->itemState; + RECT rect = lpDrawItemStruct->rcItem; + + SIZE margins; + margins.cx = ::GetSystemMetrics(SM_CXEDGE); + margins.cy = ::GetSystemMetrics(SM_CYEDGE); + + if(GetState() & BST_PUSHED) + DrawEdge(dc, &rect, EDGE_SUNKEN, BF_RECT); + else + DrawEdge(dc, &rect, EDGE_RAISED, BF_RECT); + + InflateRect(&rect, -margins.cx, -margins.cy); + + HBRUSH br = CreateSolidBrush((state & ODS_DISABLED) ? + ::GetSysColor(COLOR_3DFACE) : RGB(r,g,b)); + + FillRect(dc, &rect, br); + + if(state & ODS_FOCUS) { + InflateRect(&rect, -1, -1); + DrawFocusRect(dc, &rect); + } + + DeleteObject(br); +} + +void ColorButton::setColor(u16 c) +{ + color = c; + Invalidate(); +} + +void ColorButton::registerClass() +{ + if(!isRegistered) { + WNDCLASS wc; + ZeroMemory(&wc, sizeof(wc)); + wc.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS; + wc.lpfnWndProc = (WNDPROC)::DefWindowProc; + wc.hInstance = AfxGetInstanceHandle(); + wc.hIcon = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH )GetStockObject(BLACK_BRUSH); + wc.lpszMenuName = NULL; + wc.lpszClassName = "VbaColorButton"; + AfxRegisterClass(&wc); + isRegistered = true; + } +} diff --git a/src/win32/ColorButton.h b/src/win32/ColorButton.h new file mode 100644 index 0000000..69dd58f --- /dev/null +++ b/src/win32/ColorButton.h @@ -0,0 +1,55 @@ +#if !defined(AFX_COLORBUTTON_H__DF02109B_B91C_49FD_954F_74A48B83C314__INCLUDED_) +#define AFX_COLORBUTTON_H__DF02109B_B91C_49FD_954F_74A48B83C314__INCLUDED_ + +#include "../System.h" // Added by ClassView +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// ColorButton.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// ColorButton window + +class ColorButton : public CButton +{ + // Construction + public: + ColorButton(); + + // Attributes + public: + // Operations + static bool isRegistered; + public: + void PreSubclassWindow(); + void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct); + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(ColorButton) + //}}AFX_VIRTUAL + + // Implementation + public: + void setColor(u16 c); + u16 color; + virtual ~ColorButton(); + + void registerClass(); + + // Generated message map functions + protected: + //{{AFX_MSG(ColorButton) + // NOTE - the ClassWizard will add and remove member functions here. + //}}AFX_MSG + + DECLARE_MESSAGE_MAP() + }; + + ///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_COLORBUTTON_H__DF02109B_B91C_49FD_954F_74A48B83C314__INCLUDED_) diff --git a/src/win32/ColorControl.cpp b/src/win32/ColorControl.cpp new file mode 100644 index 0000000..f0d70e6 --- /dev/null +++ b/src/win32/ColorControl.cpp @@ -0,0 +1,82 @@ +#include "stdafx.h" +#include "vba.h" +#include "ColorControl.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +bool ColorControl::isRegistered = false; + +///////////////////////////////////////////////////////////////////////////// +// ColorControl + +ColorControl::ColorControl() +{ + color = 0; + registerClass(); +} + +ColorControl::~ColorControl() +{ +} + + +BEGIN_MESSAGE_MAP(ColorControl, CWnd) + //{{AFX_MSG_MAP(ColorControl) + ON_WM_PAINT() + ON_WM_ERASEBKGND() + //}}AFX_MSG_MAP + END_MESSAGE_MAP() + + + ///////////////////////////////////////////////////////////////////////////// +// ColorControl message handlers + +void ColorControl::OnPaint() +{ + CPaintDC dc(this); // device context for painting +} + +BOOL ColorControl::OnEraseBkgnd(CDC* pDC) +{ + int r = (color & 0x1f) << 3; + int g = (color & 0x3e0) >> 2; + int b = (color & 0x7c00) >> 7; + + CBrush br; + br.CreateSolidBrush(RGB(r,g,b)); + + RECT rect; + GetClientRect(&rect); + pDC->FillRect(&rect,&br); + pDC->DrawEdge(&rect, EDGE_SUNKEN, BF_RECT); + br.DeleteObject(); + return TRUE; +} + +void ColorControl::setColor(u16 c) +{ + color = c; + Invalidate(); +} + +void ColorControl::registerClass() +{ + if(!isRegistered) { + WNDCLASS wc; + ZeroMemory(&wc, sizeof(wc)); + wc.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS; + wc.lpfnWndProc = (WNDPROC)::DefWindowProc; + wc.hInstance = AfxGetInstanceHandle(); + wc.hIcon = NULL; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH )GetStockObject(BLACK_BRUSH); + wc.lpszMenuName = NULL; + wc.lpszClassName = "VbaColorControl"; + AfxRegisterClass(&wc); + isRegistered = true; + } +} diff --git a/src/win32/ColorControl.h b/src/win32/ColorControl.h new file mode 100644 index 0000000..42cc365 --- /dev/null +++ b/src/win32/ColorControl.h @@ -0,0 +1,55 @@ +#if !defined(AFX_COLORCONTROL_H__747E1E47_DDFA_4D67_B337_A473F2BACB86__INCLUDED_) +#define AFX_COLORCONTROL_H__747E1E47_DDFA_4D67_B337_A473F2BACB86__INCLUDED_ + +#include "../System.h" // Added by ClassView +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// ColorControl.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// ColorControl window + +class ColorControl : public CWnd +{ + // Construction + public: + ColorControl(); + + // Attributes + public: + // Operations + static bool isRegistered; + + // Operations + public: + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(ColorControl) + //}}AFX_VIRTUAL + + // Implementation + public: + void setColor(u16 c); + u16 color; + virtual ~ColorControl(); + + // Generated message map functions + protected: + //{{AFX_MSG(ColorControl) + afx_msg void OnPaint(); + afx_msg BOOL OnEraseBkgnd(CDC* pDC); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + private: + void registerClass(); +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_COLORCONTROL_H__747E1E47_DDFA_4D67_B337_A473F2BACB86__INCLUDED_) diff --git a/src/win32/Commands.cpp b/src/win32/Commands.cpp new file mode 100644 index 0000000..454d5b1 --- /dev/null +++ b/src/win32/Commands.cpp @@ -0,0 +1,224 @@ +#include "stdafx.h" +#include "AcceleratorManager.h" +#include "resource.h" +#include + +#include // MFC Templates extension +#ifndef CMapStringToWord +typedef CMap< CString, LPCSTR, WORD, WORD& > CMapStringToWord; +#endif + +static CMapStringToWord winAccelStrings; +static bool initialized = false; + +struct { + const char *command; + WORD id; +} winAccelCommands[] = { + { "FileOpenGBA", ID_FILE_OPEN_GBA }, + { "FileOpenGBC", ID_FILE_OPEN_GBC }, + { "FileOpenGB", ID_FILE_OPEN_GB }, + { "FileLoad", ID_FILE_LOAD }, + { "FileSave", ID_FILE_SAVE }, + { "FileLoadGame01", ID_FILE_LOADGAME_SLOT1 }, + { "FileLoadGame02", ID_FILE_LOADGAME_SLOT2 }, + { "FileLoadGame03", ID_FILE_LOADGAME_SLOT3 }, + { "FileLoadGame04", ID_FILE_LOADGAME_SLOT4 }, + { "FileLoadGame05", ID_FILE_LOADGAME_SLOT5 }, + { "FileLoadGame06", ID_FILE_LOADGAME_SLOT6 }, + { "FileLoadGame07", ID_FILE_LOADGAME_SLOT7 }, + { "FileLoadGame08", ID_FILE_LOADGAME_SLOT8 }, + { "FileLoadGame09", ID_FILE_LOADGAME_SLOT9 }, + { "FileLoadGame10", ID_FILE_LOADGAME_SLOT10 }, + { "FileLoadGameAutoLoad", ID_FILE_LOADGAME_AUTOLOADMOSTRECENT }, + { "FileLoadGameRecent", ID_FILE_LOADGAME_MOSTRECENT }, + { "FileSaveGame01", ID_FILE_SAVEGAME_SLOT1 }, + { "FileSaveGame02", ID_FILE_SAVEGAME_SLOT2 }, + { "FileSaveGame03", ID_FILE_SAVEGAME_SLOT3 }, + { "FileSaveGame04", ID_FILE_SAVEGAME_SLOT4 }, + { "FileSaveGame05", ID_FILE_SAVEGAME_SLOT5 }, + { "FileSaveGame06", ID_FILE_SAVEGAME_SLOT6 }, + { "FileSaveGame07", ID_FILE_SAVEGAME_SLOT7 }, + { "FileSaveGame08", ID_FILE_SAVEGAME_SLOT8 }, + { "FileSaveGame09", ID_FILE_SAVEGAME_SLOT9 }, + { "FileSaveGame10", ID_FILE_SAVEGAME_SLOT10 }, + { "FileSaveGameOldest", ID_FILE_SAVEGAME_OLDESTSLOT }, + { "FileRecentReset", ID_FILE_RECENT_RESET }, + { "FileRecentFreeze", ID_FILE_RECENT_FREEZE }, + { "FileRecent01", ID_FILE_MRU_FILE1 }, + { "FileRecent02", ID_FILE_MRU_FILE2 }, + { "FileRecent03", ID_FILE_MRU_FILE3 }, + { "FileRecent04", ID_FILE_MRU_FILE4 }, + { "FileRecent05", ID_FILE_MRU_FILE5 }, + { "FileRecent06", ID_FILE_MRU_FILE6 }, + { "FileRecent07", ID_FILE_MRU_FILE7 }, + { "FileRecent08", ID_FILE_MRU_FILE8 }, + { "FileRecent09", ID_FILE_MRU_FILE9 }, + { "FileRecent10", ID_FILE_MRU_FILE10 }, + { "FilePause", ID_FILE_PAUSE }, + { "FileReset", ID_FILE_RESET }, + { "FileImportBatteryFile", ID_FILE_IMPORT_BATTERYFILE }, + { "FileImportGamesharkCodeFile", ID_FILE_IMPORT_GAMESHARKCODEFILE }, + { "FileImportGamesharkActionReplaySnapshot", ID_FILE_IMPORT_GAMESHARKSNAPSHOT }, + { "FileExportBatteryFile", ID_FILE_EXPORT_BATTERYFILE }, + { "FileExportGamesharkSnapshot", ID_FILE_EXPORT_GAMESHARKSNAPSHOT }, + { "FileScreenCapture", ID_FILE_SCREENCAPTURE }, + { "FileRomInformation", ID_FILE_ROMINFORMATION }, + { "FileToggleFullscreen", ID_FILE_TOGGLEMENU }, + { "FileClose", ID_FILE_CLOSE }, + { "FileExit", ID_FILE_EXIT }, + { "OptionsFrameSkip0", ID_OPTIONS_VIDEO_FRAMESKIP_0 }, + { "OptionsFrameSkip1", ID_OPTIONS_VIDEO_FRAMESKIP_1 }, + { "OptionsFrameSkip2", ID_OPTIONS_VIDEO_FRAMESKIP_2 }, + { "OptionsFrameSkip3", ID_OPTIONS_VIDEO_FRAMESKIP_3 }, + { "OptionsFrameSkip4", ID_OPTIONS_VIDEO_FRAMESKIP_4 }, + { "OptionsFrameSkip5", ID_OPTIONS_VIDEO_FRAMESKIP_5 }, + { "OptionsFrameSkip6", ID_OPTIONS_VIDEO_FRAMESKIP_6 }, + { "OptionsFrameSkip7", ID_OPTIONS_VIDEO_FRAMESKIP_7 }, + { "OptionsFrameSkip8", ID_OPTIONS_VIDEO_FRAMESKIP_8 }, + { "OptionsFrameSkip9", ID_OPTIONS_VIDEO_FRAMESKIP_9 }, + { "OptionsThrottleNone", ID_OPTIONS_FRAMESKIP_THROTTLE_NOTHROTTLE }, + { "OptionsThrottle025%", ID_OPTIONS_FRAMESKIP_THROTTLE_25 }, + { "OptionsThrottle050%", ID_OPTIONS_FRAMESKIP_THROTTLE_50 }, + { "OptionsThrottle100%", ID_OPTIONS_FRAMESKIP_THROTTLE_100 }, + { "OptionsThrottle150%", ID_OPTIONS_FRAMESKIP_THROTTLE_150 }, + { "OptionsThrottle200%", ID_OPTIONS_FRAMESKIP_THROTTLE_200 }, + { "OptionsThrottleOther", ID_OPTIONS_FRAMESKIP_THROTTLE_OTHER }, + { "OptionsVideoRenderDDRAW", ID_OPTIONS_VIDEO_RENDERMETHOD_DIRECTDRAW }, + { "OptionsVideoRenderD3D", ID_OPTIONS_VIDEO_RENDERMETHOD_DIRECT3D }, + { "OptionsVideoRenderOGL", ID_OPTIONS_VIDEO_RENDERMETHOD_OPENGL }, + { "OptionsVideoVsync", ID_OPTIONS_VIDEO_VSYNC }, + { "OptionsVideoX1", ID_OPTIONS_VIDEO_X1 }, + { "OptionsVideoX2", ID_OPTIONS_VIDEO_X2 }, + { "OptionsVideoX3", ID_OPTIONS_VIDEO_X3 }, + { "OptionsVideoX4", ID_OPTIONS_VIDEO_X4 }, + { "OptionsVideoX5", ID_OPTIONS_VIDEO_X5 }, + { "OptionsVideoX6", ID_OPTIONS_VIDEO_X6 }, + { "OptionsVideo320x240", ID_OPTIONS_VIDEO_FULLSCREEN320X240 }, + { "OptionsVideo640x480", ID_OPTIONS_VIDEO_FULLSCREEN640X480 }, + { "OptionsVideo800x600", ID_OPTIONS_VIDEO_FULLSCREEN800X600 }, + { "OptionsVideoFullscreen", ID_OPTIONS_VIDEO_FULLSCREEN }, + { "OptionsVideoFullscreenMaxScale", ID_OPTIONS_VIDEO_FULLSCREENMAXSCALE }, + { "OptionsVideoLayersBG0", ID_OPTIONS_VIDEO_LAYERS_BG0 }, + { "OptionsVideoLayersBG1", ID_OPTIONS_VIDEO_LAYERS_BG1 }, + { "OptionsVideoLayersBG2", ID_OPTIONS_VIDEO_LAYERS_BG2 }, + { "OptionsVideoLayersBG3", ID_OPTIONS_VIDEO_LAYERS_BG3 }, + { "OptionsVideoLayersOBJ", ID_OPTIONS_VIDEO_LAYERS_OBJ }, + { "OptionsVideoLayersWIN0", ID_OPTIONS_VIDEO_LAYERS_WIN0 }, + { "OptionsVideoLayersWIN1", ID_OPTIONS_VIDEO_LAYERS_WIN1 }, + { "OptionsVideoLayersOBJWIN", ID_OPTIONS_VIDEO_LAYERS_OBJWIN }, + { "OptionsVideoLayersReset", ID_OPTIONS_VIDEO_LAYERS_RESET }, + { "OptionsEmulatorAssociate", ID_OPTIONS_EMULATOR_ASSOCIATE }, + { "OptionsEmulatorDirectories", ID_OPTIONS_EMULATOR_DIRECTORIES }, + { "OptionsEmulatorGameOverrides", ID_OPTIONS_EMULATOR_GAMEOVERRIDES }, + { "OptionsEmulatorShowSpeedNone", ID_OPTIONS_EMULATOR_SHOWSPEED_NONE }, + { "OptionsEmulatorShowSpeedPercentage", ID_OPTIONS_EMULATOR_SHOWSPEED_PERCENTAGE }, + { "OptionsEmulatorShowSpeedDetailed", ID_OPTIONS_EMULATOR_SHOWSPEED_DETAILED }, + { "OptionsEmulatorShowSpeedTransparent", ID_OPTIONS_EMULATOR_SHOWSPEED_TRANSPARENT }, + { "OptionsEmulatorSpeedupToggle", ID_OPTIONS_EMULATOR_SPEEDUPTOGGLE }, + { "OptionsEmulatorSaveAuto", ID_OPTIONS_EMULATOR_SAVETYPE_AUTOMATIC }, + { "OptionsEmulatorSaveEEPROM", ID_OPTIONS_EMULATOR_SAVETYPE_EEPROM }, + { "OptionsEmulatorSaveSRAM", ID_OPTIONS_EMULATOR_SAVETYPE_SRAM }, + { "OptionsEmulatorSaveFLASH", ID_OPTIONS_EMULATOR_SAVETYPE_FLASH }, + { "OptionsEmulatorSaveEEPROMSensor", ID_OPTIONS_EMULATOR_SAVETYPE_EEPROMSENSOR }, + { "OptionsEmulatorSaveFlash64K", ID_OPTIONS_EMULATOR_SAVETYPE_FLASH512K }, + { "OptionsEmulatorSaveFlash128K", ID_OPTIONS_EMULATOR_SAVETYPE_FLASH1M }, + { "OptionsEmulatorSaveDetectNow", ID_OPTIONS_EMULATOR_SAVETYPE_DETECTNOW }, + { "OptionsEmulatorAutoApplyPatchFiles", ID_OPTIONS_EMULATOR_AUTOMATICALLYAPPLYPATCHFILES }, + { "OptionsEmulatorAGBPrint", ID_OPTIONS_EMULATOR_AGBPRINT }, + { "OptionsEmulatorRTC", ID_OPTIONS_EMULATOR_REALTIMECLOCK }, + { "OptionsEmulatorRewindInterval", ID_OPTIONS_EMULATOR_REWINDINTERVAL }, + { "OptionsSoundChannel1", ID_OPTIONS_SOUND_CHANNEL1 }, + { "OptionsSoundChannel2", ID_OPTIONS_SOUND_CHANNEL2 }, + { "OptionsSoundChannel3", ID_OPTIONS_SOUND_CHANNEL3 }, + { "OptionsSoundChannel4", ID_OPTIONS_SOUND_CHANNEL4 }, + { "OptionsSoundDirectSoundA", ID_OPTIONS_SOUND_DIRECTSOUNDA }, + { "OptionsSoundDirectSoundB", ID_OPTIONS_SOUND_DIRECTSOUNDB }, + { "OptionsGameboyBorder", ID_OPTIONS_GAMEBOY_BORDER }, + { "OptionsGameboyBorderAutomatic", ID_OPTIONS_GAMEBOY_BORDERAUTOMATIC }, + { "OptionsGameboyColors", ID_OPTIONS_GAMEBOY_COLORS }, + { "OptionsFilterNormal", ID_OPTIONS_FILTER_NORMAL }, + { "OptionsFilterTVMode", ID_OPTIONS_FILTER_TVMODE }, + { "OptionsFilter2xSaI", ID_OPTIONS_FILTER_2XSAI }, + { "OptionsFilterSuper2xSaI", ID_OPTIONS_FILTER_SUPER2XSAI }, + { "OptionsFilterSuperEagle", ID_OPTIONS_FILTER_SUPEREAGLE }, + { "OptionsFilterPixelate", ID_OPTIONS_FILTER16BIT_PIXELATEEXPERIMENTAL }, + { "OptionsFilterMotionBlur", ID_OPTIONS_FILTER16BIT_MOTIONBLUREXPERIMENTAL }, + { "OptionsFilterAdMameScale2x", ID_OPTIONS_FILTER16BIT_ADVANCEMAMESCALE2X }, + { "OptionsFilterSimple2x", ID_OPTIONS_FILTER16BIT_SIMPLE2X }, + { "OptionsFilterBilinear", ID_OPTIONS_FILTER_BILINEAR }, + { "OptionsFilterBilinearPlus", ID_OPTIONS_FILTER_BILINEARPLUS }, + { "OptionsFilterScanlines", ID_OPTIONS_FILTER_SCANLINES }, + { "OptionsFilterHq2x", ID_OPTIONS_FILTER_HQ2X }, + { "OptionsFilterLq2x", ID_OPTIONS_FILTER_LQ2X }, + { "OptionsFilterIFBNone", ID_OPTIONS_FILTER_INTERFRAMEBLENDING_NONE }, + { "OptionsFilterIFBMotionBlur", ID_OPTIONS_FILTER_INTERFRAMEBLENDING_MOTIONBLUR }, + { "OptionsFilterIFBSmart", ID_OPTIONS_FILTER_INTERFRAMEBLENDING_SMART }, + { "OptionsFilterDisableMMX", ID_OPTIONS_FILTER_DISABLEMMX }, + { "OptionsJoypadConfigure1", ID_OPTIONS_JOYPAD_CONFIGURE_1 }, + { "OptionsJoypadConfigure2", ID_OPTIONS_JOYPAD_CONFIGURE_2 }, + { "OptionsJoypadConfigure3", ID_OPTIONS_JOYPAD_CONFIGURE_3 }, + { "OptionsJoypadConfigure4", ID_OPTIONS_JOYPAD_CONFIGURE_4 }, + { "OptionsJoypadMotionConfigure", ID_OPTIONS_JOYPAD_MOTIONCONFIGURE }, + { "OptionsJoypadAutofireA", ID_OPTIONS_JOYPAD_AUTOFIRE_A }, + { "OptionsJoypadAutofireB", ID_OPTIONS_JOYPAD_AUTOFIRE_B }, + { "OptionsJoypadAutofireL", ID_OPTIONS_JOYPAD_AUTOFIRE_L }, + { "OptionsJoypadAutofireR", ID_OPTIONS_JOYPAD_AUTOFIRE_R }, + { "CheatsSearch", ID_CHEATS_SEARCHFORCHEATS }, + { "CheatsList", ID_CHEATS_CHEATLIST }, + { "CheatsLoad", ID_CHEATS_LOADCHEATLIST }, + { "CheatsSave", ID_CHEATS_SAVECHEATLIST }, + { "CheatsDisable", ID_CHEATS_DISABLECHEATS }, + { "ToolsDebugGDB", ID_TOOLS_DEBUG_GDB }, + { "ToolsDebugGDBLoad", ID_TOOLS_DEBUG_LOADANDWAIT }, + { "ToolsDebugGDBBreak", ID_TOOLS_DEBUG_BREAK }, + { "ToolsDebugGDBDisconnect", ID_TOOLS_DEBUG_DISCONNECT }, + { "ToolsDisassemble", ID_TOOLS_DISASSEMBLE }, + { "ToolsIOViewer", ID_TOOLS_IOVIEWER }, + { "ToolsLogging", ID_TOOLS_LOGGING }, + { "ToolsMapViewer", ID_TOOLS_MAPVIEW }, + { "ToolsMemoryViewer", ID_TOOLS_MEMORYVIEWER }, + { "ToolsOAMViewer", ID_TOOLS_OAMVIEWER }, + { "ToolsPaletteViewer", ID_TOOLS_PALETTEVIEW }, + { "ToolsTileViewer", ID_TOOLS_TILEVIEWER }, + { "ToolsNextFrame", ID_DEBUG_NEXTFRAME }, + { "ToolsRecordSoundStartRecording", ID_OPTIONS_SOUND_STARTRECORDING }, + { "ToolsRecordSoundStopRecording", ID_OPTIONS_SOUND_STOPRECORDING }, + { "ToolsRecordAVIStartRecording", ID_TOOLS_RECORD_STARTAVIRECORDING }, + { "ToolsRecordAVIStopRecording", ID_TOOLS_RECORD_STOPAVIRECORDING }, + { "ToolsRecordMovieStartRecording", ID_TOOLS_RECORD_STARTMOVIERECORDING }, + { "ToolsRecordMovieStopRecording", ID_TOOLS_RECORD_STOPMOVIERECORDING }, + { "ToolsPlayMovieStartPlaying", ID_TOOLS_PLAY_STARTMOVIEPLAYING }, + { "ToolsPlayMovieStopPlaying", ID_TOOLS_PLAY_STOPMOVIEPLAYING }, + { "ToolsRewind", ID_TOOLS_REWIND }, + { "ToolsCustomize", ID_TOOLS_CUSTOMIZE }, + { "HelpBugReport", ID_HELP_BUGREPORT }, + { "HelpFAQ", ID_HELP_FAQ }, + { "HelpAbout", ID_HELP_ABOUT }, + { "SystemMinimize", ID_SYSTEM_MINIMIZE } +}; + +bool winAccelGetID(const char *command, WORD& id) +{ + if(!initialized) { + int count = sizeof(winAccelCommands)/sizeof(winAccelCommands[0]); + + for(int i = 0; i < count; i++) { + winAccelStrings.SetAt(winAccelCommands[i].command, winAccelCommands[i].id); + } + initialized = true; + } + + return winAccelStrings.Lookup(command, id) ? true : false; +} + +void winAccelAddCommands(CAcceleratorManager& mgr) +{ + int count = sizeof(winAccelCommands)/sizeof(winAccelCommands[0]); + + for(int i = 0; i < count; i++) { + if(!mgr.AddCommandAccel(winAccelCommands[i].id, winAccelCommands[i].command, false)) + mgr.CreateEntry(winAccelCommands[i].id, winAccelCommands[i].command); + } + +} diff --git a/src/win32/Direct3D.cpp b/src/win32/Direct3D.cpp new file mode 100644 index 0000000..6eac950 --- /dev/null +++ b/src/win32/Direct3D.cpp @@ -0,0 +1,962 @@ +#ifndef NO_D3D + +#pragma comment( lib, "d3d9" ) +#pragma comment( lib, "d3dx9" ) +#pragma comment( lib, "DxErr" ) + +#include "stdafx.h" + +#include "Display.h" + +#include "MainWnd.h" +#include "FullscreenSettings.h" + +#include "../System.h" +#include "../gba/GBA.h" +#include "../gba/Globals.h" +#include "../Util.h" +#include "../gb/gbGlobals.h" + +#include +#include + +// Direct3D +#ifdef _DEBUG +#define D3D_DEBUG_INFO +#endif +#define DIRECT3D_VERSION 0x0900 +#include // main include file +#include // required for font rendering +#include // contains debug functions + +extern int Init_2xSaI(u32); // initializes all pixel filters +extern int systemSpeed; + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +extern void log(const char *,...); +#endif + +#ifdef MMX +extern "C" bool cpu_mmx; +extern bool detectMMX(); +#endif + +struct PFTHREAD_DATA { + void (*filterFunction)(u8*,u32,u8*,u8*,u32,int,int); + u8 *sourcePointer; + u32 sourcePitch; + u8 *deltaPointer; + u8* destPointer; + u32 destPitch; + int width; + int height; +}; + +DWORD WINAPI pfthread_func( LPVOID lpParameter ) +{ + PFTHREAD_DATA *data = (PFTHREAD_DATA*)lpParameter; + + data->filterFunction( + data->sourcePointer, + data->sourcePitch, + data->deltaPointer, + data->destPointer, + data->destPitch, + data->width, + data->height ); + + return 0; +} + +class Direct3DDisplay : public IDisplay { +private: + bool initialized; + LPDIRECT3D9 pD3D; + LPDIRECT3DDEVICE9 pDevice; + D3DDISPLAYMODE mode; + D3DPRESENT_PARAMETERS dpp; + D3DFORMAT screenFormat; + LPDIRECT3DTEXTURE9 tempImage; + LPDIRECT3DTEXTURE9 emulatedImage[2]; + unsigned char mbCurrentTexture; // current texture for motion blur + bool mbTextureEmpty; + unsigned int width, height; + unsigned int textureSize; + RECT destRect; + bool failed; + ID3DXFont *pFont; + bool rectangleFillsScreen; + PFTHREAD_DATA *pfthread_data; + HANDLE *hThreads; + unsigned int nThreads; + + struct VERTEX { + FLOAT x, y, z, rhw; // screen coordinates + FLOAT tx, ty; // texture coordinates + } Vertices[4]; + // Vertices order: + // 1 3 + // 0 2 + + struct TRANSP_VERTEX { + FLOAT x, y, z, rhw; + D3DCOLOR color; + FLOAT tx, ty; + } transpVertices[4]; + + void createFont(); + void destroyFont(); + bool clearTexture( LPDIRECT3DTEXTURE9 texture, size_t textureHeight ); + void createTexture( unsigned int textureWidth, unsigned int textureHeight ); + void destroyTexture(); + void calculateDestRect(); + bool resetDevice(); + void prepareDisplayMode(); + +public: + Direct3DDisplay(); + virtual ~Direct3DDisplay(); + virtual DISPLAY_TYPE getType() { return DIRECT_3D; }; + + virtual bool initialize(); + virtual void cleanup(); + virtual void clear(); + virtual void render(); + + virtual bool changeRenderSize( int w, int h ); + virtual void resize( int w, int h ); + virtual void setOption( const char *option, int value ); + virtual bool selectFullScreenMode( VIDEO_MODE &mode ); +}; + + +Direct3DDisplay::Direct3DDisplay() +{ + initialized = false; + pD3D = NULL; + pDevice = NULL; + screenFormat = D3DFMT_X8R8G8B8; + width = 0; + height = 0; + textureSize = 0; + failed = false; + pFont = NULL; + tempImage = NULL; + emulatedImage[0] = NULL; + emulatedImage[1] = NULL; + mbCurrentTexture = 0; + mbTextureEmpty = true; + rectangleFillsScreen = false; + pfthread_data = NULL; + hThreads = NULL; + nThreads = theApp.maxCpuCores; + if( nThreads > 8 ) nThreads = 8; +} + + +Direct3DDisplay::~Direct3DDisplay() +{ + cleanup(); +} + +void Direct3DDisplay::prepareDisplayMode() +{ + // Change display mode + memset(&dpp, 0, sizeof(dpp)); + dpp.Windowed = !( theApp.videoOption >= VIDEO_320x240 ); + if( !dpp.Windowed ) { + dpp.BackBufferFormat = (theApp.fsColorDepth == 32) ? D3DFMT_X8R8G8B8 : D3DFMT_R5G6B5; + } else { + dpp.BackBufferFormat = mode.Format; + } + dpp.BackBufferCount = theApp.tripleBuffering ? 2 : 1; + dpp.MultiSampleType = D3DMULTISAMPLE_NONE; + dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; + dpp.BackBufferWidth = !dpp.Windowed ? theApp.fsWidth : theApp.surfaceSizeX; + dpp.BackBufferHeight = !dpp.Windowed ? theApp.fsHeight : theApp.surfaceSizeY; + dpp.hDeviceWindow = theApp.m_pMainWnd->GetSafeHwnd(); + dpp.FullScreen_RefreshRateInHz = ( dpp.Windowed == TRUE ) ? 0 : theApp.fsFrequency; + dpp.Flags = 0; + dpp.PresentationInterval = theApp.vsync ? D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE; + // D3DPRESENT_INTERVAL_ONE means VSync ON + + +#ifdef _DEBUG + // make debugging full screen easier + if( dpp.Windowed == FALSE ) { + dpp.Windowed = TRUE; + dpp.BackBufferFormat = D3DFMT_UNKNOWN; + dpp.BackBufferCount = 0; + dpp.FullScreen_RefreshRateInHz = 0; + dpp.Flags = 0; + } + + TRACE( _T("prepareDisplayMode:\n") ); + TRACE( _T("%i x %i @ %iHz:\n"), dpp.BackBufferWidth, dpp.BackBufferHeight, dpp.FullScreen_RefreshRateInHz ); + TRACE( _T("Buffer Count: %i\n"), dpp.BackBufferCount+1 ); + TRACE( _T("VSync: %i\n"), dpp.PresentationInterval==D3DPRESENT_INTERVAL_ONE ); + TRACE( _T("LOCKABLE_BACKBUFFER: %i\n\n"), dpp.Flags==D3DPRESENTFLAG_LOCKABLE_BACKBUFFER ); +#endif +} + + +void Direct3DDisplay::cleanup() +{ + if( hThreads ) { + free( hThreads ); + hThreads = NULL; + } + + if( pfthread_data ) { + free( pfthread_data ); + pfthread_data = NULL; + } + + destroyFont(); + destroyTexture(); + + if( pDevice ) { + pDevice->Release(); + pDevice = NULL; + } + + if( pD3D ) { + pD3D->Release(); + pD3D = NULL; + } +} + + +bool Direct3DDisplay::initialize() +{ +#ifdef _DEBUG + TRACE( _T("Initializing Direct3D renderer {\n") ); +#endif + + // load Direct3D v9 + pD3D = Direct3DCreate9( D3D_SDK_VERSION ); + + if(pD3D == NULL) { + DXTRACE_ERR_MSGBOX( _T("Error creating Direct3D object"), 0 ); + return false; + } + + pD3D->GetAdapterDisplayMode(theApp.fsAdapter, &mode); + screenFormat = mode.Format; + + switch(mode.Format) { + case D3DFMT_X8R8G8B8: + systemColorDepth = 32; + systemRedShift = 19; + systemGreenShift = 11; + systemBlueShift = 3; + Init_2xSaI(32); + break; + case D3DFMT_R5G6B5: + systemColorDepth = 16; + systemRedShift = 11; + systemGreenShift = 6; + systemBlueShift = 0; + Init_2xSaI(565); + break; + case D3DFMT_X1R5G5B5: + systemColorDepth = 16; + systemRedShift = 10; + systemGreenShift = 5; + systemBlueShift = 0; + Init_2xSaI(555); + break; + default: + DXTRACE_ERR_MSGBOX( _T("Unsupport D3D format"), 0 ); + return false; + } + theApp.fsColorDepth = systemColorDepth; + utilUpdateSystemColorMaps(theApp.cartridgeType == IMAGE_GBA && gbColorOption == 1); + + +#ifdef MMX + if(!theApp.disableMMX) { + cpu_mmx = theApp.detectMMX(); + } else { + cpu_mmx = 0; + } +#endif + + + theApp.updateFilter(); + theApp.updateIFB(); + + + // create device + // Direct3D will use the selected full screen adapter for windowed mode as well + prepareDisplayMode(); + + HRESULT hret = pD3D->CreateDevice( + theApp.fsAdapter, + D3DDEVTYPE_HAL, + theApp.m_pMainWnd->GetSafeHwnd(), + D3DCREATE_FPU_PRESERVE | + D3DCREATE_SOFTWARE_VERTEXPROCESSING, + &dpp, + &pDevice); + if( FAILED( hret ) ) { + DXTRACE_ERR_MSGBOX( _T("Error creating Direct3D device"), hret ); + return false; + } + + createFont(); + // width and height will be set from a prior call to changeRenderSize() before initialize() + createTexture( width, height ); + calculateDestRect(); + setOption( _T("d3dFilter"), theApp.d3dFilter ); + setOption( _T("motionBlur"), theApp.d3dMotionBlur ); + + // create pfthread_data + pfthread_data = (PFTHREAD_DATA*)malloc( sizeof(PFTHREAD_DATA) * nThreads ); + if( !pfthread_data ) { + failed = true; + } + + // create thread handles + hThreads = (HANDLE*)malloc( sizeof(HANDLE) * nThreads ); + if( !hThreads ) { + failed = true; + } + + if(failed) return false; + + initialized = true; + +#ifdef _DEBUG + TRACE( _T("} Finished Direct3D renderer initialization\n\n") ); +#endif + + return TRUE; +} + + +void Direct3DDisplay::clear() +{ + if( pDevice ) { +#ifdef _DEBUG + pDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0xFF,0x00,0xFF), 0.0f, 0 ); +#else + pDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0x00,0x00,0x00), 0.0f, 0 ); +#endif + } +} + + +void Direct3DDisplay::render() +{ + if( failed ) return; + if(!pDevice) return; + + // Multi-Tasking fix + HRESULT hr = pDevice->TestCooperativeLevel(); + if( FAILED( hr ) ) { + switch( hr ) { + case D3DERR_DEVICELOST: + // The device has been lost but cannot be reset at this time. + // Therefore, rendering is not possible. + return; + case D3DERR_DEVICENOTRESET: + // The device has been lost but can be reset at this time. + resetDevice(); + return; + default: + DXTRACE_ERR( _T("ERROR: D3D device has serious problems"), hr ); + return; + } + } + + if( !rectangleFillsScreen ) { + // performance: clear only when you must + clear(); + } + + pDevice->BeginScene(); + + // copy pix to tempImage and apply pixel filter if selected + D3DLOCKED_RECT lr; + const RECT target = { 0, 0, width, height }; + + if( FAILED( hr = tempImage->LockRect( 0, &lr, &target, 0 ) ) ) { + DXTRACE_ERR_MSGBOX( _T("Can not lock texture"), hr ); + return; + } else { + u32 pitch = theApp.sizeX * ( systemColorDepth >> 3 ) + 4; + + if( theApp.filterFunction ) { + if( theApp.filterMT ) { + u8 *start = pix + pitch; + int src_height_per_thread = theApp.sizeY / nThreads; + int src_height_remaining = theApp.sizeY - ( ( theApp.sizeY / nThreads ) * nThreads ); + u32 src_bytes_per_thread = pitch * src_height_per_thread; + + int dst_height_per_thread = src_height_per_thread * theApp.filterMagnification; + u32 dst_bytes_per_thread = lr.Pitch * dst_height_per_thread; + + unsigned int i = nThreads - 1; + + // Use Multi Threading + do { + // create last thread first because it could have more work than the others (for eg. if nThreads = 3) + // (last thread has to process the remaining lines if (height / nThreads) is not an integer) + + // configure thread + pfthread_data[i].filterFunction = theApp.filterFunction; + pfthread_data[i].sourcePointer = start + ( i * src_bytes_per_thread ); + pfthread_data[i].sourcePitch = pitch; + pfthread_data[i].deltaPointer = (u8*)theApp.delta; // TODO: check if thread-safe + pfthread_data[i].destPointer = ( (u8*)lr.pBits ) + ( i * dst_bytes_per_thread ); + pfthread_data[i].destPitch = lr.Pitch; + pfthread_data[i].width = theApp.sizeX; + + if( i == ( nThreads - 1 ) ) { + // last thread + pfthread_data[i].height = src_height_per_thread + src_height_remaining; + } else { + // other thread + pfthread_data[i].height = src_height_per_thread; + } + + // create thread + hThreads[i] = CreateThread( + NULL, + 0, + pfthread_func, + &pfthread_data[i], + 0, + NULL ); + assert( hThreads[i] != NULL ); + } while ( i-- ); + + // Wait until every thread has finished. + WaitForMultipleObjects( + nThreads, + hThreads, + TRUE, + INFINITE ); + + // Close all thread handles. + for( i = 0 ; i < nThreads ; i++ ) { + CloseHandle( hThreads[i] ); + } + } else { + // multi-threading disabled + theApp.filterFunction( + pix + pitch, + pitch, + (u8*)theApp.delta, + (u8*)lr.pBits, + lr.Pitch, + theApp.sizeX, + theApp.sizeY + ); + } + } else { + // pixel filter disabled + switch( systemColorDepth ) + { + case 32: + cpyImg32( + (unsigned char *)lr.pBits, + lr.Pitch, + pix + pitch, + pitch, + theApp.sizeX, + theApp.sizeY + ); + break; + case 16: + cpyImg16( + (unsigned char *)lr.pBits, + lr.Pitch, + pix + pitch, + pitch, + theApp.sizeX, + theApp.sizeY + ); + break; + } + } + tempImage->UnlockRect( 0 ); + pDevice->UpdateTexture( tempImage, emulatedImage[ mbCurrentTexture ] ); + } + + + if( !theApp.d3dMotionBlur ) { + // draw the current frame to the screen + pDevice->SetTexture( 0, emulatedImage[ mbCurrentTexture ] ); + pDevice->SetFVF( D3DFVF_XYZRHW | D3DFVF_TEX1 ); + pDevice->DrawPrimitiveUP( D3DPT_TRIANGLESTRIP, 2, Vertices, sizeof(VERTEX) ); + } else { + // Motion Blur enabled + if( !mbTextureEmpty ) { + // draw previous frame to the screen + pDevice->SetTexture( 0, emulatedImage[ mbCurrentTexture ^ 1 ] ); + pDevice->SetFVF( D3DFVF_XYZRHW | D3DFVF_TEX1 ); + pDevice->DrawPrimitiveUP( D3DPT_TRIANGLESTRIP, 2, Vertices, sizeof(VERTEX) ); + // draw the current frame with transparency to the screen + pDevice->SetTexture( 0, emulatedImage[ mbCurrentTexture ] ); + pDevice->SetFVF( D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1 ); + pDevice->DrawPrimitiveUP( D3DPT_TRIANGLESTRIP, 2, transpVertices, sizeof(TRANSP_VERTEX) ); + } else { + mbTextureEmpty = false; + // draw the current frame to the screen + pDevice->SetTexture( 0, emulatedImage[ mbCurrentTexture ] ); + pDevice->SetFVF( D3DFVF_XYZRHW | D3DFVF_TEX1 ); + pDevice->DrawPrimitiveUP( D3DPT_TRIANGLESTRIP, 2, Vertices, sizeof(VERTEX) ); + } + mbCurrentTexture ^= 1; // switch current texture + } + + + // render speed and status messages + D3DCOLOR color; + RECT r; + r.left = 0; + r.top = 0; + r.right = dpp.BackBufferWidth - 1; + r.bottom = dpp.BackBufferHeight - 1; + + if( theApp.showSpeed && ( theApp.videoOption > VIDEO_6X ) ) { + color = theApp.showSpeedTransparent ? D3DCOLOR_ARGB(0x7F, 0x00, 0x00, 0xFF) : D3DCOLOR_ARGB(0xFF, 0x00, 0x00, 0xFF); + char buffer[30]; + if( theApp.showSpeed == 1 ) { + sprintf( buffer, "%3d%%", systemSpeed ); + } else { + sprintf( buffer, "%3d%%(%d, %d fps)", systemSpeed, systemFrameSkip, theApp.showRenderedFrames ); + } + + pFont->DrawText( NULL, buffer, -1, &r, DT_CENTER | DT_TOP, color ); + } + + if( theApp.screenMessage ) { + color = theApp.showSpeedTransparent ? D3DCOLOR_ARGB(0x7F, 0xFF, 0x00, 0x00) : D3DCOLOR_ARGB(0xFF, 0xFF, 0x00, 0x00); + if( ( ( GetTickCount() - theApp.screenMessageTime ) < 3000 ) && !theApp.disableStatusMessage && pFont ) { + pFont->DrawText( NULL, theApp.screenMessageBuffer, -1, &r, DT_CENTER | DT_BOTTOM, color ); + } else { + theApp.screenMessage = false; + } + } + + pDevice->EndScene(); + + pDevice->Present( NULL, NULL, NULL, NULL ); + + return; +} + + +bool Direct3DDisplay::changeRenderSize( int w, int h ) +{ + if( (w != width) || (h != height) ) { + width = (unsigned int)w; + height = (unsigned int)h; + if( pDevice ) { + destroyTexture(); + createTexture( width, height ); + calculateDestRect(); + } + } + return true; +} + + +void Direct3DDisplay::resize( int w, int h ) +{ + if( !initialized ) { + return; + } + + if( (w != dpp.BackBufferWidth) || + (h != dpp.BackBufferHeight) || + (theApp.videoOption > VIDEO_6X) ) { + resetDevice(); + calculateDestRect(); + } +} + + +bool Direct3DDisplay::selectFullScreenMode( VIDEO_MODE &mode ) +{ + FullscreenSettings dlg; + dlg.setAPI( this->getType() ); + INT_PTR ret = dlg.DoModal(); + if( ret == IDOK ) { + mode.adapter = dlg.m_device; + switch( dlg.m_colorDepth ) + { + case 30: + // TODO: support + return false; + break; + case 24: + mode.bitDepth = 32; + break; + case 16: + case 15: + mode.bitDepth = 16; + break; + } + mode.width = dlg.m_width; + mode.height = dlg.m_height; + mode.frequency = dlg.m_refreshRate; + return true; + } else { + return false; + } +} + + +void Direct3DDisplay::createFont() +{ + if( !pFont ) { + HRESULT hr = D3DXCreateFont( + pDevice, + dpp.BackBufferHeight/20, // dynamic font size + 0, + FW_BOLD, + 1, + FALSE, + DEFAULT_CHARSET, + OUT_DEFAULT_PRECIS, + DEFAULT_QUALITY, + DEFAULT_PITCH || FF_DONTCARE, + _T("Arial"), + &pFont ); + if( FAILED( hr ) ) { + DXTRACE_ERR_MSGBOX( _T("createFont failed"), hr ); + } + } +} + + +void Direct3DDisplay::destroyFont() +{ + if( pFont ) { + pFont->Release(); + pFont = NULL; + } +} + + +// fill texture completely with black +bool Direct3DDisplay::clearTexture( LPDIRECT3DTEXTURE9 texture, size_t textureHeight ) +{ + D3DLOCKED_RECT lr; + HRESULT hr; + + if( FAILED( hr = texture->LockRect( 0, &lr, NULL, 0 ) ) ) { + DXTRACE_ERR_MSGBOX( _T("Can not lock texture"), hr ); + return false; + } else { + memset( lr.pBits, 0x00, lr.Pitch * textureHeight ); + texture->UnlockRect( 0 ); + return true; + } +} + + +// when either textureWidth or textureHeight is 0, last texture size will be used +void Direct3DDisplay::createTexture( unsigned int textureWidth, unsigned int textureHeight ) +{ + if( ( textureWidth != 0 ) && ( textureHeight != 0 ) ) { + // calculate next possible square texture size + textureSize = 1; + unsigned int reqSizeMin = ( textureWidth > textureHeight ) ? textureWidth : textureHeight; + while( textureSize < reqSizeMin ) { + textureSize <<= 1; // multiply by 2 + } + } else { + // do not recalculate texture size + + if( textureSize == 0 ) { + DXTRACE_MSG( _T("Error: createTexture: textureSize == 0") ); + return; + } + } + + + if( !tempImage ) { + HRESULT hr = pDevice->CreateTexture( + textureSize, textureSize, + 1, // 1 level, no mipmaps + 0, // dynamic textures can be locked + dpp.BackBufferFormat, + D3DPOOL_SYSTEMMEM, + &tempImage, + NULL ); + + if( FAILED( hr ) ) { + DXTRACE_ERR_MSGBOX( _T("createTexture(temp) failed"), hr ); + return; + } + + // initialize whole texture with black since we might see + // the initial noise when using bilinear texture filtering + clearTexture( tempImage, textureSize ); + } + + + if( !emulatedImage[0] ) { + HRESULT hr = pDevice->CreateTexture( + textureSize, textureSize, + 1, // 1 level, no mipmaps + 0, + dpp.BackBufferFormat, + D3DPOOL_DEFAULT, + &emulatedImage[0], + NULL ); + + if( FAILED( hr ) ) { + DXTRACE_ERR_MSGBOX( _T("createTexture(0) failed"), hr ); + return; + } + } + + if( !emulatedImage[1] && theApp.d3dMotionBlur ) { + HRESULT hr = pDevice->CreateTexture( + textureSize, textureSize, + 1, + 0, + dpp.BackBufferFormat, + D3DPOOL_DEFAULT, + &emulatedImage[1], + NULL ); + + if( FAILED( hr ) ) { + DXTRACE_ERR_MSGBOX( _T("createTexture(1) failed"), hr ); + return; + } + + mbTextureEmpty = true; + } +} + + +void Direct3DDisplay::destroyTexture() +{ + if( tempImage ) { + tempImage->Release(); + tempImage = NULL; + } + + if( emulatedImage[0] ) { + emulatedImage[0]->Release(); + emulatedImage[0] = NULL; + } + + if( emulatedImage[1] ) { + emulatedImage[1]->Release(); + emulatedImage[1] = NULL; + } +} + + +void Direct3DDisplay::calculateDestRect() +{ + if( theApp.fullScreenStretch ) { + rectangleFillsScreen = true; // no clear() necessary + destRect.left = 0; + destRect.top = 0; + destRect.right = dpp.BackBufferWidth; // for some reason there will be a black + destRect.bottom = dpp.BackBufferHeight; // border line when using -1 at the end + } else { + // use aspect ratio + float scaleX = (float)dpp.BackBufferWidth / (float)width; + float scaleY = (float)dpp.BackBufferHeight / (float)height; + float min = (scaleX < scaleY) ? scaleX : scaleY; + if( theApp.maxScale && (min > theApp.maxScale) ) { + min = (float)theApp.maxScale; + } + destRect.left = 0; + destRect.top = 0; + destRect.right = (LONG)(width * min); + destRect.bottom = (LONG)(height * min); + if( destRect.right != dpp.BackBufferWidth ) { + LONG diff = (dpp.BackBufferWidth - destRect.right) / 2; + destRect.left += diff; + destRect.right += diff; + } + if( destRect.bottom != dpp.BackBufferHeight ) { + LONG diff = (dpp.BackBufferHeight - destRect.bottom) / 2; + destRect.top += diff; + destRect.bottom += diff; + } + + if( ( destRect.left == 0 ) && + ( destRect.top == 0 ) && + ( destRect.right == dpp.BackBufferWidth ) && + ( destRect.bottom == dpp.BackBufferHeight ) ) { + rectangleFillsScreen = true; + } else { + rectangleFillsScreen = false; + } + } + + FLOAT textureX = (FLOAT)width / (FLOAT)textureSize; + FLOAT textureY = (FLOAT)height / (FLOAT)textureSize; + + // configure triangles + Vertices[0].x = (FLOAT)destRect.left - 0.5f; + // -0.5f is necessary in order to match texture alignment to display pixels + Vertices[0].y = (FLOAT)destRect.bottom - 0.5f; + Vertices[0].z = 0.0f; + Vertices[0].rhw = 1.0f; + Vertices[0].tx = 0.0f; + Vertices[0].ty = textureY; + Vertices[1].x = (FLOAT)destRect.left - 0.5f; + Vertices[1].y = (FLOAT)destRect.top - 0.5f; + Vertices[1].z = 0.0f; + Vertices[1].rhw = 1.0f; + Vertices[1].tx = 0.0f; + Vertices[1].ty = 0.0f; + Vertices[2].x = (FLOAT)destRect.right - 0.5f; + Vertices[2].y = (FLOAT)destRect.bottom - 0.5f; + Vertices[2].z = 0.0f; + Vertices[2].rhw = 1.0f; + Vertices[2].tx = textureX; + Vertices[2].ty = textureY; + Vertices[3].x = (FLOAT)destRect.right - 0.5f; + Vertices[3].y = (FLOAT)destRect.top - 0.5f; + Vertices[3].z = 0.0f; + Vertices[3].rhw = 1.0f; + Vertices[3].tx = textureX; + Vertices[3].ty = 0.0f; + + if( theApp.d3dMotionBlur ) { + // configure semi-transparent triangles + D3DCOLOR semiTrans = D3DCOLOR_ARGB( 0x7F, 0xFF, 0xFF, 0xFF ); + transpVertices[0].x = Vertices[0].x; + transpVertices[0].y = Vertices[0].y; + transpVertices[0].z = Vertices[0].z; + transpVertices[0].rhw = Vertices[0].rhw; + transpVertices[0].color = semiTrans; + transpVertices[0].tx = Vertices[0].tx; + transpVertices[0].ty = Vertices[0].ty; + transpVertices[1].x = Vertices[1].x; + transpVertices[1].y = Vertices[1].y; + transpVertices[1].z = Vertices[1].z; + transpVertices[1].rhw = Vertices[1].rhw; + transpVertices[1].color = semiTrans; + transpVertices[1].tx = Vertices[1].tx; + transpVertices[1].ty = Vertices[1].ty; + transpVertices[2].x = Vertices[2].x; + transpVertices[2].y = Vertices[2].y; + transpVertices[2].z = Vertices[2].z; + transpVertices[2].rhw = Vertices[2].rhw; + transpVertices[2].color = semiTrans; + transpVertices[2].tx = Vertices[2].tx; + transpVertices[2].ty = Vertices[2].ty; + transpVertices[3].x = Vertices[3].x; + transpVertices[3].y = Vertices[3].y; + transpVertices[3].z = Vertices[3].z; + transpVertices[3].rhw = Vertices[3].rhw; + transpVertices[3].color = semiTrans; + transpVertices[3].tx = Vertices[3].tx; + transpVertices[3].ty = Vertices[3].ty; + } +} + + +void Direct3DDisplay::setOption( const char *option, int value ) +{ + if( !_tcscmp( option, _T("vsync") ) ) { + // value of theApp.vsync has already been changed by the menu handler + // 'value' has the same value as theApp.vsync + resetDevice(); + } + + if( !_tcscmp( option, _T("tripleBuffering") ) ) { + // value of theApp.tripleBuffering has already been changed by the menu handler + // 'value' has the same value as theApp.tripleBuffering + resetDevice(); + } + + if( !_tcscmp( option, _T("d3dFilter") ) ) { + switch( value ) + { + case 0: //point + pDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_POINT ); + pDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_POINT ); + break; + case 1: //linear + pDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR ); + pDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR ); + break; + } + } + + if( !_tcscmp( option, _T("maxScale") ) ) { + calculateDestRect(); + } + + if( !_tcscmp( option, _T("fullScreenStretch") ) ) { + calculateDestRect(); + } + + if( !_tcscmp( option, _T("motionBlur") ) ) { + switch( value ) + { + case 0: + mbCurrentTexture = 0; + pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE ); + break; + case 1: + // enable vertex alpha blending + pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE ); + pDevice->SetRenderState( D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_COLOR1 ); + pDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA ); + pDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA ); + // apply vertex alpha values to texture + pDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE ); + calculateDestRect(); + createTexture( 0, 0 ); // create the second texture + break; + } + } +} + + +bool Direct3DDisplay::resetDevice() +{ + if( !pDevice ) return false; + + HRESULT hr; + if( pFont ) { + // prepares font for reset + pFont->OnLostDevice(); + } + destroyTexture(); + prepareDisplayMode(); + + if( FAILED( hr = pDevice->Reset( &dpp ) ) ) { + DXTRACE_ERR( _T("pDevice->Reset failed\n"), hr ); + failed = true; + return false; + } + + if( pFont ) { + // re-aquires font resources + pFont->OnResetDevice(); + } + createTexture( 0, 0 ); + setOption( _T("d3dFilter"), theApp.d3dFilter ); + setOption( _T("motionBlur"), theApp.d3dMotionBlur ); + failed = false; + return true; +} + + +IDisplay *newDirect3DDisplay() +{ + return new Direct3DDisplay(); +} + +#endif diff --git a/src/win32/DirectInput.cpp b/src/win32/DirectInput.cpp new file mode 100644 index 0000000..821ed59 --- /dev/null +++ b/src/win32/DirectInput.cpp @@ -0,0 +1,826 @@ +#include "stdafx.h" +#include "VBA.h" +#include "Input.h" +#include "Reg.h" +#include "WinResUtil.h" + +#define DIRECTINPUT_VERSION 0x0800 +#include +#pragma comment( lib, "dinput8" ) +#pragma comment( lib, "dxguid" ) + + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +extern void directXMessage(const char *); +extern void winlog(const char *msg,...); + +#define POV_UP 1 +#define POV_DOWN 2 +#define POV_RIGHT 4 +#define POV_LEFT 8 + +class DirectInput : public Input { +public: + virtual void checkDevices(); + DirectInput(); + virtual ~DirectInput(); + + virtual bool initialize(); + virtual bool readDevices(); + virtual u32 readDevice(int which); + virtual CString getKeyName(LONG_PTR key); + virtual void checkKeys(); + virtual void checkMotionKeys(); + virtual void activate(); + virtual void loadSettings(); + virtual void saveSettings(); +}; + +struct deviceInfo { + LPDIRECTINPUTDEVICE8 device; + BOOL isPolled; + int nButtons; + int nAxes; + int nPovs; + BOOL first; + struct { + DWORD offset; + LONG center; + LONG negative; + LONG positive; + } axis[8]; + int needed; + union { + UCHAR data[256]; + DIJOYSTATE state; + }; +}; + +static deviceInfo *currentDevice = NULL; +static int numDevices = 1; +static deviceInfo *pDevices = NULL; +static LPDIRECTINPUT8 pDirectInput = NULL; +static int axisNumber = 0; + + + + +LONG_PTR defvalues[JOYPADS * KEYS_PER_PAD + MOTION_KEYS] = + { + DIK_LEFT, DIK_RIGHT, + DIK_UP, DIK_DOWN, + DIK_X, DIK_Z, + DIK_RETURN,DIK_BACK, + DIK_A, DIK_S, + DIK_SPACE, DIK_F12, + DIK_C, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + DIK_NUMPAD4, DIK_NUMPAD6, DIK_NUMPAD8, DIK_NUMPAD2 +}; + + +void winReadKey(const char *name, KeyList& Keys) +{ + CString TxtKeyList = regQueryStringValue(name, ""); + int curPos= 0; + + CString resToken=TxtKeyList.Tokenize(",",curPos); + while (resToken != "") + { + Keys.AddTail(atoi(resToken)); + resToken= TxtKeyList.Tokenize(",",curPos); + }; +} + +void winReadKey(const char *name, int num, KeyList& Keys) +{ + char buffer[80]; + + sprintf(buffer, "Joy%d_%s", num, name); + winReadKey(buffer, Keys); +} + + +void winReadKeys() +{ + + for(int i = 0; i < JOYPADS; i++) { + winReadKey("Left", i, theApp.input->joypaddata[JOYPAD(i,KEY_LEFT)]); + winReadKey("Right", i, theApp.input->joypaddata[JOYPAD(i, KEY_RIGHT)]); + winReadKey("Up", i, theApp.input->joypaddata[JOYPAD(i,KEY_UP)]); + winReadKey("Down", i, theApp.input->joypaddata[JOYPAD(i,KEY_DOWN)]); + winReadKey("A", i, theApp.input->joypaddata[JOYPAD(i,KEY_BUTTON_A)]); + winReadKey("B", i, theApp.input->joypaddata[JOYPAD(i,KEY_BUTTON_B)]); + winReadKey("L", i, theApp.input->joypaddata[JOYPAD(i,KEY_BUTTON_L)]); + winReadKey("R", i, theApp.input->joypaddata[JOYPAD(i,KEY_BUTTON_R)]); + winReadKey("Start", i, theApp.input->joypaddata[JOYPAD(i,KEY_BUTTON_START)]); + winReadKey("Select", i, theApp.input->joypaddata[JOYPAD(i,KEY_BUTTON_SELECT)]); + winReadKey("Speed", i, theApp.input->joypaddata[JOYPAD(i,KEY_BUTTON_SPEED)]); + winReadKey("Capture", i, theApp.input->joypaddata[JOYPAD(i,KEY_BUTTON_CAPTURE)]); + winReadKey("GS", i, theApp.input->joypaddata[JOYPAD(i,KEY_BUTTON_GS)]); + } + winReadKey("Motion_Left", theApp.input->joypaddata[MOTION(KEY_LEFT)]); + winReadKey("Motion_Right", theApp.input->joypaddata[MOTION(KEY_RIGHT)]); + winReadKey("Motion_Up", theApp.input->joypaddata[MOTION(KEY_UP)]); + winReadKey("Motion_Down", theApp.input->joypaddata[MOTION(KEY_DOWN)]); +} + +void winSaveKey(char *name, KeyList& value) +{ + CString txtKeys; + + POSITION p = value.GetHeadPosition(); + while(p!=NULL) + { + CString tmp; + tmp.Format("%d", value.GetNext(p)); + txtKeys+=tmp; + if (p!=NULL) + txtKeys+=","; + } + regSetStringValue(name, txtKeys); +} + +static void winSaveKey(char *name, int num, KeyList& value) +{ + char buffer[80]; + + sprintf(buffer, "Joy%d_%s", num, name); + winSaveKey(buffer, value); +} + +void winSaveKeys() +{ + for(int i = 0; i < JOYPADS; i++) { + winSaveKey("Left", i, theApp.input->joypaddata[JOYPAD(i,KEY_LEFT)]); + winSaveKey("Right", i, theApp.input->joypaddata[JOYPAD(i,KEY_RIGHT)]); + winSaveKey("Up", i, theApp.input->joypaddata[JOYPAD(i,KEY_UP)]); + winSaveKey("Speed", i, theApp.input->joypaddata[JOYPAD(i,KEY_BUTTON_SPEED)]); + winSaveKey("Capture", i, theApp.input->joypaddata[JOYPAD(i,KEY_BUTTON_CAPTURE)]); + winSaveKey("GS", i, theApp.input->joypaddata[JOYPAD(i,KEY_BUTTON_GS)]); + winSaveKey("Down", i, theApp.input->joypaddata[JOYPAD(i,KEY_DOWN)]); + winSaveKey("A", i, theApp.input->joypaddata[JOYPAD(i,KEY_BUTTON_A)]); + winSaveKey("B", i, theApp.input->joypaddata[JOYPAD(i,KEY_BUTTON_B)]); + winSaveKey("L", i, theApp.input->joypaddata[JOYPAD(i,KEY_BUTTON_L)]); + winSaveKey("R", i, theApp.input->joypaddata[JOYPAD(i,KEY_BUTTON_R)]); + winSaveKey("Start", i, theApp.input->joypaddata[JOYPAD(i,KEY_BUTTON_START)]); + winSaveKey("Select", i, theApp.input->joypaddata[JOYPAD(i,KEY_BUTTON_SELECT)]); + } + regSetDwordValue("joyVersion", 1); + + winSaveKey("Motion_Left", + theApp.input->joypaddata[MOTION(KEY_LEFT)]); + winSaveKey("Motion_Right", + theApp.input->joypaddata[MOTION(KEY_RIGHT)]); + winSaveKey("Motion_Up", + theApp.input->joypaddata[MOTION(KEY_UP)]); + winSaveKey("Motion_Down", + theApp.input->joypaddata[MOTION(KEY_DOWN)]); +} + +static BOOL CALLBACK EnumPovsCallback( const DIDEVICEOBJECTINSTANCE* pdidoi, + VOID* pContext ) +{ + return DIENUM_CONTINUE; +} + +static BOOL CALLBACK DIEnumDevicesCallback(LPCDIDEVICEINSTANCE pInst, + LPVOID lpvContext) +{ + ZeroMemory(&pDevices[numDevices],sizeof(deviceInfo)); + + HRESULT hRet = pDirectInput->CreateDevice(pInst->guidInstance, + &pDevices[numDevices].device, + NULL); + + if (hRet != DI_OK) + return DIENUM_STOP; + + DIDEVCAPS caps; + caps.dwSize=sizeof(DIDEVCAPS); + + hRet = pDevices[numDevices].device->GetCapabilities(&caps); + + if (hRet == DI_OK) { + if (caps.dwFlags & DIDC_POLLEDDATAFORMAT || + caps.dwFlags & DIDC_POLLEDDEVICE) + pDevices[numDevices].isPolled = TRUE; + + pDevices[numDevices].nButtons = caps.dwButtons; + pDevices[numDevices].nAxes = caps.dwAxes; + pDevices[numDevices].nPovs = caps.dwPOVs; + + for (int i = 0; i < 6; i++) { + pDevices[numDevices].axis[i].center = 0x8000; + pDevices[numDevices].axis[i].negative = 0x4000; + pDevices[numDevices].axis[i].positive = 0xc000; + } + } + + + numDevices++; + + + return DIENUM_CONTINUE; +} + +BOOL CALLBACK DIEnumDevicesCallback2(LPCDIDEVICEINSTANCE pInst, + LPVOID lpvContext) +{ + numDevices++; + + return DIENUM_CONTINUE; +} + +static int getPovState(DWORD value) +{ + int state = 0; + if (LOWORD(value) != 0xFFFF) { + if (value < 9000 || value > 27000) + state |= POV_UP; + if (value > 0 && value < 18000) + state |= POV_RIGHT; + if (value > 9000 && value < 27000) + state |= POV_DOWN; + if (value > 18000) + state |= POV_LEFT; + } + return state; +} + +static void checkKeys() +{ + LONG_PTR dev = 0; + int i; + + for(i = 0; i < (sizeof(theApp.input->joypaddata) / sizeof(theApp.input->joypaddata[0])); i++) + { + if (theApp.input->joypaddata[i].IsEmpty() && defvalues[i]) + theApp.input->joypaddata[i].AddTail(defvalues[i]); + POSITION p = theApp.input->joypaddata[i].GetHeadPosition(); + while(p!=NULL) + { + LONG_PTR k = theApp.input->joypaddata[i].GetNext(p); + if (k > 0 && DEVICEOF(k) < numDevices) + pDevices[DEVICEOF(k)].needed = true; + } + } +} + +#define KEYDOWN(buffer,key) (buffer[key] & 0x80) + +static bool readKeyboard() +{ + if (pDevices[0].needed) { + HRESULT hret = pDevices[0].device-> + GetDeviceState(256, + (LPVOID)pDevices[0].data); + + if (hret == DIERR_INPUTLOST || hret == DIERR_NOTACQUIRED) { + hret = pDevices[0].device->Acquire(); + if (hret != DI_OK) + return false; + hret = pDevices[0].device->GetDeviceState(256,(LPVOID)pDevices[0].data); + } + + return hret == DI_OK; + } + return true; +} + +static bool readJoystick(int joy) +{ + if (pDevices[joy].needed) { + if (pDevices[joy].isPolled) + ((LPDIRECTINPUTDEVICE2)pDevices[joy].device)->Poll(); + + HRESULT hret = pDevices[joy].device-> + GetDeviceState(sizeof(DIJOYSTATE), + (LPVOID)&pDevices[joy].state); + + if (hret == DIERR_INPUTLOST || hret == DIERR_NOTACQUIRED) { + hret = pDevices[joy].device->Acquire(); + + if (hret == DI_OK) { + + if (pDevices[joy].isPolled) + ((LPDIRECTINPUTDEVICE2)pDevices[joy].device)->Poll(); + + hret = pDevices[joy].device-> + GetDeviceState(sizeof(DIJOYSTATE), + (LPVOID)&pDevices[joy].state); + } + } + + return hret == DI_OK; + } + + return true; +} + +static void checkKeyboard() +{ + // mham fix. Patch #1378104 + UCHAR keystate[256]; + HRESULT hret = pDevices[0].device->Acquire(); + + if (pDevices[0].first) { + pDevices[0].device->GetDeviceState(256, (LPVOID)pDevices[0].data); + pDevices[0].first = FALSE; + return; + } + + hret = pDevices[0].device-> + GetDeviceState(256, (LPVOID)keystate); + + if (hret == DIERR_INPUTLOST || hret == DIERR_NOTACQUIRED) { + return; + } + + if (hret == DI_OK) { + for (int i = 0; i < 256; i++) { + if (keystate[i] == pDevices[0].data[i]) continue; + if (KEYDOWN(keystate, i)) { + SendMessage(GetFocus(), JOYCONFIG_MESSAGE,0,i); + break; + } + } + } + memcpy(pDevices[0].data, keystate, sizeof(UCHAR) * 256); +} + +static void checkJoypads() +{ + DIDEVICEOBJECTINSTANCE di; + + ZeroMemory(&di,sizeof(DIDEVICEOBJECTINSTANCE)); + + di.dwSize = sizeof(DIDEVICEOBJECTINSTANCE); + + int i =0; + + DIJOYSTATE joystick; + + for (i = 1; i < numDevices; i++) { + HRESULT hret = pDevices[i].device->Acquire(); + + + if (pDevices[i].isPolled) + ((LPDIRECTINPUTDEVICE2)pDevices[i].device)->Poll(); + + hret = pDevices[i].device->GetDeviceState(sizeof(joystick), &joystick); + + int j; + + if (pDevices[i].first) { + memcpy(&pDevices[i].state, &joystick, sizeof(joystick)); + pDevices[i].first = FALSE; + continue; + } + + for (j = 0; j < pDevices[i].nButtons; j++) { + if (((pDevices[i].state.rgbButtons[j] ^ joystick.rgbButtons[j]) + & joystick.rgbButtons[j]) & 0x80) { + HWND focus = GetFocus(); + + SendMessage(focus, JOYCONFIG_MESSAGE, i,j+128); + } + } + + for (j = 0; j < pDevices[i].nAxes && j < 8; j++) { + LONG value = pDevices[i].axis[j].center; + LONG old = 0; + + const DWORD offset = pDevices[i].axis[j].offset; + value = *(LONG*)(((char*)&joystick.lX) + offset); + old = *(LONG*)(((char*)&pDevices[i].state.lX) + offset); + + if (value != old) { + if (value < pDevices[i].axis[j].negative) + SendMessage(GetFocus(), JOYCONFIG_MESSAGE, i, (j<<1)); + else if (value > pDevices[i].axis[j].positive) + SendMessage(GetFocus(), JOYCONFIG_MESSAGE, i, (j<<1)+1); + } + } + + for (j = 0;j < 4 && j < pDevices[i].nPovs; j++) { + if (LOWORD(pDevices[i].state.rgdwPOV[j]) != LOWORD(joystick.rgdwPOV[j])) { + int state = getPovState(joystick.rgdwPOV[j]); + + if (state & POV_UP) + SendMessage(GetFocus(), JOYCONFIG_MESSAGE, i, (j<<2)+0x20); + else if (state & POV_DOWN) + SendMessage(GetFocus(), JOYCONFIG_MESSAGE, i, (j<<2)+0x21); + else if (state & POV_RIGHT) + SendMessage(GetFocus(), JOYCONFIG_MESSAGE, i, (j<<2)+0x22); + else if (state & POV_LEFT) + SendMessage(GetFocus(), JOYCONFIG_MESSAGE, i, (j<<2)+0x23); + } + } + + memcpy(&pDevices[i].state, &joystick, sizeof(joystick)); + } +} + +BOOL checkKey(LONG_PTR key) +{ + LONG_PTR dev = (key >> 8); + + LONG_PTR k = (key & 255); + + if (dev == 0) { + return KEYDOWN(pDevices[0].data,k); + } else if (dev >= numDevices) { + return FALSE; + } else { + if (k < 16) { + LONG_PTR axis = k >> 1; + LONG value = pDevices[dev].axis[axis].center; + + value = *(LONG*)(((char*)&pDevices[dev].state.lX) + pDevices[dev].axis[axis].offset); + + if (k & 1) + return value > pDevices[dev].axis[axis].positive; + return value < pDevices[dev].axis[axis].negative; + } else if (k < 48) { + LONG_PTR hat = (k >> 2) & 3; + int state = getPovState(pDevices[dev].state.rgdwPOV[hat]); + BOOL res = FALSE; + + res = state & (1 << (k & 3)); + + return res; + } else if (k >= 128) { + return pDevices[dev].state.rgbButtons[k-128] & 0x80; + } + } + + return FALSE; +} + +BOOL checkKey(KeyList &k) +{ + POSITION p = k.GetHeadPosition(); + while(p!=NULL) + { + if (checkKey(k.GetNext(p))) + return TRUE; + } + return FALSE; +} + +DirectInput::DirectInput() +{ +} + +DirectInput::~DirectInput() +{ + saveSettings(); + if (pDirectInput != NULL) { + if (pDevices) { + for (int i = 0; i < numDevices ; i++) { + if (pDevices[i].device) { + pDevices[i].device->Unacquire(); + pDevices[i].device->Release(); + pDevices[i].device = NULL; + } + } + free(pDevices); + pDevices = NULL; + } + + pDirectInput->Release(); + pDirectInput = NULL; + } +} + +bool DirectInput::initialize() +{ + HRESULT hr; + + hr = DirectInput8Create( + GetModuleHandle( NULL ), + DIRECTINPUT_VERSION, + IID_IDirectInput8, + (LPVOID *)&pDirectInput, + NULL ); + ASSERT( hr == DI_OK ); + if( hr != DI_OK ) return false; + + + hr = pDirectInput->EnumDevices(DI8DEVCLASS_GAMECTRL, + DIEnumDevicesCallback2, + NULL, + DIEDFL_ATTACHEDONLY); + + + + pDevices = (deviceInfo *)calloc(numDevices, sizeof(deviceInfo)); + + hr = pDirectInput->CreateDevice(GUID_SysKeyboard,&pDevices[0].device,NULL); + pDevices[0].isPolled = false; + pDevices[0].needed = true; + pDevices[0].first = true; + + if (hr != DI_OK) { + return false; + } + + + numDevices = 1; + + hr = pDirectInput->EnumDevices(DI8DEVCLASS_GAMECTRL, + DIEnumDevicesCallback, + NULL, + DIEDFL_ATTACHEDONLY); + + + if (hr != DI_OK) { + return false; + } + + hr = pDevices[0].device->SetDataFormat(&c_dfDIKeyboard); + + if (hr != DI_OK) { + return false; + } + + int i; + for (i = 1; i < numDevices; i++) { + pDevices[i].device->SetDataFormat(&c_dfDIJoystick); + pDevices[i].needed = false; + pDevices[i].first = true; + currentDevice = &pDevices[i]; + axisNumber = 0; + + // get up to 6 axes and 2 sliders + DIPROPRANGE range; + range.diph.dwSize = sizeof(range); + range.diph.dwHeaderSize = sizeof(range.diph); + range.diph.dwHow = DIPH_BYOFFSET; + // screw EnumObjects, just go through all the axis offsets and try to GetProperty + // this should be more foolproof, less code, and probably faster + for (unsigned int offset = 0; offset < DIJOFS_BUTTON(0); offset += sizeof(LONG)) + { + range.diph.dwObj = offset; + // try to set some nice power of 2 values (8192) + range.lMin = -(1 << 13); + range.lMax = (1 << 13); + pDevices[i].device->SetProperty(DIPROP_RANGE, &range.diph); + // but i guess not all devices support setting range + // so i getproperty right afterward incase it didn't set :P + // this also checks that the axis is present + if (SUCCEEDED(pDevices[i].device->GetProperty(DIPROP_RANGE, &range.diph))) + { + const LONG center = (range.lMin + range.lMax)/2; + const LONG threshold = (range.lMax - center)/2; + + currentDevice->axis[axisNumber].center = center; + currentDevice->axis[axisNumber].negative = center - threshold; + currentDevice->axis[axisNumber].positive = center + threshold; + currentDevice->axis[axisNumber].offset = offset; + + ++axisNumber; + } + + } + + currentDevice->device->EnumObjects(EnumPovsCallback, NULL, DIDFT_POV); + + + currentDevice = NULL; + } + + for (i = 0; i < numDevices; i++) + pDevices[i].device->Acquire(); + + return true; +} + +bool DirectInput::readDevices() +{ + bool ok = true; + for (int i = 0; i < numDevices; i++) { + if (pDevices[i].needed) { + if (i) { + ok = readJoystick(i); + } else + ok = readKeyboard(); + } + } + return ok; +} + +u32 DirectInput::readDevice(int which) +{ + u32 res = 0; + int i = theApp.joypadDefault; + if(which >= 0 && which <= 3) + i = which; + + if(checkKey(theApp.input->joypaddata[JOYPAD(i,KEY_BUTTON_A)])) + res |= 1; + if(checkKey(theApp.input->joypaddata[JOYPAD(i,KEY_BUTTON_B)])) + res |= 2; + if(checkKey(theApp.input->joypaddata[JOYPAD(i,KEY_BUTTON_SELECT)])) + res |= 4; + if(checkKey(theApp.input->joypaddata[JOYPAD(i,KEY_BUTTON_START)])) + res |= 8; + if(checkKey(theApp.input->joypaddata[JOYPAD(i,KEY_RIGHT)])) + res |= 16; + if(checkKey(theApp.input->joypaddata[JOYPAD(i,KEY_LEFT)])) + res |= 32; + if(checkKey(theApp.input->joypaddata[JOYPAD(i,KEY_UP)])) + res |= 64; + if(checkKey(theApp.input->joypaddata[JOYPAD(i,KEY_DOWN)])) + res |= 128; + if(checkKey(theApp.input->joypaddata[JOYPAD(i,KEY_BUTTON_R)])) + res |= 256; + if(checkKey(theApp.input->joypaddata[JOYPAD(i,KEY_BUTTON_L)])) + res |= 512; + + if(checkKey(theApp.input->joypaddata[JOYPAD(i,KEY_BUTTON_GS)])) + res |= 4096; + + if(theApp.autoFire) { + res &= (~theApp.autoFire); + if(theApp.autoFireToggle) + res |= theApp.autoFire; + theApp.autoFireToggle = !theApp.autoFireToggle; + } + + // disallow L+R or U+D of being pressed at the same time + if((res & 48) == 48) + res &= ~16; + if((res & 192) == 192) + res &= ~128; + + if(theApp.movieRecording) { + if(i == theApp.joypadDefault) { + if(res != theApp.movieLastJoypad) { + fwrite(&theApp.movieFrame, 1, sizeof(theApp.movieFrame), theApp.movieFile); + fwrite(&res, 1, sizeof(res), theApp.movieFile); + theApp.movieLastJoypad = res; + } + } + } + if(theApp.moviePlaying) { + if(theApp.movieFrame == theApp.moviePlayFrame) { + theApp.movieLastJoypad = theApp.movieNextJoypad; + theApp.movieReadNext(); + } + res = theApp.movieLastJoypad; + } + // we don't record speed up or screen capture buttons + if(checkKey(theApp.input->joypaddata[JOYPAD(i,KEY_BUTTON_SPEED)]) || theApp.speedupToggle) + res |= 1024; + if(checkKey(theApp.input->joypaddata[JOYPAD(i,KEY_BUTTON_CAPTURE)])) + res |= 2048; + + return res; +} + +CString DirectInput::getKeyName(LONG_PTR key) +{ + LONG_PTR d = (key >> 8); + LONG_PTR k = key & 255; + + DIDEVICEOBJECTINSTANCE di; + + ZeroMemory(&di,sizeof(DIDEVICEOBJECTINSTANCE)); + + di.dwSize = sizeof(DIDEVICEOBJECTINSTANCE); + + CString winBuffer = winResLoadString(IDS_ERROR); + + if (d == 0) { + pDevices[0].device->GetObjectInfo( &di, (DWORD)key, DIPH_BYOFFSET ); + winBuffer = di.tszName; + } else if (d < numDevices) { + if (k < 16) + { + pDevices[d].device->GetObjectInfo(&di, + pDevices[d].axis[k>>1].offset, + DIPH_BYOFFSET); + if (k & 1) + winBuffer.Format("Joy %d %s +", d, di.tszName); + else + winBuffer.Format("Joy %d %s -", d, di.tszName); + } else if (k < 48) { + LONG_PTR hat = (k >> 2) & 3; + pDevices[d].device->GetObjectInfo(&di, + (DWORD)DIJOFS_POV(hat), + DIPH_BYOFFSET); + char *dir = "up"; + LONG_PTR dd = k & 3; + if (dd == 1) + dir = "down"; + else if (dd == 2) + dir = "right"; + else if (dd == 3) + dir = "left"; + winBuffer.Format("Joy %d %s %s", d, di.tszName, dir); + } else { + pDevices[d].device->GetObjectInfo(&di, + (DWORD)DIJOFS_BUTTON(k-128), + DIPH_BYOFFSET); + winBuffer.Format(winResLoadString(IDS_JOY_BUTTON),d,di.tszName); + } + } + else + { + // Joystick isn't plugged in. We can't decipher k, so just show its value. + winBuffer.Format("Joy %d (%d)", d, k); + } + + return winBuffer; +} + +void DirectInput::checkKeys() +{ + ::checkKeys(); +} + +void DirectInput::checkMotionKeys() +{ + if(checkKey(theApp.input->joypaddata[MOTION(KEY_LEFT)])) { + theApp.sensorX += 3; + if(theApp.sensorX > 2197) + theApp.sensorX = 2197; + if(theApp.sensorX < 2047) + theApp.sensorX = 2057; + } else if(checkKey(theApp.input->joypaddata[MOTION(KEY_RIGHT)])) { + theApp.sensorX -= 3; + if(theApp.sensorX < 1897) + theApp.sensorX = 1897; + if(theApp.sensorX > 2047) + theApp.sensorX = 2037; + } else if(theApp.sensorX > 2047) { + theApp.sensorX -= 2; + if(theApp.sensorX < 2047) + theApp.sensorX = 2047; + } else { + theApp.sensorX += 2; + if(theApp.sensorX > 2047) + theApp.sensorX = 2047; + } + + if(checkKey(theApp.input->joypaddata[MOTION(KEY_UP)])) { + theApp.sensorY += 3; + if(theApp.sensorY > 2197) + theApp.sensorY = 2197; + if(theApp.sensorY < 2047) + theApp.sensorY = 2057; + } else if(checkKey(theApp.input->joypaddata[MOTION(KEY_DOWN)])) { + theApp.sensorY -= 3; + if(theApp.sensorY < 1897) + theApp.sensorY = 1897; + if(theApp.sensorY > 2047) + theApp.sensorY = 2037; + } else if(theApp.sensorY > 2047) { + theApp.sensorY -= 2; + if(theApp.sensorY < 2047) + theApp.sensorY = 2047; + } else { + theApp.sensorY += 2; + if(theApp.sensorY > 2047) + theApp.sensorY = 2047; + } +} + +Input *newDirectInput() +{ + return new DirectInput; +} + + +void DirectInput::checkDevices() +{ + checkJoypads(); + checkKeyboard(); +} + +void DirectInput::activate() +{ + for (int i = 0; i < numDevices; i++) { + if (pDevices != NULL && pDevices[i].device != NULL) + pDevices[i].device->Acquire(); + } +} + +void DirectInput::loadSettings() +{ + winReadKeys(); +} + +void DirectInput::saveSettings() +{ + winSaveKeys(); +} diff --git a/src/win32/DirectSound.cpp b/src/win32/DirectSound.cpp new file mode 100644 index 0000000..a263f90 --- /dev/null +++ b/src/win32/DirectSound.cpp @@ -0,0 +1,316 @@ +// MFC +#include "stdafx.h" + +// Application +#include "VBA.h" + +// Tools +#include "AVIWrite.h" +#include "WavWriter.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 + +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 ) { + systemMessage( IDS_CANNOT_CREATE_DIRECTSOUND, NULL, hr ); + return false; + } + + pDirectSound->Initialize( &DSDEVID_DefaultPlayback ); + if( hr != DS_OK ) { + systemMessage( IDS_CANNOT_CREATE_DIRECTSOUND, NULL, hr ); + return false; + } + + if( FAILED( hr = pDirectSound->SetCooperativeLevel( theApp.m_pMainWnd->GetSafeHwnd(), DSSCL_EXCLUSIVE ) ) ) { + systemMessage( IDS_CANNOT_SETCOOPERATIVELEVEL, _T("Cannot SetCooperativeLevel %08x"), hr ); + return false; + } + + + // Create primary sound buffer + ZeroMemory( &dsbdesc, sizeof(DSBUFFERDESC) ); + dsbdesc.dwSize = sizeof(DSBUFFERDESC); + dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER; + if( theApp.dsoundDisableHardwareAcceleration ) { + dsbdesc.dwFlags |= DSBCAPS_LOCSOFTWARE; + } + + if( FAILED( hr = pDirectSound->CreateSoundBuffer( &dsbdesc, &dsbPrimary, NULL ) ) ) { + systemMessage(IDS_CANNOT_CREATESOUNDBUFFER, _T("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 ) ) ) { + systemMessage( IDS_CANNOT_SETFORMAT_PRIMARY, _T("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( theApp.dsoundDisableHardwareAcceleration ) { + dsbdesc.dwFlags |= DSBCAPS_LOCSOFTWARE; + } + dsbdesc.dwBufferBytes = soundBufferTotalLen; + dsbdesc.lpwfxFormat = &wfx; + + if( FAILED( hr = pDirectSound->CreateSoundBuffer( &dsbdesc, &dsbSecondary, NULL ) ) ) { + systemMessage( IDS_CANNOT_CREATESOUNDBUFFER, _T("CreateSoundBuffer(secondary) failed %08x"), hr ); + return false; + } + + if( FAILED( hr = dsbSecondary->SetCurrentPosition( 0 ) ) ) { + systemMessage( 0, _T("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 ) ) ) { + systemMessage( IDS_CANNOT_PLAY_PRIMARY, _T("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 && !theApp.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 { + systemMessage( 0, _T("dsbSecondary->Lock() failed: %08x"), hr ); + return; + } +} + +SoundDriver *newDirectSound() +{ + return new DirectSound(); +} diff --git a/src/win32/Directories.cpp b/src/win32/Directories.cpp new file mode 100644 index 0000000..edf0760 --- /dev/null +++ b/src/win32/Directories.cpp @@ -0,0 +1,284 @@ +#include "stdafx.h" +#include "vba.h" +#include "Directories.h" +#include "Reg.h" +#include "WinResUtil.h" + +#include +#include +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// Directories dialog + +static int CALLBACK browseCallbackProc(HWND hWnd, UINT msg, + LPARAM l, LPARAM data) +{ + char *buffer = (char *)data; + switch(msg) { + case BFFM_INITIALIZED: + if(buffer[0]) + SendMessage(hWnd, BFFM_SETSELECTION, TRUE, (LPARAM)buffer); + break; + default: + break; + } + return 0; +} + +Directories::Directories(CWnd* pParent /*=NULL*/) + : CDialog(Directories::IDD, pParent) +{ +} + + +void Directories::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + DDX_Control(pDX, IDC_SAVE_PATH, m_savePath); + DDX_Control(pDX, IDC_ROM_PATH, m_romPath); + DDX_Control(pDX, IDC_GBCROM_PATH, m_gbcromPath); + DDX_Control(pDX, IDC_GBROM_PATH, m_gbromPath); + DDX_Control(pDX, IDC_CAPTURE_PATH, m_capturePath); + DDX_Control(pDX, IDC_BATTERY_PATH, m_batteryPath); +} + + +BEGIN_MESSAGE_MAP(Directories, CDialog) + ON_BN_CLICKED(IDC_BATTERY_DIR, OnBatteryDir) + ON_BN_CLICKED(IDC_CAPTURE_DIR, OnCaptureDir) + ON_BN_CLICKED(IDC_GBROM_DIR, OnGbromDir) + ON_BN_CLICKED(IDC_ROM_DIR, OnRomDir) + ON_BN_CLICKED(IDC_SAVE_DIR, OnSaveDir) + ON_BN_CLICKED(IDC_GBCROM_DIR, OnGbcromDir) +END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// Directories message handlers + +BOOL Directories::OnInitDialog() +{ + CDialog::OnInitDialog(); + CString p; + + p = regQueryStringValue("romdir", NULL); + if(!p.IsEmpty()) + GetDlgItem(IDC_ROM_PATH)->SetWindowText(p); + + p = regQueryStringValue("gbcromdir", NULL); + if(!p.IsEmpty()) + GetDlgItem(IDC_GBCROM_PATH)->SetWindowText(p); + + p = regQueryStringValue("gbromdir", NULL); + if(!p.IsEmpty()) + GetDlgItem(IDC_GBROM_PATH)->SetWindowText(p); + + p = regQueryStringValue("batteryDir", NULL); + if(!p.IsEmpty()) + GetDlgItem(IDC_BATTERY_PATH)->SetWindowText( p); + + p = regQueryStringValue("saveDir", NULL); + if(!p.IsEmpty()) + GetDlgItem(IDC_SAVE_PATH)->SetWindowText(p); + + p = regQueryStringValue("captureDir", NULL); + if(!p.IsEmpty()) + GetDlgItem(IDC_CAPTURE_PATH)->SetWindowText(p); + + return TRUE; + // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void Directories::OnBatteryDir() +{ + m_batteryPath.GetWindowText(initialFolderDir); + CString p = browseForDir(winResLoadString(IDS_SELECT_BATTERY_DIR)); + if(!p.IsEmpty()) + m_batteryPath.SetWindowText(p); +} + +void Directories::OnCaptureDir() +{ + m_capturePath.GetWindowText(initialFolderDir); + CString p = browseForDir(winResLoadString(IDS_SELECT_CAPTURE_DIR)); + if(!p.IsEmpty()) + m_capturePath.SetWindowText(p); +} + +void Directories::OnGbromDir() +{ + m_gbromPath.GetWindowText(initialFolderDir); + CString p = browseForDir(winResLoadString(IDS_SELECT_ROM_DIR)); + if(!p.IsEmpty()) + m_gbromPath.SetWindowText(p); +} + +void Directories::OnGbcromDir() +{ + m_gbcromPath.GetWindowText(initialFolderDir); + CString p = browseForDir(winResLoadString(IDS_SELECT_ROM_DIR)); + if(!p.IsEmpty()) + m_gbcromPath.SetWindowText(p); +} + +void Directories::OnRomDir() +{ + m_romPath.GetWindowText(initialFolderDir); + CString p = browseForDir(winResLoadString(IDS_SELECT_ROM_DIR)); + if(!p.IsEmpty()) + m_romPath.SetWindowText(p); +} + +void Directories::OnSaveDir() +{ + m_savePath.GetWindowText(initialFolderDir); + CString p = browseForDir(winResLoadString(IDS_SELECT_SAVE_DIR)); + if(!p.IsEmpty()) + m_savePath.SetWindowText(p); +} + +void Directories::OnCancel() +{ + EndDialog(FALSE); +} + +void Directories::OnOK() +{ + CDialog::OnOK(); + + char baseDir[MAX_PATH+1]; + char temp[MAX_PATH+1]; + GetModuleFileName( NULL, baseDir, MAX_PATH ); + baseDir[MAX_PATH] = '\0'; // for security reasons + PathRemoveFileSpec( baseDir ); // removes the trailing file name and backslash + + + CString buffer; + + m_romPath.GetWindowText(buffer); + if( !buffer.IsEmpty() ) + regSetStringValue( "romdir", buffer ); + if( buffer[0] == '.' ) { + strcpy( temp, baseDir ); + strcat( temp, "\\" ); + strcat( temp, buffer ); + buffer = temp; + } + if( !directoryDoesExist( buffer ) ) + SHCreateDirectoryEx( NULL, buffer, NULL ); + + m_gbcromPath.GetWindowText(buffer); + if( !buffer.IsEmpty() ) + regSetStringValue( "gbcromdir", buffer ); + if( buffer[0] == '.' ) { + strcpy( temp, baseDir ); + strcat( temp, "\\" ); + strcat( temp, buffer ); + buffer = temp; + } + if( !directoryDoesExist( buffer ) ) + SHCreateDirectoryEx( NULL, buffer, NULL ); + + m_gbromPath.GetWindowText(buffer); + if( !buffer.IsEmpty() ) + regSetStringValue( "gbromdir", buffer ); + if( buffer[0] == '.' ) { + strcpy( temp, baseDir ); + strcat( temp, "\\" ); + strcat( temp, buffer ); + buffer = temp; + } + if( !directoryDoesExist( buffer ) ) + SHCreateDirectoryEx( NULL, buffer, NULL ); + + m_batteryPath.GetWindowText(buffer); + if( !buffer.IsEmpty() ) + regSetStringValue( "batteryDir", buffer ); + if( buffer[0] == '.' ) { + strcpy( temp, baseDir ); + strcat( temp, "\\" ); + strcat( temp, buffer ); + buffer = temp; + } + if( !directoryDoesExist( buffer ) ) + SHCreateDirectoryEx( NULL, buffer, NULL ); + + m_savePath.GetWindowText(buffer); + if( !buffer.IsEmpty() ) + regSetStringValue( "saveDir", buffer ); + if( buffer[0] == '.' ) { + strcpy( temp, baseDir ); + strcat( temp, "\\" ); + strcat( temp, buffer ); + buffer = temp; + } + if( !directoryDoesExist( buffer ) ) + SHCreateDirectoryEx( NULL, buffer, NULL ); + + m_capturePath.GetWindowText(buffer); + if( !buffer.IsEmpty() ) + regSetStringValue( "captureDir", buffer ); + if( buffer[0] == '.' ) { + strcpy( temp, baseDir ); + strcat( temp, "\\" ); + strcat( temp, buffer ); + buffer = temp; + } + if( !directoryDoesExist( buffer ) ) + SHCreateDirectoryEx( NULL, buffer, NULL ); + + EndDialog(TRUE); +} + +CString Directories::browseForDir(CString title) +{ + static char buffer[1024]; + LPMALLOC pMalloc; + LPITEMIDLIST pidl; + + CString res; + + if(SUCCEEDED(SHGetMalloc(&pMalloc))) { + BROWSEINFO bi; + ZeroMemory(&bi, sizeof(bi)); + bi.hwndOwner = m_hWnd; + bi.lpszTitle = title; + bi.pidlRoot = 0; + bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI; + bi.lpfn = browseCallbackProc; + bi.lParam = (LPARAM)(LPCTSTR)initialFolderDir; + + pidl = SHBrowseForFolder(&bi); + + if(pidl) { + if(SHGetPathFromIDList(pidl, buffer)) { + res = buffer; + } + pMalloc->Free(pidl); + pMalloc->Release(); + } + } + return res; +} + +// returns true if the directory does exist +bool Directories::directoryDoesExist(const char *directory) +{ + HANDLE hDir; + hDir = CreateFile( + directory, + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + NULL ); + bool retval = (hDir == INVALID_HANDLE_VALUE) ? false : true; + CloseHandle( hDir ); + return retval; +} diff --git a/src/win32/Directories.h b/src/win32/Directories.h new file mode 100644 index 0000000..5cb8bc8 --- /dev/null +++ b/src/win32/Directories.h @@ -0,0 +1,34 @@ +#pragma once + + +class Directories : public CDialog +{ +public: + Directories(CWnd* pParent = NULL); + enum { IDD = IDD_DIRECTORIES }; + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual BOOL OnInitDialog(); + afx_msg void OnBatteryDir(); + afx_msg void OnCaptureDir(); + afx_msg void OnGbromDir(); + afx_msg void OnGbcromDir(); + afx_msg void OnRomDir(); + afx_msg void OnSaveDir(); + virtual void OnCancel(); + virtual void OnOK(); + DECLARE_MESSAGE_MAP() + +private: + CEdit m_savePath; + CEdit m_romPath; + CEdit m_gbcromPath; + CEdit m_gbromPath; + CEdit m_capturePath; + CEdit m_batteryPath; + CString initialFolderDir; + + CString browseForDir(CString title); + bool directoryDoesExist(const char *directory); +}; diff --git a/src/win32/Disassemble.cpp b/src/win32/Disassemble.cpp new file mode 100644 index 0000000..6204218 --- /dev/null +++ b/src/win32/Disassemble.cpp @@ -0,0 +1,340 @@ +#include "stdafx.h" +#include "vba.h" +#include "Disassemble.h" + +#include "../System.h" +#include "../gba/armdis.h" +#include "../gba/GBA.h" +#include "../gba/Globals.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +extern int emulating; + +extern void CPUUpdateCPSR(); + + +///////////////////////////////////////////////////////////////////////////// +// Disassemble dialog + + +Disassemble::Disassemble(CWnd* pParent /*=NULL*/) + : ResizeDlg(Disassemble::IDD, pParent) +{ + //{{AFX_DATA_INIT(Disassemble) + m_c = FALSE; + m_f = FALSE; + m_i = FALSE; + m_n = FALSE; + m_t = FALSE; + m_v = FALSE; + m_z = FALSE; + mode = -1; + //}}AFX_DATA_INIT + mode = 0; + address = 0; + autoUpdate = false; + count = 1; +} + + +void Disassemble::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(Disassemble) + DDX_Control(pDX, IDC_ADDRESS, m_address); + DDX_Control(pDX, IDC_DISASSEMBLE, m_list); + DDX_Check(pDX, IDC_C, m_c); + DDX_Check(pDX, IDC_F, m_f); + DDX_Check(pDX, IDC_I, m_i); + DDX_Check(pDX, IDC_N, m_n); + DDX_Check(pDX, IDC_T, m_t); + DDX_Check(pDX, IDC_V, m_v); + DDX_Check(pDX, IDC_Z, m_z); + DDX_Radio(pDX, IDC_AUTOMATIC, mode); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(Disassemble, CDialog) + //{{AFX_MSG_MAP(Disassemble) + ON_BN_CLICKED(IDC_AUTO_UPDATE, OnAutoUpdate) + ON_BN_CLICKED(IDC_AUTOMATIC, OnAutomatic) + ON_BN_CLICKED(IDC_ARM, OnArm) + ON_BN_CLICKED(IDC_CLOSE, OnClose) + ON_BN_CLICKED(IDC_GO, OnGo) + ON_BN_CLICKED(IDC_GOPC, OnGopc) + ON_BN_CLICKED(IDC_NEXT, OnNext) + ON_BN_CLICKED(IDC_REFRESH, OnRefresh) + ON_BN_CLICKED(IDC_THUMB, OnThumb) + ON_WM_VSCROLL() + //}}AFX_MSG_MAP + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// Disassemble message handlers + +void Disassemble::OnAutoUpdate() +{ + autoUpdate = !autoUpdate; + if(autoUpdate) { + theApp.winAddUpdateListener(this); + } else { + theApp.winRemoveUpdateListener(this); + } +} + +void Disassemble::OnAutomatic() +{ + mode = 0; + refresh(); +} + +void Disassemble::OnArm() +{ + mode = 1; + address&=0xfffffffC; + refresh(); +} + +void Disassemble::OnClose() +{ + theApp.winRemoveUpdateListener(this); + + DestroyWindow(); +} + +void Disassemble::OnGo() +{ + CString buffer; + m_address.GetWindowText(buffer); + sscanf(buffer, "%x", &address); + if (mode==1) + address&=0xfffffffc; + else if (mode==2) + address&=0xfffffffe; + refresh(); +} + +void Disassemble::OnGopc() +{ + if(rom != NULL) + { + if(armState) + address = armNextPC - 16; + else + address = armNextPC - 8; + + refresh(); + } +} + +void Disassemble::OnNext() +{ + if(rom != NULL) + { + CPULoop(1); + if(armState) { + u32 total = address+count*4; + if(armNextPC >= address && armNextPC < total) { + } else { + OnGopc(); + } + } else { + u32 total = address+count*2; + if(armNextPC >= address && armNextPC < total) { + } else { + OnGopc(); + } + } + refresh(); + } +} + +void Disassemble::OnRefresh() +{ + refresh(); +} + +void Disassemble::OnThumb() +{ + mode = 2; + address&=0xfffffffe; + refresh(); +} + +BOOL Disassemble::OnInitDialog() +{ + CDialog::OnInitDialog(); + + DIALOG_SIZER_START( sz ) + DIALOG_SIZER_ENTRY( IDC_DISASSEMBLE, DS_SizeY) + DIALOG_SIZER_ENTRY( IDC_REFRESH, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_CLOSE, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_NEXT, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_AUTO_UPDATE, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_GOPC, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_VSCROLL, DS_SizeY) + DIALOG_SIZER_END() + SetData(sz, + TRUE, + HKEY_CURRENT_USER, + "Software\\Emulators\\VisualBoyAdvance\\Viewer\\DisassembleView", + NULL); + + SCROLLINFO si; + ZeroMemory(&si, sizeof(si)); + si.cbSize = sizeof(si); + si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS; + si.nMin = 0; + si.nMax = 100; + si.nPos = 50; + si.nPage = 0; + GetDlgItem(IDC_VSCROLL)->SetScrollInfo(SB_CTL, &si, TRUE); + + CFont *font = CFont::FromHandle((HFONT)GetStockObject(SYSTEM_FIXED_FONT)); + + m_list.SetFont(font, FALSE); + for(int i = 0; i < 17; i++) + GetDlgItem(IDC_R0+i)->SetFont(font, FALSE); + + GetDlgItem(IDC_MODE)->SetFont(font, FALSE); + + + m_address.LimitText(8); + refresh(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void Disassemble::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) +{ + switch(nSBCode) { + case SB_LINEDOWN: + if(mode == 0) { + if(armState) + address += 4; + else + address += 2; + } else if(mode == 1) + address += 4; + else + address += 2; + break; + case SB_LINEUP: + if(mode == 0) { + if(armState) + address -= 4; + else + address -= 2; + } else if(mode == 1) + address -= 4; + else + address -= 2; + break; + case SB_PAGEDOWN: + if(mode == 0) { + if(armState) + address += count*4; + else + address += count*2; + } else if(mode == 1) + address += count*4; + else + address += count*2; + break; + case SB_PAGEUP: + if(mode == 0) { + if(armState) + address -= count*4; + else + address -= count*2; + } else if(mode == 1) + address -= count*4; + else + address -= count*2; + break; + } + refresh(); + + CDialog::OnVScroll(nSBCode, nPos, pScrollBar); +} + +void Disassemble::refresh() +{ + if(rom == NULL) + return; + + bool arm = armState; + + if(mode != 0) { + if(mode == 1) + arm = true; + else + arm = false; + } + + int h = m_list.GetItemHeight(0); + RECT r; + m_list.GetClientRect(&r); + count = (r.bottom - r.top+1)/h; + + m_list.ResetContent(); + if(!emulating && theApp.cartridgeType == 0) + return; + + char buffer[80]; + u32 addr = address; + int i; + int sel = -1; + for(i = 0; i < count; i++) { + if(addr == armNextPC) + sel = i; + if(arm) { + addr += disArm(addr, buffer, 3); + } else { + addr += disThumb(addr, buffer, 3); + } + m_list.InsertString(-1, buffer); + } + + if(sel != -1) + m_list.SetCurSel(sel); + + CPUUpdateCPSR(); + + for(i = 0; i < 17; i++) { + sprintf(buffer, "%08x", reg[i].I); + GetDlgItem(IDC_R0+i)->SetWindowText(buffer); + } + + m_n = (reg[16].I & 0x80000000) != 0; + m_z = (reg[16].I & 0x40000000) != 0; + m_c = (reg[16].I & 0x20000000) != 0; + m_v = (reg[16].I & 0x10000000) != 0; + m_i = (reg[16].I & 0x80) != 0; + m_f = (reg[16].I & 0x40) != 0; + m_t = (reg[16].I & 0x20) != 0; + + UpdateData(FALSE); + + int v = reg[16].I & 0x1f; + sprintf(buffer, "%02x", v); + GetDlgItem(IDC_MODE)->SetWindowText(buffer); +} + +void Disassemble::update() +{ + OnGopc(); + refresh(); +} + +void Disassemble::PostNcDestroy() +{ + delete this; +} diff --git a/src/win32/Disassemble.h b/src/win32/Disassemble.h new file mode 100644 index 0000000..6015f77 --- /dev/null +++ b/src/win32/Disassemble.h @@ -0,0 +1,75 @@ +#if !defined(AFX_DISASSEMBLE_H__CA10E857_7D76_4B19_A62B_D0677040FD0F__INCLUDED_) +#define AFX_DISASSEMBLE_H__CA10E857_7D76_4B19_A62B_D0677040FD0F__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// Disassemble.h : header file +// + +#include "IUpdate.h" +#include "ResizeDlg.h" +#include "../System.h" // Added by ClassView + +///////////////////////////////////////////////////////////////////////////// +// Disassemble dialog + +class Disassemble : public ResizeDlg, IUpdateListener +{ + // Construction + public: + virtual void update(); + void refresh(); + int count; + bool autoUpdate; + u32 address; + Disassemble(CWnd* pParent = NULL); // standard constructor + + // Dialog Data + //{{AFX_DATA(Disassemble) + enum { IDD = IDD_DISASSEMBLE }; + CEdit m_address; + CListBox m_list; + BOOL m_c; + BOOL m_f; + BOOL m_i; + BOOL m_n; + BOOL m_t; + BOOL m_v; + BOOL m_z; + int mode; + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(Disassemble) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual void PostNcDestroy(); + //}}AFX_VIRTUAL + + // Implementation + protected: + + // Generated message map functions + //{{AFX_MSG(Disassemble) + afx_msg void OnAutoUpdate(); + afx_msg void OnAutomatic(); + afx_msg void OnArm(); + afx_msg void OnClose(); + afx_msg void OnGo(); + afx_msg void OnGopc(); + afx_msg void OnNext(); + afx_msg void OnRefresh(); + afx_msg void OnThumb(); + virtual BOOL OnInitDialog(); + afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + + //{{AFX_INSERT_LOCATION}} + // Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_DISASSEMBLE_H__CA10E857_7D76_4B19_A62B_D0677040FD0F__INCLUDED_) diff --git a/src/win32/Display.h b/src/win32/Display.h new file mode 100644 index 0000000..9e66ce9 --- /dev/null +++ b/src/win32/Display.h @@ -0,0 +1,99 @@ +#pragma once + +#include + +enum DISPLAY_TYPE { + DIRECT_3D = 0, + OPENGL = 1 +}; + +class IDisplay { + public: + IDisplay() {}; + virtual ~IDisplay() {}; + + struct VIDEO_MODE + { + unsigned int adapter; + unsigned int width; + unsigned int height; + unsigned int bitDepth; + unsigned int frequency; + }; + + virtual bool initialize() = 0; + virtual void cleanup() = 0; + virtual void render() = 0; + virtual void clear() = 0; + virtual bool changeRenderSize(int w, int h) { return true; }; + virtual void resize(int w, int h) {}; + virtual void setOption(const char *option, int value) {}; + virtual DISPLAY_TYPE getType() = 0; + virtual bool selectFullScreenMode( VIDEO_MODE &mode ) = 0; +}; + + +inline void cpyImg32( unsigned char *dst, unsigned int dstPitch, unsigned char *src, unsigned int srcPitch, unsigned short width, unsigned short height ) +{ + // fast, iterative C version + // copies an width*height array of visible pixels from src to dst + // srcPitch and dstPitch are the number of garbage bytes after a scanline + register unsigned short lineSize = width<<2; + + while( height-- ) { + memcpy( dst, src, lineSize ); + src += srcPitch; + dst += dstPitch; + } +} + + +inline void cpyImg32bmp( unsigned char *dst, unsigned char *src, unsigned int srcPitch, unsigned short width, unsigned short height ) +{ + // dst will be an upside down bitmap with 24bit colors + // pix must contain 32bit colors (XRGB) + unsigned short srcLineSize = width<<2; + dst += height * width * 3; // move to the last scanline + register unsigned char r, g, b; + + while( height-- ) { + unsigned short x = width; + src += srcLineSize; + while( x-- ) { + --src; // ignore one of 4 bytes + b = *--src; + g = *--src; + r = *--src; + *--dst = b; + *--dst = g; + *--dst = r; + } + src += srcPitch; + } +} + + +inline void cpyImg16( unsigned char *dst, unsigned int dstPitch, unsigned char *src, unsigned int srcPitch, unsigned short width, unsigned short height ) +{ + register unsigned short lineSize = width<<1; + + while( height-- ) { + memcpy( dst, src, lineSize ); + src += srcPitch; + dst += dstPitch; + } +} + + +inline void cpyImg16bmp( unsigned char *dst, unsigned char *src, unsigned int srcPitch, unsigned short width, unsigned short height ) +{ + // dst will be an upside down bitmap with 16bit colors + register unsigned short lineSize = width<<1; + dst += ( height - 1 ) * lineSize; // move to the last scanline + + while( height-- ) { + memcpy( dst, src, lineSize ); + src += srcPitch; + dst -= lineSize; + } +} diff --git a/src/win32/ExportGSASnapshot.cpp b/src/win32/ExportGSASnapshot.cpp new file mode 100644 index 0000000..a790a0d --- /dev/null +++ b/src/win32/ExportGSASnapshot.cpp @@ -0,0 +1,96 @@ +#include "stdafx.h" +#include "vba.h" +#include "ExportGSASnapshot.h" + +#include "../gba/GBA.h" +#include "../NLS.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// ExportGSASnapshot dialog + + +ExportGSASnapshot::ExportGSASnapshot(CString filename, CString title, CWnd* pParent /*=NULL*/) + : CDialog(ExportGSASnapshot::IDD, pParent) +{ + //{{AFX_DATA_INIT(ExportGSASnapshot) + m_desc = _T(""); + m_notes = _T(""); + m_title = _T(""); + //}}AFX_DATA_INIT + m_title = title; + m_filename = filename; + char date[100]; + char time[100]; + + GetDateFormat(LOCALE_USER_DEFAULT, + DATE_SHORTDATE, + NULL, + NULL, + date, + 100); + GetTimeFormat(LOCALE_USER_DEFAULT, + 0, + NULL, + NULL, + time, + 100); + m_desc.Format("%s %s", date, time); +} + + +void ExportGSASnapshot::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(ExportGSASnapshot) + DDX_Text(pDX, IDC_DESC, m_desc); + DDV_MaxChars(pDX, m_desc, 100); + DDX_Text(pDX, IDC_NOTES, m_notes); + DDV_MaxChars(pDX, m_notes, 512); + DDX_Text(pDX, IDC_TITLE, m_title); + DDV_MaxChars(pDX, m_title, 100); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(ExportGSASnapshot, CDialog) + //{{AFX_MSG_MAP(ExportGSASnapshot) + ON_BN_CLICKED(ID_CANCEL, OnCancel) + ON_BN_CLICKED(ID_OK, OnOk) + //}}AFX_MSG_MAP + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// ExportGSASnapshot message handlers + +BOOL ExportGSASnapshot::OnInitDialog() +{ + CDialog::OnInitDialog(); + CenterWindow(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void ExportGSASnapshot::OnCancel() +{ + EndDialog(FALSE); +} + +void ExportGSASnapshot::OnOk() +{ + UpdateData(TRUE); + + bool result = CPUWriteGSASnapshot(m_filename, m_title, m_desc, m_notes); + + if(!result) + systemMessage(MSG_ERROR_CREATING_FILE, "Error creating file %s", + m_filename); + + EndDialog(TRUE); +} diff --git a/src/win32/ExportGSASnapshot.h b/src/win32/ExportGSASnapshot.h new file mode 100644 index 0000000..d472105 --- /dev/null +++ b/src/win32/ExportGSASnapshot.h @@ -0,0 +1,51 @@ +#if !defined(AFX_EXPORTGSASNAPSHOT_H__ADF8566A_C64D_43CF_9CD2_A290370BA4F1__INCLUDED_) +#define AFX_EXPORTGSASNAPSHOT_H__ADF8566A_C64D_43CF_9CD2_A290370BA4F1__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// ExportGSASnapshot.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// ExportGSASnapshot dialog + +class ExportGSASnapshot : public CDialog +{ + // Construction + public: + ExportGSASnapshot(CString filename, CString title,CWnd* pParent = NULL); // standard constructor + + // Dialog Data + //{{AFX_DATA(ExportGSASnapshot) + enum { IDD = IDD_EXPORT_SPS }; + CString m_desc; + CString m_notes; + CString m_title; + //}}AFX_DATA + CString m_filename; + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(ExportGSASnapshot) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + + // Implementation + protected: + + // Generated message map functions + //{{AFX_MSG(ExportGSASnapshot) + virtual BOOL OnInitDialog(); + afx_msg void OnCancel(); + afx_msg void OnOk(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + + //{{AFX_INSERT_LOCATION}} + // Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_EXPORTGSASNAPSHOT_H__ADF8566A_C64D_43CF_9CD2_A290370BA4F1__INCLUDED_) diff --git a/src/win32/FileDlg.cpp b/src/win32/FileDlg.cpp new file mode 100644 index 0000000..bde3465 --- /dev/null +++ b/src/win32/FileDlg.cpp @@ -0,0 +1,181 @@ +#include "stdafx.h" +#include +#include + +#include "VBA.h" +#include "FileDlg.h" +#include "../System.h" +#include "resource.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +static FileDlg *instance = NULL; + +static UINT_PTR CALLBACK HookFunc(HWND hwnd, + UINT msg, + WPARAM wParam, + LPARAM lParam) +{ + if(instance) { + if(msg == WM_NOTIFY) { + OFNOTIFY *notify = (OFNOTIFY *)lParam; + if(notify) { + if(notify->hdr.code == CDN_TYPECHANGE) { + instance->OnTypeChange(hwnd); + return 1; + } + } + } + } + return 0; +} + +static UINT_PTR CALLBACK HookFuncOldStyle(HWND hwnd, + UINT msg, + WPARAM wParam, + LPARAM lParam) +{ + if(instance) { + if(msg == WM_COMMAND) { + if(HIWORD(wParam) == CBN_SELCHANGE) { + if(LOWORD(wParam) == cmb1) { + // call method with combobox handle to keep + // behaviour there + instance->OnTypeChange((HWND)lParam); + return 1; + } + } + } + } + return 0; +} + +///////////////////////////////////////////////////////////////////////////// +// FileDlg + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +FileDlg::FileDlg(CWnd *parent, LPCTSTR file, LPCTSTR filter, + int filterIndex, LPCTSTR ext, LPCTSTR *exts, LPCTSTR initialDir, + LPCTSTR title, bool save) +{ + OSVERSIONINFO info; + info.dwOSVersionInfoSize = sizeof(info); + GetVersionEx(&info); + m_file = file; + int size = sizeof(OPENFILENAME); + + // avoid problems if OPENFILENAME is already defined with the extended fields + // needed for the enhanced open/save dialog +#if _WIN32_WINNT < 0x0500 + if(info.dwPlatformId == VER_PLATFORM_WIN32_NT) { + if(info.dwMajorVersion >= 5) + size = sizeof(OPENFILENAMEEX); + } +#endif + + ZeroMemory(&m_ofn, sizeof(m_ofn)); + m_ofn.lpstrFile = m_file.GetBuffer(MAX_PATH); + m_ofn.nMaxFile = MAX_PATH; + m_ofn.lStructSize = size; + m_ofn.hwndOwner = parent ? parent->GetSafeHwnd() : NULL; + m_ofn.nFilterIndex = filterIndex; + m_ofn.lpstrInitialDir = initialDir; + m_ofn.lpstrTitle = title; + m_ofn.lpstrDefExt = ext; + m_ofn.lpfnHook = HookFunc; + m_ofn.Flags = OFN_PATHMUSTEXIST | OFN_ENABLESIZING | OFN_ENABLEHOOK; + m_ofn.Flags |= OFN_EXPLORER; + if (!save) + m_ofn.Flags |= OFN_READONLY; + m_filter = filter; + + char *p = m_filter.GetBuffer(0); + + while ((p = strchr(p, '|')) != NULL) + *p++ = 0; + m_ofn.lpstrFilter = m_filter; + + if(theApp.videoOption == VIDEO_320x240) { + m_ofn.lpTemplateName = MAKEINTRESOURCE(IDD_OPENDLG); + m_ofn.lpfnHook = HookFuncOldStyle; + m_ofn.Flags |= OFN_ENABLETEMPLATE; + m_ofn.Flags &= ~OFN_EXPLORER; + } + + isSave = save; + extensions = exts; + + instance = this; +} + +FileDlg::~FileDlg() +{ + instance = NULL; +} + +void FileDlg::OnTypeChange(HWND hwnd) +{ + HWND parent = GetParent(hwnd); + + HWND fileNameControl = ::GetDlgItem(parent, cmb13); + + if(fileNameControl == NULL) + fileNameControl = ::GetDlgItem(parent, edt1); + + if(fileNameControl == NULL) + return; + + CString filename; + GetWindowText(fileNameControl, filename.GetBuffer(MAX_PATH), MAX_PATH); + filename.ReleaseBuffer(); + + HWND typeControl = ::GetDlgItem(parent, cmb1); + + ASSERT(typeControl != NULL); + + LRESULT sel = ::SendMessage(typeControl, CB_GETCURSEL, 0, 0); + + ASSERT(sel != -1); + + LPCTSTR typeName = extensions[sel]; + + if(filename.GetLength() == 0) { + if(strlen(typeName) != 0) + filename.Format("*%s", typeName); + } else { + if(strlen(typeName) != 0) { + int index = filename.Find('.'); + if (index == -1) { + filename = filename + typeName; + } else { + filename = filename.Left(index) + typeName; + } + } + } + SetWindowText(fileNameControl, filename); +} + +int FileDlg::getFilterIndex() +{ + return m_ofn.nFilterIndex; +} + +int FileDlg::DoModal() +{ + BOOL res = isSave ? GetSaveFileName(&m_ofn) : + GetOpenFileName(&m_ofn); + + return res ? IDOK : IDCANCEL; +} + +LPCTSTR FileDlg::GetPathName() +{ + return (LPCTSTR)m_file; +} diff --git a/src/win32/FileDlg.h b/src/win32/FileDlg.h new file mode 100644 index 0000000..d4b3ebd --- /dev/null +++ b/src/win32/FileDlg.h @@ -0,0 +1,44 @@ +#if !defined(AFX_FILEDLG_H__7E4F8B92_1B63_4126_8261_D9334C645940__INCLUDED_) +#define AFX_FILEDLG_H__7E4F8B92_1B63_4126_8261_D9334C645940__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// FileDlg.h : header file +// + +struct OPENFILENAMEEX : public OPENFILENAME { + void * pvReserved; + DWORD dwReserved; + DWORD FlagsEx; +}; + +///////////////////////////////////////////////////////////////////////////// +// FileDlg dialog + +class FileDlg +{ + private: + CString m_file; + CString m_filter; + public: + OPENFILENAMEEX m_ofn; + int DoModal(); + LPCTSTR GetPathName(); + virtual int getFilterIndex(); + virtual void OnTypeChange(HWND hwnd); + FileDlg(CWnd *parent, LPCTSTR file, LPCTSTR filter, + int filterIndex, LPCTSTR ext, LPCTSTR *exts, LPCTSTR initialDir, + LPCTSTR title, bool save); + virtual ~FileDlg(); + + protected: + bool isSave; + LPCTSTR *extensions; + + protected: + //{{AFX_INSERT_LOCATION}} + // Microsoft Visual C++ will insert additional declarations immediately before the previous line. +}; + +#endif // !defined(AFX_FILEDLG_H__7E4F8B92_1B63_4126_8261_D9334C645940__INCLUDED_) diff --git a/src/win32/FullscreenSettings.cpp b/src/win32/FullscreenSettings.cpp new file mode 100644 index 0000000..8f70c09 --- /dev/null +++ b/src/win32/FullscreenSettings.cpp @@ -0,0 +1,531 @@ +#include "stdafx.h" +#include "VBA.h" + +#include "FullscreenSettings.h" + + +// FullscreenSettings dialog + +IMPLEMENT_DYNAMIC(FullscreenSettings, CDialog) + +FullscreenSettings::FullscreenSettings(CWnd* pParent /*=NULL*/) + : CDialog(FullscreenSettings::IDD, pParent) +{ + m_device = 0; + m_colorDepth = 0; + m_width = 0; + m_height = 0; + m_refreshRate = 0; + + failed = false; +#ifndef NO_D3D + pD3D = NULL; +#endif +} + +FullscreenSettings::~FullscreenSettings() +{ +#ifndef NO_D3D + if( pD3D ) { + pD3D->Release(); + pD3D = NULL; + } +#endif +} + +void FullscreenSettings::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + DDX_Control(pDX, IDC_COMBO_DEVICE, combo_device); + DDX_Control(pDX, IDC_COMBO_RESOLUTION, combo_resolution); + DDX_Control(pDX, IDC_COMBO_COLOR_DEPTH, combo_color_depth); + DDX_Control(pDX, IDC_COMBO_REFRESH_RATE, combo_refresh_rate); +} + + +BEGIN_MESSAGE_MAP(FullscreenSettings, CDialog) + ON_CBN_SELCHANGE(IDC_COMBO_DEVICE, &FullscreenSettings::OnCbnSelchangeComboDevice) + ON_CBN_SELCHANGE(IDC_COMBO_COLOR_DEPTH, &FullscreenSettings::OnCbnSelchangeComboColorDepth) + ON_CBN_SELCHANGE(IDC_COMBO_RESOLUTION, &FullscreenSettings::OnCbnSelchangeComboResolution) +END_MESSAGE_MAP() + + +// FullscreenSettings message handlers + +BOOL FullscreenSettings::OnInitDialog() +{ + CDialog::OnInitDialog(); + +#ifndef NO_D3D + if( api == DIRECT_3D ) { + pD3D = Direct3DCreate9( D3D_SDK_VERSION ); + if( pD3D == NULL) { + failed = true; + return TRUE; + } + + // enumerate devices + UINT nAdapters = pD3D->GetAdapterCount(); + if( nAdapters < 1 ) { + failed = true; + pD3D->Release(); + pD3D = NULL; + return TRUE; + } + combo_device.ResetContent(); + for( UINT i = 0 ; i < nAdapters ; i++ ) { + D3DADAPTER_IDENTIFIER9 id; + pD3D->GetAdapterIdentifier( i, 0, &id ); + int index = combo_device.AddString( id.Description ); + combo_device.SetItemData( index, (DWORD_PTR)i ); + } + combo_device.SetCurSel( 0 ); + OnCbnSelchangeComboDevice(); + } +#endif + +#ifndef NO_OGL + if( api == OPENGL ) { + // enumerate devices + DWORD iDevice = 0; + DISPLAY_DEVICE devInfo; + ZeroMemory( &devInfo, sizeof(devInfo) ); + devInfo.cb = sizeof(devInfo); + combo_device.ResetContent(); + while( TRUE == EnumDisplayDevices( NULL, iDevice, &devInfo, 0 ) ) { + int index = combo_device.AddString( devInfo.DeviceString ); + combo_device.SetItemData( index, (DWORD_PTR)iDevice ); + iDevice++; + } + combo_device.SetCurSel( 0 ); + OnCbnSelchangeComboDevice(); + } +#endif + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + + +// Call this function to select the API for the display mode enumeration. +void FullscreenSettings::setAPI( DISPLAY_TYPE type ) +{ + api = type; +} + + +void FullscreenSettings::OnCbnSelchangeComboDevice() +{ + failed = false; + +#ifndef NO_D3D + if( api == DIRECT_3D ) { + int selection; + + selection = combo_device.GetCurSel(); + if( selection == LB_ERR ) return; + UINT adapter = (UINT)combo_device.GetItemData( selection ); + + // enumerate color depths + HRESULT res; + D3DDISPLAYMODE mode; + combo_color_depth.ResetContent(); + + res = pD3D->EnumAdapterModes( adapter, D3DFMT_X8R8G8B8, 0, &mode ); + if( res == D3D_OK ) { + int index = combo_color_depth.AddString( _T("24bit (X8R8G8B8)") ); + combo_color_depth.SetItemData( index, (DWORD_PTR)D3DFMT_X8R8G8B8 ); + } + + res = pD3D->EnumAdapterModes( adapter, D3DFMT_R5G6B5, 0, &mode ); + if( res == D3D_OK ) { + int index = combo_color_depth.AddString( _T("16bit (R5G6B5)") ); + combo_color_depth.SetItemData( index, (DWORD_PTR)D3DFMT_R5G6B5 ); + } + + res = pD3D->EnumAdapterModes( adapter, D3DFMT_X1R5G5B5, 0, &mode ); + if( res == D3D_OK ) { + int index = combo_color_depth.AddString( _T("15bit (X1R5G5B5)") ); + combo_color_depth.SetItemData( index, (DWORD_PTR)D3DFMT_X1R5G5B5 ); + } + + if( combo_color_depth.GetCount() == 0 ) { + failed = true; + combo_resolution.ResetContent(); + combo_refresh_rate.ResetContent(); + return; + } + + combo_color_depth.SetCurSel( combo_color_depth.GetCount() - 1 ); + OnCbnSelchangeComboColorDepth(); + } +#endif + +#ifndef NO_OGL + if( api == OPENGL ) { + int selection; + + // get selected device's name + selection = combo_device.GetCurSel(); + if( selection == LB_ERR ) return; + DWORD iDevice = (UINT)combo_device.GetItemData( selection ); + DISPLAY_DEVICE devInfo; + ZeroMemory( &devInfo, sizeof(devInfo) ); + devInfo.cb = sizeof(devInfo); + EnumDisplayDevices( NULL, iDevice, &devInfo, 0 ); + + // enumerate color depths + DWORD iMode = 0; + DEVMODE mode; + ZeroMemory( &mode, sizeof(mode) ); + mode.dmSize = sizeof(mode); + bool alreadyExists; + combo_color_depth.ResetContent(); + + while( TRUE == EnumDisplaySettings( devInfo.DeviceName, iMode, &mode ) ) { + if( mode.dmDisplayFlags != 0 ) { + iMode++; + continue; // don't list special modes + } + alreadyExists = false; + for( int iItem = 0 ; iItem < combo_color_depth.GetCount() ; iItem++ ) { + if( mode.dmBitsPerPel == (DWORD)combo_color_depth.GetItemData( iItem ) ) { + alreadyExists = true; + break; + } + } + if( !alreadyExists ) { + CString temp; + temp.Format( _T("%2u bits per pixel"), mode.dmBitsPerPel ); + int index = combo_color_depth.AddString( temp ); + combo_color_depth.SetItemData( index, (DWORD_PTR)mode.dmBitsPerPel ); + } + iMode++; + } + + if( combo_color_depth.GetCount() == 0 ) { + failed = true; + combo_resolution.ResetContent(); + combo_refresh_rate.ResetContent(); + return; + } + + combo_color_depth.SetCurSel( combo_color_depth.GetCount() - 1 ); + OnCbnSelchangeComboColorDepth(); + } +#endif +} + + +void FullscreenSettings::OnCbnSelchangeComboColorDepth() +{ + if( failed ) return; + +#ifndef NO_D3D + if( api == DIRECT_3D ) { + int selection; + + selection = combo_device.GetCurSel(); + if( selection == LB_ERR ) return; + UINT adapter = (UINT)combo_device.GetItemData( selection ); + + selection = combo_color_depth.GetCurSel(); + if( selection == LB_ERR ) return; + D3DFORMAT format = (D3DFORMAT)combo_color_depth.GetItemData( selection ); + + // enumerate resolutions + HRESULT res; + D3DDISPLAYMODE mode; + UINT nModes = pD3D->GetAdapterModeCount( adapter, format ); + D3DDISPLAYMODE *allModes = new D3DDISPLAYMODE[nModes]; + ZeroMemory( allModes, sizeof(D3DDISPLAYMODE) * nModes ); + combo_resolution.ResetContent(); + + for( UINT i = 0 ; i < nModes ; i++ ) { + res = pD3D->EnumAdapterModes( adapter, format, i, &mode ); + if( res == D3D_OK ) { + CString temp; + temp.Format( _T("%4u x %4u"), mode.Width, mode.Height ); + bool alreadyPresent = false; + for( UINT surf = 0 ; surf < i ; surf++ ) { + // ignore same resolution with different refresh rate + if( ( allModes[surf].Width == mode.Width ) && + ( allModes[surf].Height == mode.Height ) ) { + alreadyPresent = true; + } + } + + if( !alreadyPresent ) { + int index = combo_resolution.AddString( temp ); + combo_resolution.SetItemData( index, (DWORD_PTR)i ); + } + allModes[i] = mode; + } + } + delete [] allModes; + } +#endif + +#ifndef NO_OGL + if( api == OPENGL ) { + int selection; + + // get selected device's name + selection = combo_device.GetCurSel(); + if( selection == LB_ERR ) return; + DWORD iDevice = (UINT)combo_device.GetItemData( selection ); + DISPLAY_DEVICE devInfo; + ZeroMemory( &devInfo, sizeof(devInfo) ); + devInfo.cb = sizeof(devInfo); + EnumDisplayDevices( NULL, iDevice, &devInfo, 0 ); + + // get selected bit depth value + selection = combo_color_depth.GetCurSel(); + if( selection == LB_ERR ) return; + DWORD selectedBPP = (DWORD)combo_color_depth.GetItemData( selection ); + + + // enumerate resolutions + DWORD iMode = 0; + DEVMODE mode, surfMode; + ZeroMemory( &mode, sizeof(mode) ); + ZeroMemory( &surfMode, sizeof(surfMode) ); + mode.dmSize = sizeof(mode); + surfMode.dmSize = sizeof(surfMode); + bool alreadyExists; + combo_resolution.ResetContent(); + + while( TRUE == EnumDisplaySettings( devInfo.DeviceName, iMode, &mode ) ) { + if( ( mode.dmBitsPerPel != selectedBPP) || + ( mode.dmDisplayFlags != 0 ) ) { + iMode++; + continue; + } + alreadyExists = false; + for( int iItem = 0 ; iItem < combo_resolution.GetCount() ; iItem++ ) { + DWORD iSurfMode = (DWORD)combo_resolution.GetItemData( iItem ); + EnumDisplaySettings( devInfo.DeviceName, iSurfMode, &surfMode ); + if( ( mode.dmPelsWidth == surfMode.dmPelsWidth ) && + ( mode.dmPelsHeight == surfMode.dmPelsHeight ) ) { + alreadyExists = true; + break; + } + } + if( !alreadyExists ) { + CString temp; + temp.Format( _T("%4u x %4u"), mode.dmPelsWidth, mode.dmPelsHeight ); + int index = combo_resolution.AddString( temp ); + combo_resolution.SetItemData( index, (DWORD_PTR)iMode ); + } + iMode++; + } + + if( combo_resolution.GetCount() == 0 ) { + failed = true; + return; + } + } +#endif + + combo_resolution.SetCurSel( combo_resolution.GetCount() - 1 ); // select last item + OnCbnSelchangeComboResolution(); +} + + +void FullscreenSettings::OnCbnSelchangeComboResolution() +{ + if( failed ) return; + +#ifndef NO_D3D + if( api == DIRECT_3D ) { + int selection; + + selection = combo_device.GetCurSel(); + if( selection == LB_ERR ) return; + UINT adapter = (UINT)combo_device.GetItemData( selection ); + + selection = combo_color_depth.GetCurSel(); + if( selection == LB_ERR ) return; + D3DFORMAT format = (D3DFORMAT)combo_color_depth.GetItemData( selection ); + + selection = combo_resolution.GetCurSel(); + if( selection == LB_ERR ) return; + UINT iMode = (UINT)combo_resolution.GetItemData( selection ); + + // enumerate refresh rates + HRESULT res; + D3DDISPLAYMODE mode, mode2; + pD3D->EnumAdapterModes( adapter, format, iMode, &mode ); + UINT nModes = pD3D->GetAdapterModeCount( adapter, format ); + combo_refresh_rate.ResetContent(); + + for( UINT i = 0 ; i < nModes ; i++ ) { + res = pD3D->EnumAdapterModes( adapter, format, i, &mode2 ); + if( ( res == D3D_OK ) && + ( mode2.Width == mode.Width ) && + ( mode2.Height == mode.Height ) ) + { + CString temp; + temp.Format( _T("%3u Hz"), mode2.RefreshRate ); + int index = combo_refresh_rate.AddString( temp ); + combo_refresh_rate.SetItemData( index, (DWORD_PTR)i ); + } + } + } +#endif + +#ifndef NO_OGL + if( api == OPENGL ) { + int selection; + + // get selected device's name + selection = combo_device.GetCurSel(); + if( selection == LB_ERR ) return; + DWORD iDevice = (UINT)combo_device.GetItemData( selection ); + DISPLAY_DEVICE devInfo; + ZeroMemory( &devInfo, sizeof(devInfo) ); + devInfo.cb = sizeof(devInfo); + EnumDisplayDevices( NULL, iDevice, &devInfo, 0 ); + + // get selected resolution / bpp + DEVMODE selectedResolution; + ZeroMemory( &selectedResolution, sizeof(selectedResolution) ); + selectedResolution.dmSize = sizeof(selectedResolution); + selection = combo_resolution.GetCurSel(); + if( selection == LB_ERR ) return; + DWORD dwSelectedRes = (DWORD)combo_resolution.GetItemData( selection ); + EnumDisplaySettings( devInfo.DeviceName, dwSelectedRes, &selectedResolution ); + + + // enumerate refresh rates + DWORD iMode = 0; + DEVMODE mode, surfMode; + ZeroMemory( &mode, sizeof(mode) ); + ZeroMemory( &surfMode, sizeof(surfMode) ); + mode.dmSize = sizeof(mode); + surfMode.dmSize = sizeof(surfMode); + bool alreadyExists; + combo_refresh_rate.ResetContent(); + + while( TRUE == EnumDisplaySettings( devInfo.DeviceName, iMode, &mode ) ) { + if( ( mode.dmBitsPerPel != selectedResolution.dmBitsPerPel ) || + ( mode.dmDisplayFlags != 0 ) || + ( mode.dmPelsWidth != selectedResolution.dmPelsWidth ) || + ( mode.dmPelsHeight != selectedResolution.dmPelsHeight ) ) { + iMode++; + continue; + } + alreadyExists = false; + for( int iItem = 0 ; iItem < combo_refresh_rate.GetCount() ; iItem++ ) { + DWORD iSurfMode = (DWORD)combo_refresh_rate.GetItemData( iItem ); + EnumDisplaySettings( devInfo.DeviceName, iSurfMode, &surfMode ); + if( ( mode.dmDisplayFrequency == surfMode.dmDisplayFrequency ) ) { + alreadyExists = true; + break; + } + } + if( !alreadyExists ) { + CString temp; + temp.Format( _T("%3u Hz"), mode.dmDisplayFrequency ); + int index = combo_refresh_rate.AddString( temp ); + combo_refresh_rate.SetItemData( index, (DWORD_PTR)iMode ); + } + iMode++; + } + + } +#endif + + combo_refresh_rate.SetCurSel( combo_refresh_rate.GetCount() - 1 ); // select last item +} + +void FullscreenSettings::OnOK() +{ + if( failed ) return; + +#ifndef NO_D3D + if( api == DIRECT_3D ) { + int selection; + + selection = combo_device.GetCurSel(); + if( selection == LB_ERR ) return; + UINT adapter = (UINT)combo_device.GetItemData( selection ); + + selection = combo_color_depth.GetCurSel(); + if( selection == LB_ERR ) return; + D3DFORMAT format = (D3DFORMAT)combo_color_depth.GetItemData( selection ); + + selection = combo_refresh_rate.GetCurSel(); + if( selection == LB_ERR ) return; + UINT selectedMode = (UINT)combo_refresh_rate.GetItemData( selection ); + + D3DDISPLAYMODE mode; + pD3D->EnumAdapterModes( adapter, format, selectedMode, &mode ); + + m_device = (unsigned int)adapter; + switch( mode.Format ) + { + case D3DFMT_X8R8G8B8: + m_colorDepth = 24; + break; + case D3DFMT_R5G6B5: + m_colorDepth = 16; + break; + case D3DFMT_X1R5G5B5: + m_colorDepth = 15; + break; + } + m_width = (unsigned int)mode.Width; + m_height = (unsigned int)mode.Height; + m_refreshRate = (unsigned int)mode.RefreshRate; + } +#endif + +#ifndef NO_OGL + if( api == OPENGL ) { + int selection; + + // get selected device's name + selection = combo_device.GetCurSel(); + if( selection == LB_ERR ) return; + DWORD iDevice = (UINT)combo_device.GetItemData( selection ); + DISPLAY_DEVICE devInfo; + ZeroMemory( &devInfo, sizeof(devInfo) ); + devInfo.cb = sizeof(devInfo); + EnumDisplayDevices( NULL, iDevice, &devInfo, 0 ); + + // get selected resolution / bpp + DEVMODE mode; + ZeroMemory( &mode, sizeof(mode) ); + mode.dmSize = sizeof(mode); + selection = combo_refresh_rate.GetCurSel(); + if( selection == LB_ERR ) return; + DWORD selectedMode = (DWORD)combo_refresh_rate.GetItemData( selection ); + EnumDisplaySettings( devInfo.DeviceName, selectedMode, &mode ); + + + m_device = (unsigned int)iDevice; + switch( mode.dmBitsPerPel ) + { // the policy of this dialog is to return the actually usable amount of bits + case 4: + m_colorDepth = 4; + break; + case 8: + m_colorDepth = 8; + break; + case 16: + m_colorDepth = 16; + break; + case 24: + case 32: + m_colorDepth = 24; + break; + } + m_width = (unsigned int)mode.dmPelsWidth; + m_height = (unsigned int)mode.dmPelsHeight; + m_refreshRate = (unsigned int)mode.dmDisplayFrequency; + } +#endif + + CDialog::OnOK(); +} diff --git a/src/win32/FullscreenSettings.h b/src/win32/FullscreenSettings.h new file mode 100644 index 0000000..ef63c1c --- /dev/null +++ b/src/win32/FullscreenSettings.h @@ -0,0 +1,61 @@ +#pragma once + +#include "Display.h" + + +#ifndef NO_D3D +#pragma comment( lib, "d3d9" ) +#ifdef _DEBUG +#define D3D_DEBUG_INFO +#endif +#define DIRECT3D_VERSION 0x0900 +#include +#endif + + +// FullscreenSettings dialog + +class FullscreenSettings : public CDialog +{ + DECLARE_DYNAMIC(FullscreenSettings) + +public: + FullscreenSettings(CWnd* pParent = NULL); // standard constructor + virtual ~FullscreenSettings(); + +// Dialog Data + enum { IDD = IDD_FULLSCREEN }; + + // Call this function to select the API for the display mode enumeration. + void setAPI( DISPLAY_TYPE type ); + afx_msg void OnCbnSelchangeComboDevice(); + afx_msg void OnCbnSelchangeComboColorDepth(); + afx_msg void OnCbnSelchangeComboResolution(); + + unsigned int m_device; + unsigned int m_colorDepth; + unsigned int m_width; + unsigned int m_height; + unsigned int m_refreshRate; + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual void OnOK(); + + DECLARE_MESSAGE_MAP() + +private: + DISPLAY_TYPE api; + bool failed; + +#ifndef NO_D3D + LPDIRECT3D9 pD3D; +#endif + + virtual BOOL OnInitDialog(); + + CComboBox combo_device; + CComboBox combo_resolution; + CComboBox combo_color_depth; + CComboBox combo_refresh_rate; +}; diff --git a/src/win32/GBACheats.cpp b/src/win32/GBACheats.cpp new file mode 100644 index 0000000..4abe440 --- /dev/null +++ b/src/win32/GBACheats.cpp @@ -0,0 +1,1151 @@ +#include "stdafx.h" +#include "vba.h" +#include "GBACheats.h" + +#include "../System.h" +#include "../gba/Cheats.h" +#include "../gba/CheatSearch.h" +#include "../gba/GBA.h" +#include "../gba/Globals.h" + +#include "Reg.h" +#include "StringTokenizer.h" +#include "WinResUtil.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// GBACheatSearch dialog + +GBACheatSearch::GBACheatSearch(CWnd* pParent /*=NULL*/) + : CDialog(GBACheatSearch::IDD, pParent) +{ + //{{AFX_DATA_INIT(GBACheatSearch) + valueType = -1; + sizeType = -1; + searchType = -1; + numberType = -1; + updateValues = FALSE; + //}}AFX_DATA_INIT + data = NULL; +} + +GBACheatSearch::~GBACheatSearch() +{ + if(data) + free(data); +} + +void GBACheatSearch::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(GBACheatSearch) + DDX_Control(pDX, IDC_VALUE, m_value); + DDX_Control(pDX, IDC_CHEAT_LIST, m_list); + DDX_Radio(pDX, IDC_OLD_VALUE, valueType); + DDX_Radio(pDX, IDC_SIZE_8, sizeType); + DDX_Radio(pDX, IDC_EQ, searchType); + DDX_Radio(pDX, IDC_SIGNED, numberType); + DDX_Check(pDX, IDC_UPDATE, updateValues); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(GBACheatSearch, CDialog) + //{{AFX_MSG_MAP(GBACheatSearch) + ON_BN_CLICKED(ID_OK, OnOk) + ON_BN_CLICKED(IDC_START, OnStart) + ON_BN_CLICKED(IDC_SEARCH, OnSearch) + ON_BN_CLICKED(IDC_ADD_CHEAT, OnAddCheat) + ON_BN_CLICKED(IDC_UPDATE, OnUpdate) + ON_NOTIFY(LVN_GETDISPINFO, IDC_CHEAT_LIST, OnGetdispinfoCheatList) + ON_NOTIFY(LVN_ITEMCHANGED, IDC_CHEAT_LIST, OnItemchangedCheatList) + ON_CONTROL_RANGE(BN_CLICKED, IDC_OLD_VALUE, IDC_SPECIFIC_VALUE, OnValueType) + ON_CONTROL_RANGE(BN_CLICKED, IDC_EQ, IDC_GE, OnSearchType) + ON_CONTROL_RANGE(BN_CLICKED, IDC_SIGNED, IDC_HEXADECIMAL, OnNumberType) + ON_CONTROL_RANGE(BN_CLICKED, IDC_SIZE_8, IDC_SIZE_32, OnSizeType) + //}}AFX_MSG_MAP + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// GBACheatSearch message handlers + +void GBACheatSearch::OnOk() +{ + EndDialog(TRUE); +} + +void GBACheatSearch::OnStart() +{ + if(cheatSearchData.count == 0) { + CheatSearchBlock *block = &cheatSearchData.blocks[0]; + block->size = 0x40000; + block->offset = 0x2000000; + block->bits = (u8 *)malloc(0x40000>>3); + block->data = workRAM; + block->saved = (u8 *)malloc(0x40000); + + block = &cheatSearchData.blocks[1]; + block->size = 0x8000; + block->offset = 0x3000000; + block->bits = (u8 *)malloc(0x8000>>3); + block->data = internalRAM; + block->saved = (u8 *)malloc(0x8000); + + cheatSearchData.count = 2; + } + + cheatSearchStart(&cheatSearchData); + GetDlgItem(IDC_SEARCH)->EnableWindow(TRUE); +} + +void GBACheatSearch::OnSearch() +{ + CString buffer; + + if(valueType == 0) + cheatSearch(&cheatSearchData, + searchType, + sizeType, + numberType == 0); + else { + m_value.GetWindowText(buffer); + if(buffer.IsEmpty()) { + systemMessage(IDS_NUMBER_CANNOT_BE_EMPTY, "Number cannot be empty"); + return; + } + int value = 0; + switch(numberType) { + case 0: + sscanf(buffer, "%d", &value); + break; + case 1: + sscanf(buffer, "%u", &value); + break; + default: + sscanf(buffer, "%x", &value); + } + cheatSearchValue(&cheatSearchData, + searchType, + sizeType, + numberType == 0, + value); + } + + addChanges(true); + + if(updateValues) + cheatSearchUpdateValues(&cheatSearchData); +} + +void GBACheatSearch::OnAddCheat() +{ + int mark = m_list.GetSelectionMark(); + + if(mark != -1) { + LVITEM item; + memset(&item,0, sizeof(item)); + item.mask = LVIF_PARAM; + item.iItem = mark; + if(m_list.GetItem(&item)) { + AddCheat dlg((u32)item.lParam); + dlg.DoModal(); + } + } +} + +void GBACheatSearch::OnUpdate() +{ + if(GetDlgItem(IDC_UPDATE)->SendMessage(BM_GETCHECK, + 0, + 0) & BST_CHECKED) + updateValues = true; + else + updateValues = false; + regSetDwordValue("cheatsUpdate", updateValues); +} + +void GBACheatSearch::OnGetdispinfoCheatList(NMHDR* pNMHDR, LRESULT* pResult) +{ + LV_DISPINFO* info = (LV_DISPINFO*)pNMHDR; + if(info->item.mask & LVIF_TEXT) { + int index = info->item.iItem; + int col = info->item.iSubItem; + + switch(col) { + case 0: + strcpy(info->item.pszText, data[index].address); + break; + case 1: + strcpy(info->item.pszText, data[index].oldValue); + break; + case 2: + strcpy(info->item.pszText, data[index].newValue); + break; + } + } + *pResult = TRUE; + +} + +void GBACheatSearch::OnItemchangedCheatList(NMHDR* pNMHDR, LRESULT* pResult) +{ + GetDlgItem(IDC_ADD_CHEAT)->EnableWindow(m_list.GetSelectionMark() != -1); + *pResult = TRUE; +} + +BOOL GBACheatSearch::OnInitDialog() +{ + CDialog::OnInitDialog(); + + CString temp = winResLoadString(IDS_ADDRESS); + + m_list.InsertColumn(0, temp, LVCFMT_CENTER, 125, 0); + + temp = winResLoadString(IDS_OLD_VALUE); + m_list.InsertColumn(1, temp, LVCFMT_CENTER, 125, 1); + + temp = winResLoadString(IDS_NEW_VALUE); + m_list.InsertColumn(2, temp, LVCFMT_CENTER, 125, 2); + + m_list.SetFont(CFont::FromHandle((HFONT)GetStockObject(SYSTEM_FIXED_FONT)), + TRUE); + + m_list.SetExtendedStyle(LVS_EX_FULLROWSELECT); + + if(!cheatSearchData.count) { + GetDlgItem(IDC_SEARCH)->EnableWindow(FALSE); + GetDlgItem(IDC_ADD_CHEAT)->EnableWindow(FALSE); + } + + valueType = regQueryDwordValue("cheatsValueType", 0); + if(valueType < 0 || valueType > 1) + valueType = 0; + + searchType = regQueryDwordValue("cheatsSearchType", SEARCH_EQ); + if(searchType > 5 || searchType < 0) + searchType = 0; + + numberType = regQueryDwordValue("cheatsNumberType", 2); + if(numberType < 0 || numberType > 2) + numberType = 2; + + sizeType = regQueryDwordValue("cheatsSizeType", 0); + if(sizeType < 0 || sizeType > 2) + sizeType = 0; + + updateValues = regQueryDwordValue("cheatsUpdate", 0) ? + true : false; + + UpdateData(FALSE); + + if(valueType == 0) + m_value.EnableWindow(FALSE); + CenterWindow(); + + if(cheatSearchData.count) { + addChanges(false); + } + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void GBACheatSearch::addChanges(bool showMsgs) +{ + int count = cheatSearchGetCount(&cheatSearchData, sizeType); + + m_list.DeleteAllItems(); + + if(count > 1000) { + if(showMsgs) + systemMessage(IDS_SEARCH_PRODUCED_TOO_MANY, + "Search produced %d results. Please refine better", + count); + return; + } + + if(count == 0) { + if(showMsgs) + systemMessage(IDS_SEARCH_PRODUCED_NO_RESULTS, + "Search produced no results."); + return; + } + + m_list.SetItemCount(count); + if(data) + free(data); + + data = (WinCheatsData *)calloc(count,sizeof(WinCheatsData)); + + int inc = 1; + switch(sizeType) { + case 1: + inc = 2; + break; + case 2: + inc = 4; + break; + } + + int index = 0; + if(numberType == 0) { + for(int i = 0; i < cheatSearchData.count; i++) { + CheatSearchBlock *block = &cheatSearchData.blocks[i]; + + for(int j = 0; j < block->size; j+= inc) { + if(IS_BIT_SET(block->bits, j)) { + addChange(index++, + block->offset | j, + cheatSearchSignedRead(block->saved, + j, + sizeType), + cheatSearchSignedRead(block->data, + j, + sizeType)); + } + } + } + } else { + for(int i = 0; i < cheatSearchData.count; i++) { + CheatSearchBlock *block = &cheatSearchData.blocks[i]; + + for(int j = 0; j < block->size; j+= inc) { + if(IS_BIT_SET(block->bits, j)) { + addChange(index++, + block->offset | j, + cheatSearchRead(block->saved, + j, + sizeType), + cheatSearchRead(block->data, + j, + sizeType)); + } + } + } + } + + for(int i = 0; i < count; i++) { + LVITEM item; + + item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE; + item.iItem = i; + item.iSubItem = 0; + item.lParam = data[i].addr; + item.state = 0; + item.stateMask = 0; + item.pszText = LPSTR_TEXTCALLBACK; + m_list.InsertItem(&item); + + m_list.SetItemText(i, 1, LPSTR_TEXTCALLBACK); + m_list.SetItemText(i, 2, LPSTR_TEXTCALLBACK); + } +} + +void GBACheatSearch::addChange(int index, u32 address, u32 oldValue, u32 newValue) +{ + data[index].addr = address; + sprintf(data[index].address, "%08x",address); + switch(numberType) { + case 0: + sprintf(data[index].oldValue, "%d", oldValue); + sprintf(data[index].newValue, "%d", newValue); + break; + case 1: + sprintf(data[index].oldValue, "%u", oldValue); + sprintf(data[index].newValue, "%u", newValue); + break; + case 2: + switch(sizeType) { + case 0: + sprintf(data[index].oldValue, "%02x", oldValue); + sprintf(data[index].newValue, "%02x", newValue); + break; + case 1: + sprintf(data[index].oldValue, "%04x", oldValue); + sprintf(data[index].newValue, "%04x", newValue); + break; + case 2: + sprintf(data[index].oldValue, "%08x", oldValue); + sprintf(data[index].newValue, "%08x", newValue); + break; + } + } +} + +void GBACheatSearch::OnValueType(UINT id) +{ + switch(id) { + case IDC_OLD_VALUE: + valueType = 0; + m_value.EnableWindow(FALSE); + regSetDwordValue("cheatsValueType", 0); + break; + case IDC_SPECIFIC_VALUE: + valueType = 1; + m_value.EnableWindow(TRUE); + regSetDwordValue("cheatsValueType", 1); + break; + } +} + +void GBACheatSearch::OnSearchType(UINT id) +{ + switch(id) { + case IDC_EQ: + searchType = SEARCH_EQ; + regSetDwordValue("cheatsSearchType", 0); + break; + case IDC_NE: + searchType = SEARCH_NE; + regSetDwordValue("cheatsSearchType", 1); + break; + case IDC_LT: + searchType = SEARCH_LT; + regSetDwordValue("cheatsSearchType", 2); + break; + case IDC_LE: + searchType = SEARCH_LE; + regSetDwordValue("cheatsSearchType", 3); + break; + case IDC_GT: + searchType = SEARCH_GT; + regSetDwordValue("cheatsSearchType", 4); + break; + case IDC_GE: + searchType = SEARCH_GE; + regSetDwordValue("cheatsSearchType", 5); + break; + } +} + +void GBACheatSearch::OnNumberType(UINT id) +{ + switch(id) { + case IDC_SIGNED: + numberType = 0; + regSetDwordValue("cheatsNumberType", 0); + if(m_list.GetItemCount()) { + addChanges(false); + } + break; + case IDC_UNSIGNED: + numberType = 1; + regSetDwordValue("cheatsNumberType", 1); + if(m_list.GetItemCount()) { + addChanges(false); + } + break; + case IDC_HEXADECIMAL: + numberType = 2; + regSetDwordValue("cheatsNumberType", 2); + if(m_list.GetItemCount()) { + addChanges(false); + } + break; + } +} + +void GBACheatSearch::OnSizeType(UINT id) +{ + switch(id) { + case IDC_SIZE_8: + sizeType = BITS_8; + regSetDwordValue("cheatsSizeType", 0); + if(m_list.GetItemCount()) { + addChanges(false); + } + break; + case IDC_SIZE_16: + sizeType = BITS_16; + regSetDwordValue("cheatsSizeType", 1); + if(m_list.GetItemCount()) { + addChanges(false); + } + break; + case IDC_SIZE_32: + sizeType = BITS_32; + regSetDwordValue("cheatsSizeType", 2); + if(m_list.GetItemCount()) { + addChanges(false); + } + break; + } +} +///////////////////////////////////////////////////////////////////////////// +// AddCheat dialog + + +AddCheat::AddCheat(u32 address, CWnd* pParent /*=NULL*/) + : CDialog(AddCheat::IDD, pParent) +{ + //{{AFX_DATA_INIT(AddCheat) + sizeType = -1; + numberType = -1; + //}}AFX_DATA_INIT + this->address = address; +} + + +void AddCheat::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(AddCheat) + DDX_Control(pDX, IDC_VALUE, m_value); + DDX_Control(pDX, IDC_DESC, m_desc); + DDX_Control(pDX, IDC_ADDRESS, m_address); + DDX_Radio(pDX, IDC_SIZE_8, sizeType); + DDX_Radio(pDX, IDC_SIGNED, numberType); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(AddCheat, CDialog) + //{{AFX_MSG_MAP(AddCheat) + ON_BN_CLICKED(ID_OK, OnOk) + ON_BN_CLICKED(ID_CANCEL, OnCancel) + ON_CONTROL_RANGE(BN_CLICKED, IDC_SIGNED, IDC_HEXADECIMAL, OnNumberType) + ON_CONTROL_RANGE(BN_CLICKED, IDC_SIZE_8, IDC_SIZE_32, OnSizeType) + //}}AFX_MSG_MAP + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// AddCheat message handlers + +void AddCheat::OnOk() +{ + // add cheat + if(addCheat()) { + EndDialog(TRUE); + } +} + +void AddCheat::OnCancel() +{ + EndDialog(FALSE); +} + +BOOL AddCheat::OnInitDialog() +{ + CDialog::OnInitDialog(); + + if(address != 0) { + CString buffer; + buffer.Format("%08x", address); + m_address.SetWindowText(buffer); + m_address.EnableWindow(FALSE); + } + + numberType = regQueryDwordValue("cheatsNumberType", 2); + if(numberType < 0 || numberType > 2) + numberType = 2; + + sizeType = regQueryDwordValue("cheatsSizeType", 0); + if(sizeType < 0 || sizeType > 2) + sizeType = 0; + + UpdateData(FALSE); + + GetDlgItem(IDC_DESC)->SendMessage(EM_LIMITTEXT, + 32, + 0); + if(address != 0) { + GetDlgItem(IDC_SIZE_8)->EnableWindow(FALSE); + GetDlgItem(IDC_SIZE_16)->EnableWindow(FALSE); + GetDlgItem(IDC_SIZE_32)->EnableWindow(FALSE); + GetDlgItem(IDC_HEXADECIMAL)->EnableWindow(FALSE); + GetDlgItem(IDC_UNSIGNED)->EnableWindow(FALSE); + GetDlgItem(IDC_SIGNED)->EnableWindow(FALSE); + } + + CenterWindow(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void AddCheat::OnNumberType(UINT id) +{ + switch(id) { + case IDC_SIGNED: + numberType = 0; + regSetDwordValue("cheatsNumberType", 0); + break; + case IDC_UNSIGNED: + numberType = 1; + regSetDwordValue("cheatsNumberType", 1); + break; + case IDC_HEXADECIMAL: + numberType = 2; + regSetDwordValue("cheatsNumberType", 2); + break; + } +} + +void AddCheat::OnSizeType(UINT id) +{ + switch(id) { + case IDC_SIZE_8: + sizeType = BITS_8; + regSetDwordValue("cheatsSizeType", 0); + break; + case IDC_SIZE_16: + sizeType = BITS_16; + regSetDwordValue("cheatsSizeType", 1); + break; + case IDC_SIZE_32: + sizeType = BITS_32; + regSetDwordValue("cheatsSizeType", 2); + break; + } +} + +bool AddCheat::addCheat() +{ + CString buffer; + CString code; + + m_address.GetWindowText(buffer); + u32 address = 0; + sscanf(buffer, "%x", &address); + if((address >= 0x02000000 && address < 0x02040000) || + (address >= 0x03000000 && address < 0x03008000)) { + } else { + systemMessage(IDS_INVALID_ADDRESS, "Invalid address: %08x", address); + return false; + } + if(sizeType != 0) { + if(sizeType == 1 && address & 1) { + systemMessage(IDS_MISALIGNED_HALFWORD, + "Misaligned half-word address: %08x", address); + return false; + } + if(sizeType == 2 && address & 3) { + systemMessage(IDS_MISALIGNED_WORD, + "Misaligned word address: %08x", address); + return false; + } + } + u32 value; + m_value.GetWindowText(buffer); + + if(buffer.IsEmpty()) { + systemMessage(IDS_VALUE_CANNOT_BE_EMPTY, "Value cannot be empty"); + return false; + } + + switch(numberType) { + case 0: + sscanf(buffer, "%d", &value); + break; + case 1: + sscanf(buffer, "%u", &value); + break; + default: + sscanf(buffer, "%x", &value); + } + + m_desc.GetWindowText(buffer); + + switch(sizeType) { + case 0: + code.Format("%08x:%02x", address, value); + break; + case 1: + code.Format("%08x:%04x", address, value); + break; + case 2: + code.Format("%08x:%08x", address, value); + break; + } + + cheatsAdd(code, buffer, address, address, value, -1, sizeType); + return true; +} +///////////////////////////////////////////////////////////////////////////// +// GBACheatList dialog + + +GBACheatList::GBACheatList(CWnd* pParent /*=NULL*/) + : CDialog(GBACheatList::IDD, pParent) +{ + //{{AFX_DATA_INIT(GBACheatList) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT + duringRefresh = false; +} + + +void GBACheatList::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(GBACheatList) + DDX_Control(pDX, IDC_RESTORE, m_restore); + DDX_Control(pDX, IDC_CHEAT_LIST, m_list); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(GBACheatList, CDialog) + //{{AFX_MSG_MAP(GBACheatList) + ON_BN_CLICKED(IDC_ADD_CHEAT, OnAddCheat) + ON_BN_CLICKED(IDC_ADD_CODE, OnAddCode) + ON_BN_CLICKED(IDC_ADD_CODEBREAKER, OnAddCodebreaker) + ON_BN_CLICKED(IDC_ADD_GAMESHARK, OnAddGameshark) + ON_BN_CLICKED(IDC_ENABLE, OnEnable) + ON_BN_CLICKED(IDC_REMOVE, OnRemove) + ON_BN_CLICKED(IDC_REMOVE_ALL, OnRemoveAll) + ON_BN_CLICKED(IDC_RESTORE, OnRestore) + ON_BN_CLICKED(ID_OK, OnOk) + ON_NOTIFY(LVN_ITEMCHANGED, IDC_CHEAT_LIST, OnItemchangedCheatList) + //}}AFX_MSG_MAP + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// GBACheatList message handlers + +void GBACheatList::OnAddCheat() +{ + AddCheat dlg(0); + dlg.DoModal(); + refresh(); +} + +void GBACheatList::OnAddCode() +{ + AddCheatCode dlg; + dlg.DoModal(); + refresh(); +} + +void GBACheatList::OnAddCodebreaker() +{ + AddCBACode dlg; + dlg.DoModal(); + refresh(); +} + +void GBACheatList::OnAddGameshark() +{ + AddGSACode dlg; + dlg.DoModal(); + refresh(); +} + +void GBACheatList::OnEnable() +{ + int mark = m_list.GetSelectionMark(); + int count = m_list.GetItemCount(); + + if(mark != -1) { + LVITEM item; + for(int i = 0; i < count; i++) { + memset(&item, 0, sizeof(item)); + item.mask = LVIF_PARAM|LVIF_STATE; + item.stateMask = LVIS_SELECTED; + item.iItem = i; + if(m_list.GetItem(&item)) { + if(item.state & LVIS_SELECTED) { + if(cheatsList[item.lParam].enabled) + cheatsDisable((int)(item.lParam & 0xFFFFFFFF)); + else + cheatsEnable((int)(item.lParam & 0xFFFFFFFF)); + } + } + } + refresh(); + } +} + +void GBACheatList::OnRemove() +{ + int mark = m_list.GetSelectionMark(); + int count = m_list.GetItemCount(); + + if(mark != -1) { + for(int i = count - 1; i >= 0; i--) { + LVITEM item; + memset(&item,0, sizeof(item)); + item.mask = LVIF_PARAM|LVIF_STATE; + item.iItem = i; + item.stateMask = LVIS_SELECTED; + if(m_list.GetItem(&item)) { + if(item.state & LVIS_SELECTED) { + cheatsDelete((int)(item.lParam & 0xFFFFFFFF), restoreValues); + } + } + } + refresh(); + } +} + +void GBACheatList::OnRemoveAll() +{ + cheatsDeleteAll(restoreValues); + refresh(); +} + + +void GBACheatList::OnRestore() +{ + restoreValues = !restoreValues; + regSetDwordValue("cheatsRestore", restoreValues); +} + +void GBACheatList::OnOk() +{ + EndDialog(TRUE); +} + +void GBACheatList::OnItemchangedCheatList(NMHDR* pNMHDR, LRESULT* pResult) +{ + if(m_list.GetSelectionMark() != -1) { + GetDlgItem(IDC_REMOVE)->EnableWindow(TRUE); + GetDlgItem(IDC_ENABLE)->EnableWindow(TRUE); + } else { + GetDlgItem(IDC_REMOVE)->EnableWindow(FALSE); + GetDlgItem(IDC_ENABLE)->EnableWindow(FALSE); + } + + if(!duringRefresh) { + LPNMLISTVIEW l = (LPNMLISTVIEW)pNMHDR; + if(l->uChanged & LVIF_STATE) { + if(((l->uOldState & LVIS_STATEIMAGEMASK)>>12) != + (((l->uNewState & LVIS_STATEIMAGEMASK)>>12))) { + if(m_list.GetCheck(l->iItem)) + cheatsEnable((int)(l->lParam & 0xFFFFFFFF)); + else + cheatsDisable((int)(l->lParam & 0xFFFFFFFF)); + refresh(); + } + } + } + + *pResult = 0; +} + +BOOL GBACheatList::OnInitDialog() +{ + CDialog::OnInitDialog(); + + CString temp = winResLoadString(IDS_CODE); + m_list.InsertColumn(0, temp, LVCFMT_LEFT, 170, 0); + temp = winResLoadString(IDS_DESCRIPTION); + m_list.InsertColumn(1, temp, LVCFMT_LEFT, 150, 1); + temp = winResLoadString(IDS_STATUS); + m_list.InsertColumn(2, temp, LVCFMT_LEFT, 80, 1); + + m_list.SetFont(CFont::FromHandle((HFONT)GetStockObject(SYSTEM_FIXED_FONT)), + TRUE); + + m_list.SetExtendedStyle(LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT); + + restoreValues = regQueryDwordValue("cheatsRestore", 0) ? + true : false; + + m_restore.SetCheck(restoreValues); + + refresh(); + GetDlgItem(IDC_REMOVE)->EnableWindow(FALSE); + GetDlgItem(IDC_ENABLE)->EnableWindow(FALSE); + CenterWindow(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void GBACheatList::refresh() +{ + duringRefresh = true; + m_list.DeleteAllItems(); + + CString buffer; + + for(int i = 0; i < cheatsNumber; i++) { + LVITEM item; + + item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE; + item.iItem = i; + item.iSubItem = 0; + item.lParam = i; + item.state = 0; + item.stateMask = 0; + item.pszText = cheatsList[i].codestring; + m_list.InsertItem(&item); + + m_list.SetCheck(i, (cheatsList[i].enabled) ? TRUE : FALSE); + + m_list.SetItemText(i, 1, cheatsList[i].desc); + + buffer = (cheatsList[i].enabled) ? 'E' : 'D'; + m_list.SetItemText(i, 2, buffer); + } + duringRefresh = false; +} +///////////////////////////////////////////////////////////////////////////// +// AddGSACode dialog + + +AddGSACode::AddGSACode(CWnd* pParent /*=NULL*/) + : CDialog(AddGSACode::IDD, pParent) +{ + //{{AFX_DATA_INIT(AddGSACode) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT +} + + +void AddGSACode::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(AddGSACode) + DDX_Control(pDX, IDC_DESC, m_desc); + DDX_Control(pDX, IDC_CODE, m_code); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(AddGSACode, CDialog) + //{{AFX_MSG_MAP(AddGSACode) + ON_BN_CLICKED(ID_OK, OnOk) + ON_BN_CLICKED(ID_CANCEL, OnCancel) + //}}AFX_MSG_MAP + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// AddGSACode message handlers + +void AddGSACode::OnOk() +{ + CString desc; + CString buffer; + CString part1; + CString code; + CString token; + + m_code.GetWindowText(buffer); + m_desc.GetWindowText(desc); + + StringTokenizer st(buffer, " \t\n\r"); + part1.Empty(); + const char *t = st.next(); + while(t) { + token = t; + token.MakeUpper(); + if(token.GetLength() == 16) + cheatsAddGSACode(token, desc, false); + else if(token.GetLength() == 12) { + code = token.Left(8); + code += " "; + code += token.Right(4); + cheatsAddCBACode(code, desc); + } else if(part1.IsEmpty()) + part1 = token; + else { + if(token.GetLength() == 4) { + code = part1; + code += " "; + code += token; + cheatsAddCBACode(code, desc); + } else { + code = part1 + token; + cheatsAddGSACode(code, desc, true); + } + part1.Empty(); + } + + t = st.next(); + } + EndDialog(TRUE); +} + +void AddGSACode::OnCancel() +{ + EndDialog(FALSE); +} + +BOOL AddGSACode::OnInitDialog() +{ + CDialog::OnInitDialog(); + + m_code.LimitText(1024); + m_desc.LimitText(32); + CString title = winResLoadString(IDS_ADD_GSA_CODE); + SetWindowText(title); + CenterWindow(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +///////////////////////////////////////////////////////////////////////////// +// AddCBACode dialog + + +AddCBACode::AddCBACode(CWnd* pParent /*=NULL*/) + : CDialog(AddCBACode::IDD, pParent) +{ + //{{AFX_DATA_INIT(AddCBACode) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT +} + + +void AddCBACode::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(AddCBACode) + DDX_Control(pDX, IDC_DESC, m_desc); + DDX_Control(pDX, IDC_CODE, m_code); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(AddCBACode, CDialog) + //{{AFX_MSG_MAP(AddCBACode) + ON_BN_CLICKED(ID_OK, OnOk) + ON_BN_CLICKED(ID_CANCEL, OnCancel) + //}}AFX_MSG_MAP + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// AddCBACode message handlers + +void AddCBACode::OnOk() +{ + CString desc; + CString buffer; + CString part1; + CString code; + CString token; + + m_code.GetWindowText(buffer); + m_desc.GetWindowText(desc); + + StringTokenizer st(buffer, " \t\n\r"); + part1.Empty(); + const char *t = st.next(); + while(t) { + token = t; + token.MakeUpper(); + if(token.GetLength() == 16) + cheatsAddGSACode(token, desc, false); + else if(token.GetLength() == 12) { + code = token.Left(8); + code += " "; + code += token.Right(4); + cheatsAddCBACode(code, desc); + } else if(part1.IsEmpty()) + part1 = token; + else { + if(token.GetLength() == 4) { + code = part1; + code += " "; + code += token; + cheatsAddCBACode(code, desc); + } else { + code = part1 + token; + cheatsAddGSACode(code, desc, true); + } + part1.Empty(); + } + + t = st.next(); + } + EndDialog(TRUE); +} + +void AddCBACode::OnCancel() +{ + EndDialog(FALSE); +} + +BOOL AddCBACode::OnInitDialog() +{ + CDialog::OnInitDialog(); + + m_code.LimitText(1024); + m_desc.LimitText(32); + CString title = winResLoadString(IDS_ADD_CBA_CODE); + SetWindowText(title); + CenterWindow(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +///////////////////////////////////////////////////////////////////////////// +// AddCheatCode dialog + + +AddCheatCode::AddCheatCode(CWnd* pParent /*=NULL*/) + : CDialog(AddCheatCode::IDD, pParent) +{ + //{{AFX_DATA_INIT(AddCheatCode) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT +} + + +void AddCheatCode::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(AddCheatCode) + DDX_Control(pDX, IDC_DESC, m_desc); + DDX_Control(pDX, IDC_CODE, m_code); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(AddCheatCode, CDialog) + //{{AFX_MSG_MAP(AddCheatCode) + ON_BN_CLICKED(ID_OK, OnOk) + ON_BN_CLICKED(ID_CANCEL, OnCancel) + //}}AFX_MSG_MAP + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// AddCheatCode message handlers + +void AddCheatCode::OnOk() +{ + CString desc; + CString buffer; + CString token; + + m_code.GetWindowText(buffer); + m_desc.GetWindowText(desc); + + StringTokenizer st(buffer, " \t\n\r"); + const char *t = st.next(); + while(t) { + token = t; + token.MakeUpper(); + cheatsAddCheatCode(token, desc); + t = st.next(); + } + EndDialog(TRUE); +} + +void AddCheatCode::OnCancel() +{ + EndDialog(FALSE); +} + +BOOL AddCheatCode::OnInitDialog() +{ + CDialog::OnInitDialog(); + + m_code.LimitText(1024); + m_desc.LimitText(32); + CString title = winResLoadString(IDS_ADD_CHEAT_CODE); + SetWindowText(title); + CenterWindow(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} diff --git a/src/win32/GBACheats.h b/src/win32/GBACheats.h new file mode 100644 index 0000000..b2d2fd9 --- /dev/null +++ b/src/win32/GBACheats.h @@ -0,0 +1,272 @@ +#if !defined(AFX_GBACHEATS_H__FC31D47D_52C8_42B2_95C7_7C3FD09316A4__INCLUDED_) +#define AFX_GBACHEATS_H__FC31D47D_52C8_42B2_95C7_7C3FD09316A4__INCLUDED_ + +#include "../System.h" // Added by ClassView +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// GBACheats.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// GBACheatSearch dialog + +struct WinCheatsData { + u32 addr; + char address[9]; + char oldValue[12]; + char newValue[12]; +}; + +class GBACheatSearch : public CDialog +{ + // Construction + public: + afx_msg void OnSizeType(UINT id); + afx_msg void OnNumberType(UINT id); + afx_msg void OnSearchType(UINT id); + afx_msg void OnValueType(UINT id); + void addChange(int index, u32 address, u32 oldValue, u32 newValue); + GBACheatSearch(CWnd* pParent = NULL); // standard constructor + ~GBACheatSearch(); + + // Dialog Data + //{{AFX_DATA(GBACheatSearch) + enum { IDD = IDD_CHEATS }; + CEdit m_value; + CListCtrl m_list; + int valueType; + int sizeType; + int searchType; + int numberType; + BOOL updateValues; + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(GBACheatSearch) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + + // Implementation + protected: + + // Generated message map functions + //{{AFX_MSG(GBACheatSearch) + afx_msg void OnOk(); + afx_msg void OnStart(); + afx_msg void OnSearch(); + afx_msg void OnAddCheat(); + afx_msg void OnUpdate(); + afx_msg void OnGetdispinfoCheatList(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnItemchangedCheatList(NMHDR* pNMHDR, LRESULT* pResult); + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + private: + void addChanges(bool showMsgs); + WinCheatsData *data; +}; + +///////////////////////////////////////////////////////////////////////////// +// AddCheat dialog + +class AddCheat : public CDialog +{ + // Construction + public: + bool addCheat(); + afx_msg void OnSizeType(UINT id); + afx_msg void OnNumberType(UINT id); + u32 address; + AddCheat(u32 address, CWnd* pParent = NULL); // standard constructor + + // Dialog Data + //{{AFX_DATA(AddCheat) + enum { IDD = IDD_ADD_CHEAT }; + CEdit m_value; + CEdit m_desc; + CEdit m_address; + int sizeType; + int numberType; + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(AddCheat) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + + // Implementation + protected: + + // Generated message map functions + //{{AFX_MSG(AddCheat) + afx_msg void OnOk(); + afx_msg void OnCancel(); + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + ///////////////////////////////////////////////////////////////////////////// +// GBACheatList dialog + +class GBACheatList : public CDialog +{ + // Construction + public: + void refresh(); + bool duringRefresh; + bool restoreValues; + + GBACheatList(CWnd* pParent = NULL); // standard constructor + + // Dialog Data + //{{AFX_DATA(GBACheatList) + enum { IDD = IDD_CHEAT_LIST }; + CButton m_restore; + CListCtrl m_list; + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(GBACheatList) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + + // Implementation + protected: + + // Generated message map functions + //{{AFX_MSG(GBACheatList) + afx_msg void OnAddCheat(); + afx_msg void OnAddCode(); + afx_msg void OnAddCodebreaker(); + afx_msg void OnAddGameshark(); + afx_msg void OnEnable(); + afx_msg void OnRemove(); + afx_msg void OnRemoveAll(); + afx_msg void OnRestore(); + afx_msg void OnOk(); + afx_msg void OnItemchangedCheatList(NMHDR* pNMHDR, LRESULT* pResult); + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + ///////////////////////////////////////////////////////////////////////////// +// AddGSACode dialog + +class AddGSACode : public CDialog +{ + // Construction + public: + AddGSACode(CWnd* pParent = NULL); // standard constructor + + // Dialog Data + //{{AFX_DATA(AddGSACode) + enum { IDD = IDD_ADD_CHEAT_DLG }; + CEdit m_desc; + CEdit m_code; + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(AddGSACode) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + + // Implementation + protected: + + // Generated message map functions + //{{AFX_MSG(AddGSACode) + afx_msg void OnOk(); + afx_msg void OnCancel(); + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + + ///////////////////////////////////////////////////////////////////////////// +// AddCBACode dialog + +class AddCBACode : public CDialog +{ + // Construction + public: + AddCBACode(CWnd* pParent = NULL); // standard constructor + + // Dialog Data + //{{AFX_DATA(AddCBACode) + enum { IDD = IDD_ADD_CHEAT_DLG }; + CEdit m_desc; + CEdit m_code; + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(AddCBACode) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + + // Implementation + protected: + + // Generated message map functions + //{{AFX_MSG(AddCBACode) + afx_msg void OnOk(); + afx_msg void OnCancel(); + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + + ///////////////////////////////////////////////////////////////////////////// +// AddCheatCode dialog + +class AddCheatCode : public CDialog +{ + // Construction + public: + AddCheatCode(CWnd* pParent = NULL); // standard constructor + + // Dialog Data + //{{AFX_DATA(AddCheatCode) + enum { IDD = IDD_ADD_CHEAT_DLG }; + CEdit m_desc; + CEdit m_code; + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(AddCheatCode) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + + // Implementation + protected: + + // Generated message map functions + //{{AFX_MSG(AddCheatCode) + afx_msg void OnOk(); + afx_msg void OnCancel(); + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + //{{AFX_INSERT_LOCATION}} + // Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_GBACHEATS_H__FC31D47D_52C8_42B2_95C7_7C3FD09316A4__INCLUDED_) diff --git a/src/win32/GBCheatsDlg.cpp b/src/win32/GBCheatsDlg.cpp new file mode 100644 index 0000000..404563b --- /dev/null +++ b/src/win32/GBCheatsDlg.cpp @@ -0,0 +1,1028 @@ +#include "stdafx.h" +#include "vba.h" +#include "GBCheatsDlg.h" +#include "Reg.h" +#include "StringTokenizer.h" +#include "WinResUtil.h" + +#include "../System.h" +#include "../gba/CheatSearch.h" +#include "../gb/gbCheats.h" +#include "../gb/gbGlobals.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +static bool winGbCheatAddVerifyGs(const char *code, const char *desc) +{ + return gbAddGsCheat(code, desc); +} + +static bool winGbCheatAddVerifyGg(const char *code, const char *desc) +{ + return gbAddGgCheat(code, desc); +} + +///////////////////////////////////////////////////////////////////////////// +// GBCheatSearch dialog + +GBCheatSearch::GBCheatSearch(CWnd* pParent /*=NULL*/) + : CDialog(GBCheatSearch::IDD, pParent) +{ + //{{AFX_DATA_INIT(GBCheatSearch) + searchType = -1; + numberType = -1; + sizeType = -1; + updateValues = FALSE; + valueType = -1; + //}}AFX_DATA_INIT + data = NULL; +} + +GBCheatSearch::~GBCheatSearch() +{ + if(data) + free(data); +} + +void GBCheatSearch::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(GBCheatSearch) + DDX_Control(pDX, IDC_VALUE, m_value); + DDX_Control(pDX, IDC_CHEAT_LIST, m_list); + DDX_Radio(pDX, IDC_EQ, searchType); + DDX_Radio(pDX, IDC_SIGNED, numberType); + DDX_Radio(pDX, IDC_SIZE_8, sizeType); + DDX_Check(pDX, IDC_UPDATE, updateValues); + DDX_Radio(pDX, IDC_OLD_VALUE, valueType); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(GBCheatSearch, CDialog) + //{{AFX_MSG_MAP(GBCheatSearch) + ON_BN_CLICKED(ID_OK, OnOk) + ON_BN_CLICKED(IDC_ADD_CHEAT, OnAddCheat) + ON_BN_CLICKED(IDC_SEARCH, OnSearch) + ON_BN_CLICKED(IDC_START, OnStart) + ON_BN_CLICKED(IDC_UPDATE, OnUpdate) + ON_NOTIFY(LVN_GETDISPINFO, IDC_CHEAT_LIST, OnGetdispinfoCheatList) + ON_NOTIFY(LVN_ITEMCHANGED, IDC_CHEAT_LIST, OnItemchangedCheatList) + ON_CONTROL_RANGE(BN_CLICKED, IDC_OLD_VALUE, IDC_SPECIFIC_VALUE, OnValueType) + ON_CONTROL_RANGE(BN_CLICKED, IDC_EQ, IDC_GE, OnSearchType) + ON_CONTROL_RANGE(BN_CLICKED, IDC_SIGNED, IDC_HEXADECIMAL, OnNumberType) + ON_CONTROL_RANGE(BN_CLICKED, IDC_SIZE_8, IDC_SIZE_32, OnSizeType) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// GBCheatSearch message handlers + +void GBCheatSearch::OnOk() +{ + if(data) + free(data); + data = NULL; + EndDialog(TRUE); +} + +void GBCheatSearch::OnAddCheat() +{ + int mark = m_list.GetSelectionMark(); + + if(mark != -1) { + LVITEM item; + memset(&item,0, sizeof(item)); + item.mask = LVIF_PARAM; + item.iItem = mark; + if(m_list.GetItem(&item)) { + AddGBCheat dlg((u32)item.lParam); + dlg.DoModal(); + } + } +} + +void GBCheatSearch::OnSearch() +{ + CString buffer; + if(valueType == 0) + cheatSearch(&cheatSearchData, + searchType, + sizeType, + numberType == 0); + else { + m_value.GetWindowText(buffer); + if(buffer.IsEmpty()) { + systemMessage(IDS_NUMBER_CANNOT_BE_EMPTY, "Number cannot be empty"); + return; + } + int value = 0; + switch(numberType) { + case 0: + sscanf(buffer, "%d", &value); + break; + case 1: + sscanf(buffer, "%u", &value); + break; + default: + sscanf(buffer, "%x", &value); + } + cheatSearchValue(&cheatSearchData, + searchType, + sizeType, + numberType == 0, + value); + } + + addChanges(true); + + if(updateValues) + cheatSearchUpdateValues(&cheatSearchData); +} + +void GBCheatSearch::OnStart() +{ + if(cheatSearchData.count == 0) { + int i = 0; + + CheatSearchBlock *block = &cheatSearchData.blocks[0]; + + if(gbRamSize) { + block->offset = 0xa000; + if(gbRam) + block->data = gbRam; + else + block->data = &gbMemory[0xa000]; + block->saved = (u8*)malloc(gbRamSize); + block->size = gbRamSize; + block->bits = (u8 *)malloc(gbRamSize >> 3); + i++; + } + block = &cheatSearchData.blocks[i]; + if(gbCgbMode) { + block->offset = 0xc000; + block->data = &gbMemory[0xc000]; + block->saved = (u8*)malloc(0x1000); + block->size = 0x1000; + block->bits = (u8 *)malloc(0x1000 >> 3); + i++; + block =&cheatSearchData.blocks[i]; + block->offset = 0xd000; + block->data = gbWram; + block->saved = (u8*)malloc(0x8000); + block->size = 0x8000; + block->bits = (u8 *)malloc(0x8000 >> 3); + i++; + } else { + block->offset = 0xc000; + block->data = &gbMemory[0xc000]; + block->saved = (u8*)malloc(0x2000); + block->size = 0x2000; + block->bits = (u8 *)malloc(0x2000 >> 3); + i++; + } + cheatSearchData.count = i; + } + + cheatSearchStart(&cheatSearchData); + GetDlgItem(IDC_SEARCH)->EnableWindow(TRUE); +} + +void GBCheatSearch::OnUpdate() +{ + if(GetDlgItem(IDC_UPDATE)->SendMessage(BM_GETCHECK, + 0, + 0) & BST_CHECKED) + updateValues = true; + else + updateValues = false; + regSetDwordValue("gbCheatsUpdate", updateValues); +} + +BOOL GBCheatSearch::OnInitDialog() +{ + CDialog::OnInitDialog(); + + CString temp = winResLoadString(IDS_ADDRESS); + + m_list.InsertColumn(0, temp, LVCFMT_CENTER, 125, 0); + + temp = winResLoadString(IDS_OLD_VALUE); + m_list.InsertColumn(1, temp, LVCFMT_CENTER, 125, 1); + + temp = winResLoadString(IDS_NEW_VALUE); + m_list.InsertColumn(2, temp, LVCFMT_CENTER, 125, 2); + + m_list.SetFont(CFont::FromHandle((HFONT)GetStockObject(SYSTEM_FIXED_FONT)), + TRUE); + + m_list.SetExtendedStyle(LVS_EX_FULLROWSELECT); + + if(!cheatSearchData.count) { + GetDlgItem(IDC_SEARCH)->EnableWindow(FALSE); + GetDlgItem(IDC_ADD_CHEAT)->EnableWindow(FALSE); + } + + valueType = regQueryDwordValue("gbCheatsValueType", 0); + if(valueType < 0 || valueType > 1) + valueType = 2; + + searchType = regQueryDwordValue("gbCheatsSearchType", + SEARCH_EQ); + if(searchType < 0 || searchType > 5) + searchType = 0; + + numberType = regQueryDwordValue("gbCheatsNumberType", 2); + if(numberType < 0 || numberType > 2) + numberType = 2; + + sizeType = regQueryDwordValue("gbCheatsSizeType", 0); + if(sizeType < 0 || sizeType > 2) + sizeType = 0; + + updateValues = regQueryDwordValue("gbCheatsUpdate", 0) ? + true : false; + + UpdateData(FALSE); + + if(valueType == 0) + m_value.EnableWindow(FALSE); + + CenterWindow(); + + if(cheatSearchData.count) { + addChanges(false); + } + + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void GBCheatSearch::OnGetdispinfoCheatList(NMHDR* pNMHDR, LRESULT* pResult) +{ + LV_DISPINFO* info = (LV_DISPINFO*)pNMHDR; + if(info->item.mask & LVIF_TEXT) { + int index = info->item.iItem; + int col = info->item.iSubItem; + + switch(col) { + case 0: + strcpy(info->item.pszText, data[index].address); + break; + case 1: + strcpy(info->item.pszText, data[index].oldValue); + break; + case 2: + strcpy(info->item.pszText, data[index].newValue); + break; + } + } + *pResult = TRUE; +} + +void GBCheatSearch::OnItemchangedCheatList(NMHDR* pNMHDR, LRESULT* pResult) +{ + GetDlgItem(IDC_ADD_CHEAT)->EnableWindow(m_list.GetSelectionMark() != -1); +} + +int GBCheatSearch::getBank(u16 addr, int j) +{ + switch(addr >> 12) { + case 0x0a: + return j / 0x2000; + case 0x0d: + return j / 0x1000; + } + return 0; +} + +void GBCheatSearch::addChange(int index, int bank, u16 address, int offset, u32 oldValue, u32 newValue) +{ + data[index].bank = bank; + if(bank) { + if(address == 0xa000) + address |= offset & 0x1fff; + else + address |= offset & 0xfff; + } else + address |= offset; + data[index].addr = address; + sprintf(data[index].address, "%02x:%04x",bank,address); + switch(numberType) { + case 0: + sprintf(data[index].oldValue, "%d", oldValue); + sprintf(data[index].newValue, "%d", newValue); + break; + case 1: + sprintf(data[index].oldValue, "%u", oldValue); + sprintf(data[index].newValue, "%u", newValue); + break; + case 2: + switch(sizeType) { + case 0: + sprintf(data[index].oldValue, "%02x", oldValue); + sprintf(data[index].newValue, "%02x", newValue); + break; + case 1: + sprintf(data[index].oldValue, "%04x", oldValue); + sprintf(data[index].newValue, "%04x", newValue); + break; + case 2: + sprintf(data[index].oldValue, "%08x", oldValue); + sprintf(data[index].newValue, "%08x", newValue); + break; + } + } +} + +void GBCheatSearch::addChanges(bool showMsg) +{ + int count = cheatSearchGetCount(&cheatSearchData, sizeType); + + m_list.DeleteAllItems(); + + if(count > 1000) { + if(showMsg) + systemMessage(IDS_SEARCH_PRODUCED_TOO_MANY, + "Search produced %d results. Please refine better", + count); + return; + } + + if(count == 0) { + if(showMsg) + systemMessage(IDS_SEARCH_PRODUCED_NO_RESULTS, + "Search produced no results"); + return; + } + + m_list.SetItemCount(count); + if(data) + free(data); + + data = (WinGbCheatsData *)calloc(count, sizeof(WinGbCheatsData)); + + int inc = 1; + switch(sizeType) { + case 1: + inc = 2; + break; + case 2: + inc = 4; + break; + } + + int index = 0; + if(numberType == 0) { + for(int i = 0; i < cheatSearchData.count; i++) { + CheatSearchBlock *block = &cheatSearchData.blocks[i]; + + for(int j = 0; j < block->size; j+= inc) { + if(IS_BIT_SET(block->bits, j)) { + addChange(index++, + getBank(block->offset|j, j), + block->offset, + j, + cheatSearchSignedRead(block->saved, + j, + sizeType), + cheatSearchSignedRead(block->data, + j, + sizeType)); + } + } + } + } else { + for(int i = 0; i < cheatSearchData.count; i++) { + CheatSearchBlock *block = &cheatSearchData.blocks[i]; + + for(int j = 0; j < block->size; j+= inc) { + if(IS_BIT_SET(block->bits, j)) { + addChange(index++, + getBank(block->offset|j, j), + block->offset, + j, + cheatSearchRead(block->saved, + j, + sizeType), + cheatSearchRead(block->data, + j, + sizeType)); + } + } + } + } + + for(int i = 0; i < count; i++) { + LVITEM item; + + item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE; + item.iItem = i; + item.iSubItem = 0; + item.lParam = data[i].addr| + (data[i].bank << 16); + item.state = 0; + item.stateMask = 0; + item.pszText = LPSTR_TEXTCALLBACK; + m_list.InsertItem(&item); + + m_list.SetItemText(i, 1, LPSTR_TEXTCALLBACK); + m_list.SetItemText(i, 2, LPSTR_TEXTCALLBACK); + } +} + +void GBCheatSearch::OnValueType(UINT id) +{ + switch(id) { + case IDC_OLD_VALUE: + valueType = 0; + m_value.EnableWindow(FALSE); + regSetDwordValue("gbCheatsValueType", 0); + break; + case IDC_SPECIFIC_VALUE: + valueType = 1; + m_value.EnableWindow(TRUE); + regSetDwordValue("gbCheatsValueType", 1); + break; + } +} + +void GBCheatSearch::OnSearchType(UINT id) +{ + switch(id) { + case IDC_EQ: + searchType = SEARCH_EQ; + regSetDwordValue("gbCheatsSearchType", 0); + break; + case IDC_NE: + searchType = SEARCH_NE; + regSetDwordValue("gbCheatsSearchType", 1); + break; + case IDC_LT: + searchType = SEARCH_LT; + regSetDwordValue("gbCheatsSearchType", 2); + break; + case IDC_LE: + searchType = SEARCH_LE; + regSetDwordValue("gbCheatsSearchType", 3); + break; + case IDC_GT: + searchType = SEARCH_GT; + regSetDwordValue("gbCheatsSearchType", 4); + break; + case IDC_GE: + searchType = SEARCH_GE; + regSetDwordValue("gbCheatsSearchType", 5); + break; + } +} + +void GBCheatSearch::OnNumberType(UINT id) +{ + switch(id) { + case IDC_SIGNED: + numberType = 0; + regSetDwordValue("gbCheatsNumberType", 0); + if(m_list.GetItemCount()) { + addChanges(false); + } + break; + case IDC_UNSIGNED: + numberType = 1; + regSetDwordValue("gbCheatsNumberType", 1); + if(m_list.GetItemCount()) { + addChanges(false); + } + break; + case IDC_HEXADECIMAL: + numberType = 2; + regSetDwordValue("gbCheatsNumberType", 2); + if(m_list.GetItemCount()) { + addChanges(false); + } + break; + } +} + +void GBCheatSearch::OnSizeType(UINT id) +{ + switch(id) { + case IDC_SIZE_8: + sizeType = BITS_8; + regSetDwordValue("gbCheatsSizeType", 0); + if(m_list.GetItemCount()) { + addChanges(false); + } + break; + case IDC_SIZE_16: + sizeType = BITS_16; + regSetDwordValue("gbCheatsSizeType", 1); + if(m_list.GetItemCount()) { + addChanges(false); + } + break; + case IDC_SIZE_32: + sizeType = BITS_32; + regSetDwordValue("gbCheatsSizeType", 2); + if(m_list.GetItemCount()) { + addChanges(false); + } + break; + } +} +///////////////////////////////////////////////////////////////////////////// +// AddGBCheat dialog + + +AddGBCheat::AddGBCheat(u32 addr, CWnd* pParent /*=NULL*/) + : CDialog(AddGBCheat::IDD, pParent) +{ + //{{AFX_DATA_INIT(AddGBCheat) + sizeType = -1; + numberType = -1; + //}}AFX_DATA_INIT + address = addr; +} + + +void AddGBCheat::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(AddGBCheat) + DDX_Control(pDX, IDC_VALUE, m_value); + DDX_Control(pDX, IDC_ADDRESS, m_address); + DDX_Control(pDX, IDC_DESC, m_desc); + DDX_Radio(pDX, IDC_SIZE_8, sizeType); + DDX_Radio(pDX, IDC_SIGNED, numberType); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(AddGBCheat, CDialog) + //{{AFX_MSG_MAP(AddGBCheat) + ON_BN_CLICKED(ID_OK, OnOk) + ON_BN_CLICKED(ID_CANCEL, OnCancel) + ON_CONTROL_RANGE(BN_CLICKED, IDC_SIGNED, IDC_HEXADECIMAL, OnNumberType) + ON_CONTROL_RANGE(BN_CLICKED, IDC_SIZE_8, IDC_SIZE_32, OnSizeType) + //}}AFX_MSG_MAP + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// AddGBCheat message handlers + +void AddGBCheat::OnCancel() +{ + EndDialog(FALSE); +} + +void AddGBCheat::OnOk() +{ + // add cheat + if(addCheat()) { + EndDialog(TRUE); + } +} + +bool AddGBCheat::addCheat() +{ + CString buffer; + CString code; + + u32 value; + m_value.GetWindowText(buffer); + + if(buffer.IsEmpty()) { + systemMessage(IDS_VALUE_CANNOT_BE_EMPTY, "Value cannot be empty"); + return false; + } + + switch(numberType) { + case 0: + sscanf(buffer, "%d", &value); + break; + case 1: + sscanf(buffer, "%u", &value); + break; + default: + sscanf(buffer, "%x", &value); + } + + m_desc.GetWindowText(buffer); + + LONG_PTR bank = (address >> 16); + address &= 0xFFFF; + + if(address >= 0xd000) + bank += 0x90; + else + bank = 0x01; + + switch(sizeType) { + case 0: + code.Format("%02X%02X%02X%02X", bank, value, address&0xFF, address>>8); + gbAddGsCheat(code, buffer); + break; + case 1: + code.Format("%02X%02X%02X%02X", bank, value&0xFF, address&0xFF, + address>>8); + gbAddGsCheat(code, buffer); + address++; + code.Format("%02X%02X%02X%02X", bank, value>>8, address&0xFF, + address>>8); + gbAddGsCheat(code, buffer); + break; + case 2: + code.Format("%02X%02X%02X%02X", bank, value&0xFF, address&0xFF, + address>>8); + gbAddGsCheat(code, buffer); + address++; + code.Format("%02X%02X%02X%02X", bank, (value>>8) & 0xFF, address&0xFF, + address>>8); + gbAddGsCheat(code, buffer); + address++; + code.Format("%02X%02X%02X%02X", bank, (value>>16)&0xFF, address&0xFF, + address>>8); + gbAddGsCheat(code, buffer); + address++; + code.Format("%02X%02X%02X%02X", bank, value>>24, address&0xFF, + address>>8); + gbAddGsCheat(code, buffer); + break; + } + + return true; +} + +BOOL AddGBCheat::OnInitDialog() +{ + CDialog::OnInitDialog(); + + CString buffer; + buffer.Format("%02x:%08x", (address>>16), address&0xFFFF); + m_address.SetWindowText(buffer); + m_address.EnableWindow(FALSE); + ::SetWindowLongPtr( m_address.GetSafeHwnd(), GWLP_USERDATA, address); + + numberType = regQueryDwordValue("gbCheatsNumberType", 2); + if(numberType < 0 || numberType > 2) + numberType = 2; + + sizeType = regQueryDwordValue("gbCheatsSizeType", 0); + if(sizeType < 0 || sizeType > 2) + sizeType = 0; + + UpdateData(FALSE); + + m_desc.LimitText(32); + + if(address != 0) { + GetDlgItem(IDC_SIZE_8)->EnableWindow(FALSE); + GetDlgItem(IDC_SIZE_16)->EnableWindow(FALSE); + GetDlgItem(IDC_SIZE_32)->EnableWindow(FALSE); + GetDlgItem(IDC_HEXADECIMAL)->EnableWindow(FALSE); + GetDlgItem(IDC_UNSIGNED)->EnableWindow(FALSE); + GetDlgItem(IDC_SIGNED)->EnableWindow(FALSE); + } + CenterWindow(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void AddGBCheat::OnNumberType(UINT id) +{ + switch(id) { + case IDC_SIGNED: + numberType = 0; + regSetDwordValue("gbCheatsNumberType", 0); + break; + case IDC_UNSIGNED: + numberType = 1; + regSetDwordValue("gbCheatsNumberType", 1); + break; + case IDC_HEXADECIMAL: + numberType = 2; + regSetDwordValue("gbCheatsNumberType", 2); + break; + } +} + +void AddGBCheat::OnSizeType(UINT id) +{ + switch(id) { + case IDC_SIZE_8: + sizeType = BITS_8; + regSetDwordValue("gbCheatsSizeType", 0); + break; + case IDC_SIZE_16: + sizeType = BITS_16; + regSetDwordValue("gbCheatsSizeType", 1); + break; + case IDC_SIZE_32: + sizeType = BITS_32; + regSetDwordValue("gbCheatsSizeType", 2); + break; + } +} + +///////////////////////////////////////////////////////////////////////////// +// GBCheatList dialog + + +GBCheatList::GBCheatList(CWnd* pParent /*=NULL*/) + : CDialog(GBCheatList::IDD, pParent) +{ + //{{AFX_DATA_INIT(GBCheatList) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT + duringRefresh = false; +} + + +void GBCheatList::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(GBCheatList) + DDX_Control(pDX, IDC_CHEAT_LIST, m_list); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(GBCheatList, CDialog) + //{{AFX_MSG_MAP(GBCheatList) + ON_BN_CLICKED(ID_OK, OnOk) + ON_BN_CLICKED(IDC_ADD_GG_CHEAT, OnAddGgCheat) + ON_BN_CLICKED(IDC_ADD_GS_CHEAT, OnAddGsCheat) + ON_BN_CLICKED(IDC_ENABLE, OnEnable) + ON_BN_CLICKED(IDC_REMOVE, OnRemove) + ON_BN_CLICKED(IDC_REMOVE_ALL, OnRemoveAll) + ON_NOTIFY(LVN_ITEMCHANGED, IDC_CHEAT_LIST, OnItemchangedCheatList) + //}}AFX_MSG_MAP + ON_NOTIFY(NM_DBLCLK, IDC_CHEAT_LIST, &GBCheatList::OnNMDblclkCheatList) +END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// GBCheatList message handlers + +void GBCheatList::OnOk() +{ + EndDialog(TRUE); +} + +void GBCheatList::OnAddGgCheat() +{ + CString temp = winResLoadString(IDS_ADD_GG_CODE); + AddGBCode dlg(winGbCheatAddVerifyGg, 11, temp); + dlg.DoModal(); + refresh(); +} + +void GBCheatList::OnAddGsCheat() +{ + CString temp = winResLoadString(IDS_ADD_GS_CODE); + + AddGBCode dlg(winGbCheatAddVerifyGs, 8, temp); + dlg.DoModal(); + refresh(); +} + +void GBCheatList::OnEnable() +{ + int mark = m_list.GetSelectionMark(); + + if(mark != -1) { + LVITEM item; + memset(&item,0, sizeof(item)); + item.mask = LVIF_PARAM; + item.iItem = mark; + if(m_list.GetItem(&item)) { + if(gbCheatList[item.lParam].enabled) + gbCheatDisable((int)item.lParam); + else + gbCheatEnable((int)item.lParam); + refresh(); + } + } +} + +void GBCheatList::OnRemove() +{ + int mark = m_list.GetSelectionMark(); + + if(mark != -1) { + LVITEM item; + memset(&item,0, sizeof(item)); + item.mask = LVIF_PARAM; + item.iItem = mark; + if(m_list.GetItem(&item)) { + gbCheatRemove((int)item.lParam); + refresh(); + } + } +} + +void GBCheatList::OnRemoveAll() +{ + gbCheatRemoveAll(); + refresh(); +} + +void GBCheatList::OnItemchangedCheatList(NMHDR* pNMHDR, LRESULT* pResult) +{ + if(m_list.GetSelectionMark() != -1) { + GetDlgItem(IDC_REMOVE)->EnableWindow(TRUE); + GetDlgItem(IDC_ENABLE)->EnableWindow(TRUE); + } else { + GetDlgItem(IDC_REMOVE)->EnableWindow(FALSE); + GetDlgItem(IDC_ENABLE)->EnableWindow(FALSE); + } + + if(!duringRefresh) { + LPNMLISTVIEW l = (LPNMLISTVIEW)pNMHDR; + if(l->uChanged & LVIF_STATE) { + if(((l->uOldState & LVIS_STATEIMAGEMASK)>>12) != + (((l->uNewState & LVIS_STATEIMAGEMASK)>>12))) { + if(m_list.GetCheck(l->iItem)) + gbCheatEnable((int)l->lParam); + else + gbCheatDisable((int)l->lParam); + refresh(); + } + } + } +} + +BOOL GBCheatList::OnInitDialog() +{ + CDialog::OnInitDialog(); + + CString temp = winResLoadString(IDS_CODE); + m_list.InsertColumn(0, temp, LVCFMT_LEFT, 120, 0); + temp = winResLoadString(IDS_DESCRIPTION); + m_list.InsertColumn(1, temp, LVCFMT_LEFT, 200, 1); + temp = winResLoadString(IDS_STATUS); + m_list.InsertColumn(2, temp, LVCFMT_LEFT, 80, 2); + + m_list.SetFont(CFont::FromHandle((HFONT)GetStockObject(SYSTEM_FIXED_FONT)), + TRUE); + + m_list.SetExtendedStyle(LVS_EX_CHECKBOXES | + LVS_EX_FULLROWSELECT); + + refresh(); + GetDlgItem(IDC_REMOVE)->EnableWindow(FALSE); + GetDlgItem(IDC_ENABLE)->EnableWindow(FALSE); + CenterWindow(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void GBCheatList::refresh() +{ + duringRefresh = true; + + m_list.DeleteAllItems(); + + char buffer[2]; + + for(int i = 0; i < gbCheatNumber; i++) { + LVITEM item; + + item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE; + item.iItem = i; + item.iSubItem = 0; + item.lParam = i; + item.state = 0; + item.stateMask = 0; + item.pszText = gbCheatList[i].cheatCode; + m_list.InsertItem(&item); + + m_list.SetCheck(i, (gbCheatList[i].enabled ? TRUE : FALSE)); + + m_list.SetItemText(i, 1, gbCheatList[i].cheatDesc); + + buffer[0] = (gbCheatList[i].enabled) ? 'E' : 'D'; + buffer[1] = 0; + m_list.SetItemText(i, 2, buffer); + } + duringRefresh = false; +} + +///////////////////////////////////////////////////////////////////////////// +// AddGBCode dialog + + +AddGBCode::AddGBCode(bool (*verify)(const char *,const char*), int len, const char *title, CWnd* pParent /*=NULL*/) + : CDialog(AddGBCode::IDD, pParent) +{ + //{{AFX_DATA_INIT(AddGBCode) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT + addVerify = verify; + addLength = len; + addTitle = title; + m_onlyOneLine = false; +} + + +void AddGBCode::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + DDX_Control(pDX, IDC_DESC, m_desc); + DDX_Control(pDX, IDC_CODE, m_code); + DDX_Text(pDX, IDC_DESC, m_descVal); + DDX_Text(pDX, IDC_CODE, m_codeVal); +} + + +BEGIN_MESSAGE_MAP(AddGBCode, CDialog) + //{{AFX_MSG_MAP(AddGBCode) + ON_BN_CLICKED(ID_OK, OnOk) + ON_BN_CLICKED(ID_CANCEL, OnCancel) + //}}AFX_MSG_MAP + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// AddGBCode message handlers + +void AddGBCode::OnOk() +{ + CString desc; + CString buffer; + m_code.GetWindowText(buffer); + m_desc.GetWindowText(desc); + + StringTokenizer st(buffer, " \t\n\r"); + const char *t = st.next(); + while(t) { + if( !addVerify(t, desc) ) { + EndDialog( IDABORT ); + return; + } + if( m_onlyOneLine ) break; + t = st.next(); + } + EndDialog( IDOK ); +} + +void AddGBCode::OnCancel() +{ + EndDialog( IDCANCEL ); +} + +BOOL AddGBCode::OnInitDialog() +{ + CDialog::OnInitDialog(); + + m_code.LimitText(1024); + m_desc.LimitText(32); + SetWindowText(addTitle); + CenterWindow(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void GBCheatList::OnNMDblclkCheatList(NMHDR *pNMHDR, LRESULT *pResult) +{ + int selection = m_list.GetSelectionMark(); + // get index value of corresponding code in cheatlist + if( selection == -1 ) return; + + LVITEM item; + ZeroMemory( &item, sizeof(item) ); + item.mask = LVIF_PARAM; + item.iItem = selection; + if( FALSE == m_list.GetItem( &item ) ) return; + + // modify code + INT_PTR res = IDABORT; + if( gbVerifyGsCode( gbCheatList[ item.lParam ].cheatCode ) ) { + CString temp = winResLoadString(IDS_ADD_GS_CODE); + AddGBCode dlg( winGbCheatAddVerifyGs, 8, temp ); + dlg.m_codeVal = gbCheatList[ item.lParam ].cheatCode; + dlg.m_descVal = gbCheatList[ item.lParam ].cheatDesc; + dlg.m_onlyOneLine = true; + res = dlg.DoModal(); + } else if( gbVerifyGgCode( gbCheatList[ item.lParam ].cheatCode ) ) { + CString temp = winResLoadString(IDS_ADD_GG_CODE); + AddGBCode dlg( winGbCheatAddVerifyGg, 11, temp ); + dlg.m_codeVal = gbCheatList[ item.lParam ].cheatCode; + dlg.m_descVal = gbCheatList[ item.lParam ].cheatDesc; + dlg.m_onlyOneLine = true; + res = dlg.DoModal(); + } + + if( res == IDOK ) { + gbCheatRemove( item.lParam ); // remove old cheat + refresh(); + } + + *pResult = 0; +} diff --git a/src/win32/GBCheatsDlg.h b/src/win32/GBCheatsDlg.h new file mode 100644 index 0000000..8bb6594 --- /dev/null +++ b/src/win32/GBCheatsDlg.h @@ -0,0 +1,203 @@ +#if !defined(AFX_GBCHEATSDLG_H__8ECCB04A_AB75_4552_8625_C6FBF30A95D9__INCLUDED_) +#define AFX_GBCHEATSDLG_H__8ECCB04A_AB75_4552_8625_C6FBF30A95D9__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// GBCheats.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// GBCheatSearch dialog + +struct WinGbCheatsData { + int bank; + u16 addr; + char address[9]; + char oldValue[12]; + char newValue[12]; +}; + +class GBCheatSearch : public CDialog +{ + // Construction + public: + afx_msg void OnSizeType(UINT id); + afx_msg void OnNumberType(UINT id); + afx_msg void OnSearchType(UINT id); + afx_msg void OnValueType(UINT id); + void addChanges(bool showMsg); + void addChange(int index, int bank, u16 address, int offset, u32 oldValue, u32 newValue); + int getBank(u16 addr, int j); + GBCheatSearch(CWnd* pParent = NULL); // standard constructor + ~GBCheatSearch(); + + // Dialog Data + //{{AFX_DATA(GBCheatSearch) + enum { IDD = IDD_CHEATS }; + CEdit m_value; + CListCtrl m_list; + int searchType; + int numberType; + int sizeType; + BOOL updateValues; + int valueType; + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(GBCheatSearch) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + + // Implementation + protected: + WinGbCheatsData *data; + + // Generated message map functions + //{{AFX_MSG(GBCheatSearch) + afx_msg void OnOk(); + afx_msg void OnAddCheat(); + afx_msg void OnSearch(); + afx_msg void OnStart(); + afx_msg void OnUpdate(); + virtual BOOL OnInitDialog(); + afx_msg void OnGetdispinfoCheatList(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnItemchangedCheatList(NMHDR* pNMHDR, LRESULT* pResult); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// +// AddGBCheat dialog + +class AddGBCheat : public CDialog +{ + // Construction + public: + afx_msg void OnSizeType(UINT id); + afx_msg void OnNumberType(UINT id); + bool addCheat(); + AddGBCheat(u32 addr, CWnd* pParent = NULL); // standard constructor + + // Dialog Data + //{{AFX_DATA(AddGBCheat) + enum { IDD = IDD_ADD_CHEAT }; + CEdit m_value; + CEdit m_address; + CEdit m_desc; + int sizeType; + int numberType; + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(AddGBCheat) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + + // Implementation + protected: + LONG_PTR address; + + // Generated message map functions + //{{AFX_MSG(AddGBCheat) + afx_msg void OnCancel(); + afx_msg void OnOk(); + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + ///////////////////////////////////////////////////////////////////////////// +// GBCheatList dialog + +class GBCheatList : public CDialog +{ + // Construction + public: + void refresh(); + bool duringRefresh; + GBCheatList(CWnd* pParent = NULL); // standard constructor + + // Dialog Data + //{{AFX_DATA(GBCheatList) + enum { IDD = IDD_GB_CHEAT_LIST }; + CListCtrl m_list; + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(GBCheatList) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + + // Implementation + protected: + + // Generated message map functions + //{{AFX_MSG(GBCheatList) + afx_msg void OnOk(); + afx_msg void OnAddGgCheat(); + afx_msg void OnAddGsCheat(); + afx_msg void OnEnable(); + afx_msg void OnRemove(); + afx_msg void OnRemoveAll(); + afx_msg void OnItemchangedCheatList(NMHDR* pNMHDR, LRESULT* pResult); + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +public: + afx_msg void OnNMDblclkCheatList(NMHDR *pNMHDR, LRESULT *pResult); +}; + + ///////////////////////////////////////////////////////////////////////////// +// AddGBCode dialog + +class AddGBCode : public CDialog +{ + // Construction + public: + AddGBCode(bool (*verify)(const char *, const char *),int, const char *, CWnd* pParent = NULL); // standard constructor + + // Dialog Data + //{{AFX_DATA(AddGBCode) + enum { IDD = IDD_ADD_CHEAT_DLG }; + CEdit m_desc; + CEdit m_code; + CString m_descVal; + CString m_codeVal; + bool m_onlyOneLine; + //}}AFX_DATA + + int addLength; + CString addTitle; + bool (*addVerify)(const char *, const char*); + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(AddGBCode) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + + // Implementation + protected: + + // Generated message map functions + //{{AFX_MSG(AddGBCode) + afx_msg void OnOk(); + afx_msg void OnCancel(); + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + //{{AFX_INSERT_LOCATION}} + // Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_GBCHEATSDLG_H__8ECCB04A_AB75_4552_8625_C6FBF30A95D9__INCLUDED_) diff --git a/src/win32/GBColorDlg.cpp b/src/win32/GBColorDlg.cpp new file mode 100644 index 0000000..d6c9eb9 --- /dev/null +++ b/src/win32/GBColorDlg.cpp @@ -0,0 +1,239 @@ +#include "stdafx.h" +#include "vba.h" +#include "GBColorDlg.h" +#include "../System.h" +#include "Reg.h" + +extern int gbPaletteOption; +extern int emulating; +extern int cartridgeType; +extern u16 gbPalette[128]; + +static u16 defaultPalettes[][24] = { + { + 0x7FFF, 0x56B5, 0x318C, 0x0000, 0x7FFF, 0x56B5, 0x318C, 0x0000, + }, + { + 0x6200, 0x7E10, 0x7C10, 0x5000, 0x6200, 0x7E10, 0x7C10, 0x5000, + }, + { + 0x4008, 0x4000, 0x2000, 0x2008, 0x4008, 0x4000, 0x2000, 0x2008, + }, + { + 0x43F0, 0x03E0, 0x4200, 0x2200, 0x43F0, 0x03E0, 0x4200, 0x2200, + }, + { + 0x43FF, 0x03FF, 0x221F, 0x021F, 0x43FF, 0x03FF, 0x221F, 0x021F, + }, + { + 0x621F, 0x7E1F, 0x7C1F, 0x2010, 0x621F, 0x7E1F, 0x7C1F, 0x2010, + }, + { + 0x621F, 0x401F, 0x001F, 0x2010, 0x621F, 0x401F, 0x001F, 0x2010, + }, + { + 0x1B8E, 0x02C0, 0x0DA0, 0x1140, 0x1B8E, 0x02C0, 0x0DA0, 0x1140, + }, + { + 0x7BDE, /*0x23F0*/ 0x5778, /*0x5DC0*/ 0x5640, 0x0000, 0x7BDE, /*0x3678*/ 0x529C, /*0x0980*/ 0x2990, 0x0000, + } +}; + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// GBColorDlg dialog + + +GBColorDlg::GBColorDlg(CWnd* pParent /*=NULL*/) + : CDialog(GBColorDlg::IDD, pParent) +{ + which = gbPaletteOption; +} + + +void GBColorDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + DDX_Control(pDX, IDC_PREDEFINED, m_predefined); + DDX_Radio(pDX, IDC_DEFAULT, which); +} + + +BEGIN_MESSAGE_MAP(GBColorDlg, CDialog) + //{{AFX_MSG_MAP(GBColorDlg) + ON_BN_CLICKED(IDC_DEFAULT, OnDefault) + ON_BN_CLICKED(IDC_RESET, OnReset) + ON_BN_CLICKED(IDC_USER1, OnUser1) + ON_BN_CLICKED(IDC_USER2, OnUser2) + ON_BN_CLICKED(ID_OK, OnOk) + ON_BN_CLICKED(ID_CANCEL, OnCancel) + ON_CBN_SELCHANGE(IDC_PREDEFINED, OnSelchangePredefined) + //}}AFX_MSG_MAP + ON_CONTROL_RANGE(BN_CLICKED, IDC_COLOR_BG0, IDC_COLOR_OB3, OnColorClicked) + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// GBColorDlg message handlers + +void GBColorDlg::OnDefault() +{ + setWhich(0); +} + +void GBColorDlg::OnReset() +{ + int s = which * 8; + colors[s++] = (0x1f) | (0x1f << 5) | (0x1f << 10); + colors[s++] = (0x15) | (0x15 << 5) | (0x15 << 10); + colors[s++] = (0x0c) | (0x0c << 5) | (0x0c << 10); + colors[s++] = 0; + + colors[s++] = (0x1f) | (0x1f << 5) | (0x1f << 10); + colors[s++] = (0x15) | (0x15 << 5) | (0x15 << 10); + colors[s++] = (0x0c) | (0x0c << 5) | (0x0c << 10); + colors[s] = 0; + setWhich(which); +} + +void GBColorDlg::OnUser1() +{ + setWhich(1); +} + +void GBColorDlg::OnUser2() +{ + setWhich(2); +} + +void GBColorDlg::OnCancel() +{ + EndDialog(FALSE); +} + +void GBColorDlg::OnOk() +{ + EndDialog(TRUE); +} + +BOOL GBColorDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + + colorControls[0].SubclassDlgItem(IDC_COLOR_BG0, this); + colorControls[1].SubclassDlgItem(IDC_COLOR_BG1, this); + colorControls[2].SubclassDlgItem(IDC_COLOR_BG2, this); + colorControls[3].SubclassDlgItem(IDC_COLOR_BG3, this); + colorControls[4].SubclassDlgItem(IDC_COLOR_OB0, this); + colorControls[5].SubclassDlgItem(IDC_COLOR_OB1, this); + colorControls[6].SubclassDlgItem(IDC_COLOR_OB2, this); + colorControls[7].SubclassDlgItem(IDC_COLOR_OB3, this); + + for(int i = 0; i < 24; i++) { + colors[i] = systemGbPalette[i]; + } + + const char *names[] = { + "Standard", + "Blue Sea", + "Dark Night", + "Green Forest", + "Hot Desert", + "Pink Dreams", + "Weird Colors", + "Real GB Colors", + "Real 'GB on GBASP' Colors" + }; + + for(int j = 0; j < 9; j++) { + int index = m_predefined.AddString(names[j]); + m_predefined.SetItemData(index, j); + } + + RECT cbSize; + int Height; + + m_predefined.GetClientRect(&cbSize); + Height = m_predefined.GetItemHeight(0); + Height += m_predefined.GetItemHeight(0) * (10); + + // Note: The use of SM_CYEDGE assumes that we're using Windows '95 + // Now add on the height of the border of the edit box + Height += GetSystemMetrics(SM_CYEDGE) * 2; // top & bottom edges + + // The height of the border of the drop-down box + Height += GetSystemMetrics(SM_CYEDGE) * 2; // top & bottom edges + + // now set the size of the window + m_predefined.SetWindowPos(NULL, + 0, 0, + cbSize.right, Height, + SWP_NOMOVE | SWP_NOZORDER); + + + setWhich(which); + + CenterWindow(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void GBColorDlg::setWhich(int w) +{ + which = w; + + for(int i = 0; i < 8; i++) { + colorControls[i].setColor(colors[which*8+i]); + } +} + +u16 * GBColorDlg::getColors() +{ + return colors; +} + +void GBColorDlg::OnColorClicked(UINT id) +{ + id -= IDC_COLOR_BG0; + + u16 color = colors[which*8+id]; + + COLORREF colorInit = + RGB((color & 0x1f) << 3, ((color >> 5) & 0x1f) << 3, ((color >> 10) & 0x1f) << 3); + + CColorDialog dlg(colorInit, + CC_FULLOPEN | CC_ANYCOLOR, this); + + if(IDOK == dlg.DoModal()) + { + COLORREF c = dlg.GetColor(); + + colors[which*8+id] = (u16)((c >> 3) & 0x1f | ((c >> 11) & 0x1f) << 5 | ((c >> 19) & 0x1f) << 10); + + colorControls[id].setColor(colors[which*8+id]); + } +} + +int GBColorDlg::getWhich() +{ + return which; +} + + +void GBColorDlg::OnSelchangePredefined() +{ + int sel = m_predefined.GetCurSel(); + + if(sel != -1) { + DWORD_PTR data = m_predefined.GetItemData(sel); + for(int i = 0; i < 8; i++) { + colorControls[i].setColor(defaultPalettes[data][i]); + colors[which*8+i] = defaultPalettes[data][i]; + } + } +} diff --git a/src/win32/GBColorDlg.h b/src/win32/GBColorDlg.h new file mode 100644 index 0000000..bb9198b --- /dev/null +++ b/src/win32/GBColorDlg.h @@ -0,0 +1,62 @@ +#if !defined(AFX_GBCOLORDLG_H__8D6126EF_06BB_48CF_ABB3_2CC4B1B60358__INCLUDED_) +#define AFX_GBCOLORDLG_H__8D6126EF_06BB_48CF_ABB3_2CC4B1B60358__INCLUDED_ + +#include "ColorButton.h" // Added by ClassView +#include "../System.h" // Added by ClassView +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// GBColorDlg.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// GBColorDlg dialog + +class GBColorDlg : public CDialog +{ + // Construction + public: + int getWhich(); + afx_msg void OnColorClicked(UINT id); + u16 * getColors(); + void setWhich(int w); + u16 colors[24]; + ColorButton colorControls[8]; + GBColorDlg(CWnd* pParent = NULL); // standard constructor + + // Dialog Data + //{{AFX_DATA(GBColorDlg) + enum { IDD = IDD_GB_COLORS }; + CComboBox m_predefined; + int which; + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(GBColorDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + + // Implementation + protected: + + // Generated message map functions + //{{AFX_MSG(GBColorDlg) + afx_msg void OnDefault(); + afx_msg void OnReset(); + afx_msg void OnUser1(); + afx_msg void OnUser2(); + afx_msg void OnCancel(); + afx_msg void OnOk(); + virtual BOOL OnInitDialog(); + afx_msg void OnSelchangePredefined(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + + //{{AFX_INSERT_LOCATION}} + // Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_GBCOLORDLG_H__8D6126EF_06BB_48CF_ABB3_2CC4B1B60358__INCLUDED_) diff --git a/src/win32/GBDisassemble.cpp b/src/win32/GBDisassemble.cpp new file mode 100644 index 0000000..89cb1d4 --- /dev/null +++ b/src/win32/GBDisassemble.cpp @@ -0,0 +1,245 @@ +#include "stdafx.h" +#include "vba.h" +#include "GBDisassemble.h" + +#include "../System.h" +#include "../gb/GB.h" +#include "../gb/gbGlobals.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +extern gbRegister AF; +extern gbRegister BC; +extern gbRegister DE; +extern gbRegister HL; +extern gbRegister SP; +extern gbRegister PC; +extern u8 register_LY; +extern u16 IFF; +extern int gbDis(char *, u16); + +///////////////////////////////////////////////////////////////////////////// +// GBDisassemble dialog + + +GBDisassemble::GBDisassemble(CWnd* pParent /*=NULL*/) + : ResizeDlg(GBDisassemble::IDD, pParent) +{ + //{{AFX_DATA_INIT(GBDisassemble) + m_c = FALSE; + m_h = FALSE; + m_n = FALSE; + m_z = FALSE; + //}}AFX_DATA_INIT + address = 0; + autoUpdate = false; + count = 1; + lastAddress = 0; +} + + +void GBDisassemble::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(GBDisassemble) + DDX_Control(pDX, IDC_ADDRESS, m_address); + DDX_Control(pDX, IDC_DISASSEMBLE, m_list); + DDX_Check(pDX, IDC_C, m_c); + DDX_Check(pDX, IDC_H, m_h); + DDX_Check(pDX, IDC_N, m_n); + DDX_Check(pDX, IDC_Z, m_z); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(GBDisassemble, CDialog) + //{{AFX_MSG_MAP(GBDisassemble) + ON_BN_CLICKED(IDC_CLOSE, OnClose) + ON_BN_CLICKED(IDC_REFRESH, OnRefresh) + ON_BN_CLICKED(IDC_NEXT, OnNext) + ON_BN_CLICKED(IDC_GO, OnGo) + ON_BN_CLICKED(IDC_GOPC, OnGopc) + ON_BN_CLICKED(IDC_AUTO_UPDATE, OnAutoUpdate) + ON_WM_VSCROLL() + //}}AFX_MSG_MAP + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// GBDisassemble message handlers + +void GBDisassemble::OnClose() +{ + theApp.winRemoveUpdateListener(this); + + DestroyWindow(); +} + +void GBDisassemble::OnRefresh() +{ + refresh(); +} + +void GBDisassemble::OnNext() +{ + gbEmulate(1); + if(PC.W < address || PC.W >= lastAddress) + OnGopc(); + refresh(); +} + +void GBDisassemble::OnGo() +{ + CString buffer; + + m_address.GetWindowText(buffer); + sscanf(buffer, "%hx", &address); + refresh(); +} + +void GBDisassemble::OnGopc() +{ + address = PC.W; + + refresh(); +} + +void GBDisassemble::OnAutoUpdate() +{ + autoUpdate = !autoUpdate; + if(autoUpdate) { + theApp.winAddUpdateListener(this); + } else { + theApp.winRemoveUpdateListener(this); + } +} + +BOOL GBDisassemble::OnInitDialog() +{ + CDialog::OnInitDialog(); + + DIALOG_SIZER_START( sz ) + DIALOG_SIZER_ENTRY( IDC_DISASSEMBLE, DS_SizeY) + DIALOG_SIZER_ENTRY( IDC_REFRESH, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_CLOSE, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_NEXT, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_AUTO_UPDATE, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_GOPC, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_VSCROLL, DS_SizeY) + DIALOG_SIZER_END() + SetData(sz, + TRUE, + HKEY_CURRENT_USER, + "Software\\Emulators\\VisualBoyAdvance\\Viewer\\GBDisassembleView", + NULL); + + SCROLLINFO si; + ZeroMemory(&si, sizeof(si)); + si.cbSize = sizeof(si); + si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS; + si.nMin = 0; + si.nMax = 100; + si.nPos = 50; + si.nPage = 0; + GetDlgItem(IDC_VSCROLL)->SetScrollInfo(SB_CTL, &si, TRUE); + CFont *font = CFont::FromHandle((HFONT)GetStockObject(SYSTEM_FIXED_FONT)); + m_list.SetFont(font); + + for(int i = 0; i < 6; i++) + GetDlgItem(IDC_R0+i)->SetFont(font); + + m_address.LimitText(4); + refresh(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void GBDisassemble::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) +{ + char buffer[80]; + + switch(nSBCode) { + case SB_LINEDOWN: + address += gbDis(buffer, address); + break; + case SB_LINEUP: + address--; + break; + case SB_PAGEDOWN: + address = lastAddress; + break; + case SB_PAGEUP: + address -= count; + break; + } + refresh(); + + CDialog::OnVScroll(nSBCode, nPos, pScrollBar); +} + +void GBDisassemble::refresh() +{ + if(gbRom == NULL) + return; + + int h = m_list.GetItemHeight(0); + RECT r; + m_list.GetClientRect(&r); + count = (r.bottom - r.top+1)/h; + + m_list.ResetContent(); + if(!emulating || theApp.cartridgeType != 1) + return; + + char buffer[80]; + u16 addr = address; + int i; + int sel = -1; + for(i = 0; i < count; i++) { + if(addr == PC.W) + sel = i; + addr += gbDis(buffer, addr); + m_list.InsertString(-1, buffer); + } + lastAddress = addr-1; + if(sel != -1) + m_list.SetCurSel(sel); + + sprintf(buffer, "%04x", AF.W); + GetDlgItem(IDC_R0)->SetWindowText(buffer); + sprintf(buffer, "%04x", BC.W); + GetDlgItem(IDC_R1)->SetWindowText(buffer); + sprintf(buffer, "%04x", DE.W); + GetDlgItem(IDC_R2)->SetWindowText(buffer); + sprintf(buffer, "%04x", HL.W); + GetDlgItem(IDC_R3)->SetWindowText(buffer); + sprintf(buffer, "%04x", SP.W); + GetDlgItem(IDC_R4)->SetWindowText(buffer); + sprintf(buffer, "%04x", PC.W); + GetDlgItem(IDC_R5)->SetWindowText(buffer); + sprintf(buffer, "%04x", IFF); + GetDlgItem(IDC_R6)->SetWindowText(buffer); + sprintf(buffer, "%04x", register_LY); + GetDlgItem(IDC_LY)->SetWindowText(buffer); + + m_z = (AF.B.B0 & 0x80) != 0; + m_n = (AF.B.B0 & 0x40) != 0; + m_h = (AF.B.B0 & 0x20) != 0; + m_c = (AF.B.B0 & 0x10) != 0; + UpdateData(FALSE); +} + +void GBDisassemble::update() +{ + OnGopc(); + refresh(); +} + +void GBDisassemble::PostNcDestroy() +{ + delete this; +} diff --git a/src/win32/GBDisassemble.h b/src/win32/GBDisassemble.h new file mode 100644 index 0000000..bc57b0e --- /dev/null +++ b/src/win32/GBDisassemble.h @@ -0,0 +1,70 @@ +#if !defined(AFX_GBDISASSEMBLE_H__3EFD5B47_6DBF_4F63_8F91_A9511EC590EB__INCLUDED_) +#define AFX_GBDISASSEMBLE_H__3EFD5B47_6DBF_4F63_8F91_A9511EC590EB__INCLUDED_ + +#include "../System.h" // Added by ClassView +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// GBDisassemble.h : header file +// + +#include "IUpdate.h" +#include "ResizeDlg.h" + +///////////////////////////////////////////////////////////////////////////// +// GBDisassemble dialog + +class GBDisassemble : public ResizeDlg, IUpdateListener +{ + // Construction + public: + void refresh(); + u16 lastAddress; + int count; + bool autoUpdate; + u16 address; + GBDisassemble(CWnd* pParent = NULL); // standard constructor + + virtual void update(); + + // Dialog Data + //{{AFX_DATA(GBDisassemble) + enum { IDD = IDD_GB_DISASSEMBLE }; + CEdit m_address; + CListBox m_list; + BOOL m_c; + BOOL m_h; + BOOL m_n; + BOOL m_z; + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(GBDisassemble) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual void PostNcDestroy(); + //}}AFX_VIRTUAL + + // Implementation + protected: + + // Generated message map functions + //{{AFX_MSG(GBDisassemble) + afx_msg void OnClose(); + afx_msg void OnRefresh(); + afx_msg void OnNext(); + afx_msg void OnGo(); + afx_msg void OnGopc(); + afx_msg void OnAutoUpdate(); + virtual BOOL OnInitDialog(); + afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + + //{{AFX_INSERT_LOCATION}} + // Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_GBDISASSEMBLE_H__3EFD5B47_6DBF_4F63_8F91_A9511EC590EB__INCLUDED_) diff --git a/src/win32/GBMapView.cpp b/src/win32/GBMapView.cpp new file mode 100644 index 0000000..51dcce9 --- /dev/null +++ b/src/win32/GBMapView.cpp @@ -0,0 +1,556 @@ +#include "stdafx.h" +#include "vba.h" +#include "FileDlg.h" +#include "GBMapView.h" +#include "Reg.h" +#include "WinResUtil.h" + +#include "../System.h" +#include "../NLS.h" +#include "../Util.h" +#include "../gb/gbGlobals.h" + +extern "C" { +#include +} + +extern u8 gbInvertTab[256]; + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// GBMapView dialog + + +GBMapView::GBMapView(CWnd* pParent /*=NULL*/) + : ResizeDlg(GBMapView::IDD, pParent) +{ + //{{AFX_DATA_INIT(GBMapView) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT + autoUpdate = false; + + memset(&bmpInfo.bmiHeader, 0, sizeof(bmpInfo.bmiHeader)); + + bmpInfo.bmiHeader.biSize = sizeof(bmpInfo.bmiHeader); + bmpInfo.bmiHeader.biWidth = 1024; + bmpInfo.bmiHeader.biHeight = -1024; + bmpInfo.bmiHeader.biPlanes = 1; + bmpInfo.bmiHeader.biBitCount = 24; + bmpInfo.bmiHeader.biCompression = BI_RGB; + data = (u8 *)calloc(1, 3 * 1024 * 1024); + + mapView.setData(data); + mapView.setBmpInfo(&bmpInfo); + + bg = 0; + bank = 0; +} + + +void GBMapView::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(GBMapView) + // NOTE: the ClassWizard will add DDX and DDV calls here + //}}AFX_DATA_MAP + DDX_Control(pDX, IDC_MAP_VIEW, mapView); + DDX_Control(pDX, IDC_MAP_VIEW_ZOOM, mapViewZoom); + DDX_Control(pDX, IDC_COLOR, color); +} + + +BEGIN_MESSAGE_MAP(GBMapView, CDialog) + //{{AFX_MSG_MAP(GBMapView) + ON_BN_CLICKED(IDC_SAVE, OnSave) + ON_BN_CLICKED(IDC_REFRESH, OnRefresh) + ON_BN_CLICKED(IDC_BG0, OnBg0) + ON_BN_CLICKED(IDC_BG1, OnBg1) + ON_BN_CLICKED(IDC_BANK_0, OnBank0) + ON_BN_CLICKED(IDC_BANK_1, OnBank1) + ON_BN_CLICKED(IDC_STRETCH, OnStretch) + ON_BN_CLICKED(IDC_AUTO_UPDATE, OnAutoUpdate) + ON_BN_CLICKED(IDC_CLOSE, OnClose) + //}}AFX_MSG_MAP + ON_MESSAGE(WM_MAPINFO, OnMapInfo) + ON_MESSAGE(WM_COLINFO, OnColInfo) + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// GBMapView message handlers + +GBMapView::~GBMapView() +{ + free(data); + data = NULL; +} + +void GBMapView::saveBMP(const char *name) +{ + u8 writeBuffer[1024 * 3]; + + FILE *fp = fopen(name,"wb"); + + if(!fp) { + systemMessage(MSG_ERROR_CREATING_FILE, "Error creating file %s", name); + return; + } + + struct { + u8 ident[2]; + u8 filesize[4]; + u8 reserved[4]; + u8 dataoffset[4]; + u8 headersize[4]; + u8 width[4]; + u8 height[4]; + u8 planes[2]; + u8 bitsperpixel[2]; + u8 compression[4]; + u8 datasize[4]; + u8 hres[4]; + u8 vres[4]; + u8 colors[4]; + u8 importantcolors[4]; + u8 pad[2]; + } bmpheader; + memset(&bmpheader, 0, sizeof(bmpheader)); + + bmpheader.ident[0] = 'B'; + bmpheader.ident[1] = 'M'; + + u32 fsz = sizeof(bmpheader) + w*h*3; + utilPutDword(bmpheader.filesize, fsz); + utilPutDword(bmpheader.dataoffset, 0x38); + utilPutDword(bmpheader.headersize, 0x28); + utilPutDword(bmpheader.width, w); + utilPutDword(bmpheader.height, h); + utilPutDword(bmpheader.planes, 1); + utilPutDword(bmpheader.bitsperpixel, 24); + utilPutDword(bmpheader.datasize, 3*w*h); + + fwrite(&bmpheader, 1, sizeof(bmpheader), fp); + + u8 *b = writeBuffer; + + int sizeX = w; + int sizeY = h; + + u8 *pixU8 = (u8 *)data+3*w*(h-1); + for(int y = 0; y < sizeY; y++) { + for(int x = 0; x < sizeX; x++) { + *b++ = *pixU8++; // B + *b++ = *pixU8++; // G + *b++ = *pixU8++; // R + } + pixU8 -= 2*3*w; + fwrite(writeBuffer, 1, 3*w, fp); + + b = writeBuffer; + } + + fclose(fp); +} + +void GBMapView::savePNG(const char *name) +{ + u8 writeBuffer[1024 * 3]; + + FILE *fp = fopen(name,"wb"); + + if(!fp) { + systemMessage(MSG_ERROR_CREATING_FILE, "Error creating file %s", name); + return; + } + + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, + NULL, + NULL, + NULL); + if(!png_ptr) { + fclose(fp); + return; + } + + png_infop info_ptr = png_create_info_struct(png_ptr); + + if(!info_ptr) { + png_destroy_write_struct(&png_ptr,NULL); + fclose(fp); + return; + } + + if(setjmp(png_ptr->jmpbuf)) { + png_destroy_write_struct(&png_ptr,NULL); + fclose(fp); + return; + } + + png_init_io(png_ptr,fp); + + png_set_IHDR(png_ptr, + info_ptr, + w, + h, + 8, + PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + png_write_info(png_ptr,info_ptr); + + u8 *b = writeBuffer; + + int sizeX = w; + int sizeY = h; + + u8 *pixU8 = (u8 *)data; + for(int y = 0; y < sizeY; y++) { + for(int x = 0; x < sizeX; x++) { + int blue = *pixU8++; + int green = *pixU8++; + int red = *pixU8++; + + *b++ = red; + *b++ = green; + *b++ = blue; + } + png_write_row(png_ptr,writeBuffer); + + b = writeBuffer; + } + + png_write_end(png_ptr, info_ptr); + + png_destroy_write_struct(&png_ptr, &info_ptr); + + fclose(fp); +} + +void GBMapView::OnSave() +{ + CString filename; + + if(theApp.captureFormat == 0) + filename = "map.png"; + else + filename = "map.bmp"; + + LPCTSTR exts[] = {".png", ".bmp" }; + CString title = winResLoadString(IDS_SELECT_CAPTURE_NAME); + CString filter = theApp.winLoadFilter(IDS_FILTER_PNG); + + FileDlg dlg(this, + filename, + filter, + theApp.captureFormat ? 2 : 1, + theApp.captureFormat ? "BMP" : "PNG", + exts, + "", + title, + true); + + if(dlg.DoModal() == IDCANCEL) { + return; + } + + if(dlg.getFilterIndex() == 2) + saveBMP(dlg.GetPathName()); + else + savePNG(dlg.GetPathName()); +} + +void GBMapView::render() +{ + u8 * bank0; + u8 * bank1; + if(gbCgbMode) { + bank0 = &gbVram[0x0000]; + bank1 = &gbVram[0x2000]; + } else { + bank0 = &gbMemory[0x8000]; + bank1 = NULL; + } + + int tile_map_address = 0x1800; + if(bg == 1) + tile_map_address = 0x1c00; + + int tile_pattern = 0x0000; + if(bank == 1) + tile_pattern = 0x0800; + + w = 256; + h = 256; + + int tile = 0; + for(int y = 0; y < 32; y++) { + for(int x = 0; x < 32; x++) { + u8 *bmp = &data[y * 8 * 32 * 24 + x*24]; + u8 attrs = 0; + if(bank1 != NULL) + attrs = bank1[tile_map_address]; + u8 tile = bank0[tile_map_address]; + tile_map_address++; + + if(bank == 1) { + if(tile < 128) tile += 128; + else tile -= 128; + } + for(int j = 0; j < 8; j++) { + int tile_pattern_address = attrs & 0x40 ? + tile_pattern + tile*16 + (7-j)*2: + tile_pattern + tile*16+j*2; + + u8 tile_a = 0; + u8 tile_b = 0; + + if(attrs & 0x08) { + tile_a = bank1[tile_pattern_address++]; + tile_b = bank1[tile_pattern_address]; + } else { + tile_a = bank0[tile_pattern_address++]; + tile_b = bank0[tile_pattern_address]; + } + + if(attrs & 0x20) { + tile_a = gbInvertTab[tile_a]; + tile_b = gbInvertTab[tile_b]; + } + + u8 mask = 0x80; + + while(mask > 0) { + u8 c = (tile_a & mask) ? 1 : 0; + c += (tile_b & mask) ? 2 : 0; + + if(gbCgbMode) + c = c + (attrs & 7)*4; + + u16 color = gbPalette[c]; + + *bmp++ = ((color >> 10) & 0x1f) << 3; + *bmp++ = ((color >> 5) & 0x1f) << 3; + *bmp++ = (color & 0x1f) << 3; + + mask >>= 1; + } + bmp += 31*24; + } + } + } +} + +void GBMapView::paint() +{ + if(gbRom == NULL) + return; + render(); + + SIZE s; + if(mapView.getStretch()) { + mapView.setSize(w, h); + s.cx = s.cy = 1; + mapView.SetScrollSizes(MM_TEXT, s); + } else { + mapView.setSize(w, h); + s.cx = w; + s.cy = h; + mapView.SetScrollSizes(MM_TEXT, s); + } + + mapView.refresh(); +} + +void GBMapView::OnRefresh() +{ + paint(); +} + +void GBMapView::update() +{ + paint(); +} + +BOOL GBMapView::OnInitDialog() +{ + CDialog::OnInitDialog(); + + DIALOG_SIZER_START( sz ) + DIALOG_SIZER_ENTRY( IDC_MAP_VIEW, DS_SizeX | DS_SizeY ) + DIALOG_SIZER_ENTRY( IDC_REFRESH, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_CLOSE, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_SAVE, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_COLOR, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_R, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_G, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_B, DS_MoveY) + DIALOG_SIZER_END() + SetData(sz, + TRUE, + HKEY_CURRENT_USER, + "Software\\Emulators\\VisualBoyAdvance\\Viewer\\GBMapView", + NULL); + + int s = regQueryDwordValue("mapViewStretch", 0); + if(s) + mapView.setStretch(true); + ((CButton *)GetDlgItem(IDC_STRETCH))->SetCheck(s); + + UINT id = IDC_BANK_0; + if(bank == 1) + id = IDC_BANK_1; + CheckRadioButton(IDC_BANK_0, IDC_BANK_1, id); + id = IDC_BG0; + if(bg == 1) + id = IDC_BG1; + CheckRadioButton(IDC_BG0, IDC_BG1, id); + paint(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void GBMapView::OnBg0() +{ + bg = 0; + paint(); +} + +void GBMapView::OnBg1() +{ + bg = 1; + paint(); +} + +void GBMapView::OnBank0() +{ + bank = 0; + paint(); +} + +void GBMapView::OnBank1() +{ + bank = 1; + paint(); +} + +void GBMapView::OnStretch() +{ + mapView.setStretch(!mapView.getStretch()); + paint(); + regSetDwordValue("mapViewStretch", mapView.getStretch()); +} + +void GBMapView::OnAutoUpdate() +{ + autoUpdate = !autoUpdate; + if(autoUpdate) { + theApp.winAddUpdateListener(this); + } else { + theApp.winRemoveUpdateListener(this); + } +} + +void GBMapView::OnClose() +{ + theApp.winRemoveUpdateListener(this); + + DestroyWindow(); +} + +u32 GBMapView::GetClickAddress(int x, int y) +{ + u32 base = 0x9800; + if(bg == 1) + base = 0x9c00; + + return base + (y >> 3)*32 + (x >> 3); +} + +LRESULT GBMapView::OnMapInfo(WPARAM wParam, LPARAM lParam) +{ + u8 *colors = (u8 *)lParam; + mapViewZoom.setColors(colors); + + int x = (int)(wParam & 0xffff); + int y = (int)(wParam >> 16); + + CString buffer; + buffer.Format("(%d,%d)", x, y); + GetDlgItem(IDC_XY)->SetWindowText(buffer); + + u32 address = GetClickAddress(x,y); + buffer.Format("0x%08X", address); + GetDlgItem(IDC_ADDRESS)->SetWindowText(buffer); + + u8 attrs = 0; + + u8 tile = gbMemoryMap[9][address & 0xfff]; + if(gbCgbMode) { + attrs = gbVram[0x2000 + address - 0x8000]; + tile = gbVram[address & 0x1fff]; + } + + if(bank == 1) { + if(tile > 128) tile -= 128; + else tile += 128; + } + + buffer.Format("%d", tile); + GetDlgItem(IDC_TILE_NUM)->SetWindowText(buffer); + + buffer.Empty(); + buffer += attrs & 0x20 ? 'H' : '-'; + buffer += attrs & 0x40 ? 'V' : '-'; + GetDlgItem(IDC_FLIP)->SetWindowText(buffer); + + if(gbCgbMode) { + buffer.Format("%d", (attrs & 7)); + } else + buffer = "---"; + GetDlgItem(IDC_PALETTE_NUM)->SetWindowText(buffer); + + buffer.Empty(); + if(gbCgbMode) + buffer += attrs & 0x80 ? 'P' : '-'; + else + buffer += '-'; + GetDlgItem(IDC_PRIORITY)->SetWindowText(buffer); + + return TRUE; +} + +LRESULT GBMapView::OnColInfo(WPARAM wParam, LPARAM) +{ + u16 c = (u16)wParam; + + color.setColor(c); + + int r = (c & 0x1f); + int g = (c & 0x3e0) >> 5; + int b = (c & 0x7c00) >> 10; + + CString buffer; + buffer.Format("R: %d", r); + GetDlgItem(IDC_R)->SetWindowText(buffer); + + buffer.Format("G: %d", g); + GetDlgItem(IDC_G)->SetWindowText(buffer); + + buffer.Format("B: %d", b); + GetDlgItem(IDC_B)->SetWindowText(buffer); + + return TRUE; +} + +void GBMapView::PostNcDestroy() +{ + delete this; + CDialog::PostNcDestroy(); +} diff --git a/src/win32/GBMapView.h b/src/win32/GBMapView.h new file mode 100644 index 0000000..b91c18f --- /dev/null +++ b/src/win32/GBMapView.h @@ -0,0 +1,83 @@ +#if !defined(AFX_GBMAPVIEW_H__4CD23D38_F2CD_4B95_AE76_2781591DD077__INCLUDED_) +#define AFX_GBMAPVIEW_H__4CD23D38_F2CD_4B95_AE76_2781591DD077__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// GBMapView.h : header file +// + +#include "BitmapControl.h" +#include "ColorControl.h" +#include "ZoomControl.h" +#include "ResizeDlg.h" +#include "IUpdate.h" +#include "../System.h" // Added by ClassView + +///////////////////////////////////////////////////////////////////////////// +// GBMapView dialog + +class GBMapView : public ResizeDlg, IUpdateListener +{ + private: + BITMAPINFO bmpInfo; + u8 *data; + int bank; + int bg; + int w; + int h; + BitmapControl mapView; + ZoomControl mapViewZoom; + ColorControl color; + bool autoUpdate; + // Construction + public: + afx_msg LRESULT OnColInfo(WPARAM wParam, LPARAM lParam); + afx_msg LRESULT OnMapInfo(WPARAM wParam, LPARAM lParam); + u32 GetClickAddress(int x, int y); + void update(); + void paint(); + void render(); + void savePNG(const char *name); + void saveBMP(const char *name); + ~GBMapView(); + GBMapView(CWnd* pParent = NULL); // standard constructor + + // Dialog Data + //{{AFX_DATA(GBMapView) + enum { IDD = IDD_GB_MAP_VIEW }; + // NOTE: the ClassWizard will add data members here + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(GBMapView) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual void PostNcDestroy(); + //}}AFX_VIRTUAL + + // Implementation + protected: + + // Generated message map functions + //{{AFX_MSG(GBMapView) + afx_msg void OnSave(); + afx_msg void OnRefresh(); + virtual BOOL OnInitDialog(); + afx_msg void OnBg0(); + afx_msg void OnBg1(); + afx_msg void OnBank0(); + afx_msg void OnBank1(); + afx_msg void OnStretch(); + afx_msg void OnAutoUpdate(); + afx_msg void OnClose(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + + //{{AFX_INSERT_LOCATION}} + // Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_GBMAPVIEW_H__4CD23D38_F2CD_4B95_AE76_2781591DD077__INCLUDED_) diff --git a/src/win32/GBMemoryViewerDlg.cpp b/src/win32/GBMemoryViewerDlg.cpp new file mode 100644 index 0000000..f796c19 --- /dev/null +++ b/src/win32/GBMemoryViewerDlg.cpp @@ -0,0 +1,400 @@ +#include "stdafx.h" +#include "vba.h" +#include "FileDlg.h" +#include "GBMemoryViewerDlg.h" +#include "MemoryViewerAddressSize.h" +#include "Reg.h" +#include "WinResUtil.h" + +#include "../System.h" +#include "../gb/gbGlobals.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +GBMemoryViewer::GBMemoryViewer() + : MemoryViewer() +{ + setAddressSize(1); +} + +void GBMemoryViewer::readData(u32 address, int len, u8 *data) +{ + u16 addr = address & 0xffff; + if(emulating && gbRom != NULL) { + for(int i = 0; i < len; i++) { + *data++ = gbMemoryMap[addr >> 12][addr & 0xfff]; + addr++; + } + } else { + for(int i = 0; i < len; i++) { + *data++ = 0; + addr++; + } + } +} + +#define GB_READBYTE_QUICK(addr) \ + gbMemoryMap[(addr) >> 12][(addr) & 0xfff] + +#define GB_WRITEBYTE_QUICK(addr,v) \ + gbMemoryMap[(addr) >> 12][(addr) & 0xfff] = (v) + +void GBMemoryViewer::editData(u32 address, int size, int mask, u32 value) +{ + u32 oldValue; + u16 addr = (u16)address & 0xffff; + switch(size) { + case 8: + oldValue = GB_READBYTE_QUICK(addr); + oldValue &= mask; + oldValue |= (u8)value; + GB_WRITEBYTE_QUICK(addr, oldValue); + break; + case 16: + oldValue = GB_READBYTE_QUICK(addr) | + (GB_READBYTE_QUICK(addr + 1) << 8); + oldValue &= mask; + oldValue |= (u16)value; + GB_WRITEBYTE_QUICK(addr, (oldValue & 255)); + GB_WRITEBYTE_QUICK(addr+1, (oldValue >> 8)); + break; + case 32: + oldValue = GB_READBYTE_QUICK(addr) | + (GB_READBYTE_QUICK(addr + 1) << 8) | + (GB_READBYTE_QUICK(addr + 2) << 16) | + (GB_READBYTE_QUICK(addr + 3) << 24); + oldValue &= mask; + oldValue |= (u32)value; + GB_WRITEBYTE_QUICK(addr, (oldValue & 255)); + GB_WRITEBYTE_QUICK(addr+1, (oldValue >> 8)); + GB_WRITEBYTE_QUICK(addr+2, (oldValue >> 16)); + GB_WRITEBYTE_QUICK(addr+3, (oldValue >> 24)); + break; + } +} + +///////////////////////////////////////////////////////////////////////////// +// GBMemoryViewerDlg dialog + + +GBMemoryViewerDlg::GBMemoryViewerDlg(CWnd* pParent /*=NULL*/) + : ResizeDlg(GBMemoryViewerDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(GBMemoryViewerDlg) + m_size = -1; + //}}AFX_DATA_INIT + autoUpdate = false; +} + + +void GBMemoryViewerDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(GBMemoryViewerDlg) + DDX_Control(pDX, IDC_CURRENT_ADDRESS, m_current); + DDX_Control(pDX, IDC_ADDRESS, m_address); + DDX_Control(pDX, IDC_ADDRESSES, m_addresses); + DDX_Radio(pDX, IDC_8_BIT, m_size); + //}}AFX_DATA_MAP + DDX_Control(pDX, IDC_VIEWER, m_viewer); +} + + +BEGIN_MESSAGE_MAP(GBMemoryViewerDlg, CDialog) + //{{AFX_MSG_MAP(GBMemoryViewerDlg) + ON_BN_CLICKED(IDC_CLOSE, OnClose) + ON_BN_CLICKED(IDC_REFRESH, OnRefresh) + ON_BN_CLICKED(IDC_8_BIT, On8Bit) + ON_BN_CLICKED(IDC_16_BIT, On16Bit) + ON_BN_CLICKED(IDC_32_BIT, On32Bit) + ON_BN_CLICKED(IDC_AUTO_UPDATE, OnAutoUpdate) + ON_BN_CLICKED(IDC_GO, OnGo) + ON_CBN_SELCHANGE(IDC_ADDRESSES, OnSelchangeAddresses) + ON_BN_CLICKED(IDC_SAVE, OnSave) + ON_BN_CLICKED(IDC_LOAD, OnLoad) + //}}AFX_MSG_MAP + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// GBMemoryViewerDlg message handlers + +BOOL GBMemoryViewerDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + + DIALOG_SIZER_START( sz ) + DIALOG_SIZER_ENTRY( IDC_VIEWER, DS_SizeX | DS_SizeY ) + DIALOG_SIZER_ENTRY( IDC_REFRESH, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_CLOSE, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_LOAD, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_SAVE, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_AUTO_UPDATE, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_CURRENT_ADDRESS_LABEL, DS_MoveY | DS_MoveX) + DIALOG_SIZER_ENTRY( IDC_CURRENT_ADDRESS, DS_MoveY | DS_MoveX) + DIALOG_SIZER_END() + SetData(sz, + TRUE, + HKEY_CURRENT_USER, + "Software\\Emulators\\VisualBoyAdvance\\Viewer\\GBMemoryView", + NULL); + + m_viewer.setDialog(this); + m_viewer.ShowScrollBar(SB_VERT, TRUE); + m_viewer.EnableScrollBar(SB_VERT, ESB_ENABLE_BOTH); + + LPCTSTR s[] = { + "0x0000 - ROM", + "0x4000 - ROM", + "0x8000 - VRAM", + "0xA000 - SRAM", + "0xC000 - RAM", + "0xD000 - WRAM", + "0xFF00 - I/O", + "0xFF80 - RAM" + }; + + for(int i = 0; i < 8; i++) + m_addresses.AddString(s[i]); + + m_addresses.SetCurSel(0); + + RECT cbSize; + int Height; + + m_addresses.GetClientRect(&cbSize); + Height = m_addresses.GetItemHeight(-1); + Height += m_addresses.GetItemHeight(0) * (9); + + // Note: The use of SM_CYEDGE assumes that we're using Windows '95 + // Now add on the height of the border of the edit box + Height += GetSystemMetrics(SM_CYEDGE) * 2; // top & bottom edges + + // The height of the border of the drop-down box + Height += GetSystemMetrics(SM_CYEDGE) * 2; // top & bottom edges + + // now set the size of the window + m_addresses.SetWindowPos(NULL, + 0, 0, + cbSize.right, Height, + SWP_NOMOVE | SWP_NOZORDER); + + m_address.LimitText(8); + + m_size = regQueryDwordValue("memViewerDataSize", 0); + if(m_size < 0 || m_size > 2) + m_size = 0; + m_viewer.setSize(m_size); + UpdateData(FALSE); + + m_current.SetFont(CFont::FromHandle((HFONT)GetStockObject(SYSTEM_FIXED_FONT))); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void GBMemoryViewerDlg::OnClose() +{ + theApp.winRemoveUpdateListener(this); + + DestroyWindow(); +} + +void GBMemoryViewerDlg::OnRefresh() +{ + m_viewer.Invalidate(); +} + +void GBMemoryViewerDlg::update() +{ + OnRefresh(); +} + +void GBMemoryViewerDlg::On8Bit() +{ + m_viewer.setSize(0); + regSetDwordValue("memViewerDataSize", 0); +} + +void GBMemoryViewerDlg::On16Bit() +{ + m_viewer.setSize(1); + regSetDwordValue("memViewerDataSize", 1); +} + +void GBMemoryViewerDlg::On32Bit() +{ + m_viewer.setSize(2); + regSetDwordValue("memViewerDataSize", 2); +} + +void GBMemoryViewerDlg::OnAutoUpdate() +{ + autoUpdate = !autoUpdate; + if(autoUpdate) { + theApp.winAddUpdateListener(this); + } else { + theApp.winRemoveUpdateListener(this); + } +} + +void GBMemoryViewerDlg::OnGo() +{ + CString buffer; + + m_address.GetWindowText(buffer); + + u32 address; + sscanf(buffer, "%x", &address); + if(m_viewer.getSize() == 1) + address &= ~1; + else if(m_viewer.getSize() == 2) + address &= ~3; + m_viewer.setAddress(address); +} + +void GBMemoryViewerDlg::OnSelchangeAddresses() +{ + int cur = m_addresses.GetCurSel(); + + switch(cur) { + case 0: + m_viewer.setAddress(0x0000); + break; + case 1: + m_viewer.setAddress(0x4000); + break; + case 2: + m_viewer.setAddress(0x8000); + break; + case 3: + m_viewer.setAddress(0xa000); + break; + case 4: + m_viewer.setAddress(0xc000); + break; + case 5: + m_viewer.setAddress(0xd000); + break; + case 6: + m_viewer.setAddress(0xff00); + break; + case 7: + m_viewer.setAddress(0xff80); + break; + } +} + +void GBMemoryViewerDlg::setCurrentAddress(u32 address) +{ + CString buffer; + + buffer.Format("0x%08X", address); + m_current.SetWindowText(buffer); +} + +void GBMemoryViewerDlg::OnSave() +{ + MemoryViewerAddressSize dlg; + CString buffer; + + dlg.setAddress(m_viewer.getCurrentAddress()); + + LPCTSTR exts[] = { ".dmp" }; + + CString filter = theApp.winLoadFilter(IDS_FILTER_DUMP); + CString title = winResLoadString(IDS_SELECT_DUMP_FILE); + + if(dlg.DoModal() == IDOK) { + FileDlg file(this, + buffer, + filter, + 0, + "DMP", + exts, + "", + title, + true); + if(file.DoModal() == IDOK) { + buffer = file.GetPathName(); + FILE *f = fopen(buffer, "wb"); + + if(f == NULL) { + systemMessage(IDS_ERROR_CREATING_FILE, buffer); + return; + } + + int size = dlg.getSize(); + u16 addr = dlg.getAddress() & 0xffff; + + for(int i = 0; i < size; i++) { + fputc(gbMemoryMap[addr >> 12][addr & 0xfff], f); + addr++; + } + + fclose(f); + } + } +} + +void GBMemoryViewerDlg::OnLoad() +{ + CString buffer; + LPCTSTR exts[] = { ".dmp" }; + CString filter = theApp.winLoadFilter(IDS_FILTER_DUMP); + CString title = winResLoadString(IDS_SELECT_DUMP_FILE); + + FileDlg file(this, + buffer, + filter, + 0, + "DMP", + exts, + "", + title, + false); + + if(file.DoModal() == IDOK) { + buffer = file.GetPathName(); + FILE *f = fopen(buffer, "rb"); + if(f == NULL) { + systemMessage(IDS_CANNOT_OPEN_FILE, + "Cannot open file %s", + buffer); + return; + } + + MemoryViewerAddressSize dlg; + + fseek(f, 0, SEEK_END); + int size = ftell(f); + + fseek(f, 0, SEEK_SET); + + dlg.setAddress(m_viewer.getCurrentAddress()); + dlg.setSize(size); + + if(dlg.DoModal() == IDOK) { + int size = dlg.getSize(); + u16 addr = dlg.getAddress() & 0xffff; + + for(int i = 0; i < size; i++) { + int c = fgetc(f); + if(c == -1) + break; + gbMemoryMap[addr >> 12][addr & 0xfff] = c; + addr++; + } + OnRefresh(); + } + fclose(f); + } +} + +void GBMemoryViewerDlg::PostNcDestroy() +{ + delete this; +} diff --git a/src/win32/GBMemoryViewerDlg.h b/src/win32/GBMemoryViewerDlg.h new file mode 100644 index 0000000..f0f8e67 --- /dev/null +++ b/src/win32/GBMemoryViewerDlg.h @@ -0,0 +1,75 @@ +#if !defined(AFX_GBMEMORYVIEWERDLG_H__23AD2804_EFA5_4900_AEC5_47196A41C50D__INCLUDED_) +#define AFX_GBMEMORYVIEWERDLG_H__23AD2804_EFA5_4900_AEC5_47196A41C50D__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// GBMemoryViewerDlg.h : header file +// +#include "MemoryViewer.h" +#include "ResizeDlg.h" +#include "IUpdate.h" + +class GBMemoryViewer : public MemoryViewer { + public: + GBMemoryViewer(); + virtual void readData(u32, int, u8 *); + virtual void editData(u32, int, int, u32); +}; + +///////////////////////////////////////////////////////////////////////////// +// GBMemoryViewerDlg dialog + +class GBMemoryViewerDlg : public ResizeDlg, IUpdateListener, IMemoryViewerDlg +{ + GBMemoryViewer m_viewer; + bool autoUpdate; + // Construction + public: + void setCurrentAddress(u32 address); + GBMemoryViewerDlg(CWnd* pParent = NULL); // standard constructor + + virtual void update(); + + // Dialog Data + //{{AFX_DATA(GBMemoryViewerDlg) + enum { IDD = IDD_MEM_VIEWER }; + CEdit m_current; + CEdit m_address; + CComboBox m_addresses; + int m_size; + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(GBMemoryViewerDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual void PostNcDestroy(); + //}}AFX_VIRTUAL + + // Implementation + protected: + + // Generated message map functions + //{{AFX_MSG(GBMemoryViewerDlg) + virtual BOOL OnInitDialog(); + afx_msg void OnClose(); + afx_msg void OnRefresh(); + afx_msg void On8Bit(); + afx_msg void On16Bit(); + afx_msg void On32Bit(); + afx_msg void OnAutoUpdate(); + afx_msg void OnGo(); + afx_msg void OnSelchangeAddresses(); + afx_msg void OnSave(); + afx_msg void OnLoad(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + + //{{AFX_INSERT_LOCATION}} + // Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_GBMEMORYVIEWERDLG_H__23AD2804_EFA5_4900_AEC5_47196A41C50D__INCLUDED_) diff --git a/src/win32/GBOamView.cpp b/src/win32/GBOamView.cpp new file mode 100644 index 0000000..d2b07af --- /dev/null +++ b/src/win32/GBOamView.cpp @@ -0,0 +1,573 @@ +#include "stdafx.h" +#include "vba.h" +#include "FileDlg.h" +#include "GBOamView.h" +#include "Reg.h" +#include "WinResUtil.h" + +#include "../System.h" +#include "../NLS.h" +#include "../Util.h" +#include "../gb/gbGlobals.h" + +extern "C" { +#include +} + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// GBOamView dialog + + +GBOamView::GBOamView(CWnd* pParent /*=NULL*/) + : ResizeDlg(GBOamView::IDD, pParent) +{ + //{{AFX_DATA_INIT(GBOamView) + m_stretch = FALSE; + //}}AFX_DATA_INIT + autoUpdate = false; + + memset(&bmpInfo.bmiHeader, 0, sizeof(bmpInfo.bmiHeader)); + + bmpInfo.bmiHeader.biSize = sizeof(bmpInfo.bmiHeader); + bmpInfo.bmiHeader.biWidth = 8; + bmpInfo.bmiHeader.biHeight = 16; + bmpInfo.bmiHeader.biPlanes = 1; + bmpInfo.bmiHeader.biBitCount = 24; + bmpInfo.bmiHeader.biCompression = BI_RGB; + data = (u8 *)calloc(1, 3 * 8 * 16); + + oamView.setData(data); + oamView.setBmpInfo(&bmpInfo); + + number = 0; +} + + +void GBOamView::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(GBOamView) + DDX_Control(pDX, IDC_SPRITE, m_sprite); + DDX_Check(pDX, IDC_STRETCH, m_stretch); + //}}AFX_DATA_MAP + DDX_Control(pDX, IDC_COLOR, color); + DDX_Control(pDX, IDC_OAM_VIEW, oamView); + DDX_Control(pDX, IDC_OAM_VIEW_ZOOM, oamZoom); +} + + +BEGIN_MESSAGE_MAP(GBOamView, CDialog) + //{{AFX_MSG_MAP(GBOamView) + ON_BN_CLICKED(IDC_STRETCH, OnStretch) + ON_BN_CLICKED(IDC_AUTO_UPDATE, OnAutoUpdate) + ON_EN_CHANGE(IDC_SPRITE, OnChangeSprite) + ON_BN_CLICKED(IDC_CLOSE, OnClose) + ON_WM_HSCROLL() + //}}AFX_MSG_MAP + ON_MESSAGE(WM_MAPINFO, OnMapInfo) + ON_MESSAGE(WM_COLINFO, OnColInfo) + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// GBOamView message handlers + +GBOamView::~GBOamView() +{ + free(data); + data = NULL; +} + + +void GBOamView::paint() +{ + if(gbRom == NULL) + return; + + render(); + oamView.setSize(w,h); + oamView.refresh(); +} + +void GBOamView::update() +{ + paint(); +} + + +void GBOamView::setAttributes(int y, int x, int tile, int flags) +{ + CString buffer; + + int flipH = flags & 0x20; + int flipV = flags & 0x40; + int prio = (flags & 0x80) >> 7; + int pal = flags & 0x7; + int oap = (flags & 0x08) >> 3; + int bank = (flags & 0x10) >> 4; + + buffer.Format("%d,%d", x,y); + GetDlgItem(IDC_POS)->SetWindowText(buffer); + + buffer.Format("%d", pal); + GetDlgItem(IDC_PALETTE)->SetWindowText(buffer); + + buffer.Format("%d", tile); + GetDlgItem(IDC_TILE)->SetWindowText(buffer); + + buffer.Format("%d", prio); + GetDlgItem(IDC_PRIO)->SetWindowText(buffer); + + buffer.Format("%d", bank); + GetDlgItem(IDC_BANK)->SetWindowText(buffer); + + buffer.Empty(); + if(flipH) + buffer += 'H'; + else + buffer += ' '; + if(flipV) + buffer += 'V'; + else + buffer += ' '; + GetDlgItem(IDC_FLAGS)->SetWindowText(buffer); + + buffer.Format("%d", oap); + GetDlgItem(IDC_OAP)->SetWindowText(buffer); +} + +void GBOamView::render() +{ + int m=0; + if(gbRom == NULL) + return; + + u16 addr = number * 4 + 0xfe00; + + int size = register_LCDC & 4; + + u8 y = gbMemory[addr++]; + u8 x = gbMemory[addr++]; + u8 tile = gbMemory[addr++]; + if(size) + tile &= 254; + u8 flags = gbMemory[addr++]; + + u8 *bmp = data; + + w = 8; + h = size ? 16 : 8; + + setAttributes(y, x, tile, flags); + + u8 * bank0; + u8 * bank1; + if(gbCgbMode) { + bank0 = &gbVram[0x0000]; + bank1 = &gbVram[0x2000]; + } else { + bank0 = &gbMemory[0x8000]; + bank1 = NULL; + } + + int init = 0x0000; + + u8 *pal = gbObp0; + + if((flags & 0x10)) + pal = gbObp1; + + for(int yy = 0; yy < h; yy++) { + int address = init + tile * 16 + 2*yy; + int a = 0; + int b = 0; + + if(gbCgbMode && flags & 0x08) { + a = bank1[address++]; + b = bank1[address++]; + } else { + a = bank0[address++]; + b = bank0[address++]; + } + + for(int xx = 0; xx < 8; xx++) { + u8 mask = 1 << (7-xx); + u8 c = 0; + if( (a & mask)) + c++; + if( (b & mask)) + c+=2; + + // make sure that sprites will work even in CGB mode + if(gbCgbMode) { + c = c + (flags & 0x07)*4 + 32; + } else { + c = pal[c]; + } + + u16 color = gbPalette[c]; + *bmp++ = ((color >> 10) & 0x1f) << 3; + *bmp++ = ((color >> 5) & 0x1f) << 3; + *bmp++ = (color & 0x1f) << 3; + } + } +} + +void GBOamView::saveBMP(const char *name) +{ + u8 writeBuffer[1024 * 3]; + + FILE *fp = fopen(name,"wb"); + + if(!fp) { + systemMessage(MSG_ERROR_CREATING_FILE, "Error creating file %s", name); + return; + } + + struct { + u8 ident[2]; + u8 filesize[4]; + u8 reserved[4]; + u8 dataoffset[4]; + u8 headersize[4]; + u8 width[4]; + u8 height[4]; + u8 planes[2]; + u8 bitsperpixel[2]; + u8 compression[4]; + u8 datasize[4]; + u8 hres[4]; + u8 vres[4]; + u8 colors[4]; + u8 importantcolors[4]; + u8 pad[2]; + } bmpheader; + memset(&bmpheader, 0, sizeof(bmpheader)); + + bmpheader.ident[0] = 'B'; + bmpheader.ident[1] = 'M'; + + u32 fsz = sizeof(bmpheader) + w*h*3; + utilPutDword(bmpheader.filesize, fsz); + utilPutDword(bmpheader.dataoffset, 0x38); + utilPutDword(bmpheader.headersize, 0x28); + utilPutDword(bmpheader.width, w); + utilPutDword(bmpheader.height, h); + utilPutDword(bmpheader.planes, 1); + utilPutDword(bmpheader.bitsperpixel, 24); + utilPutDword(bmpheader.datasize, 3*w*h); + + fwrite(&bmpheader, 1, sizeof(bmpheader), fp); + + u8 *b = writeBuffer; + + int sizeX = w; + int sizeY = h; + + u8 *pixU8 = (u8 *)data+3*w*(h-1); + for(int y = 0; y < sizeY; y++) { + for(int x = 0; x < sizeX; x++) { + *b++ = *pixU8++; // B + *b++ = *pixU8++; // G + *b++ = *pixU8++; // R + } + pixU8 -= 2*3*w; + fwrite(writeBuffer, 1, 3*w, fp); + + b = writeBuffer; + } + + fclose(fp); +} + + +void GBOamView::savePNG(const char *name) +{ + u8 writeBuffer[1024 * 3]; + + FILE *fp = fopen(name,"wb"); + + if(!fp) { + systemMessage(MSG_ERROR_CREATING_FILE, "Error creating file %s", name); + return; + } + + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, + NULL, + NULL, + NULL); + if(!png_ptr) { + fclose(fp); + return; + } + + png_infop info_ptr = png_create_info_struct(png_ptr); + + if(!info_ptr) { + png_destroy_write_struct(&png_ptr,NULL); + fclose(fp); + return; + } + + if(setjmp(png_ptr->jmpbuf)) { + png_destroy_write_struct(&png_ptr,NULL); + fclose(fp); + return; + } + + png_init_io(png_ptr,fp); + + png_set_IHDR(png_ptr, + info_ptr, + w, + h, + 8, + PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + png_write_info(png_ptr,info_ptr); + + u8 *b = writeBuffer; + + int sizeX = w; + int sizeY = h; + + u8 *pixU8 = (u8 *)data; + for(int y = 0; y < sizeY; y++) { + for(int x = 0; x < sizeX; x++) { + int blue = *pixU8++; + int green = *pixU8++; + int red = *pixU8++; + + *b++ = red; + *b++ = green; + *b++ = blue; + } + png_write_row(png_ptr,writeBuffer); + + b = writeBuffer; + } + + png_write_end(png_ptr, info_ptr); + + png_destroy_write_struct(&png_ptr, &info_ptr); + + fclose(fp); +} + + +void GBOamView::save() +{ + CString captureBuffer; + + if(theApp.captureFormat == 0) + captureBuffer = "oam.png"; + else + captureBuffer = "oam.bmp"; + + LPCTSTR exts[] = {".png", ".bmp" }; + + CString filter = theApp.winLoadFilter(IDS_FILTER_PNG); + CString title = winResLoadString(IDS_SELECT_CAPTURE_NAME); + + FileDlg dlg(this, + captureBuffer, + filter, + theApp.captureFormat ? 2 : 1, + theApp.captureFormat ? "BMP" : "PNG", + exts, + "", + title, + true); + + if(dlg.DoModal() == IDCANCEL) { + return; + } + captureBuffer = dlg.GetPathName(); + + if(dlg.getFilterIndex() == 2) + saveBMP(captureBuffer); + else + savePNG(captureBuffer); +} + +BOOL GBOamView::OnInitDialog() +{ + CDialog::OnInitDialog(); + + DIALOG_SIZER_START( sz ) + DIALOG_SIZER_ENTRY( IDC_OAM_VIEW, DS_SizeX | DS_SizeY ) + DIALOG_SIZER_ENTRY( IDC_OAM_VIEW_ZOOM, DS_MoveX) + DIALOG_SIZER_ENTRY( IDC_REFRESH, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_SAVE, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_CLOSE, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_COLOR, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_R, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_G, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_B, DS_MoveY) + DIALOG_SIZER_END() + SetData(sz, + TRUE, + HKEY_CURRENT_USER, + "Software\\Emulators\\VisualBoyAdvance\\Viewer\\GBOamView", + NULL); + m_sprite.SetWindowText("0"); + + updateScrollInfo(); + + m_stretch = regQueryDwordValue("GBOamViewStretch", 0); + if(m_stretch) + oamView.setStretch(true); + UpdateData(FALSE); + + paint(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void GBOamView::OnStretch() +{ + oamView.setStretch(!oamView.getStretch()); + paint(); + regSetDwordValue("GBOamViewStretch", oamView.getStretch()); +} + +void GBOamView::OnAutoUpdate() +{ + autoUpdate = !autoUpdate; + if(autoUpdate) { + theApp.winAddUpdateListener(this); + } else { + theApp.winRemoveUpdateListener(this); + } +} + + +void GBOamView::OnChangeSprite() +{ + CString buffer; + m_sprite.GetWindowText(buffer); + int n = atoi(buffer); + if(n < 0 || n > 39) { + buffer.Format("%d", number); + m_sprite.SetWindowText(buffer); + return; + } + number = n; + paint(); + updateScrollInfo(); +} + +void GBOamView::OnClose() +{ + theApp.winRemoveUpdateListener(this); + + DestroyWindow(); +} + +LRESULT GBOamView::OnMapInfo(WPARAM, LPARAM lParam) +{ + u8 *colors = (u8 *)lParam; + oamZoom.setColors(colors); + + return TRUE; +} + +LRESULT GBOamView::OnColInfo(WPARAM wParam, LPARAM lParam) +{ + u16 c = (u16)wParam; + + color.setColor(c); + + int r = (c & 0x1f); + int g = (c & 0x3e0) >> 5; + int b = (c & 0x7c00) >> 10; + + CString buffer; + buffer.Format("R: %d", r); + GetDlgItem(IDC_R)->SetWindowText(buffer); + + buffer.Format("G: %d", g); + GetDlgItem(IDC_G)->SetWindowText(buffer); + + buffer.Format("B: %d", b); + GetDlgItem(IDC_B)->SetWindowText(buffer); + + return TRUE; +} + + +void GBOamView::updateScrollInfo() +{ + SCROLLINFO si; + ZeroMemory(&si, sizeof(si)); + si.cbSize = sizeof(si); + si.fMask = SIF_PAGE | SIF_RANGE | SIF_DISABLENOSCROLL | SIF_POS; + si.nMin = 0; + si.nMax = 39; + si.nPage = 1; + si.nPos = number; + GetDlgItem(IDC_SCROLLBAR)->SetScrollInfo(SB_CTL, + &si, + TRUE); +} + + +void GBOamView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) +{ + switch(nSBCode) { + case SB_BOTTOM: + number = 39; + break; + case SB_LINEDOWN: + number++; + if(number > 39) + number = 39; + break; + case SB_LINEUP: + number--; + if(number < 0) + number = 0; + break; + case SB_PAGEDOWN: + number += 16; + if(number > 39) + number = 39; + break; + case SB_PAGEUP: + number -= 16; + if(number < 0) + number = 0; + break; + case SB_TOP: + number = 0; + break; + case SB_THUMBTRACK: + number = nPos; + if(number < 0) + number = 0; + if(number > 39) + number = 39; + break; + } + + updateScrollInfo(); + + CString buffer; + buffer.Format("%d", number); + m_sprite.SetWindowText(buffer); + paint(); +} + +void GBOamView::PostNcDestroy() +{ + delete this; +} diff --git a/src/win32/GBOamView.h b/src/win32/GBOamView.h new file mode 100644 index 0000000..8a98e0a --- /dev/null +++ b/src/win32/GBOamView.h @@ -0,0 +1,84 @@ +#if !defined(AFX_GBOAMVIEW_H__FE8105E6_9693_479A_8C57_DEEA1B2EA3D6__INCLUDED_) +#define AFX_GBOAMVIEW_H__FE8105E6_9693_479A_8C57_DEEA1B2EA3D6__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// GBOamView.h : header file +// + +#include "BitmapControl.h" +#include "ZoomControl.h" +#include "ColorControl.h" + +#include "IUpdate.h" +#include "ResizeDlg.h" + +///////////////////////////////////////////////////////////////////////////// +// GBOamView dialog + +class GBOamView : public ResizeDlg, IUpdateListener +{ + private: + BITMAPINFO bmpInfo; + u8 *data; + int w; + int h; + int number; + bool autoUpdate; + BitmapControl oamView; + ZoomControl oamZoom; + ColorControl color; + + + // Construction + public: + void updateScrollInfo(); + void save(); + void savePNG(const char *name); + void saveBMP(const char *name); + void render(); + void setAttributes(int y, int x, int tile, int flags); + void paint(); + ~GBOamView(); + GBOamView(CWnd* pParent = NULL); // standard constructor + + afx_msg LRESULT OnColInfo(WPARAM wParam, LPARAM lParam); + afx_msg LRESULT OnMapInfo(WPARAM wParam, LPARAM lParam); + virtual void update(); + + // Dialog Data + //{{AFX_DATA(GBOamView) + enum { IDD = IDD_GB_OAM_VIEW }; + CEdit m_sprite; + BOOL m_stretch; + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(GBOamView) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual void PostNcDestroy(); + //}}AFX_VIRTUAL + + // Implementation + protected: + + // Generated message map functions + //{{AFX_MSG(GBOamView) + virtual BOOL OnInitDialog(); + afx_msg void OnStretch(); + afx_msg void OnAutoUpdate(); + afx_msg void OnChangeSprite(); + afx_msg void OnClose(); + afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + + //{{AFX_INSERT_LOCATION}} + // Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_GBOAMVIEW_H__FE8105E6_9693_479A_8C57_DEEA1B2EA3D6__INCLUDED_) diff --git a/src/win32/GBPaletteView.cpp b/src/win32/GBPaletteView.cpp new file mode 100644 index 0000000..6ee1917 --- /dev/null +++ b/src/win32/GBPaletteView.cpp @@ -0,0 +1,224 @@ +#include "stdafx.h" +#include "vba.h" +#include "FileDlg.h" +#include "GBPaletteView.h" +#include "WinResUtil.h" + +#include "../System.h" +#include "../gb/gbGlobals.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +void GBPaletteViewControl::updatePalette() +{ + if(gbRom) { + memcpy(palette, &gbPalette[paletteAddress], 64); + } +} + +///////////////////////////////////////////////////////////////////////////// +// GBPaletteView dialog + + +GBPaletteView::GBPaletteView(CWnd* pParent /*=NULL*/) + : ResizeDlg(GBPaletteView::IDD, pParent) +{ + //{{AFX_DATA_INIT(GBPaletteView) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT + autoUpdate = false; +} + +GBPaletteView::~GBPaletteView() +{ +} + +void GBPaletteView::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(GBPaletteView) + // NOTE: the ClassWizard will add DDX and DDV calls here + //}}AFX_DATA_MAP + DDX_Control(pDX, IDC_PALETTE_VIEW, paletteView); + DDX_Control(pDX, IDC_PALETTE_VIEW_OBJ, paletteViewOBJ); + DDX_Control(pDX, IDC_COLOR, colorControl); +} + + +BEGIN_MESSAGE_MAP(GBPaletteView, CDialog) + //{{AFX_MSG_MAP(GBPaletteView) + ON_BN_CLICKED(IDC_SAVE_BG, OnSaveBg) + ON_BN_CLICKED(IDC_SAVE_OBJ, OnSaveObj) + ON_BN_CLICKED(IDC_REFRESH2, OnRefresh2) + ON_BN_CLICKED(IDC_AUTO_UPDATE, OnAutoUpdate) + ON_BN_CLICKED(IDC_CLOSE, OnClose) + //}}AFX_MSG_MAP + ON_MESSAGE(WM_PALINFO, OnPalInfo) + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// GBPaletteView message handlers + +BOOL GBPaletteView::OnInitDialog() +{ + CDialog::OnInitDialog(); + + DIALOG_SIZER_START( sz ) + DIALOG_SIZER_END() + SetData(sz, + FALSE, + HKEY_CURRENT_USER, + "Software\\Emulators\\VisualBoyAdvance\\Viewer\\GBPaletteView", + NULL); + + paletteView.init(32, 64, 128); + paletteViewOBJ.init(32, 64, 128); + + paletteView.setPaletteAddress(0); + paletteView.refresh(); + + paletteViewOBJ.setPaletteAddress(32); + paletteViewOBJ.refresh(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void GBPaletteView::save(int which) +{ + CString captureBuffer; + + if(which == 0) + captureBuffer = "bg.pal"; + else + captureBuffer = "obj.pal"; + + LPCTSTR exts[] = {".pal", ".pal", ".act" }; + + CString filter = theApp.winLoadFilter(IDS_FILTER_PAL); + CString title = winResLoadString(IDS_SELECT_PALETTE_NAME); + FileDlg dlg(this, + captureBuffer, + filter, + 1, + "PAL", + exts, + "", + title, + true); + + if(dlg.DoModal() == IDCANCEL) { + return; + } + + captureBuffer = dlg.GetPathName(); + + PaletteViewControl *p = NULL; + + if(which == 0) + p = &paletteView; + else + p = &paletteViewOBJ; + + switch(dlg.getFilterIndex()) { + case 0: + case 1: + p->saveMSPAL(captureBuffer); + break; + case 2: + p->saveJASCPAL(captureBuffer); + break; + case 3: + p->saveAdobe(captureBuffer); + break; + } +} + + +void GBPaletteView::OnSaveBg() +{ + save(0); +} + +void GBPaletteView::OnSaveObj() +{ + save(1); +} + +void GBPaletteView::OnRefresh2() +{ + paletteView.refresh(); + paletteViewOBJ.refresh(); +} + +void GBPaletteView::update() +{ + OnRefresh2(); +} + + +void GBPaletteView::OnAutoUpdate() +{ + autoUpdate = !autoUpdate; + if(autoUpdate) { + theApp.winAddUpdateListener(this); + } else { + theApp.winRemoveUpdateListener(this); + } +} + + +void GBPaletteView::OnClose() +{ + theApp.winRemoveUpdateListener(this); + + DestroyWindow(); +} + +LRESULT GBPaletteView::OnPalInfo(WPARAM wParam, LPARAM lParam) +{ + u16 color = (u16)wParam; + u32 address = (u32)lParam; + CString buffer; + + bool isOBJ = address >= 32; + address &= 31; + + buffer.Format("%d", address); + GetDlgItem(IDC_ADDRESS)->SetWindowText(buffer); + + int r = (color & 0x1f); + int g = (color & 0x3e0) >> 5; + int b = (color & 0x7c00) >> 10; + + buffer.Format("%d", r); + GetDlgItem(IDC_R)->SetWindowText(buffer); + + buffer.Format("%d", g); + GetDlgItem(IDC_G)->SetWindowText(buffer); + + buffer.Format("%d", b); + GetDlgItem(IDC_B)->SetWindowText(buffer); + + + buffer.Format("0x%04X", color); + GetDlgItem(IDC_VALUE)->SetWindowText(buffer); + + colorControl.setColor(color); + + if(isOBJ) + paletteView.setSelected(-1); + else + paletteViewOBJ.setSelected(-1); + + return TRUE; +} + +void GBPaletteView::PostNcDestroy() +{ + delete this; +} diff --git a/src/win32/GBPaletteView.h b/src/win32/GBPaletteView.h new file mode 100644 index 0000000..9f2b51f --- /dev/null +++ b/src/win32/GBPaletteView.h @@ -0,0 +1,71 @@ +#if !defined(AFX_GBPALETTEVIEW_H__F909FF55_3021_4301_B017_0C2C9D8D8C08__INCLUDED_) +#define AFX_GBPALETTEVIEW_H__F909FF55_3021_4301_B017_0C2C9D8D8C08__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// GBPaletteView.h : header file +// + +#include "ColorControl.h" +#include "IUpdate.h" +#include "PaletteViewControl.h" +#include "ResizeDlg.h" + +class GBPaletteViewControl : public PaletteViewControl { + public: + virtual void updatePalette(); +}; + +///////////////////////////////////////////////////////////////////////////// +// GBPaletteView dialog + +class GBPaletteView : public ResizeDlg, IUpdateListener +{ + private: + GBPaletteViewControl paletteView; + GBPaletteViewControl paletteViewOBJ; + ColorControl colorControl; + bool autoUpdate; + // Construction + public: + void save(int which); + GBPaletteView(CWnd* pParent = NULL); // standard constructor + virtual ~GBPaletteView(); + + // Dialog Data + //{{AFX_DATA(GBPaletteView) + enum { IDD = IDD_GB_PALETTE_VIEW }; + // NOTE: the ClassWizard will add data members here + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(GBPaletteView) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual void PostNcDestroy(); + //}}AFX_VIRTUAL + + virtual void update(); + + // Implementation + protected: + afx_msg virtual LRESULT OnPalInfo(WPARAM wParam, LPARAM lParam); + // Generated message map functions + //{{AFX_MSG(GBPaletteView) + virtual BOOL OnInitDialog(); + afx_msg void OnSaveBg(); + afx_msg void OnSaveObj(); + afx_msg void OnRefresh2(); + afx_msg void OnAutoUpdate(); + afx_msg void OnClose(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + + //{{AFX_INSERT_LOCATION}} + // Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_GBPALETTEVIEW_H__F909FF55_3021_4301_B017_0C2C9D8D8C08__INCLUDED_) diff --git a/src/win32/GBPrinterDlg.cpp b/src/win32/GBPrinterDlg.cpp new file mode 100644 index 0000000..9af2a73 --- /dev/null +++ b/src/win32/GBPrinterDlg.cpp @@ -0,0 +1,481 @@ +#include "stdafx.h" +#include "vba.h" +#include "FileDlg.h" +#include "GBPrinterDlg.h" +#include "Reg.h" +#include "WinResUtil.h" + +#include "../System.h" +#include "../NLS.h" +#include "../Util.h" + +extern "C" { +#include +} + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// GBPrinter dialog + + +GBPrinterDlg::GBPrinterDlg(CWnd* pParent /*=NULL*/) + : CDialog(GBPrinterDlg::IDD, pParent) +{ + bitmapData = new u8[160*144]; + //{{AFX_DATA_INIT(GBPrinterDlg) + m_scale = -1; + //}}AFX_DATA_INIT + bitmap = (BITMAPINFO *)bitmapHeader; + + bitmap->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bitmap->bmiHeader.biWidth = 160; + bitmap->bmiHeader.biHeight = -144; + bitmap->bmiHeader.biPlanes = 1; + bitmap->bmiHeader.biBitCount = 8; + bitmap->bmiHeader.biCompression = BI_RGB; + bitmap->bmiHeader.biSizeImage = 160*144; + bitmap->bmiHeader.biXPelsPerMeter = 0; + bitmap->bmiHeader.biYPelsPerMeter = 0; + bitmap->bmiHeader.biClrUsed = 4; + bitmap->bmiHeader.biClrImportant = 4; + bitmap->bmiColors[0].rgbBlue = + bitmap->bmiColors[0].rgbGreen = + bitmap->bmiColors[0].rgbRed = + 255; + bitmap->bmiColors[0].rgbReserved = 0; + bitmap->bmiColors[1].rgbBlue = + bitmap->bmiColors[1].rgbGreen = + bitmap->bmiColors[1].rgbRed = + 168; + bitmap->bmiColors[1].rgbReserved = 0; + bitmap->bmiColors[2].rgbBlue = + bitmap->bmiColors[2].rgbGreen = + bitmap->bmiColors[2].rgbRed = + 96; + bitmap->bmiColors[2].rgbReserved = 0; + bitmap->bmiColors[3].rgbBlue = + bitmap->bmiColors[3].rgbGreen = + bitmap->bmiColors[3].rgbRed = + 0; + bitmap->bmiColors[3].rgbReserved = 0; +} + + +GBPrinterDlg::~GBPrinterDlg() +{ + delete [] bitmapData; +} + + +void GBPrinterDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(GBPrinterDlg) + DDX_Radio(pDX, IDC_1X, m_scale); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(GBPrinterDlg, CDialog) + //{{AFX_MSG_MAP(GBPrinterDlg) + ON_BN_CLICKED(ID_SAVE, OnSave) + ON_BN_CLICKED(ID_PRINT, OnPrint) + ON_BN_CLICKED(ID_OK, OnOk) + ON_BN_CLICKED(IDC_1X, On1x) + ON_BN_CLICKED(IDC_2X, On2x) + ON_BN_CLICKED(IDC_3X, On3x) + ON_BN_CLICKED(IDC_4X, On4x) + ON_WM_PAINT() + //}}AFX_MSG_MAP + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// GBPrinter message handlers + +void GBPrinterDlg::saveAsBMP(const char *name) +{ + u8 writeBuffer[512 * 3]; + + FILE *fp = fopen(name,"wb"); + + if(!fp) { + systemMessage(MSG_ERROR_CREATING_FILE, "Error creating file %s", name); + return; + } + + struct { + u8 ident[2]; + u8 filesize[4]; + u8 reserved[4]; + u8 dataoffset[4]; + u8 headersize[4]; + u8 width[4]; + u8 height[4]; + u8 planes[2]; + u8 bitsperpixel[2]; + u8 compression[4]; + u8 datasize[4]; + u8 hres[4]; + u8 vres[4]; + u8 colors[4]; + u8 importantcolors[4]; + u8 pad[2]; + } bmpheader; + memset(&bmpheader, 0, sizeof(bmpheader)); + + bmpheader.ident[0] = 'B'; + bmpheader.ident[1] = 'M'; + + u32 fsz = sizeof(bmpheader) + 160*144*3; + utilPutDword(bmpheader.filesize, fsz); + utilPutDword(bmpheader.dataoffset, 0x38); + utilPutDword(bmpheader.headersize, 0x28); + utilPutDword(bmpheader.width, 160); + utilPutDword(bmpheader.height, 144); + utilPutDword(bmpheader.planes, 1); + utilPutDword(bmpheader.bitsperpixel, 24); + utilPutDword(bmpheader.datasize, 160*144); + + fwrite(&bmpheader, 1, sizeof(bmpheader), fp); + + u8 *b = writeBuffer; + u8 *data = (u8 *)bitmapData; + u8 *p = data + (160*143); + for(int y = 0; y < 144; y++) { + for(int x = 0; x < 160; x++) { + u8 c = *p++; + + *b++ = bitmap->bmiColors[c].rgbBlue; + *b++ = bitmap->bmiColors[c].rgbGreen; + *b++ = bitmap->bmiColors[c].rgbRed; + } + p -= 2*(160); + fwrite(writeBuffer, 1, 3*160, fp); + + b = writeBuffer; + } + + fclose(fp); +} + + +void GBPrinterDlg::saveAsPNG(const char *name) +{ + u8 writeBuffer[160 * 3]; + + FILE *fp = fopen(name,"wb"); + + if(!fp) { + systemMessage(MSG_ERROR_CREATING_FILE, "Error creating file %s", + name); + return; + } + + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, + NULL, + NULL, + NULL); + if(!png_ptr) { + fclose(fp); + return; + } + + png_infop info_ptr = png_create_info_struct(png_ptr); + + if(!info_ptr) { + png_destroy_write_struct(&png_ptr,NULL); + fclose(fp); + return; + } + + if(setjmp(png_ptr->jmpbuf)) { + png_destroy_write_struct(&png_ptr,NULL); + fclose(fp); + return; + } + + png_init_io(png_ptr,fp); + + png_set_IHDR(png_ptr, + info_ptr, + 160, + 144, + 8, + PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + png_write_info(png_ptr,info_ptr); + + u8 *b = writeBuffer; + + int sizeX = 160; + int sizeY = 144; + + u8 *pixU8 = bitmapData; + for(int y = 0; y < sizeY; y++) { + for(int x = 0; x < sizeX; x++) { + u8 c = *pixU8++; + *b++ = bitmap->bmiColors[c].rgbRed; + *b++ = bitmap->bmiColors[c].rgbGreen; + *b++ = bitmap->bmiColors[c].rgbBlue; + } + png_write_row(png_ptr,writeBuffer); + + b = writeBuffer; + } + + png_write_end(png_ptr, info_ptr); + + png_destroy_write_struct(&png_ptr, &info_ptr); + + fclose(fp); +} + + +void GBPrinterDlg::OnSave() +{ + CString captureBuffer; + + if(theApp.captureFormat == 0) + captureBuffer = "printer.png"; + else + captureBuffer = "printer.bmp"; + + LPCTSTR exts[] = {".png", ".bmp" }; + + CString filter = theApp.winLoadFilter(IDS_FILTER_PNG); + CString title = winResLoadString(IDS_SELECT_CAPTURE_NAME); + + FileDlg dlg(this, + captureBuffer, + filter, + theApp.captureFormat ? 2 : 1, + theApp.captureFormat ? "BMP" : "PNG", + exts, + "", + title, + true); + + if(dlg.DoModal() == IDCANCEL) { + return; + } + + captureBuffer = dlg.GetPathName(); + + if(dlg.getFilterIndex() == 2) + saveAsBMP(captureBuffer); + else + saveAsPNG(captureBuffer); +} + +void GBPrinterDlg::OnPrint() +{ + CPrintDialog dlg(FALSE); + + dlg.m_pd.nFromPage = 1; + dlg.m_pd.nToPage = 1; + dlg.m_pd.nMinPage = 1; + dlg.m_pd.nMaxPage = 1; + dlg.m_pd.nCopies = 1; + + if(dlg.DoModal() == IDOK) { + DOCINFO di; + float fLogPelsX1 = 0; + float fLogPelsX2 = 0; + float fLogPelsY1 = 0; + float fLogPelsY2 = 0; + float fScaleX = 0, fScaleY = 0; + CDC *winDC = NULL; + memset(&di, 0, sizeof(di)); + di.cbSize = sizeof(DOCINFO); + CString docName = winResLoadString(IDS_POCKET_PRINTER); + di.lpszDocName = docName; + CDC dc; + dc.Attach(dlg.GetPrinterDC()); + int nError = dc.StartDoc(&di); + + if(nError == SP_ERROR) { + systemMessage(IDS_ERROR_ON_STARTDOC,"Error on StartDoc"); + goto error; + } + nError = dc.StartPage(); + if(nError <= 0) { + systemMessage(IDS_ERROR_ON_STARTPAGE, "Error on StartPage"); + goto error; + } + + winDC = GetDC(); + fLogPelsX1 = (float)winDC->GetDeviceCaps(LOGPIXELSX); + fLogPelsY1 = (float)winDC->GetDeviceCaps(LOGPIXELSY); + ReleaseDC(winDC); + + fLogPelsX2 = (float)dc.GetDeviceCaps(LOGPIXELSX); + fLogPelsY2 = (float)dc.GetDeviceCaps(LOGPIXELSY); + + if(fLogPelsX1 > fLogPelsX2) + fScaleX = fLogPelsX1 / fLogPelsX2; + else + fScaleX = fLogPelsX2 / fLogPelsX1; + + if(fLogPelsY1 > fLogPelsY2) + fScaleY = fLogPelsY1 / fLogPelsY2; + else + fScaleY = fLogPelsY2 / fLogPelsY1; + + fScaleX *= (scale+1); + fScaleY *= (scale+1); + + if(StretchDIBits(dc, + 0, + 0, + (int)((float)160*fScaleX), + (int)((float)144*fScaleY), + 0, + 0, + 160, + 144, + bitmapData, + bitmap, + DIB_RGB_COLORS, + SRCCOPY) == GDI_ERROR) { + systemMessage(IDS_ERROR_PRINTING_ON_STRETCH, + "Error printing on StretchDIBits"); + } + + nError = dc.EndPage(); + + if(nError <= 0) { + systemMessage(IDS_ERROR_ON_ENDPAGE, "Error on EndPage"); + goto error; + } + + nError = dc.EndDoc(); + + if(nError <= 0) + systemMessage(IDS_ERROR_ON_ENDDOC, "Error on EndDoc"); + error: + dc.DeleteDC(); + } +} + +void GBPrinterDlg::processData(u8 *data) +{ + for(int y = 0; y < 0x12; y++) { + for(int x = 0; x < 0x14; x++) { + for(int k = 0; k < 8; k++) { + int a = *data++; + int b = *data++; + for(int j = 0; j < 8; j++) { + int mask = 1 << (7-j); + int c = 0; + if(a & mask) + c++; + if(b & mask) + c+=2; + bitmapData[x*8+j + 160*(y*8+k)] = c; + } + } + } + } +} + + +BOOL GBPrinterDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + + scale = regQueryDwordValue("printerScale", 0); + if(scale < 0 || scale > 3) + scale = 0; + m_scale = scale; + UpdateData(FALSE); + + CenterWindow(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void GBPrinterDlg::OnOk() +{ + EndDialog(TRUE); +} + +void GBPrinterDlg::On1x() +{ + regSetDwordValue("printerScale", 0); + scale = 0; +} + +void GBPrinterDlg::On2x() +{ + regSetDwordValue("printerScale", 1); + scale = 1; +} + +void GBPrinterDlg::On3x() +{ + regSetDwordValue("printerScale", 2); + scale = 2; +} + +void GBPrinterDlg::On4x() +{ + regSetDwordValue("printerScale", 3); + scale = 3; +} + +void GBPrinterDlg::OnPaint() +{ + CPaintDC dc(this); // device context for painting + + RECT rect; + CWnd *h = GetDlgItem(IDC_GB_PRINTER); + h->GetWindowRect(&rect); + POINT p; + p.x = rect.left; + p.y = rect.top; + ScreenToClient((POINT *)&p); + rect.left = p.x+1; + rect.top = p.y+1; + p.x = rect.right; + p.y = rect.bottom; + ScreenToClient((POINT *)&p); + rect.right = p.x-1; + rect.bottom = p.y-1; + + StretchDIBits(dc, + rect.left, + rect.top, + rect.right - rect.left, + rect.bottom - rect.top, + 0, + 0, + 160, + 144, + bitmapData, + bitmap, + DIB_RGB_COLORS, + SRCCOPY); +} + +void systemGbPrint(u8 *data, + int datalen, + int pages, + int feed, + int palette, + int contrast) +{ + GBPrinterDlg printer; + memset(data + datalen, 0, 160*144/4 - datalen); + printer.processData(data); + printer.DoModal(); +} + diff --git a/src/win32/GBPrinterDlg.h b/src/win32/GBPrinterDlg.h new file mode 100644 index 0000000..fd4261a --- /dev/null +++ b/src/win32/GBPrinterDlg.h @@ -0,0 +1,63 @@ +#if !defined(AFX_GBPRINTER_H__3180CC5A_1F9D_47E5_B044_407442CB40A4__INCLUDED_) +#define AFX_GBPRINTER_H__3180CC5A_1F9D_47E5_B044_407442CB40A4__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// GBPrinter.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// GBPrinter dialog + +class GBPrinterDlg : public CDialog +{ + private: + u8 bitmapHeader[sizeof(BITMAPINFO)+4*sizeof(RGBQUAD)]; + BITMAPINFO *bitmap; + u8 *bitmapData; + int scale; + // Construction + public: + void processData(u8 *data); + void saveAsPNG(const char *name); + void saveAsBMP(const char *name); + GBPrinterDlg(CWnd* pParent = NULL); // standard constructor + ~GBPrinterDlg(); + + // Dialog Data + //{{AFX_DATA(GBPrinterDlg) + enum { IDD = IDD_GB_PRINTER }; + int m_scale; + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(GBPrinterDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + + // Implementation + protected: + + // Generated message map functions + //{{AFX_MSG(GBPrinterDlg) + afx_msg void OnSave(); + afx_msg void OnPrint(); + virtual BOOL OnInitDialog(); + afx_msg void OnOk(); + afx_msg void On1x(); + afx_msg void On2x(); + afx_msg void On3x(); + afx_msg void On4x(); + afx_msg void OnPaint(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + + //{{AFX_INSERT_LOCATION}} + // Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_GBPRINTER_H__3180CC5A_1F9D_47E5_B044_407442CB40A4__INCLUDED_) diff --git a/src/win32/GBTileView.cpp b/src/win32/GBTileView.cpp new file mode 100644 index 0000000..585f554 --- /dev/null +++ b/src/win32/GBTileView.cpp @@ -0,0 +1,503 @@ +#include "stdafx.h" +#include "vba.h" +#include "FileDlg.h" +#include "GBTileView.h" +#include "Reg.h" +#include "WinResUtil.h" + +#include "../System.h" +#include "../NLS.h" +#include "../Util.h" +#include "../gb/gbGlobals.h" + +extern "C" { +#include +} + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// GBTileView dialog + + +GBTileView::GBTileView(CWnd* pParent /*=NULL*/) + : ResizeDlg(GBTileView::IDD, pParent) +{ + //{{AFX_DATA_INIT(GBTileView) + m_charBase = -1; + m_bank = -1; + m_stretch = FALSE; + //}}AFX_DATA_INIT + autoUpdate = false; + + memset(&bmpInfo, 0, sizeof(bmpInfo)); + + bmpInfo.bmiHeader.biSize = sizeof(bmpInfo.bmiHeader); + bmpInfo.bmiHeader.biWidth = 32*8; + bmpInfo.bmiHeader.biHeight = 32*8; + bmpInfo.bmiHeader.biPlanes = 1; + bmpInfo.bmiHeader.biBitCount = 24; + bmpInfo.bmiHeader.biCompression = BI_RGB; + data = (u8 *)calloc(1, 3 * 32*32 * 64); + + tileView.setData(data); + tileView.setBmpInfo(&bmpInfo); + + charBase = 0; + palette = 0; + bank = 0; + w = h = 0; +} + +GBTileView::~GBTileView() +{ + free(data); + data = NULL; +} + +void GBTileView::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(GBTileView) + DDX_Control(pDX, IDC_PALETTE_SLIDER, m_slider); + DDX_Radio(pDX, IDC_CHARBASE_0, m_charBase); + DDX_Radio(pDX, IDC_BANK_0, m_bank); + DDX_Check(pDX, IDC_STRETCH, m_stretch); + //}}AFX_DATA_MAP + DDX_Control(pDX, IDC_TILE_VIEW, tileView); + DDX_Control(pDX, IDC_MAP_VIEW_ZOOM, zoom); + DDX_Control(pDX, IDC_COLOR, color); +} + + +BEGIN_MESSAGE_MAP(GBTileView, CDialog) + //{{AFX_MSG_MAP(GBTileView) + ON_BN_CLICKED(IDC_SAVE, OnSave) + ON_BN_CLICKED(IDC_CLOSE, OnClose) + ON_BN_CLICKED(IDC_AUTO_UPDATE, OnAutoUpdate) + ON_BN_CLICKED(IDC_CHARBASE_0, OnCharbase0) + ON_BN_CLICKED(IDC_CHARBASE_1, OnCharbase1) + ON_BN_CLICKED(IDC_BANK_0, OnBank0) + ON_BN_CLICKED(IDC_BANK_1, OnBank1) + ON_BN_CLICKED(IDC_STRETCH, OnStretch) + ON_WM_HSCROLL() + //}}AFX_MSG_MAP + ON_MESSAGE(WM_MAPINFO, OnMapInfo) + ON_MESSAGE(WM_COLINFO, OnColInfo) + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// GBTileView message handlers + +void GBTileView::saveBMP(const char *name) +{ + u8 writeBuffer[1024 * 3]; + + FILE *fp = fopen(name,"wb"); + + if(!fp) { + systemMessage(MSG_ERROR_CREATING_FILE, "Error creating file %s", name); + return; + } + + struct { + u8 ident[2]; + u8 filesize[4]; + u8 reserved[4]; + u8 dataoffset[4]; + u8 headersize[4]; + u8 width[4]; + u8 height[4]; + u8 planes[2]; + u8 bitsperpixel[2]; + u8 compression[4]; + u8 datasize[4]; + u8 hres[4]; + u8 vres[4]; + u8 colors[4]; + u8 importantcolors[4]; + u8 pad[2]; + } bmpheader; + memset(&bmpheader, 0, sizeof(bmpheader)); + + bmpheader.ident[0] = 'B'; + bmpheader.ident[1] = 'M'; + + u32 fsz = sizeof(bmpheader) + w*h*3; + utilPutDword(bmpheader.filesize, fsz); + utilPutDword(bmpheader.dataoffset, 0x38); + utilPutDword(bmpheader.headersize, 0x28); + utilPutDword(bmpheader.width, w); + utilPutDword(bmpheader.height, h); + utilPutDword(bmpheader.planes, 1); + utilPutDword(bmpheader.bitsperpixel, 24); + utilPutDword(bmpheader.datasize, 3*w*h); + + fwrite(&bmpheader, 1, sizeof(bmpheader), fp); + + u8 *b = writeBuffer; + + int sizeX = w; + int sizeY = h; + + u8 *pixU8 = (u8 *)data+3*w*(h-1); + for(int y = 0; y < sizeY; y++) { + for(int x = 0; x < sizeX; x++) { + *b++ = *pixU8++; // B + *b++ = *pixU8++; // G + *b++ = *pixU8++; // R + } + pixU8 -= 2*3*w; + fwrite(writeBuffer, 1, 3*w, fp); + + b = writeBuffer; + } + + fclose(fp); +} + + +void GBTileView::savePNG(const char *name) +{ + u8 writeBuffer[1024 * 3]; + + FILE *fp = fopen(name,"wb"); + + if(!fp) { + systemMessage(MSG_ERROR_CREATING_FILE, "Error creating file %s", name); + return; + } + + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, + NULL, + NULL, + NULL); + if(!png_ptr) { + fclose(fp); + return; + } + + png_infop info_ptr = png_create_info_struct(png_ptr); + + if(!info_ptr) { + png_destroy_write_struct(&png_ptr,NULL); + fclose(fp); + return; + } + + if(setjmp(png_ptr->jmpbuf)) { + png_destroy_write_struct(&png_ptr,NULL); + fclose(fp); + return; + } + + png_init_io(png_ptr,fp); + + png_set_IHDR(png_ptr, + info_ptr, + w, + h, + 8, + PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + png_write_info(png_ptr,info_ptr); + + u8 *b = writeBuffer; + + int sizeX = w; + int sizeY = h; + + u8 *pixU8 = (u8 *)data; + for(int y = 0; y < sizeY; y++) { + for(int x = 0; x < sizeX; x++) { + int blue = *pixU8++; + int green = *pixU8++; + int red = *pixU8++; + + *b++ = red; + *b++ = green; + *b++ = blue; + } + png_write_row(png_ptr,writeBuffer); + + b = writeBuffer; + } + + png_write_end(png_ptr, info_ptr); + + png_destroy_write_struct(&png_ptr, &info_ptr); + + fclose(fp); +} + + +void GBTileView::OnSave() +{ + CString captureBuffer; + + if(theApp.captureFormat == 0) + captureBuffer = "tiles.png"; + else + captureBuffer = "tiles.bmp"; + + LPCTSTR exts[] = {".png", ".bmp" }; + + CString filter = theApp.winLoadFilter(IDS_FILTER_PNG); + CString title = winResLoadString(IDS_SELECT_CAPTURE_NAME); + + FileDlg dlg(this, + captureBuffer, + filter, + theApp.captureFormat ? 2 : 1, + theApp.captureFormat ? "BMP" : "PNG", + exts, + "", + title, + true); + + if(dlg.DoModal() == IDCANCEL) { + return; + } + + captureBuffer = dlg.GetPathName(); + + if(theApp.captureFormat) + saveBMP(captureBuffer); + else + savePNG(captureBuffer); +} + +void GBTileView::renderTile(int tile, int x, int y, u8 *charBase) +{ + u8 *bmp = &data[24*x + 8*16*24*y]; + + for(int j = 0; j < 8; j++) { + u8 mask = 0x80; + u8 tile_a = charBase[tile*16+j*2]; + u8 tile_b = charBase[tile*16+j*2+1]; + + for(int i = 0; i < 8; i++) { + u8 c = (tile_a & mask) ? 1 : 0; + c += ((tile_b & mask) ? 2 : 0); + + if(gbCgbMode) { + c = c + palette*4; + } else { + c = gbBgp[c]; + } + + u16 color = gbPalette[c]; + + *bmp++ = ((color >> 10) & 0x1f) << 3; + *bmp++ = ((color >> 5) & 0x1f) << 3; + *bmp++ = (color & 0x1f) << 3; + + mask >>= 1; + } + bmp += 15*24; // advance line + } +} + + +void GBTileView::render() +{ + int tiles = 0x0000; + if(charBase) + tiles = 0x0800; + u8 *charBase = (gbVram != NULL) ? + (bank ? &gbVram[0x2000+tiles] : &gbVram[tiles]) : + &gbMemory[0x8000+tiles]; + + int tile = 0; + for(int y = 0; y < 16; y++) { + for(int x = 0; x < 16; x++) { + renderTile(tile, x, y, charBase); + tile++; + } + } + tileView.setSize(16*8, 16*8); + w = 16*8; + h = 16*8; + SIZE s; + s.cx = s.cy = 16*8; + if(tileView.getStretch()) { + s.cx = s.cy = 1; + } + tileView.SetScrollSizes(MM_TEXT, s); +} + +void GBTileView::update() +{ + paint(); +} + + +BOOL GBTileView::OnInitDialog() +{ + CDialog::OnInitDialog(); + + DIALOG_SIZER_START( sz ) + DIALOG_SIZER_ENTRY( IDC_TILE_VIEW, DS_SizeX | DS_SizeY ) + DIALOG_SIZER_ENTRY( IDC_COLOR, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_R, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_G, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_B, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_REFRESH, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_CLOSE, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_SAVE, DS_MoveY) + DIALOG_SIZER_END() + SetData(sz, + TRUE, + HKEY_CURRENT_USER, + "Software\\Emulators\\VisualBoyAdvance\\Viewer\\GBTileView", + NULL); + + m_charBase = charBase; + m_bank = bank; + + m_slider.SetRange(0, 7); + m_slider.SetPageSize(2); + m_slider.SetTicFreq(1); + paint(); + + m_stretch = regQueryDwordValue("tileViewStretch", 0); + if(m_stretch) + tileView.setStretch(true); + UpdateData(FALSE); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void GBTileView::OnClose() +{ + theApp.winRemoveUpdateListener(this); + + DestroyWindow(); +} + + +void GBTileView::OnAutoUpdate() +{ + autoUpdate = !autoUpdate; + if(autoUpdate) { + theApp.winAddUpdateListener(this); + } else { + theApp.winRemoveUpdateListener(this); + } +} + +void GBTileView::paint() +{ + if(gbRom != NULL) { + render(); + tileView.refresh(); + } +} + +void GBTileView::OnCharbase0() +{ + charBase = 0; + paint(); +} + +void GBTileView::OnCharbase1() +{ + charBase = 1; + paint(); +} + +void GBTileView::OnBank0() +{ + bank = 0; + paint(); +} + +void GBTileView::OnBank1() +{ + bank = 1; + paint(); +} + + +void GBTileView::OnStretch() +{ + tileView.setStretch(!tileView.getStretch()); + paint(); + regSetDwordValue("tileViewStretch", tileView.getStretch()); +} + +LRESULT GBTileView::OnMapInfo(WPARAM wParam, LPARAM lParam) +{ + u8 *colors = (u8 *)lParam; + zoom.setColors(colors); + + int x = (int)((wParam & 0xffff)/8); + int y = (int)(((wParam >> 16) & 0xFFFF)/8); + + int tiles = 0x0000; + if(charBase) + tiles = 0x0800; + u32 address = 0x8000 + tiles; + int tile = 16 * y + x; + + address += 16 * tile; + + CString buffer; + buffer.Format("%d", tile); + GetDlgItem(IDC_TILE_NUMBER)->SetWindowText(buffer); + + buffer.Format("%04x", address); + GetDlgItem(IDC_ADDRESS)->SetWindowText(buffer); + + return TRUE; +} + +LRESULT GBTileView::OnColInfo(WPARAM wParam, LPARAM) +{ + u16 c = (u16)wParam; + + color.setColor(c); + + int r = (c & 0x1f); + int g = (c & 0x3e0) >> 5; + int b = (c & 0x7c00) >> 10; + + CString buffer; + buffer.Format("R: %d", r); + GetDlgItem(IDC_R)->SetWindowText(buffer); + + buffer.Format("G: %d", g); + GetDlgItem(IDC_G)->SetWindowText(buffer); + + buffer.Format("B: %d", b); + GetDlgItem(IDC_B)->SetWindowText(buffer); + + return TRUE; +} + +void GBTileView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) +{ + switch(nSBCode) { + case TB_THUMBPOSITION: + palette = nPos; + break; + default: + palette = m_slider.GetPos(); + break; + } + paint(); +} + + +void GBTileView::PostNcDestroy() +{ + delete this; +} diff --git a/src/win32/GBTileView.h b/src/win32/GBTileView.h new file mode 100644 index 0000000..b3974d9 --- /dev/null +++ b/src/win32/GBTileView.h @@ -0,0 +1,85 @@ +#if !defined(AFX_GBTILEVIEW_H__C8C8DEBB_17ED_4C5C_9DBE_D730A49B312C__INCLUDED_) +#define AFX_GBTILEVIEW_H__C8C8DEBB_17ED_4C5C_9DBE_D730A49B312C__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// GBTileView.h : header file +// +#include "BitmapControl.h" +#include "ColorControl.h" +#include "IUpdate.h" +#include "ResizeDlg.h" +#include "ZoomControl.h" + +///////////////////////////////////////////////////////////////////////////// +// GBTileView dialog + +class GBTileView : public ResizeDlg, IUpdateListener +{ + int charBase; + int palette; + int bank; + BitmapControl tileView; + BITMAPINFO bmpInfo; + u8 *data; + ZoomControl zoom; + ColorControl color; + int w; + int h; + bool autoUpdate; + // Construction + public: + void paint(); + void render(); + void renderTile(int tile, int x, int y, u8 *charBase); + void savePNG(const char *name); + void saveBMP(const char *name); + GBTileView(CWnd* pParent = NULL); // standard constructor + virtual ~GBTileView(); + + virtual void update(); + + // Dialog Data + //{{AFX_DATA(GBTileView) + enum { IDD = IDD_GB_TILE_VIEWER }; + CSliderCtrl m_slider; + int m_charBase; + int m_bank; + BOOL m_stretch; + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(GBTileView) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual void PostNcDestroy(); + //}}AFX_VIRTUAL + + // Implementation + protected: + virtual afx_msg LRESULT OnMapInfo(WPARAM wParam, LPARAM lParam); + virtual afx_msg LRESULT OnColInfo(WPARAM wParam, LPARAM lParam); + + // Generated message map functions + //{{AFX_MSG(GBTileView) + afx_msg void OnSave(); + virtual BOOL OnInitDialog(); + afx_msg void OnClose(); + afx_msg void OnAutoUpdate(); + afx_msg void OnCharbase0(); + afx_msg void OnCharbase1(); + afx_msg void OnBank0(); + afx_msg void OnBank1(); + afx_msg void OnStretch(); + afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + + //{{AFX_INSERT_LOCATION}} + // Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_GBTILEVIEW_H__C8C8DEBB_17ED_4C5C_9DBE_D730A49B312C__INCLUDED_) diff --git a/src/win32/GDBConnection.cpp b/src/win32/GDBConnection.cpp new file mode 100644 index 0000000..837317a --- /dev/null +++ b/src/win32/GDBConnection.cpp @@ -0,0 +1,241 @@ +#include "stdafx.h" +#include "vba.h" +#include "GDBConnection.h" + +#include + +#define SOCKET_MESSAGE WM_APP+1 + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +static bool initialized = false; + +///////////////////////////////////////////////////////////////////////////// +// GDBPortDlg dialog + + +GDBPortDlg::GDBPortDlg(CWnd* pParent /*=NULL*/) + : CDialog(GDBPortDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(GDBPortDlg) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT + port = 55555; + sock = INVALID_SOCKET; + + if(!initialized) { + WSADATA wsaData; + + int error = WSAStartup(MAKEWORD(1,1), &wsaData); + + initialized = true; + } +} + + +void GDBPortDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(GDBPortDlg) + DDX_Control(pDX, IDC_PORT, m_port); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(GDBPortDlg, CDialog) + //{{AFX_MSG_MAP(GDBPortDlg) + ON_BN_CLICKED(ID_OK, OnOk) + ON_BN_CLICKED(ID_CANCEL, OnCancel) + ON_WM_CLOSE() + //}}AFX_MSG_MAP + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// GDBPortDlg message handlers + +int GDBPortDlg::getPort() +{ + return port; +} + + +SOCKET GDBPortDlg::getSocket() +{ + return sock; +} + + +BOOL GDBPortDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + + CString buffer; + + buffer.Format("%d", port); + + m_port.SetWindowText(buffer); + + CenterWindow(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void GDBPortDlg::OnOk() +{ + CString buffer; + + m_port.GetWindowText(buffer); + + sockaddr_in address; + address.sin_family = AF_INET; + address.sin_addr.s_addr = inet_addr("0.0.0.0"); + address.sin_port = htons(atoi(buffer)); + port = ntohs(address.sin_port); + + SOCKET s = socket(AF_INET, SOCK_STREAM, 0); + + if(s != INVALID_SOCKET) { + int error = bind(s, (sockaddr *)&address, sizeof(address)); + + if(error) { + systemMessage(IDS_ERROR_BINDING, "Error binding socket. Port probably in use."); + error = closesocket(s); + EndDialog(FALSE); + } else { + error = listen(s, 1); + if(!error) { + sock = s; + EndDialog(TRUE); + } else { + systemMessage(IDS_ERROR_LISTENING, "Error listening on socket."); + closesocket(s); + EndDialog(FALSE); + } + } + } else { + systemMessage(IDS_ERROR_CREATING_SOCKET, "Error creating socket."); + EndDialog(FALSE); + } +} + +void GDBPortDlg::OnCancel() +{ + OnClose(); +} + +void GDBPortDlg::OnClose() +{ + EndDialog(FALSE); +} +///////////////////////////////////////////////////////////////////////////// +// GDBWaitingDlg dialog + + +GDBWaitingDlg::GDBWaitingDlg(SOCKET s, int p, CWnd* pParent /*=NULL*/) + : CDialog(GDBWaitingDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(GDBWaitingDlg) + //}}AFX_DATA_INIT + port = p & 65535; + listenSocket = s; +} + + +void GDBWaitingDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(GDBWaitingDlg) + DDX_Control(pDX, IDC_PORT, m_port); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(GDBWaitingDlg, CDialog) + //{{AFX_MSG_MAP(GDBWaitingDlg) + ON_BN_CLICKED(ID_CANCEL, OnCancel) + ON_WM_CLOSE() + //}}AFX_MSG_MAP + ON_MESSAGE(SOCKET_MESSAGE, OnSocketAccept) + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// GDBWaitingDlg message handlers + +BOOL GDBWaitingDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + + CString buffer; + + buffer.Format("%d", port); + + m_port.SetWindowText(buffer); + + CenterWindow(); + + int error = WSAAsyncSelect(listenSocket, + (HWND )*this, + SOCKET_MESSAGE, + FD_ACCEPT); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +LRESULT GDBWaitingDlg::OnSocketAccept(WPARAM wParam, LPARAM lParam) +{ + if(LOWORD(lParam) == FD_ACCEPT) { + WSAAsyncSelect(listenSocket, (HWND)*this, 0, 0); + + int flag = 0; + ioctlsocket(listenSocket, FIONBIO, (unsigned long *)&flag); + + SOCKET s = accept(listenSocket, NULL, NULL); + if(s != INVALID_SOCKET) { + char dummy; + recv(s, &dummy, 1, 0); + if(dummy != '+') { + systemMessage(IDS_ACK_NOT_RECEIVED, "ACK not received from GDB."); + OnClose(); // calls EndDialog + } else { + sock = s; + EndDialog(TRUE); + } + } + } + + return TRUE; +} + +void GDBWaitingDlg::OnCancel() +{ + OnClose(); +} + +void GDBWaitingDlg::OnClose() +{ + if(sock != INVALID_SOCKET) { + closesocket(sock); + sock = INVALID_SOCKET; + } + if(listenSocket != INVALID_SOCKET) { + closesocket(listenSocket); + listenSocket = INVALID_SOCKET; + } + EndDialog(FALSE); +} + +SOCKET GDBWaitingDlg::getListenSocket() +{ + return listenSocket; +} + +SOCKET GDBWaitingDlg::getSocket() +{ + return sock; +} diff --git a/src/win32/GDBConnection.h b/src/win32/GDBConnection.h new file mode 100644 index 0000000..d2fa805 --- /dev/null +++ b/src/win32/GDBConnection.h @@ -0,0 +1,95 @@ +#if !defined(AFX_GDBCONNECTION_H__DD73B298_E1A7_4A46_B282_E7A2B37FC9D9__INCLUDED_) +#define AFX_GDBCONNECTION_H__DD73B298_E1A7_4A46_B282_E7A2B37FC9D9__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// GDBConnection.h : header file +// + +#include + +///////////////////////////////////////////////////////////////////////////// +// GDBPortDlg dialog + +class GDBPortDlg : public CDialog +{ + int port; + SOCKET sock; + // Construction + public: + SOCKET getSocket(); + int getPort(); + GDBPortDlg(CWnd* pParent = NULL); // standard constructor + + // Dialog Data + //{{AFX_DATA(GDBPortDlg) + enum { IDD = IDD_GDB_PORT }; + CEdit m_port; + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(GDBPortDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + + // Implementation + protected: + + // Generated message map functions + //{{AFX_MSG(GDBPortDlg) + virtual BOOL OnInitDialog(); + afx_msg void OnOk(); + afx_msg void OnCancel(); + afx_msg void OnClose(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + + ///////////////////////////////////////////////////////////////////////////// +// GDBWaitingDlg dialog + +class GDBWaitingDlg : public CDialog +{ + int port; + SOCKET listenSocket; + SOCKET sock; + // Construction + public: + SOCKET getSocket(); + SOCKET getListenSocket(); + afx_msg LRESULT OnSocketAccept(WPARAM wParam, LPARAM lParam); + GDBWaitingDlg(SOCKET s,int p, CWnd* pParent = NULL); // standard constructor + + // Dialog Data + //{{AFX_DATA(GDBWaitingDlg) + enum { IDD = IDD_GDB_WAITING }; + CStatic m_port; + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(GDBWaitingDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + + // Implementation + protected: + + // Generated message map functions + //{{AFX_MSG(GDBWaitingDlg) + virtual BOOL OnInitDialog(); + afx_msg void OnCancel(); + afx_msg void OnClose(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + //{{AFX_INSERT_LOCATION}} + // Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_GDBCONNECTION_H__DD73B298_E1A7_4A46_B282_E7A2B37FC9D9__INCLUDED_) diff --git a/src/win32/GSACodeSelect.cpp b/src/win32/GSACodeSelect.cpp new file mode 100644 index 0000000..e57ef62 --- /dev/null +++ b/src/win32/GSACodeSelect.cpp @@ -0,0 +1,99 @@ +#include "stdafx.h" +#include "vba.h" +#include "GSACodeSelect.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// GSACodeSelect dialog + + +GSACodeSelect::GSACodeSelect(FILE *file, CWnd* pParent /*=NULL*/) + : CDialog(GSACodeSelect::IDD, pParent) +{ + //{{AFX_DATA_INIT(GSACodeSelect) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT + m_file = file; +} + + +void GSACodeSelect::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(GSACodeSelect) + DDX_Control(pDX, IDC_GAME_LIST, m_games); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(GSACodeSelect, CDialog) + //{{AFX_MSG_MAP(GSACodeSelect) + ON_BN_CLICKED(ID_OK, OnOk) + ON_LBN_SELCHANGE(IDC_GAME_LIST, OnSelchangeGameList) + ON_BN_CLICKED(ID_CANCEL, OnCancel) + //}}AFX_MSG_MAP + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// GSACodeSelect message handlers + +void GSACodeSelect::OnCancel() +{ + EndDialog(-1); +} + +void GSACodeSelect::OnOk() +{ + EndDialog(m_games.GetCurSel()); +} + +void GSACodeSelect::OnSelchangeGameList() +{ + int item = m_games.GetCurSel(); + CWnd *ok = GetDlgItem(ID_OK); + + ok->EnableWindow(item != -1); +} + +BOOL GSACodeSelect::OnInitDialog() +{ + CDialog::OnInitDialog(); + + char buffer[1024]; + + FILE *f = m_file; + int games = 0; + int len = 0; + fseek(f, -4, SEEK_CUR); + fread(&games, 1, 4, f); + while(games > 0) { + fread(&len, 1, 4, f); + fread(buffer, 1, len, f); + buffer[len] = 0; + m_games.AddString(buffer); + int codes = 0; + fread(&codes, 1, 4, f); + + while(codes > 0) { + fread(&len, 1, 4, f); + fseek(f, len, SEEK_CUR); + fread(&len, 1, 4, f); + fseek(f, len, SEEK_CUR); + fseek(f, 4, SEEK_CUR); + fread(&len, 1, 4, f); + fseek(f, len*12, SEEK_CUR); + codes--; + } + games--; + } + GetDlgItem(ID_OK)->EnableWindow(FALSE); + CenterWindow(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} diff --git a/src/win32/GSACodeSelect.h b/src/win32/GSACodeSelect.h new file mode 100644 index 0000000..a0a5959 --- /dev/null +++ b/src/win32/GSACodeSelect.h @@ -0,0 +1,50 @@ +#if !defined(AFX_GSACODESELECT_H__189BD94D_288F_4E2A_9395_EAB16F104D87__INCLUDED_) +#define AFX_GSACODESELECT_H__189BD94D_288F_4E2A_9395_EAB16F104D87__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// GSACodeSelect.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// GSACodeSelect dialog + +class GSACodeSelect : public CDialog +{ + // Construction + public: + GSACodeSelect(FILE *file, CWnd* pParent = NULL); // standard constructor + + // Dialog Data + //{{AFX_DATA(GSACodeSelect) + enum { IDD = IDD_CODE_SELECT }; + CListBox m_games; + //}}AFX_DATA + FILE *m_file; + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(GSACodeSelect) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + + // Implementation + protected: + + // Generated message map functions + //{{AFX_MSG(GSACodeSelect) + afx_msg void OnCancel(); + afx_msg void OnOk(); + afx_msg void OnSelchangeGameList(); + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + + //{{AFX_INSERT_LOCATION}} + // Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_GSACODESELECT_H__189BD94D_288F_4E2A_9395_EAB16F104D87__INCLUDED_) diff --git a/src/win32/GameOverrides.cpp b/src/win32/GameOverrides.cpp new file mode 100644 index 0000000..9f67c3a --- /dev/null +++ b/src/win32/GameOverrides.cpp @@ -0,0 +1,270 @@ +#include "stdafx.h" +#include "vba.h" +#include "GameOverrides.h" +#include "../gba/GBA.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// GameOverrides dialog + + +GameOverrides::GameOverrides(CWnd* pParent /*=NULL*/) + : CDialog(GameOverrides::IDD, pParent) +{ + //{{AFX_DATA_INIT(GameOverrides) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT +} + + +void GameOverrides::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(GameOverrides) + DDX_Control(pDX, IDC_NAME, m_name); + DDX_Control(pDX, IDC_MIRRORING, m_mirroring); + DDX_Control(pDX, IDC_FLASH_SIZE, m_flashSize); + DDX_Control(pDX, IDC_SAVE_TYPE, m_saveType); + DDX_Control(pDX, IDC_RTC, m_rtc); + DDX_Control(pDX, IDC_COMMENT, m_comment); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(GameOverrides, CDialog) + //{{AFX_MSG_MAP(GameOverrides) + ON_BN_CLICKED(IDC_DEFAULTS, OnDefaults) + //}}AFX_MSG_MAP + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// GameOverrides message handlers + +void GameOverrides::OnOK() +{ + char tempName[2048]; + + GetModuleFileName(NULL, tempName, 2048); + + char *p = strrchr(tempName, '\\'); + if(p) + *p = 0; + + char buffer[5]; + strncpy(buffer, (const char *)&rom[0xac], 4); + buffer[4] = 0; + + strcat(tempName, "\\vba-over.ini"); + + char comment[0xFF]; + m_comment.GetWindowText(comment, 0xFF); + WritePrivateProfileString(buffer, "comment", !strncmp(comment, "", 0xFF) ? NULL : comment, tempName); + + int rtc = m_rtc.GetCurSel(); + int flash = m_flashSize.GetCurSel(); + int save = m_saveType.GetCurSel(); + int mirroring = m_mirroring.GetCurSel(); + if(rtc == 0 && flash == 0 && save == 0 && mirroring == 0) + WritePrivateProfileString(buffer, NULL, NULL, tempName); + else { + char *value = NULL; + switch(rtc) { + case 1: + value = "0"; + break; + case 2: + value = "1"; + break; + } + WritePrivateProfileString(buffer, "rtcEnabled", value, tempName); + value = NULL; + switch(flash) { + case 1: + value = "0x10000"; + break; + case 2: + value = "0x20000"; + break; + } + WritePrivateProfileString(buffer, "flashSize", value, tempName); + value = NULL; + switch(save) { + case 1: + value = "0"; + break; + case 2: + value = "1"; + break; + case 3: + value = "2"; + break; + case 4: + value = "3"; + break; + case 5: + value = "4"; + break; + case 6: + value = "5"; + break; + } + WritePrivateProfileString(buffer, "saveType", value, tempName); + value = NULL; + switch(mirroring) { + case 1: + value = "0"; + break; + case 2: + value = "1"; + break; + } + WritePrivateProfileString(buffer, "mirroringEnabled", value, tempName); + } + CDialog::OnOK(); +} + +void GameOverrides::OnDefaults() +{ + m_rtc.SetCurSel(0); + m_flashSize.SetCurSel(0); + m_saveType.SetCurSel(0); + m_mirroring.SetCurSel(0); +} + +void GameOverrides::OnCancel() +{ + CDialog::OnCancel(); +} + +BOOL GameOverrides::OnInitDialog() +{ + CDialog::OnInitDialog(); + + char tempName[2048]; + + const char *rtcValues[] = { + "Default", + "Disabled", + "Enabled" + }; + const char *flashValues[] = { + "Default", + "64K", + "128K" + }; + const char *saveValues[] = { + "Default", + "Automatic", + "EEPROM", + "SRAM", + "Flash", + "EEPROM+Sensor", + "None" + }; + const char *mirroringValues[] = { + "Default", + "Disabled", + "Enabled" + }; + + int i; + + for(i = 0; i < 3; i++) { + m_rtc.AddString(rtcValues[i]); + } + for(i = 0; i < 3; i++) { + m_flashSize.AddString(flashValues[i]); + } + for(i = 0; i < 7; i++) { + m_saveType.AddString(saveValues[i]); + } + for(i = 0; i < 3; i++) { + m_mirroring.AddString(mirroringValues[i]); + } + + GetModuleFileName(NULL, tempName, 2048); + + char *p = strrchr(tempName, '\\'); + if(p) + *p = 0; + + char buffer[5]; + strncpy(buffer, (const char *)&rom[0xac], 4); + buffer[4] = 0; + + strcat(tempName, "\\vba-over.ini"); + + m_name.SetWindowText(buffer); + + char comment[0xFF]; + GetPrivateProfileString(buffer, + "comment", + "", + comment, + 0xFF, + tempName); + m_comment.SetWindowText(comment); + + UINT v = GetPrivateProfileInt(buffer, + "rtcEnabled", + -1, + tempName); + switch(v) { + case 0: + m_rtc.SetCurSel(1); + break; + case 1: + m_rtc.SetCurSel(2); + break; + default: + m_rtc.SetCurSel(0); + } + v = GetPrivateProfileInt(buffer, + "flashSize", + -1, + tempName); + switch(v) { + case 0x10000: + m_flashSize.SetCurSel(1); + break; + case 0x20000: + m_flashSize.SetCurSel(2); + break; + default: + m_flashSize.SetCurSel(0); + } + + v = GetPrivateProfileInt(buffer, + "saveType", + -1, + tempName); + if(v != (UINT)-1 && (v > 5)) + v = (UINT)-1; + if(v != (UINT)-1) + m_saveType.SetCurSel(v+1); + else + m_saveType.SetCurSel(0); + + v = GetPrivateProfileInt(buffer, + "mirroringEnabled", + -1, + tempName); + switch(v) { + case 0: + m_mirroring.SetCurSel(1); + break; + case 1: + m_mirroring.SetCurSel(2); + break; + default: + m_mirroring.SetCurSel(0); + } + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} diff --git a/src/win32/GameOverrides.h b/src/win32/GameOverrides.h new file mode 100644 index 0000000..3fe6b23 --- /dev/null +++ b/src/win32/GameOverrides.h @@ -0,0 +1,74 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2005 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "afxwin.h" +#if !defined(AFX_GAMEOVERRIDES_H__EEEFE37F_F477_455D_8682_705FB2DBCC0C__INCLUDED_) +#define AFX_GAMEOVERRIDES_H__EEEFE37F_F477_455D_8682_705FB2DBCC0C__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// GameOverrides.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// GameOverrides dialog + +class GameOverrides : public CDialog +{ +// Construction +public: + GameOverrides(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(GameOverrides) + enum { IDD = IDD_GAME_OVERRIDES }; + CEdit m_name; + CComboBox m_mirroring; + CComboBox m_flashSize; + CComboBox m_saveType; + CComboBox m_rtc; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(GameOverrides) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(GameOverrides) + virtual void OnOK(); + afx_msg void OnDefaults(); + virtual void OnCancel(); + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +public: + CEdit m_comment; +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_GAMEOVERRIDES_H__EEEFE37F_F477_455D_8682_705FB2DBCC0C__INCLUDED_) diff --git a/src/win32/Hyperlink.cpp b/src/win32/Hyperlink.cpp new file mode 100644 index 0000000..f49fdfe --- /dev/null +++ b/src/win32/Hyperlink.cpp @@ -0,0 +1,97 @@ +#include "stdafx.h" +#include "vba.h" +#include "Hyperlink.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// Hyperlink + +Hyperlink::Hyperlink() +{ + m_over = false; +} + +Hyperlink::~Hyperlink() +{ + m_underlineFont.DeleteObject(); +} + + +BEGIN_MESSAGE_MAP(Hyperlink, CStatic) + //{{AFX_MSG_MAP(Hyperlink) + ON_WM_CTLCOLOR_REFLECT() + ON_WM_ERASEBKGND() + ON_WM_MOUSEMOVE() + //}}AFX_MSG_MAP + ON_CONTROL_REFLECT(STN_CLICKED, OnClicked) +END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// Hyperlink message handlers + +void Hyperlink::PreSubclassWindow() +{ + DWORD dwStyle = GetStyle(); + ::SetWindowLong(GetSafeHwnd(), GWL_STYLE, dwStyle | SS_NOTIFY); + + // 32649 is the hand cursor + m_cursor = LoadCursor(NULL, MAKEINTRESOURCE(32649)); + + CFont *font = GetFont(); + + LOGFONT lg; + font->GetLogFont(&lg); + + lg.lfUnderline = TRUE; + + m_underlineFont.CreateFontIndirect(&lg); + SetFont(&m_underlineFont); + + CStatic::PreSubclassWindow(); +} + +void Hyperlink::OnClicked() +{ + CString url; + GetWindowText(url); + ::ShellExecute(0, _T("open"), url, + 0, 0, SW_SHOWNORMAL); +} + +HBRUSH Hyperlink::CtlColor(CDC* pDC, UINT nCtlColor) +{ + pDC->SetTextColor(RGB(0,0,240)); + + return (HBRUSH)GetStockObject(NULL_BRUSH); +} + +BOOL Hyperlink::OnEraseBkgnd(CDC* pDC) +{ + CRect rect; + GetClientRect(rect); + pDC->FillSolidRect(rect, ::GetSysColor(COLOR_3DFACE)); + + return TRUE; +} + +void Hyperlink::OnMouseMove(UINT nFlags, CPoint point) +{ + if(!m_over) { + m_over = true; + SetCapture(); + ::SetCursor(m_cursor); + } else { + CRect r; + GetClientRect(&r); + + if(!r.PtInRect(point)) { + m_over = false; + ReleaseCapture(); + } + } +} diff --git a/src/win32/Hyperlink.h b/src/win32/Hyperlink.h new file mode 100644 index 0000000..6fcf011 --- /dev/null +++ b/src/win32/Hyperlink.h @@ -0,0 +1,56 @@ +#if !defined(AFX_HYPERLINK_H__BECEAB7D_31FB_4727_A42B_8732162EEBCC__INCLUDED_) +#define AFX_HYPERLINK_H__BECEAB7D_31FB_4727_A42B_8732162EEBCC__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// Hyperlink.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// Hyperlink window + +class Hyperlink : public CStatic +{ +// Construction +public: + Hyperlink(); + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(Hyperlink) + protected: + virtual void PreSubclassWindow(); + //}}AFX_VIRTUAL + +// Implementation +public: + bool m_over; + HCURSOR m_cursor; + afx_msg void OnClicked(); + CFont m_underlineFont; + virtual ~Hyperlink(); + + // Generated message map functions +protected: + //{{AFX_MSG(Hyperlink) + afx_msg HBRUSH CtlColor(CDC* pDC, UINT nCtlColor); + afx_msg BOOL OnEraseBkgnd(CDC* pDC); + afx_msg void OnMouseMove(UINT nFlags, CPoint point); + //}}AFX_MSG + + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_HYPERLINK_H__BECEAB7D_31FB_4727_A42B_8732162EEBCC__INCLUDED_) diff --git a/src/win32/IOViewer.cpp b/src/win32/IOViewer.cpp new file mode 100644 index 0000000..1683ca8 --- /dev/null +++ b/src/win32/IOViewer.cpp @@ -0,0 +1,218 @@ +#include "stdafx.h" +#include "vba.h" +#include "IOViewer.h" + +#include "../System.h" +#include "../gba/GBA.h" +#include "../gba/Globals.h" +#include "../gba/GBAinline.h" +#include "IOViewerRegs.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// IOViewer dialog + + +IOViewer::IOViewer(CWnd* pParent /*=NULL*/) + : ResizeDlg(IOViewer::IDD, pParent) +{ + //{{AFX_DATA_INIT(IOViewer) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT + selected = 0; + autoUpdate = false; +} + + +void IOViewer::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(IOViewer) + DDX_Control(pDX, IDC_VALUE, m_value); + DDX_Control(pDX, IDC_ADDRESSES, m_address); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(IOViewer, CDialog) + //{{AFX_MSG_MAP(IOViewer) + ON_BN_CLICKED(IDC_CLOSE, OnClose) + ON_BN_CLICKED(IDC_REFRESH, OnRefresh) + ON_BN_CLICKED(IDC_AUTO_UPDATE, OnAutoUpdate) + ON_CBN_SELCHANGE(IDC_ADDRESSES, OnSelchangeAddresses) + ON_BN_CLICKED(IDC_APPLY, OnApply) + ON_BN_CLICKED(IDC_BIT_0, bitChange) + ON_BN_CLICKED(IDC_BIT_1, bitChange) + ON_BN_CLICKED(IDC_BIT_2, bitChange) + ON_BN_CLICKED(IDC_BIT_3, bitChange) + ON_BN_CLICKED(IDC_BIT_4, bitChange) + ON_BN_CLICKED(IDC_BIT_5, bitChange) + ON_BN_CLICKED(IDC_BIT_6, bitChange) + ON_BN_CLICKED(IDC_BIT_7, bitChange) + ON_BN_CLICKED(IDC_BIT_8, bitChange) + ON_BN_CLICKED(IDC_BIT_9, bitChange) + ON_BN_CLICKED(IDC_BIT_10, bitChange) + ON_BN_CLICKED(IDC_BIT_11, bitChange) + ON_BN_CLICKED(IDC_BIT_12, bitChange) + ON_BN_CLICKED(IDC_BIT_13, bitChange) + ON_BN_CLICKED(IDC_BIT_14, bitChange) + ON_BN_CLICKED(IDC_BIT_15, bitChange) + //}}AFX_MSG_MAP + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// IOViewer message handlers + +void IOViewer::OnClose() +{ + theApp.winRemoveUpdateListener(this); + + DestroyWindow(); +} + +void IOViewer::bitChange() +{ + CString buffer; + u16 data = 0; + + for(int i = 0; i < 16; i++) { + CButton *pWnd = (CButton *)GetDlgItem(IDC_BIT_0 + i); + + if(pWnd) { + if(pWnd->GetCheck()) + data |= (1 << i); + } + } + + buffer.Format("%04X", data); + m_value.SetWindowText(buffer); +} + +void IOViewer::OnRefresh() +{ + // TODO: Add your control notification handler code here + + update(); +} + +void IOViewer::OnAutoUpdate() +{ + autoUpdate = !autoUpdate; + if(autoUpdate) { + theApp.winAddUpdateListener(this); + } else { + theApp.winRemoveUpdateListener(this); + } +} + +void IOViewer::OnSelchangeAddresses() +{ + selected = m_address.GetCurSel(); + + update(); +} + +void IOViewer::PostNcDestroy() +{ + delete this; +} + +BOOL IOViewer::OnInitDialog() +{ + CDialog::OnInitDialog(); + + // winCenterWindow(getHandle()); + DIALOG_SIZER_START( sz ) + DIALOG_SIZER_END() + SetData(sz, + TRUE, + HKEY_CURRENT_USER, + "Software\\Emulators\\VisualBoyAdvance\\Viewer\\IOView", + NULL); + + CFont *font = CFont::FromHandle((HFONT)GetStockObject(SYSTEM_FIXED_FONT)); + int i; + for(i = 0; i < sizeof(ioViewRegisters)/sizeof(IOData); i++) { + m_address.AddString(ioViewRegisters[i].name); + } + m_address.SetFont(font); + for(i = 0; i < 16; i++) { + GetDlgItem(IDC_BIT_0+i)->SetFont(font); + } + + RECT cbSize; + int Height; + + m_address.GetClientRect(&cbSize); + Height = m_address.GetItemHeight(0); + Height += m_address.GetItemHeight(0) * (10); + + // Note: The use of SM_CYEDGE assumes that we're using Windows '95 + // Now add on the height of the border of the edit box + Height += GetSystemMetrics(SM_CYEDGE) * 2; // top & bottom edges + + // The height of the border of the drop-down box + Height += GetSystemMetrics(SM_CYEDGE) * 2; // top & bottom edges + + // now set the size of the window + m_address.SetWindowPos(NULL, + 0, 0, + cbSize.right, Height, + SWP_NOMOVE | SWP_NOZORDER); + + m_address.SetCurSel(0); + update(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void IOViewer::update() +{ + CString buffer; + + const IOData *sel = &ioViewRegisters[selected]; + u16 data = sel->address ? *sel->address : + (ioMem ? *((u16 *)&ioMem[sel->offset]) : 0); + + for(int i = 0; i < 16; i++) { + CButton *pWnd = (CButton *)GetDlgItem(IDC_BIT_0 + i); + + if(pWnd) { + if(!(sel->write & (1 << i))) + pWnd->EnableWindow(FALSE); + else + pWnd->EnableWindow(TRUE); + pWnd->SetCheck(((data & (1 << i)) >> i)); + buffer.Format("%2d %s", i, sel->bits[i]); + pWnd->SetWindowText(buffer); + } + } + + buffer.Format("%04X", data); + m_value.SetWindowText(buffer); +} + +void IOViewer::OnApply() +{ + if(rom != NULL) + { + const IOData *sel = &ioViewRegisters[selected]; + u16 res = 0; + for(int i = 0; i < 16; i++) { + CButton *pWnd = (CButton *)GetDlgItem(IDC_BIT_0 + i); + + if(pWnd) { + if(pWnd->GetCheck()) + res |= (1 << i); + } + } + CPUWriteHalfWord(0x4000000+sel->offset, res); + update(); + } +} diff --git a/src/win32/IOViewer.h b/src/win32/IOViewer.h new file mode 100644 index 0000000..a6782d1 --- /dev/null +++ b/src/win32/IOViewer.h @@ -0,0 +1,60 @@ +#if !defined(AFX_IOVIEWER_H__9C266B78_FC02_4572_9062_0241802B0E76__INCLUDED_) +#define AFX_IOVIEWER_H__9C266B78_FC02_4572_9062_0241802B0E76__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// IOViewer.h : header file +// + +#include "ResizeDlg.h" +#include "IUpdate.h" + +///////////////////////////////////////////////////////////////////////////// +// IOViewer dialog + +class IOViewer : public ResizeDlg, IUpdateListener +{ + // Construction + public: + void update(); + void bitChange(); + bool autoUpdate; + int selected; + IOViewer(CWnd* pParent = NULL); // standard constructor + + // Dialog Data + //{{AFX_DATA(IOViewer) + enum { IDD = IDD_IO_VIEWER }; + CStatic m_value; + CComboBox m_address; + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(IOViewer) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual void PostNcDestroy(); + //}}AFX_VIRTUAL + + // Implementation + protected: + + // Generated message map functions + //{{AFX_MSG(IOViewer) + afx_msg void OnClose(); + afx_msg void OnRefresh(); + afx_msg void OnAutoUpdate(); + afx_msg void OnSelchangeAddresses(); + virtual BOOL OnInitDialog(); + afx_msg void OnApply(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + + //{{AFX_INSERT_LOCATION}} + // Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_IOVIEWER_H__9C266B78_FC02_4572_9062_0241802B0E76__INCLUDED_) diff --git a/src/win32/IOViewerRegs.h b/src/win32/IOViewerRegs.h new file mode 100644 index 0000000..ba3c27d --- /dev/null +++ b/src/win32/IOViewerRegs.h @@ -0,0 +1,2068 @@ +struct IOData { + u16 *address; + u16 offset; + char *name; + u16 write; + char *bits[16]; +}; + +const IOData ioViewRegisters[] = { + { + &DISPCNT, 0, "0x4000000-DISPCNT", 0xFFF7, + { + "", + "", + "BG Mode (3 bits)", + "CGB Mode", + "Display Frame", + "H-Blank Interval OBJ processing", + "OBJ Character mapping", + "Forced blank", + "BG0", + "BG1", + "BG2", + "BG3", + "OBJ", + "WIN0", + "WIN1", + "OBJWIN" + } + }, + { + &DISPSTAT, 4, "0x4000004-DISPSTAT", 0xFF38, + { + "V-Blank Status", + "H-Blank Status", + "VCOUNT Evaluation", + "V-Blank Interrupt Enable", + "H-Blank Interrupt Enable", + "VCOUNT Match Interrupt Enable", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "VCOUNT setting (8 bits)" + } + }, + { + &VCOUNT, 6, "0x4000006-VCOUNT", 0x0000, + { + "", + "", + "", + "", + "", + "", + "", + "VCOUNT (8 bits)", + "", + "", + "", + "", + "", + "", + "", + "" + } + }, + { + &BG0CNT, 8, "0x4000008-BG0CNT", 0xDFCF, + { + "", + "Priority (2 bits)", + "", + "Char base (2 bits)", + "", + "", + "Mosaic", + "16/256 colors", + "", + "", + "", + "", + "Screen Base Block (5 bits)", + "", + "", + "Size (2 bits)" + } + }, + { + &BG1CNT, 0xA, "0x400000A-BG1CNT", 0xDFCF, + { + "", + "Priority (2 bits)", + "", + "Char base (2 bits)", + "", + "", + "Mosaic", + "16/256 colors", + "", + "", + "", + "", + "Screen Base Block (5 bits)", + "", + "", + "Size (2 bits)" + } + }, + { + &BG2CNT, 0xC, "0x400000C-BG2CNT", 0xFFCF, + { + "", + "Priority (2 bits)", + "", + "Char base (2 bits)", + "", + "", + "Mosaic", + "16/256 colors", + "", + "", + "", + "", + "Screen Base Block (5 bits)", + "Area Overflow", + "", + "Size (2 bits)" + } + }, + { + &BG3CNT, 0xE, "0x400000E-BG3CNT", 0xFFCF, + { + "", + "Priority (2 bits)", + "", + "Char base (2 bits)", + "", + "", + "Mosaic", + "16/256 colors", + "", + "", + "", + "", + "Screen Base Block (5 bits)", + "Area Overflow", + "", + "Size (2 bits)" + } + }, + { + &BG0HOFS, 0x10, "0x4000010-BG0HOFS", 0x01FF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "Horizontal Offset (9 bits, W)", + "", + "", + "", + "", + "", + "", + "" + } + }, + { + &BG0VOFS, 0x12, "0x4000012-BG0VOFS", 0x01FF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "Vertical Offset (9 bits, W)", + "", + "", + "", + "", + "", + "", + "" + } + }, + { + &BG1HOFS, 0x14, "0x4000014-BG1HOFS", 0x01FF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "Horizontal Offset (9 bits, W)", + "", + "", + "", + "", + "", + "", + "" + } + }, + { + &BG1VOFS, 0x16, "0x4000016-BG1VOFS", 0x01FF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "Vertical Offset (9 bits, W)", + "", + "", + "", + "", + "", + "", + "" + } + }, + { + &BG2HOFS, 0x18, "0x4000018-BG2HOFS", 0x01FF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "Horizontal Offset (9 bits, W)", + "", + "", + "", + "", + "", + "", + "" + } + }, + { + &BG2VOFS, 0x1A, "0x400001A-BG2VOFS", 0x01FF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "Vertical Offset (9 bits, W)", + "", + "", + "", + "", + "", + "", + "" + } + }, + { + &BG3HOFS, 0x1C, "0x400001C-BG3HOFS", 0x01FF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "Horizontal Offset (9 bits,W)", + "", + "", + "", + "", + "", + "", + "" + } + }, + { + &BG3VOFS, 0x1E, "0x400001E-BG3VOFS", 0x01FF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "Vertical Offset (9 bits,W)", + "", + "", + "", + "", + "", + "", + "" + } + }, + { + &BG2PA, 0x20, "0x4000020-BG2PA", 0xFFFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "dx (16 bits,W)" + } + }, + { + &BG2PB, 0x22, "0x4000022-BG2PB", 0xFFFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "dmx (16 bits,W)" + } + }, + { + &BG2PC, 0x24, "0x4000024-BG2PC", 0xFFFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "dy (16 bits,W)" + } + }, + { + &BG2PD, 0x26, "0x4000026-BG2PD", 0xFFFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "dmy (16 bits,W)" + } + }, + { + &BG2X_L, 0x28, "0x4000028-BG2X_L", 0xFFFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "X low bits (16 bits,W)" + } + }, + { + &BG2X_H, 0x2A, "0x400002A-BG2X_H", 0x0FFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "X high bits (12 bits,W)", + "", + "", + "", + "", + } + }, + { + &BG2Y_L, 0x2C, "0x400002C-BG2Y_L", 0xFFFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Y low bits (16 bits,W)" + } + }, + { + &BG2Y_H, 0x2E, "0x400002E-BG2Y_H", 0x0FFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Y hight bits (12 bits,W)", + "", + "", + "", + "", + } + }, + { + &BG3PA, 0x30, "0x4000030-BG3PA", 0xFFFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "dx (16 bits,W)" + } + }, + { + &BG3PB, 0x32, "0x4000032-BG3PB", 0xFFFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "dmx (16 bits,W)" + } + }, + { + &BG3PC, 0x34, "0x4000034-BG3PC", 0xFFFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "dy (16 bits,W)" + } + }, + { + &BG3PD, 0x36, "0x4000036-BG3PD", 0xFFFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "dmy (16 bits,W)" + } + }, + { + &BG3X_L, 0x38, "0x4000038-BG3X_L", 0xFFFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "X low bits (16 bits,W)" + } + }, + { + &BG3X_H, 0x3A, "0x400003A-BG3X_H", 0x0FFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "X hight bits (12 bits,W)", + "", + "", + "", + "", + } + }, + { + &BG3Y_L, 0x3C, "0x400003C-BG3Y_L", 0xFFFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Y low bits (16 bits,W)" + } + }, + { + &BG3Y_H, 0x3E, "0x400003E-BG3Y_H", 0x0FFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Y hight bits (12 bits,W)", + "", + "", + "", + "", + } + }, + { + &WIN0H, 0x40, "0x4000040-WIN0H", 0xFFFF, + { + "", + "", + "", + "", + "", + "", + "", + "Win 0 lower-right X (8 bits,W)", + "", + "", + "", + "", + "", + "", + "", + "Win 0 upper-left X (8 bits,W)", + } + }, + { + &WIN1H, 0x42, "0x4000042-WIN1H", 0xFFFF, + { + "", + "", + "", + "", + "", + "", + "", + "Win 1 lower-right X (8 bits,W)", + "", + "", + "", + "", + "", + "", + "", + "Win 1 upper-left (8 bits,W)", + } + }, + { + &WIN0V, 0x44, "0x4000044-WIN0V", 0xFFFF, + { + "", + "", + "", + "", + "", + "", + "", + "Win 0 lower-right Y (8 bits,W)", + "", + "", + "", + "", + "", + "", + "", + "Win 0 upper-left Y (8 bits,W)", + } + }, + { + &WIN1V, 0x46, "0x4000046-WIN1V", 0xFFFF, + { + "", + "", + "", + "", + "", + "", + "", + "Win 1 lower-right Y (8 bits,W)", + "", + "", + "", + "", + "", + "", + "", + "Win 1 upper-left Y (8 bits,W)", + } + }, + { + &WININ, 0x48, "0x4000048-WININ", 0x3F3F, + { + "WIN0 BG0", + "WIN0 BG1", + "WIN0 BG2", + "WIN0 BG3", + "WIN0 OBJ", + "WIN0 Special FX", + "", + "", + "WIN1 BG0", + "WIN1 BG1", + "WIN1 BG2", + "WIN1 BG3", + "WIN1 OBJ", + "WIN1 Special FX", + "", + "", + } + }, + { + &WINOUT, 0x4A, "0x400004A-WINOUT", 0x3F3F, + { + "WIN0/1 BG0", + "WIN0/1 BG1", + "WIN0/1 BG2", + "WIN0/1 BG3", + "WIN0/1 OBJ", + "WIN0/1 Special FX", + "", + "", + "OBJWIN BG0", + "OBJWIN BG1", + "OBJWIN BG2", + "OBJWIN BG3", + "OBJWIN OBJ", + "OBJWIN Special FX", + "", + "", + } + }, + { + &MOSAIC, 0x4C, "0x400004C-MOSAIC", 0xFFFF, + { + "", + "", + "", + "BG H Size (4 bits,W)", + "", + "", + "", + "BG V Size (4 bits,W)", + "", + "", + "", + "OBJ H Size (4 bits,W)", + "", + "", + "", + "OBJ V Size (4 bits,W)", + } + }, + { + &BLDMOD, 0x50, "0x4000050-BLDMOD", 0x3FFF, + { + "1st BG0", + "1st BG1", + "1st BG2", + "1st BG3", + "1st OBJ", + "1st BD", + "", + "FX Type (2 bits)", + "2nd BG0", + "2nd BG1", + "2nd BG2", + "2nd BG3", + "2nd OBJ", + "2nd BD", + "", + "", + } + }, + { + &COLEV, 0x52, "0x4000052-COLEV", 0x1F1F, + { + "", + "", + "", + "", + "Coefficient EVA (5 bits,W)", + "", + "", + "", + "", + "", + "", + "", + "Coefficient EVB (5 bits,W)", + "", + "", + "", + } + }, + { + &COLY, 0x54, "0x4000054-COLEY", 0x001F, + { + "", + "", + "", + "", + "Coefficient EVY (5 bits,W)", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + } + }, + { + NULL, 0x60, "0x4000060-SG10_L", 0x007F, + { + "", + "", + "Sweep Shifts (3 bits)", + "Sweep addition/decrease", + "", + "", + "Sweep Time (3 bits)", + "", + "", + "", + "", + "", + "", + "", + "", + "", + } + }, + { + NULL, 0x62, "0x4000062-SG10_H", 0xFFFF, + { + "", + "", + "", + "", + "", + "Sound Length (6 bits,W)", + "", + "Waveform Type (2 bits)", + "", + "", + "Envelope Steps (3 bits)", + "Envelope Attenuate/Amplify", + "", + "", + "", + "Envelope Initial Value", + } + }, + { + NULL, 0x64, "0x4000064-SG11", 0xC7FF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Frequency (11 bits,W)", + "", + "", + "", + "Sound Continuous/Counter", + "Initialization (W)", + } + }, + { + NULL, 0x68, "0x4000068-SG20", 0xFFFF, + { + "", + "", + "", + "", + "", + "Sound Length (6 bits,W)", + "", + "Waveform Type (2 bits)", + "", + "", + "Envelope Steps (3 bits)", + "Envelope Attenuate/Amplify", + "", + "", + "", + "Envelope Initial Value", + } + }, + { + NULL, 0x6C, "0x400006C-SG21", 0xC7FF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Frequency (11 bits,W)", + "", + "", + "", + "Sound Continuous/Counter", + "Initialization (W)", + } + }, + { + NULL, 0x70, "0x4000070-SG30_L", 0x00E0, + { + "", + "", + "", + "", + "", + "Waveform 32/64 Steps", + "Waveform Bank 0/1", + "Sound Output", + "", + "", + "", + "", + "", + "", + "", + "", + } + }, + { + NULL, 0x72, "0x4000072-SG30_H", 0xE0FF, + { + "", + "", + "", + "", + "", + "", + "", + "Sound Length (8 bits,W)", + "", + "", + "", + "", + "", + "", + "Output Level (2 bits)", + "Forced 3/4 Output Level", + } + }, + { + NULL, 0x74, "0x4000074-SG31", 0xC7FF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Frequency (11 bits,W)", + "", + "", + "", + "Sound Continuous/Counter", + "Initialization (W)", + } + }, + { + NULL, 0x78, "0x4000078-SG40", 0xFF3F, + { + "", + "", + "", + "", + "", + "Sound Length (6 bits,W)", + "", + "", + "", + "", + "Envelope Steps (3 bits)", + "Envelope Attenuate/Amplify", + "", + "", + "", + "Envelope Initial Value", + } + }, + { + NULL, 0x7C, "0x400007C-SG41", 0xC0FF, + { + "", + "", + "Dividing Ratio Freq. (3 bits)", + "Counter 15/7 Steps", + "", + "", + "", + "Counter Shift Clock (4 bits)", + "", + "", + "", + "", + "", + "", + "Sound Continuous/Counter", + "Initialization (W)", + } + }, + { + NULL, 0x80, "0x4000080-SGCNT0_L", 0xFF77, + { + "", + "", + "Right Volume (3 bits)", + "", + "", + "", + "Left Volume (3 bits)", + "", + "Channel 1->Right", + "Channel 2->Right", + "Channel 3->Right", + "Channel 4->Right", + "Channel 1->Left", + "Channel 2->Left", + "Channel 3->Left", + "Channel 4->Left", + } + }, + { + NULL, 0x82, "0x4000082-SGCNT0_H", 0xFF0F, + { + "", + "Sound 1-4 Volume (2 bits)", + "DMA Sound A Volume", + "DMA Sound B Volume", + "", + "", + "", + "", + "DMA Sound A->Right", + "DMA Sound A->Left", + "DMA Sound A Timer", + "DMA Sound A Reset FIFO", + "DMA Sound B->Right", + "DMA Sound B->Left", + "DMA Sound B Timer", + "DMA Sound B Reset FIFO", + } + }, + { + NULL, 0x84, "0x4000084-SGCNT1", 0x0080, + { + "Sound 1 On", + "Sound 2 On", + "Sound 3 On", + "Sound 4 On", + "", + "", + "", + "Master Sound Enable", + "", + "", + "", + "", + "", + "", + "", + "", + } + }, + { + NULL, 0x88, "0x4000088-SGBIAS", 0xC3FF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Bias Level (10 bits)", + "", + "", + "", + "", + "", + "Sampling Rate (2 bits)", + } + }, + { + NULL, 0xA0, "0x40000A0-SIGFIFOA_L", 0xFFFF, + { + "", + "", + "", + "", + "", + "", + "", + "Data 0 (8 bits)", + "", + "", + "", + "", + "", + "", + "", + "Data 1 (8 bits)", + } + }, + { + NULL, 0xA2, "0x40000A2-SIGFIFOA_H", 0xFFFF, + { + "", + "", + "", + "", + "", + "", + "", + "Data 2 (8 bits)", + "", + "", + "", + "", + "", + "", + "", + "Data 3 (8 bits)", + } + }, + { + NULL, 0xA4, "0x40000A4-SIGFIFOB_L", 0xFFFF, + { + "", + "", + "", + "", + "", + "", + "", + "Data 0 (8 bits)", + "", + "", + "", + "", + "", + "", + "", + "Data 1 (8 bits)", + } + }, + { + NULL, 0xA6, "0x40000A6-SIGFIFOB_H", 0xFFFF, + { + "", + "", + "", + "", + "", + "", + "", + "Data 2 (8 bits)", + "", + "", + "", + "", + "", + "", + "", + "Data 3 (8 bits)", + } + }, + { + &DM0SAD_L, 0xB0, "0x40000B0-DM0SAD_L", 0xFFFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Source Address (lower 16 bits)", + } + }, + { + &DM0SAD_H, 0xB2, "0x40000B2-DM0SAD_H", 0x07FF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Source Address (upper 11 bits)", + "", + "", + "", + "", + "", + } + }, + { + &DM0DAD_L, 0xB4, "0x40000B4-DM0DAD_L", 0xFFFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Destination Address (lower 16 bits)", + } + }, + { + &DM0DAD_H, 0xB6, "0x40000B6-DM0DAD_H", 0x07FF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Destination Address (upper 11 bits)", + "", + "", + "", + "", + "", + } + }, + { + &DM0CNT_L, 0xB8, "0x40000B8-DM0CNT_L", 0x3FFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Count (14 bits)", + "", + "", + } + }, + { + &DM0CNT_H, 0xBA, "0x40000BA-DM0CNT_H", 0xF7E0, + { + "", + "", + "", + "", + "", + "", + "Destination Address Control (2 bits)", + "", + "Source Address Control (2 bits)", + "Repeat", + "Transfer Type", + "", + "", + "Start Timing (2 bits)", + "Interrupt Request", + "Enable", + } + }, + { + &DM1SAD_L, 0xBC, "0x40000BC-DM1SAD_L", 0xFFFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Source Address (lower 16 bits)", + } + }, + { + &DM1SAD_H, 0xBE, "0x40000BE-DM1SAD_H", 0x0FFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Source Address (upper 12 bits)", + "", + "", + "", + "", + } + }, + { + &DM1DAD_L, 0xC0, "0x40000C0-DM1DAD_L", 0xFFFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Destination Address (lower 16 bits)", + } + }, + { + &DM1DAD_H, 0xC2, "0x40000C2-DM1DAD_H", 0x07FF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Destination Address (upper 11 bits)", + "", + "", + "", + "", + "", + } + }, + { + &DM1CNT_L, 0xC4, "0x40000C4-DM1CNT_L", 0x3FFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Count (14 bits)", + "", + "", + } + }, + { + &DM1CNT_H, 0xC6, "0x40000C6-DM1CNT_H", 0xF7E0, + { + "", + "", + "", + "", + "", + "", + "Destination Address Control (2 bits)", + "", + "Source Address Control (2 bits)", + "Repeat", + "Transfer Type", + "", + "", + "Start Timing (2 bits)", + "Interrupt Request", + "Enable", + } + }, + { + &DM2SAD_L, 0xC8, "0x40000C8-DM2SAD_L", 0xFFFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Source Address (lower 16 bits)", + } + }, + { + &DM2SAD_H, 0xCA, "0x40000CA-DM2SAD_H", 0x0FFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Source Address (upper 12 bits)", + "", + "", + "", + "", + } + }, + { + &DM2DAD_L, 0xCC, "0x40000CC-DM2DAD_L", 0xFFFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Destination Address (lower 16 bits)", + } + }, + { + &DM2DAD_H, 0xCE, "0x40000CE-DM2DAD_H", 0x07FF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Destination Address (upper 11 bits)", + "", + "", + "", + "", + "", + } + }, + { + &DM2CNT_L, 0xD0, "0x40000D0-DM2CNT_L", 0x3FFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Count (14 bits)", + "", + "", + } + }, + { + &DM2CNT_H, 0xD2, "0x40000D2-DM2CNT_H", 0xF7E0, + { + "", + "", + "", + "", + "", + "", + "Destination Address Control (2 bits)", + "", + "Source Address Control (2 bits)", + "Repeat", + "Transfer Type", + "", + "", + "Start Timing (2 bits)", + "Interrupt Request", + "Enable", + } + }, + { + &DM3SAD_L, 0xD4, "0x40000D4-DM3SAD_L", 0xFFFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Source Address (lower 16 bits)", + } + }, + { + &DM3SAD_H, 0xD6, "0x40000D6-DM3SAD_H", 0x0FFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Source Address (upper 12 bits)", + "", + "", + "", + "", + } + }, + { + &DM3DAD_L, 0xD8, "0x40000D8-DM3DAD_L", 0xFFFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Destination Address (lower 16 bits)", + } + }, + { + &DM3DAD_H, 0xDA, "0x40000DA-DM3DAD_H", 0x0FFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Destination Address (upper 12 bits)", + "", + "", + "", + "", + } + }, + { + &DM3CNT_L, 0xDC, "0x40000DC-DM3CNT_L", 0xFFFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Count (16 bits)", + } + }, + { + &DM3CNT_H, 0xDE, "0x40000DE-DM3CNT_H", 0xFFE0, + { + "", + "", + "", + "", + "", + "", + "Destination Address Control (2 bits)", + "", + "Source Address Control (2 bits)", + "Repeat", + "Transfer Type", + "Game Pak Data Request", + "", + "Start Timing (2 bits)", + "Interrupt Request", + "Enable", + } + }, + { + &TM0D, 0x100, "0x4000100-TM0D", 0xFFFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Timer Counter (16 bits)", + } + }, + { + &TM0CNT, 0x102, "0x4000102-TM0CNT", 0x00C7, + { + "", + "Scalar Selection (2 bits)", + "Count Up", + "", + "", + "", + "Interrupt Request", + "Enable", + "", + "", + "", + "", + "", + "", + "", + "", + } + }, + { + &TM1D, 0x104, "0x4000104-TM1D", 0xFFFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Timer Counter (16 bits)", + } + }, + { + &TM1CNT, 0x106, "0x4000106-TM1CNT", 0x00C7, + { + "", + "Scalar Selection (2 bits)", + "Count Up", + "", + "", + "", + "Interrupt Request", + "Enable", + "", + "", + "", + "", + "", + "", + "", + "", + } + }, + { + &TM2D, 0x108, "0x4000108-TM2D", 0xFFFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Timer Counter (16 bits)", + } + }, + { + &TM2CNT, 0x10A, "0x400010A-TM2CNT", 0x00C7, + { + "", + "Scalar Selection (2 bits)", + "Count Up", + "", + "", + "", + "Interrupt Request", + "Enable", + "", + "", + "", + "", + "", + "", + "", + "", + } + }, + { + &TM3D, 0x10C, "0x400010C-TM3D", 0xFFFF, + { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Timer Counter (16 bits)", + } + }, + { + &TM3CNT, 0x10E, "0x400010E-TM3CNT", 0x00C7, + { + "", + "Scalar Selection (2 bits)", + "Count Up", + "", + "", + "", + "Interrupt Request", + "Enable", + "", + "", + "", + "", + "", + "", + "", + "", + } + }, + { + &P1, 0x130, "0x4000130-P1", 0x03FF, + { + "A", + "B", + "Select", + "Start", + "Right", + "Left", + "Up", + "Down", + "Shoulder Right", + "Shoulder Left", + "", + "", + "", + "", + "", + "", + } + }, + { + NULL, 0x132, "0x4000132-P1CNT", 0xC3FF, + { + "A", + "B", + "Select", + "Start", + "Right", + "Left", + "Up", + "Down", + "Shoulder Right", + "Shoulder Left", + "", + "", + "", + "", + "Interrupt Request", + "Interrupt Condition", + } + }, + { + &IE, 0x200, "0x4000200-IE", 0x3FFF, + { + "VBlank", + "HBlank", + "VCount", + "Timer 0", + "Timer 1", + "Timer 2", + "Timer 3", + "Serial", + "DMA 0", + "DMA 1", + "DMA 2", + "DMA 3", + "Keypad", + "Game Pak", + "", + "", + } + }, + { + &IF, 0x202, "0x4000202-IF", 0x0000, + { + "VBlank", + "HBlank", + "VCount", + "Timer 0", + "Timer 1", + "Timer 2", + "Timer 3", + "Serial", + "DMA 0", + "DMA 1", + "DMA 2", + "DMA 3", + "Keypad", + "Game Pak", + "", + "", + } + }, + { + NULL, 0x204, "0x4000204-WAITCNT", 0x5FFF, + { + "", + "SRAM Wait Control (2 bits)", + "", + "Wait State 0 First Access (2 bits)", + "Wait State 0 Second Access", + "", + "Wait State 1 First Access (2 bits)", + "Wait State 1 Second Access", + "", + "Wait State 2 First Access (2 bits)", + "Wait State 2 Second Access", + "", + "PHI Terminal Output (2 bits)", + "", + "Game Pak Prefetch Buffer", + "Game Pak Type Flag", + } + }, + { + &IME, 0x208, "0x4000208-IME", 0x0001, + { + "Master Interrupt Enable", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + } + }, + { + NULL, 0x300, "0x4000300-HALTCNT", 0x8001, + { + "First Boot", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Power Down", + } + }, +}; diff --git a/src/win32/IUpdate.h b/src/win32/IUpdate.h new file mode 100644 index 0000000..a11a266 --- /dev/null +++ b/src/win32/IUpdate.h @@ -0,0 +1,8 @@ +#ifndef VBA_WIN32_IUPDATE_H +#define VBA_WIN32_IUPDATE_H + +class IUpdateListener { + public: + virtual void update()=0; +}; +#endif diff --git a/src/win32/Input.h b/src/win32/Input.h new file mode 100644 index 0000000..7fe3c5f --- /dev/null +++ b/src/win32/Input.h @@ -0,0 +1,46 @@ +#pragma once + +#include "../System.h" + +#define JOYCONFIG_MESSAGE (WM_USER + 1000) +#define JOYPADS 4 +#define MOTION_KEYS 4 +#define KEYS_PER_PAD 13 +#define MOTION(i) ((JOYPADS*KEYS_PER_PAD)+i) +#define JOYPAD(i,j) ((i*KEYS_PER_PAD)+j) + +#define DEVICEOF(key) (key >> 8) +#define KEYOF(key) (key & 255) + +typedef CList KeyList; + +enum { + KEY_LEFT, KEY_RIGHT, + KEY_UP, KEY_DOWN, + KEY_BUTTON_A, KEY_BUTTON_B, + KEY_BUTTON_START, KEY_BUTTON_SELECT, + KEY_BUTTON_L, KEY_BUTTON_R, + KEY_BUTTON_SPEED, KEY_BUTTON_CAPTURE, + KEY_BUTTON_GS +}; + +class Input { + + public: + KeyList joypaddata[JOYPADS * KEYS_PER_PAD + MOTION_KEYS]; + + Input() {}; + virtual ~Input() {}; + + virtual bool initialize() = 0; + + virtual bool readDevices() = 0; + virtual u32 readDevice(int which) = 0; + virtual CString getKeyName(LONG_PTR key) = 0; + virtual void checkKeys() = 0; + virtual void checkMotionKeys() = 0; + virtual void checkDevices() = 0; + virtual void activate() = 0; + virtual void loadSettings() = 0; + virtual void saveSettings() = 0; +}; diff --git a/src/win32/JoybusOptions.cpp b/src/win32/JoybusOptions.cpp new file mode 100644 index 0000000..fe3e1c7 --- /dev/null +++ b/src/win32/JoybusOptions.cpp @@ -0,0 +1,80 @@ +#ifndef NO_LINK + +#include "stdafx.h" +#include "vba.h" +#include "JoybusOptions.h" +#include "../gba/GBALink.h" + +// JoybusOptions dialog + +IMPLEMENT_DYNAMIC(JoybusOptions, CDialog) + +JoybusOptions::JoybusOptions(CWnd* pParent /*=NULL*/) + : CDialog(JoybusOptions::IDD, pParent) +{ +} + +JoybusOptions::~JoybusOptions() +{ +} + +void JoybusOptions::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + DDX_Control(pDX, IDC_JOYBUS_ENABLE, enable_check); + DDX_Control(pDX, IDC_JOYBUS_HOSTNAME, hostname); +} + +BEGIN_MESSAGE_MAP(JoybusOptions, CDialog) + ON_BN_CLICKED(IDC_JOYBUS_ENABLE, &JoybusOptions::OnBnClickedJoybusEnable) + ON_BN_CLICKED(IDOK, &JoybusOptions::OnBnClickedOk) +END_MESSAGE_MAP() + +BOOL JoybusOptions::OnInitDialog() +{ + CDialog::OnInitDialog(); + + enable_check.SetCheck(gba_joybus_enabled ? BST_CHECKED : BST_UNCHECKED); + + hostname.EnableWindow(enable_check.GetCheck() == BST_CHECKED); + + hostname.SetWindowText(joybusHostAddr.ToString().c_str()); + + return TRUE; +} + +void JoybusOptions::OnBnClickedJoybusEnable() +{ + hostname.EnableWindow(enable_check.GetCheck() == BST_CHECKED); +} + +void JoybusOptions::OnBnClickedOk() +{ + if ( (hostname.GetWindowTextLength() == 0) + && (enable_check.GetCheck() == BST_CHECKED) ) + { + hostname.SetWindowText("Enter IP or Hostname"); + return; + } + + gba_joybus_enabled = enable_check.GetCheck() == BST_CHECKED; + + CString address; + hostname.GetWindowText(address); + + sf::IPAddress new_server; + new_server = std::string(address); + + if (!new_server.IsValid()) + { + hostname.SetWindowText("Enter IP or Hostname"); + return; + } + + joybusHostAddr = new_server; + JoyBusConnect(); + + OnOK(); +} + +#endif // NO_LINK diff --git a/src/win32/JoybusOptions.h b/src/win32/JoybusOptions.h new file mode 100644 index 0000000..bfbda83 --- /dev/null +++ b/src/win32/JoybusOptions.h @@ -0,0 +1,27 @@ +#pragma once +#include "afxwin.h" + +// JoybusOptions dialog + +class JoybusOptions : public CDialog +{ + DECLARE_DYNAMIC(JoybusOptions) + +public: + JoybusOptions(CWnd* pParent = NULL); // standard constructor + virtual ~JoybusOptions(); + +// Dialog Data + enum { IDD = IDD_JOYBUS_DIALOG }; + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + + DECLARE_MESSAGE_MAP() +public: + virtual BOOL OnInitDialog(); + afx_msg void OnBnClickedJoybusEnable(); + CButton enable_check; + CEdit hostname; + afx_msg void OnBnClickedOk(); +}; diff --git a/src/win32/Joypad.cpp b/src/win32/Joypad.cpp new file mode 100644 index 0000000..bc80dec --- /dev/null +++ b/src/win32/Joypad.cpp @@ -0,0 +1,431 @@ +#include "stdafx.h" +#include "vba.h" +#include "Joypad.h" +#include "Input.h" + + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +BOOL bAppendMode; + +void AssignKey(KeyList &Key, LONG_PTR Out) +{ + if( !bAppendMode ) { + Key.RemoveAll(); + } else { + POSITION pos = Key.GetHeadPosition(); + if( pos != NULL ) { + // the list is not empty + while( true ) { + // we don't want to assign the same key twice + if( Key.GetAt( pos ) == Out ) return; + if( pos == Key.GetTailPosition() ) break; + Key.GetNext( pos ); + } + } + } + Key.AddTail(Out); +} + + +CString GetKeyListName(KeyList& Keys) +{ + CString txtKeys; + + POSITION p = Keys.GetHeadPosition(); + while(p!=NULL) + { + txtKeys+=theApp.input->getKeyName(Keys.GetNext(p)); + if (p!=NULL) + txtKeys+=_T(", "); + } + return txtKeys; +} + +void CopyKeys(KeyList &Out, KeyList &In) +{ + Out.RemoveAll(); + POSITION p = In.GetHeadPosition(); + while(p!=NULL) + Out.AddTail(In.GetNext(p)); +} + +#define AssignKeys(in, out) CopyKeys(out, in); + +///////////////////////////////////////////////////////////////////////////// +// JoypadEditControl + +JoypadEditControl::JoypadEditControl() +{ +} + +JoypadEditControl::~JoypadEditControl() +{ +} + + +BEGIN_MESSAGE_MAP(JoypadEditControl, CEdit) + ON_MESSAGE(JOYCONFIG_MESSAGE, OnJoyConfig) + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// JoypadEditControl message handlers + + +LRESULT JoypadEditControl::OnJoyConfig(WPARAM wParam, LPARAM lParam) +{ + AssignKey(m_Keys, ((wParam<<8)|lParam)); + SetWindowText(GetKeyListName(m_Keys)); + GetParent()->GetNextDlgTabItem(this, FALSE)->SetFocus(); + return TRUE; +} + + +BOOL JoypadEditControl::PreTranslateMessage(MSG *pMsg) +{ + if(pMsg->message == WM_KEYDOWN && (pMsg->wParam == VK_ESCAPE || pMsg->wParam == VK_RETURN)) + return TRUE; + + return CEdit::PreTranslateMessage(pMsg); +} + +///////////////////////////////////////////////////////////////////////////// +// JoypadConfig dialog + + +JoypadConfig::JoypadConfig(int w, CWnd* pParent /*=NULL*/) + : CDialog(JoypadConfig::IDD, pParent) +{ + //{{AFX_DATA_INIT(JoypadConfig) + //}}AFX_DATA_INIT + timerId = 0; + which = w; + if(which < 0 || which > 3) + which = 0; +} + +void JoypadConfig::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(JoypadConfig) + DDX_Control(pDX, IDC_EDIT_UP, up); + DDX_Control(pDX, IDC_EDIT_SPEED, speed); + DDX_Control(pDX, IDC_EDIT_RIGHT, right); + DDX_Control(pDX, IDC_EDIT_LEFT, left); + DDX_Control(pDX, IDC_EDIT_DOWN, down); + DDX_Control(pDX, IDC_EDIT_CAPTURE, capture); + DDX_Control(pDX, IDC_EDIT_BUTTON_START, buttonStart); + DDX_Control(pDX, IDC_EDIT_BUTTON_SELECT, buttonSelect); + DDX_Control(pDX, IDC_EDIT_BUTTON_R, buttonR); + DDX_Control(pDX, IDC_EDIT_BUTTON_L, buttonL); + DDX_Control(pDX, IDC_EDIT_BUTTON_GS, buttonGS); + DDX_Control(pDX, IDC_EDIT_BUTTON_B, buttonB); + DDX_Control(pDX, IDC_EDIT_BUTTON_A, buttonA); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(JoypadConfig, CDialog) + ON_BN_CLICKED(ID_CANCEL, OnCancel) + ON_BN_CLICKED(ID_OK, OnOk) + ON_WM_CHAR() + ON_WM_DESTROY() + ON_WM_TIMER() + ON_WM_KEYDOWN() + ON_BN_CLICKED(IDC_APPENDMODE, &JoypadConfig::OnBnClickedAppendmode) + ON_BN_CLICKED(IDC_CLEAR_ALL, &JoypadConfig::OnBnClickedClearAll) +END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// JoypadConfig message handlers + +void JoypadConfig::OnCancel() +{ + EndDialog(FALSE); +} + +void JoypadConfig::OnOk() +{ + AssignKeys(up.m_Keys,theApp.input->joypaddata[JOYPAD(which,KEY_UP)]); + AssignKeys(speed.m_Keys,theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_SPEED)]); + AssignKeys(right.m_Keys,theApp.input->joypaddata[JOYPAD(which,KEY_RIGHT)]); + AssignKeys(left.m_Keys,theApp.input->joypaddata[JOYPAD(which,KEY_LEFT)]); + AssignKeys(down.m_Keys,theApp.input->joypaddata[JOYPAD(which,KEY_DOWN)]); + AssignKeys(capture.m_Keys,theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_CAPTURE)]); + AssignKeys(buttonStart.m_Keys,theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_START)]); + AssignKeys(buttonSelect.m_Keys,theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_SELECT)]); + AssignKeys(buttonR.m_Keys,theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_R)]); + AssignKeys(buttonL.m_Keys,theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_L)]); + AssignKeys(buttonGS.m_Keys,theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_GS)]); + AssignKeys(buttonB.m_Keys,theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_B)]); + AssignKeys(buttonA.m_Keys,theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_A)]); + + theApp.input->checkKeys(); + EndDialog(TRUE); +} + +void JoypadConfig::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) +{ +} + +void JoypadConfig::OnDestroy() +{ + CDialog::OnDestroy(); + + KillTimer(timerId); +} + +void JoypadConfig::OnTimer(UINT_PTR nIDEvent) +{ + theApp.input->checkDevices(); + + CDialog::OnTimer(nIDEvent); +} + +BOOL JoypadConfig::OnInitDialog() +{ + CDialog::OnInitDialog(); + + bAppendMode = FALSE; + + timerId = SetTimer(0,50,NULL); + + CopyKeys(up.m_Keys,theApp.input->joypaddata[JOYPAD(which,KEY_UP)]); + CopyKeys(speed.m_Keys,theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_SPEED)]); + CopyKeys(right.m_Keys,theApp.input->joypaddata[JOYPAD(which,KEY_RIGHT)]); + CopyKeys(left.m_Keys,theApp.input->joypaddata[JOYPAD(which,KEY_LEFT)]); + CopyKeys(down.m_Keys,theApp.input->joypaddata[JOYPAD(which,KEY_DOWN)]); + CopyKeys(capture.m_Keys,theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_CAPTURE)]); + CopyKeys(buttonStart.m_Keys,theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_START)]); + CopyKeys(buttonSelect.m_Keys,theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_SELECT)]); + CopyKeys(buttonR.m_Keys,theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_R)]); + CopyKeys(buttonL.m_Keys,theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_L)]); + CopyKeys(buttonGS.m_Keys,theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_GS)]); + CopyKeys(buttonB.m_Keys,theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_B)]); + CopyKeys(buttonA.m_Keys,theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_A)]); + + up.SetWindowText(GetKeyListName(theApp.input->joypaddata[JOYPAD(which,KEY_UP)])); + down.SetWindowText(GetKeyListName(theApp.input->joypaddata[JOYPAD(which,KEY_DOWN)])); + left.SetWindowText(GetKeyListName(theApp.input->joypaddata[JOYPAD(which,KEY_LEFT)])); + right.SetWindowText(GetKeyListName(theApp.input->joypaddata[JOYPAD(which,KEY_RIGHT)])); + buttonA.SetWindowText(GetKeyListName(theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_A)])); + buttonB.SetWindowText(GetKeyListName(theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_B)])); + buttonL.SetWindowText(GetKeyListName(theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_L)])); + buttonR.SetWindowText(GetKeyListName(theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_R)])); + buttonSelect.SetWindowText(GetKeyListName(theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_SELECT)])); + buttonStart.SetWindowText(GetKeyListName(theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_START)])); + speed.SetWindowText(GetKeyListName(theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_SPEED)])); + capture.SetWindowText(GetKeyListName(theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_CAPTURE)])); + buttonGS.SetWindowText(GetKeyListName(theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_GS)])); + + CenterWindow(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void JoypadConfig::assignKey(int id, LONG_PTR key) +{ + switch(id) { + case IDC_EDIT_LEFT: + AssignKey(theApp.input->joypaddata[JOYPAD(which,KEY_LEFT)],key); + break; + case IDC_EDIT_RIGHT: + AssignKey(theApp.input->joypaddata[JOYPAD(which,KEY_RIGHT)],key); + break; + case IDC_EDIT_UP: + AssignKey(theApp.input->joypaddata[JOYPAD(which,KEY_UP)],key); + break; + case IDC_EDIT_SPEED: + AssignKey(theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_SPEED)],key); + break; + case IDC_EDIT_CAPTURE: + AssignKey(theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_CAPTURE)],key); + break; + case IDC_EDIT_DOWN: + AssignKey(theApp.input->joypaddata[JOYPAD(which,KEY_DOWN)],key); + break; + case IDC_EDIT_BUTTON_A: + AssignKey(theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_A)],key); + break; + case IDC_EDIT_BUTTON_B: + AssignKey(theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_B)],key); + break; + case IDC_EDIT_BUTTON_L: + AssignKey(theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_L)],key); + break; + case IDC_EDIT_BUTTON_R: + AssignKey(theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_R)],key); + break; + case IDC_EDIT_BUTTON_START: + AssignKey(theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_START)],key); + break; + case IDC_EDIT_BUTTON_SELECT: + AssignKey(theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_SELECT)],key); + break; + case IDC_EDIT_BUTTON_GS: + AssignKey(theApp.input->joypaddata[JOYPAD(which,KEY_BUTTON_GS)],key); + break; + } +} + + +///////////////////////////////////////////////////////////////////////////// +// MotionConfig dialog + + +MotionConfig::MotionConfig(CWnd* pParent /*=NULL*/) + : CDialog(MotionConfig::IDD, pParent) +{ + //{{AFX_DATA_INIT(MotionConfig) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT + timerId = 0; + bAppendMode = 0; +} + + +void MotionConfig::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(MotionConfig) + DDX_Control(pDX, IDC_EDIT_UP, up); + DDX_Control(pDX, IDC_EDIT_RIGHT, right); + DDX_Control(pDX, IDC_EDIT_LEFT, left); + DDX_Control(pDX, IDC_EDIT_DOWN, down); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(MotionConfig, CDialog) + ON_BN_CLICKED(ID_CANCEL, OnCancel) + ON_BN_CLICKED(ID_OK, OnOk) + ON_WM_DESTROY() + ON_WM_KEYDOWN() + ON_WM_TIMER() + ON_BN_CLICKED(IDC_APPENDMODE, &MotionConfig::OnBnClickedAppendmode) +END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// MotionConfig message handlers + +void MotionConfig::OnCancel() +{ + EndDialog(FALSE); +} + +void MotionConfig::OnOk() +{ + assignKeys(); + theApp.input->checkKeys(); + EndDialog( TRUE); +} + +void MotionConfig::OnDestroy() +{ + CDialog::OnDestroy(); + + KillTimer(timerId); +} + +BOOL MotionConfig::OnInitDialog() +{ + CDialog::OnInitDialog(); + + timerId = SetTimer(0,50,NULL); + + CopyKeys(up.m_Keys, theApp.input->joypaddata[MOTION(KEY_UP)]); + up.SetWindowText(GetKeyListName(theApp.input->joypaddata[MOTION(KEY_UP)])); + + CopyKeys(down.m_Keys, theApp.input->joypaddata[MOTION(KEY_DOWN)]); + down.SetWindowText(GetKeyListName(theApp.input->joypaddata[MOTION(KEY_DOWN)])); + + CopyKeys(left.m_Keys, theApp.input->joypaddata[MOTION(KEY_LEFT)]); + left.SetWindowText(GetKeyListName(theApp.input->joypaddata[MOTION(KEY_LEFT)])); + + CopyKeys(right.m_Keys, theApp.input->joypaddata[MOTION(KEY_RIGHT)]); + right.SetWindowText(GetKeyListName(theApp.input->joypaddata[MOTION(KEY_RIGHT)])); + + CenterWindow(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void MotionConfig::OnTimer(UINT_PTR nIDEvent) +{ + theApp.input->checkDevices(); + + CDialog::OnTimer(nIDEvent); +} + +void MotionConfig::assignKey(int id, int key) +{ + switch(id) { + case IDC_EDIT_LEFT: + AssignKey(theApp.input->joypaddata[MOTION(KEY_LEFT)],key); + break; + case IDC_EDIT_RIGHT: + AssignKey(theApp.input->joypaddata[MOTION(KEY_RIGHT)],key); + break; + case IDC_EDIT_UP: + AssignKey(theApp.input->joypaddata[MOTION(KEY_UP)],key); + break; + case IDC_EDIT_DOWN: + AssignKey(theApp.input->joypaddata[MOTION(KEY_DOWN)],key); + break; + } +} + +void MotionConfig::assignKeys() +{ + AssignKeys(up.m_Keys,theApp.input->joypaddata[MOTION(KEY_UP)]); + AssignKeys(down.m_Keys, theApp.input->joypaddata[MOTION(KEY_DOWN)]); + AssignKeys(left.m_Keys, theApp.input->joypaddata[MOTION(KEY_LEFT)]); + AssignKeys(right.m_Keys, theApp.input->joypaddata[MOTION(KEY_RIGHT)]); +} + +void JoypadConfig::OnBnClickedAppendmode() +{ + bAppendMode = (::SendMessage(GetDlgItem(IDC_APPENDMODE)->GetSafeHwnd(), BM_GETCHECK, 0, 0L) != 0); +} + +void MotionConfig::OnBnClickedAppendmode() +{ + bAppendMode = (::SendMessage(GetDlgItem(IDC_APPENDMODE)->GetSafeHwnd(), BM_GETCHECK, 0, 0L) != 0); +} + +void JoypadConfig::OnBnClickedClearAll() +{ + up.m_Keys.RemoveAll(); + speed.m_Keys.RemoveAll(); + right.m_Keys.RemoveAll(); + left.m_Keys.RemoveAll(); + down.m_Keys.RemoveAll(); + capture.m_Keys.RemoveAll(); + buttonStart.m_Keys.RemoveAll(); + buttonSelect.m_Keys.RemoveAll(); + buttonR.m_Keys.RemoveAll(); + buttonL.m_Keys.RemoveAll(); + buttonGS.m_Keys.RemoveAll(); + buttonB.m_Keys.RemoveAll(); + buttonA.m_Keys.RemoveAll(); + + up.SetWindowText( _T("") ); + speed.SetWindowText( _T("") ); + right.SetWindowText( _T("") ); + left.SetWindowText( _T("") ); + down.SetWindowText( _T("") ); + capture.SetWindowText( _T("") ); + buttonStart.SetWindowText( _T("") ); + buttonSelect.SetWindowText( _T("") ); + buttonR.SetWindowText( _T("") ); + buttonL.SetWindowText( _T("") ); + buttonGS.SetWindowText( _T("") ); + buttonB.SetWindowText( _T("") ); + buttonA.SetWindowText( _T("") ); +} diff --git a/src/win32/Joypad.h b/src/win32/Joypad.h new file mode 100644 index 0000000..3af4c07 --- /dev/null +++ b/src/win32/Joypad.h @@ -0,0 +1,152 @@ +#include "afxwin.h" +#if !defined(AFX_JOYPAD_H__FFFB2470_9EEC_4D2D_A5F0_3BF31579999A__INCLUDED_) +#define AFX_JOYPAD_H__FFFB2470_9EEC_4D2D_A5F0_3BF31579999A__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// Joypad.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// JoypadEditControl window + +class JoypadEditControl : public CEdit +{ + // Construction +public: + + JoypadEditControl(); + + KeyList m_Keys; + + // Attributes + public: + + // Operations + public: + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(JoypadEditControl) + //}}AFX_VIRTUAL + + // Implementation + public: + virtual BOOL PreTranslateMessage(MSG *pMsg); + afx_msg LRESULT OnJoyConfig(WPARAM wParam, LPARAM lParam); + virtual ~JoypadEditControl(); + + // Generated message map functions + protected: + DECLARE_MESSAGE_MAP() + }; + + ///////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// JoypadConfig dialog + +class JoypadConfig : public CDialog +{ + // Construction + public: + void assignKey(int id, LONG_PTR key); + JoypadConfig(int w, CWnd* pParent = NULL); // standard constructor + + // Dialog Data + //{{AFX_DATA(JoypadConfig) + enum { IDD = IDD_CONFIG }; + JoypadEditControl up; + JoypadEditControl speed; + JoypadEditControl right; + JoypadEditControl left; + JoypadEditControl down; + JoypadEditControl capture; + JoypadEditControl buttonStart; + JoypadEditControl buttonSelect; + JoypadEditControl buttonR; + JoypadEditControl buttonL; + JoypadEditControl buttonGS; + JoypadEditControl buttonB; + JoypadEditControl buttonA; + + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(JoypadConfig) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + + // Implementation + protected: + UINT_PTR timerId; + int which; + + // Generated message map functions + //{{AFX_MSG(JoypadConfig) + afx_msg void OnCancel(); + afx_msg void OnOk(); + afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnDestroy(); + afx_msg void OnTimer(UINT_PTR nIDEvent); + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +public: + afx_msg void OnBnClickedAppendmode(); + afx_msg void OnBnClickedClearAll(); +}; + ///////////////////////////////////////////////////////////////////////////// +// MotionConfig dialog + +class MotionConfig : public CDialog +{ + // Construction + public: + void assignKeys(); + void assignKey(int id, int key); + MotionConfig(CWnd* pParent = NULL); // standard constructor + + // Dialog Data + //{{AFX_DATA(MotionConfig) + enum { IDD = IDD_MOTION_CONFIG }; + JoypadEditControl up; + JoypadEditControl right; + JoypadEditControl left; + JoypadEditControl down; + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(MotionConfig) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + + // Implementation + protected: + + // Generated message map functions + //{{AFX_MSG(MotionConfig) + afx_msg void OnCancel(); + afx_msg void OnOk(); + afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnDestroy(); + virtual BOOL OnInitDialog(); + afx_msg void OnTimer(UINT_PTR nIDEvent); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + private: + UINT_PTR timerId; +public: + afx_msg void OnBnClickedAppendmode(); +}; +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_JOYPAD_H__FFFB2470_9EEC_4D2D_A5F0_3BF31579999A__INCLUDED_) diff --git a/src/win32/KeyboardEdit.cpp b/src/win32/KeyboardEdit.cpp new file mode 100644 index 0000000..6164baa --- /dev/null +++ b/src/win32/KeyboardEdit.cpp @@ -0,0 +1,148 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 1998 by Thierry Maurel +// All rights reserved +// +// Distribute freely, except: don't remove my name from the source or +// documentation (don't take credit for my work), mark your changes (don't +// get me blamed for your possible bugs), don't alter or remove this +// notice. +// No warrantee of any kind, express or implied, is included with this +// software; use at your own risk, responsibility for damages (if any) to +// anyone resulting from the use of this software rests entirely with the +// user. +// +// Send bug reports, bug fixes, enhancements, requests, flames, etc., and +// I'll try to keep a version up to date. I can be reached as follows: +// tmaurel@caramail.com (or tmaurel@hol.fr) +// +//////////////////////////////////////////////////////////////////////////////// +// File : KeyboardEdit.cpp +// Project : AccelsEditor +//////////////////////////////////////////////////////////////////////////////// +// Version : 1.0 * Authors : A.Lebatard + T.Maurel +// Date : 17.08.98 +// +// Remarks : implementation file +// +//////////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "KeyboardEdit.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +extern TCHAR* mapVirtKeysStringFromWORD(WORD wKey); + +///////////////////////////////////////////////////////////////////////////// +// CKeyboardEdit + +CKeyboardEdit::CKeyboardEdit() +{ + m_bKeyDefined = false; + ResetKey (); +} + +CKeyboardEdit::~CKeyboardEdit() +{ +} + + +BEGIN_MESSAGE_MAP(CKeyboardEdit, CEdit) + //{{AFX_MSG_MAP(CKeyboardEdit) + //}}AFX_MSG_MAP + END_MESSAGE_MAP() + +#pragma warning( disable : 4706 ) + ///////////////////////////////////////////////////////////////////////////// +// CKeyboardEdit message handlers +BOOL CKeyboardEdit::PreTranslateMessage (MSG* pMsg) +{ + bool bPressed; + if ((bPressed = (pMsg->message == WM_KEYDOWN)) || pMsg->message == WM_KEYUP || (bPressed = (pMsg->message == WM_SYSKEYDOWN)) || pMsg->message == WM_SYSKEYUP) { + if (bPressed && m_bKeyDefined && !((1 << 30) & pMsg->lParam)) + ResetKey (); + if (pMsg->wParam == VK_SHIFT && !m_bKeyDefined) + m_bShiftPressed = bPressed; + else if (pMsg->wParam == VK_CONTROL &&!m_bKeyDefined) { + m_bCtrlPressed = bPressed; + } + else if (pMsg->wParam == VK_MENU && !m_bKeyDefined) + m_bAltPressed = bPressed; + else { + if (!m_bKeyDefined) { + m_wVirtKey = (WORD)pMsg->wParam; + if (bPressed) + m_bKeyDefined = true; + } + } + DisplayKeyboardString (); + return TRUE; + } + + return CEdit::PreTranslateMessage(pMsg); +} +#pragma warning( default : 4706 ) + +//////////////////////////////////////////////////////////////////////// +// +void CKeyboardEdit::DisplayKeyboardString() +{ + CString strKbd; + + // modifiers + if (m_bCtrlPressed) + strKbd = "Ctrl"; + if (m_bAltPressed) { + if (strKbd.GetLength () > 0) + strKbd += '+'; + strKbd += "Alt"; + } + if (m_bShiftPressed) { + if (strKbd.GetLength () > 0) + strKbd += '+'; + strKbd += "Shift"; + } + // virtual key + LPCTSTR szVirtKey = mapVirtKeysStringFromWORD(m_wVirtKey); + if (szVirtKey != NULL) { + if (strKbd.GetLength () > 0) + strKbd += '+'; + strKbd += szVirtKey; + } + SetWindowText (strKbd); +} + + +//////////////////////////////////////////////////////////////////////// +// +void CKeyboardEdit::ResetKey () +{ + m_wVirtKey = 0; + m_bCtrlPressed = false; + m_bAltPressed = false; + m_bShiftPressed = false; + + m_bKeyDefined = false; + if(m_hWnd != NULL) + SetWindowText(_T("")); +} + + +//////////////////////////////////////////////////////////////////////// +// +bool CKeyboardEdit::GetAccelKey(WORD& wVirtKey, bool& bCtrl, bool& bAlt, bool& bShift) +{ + if (!m_bKeyDefined) + return false; + + wVirtKey = m_wVirtKey; + bAlt = m_bAltPressed; + bCtrl = m_bCtrlPressed; + bShift = m_bShiftPressed; + return true; +} + diff --git a/src/win32/KeyboardEdit.h b/src/win32/KeyboardEdit.h new file mode 100644 index 0000000..10092d7 --- /dev/null +++ b/src/win32/KeyboardEdit.h @@ -0,0 +1,87 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 1998 by Thierry Maurel +// All rights reserved +// +// Distribute freely, except: don't remove my name from the source or +// documentation (don't take credit for my work), mark your changes (don't +// get me blamed for your possible bugs), don't alter or remove this +// notice. +// No warrantee of any kind, express or implied, is included with this +// software; use at your own risk, responsibility for damages (if any) to +// anyone resulting from the use of this software rests entirely with the +// user. +// +// Send bug reports, bug fixes, enhancements, requests, flames, etc., and +// I'll try to keep a version up to date. I can be reached as follows: +// tmaurel@caramail.com (or tmaurel@hol.fr) +// +//////////////////////////////////////////////////////////////////////////////// +// File : KeyboardEdit.h +// Project : AccelsEditor +//////////////////////////////////////////////////////////////////////////////// +// Version : 1.0 * Authors : A.Lebatard + T.Maurel +// Date : 17.08.98 +// +// Remarks : +// +//////////////////////////////////////////////////////////////////////////////// +#if !defined(AFX_KEYBOARDEDIT_H__88E35AB0_2E23_11D2_BA24_0060B0B5E151__INCLUDED_) +#define AFX_KEYBOARDEDIT_H__88E35AB0_2E23_11D2_BA24_0060B0B5E151__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// KeyboardEdit.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CKeyboardEdit window + +class CKeyboardEdit : public CEdit +{ + // Construction + public: + CKeyboardEdit(); + + // Attributes + public: + bool m_bKeyDefined; + + WORD m_wVirtKey; + bool m_bCtrlPressed; + bool m_bAltPressed; + bool m_bShiftPressed; + + // Operations + public: + bool GetAccelKey(WORD& wVirtKey, bool& bCtrl, bool& bAlt, bool& bShift); + void ResetKey (); + + protected: + void DisplayKeyboardString (); + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CKeyboardEdit) + public: + virtual BOOL PreTranslateMessage(MSG* pMsg); + //}}AFX_VIRTUAL + + // Implementation + public: + virtual ~CKeyboardEdit(); + + // Generated message map functions + protected: + //{{AFX_MSG(CKeyboardEdit) + //}}AFX_MSG + + DECLARE_MESSAGE_MAP() + }; + + ///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_KEYBOARDEDIT_H__88E35AB0_2E23_11D2_BA24_0060B0B5E151__INCLUDED_) diff --git a/src/win32/LangSelect.cpp b/src/win32/LangSelect.cpp new file mode 100644 index 0000000..bacbac7 --- /dev/null +++ b/src/win32/LangSelect.cpp @@ -0,0 +1,76 @@ +#include "stdafx.h" +#include "VBA.h" +#include "LangSelect.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// LangSelect dialog + + +LangSelect::LangSelect(CWnd* pParent /*=NULL*/) + : CDialog(LangSelect::IDD, pParent) +{ + //{{AFX_DATA_INIT(LangSelect) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT +} + + +void LangSelect::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(LangSelect) + DDX_Control(pDX, IDC_LANG_STRING, m_langString); + DDX_Control(pDX, IDC_LANG_NAME, m_langName); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(LangSelect, CDialog) + //{{AFX_MSG_MAP(LangSelect) + ON_BN_CLICKED(ID_CANCEL, OnCancel) + ON_BN_CLICKED(ID_OK, OnOk) + //}}AFX_MSG_MAP + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// LangSelect message handlers + +void LangSelect::OnCancel() +{ + EndDialog(FALSE); +} + +void LangSelect::OnOk() +{ + m_langString.GetWindowText(theApp.languageName); + EndDialog(TRUE); +} + +BOOL LangSelect::OnInitDialog() +{ + CDialog::OnInitDialog(); + + char lbuffer[10]; + if(GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_SABBREVLANGNAME, + lbuffer, 10)) { + m_langName.SetWindowText(lbuffer); + } else { + m_langName.SetWindowText("???"); + } + + if(!theApp.languageName.IsEmpty()) + m_langString.SetWindowText(theApp.languageName); + + m_langString.LimitText(3); + + CenterWindow(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} diff --git a/src/win32/LangSelect.h b/src/win32/LangSelect.h new file mode 100644 index 0000000..ca601c7 --- /dev/null +++ b/src/win32/LangSelect.h @@ -0,0 +1,49 @@ +#if !defined(AFX_LANGSELECT_H__63619E13_A375_4ED4_952F_DCF8998C2914__INCLUDED_) +#define AFX_LANGSELECT_H__63619E13_A375_4ED4_952F_DCF8998C2914__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// LangSelect.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// LangSelect dialog + +class LangSelect : public CDialog +{ + // Construction + public: + LangSelect(CWnd* pParent = NULL); // standard constructor + + // Dialog Data + //{{AFX_DATA(LangSelect) + enum { IDD = IDD_LANG_SELECT }; + CEdit m_langString; + CStatic m_langName; + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(LangSelect) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + + // Implementation + protected: + + // Generated message map functions + //{{AFX_MSG(LangSelect) + afx_msg void OnCancel(); + afx_msg void OnOk(); + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + + //{{AFX_INSERT_LOCATION}} + // Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_LANGSELECT_H__63619E13_A375_4ED4_952F_DCF8998C2914__INCLUDED_) diff --git a/src/win32/LinkOptions.cpp b/src/win32/LinkOptions.cpp new file mode 100644 index 0000000..dd04a08 --- /dev/null +++ b/src/win32/LinkOptions.cpp @@ -0,0 +1,753 @@ +#ifndef NO_LINK + +#include "stdafx.h" +#include "vba.h" +#include "LinkOptions.h" +#include "../gba/GBALink.h" + +extern lserver ls; + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// LinkOptions dialog + +CMyTabCtrl::CMyTabCtrl(){ + m_tabdialog[0] = new LinkGeneral; + m_tabdialog[1] = new LinkServer; + m_tabdialog[2] = new LinkClient; +} + +CMyTabCtrl::~CMyTabCtrl() +{ + m_tabdialog[0]->DestroyWindow(); + m_tabdialog[1]->DestroyWindow(); + m_tabdialog[2]->DestroyWindow(); + + delete m_tabdialog[0]; + delete m_tabdialog[1]; + delete m_tabdialog[2]; +} + +LinkOptions::LinkOptions(CWnd* pParent /*=NULL*/) + : CDialog(LinkOptions::IDD, pParent) +{ + //{{AFX_DATA_INIT(LinkOptions) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT +} + + +void LinkOptions::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(LinkOptions) + //}}AFX_DATA_MAP +} + +BOOL LinkOptions::OnInitDialog(){ + TCITEM tabitem; + char tabtext[3][8] = {"General", "Server", "Client"}; + int i; + + CDialog::OnInitDialog(); + + m_tabctrl.SubclassDlgItem(IDC_TAB1, this); + + tabitem.mask = TCIF_TEXT; + + for(i=0;i<3;i++){ + tabitem.pszText = tabtext[i]; + m_tabctrl.InsertItem(i, &tabitem); + } + m_tabctrl.m_tabdialog[0]->Create(IDD_LINKTAB1, this); + m_tabctrl.m_tabdialog[1]->Create(IDD_LINKTAB2, this); + m_tabctrl.m_tabdialog[2]->Create(IDD_LINKTAB3, this); + + m_tabctrl.m_tabdialog[0]->ShowWindow(SW_SHOW); + m_tabctrl.m_tabdialog[1]->ShowWindow(SW_HIDE); + m_tabctrl.m_tabdialog[2]->ShowWindow(SW_HIDE); + + m_tabctrl.SetCurSel(0); + m_tabctrl.OnSwitchTabs(); + + return TRUE; +} + + + BOOL LinkOptions::PreTranslateMessage(MSG* pMsg) + { + return m_tabctrl.TranslatePropSheetMsg(pMsg) ? TRUE : + CDialog::PreTranslateMessage(pMsg); + } + + + + +BEGIN_MESSAGE_MAP(LinkOptions, CDialog) + //{{AFX_MSG_MAP(LinkOptions) + ON_NOTIFY(TCN_SELCHANGE, IDC_TAB1, OnSelchangeTab1) + ON_BN_CLICKED(ID_OK, OnOk) + ON_BN_CLICKED(ID_CANCEL, OnCancel) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// LinkOptions message handlers +///////////////////////////////////////////////////////////////////////////// +// LinkGeneral dialog + + +LinkGeneral::LinkGeneral(CWnd* pParent /*=NULL*/) + : CDialog(LinkGeneral::IDD, pParent) +{ + //{{AFX_DATA_INIT(LinkGeneral) + //}}AFX_DATA_INIT +} + +void LinkGeneral::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(LinkGeneral) + DDX_Radio(pDX, IDC_LINK_SINGLE, m_type); + DDX_Control(pDX, IDC_LINKTIMEOUT, m_timeout); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(LinkGeneral, CDialog) + //{{AFX_MSG_MAP(LinkGeneral) + ON_BN_CLICKED(IDC_LINK_SINGLE, OnRadio1) + ON_BN_CLICKED(IDC_LINK_LAN, OnRadio2) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// LinkGeneral message handlers +///////////////////////////////////////////////////////////////////////////// +// LinkServer dialog + + +LinkServer::LinkServer(CWnd* pParent /*=NULL*/) + : CDialog(LinkServer::IDD, pParent) +{ + //{{AFX_DATA_INIT(LinkServer) + m_numplayers = -1; + m_prottype = -1; + m_speed = FALSE; + //}}AFX_DATA_INIT +} + + +void LinkServer::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(LinkServer) + DDX_Radio(pDX, IDC_LINK2P, m_numplayers); + DDX_Radio(pDX, IDC_LINKTCP, m_prottype); + DDX_Check(pDX, IDC_SSPEED, m_speed); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(LinkServer, CDialog) + //{{AFX_MSG_MAP(LinkServer) + ON_BN_CLICKED(IDC_SERVERSTART, OnServerStart) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// LinkServer message handlers + +LinkClient::LinkClient(CWnd* pParent /*=NULL*/) + : CDialog(LinkClient::IDD, pParent) +{ + //{{AFX_DATA_INIT(LinkClient) + m_prottype = -1; + m_hacks = -1; + //}}AFX_DATA_INIT +} + + +void LinkClient::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(LinkClient) + DDX_Control(pDX, IDC_SERVERIP, m_serverip); + DDX_Radio(pDX, IDC_CLINKTCP, m_prottype); + DDX_Radio(pDX, IDC_SPEEDOFF, m_hacks); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(LinkClient, CDialog) + //{{AFX_MSG_MAP(LinkClient) + ON_BN_CLICKED(IDC_LINKCONNECT, OnLinkConnect) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// LinkClient message handlers + +BOOL LinkServer::OnInitDialog() +{ + CDialog::OnInitDialog(); + + m_numplayers = lanlink.numslaves; + m_prottype = lanlink.type; + m_speed = lanlink.speed; + + UpdateData(FALSE); + + return TRUE; +} + +void LinkOptions::OnSelchangeTab1(NMHDR* pNMHDR, LRESULT* pResult) +{ + m_tabctrl.OnSwitchTabs(); + *pResult = 0; +} + +IMPLEMENT_DYNAMIC(CMyTabCtrl, CTabCtrl) +BEGIN_MESSAGE_MAP(CMyTabCtrl, CTabCtrl) + ON_NOTIFY_REFLECT(TCN_SELCHANGING, OnSelChanging) +END_MESSAGE_MAP() + +BOOL CMyTabCtrl::SubclassDlgItem(UINT nID, CWnd* pParent) +{ + if (!CTabCtrl::SubclassDlgItem(nID, pParent)) + return FALSE; + + ModifyStyle(0, TCS_OWNERDRAWFIXED); + + // If first tab is disabled, go to next enabled tab + if (!IsTabEnabled(0)) { + int iTab = NextEnabledTab(0, TRUE); + SetActiveTab(iTab); + } + return TRUE; +} + +BOOL CMyTabCtrl::IsTabEnabled(int iTab) +{ + if (!lanlink.active && iTab > 0) + return false; + return true; +} + +////////////////// +// Draw the tab: mimic SysTabControl32, except use gray if tab is disabled +// +void CMyTabCtrl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) +{ + DRAWITEMSTRUCT& ds = *lpDrawItemStruct; + + int iItem = ds.itemID; + + // Get tab item info + char text[128]; + TCITEM tci; + tci.mask = TCIF_TEXT; + tci.pszText = text; + tci.cchTextMax = sizeof(text); + GetItem(iItem, &tci); + + // use draw item DC + CDC dc; + dc.Attach(ds.hDC); + + // calculate text rectangle and color + CRect rc = ds.rcItem; + rc += CPoint(1,4); // ?? by trial and error + + // draw the text + OnDrawText(dc, rc, text, !IsTabEnabled(iItem)); + + dc.Detach(); +} + +////////////////// +// Draw tab text. You can override to use different color/font. +// +void CMyTabCtrl::OnDrawText(CDC& dc, CRect rc, + CString sText, BOOL bDisabled) +{ + dc.SetTextColor(GetSysColor(bDisabled ? COLOR_3DHILIGHT : COLOR_BTNTEXT)); + dc.DrawText(sText, &rc, DT_CENTER|DT_VCENTER); + + if (bDisabled) { + // disabled: draw again shifted northwest for shadow effect + rc += CPoint(-1,-1); + dc.SetTextColor(GetSysColor(COLOR_GRAYTEXT)); + dc.DrawText(sText, &rc, DT_CENTER|DT_VCENTER); + } +} + +////////////////// +// Selection is changing: disallow if tab is disabled +// +void CMyTabCtrl::OnSelChanging(NMHDR* pnmh, LRESULT* pRes) +{ + TRACE("CMyTabCtrl::OnSelChanging\n"); + + // Figure out index of new tab we are about to go to, as opposed + // to the current one we're at. Believe it or not, Windows doesn't + // pass this info + // + TC_HITTESTINFO htinfo; + GetCursorPos(&htinfo.pt); + ScreenToClient(&htinfo.pt); + int iNewTab = HitTest(&htinfo); + + if (iNewTab >= 0 && !IsTabEnabled(iNewTab)) + *pRes = TRUE; // tab disabled: prevent selection +} + +////////////////// +// Trap arrow-left key to skip disabled tabs. +// This is the only way to know where we're coming from--ie from +// arrow-left (prev) or arrow-right (next). +// +BOOL CMyTabCtrl::PreTranslateMessage(MSG* pMsg) +{ + if (pMsg->message == WM_KEYDOWN && + (pMsg->wParam == VK_LEFT || pMsg->wParam == VK_RIGHT)) { + + int iNewTab = (pMsg->wParam == VK_LEFT) ? + PrevEnabledTab(GetCurSel(), FALSE) : + NextEnabledTab(GetCurSel(), FALSE); + if (iNewTab >= 0) + SetActiveTab(iNewTab); + return TRUE; + } + return CTabCtrl::PreTranslateMessage(pMsg); +} + +//////////////// +// Translate parent property sheet message. Translates Control-Tab and +// Control-Shift-Tab keys. These are normally handled by the property +// sheet, so you must call this function from your prop sheet's +// PreTranslateMessage function. +// +BOOL CMyTabCtrl::TranslatePropSheetMsg(MSG* pMsg) +{ + WPARAM key = pMsg->wParam; + if (pMsg->message == WM_KEYDOWN && GetAsyncKeyState(VK_CONTROL) < 0 && + (key == VK_TAB || key == VK_PRIOR || key == VK_NEXT)) { + + int iNewTab = (key==VK_PRIOR || GetAsyncKeyState(VK_SHIFT) < 0) ? + PrevEnabledTab(GetCurSel(), TRUE) : + NextEnabledTab(GetCurSel(), TRUE); + if (iNewTab >= 0) + SetActiveTab(iNewTab); + return TRUE; + } + return FALSE; +} + +////////////////// +// Helper to set the active page, when moving backwards (left-arrow and +// Control-Shift-Tab). Must simulate Windows messages to tell parent I +// am changing the tab; SetCurSel does not do this!! +// +// In normal operation, this fn will always succeed, because I don't call it +// unless I already know IsTabEnabled() = TRUE; but if you call SetActiveTab +// with a random value, it could fail. +// +BOOL CMyTabCtrl::SetActiveTab(UINT iNewTab) +{ + TRACE("CMyTabCtrl::SetActiveTab\n"); + + // send the parent TCN_SELCHANGING + NMHDR nmh; + nmh.hwndFrom = m_hWnd; + nmh.idFrom = GetDlgCtrlID(); + nmh.code = TCN_SELCHANGING; + + if (GetParent()->SendMessage(WM_NOTIFY, nmh.idFrom, (LPARAM)&nmh) >=0) { + // OK to change: set the new tab + SetCurSel(iNewTab); + + // send parent TCN_SELCHANGE + nmh.code = TCN_SELCHANGE; + GetParent()->SendMessage(WM_NOTIFY, nmh.idFrom, (LPARAM)&nmh); + return TRUE; + } + return FALSE; +} + +///////////////// +// Return the index of the next enabled tab after a given index, or -1 if none +// (0 = first tab). +// If bWrap is TRUE, wrap from beginning to end; otherwise stop at zero. +// +int CMyTabCtrl::NextEnabledTab(int iCurrentTab, BOOL bWrap) +{ + int nTabs = GetItemCount(); + for (int iTab = iCurrentTab+1; iTab != iCurrentTab; iTab++) { + if (iTab >= nTabs) { + if (!bWrap) + return -1; + iTab = 0; + } + if (IsTabEnabled(iTab)) { + return iTab; + } + } + return -1; +} + +///////////////// +// Return the index of the previous enabled tab before a given index, or -1. +// (0 = first tab). +// If bWrap is TRUE, wrap from beginning to end; otherwise stop at zero. +// +int CMyTabCtrl::PrevEnabledTab(int iCurrentTab, BOOL bWrap) +{ + for (int iTab = iCurrentTab-1; iTab != iCurrentTab; iTab--) { + if (iTab < 0) { + if (!bWrap) + return -1; + iTab = GetItemCount() - 1; + } + if (IsTabEnabled(iTab)) { + return iTab; + } + } + return -1; +} + + +void CMyTabCtrl::OnSwitchTabs(void) +{ + CRect clientRect, wndRect; + int i; + + GetClientRect(clientRect); + AdjustRect(FALSE, clientRect); + GetWindowRect(wndRect); + GetParent()->ScreenToClient(wndRect); + clientRect.OffsetRect(wndRect.left, wndRect.top); + + if(lanlink.active==0) + SetCurSel(0); + + for(i=0;i<3;i++){ + if(i==GetCurSel()){ + m_tabdialog[i]->SetWindowPos(&wndTop, clientRect.left, clientRect.top, clientRect.Width(), clientRect.Height(), SWP_SHOWWINDOW); + } else { + m_tabdialog[i]->ShowWindow(SW_HIDE); + } + } + return; +} + + +void LinkOptions::OnOk() +{ + GetAllData((LinkGeneral*)m_tabctrl.m_tabdialog[0]); + CDialog::OnOK(); + return; +} + +void LinkGeneral::OnRadio1() +{ + m_type = 0; + lanlink.active = 0; + GetParent()->Invalidate(); +} + +void LinkGeneral::OnRadio2() +{ + m_type = 1; + lanlink.active = 1; + GetParent()->Invalidate(); +} + +BOOL LinkGeneral::OnInitDialog(){ + + char timeout[6]; + + CDialog::OnInitDialog(); + + m_timeout.LimitText(5); + sprintf(timeout, "%d", linktimeout); + m_timeout.SetWindowText(timeout); + + m_type = lanlink.active; + + UpdateData(FALSE); + + return TRUE; +} + + +void LinkOptions::OnCancel() +{ + CDialog::OnCancel(); + return; +} + +class Win32ServerInfoDisplay : public ServerInfoDisplay +{ +public: + Win32ServerInfoDisplay(ServerWait *_dlg) + { + dlg = _dlg; + } + + ~Win32ServerInfoDisplay() + { + if (dlg) + { + // not connected + MessageBox(NULL, "Failed to connect.", "Link", MB_OK); + dlg->SendMessage(WM_CLOSE, 0, 0); + } + + delete dlg; + dlg = NULL; + } + + void ShowServerIP(const sf::IPAddress& addr) + { + dlg->m_serveraddress.Format("Server IP address is: %s", addr.ToString()); + } + + void ShowConnect(const int player) + { + dlg->m_plconn[0].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); + delete dlg; + dlg = NULL; + } + +private: + ServerWait *dlg; +}; + +void LinkServer::OnServerStart() +{ + UpdateData(TRUE); + + lanlink.numslaves = m_numplayers+1; + lanlink.type = m_prottype; + lanlink.server = true; + lanlink.speed = m_speed==1 ? true : false; + sf::IPAddress addr; + + // These must be created on the heap - referenced from the connection thread + ServerWait *dlg = new ServerWait(); + dlg->Create(IDD_SERVERWAIT, this); + dlg->ShowWindow(SW_SHOW); + + // Owns the ServerWait* + Win32ServerInfoDisplay *dlginfo = new Win32ServerInfoDisplay(dlg); + + // ls thread will own the dlginfo + if (!ls.Init(dlginfo)) + { + // Thread didn't get created + delete dlginfo; + MessageBox("Error occurred.\nPlease try again.", "Error", MB_OK); + } + + return; +} + +BOOL LinkClient::OnInitDialog() +{ + CDialog::OnInitDialog(); + + m_prottype = lanlink.type; + m_hacks = lanlink.speed; + + UpdateData(FALSE); + + return TRUE; +} + +class Win32ClientInfoDisplay : public ClientInfoDisplay +{ +public: + Win32ClientInfoDisplay(ServerWait *_dlg) + { + dlg = _dlg; + } + + ~Win32ClientInfoDisplay() + { + if (dlg) + { + // not connected + MessageBox(NULL, "Failed to connect.", "Link", MB_OK); + dlg->SendMessage(WM_CLOSE, 0, 0); + } + + delete dlg; + dlg = NULL; + } + + void ConnectStart(const sf::IPAddress& addr) + { + dlg->SetWindowText("Connecting..."); + } + + void ShowConnect(const int player, const 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); + delete dlg; + dlg = NULL; + } + +private: + ServerWait *dlg; +}; + +void LinkClient::OnLinkConnect() +{ + char ipaddress[31]; + + UpdateData(TRUE); + + lanlink.type = m_prottype; + lanlink.server = false; + lanlink.speed = m_hacks==1 ? true : false; + + m_serverip.GetWindowText(ipaddress, 30); + + // These must be created on the heap - referenced from the connection thread + ServerWait *dlg = new ServerWait(); + dlg->Create(IDD_SERVERWAIT, this); + dlg->ShowWindow(SW_SHOW); + + // Owns the ServerWait* + Win32ClientInfoDisplay *dlginfo = new Win32ClientInfoDisplay(dlg); + + // lc thread will own the dlginfo + if (!lc.Init(sf::IPAddress(std::string(ipaddress)), dlginfo)) + { + // Thread didn't get created + delete dlginfo; + MessageBox("Error occurred.\nPlease try again.", "Error", MB_OK); + } + + return; +} + +void LinkOptions::GetAllData(LinkGeneral *src) +{ + char timeout[6]; + + src->UpdateData(true); + + src->m_timeout.GetWindowText(timeout, 5); + sscanf(timeout, "%d", &linktimeout); + + if(src->m_type==0){ + lanlink.speed = 0; + } + + return; +} +///////////////////////////////////////////////////////////////////////////// +// ServerWait dialog + + +ServerWait::ServerWait(CWnd* pParent /*=NULL*/) + : CDialog(ServerWait::IDD, pParent) +{ + //{{AFX_DATA_INIT(ServerWait) + m_serveraddress = _T(""); + m_plconn[0] = _T(""); + m_plconn[1] = _T(""); + m_plconn[2] = _T(""); + //}}AFX_DATA_INIT +} + + +void ServerWait::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(ServerWait) + DDX_Control(pDX, IDC_SERVERWAIT, m_prgctrl); + DDX_Text(pDX, IDC_STATIC1, m_serveraddress); + DDX_Text(pDX, IDC_STATIC2, m_plconn[0]); + DDX_Text(pDX, IDC_STATIC3, m_plconn[1]); + DDX_Text(pDX, IDC_STATIC4, m_plconn[2]); + //}}AFX_DATA_MAP +} + +BEGIN_MESSAGE_MAP(ServerWait, CDialog) + //{{AFX_MSG_MAP(ServerWait) + ON_BN_CLICKED(ID_CANCEL, OnCancel) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// ServerWait message handlers + +void ServerWait::OnCancel() +{ + lanlink.terminate = true; + CDialog::OnCancel(); +} + +BOOL LinkGeneral::PreTranslateMessage(MSG* pMsg) +{ + if(pMsg->message==WM_KEYDOWN) + if(pMsg->wParam==VK_RETURN||pMsg->wParam==VK_ESCAPE) + pMsg->wParam = NULL; + + return CDialog::PreTranslateMessage(pMsg); +} + +BOOL LinkClient::PreTranslateMessage(MSG* pMsg) +{ + if(pMsg->message==WM_KEYDOWN) + if(pMsg->wParam==VK_RETURN||pMsg->wParam==VK_ESCAPE) + pMsg->wParam = NULL; + + return CDialog::PreTranslateMessage(pMsg); +} + +BOOL LinkServer::PreTranslateMessage(MSG* pMsg) +{ + if(pMsg->message==WM_KEYDOWN) + if(pMsg->wParam==VK_RETURN||pMsg->wParam==VK_ESCAPE) + pMsg->wParam = NULL; + + return CDialog::PreTranslateMessage(pMsg); +} + +#endif // NO_LINK diff --git a/src/win32/LinkOptions.h b/src/win32/LinkOptions.h new file mode 100644 index 0000000..17d1383 --- /dev/null +++ b/src/win32/LinkOptions.h @@ -0,0 +1,221 @@ +#pragma once + +class CMyTabCtrl : public CTabCtrl { + DECLARE_DYNAMIC(CMyTabCtrl) +public: + CMyTabCtrl(void); + ~CMyTabCtrl(void); + + BOOL IsTabEnabled(int iTab); // you must override + BOOL TranslatePropSheetMsg(MSG* pMsg); // call from prop sheet + BOOL SubclassDlgItem(UINT nID, CWnd* pParent); // non-virtual override + + // helpers + int NextEnabledTab(int iTab, BOOL bWrap); // get next enabled tab + int PrevEnabledTab(int iTab, BOOL bWrap); // get prev enabled tab + BOOL SetActiveTab(UINT iNewTab); // set tab (fail if disabled) + + CDialog *m_tabdialog[3]; + + void OnSwitchTabs(void); +protected: + DECLARE_MESSAGE_MAP() + afx_msg void OnSelChanging(NMHDR* pNmh, LRESULT* pRes); + + // MFC overrides + virtual BOOL PreTranslateMessage(MSG* pMsg); + virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct); + + // override to draw text only; eg, colored text or different font + virtual void OnDrawText(CDC& dc, CRect rc, CString sText, BOOL bDisabled); + +}; + +///////////////////////////////////////////////////////////////////////////// +// LinkGeneral dialog + +class LinkGeneral : public CDialog +{ +// Construction +public: + LinkGeneral(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(LinkGeneral) + enum { IDD = IDD_LINKTAB1 }; + int m_type; + CEdit m_timeout; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(LinkGeneral) + public: + virtual BOOL PreTranslateMessage(MSG* pMsg); + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(LinkGeneral) + virtual BOOL OnInitDialog(); + afx_msg void OnRadio1(); + afx_msg void OnRadio2(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// +// LinkOptions dialog + +class LinkOptions : public CDialog +{ +// Construction +public: + LinkOptions(CWnd* pParent = NULL); // standard constructor + void GetAllData(LinkGeneral*); +// Dialog Data + //{{AFX_DATA(LinkOptions) + enum { IDD = IDD_LINKTAB }; + CMyTabCtrl m_tabctrl; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(LinkOptions) + public: + virtual BOOL PreTranslateMessage(MSG* pMsg); + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(LinkOptions) + afx_msg void OnSelchangeTab1(NMHDR* pNMHDR, LRESULT* pResult); + virtual BOOL OnInitDialog(); + afx_msg void OnOk(); + afx_msg void OnCancel(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// +// LinkServer dialog + +class LinkServer : public CDialog +{ +// Construction +public: + LinkServer(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(LinkServer) + enum { IDD = IDD_LINKTAB2 }; + int m_numplayers; + int m_prottype; + BOOL m_speed; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(LinkServer) + public: + virtual BOOL PreTranslateMessage(MSG* pMsg); + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(LinkServer) + virtual BOOL OnInitDialog(); + afx_msg void OnServerStart(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +class LinkClient : public CDialog +{ +// Construction +public: + LinkClient(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(LinkClient) + enum { IDD = IDD_LINKTAB3 }; + CEdit m_serverip; + int m_prottype; + int m_hacks; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(LinkClient) + public: + virtual BOOL PreTranslateMessage(MSG* pMsg); + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(LinkClient) + virtual BOOL OnInitDialog(); + afx_msg void OnLinkConnect(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +// ServerWait dialog + +class ServerWait : public CDialog +{ +// Construction +public: + ServerWait(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(ServerWait) + enum { IDD = IDD_SERVERWAIT }; + CProgressCtrl m_prgctrl; + CString m_serveraddress; + CString m_plconn[3]; + //CString m_p2conn; + //CString m_p3conn; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(ServerWait) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + afx_msg void OnCancel(); + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(ServerWait) + // NOTE: the ClassWizard will add member functions here + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + diff --git a/src/win32/LoadOAL.cpp b/src/win32/LoadOAL.cpp new file mode 100644 index 0000000..d95371c --- /dev/null +++ b/src/win32/LoadOAL.cpp @@ -0,0 +1,454 @@ +/* + * Copyright (c) 2006, Creative Labs Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this list of conditions and + * the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the distribution. + * * Neither the name of Creative Labs Inc. nor the names of its contributors may be used to endorse or + * promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "stdafx.h" + +#ifndef NO_OAL + +#include "windows.h" +#include "LoadOAL.h" + +HINSTANCE g_hOpenALDLL = NULL; + +ALboolean LoadOAL10Library(char *szOALFullPathName, LPOPENALFNTABLE lpOALFnTable) +{ + if (!lpOALFnTable) + return AL_FALSE; + + if (szOALFullPathName) + g_hOpenALDLL = LoadLibrary(szOALFullPathName); + else + g_hOpenALDLL = LoadLibrary("openal32.dll"); + + if (!g_hOpenALDLL) + return AL_FALSE; + + memset(lpOALFnTable, 0, sizeof(OPENALFNTABLE)); + + // Get function pointers + lpOALFnTable->alEnable = (LPALENABLE)GetProcAddress(g_hOpenALDLL, "alEnable"); + if (lpOALFnTable->alEnable == NULL) + { + OutputDebugString("Failed to retrieve 'alEnable' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alDisable = (LPALDISABLE)GetProcAddress(g_hOpenALDLL, "alDisable"); + if (lpOALFnTable->alDisable == NULL) + { + OutputDebugString("Failed to retrieve 'alDisable' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alIsEnabled = (LPALISENABLED)GetProcAddress(g_hOpenALDLL, "alIsEnabled"); + if (lpOALFnTable->alIsEnabled == NULL) + { + OutputDebugString("Failed to retrieve 'alIsEnabled' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetBoolean = (LPALGETBOOLEAN)GetProcAddress(g_hOpenALDLL, "alGetBoolean"); + if (lpOALFnTable->alGetBoolean == NULL) + { + OutputDebugString("Failed to retrieve 'alGetBoolean' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetInteger = (LPALGETINTEGER)GetProcAddress(g_hOpenALDLL, "alGetInteger"); + if (lpOALFnTable->alGetInteger == NULL) + { + OutputDebugString("Failed to retrieve 'alGetInteger' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetFloat = (LPALGETFLOAT)GetProcAddress(g_hOpenALDLL, "alGetFloat"); + if (lpOALFnTable->alGetFloat == NULL) + { + OutputDebugString("Failed to retrieve 'alGetFloat' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetDouble = (LPALGETDOUBLE)GetProcAddress(g_hOpenALDLL, "alGetDouble"); + if (lpOALFnTable->alGetDouble == NULL) + { + OutputDebugString("Failed to retrieve 'alGetDouble' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetBooleanv = (LPALGETBOOLEANV)GetProcAddress(g_hOpenALDLL, "alGetBooleanv"); + if (lpOALFnTable->alGetBooleanv == NULL) + { + OutputDebugString("Failed to retrieve 'alGetBooleanv' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetIntegerv = (LPALGETINTEGERV)GetProcAddress(g_hOpenALDLL, "alGetIntegerv"); + if (lpOALFnTable->alGetIntegerv == NULL) + { + OutputDebugString("Failed to retrieve 'alGetIntegerv' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetFloatv = (LPALGETFLOATV)GetProcAddress(g_hOpenALDLL, "alGetFloatv"); + if (lpOALFnTable->alGetFloatv == NULL) + { + OutputDebugString("Failed to retrieve 'alGetFloatv' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetDoublev = (LPALGETDOUBLEV)GetProcAddress(g_hOpenALDLL, "alGetDoublev"); + if (lpOALFnTable->alGetDoublev == NULL) + { + OutputDebugString("Failed to retrieve 'alGetDoublev' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetString = (LPALGETSTRING)GetProcAddress(g_hOpenALDLL, "alGetString"); + if (lpOALFnTable->alGetString == NULL) + { + OutputDebugString("Failed to retrieve 'alGetString' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetError = (LPALGETERROR)GetProcAddress(g_hOpenALDLL, "alGetError"); + if (lpOALFnTable->alGetError == NULL) + { + OutputDebugString("Failed to retrieve 'alGetError' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alIsExtensionPresent = (LPALISEXTENSIONPRESENT)GetProcAddress(g_hOpenALDLL, "alIsExtensionPresent"); + if (lpOALFnTable->alIsExtensionPresent == NULL) + { + OutputDebugString("Failed to retrieve 'alIsExtensionPresent' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetProcAddress = (LPALGETPROCADDRESS)GetProcAddress(g_hOpenALDLL, "alGetProcAddress"); + if (lpOALFnTable->alGetProcAddress == NULL) + { + OutputDebugString("Failed to retrieve 'alGetProcAddress' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetEnumValue = (LPALGETENUMVALUE)GetProcAddress(g_hOpenALDLL, "alGetEnumValue"); + if (lpOALFnTable->alGetEnumValue == NULL) + { + OutputDebugString("Failed to retrieve 'alGetEnumValue' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alListeneri = (LPALLISTENERI)GetProcAddress(g_hOpenALDLL, "alListeneri"); + if (lpOALFnTable->alListeneri == NULL) + { + OutputDebugString("Failed to retrieve 'alListeneri' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alListenerf = (LPALLISTENERF)GetProcAddress(g_hOpenALDLL, "alListenerf"); + if (lpOALFnTable->alListenerf == NULL) + { + OutputDebugString("Failed to retrieve 'alListenerf' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alListener3f = (LPALLISTENER3F)GetProcAddress(g_hOpenALDLL, "alListener3f"); + if (lpOALFnTable->alListener3f == NULL) + { + OutputDebugString("Failed to retrieve 'alListener3f' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alListenerfv = (LPALLISTENERFV)GetProcAddress(g_hOpenALDLL, "alListenerfv"); + if (lpOALFnTable->alListenerfv == NULL) + { + OutputDebugString("Failed to retrieve 'alListenerfv' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetListeneri = (LPALGETLISTENERI)GetProcAddress(g_hOpenALDLL, "alGetListeneri"); + if (lpOALFnTable->alGetListeneri == NULL) + { + OutputDebugString("Failed to retrieve 'alGetListeneri' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetListenerf =(LPALGETLISTENERF)GetProcAddress(g_hOpenALDLL, "alGetListenerf"); + if (lpOALFnTable->alGetListenerf == NULL) + { + OutputDebugString("Failed to retrieve 'alGetListenerf' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetListener3f = (LPALGETLISTENER3F)GetProcAddress(g_hOpenALDLL, "alGetListener3f"); + if (lpOALFnTable->alGetListener3f == NULL) + { + OutputDebugString("Failed to retrieve 'alGetListener3f' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetListenerfv = (LPALGETLISTENERFV)GetProcAddress(g_hOpenALDLL, "alGetListenerfv"); + if (lpOALFnTable->alGetListenerfv == NULL) + { + OutputDebugString("Failed to retrieve 'alGetListenerfv' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGenSources = (LPALGENSOURCES)GetProcAddress(g_hOpenALDLL, "alGenSources"); + if (lpOALFnTable->alGenSources == NULL) + { + OutputDebugString("Failed to retrieve 'alGenSources' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alDeleteSources = (LPALDELETESOURCES)GetProcAddress(g_hOpenALDLL, "alDeleteSources"); + if (lpOALFnTable->alDeleteSources == NULL) + { + OutputDebugString("Failed to retrieve 'alDeleteSources' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alIsSource = (LPALISSOURCE)GetProcAddress(g_hOpenALDLL, "alIsSource"); + if (lpOALFnTable->alIsSource == NULL) + { + OutputDebugString("Failed to retrieve 'alIsSource' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSourcei = (LPALSOURCEI)GetProcAddress(g_hOpenALDLL, "alSourcei"); + if (lpOALFnTable->alSourcei == NULL) + { + OutputDebugString("Failed to retrieve 'alSourcei' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSourcef = (LPALSOURCEF)GetProcAddress(g_hOpenALDLL, "alSourcef"); + if (lpOALFnTable->alSourcef == NULL) + { + OutputDebugString("Failed to retrieve 'alSourcef' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSource3f = (LPALSOURCE3F)GetProcAddress(g_hOpenALDLL, "alSource3f"); + if (lpOALFnTable->alSource3f == NULL) + { + OutputDebugString("Failed to retrieve 'alSource3f' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSourcefv = (LPALSOURCEFV)GetProcAddress(g_hOpenALDLL, "alSourcefv"); + if (lpOALFnTable->alSourcefv == NULL) + { + OutputDebugString("Failed to retrieve 'alSourcefv' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetSourcei = (LPALGETSOURCEI)GetProcAddress(g_hOpenALDLL, "alGetSourcei"); + if (lpOALFnTable->alGetSourcei == NULL) + { + OutputDebugString("Failed to retrieve 'alGetSourcei' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetSourcef = (LPALGETSOURCEF)GetProcAddress(g_hOpenALDLL, "alGetSourcef"); + if (lpOALFnTable->alGetSourcef == NULL) + { + OutputDebugString("Failed to retrieve 'alGetSourcef' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetSourcefv = (LPALGETSOURCEFV)GetProcAddress(g_hOpenALDLL, "alGetSourcefv"); + if (lpOALFnTable->alGetSourcefv == NULL) + { + OutputDebugString("Failed to retrieve 'alGetSourcefv' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSourcePlayv = (LPALSOURCEPLAYV)GetProcAddress(g_hOpenALDLL, "alSourcePlayv"); + if (lpOALFnTable->alSourcePlayv == NULL) + { + OutputDebugString("Failed to retrieve 'alSourcePlayv' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSourceStopv = (LPALSOURCESTOPV)GetProcAddress(g_hOpenALDLL, "alSourceStopv"); + if (lpOALFnTable->alSourceStopv == NULL) + { + OutputDebugString("Failed to retrieve 'alSourceStopv' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSourcePlay = (LPALSOURCEPLAY)GetProcAddress(g_hOpenALDLL, "alSourcePlay"); + if (lpOALFnTable->alSourcePlay == NULL) + { + OutputDebugString("Failed to retrieve 'alSourcePlay' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSourcePause = (LPALSOURCEPAUSE)GetProcAddress(g_hOpenALDLL, "alSourcePause"); + if (lpOALFnTable->alSourcePause == NULL) + { + OutputDebugString("Failed to retrieve 'alSourcePause' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSourceStop = (LPALSOURCESTOP)GetProcAddress(g_hOpenALDLL, "alSourceStop"); + if (lpOALFnTable->alSourceStop == NULL) + { + OutputDebugString("Failed to retrieve 'alSourceStop' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGenBuffers = (LPALGENBUFFERS)GetProcAddress(g_hOpenALDLL, "alGenBuffers"); + if (lpOALFnTable->alGenBuffers == NULL) + { + OutputDebugString("Failed to retrieve 'alGenBuffers' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alDeleteBuffers = (LPALDELETEBUFFERS)GetProcAddress(g_hOpenALDLL, "alDeleteBuffers"); + if (lpOALFnTable->alDeleteBuffers == NULL) + { + OutputDebugString("Failed to retrieve 'alDeleteBuffers' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alIsBuffer = (LPALISBUFFER)GetProcAddress(g_hOpenALDLL, "alIsBuffer"); + if (lpOALFnTable->alIsBuffer == NULL) + { + OutputDebugString("Failed to retrieve 'alIsBuffer' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alBufferData = (LPALBUFFERDATA)GetProcAddress(g_hOpenALDLL, "alBufferData"); + if (lpOALFnTable->alBufferData == NULL) + { + OutputDebugString("Failed to retrieve 'alBufferData' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetBufferi = (LPALGETBUFFERI)GetProcAddress(g_hOpenALDLL, "alGetBufferi"); + if (lpOALFnTable->alGetBufferi == NULL) + { + OutputDebugString("Failed to retrieve 'alGetBufferi' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetBufferf = (LPALGETBUFFERF)GetProcAddress(g_hOpenALDLL, "alGetBufferf"); + if (lpOALFnTable->alGetBufferf == NULL) + { + OutputDebugString("Failed to retrieve 'alGetBufferf' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSourceQueueBuffers = (LPALSOURCEQUEUEBUFFERS)GetProcAddress(g_hOpenALDLL, "alSourceQueueBuffers"); + if (lpOALFnTable->alSourceQueueBuffers == NULL) + { + OutputDebugString("Failed to retrieve 'alSourceQueueBuffers' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSourceUnqueueBuffers = (LPALSOURCEUNQUEUEBUFFERS)GetProcAddress(g_hOpenALDLL, "alSourceUnqueueBuffers"); + if (lpOALFnTable->alSourceUnqueueBuffers == NULL) + { + OutputDebugString("Failed to retrieve 'alSourceUnqueueBuffers' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alDistanceModel = (LPALDISTANCEMODEL)GetProcAddress(g_hOpenALDLL, "alDistanceModel"); + if (lpOALFnTable->alDistanceModel == NULL) + { + OutputDebugString("Failed to retrieve 'alDistanceModel' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alDopplerFactor = (LPALDOPPLERFACTOR)GetProcAddress(g_hOpenALDLL, "alDopplerFactor"); + if (lpOALFnTable->alDopplerFactor == NULL) + { + OutputDebugString("Failed to retrieve 'alDopplerFactor' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alDopplerVelocity = (LPALDOPPLERVELOCITY)GetProcAddress(g_hOpenALDLL, "alDopplerVelocity"); + if (lpOALFnTable->alDopplerVelocity == NULL) + { + OutputDebugString("Failed to retrieve 'alDopplerVelocity' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcGetString = (LPALCGETSTRING)GetProcAddress(g_hOpenALDLL, "alcGetString"); + if (lpOALFnTable->alcGetString == NULL) + { + OutputDebugString("Failed to retrieve 'alcGetString' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcGetIntegerv = (LPALCGETINTEGERV)GetProcAddress(g_hOpenALDLL, "alcGetIntegerv"); + if (lpOALFnTable->alcGetIntegerv == NULL) + { + OutputDebugString("Failed to retrieve 'alcGetIntegerv' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcOpenDevice = (LPALCOPENDEVICE)GetProcAddress(g_hOpenALDLL, "alcOpenDevice"); + if (lpOALFnTable->alcOpenDevice == NULL) + { + OutputDebugString("Failed to retrieve 'alcOpenDevice' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcCloseDevice = (LPALCCLOSEDEVICE)GetProcAddress(g_hOpenALDLL, "alcCloseDevice"); + if (lpOALFnTable->alcCloseDevice == NULL) + { + OutputDebugString("Failed to retrieve 'alcCloseDevice' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcCreateContext = (LPALCCREATECONTEXT)GetProcAddress(g_hOpenALDLL, "alcCreateContext"); + if (lpOALFnTable->alcCreateContext == NULL) + { + OutputDebugString("Failed to retrieve 'alcCreateContext' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcMakeContextCurrent = (LPALCMAKECONTEXTCURRENT)GetProcAddress(g_hOpenALDLL, "alcMakeContextCurrent"); + if (lpOALFnTable->alcMakeContextCurrent == NULL) + { + OutputDebugString("Failed to retrieve 'alcMakeContextCurrent' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcProcessContext = (LPALCPROCESSCONTEXT)GetProcAddress(g_hOpenALDLL, "alcProcessContext"); + if (lpOALFnTable->alcProcessContext == NULL) + { + OutputDebugString("Failed to retrieve 'alcProcessContext' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcGetCurrentContext = (LPALCGETCURRENTCONTEXT)GetProcAddress(g_hOpenALDLL, "alcGetCurrentContext"); + if (lpOALFnTable->alcGetCurrentContext == NULL) + { + OutputDebugString("Failed to retrieve 'alcGetCurrentContext' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcGetContextsDevice = (LPALCGETCONTEXTSDEVICE)GetProcAddress(g_hOpenALDLL, "alcGetContextsDevice"); + if (lpOALFnTable->alcGetContextsDevice == NULL) + { + OutputDebugString("Failed to retrieve 'alcGetContextsDevice' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcSuspendContext = (LPALCSUSPENDCONTEXT)GetProcAddress(g_hOpenALDLL, "alcSuspendContext"); + if (lpOALFnTable->alcSuspendContext == NULL) + { + OutputDebugString("Failed to retrieve 'alcSuspendContext' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcDestroyContext = (LPALCDESTROYCONTEXT)GetProcAddress(g_hOpenALDLL, "alcDestroyContext"); + if (lpOALFnTable->alcDestroyContext == NULL) + { + OutputDebugString("Failed to retrieve 'alcDestroyContext' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcGetError = (LPALCGETERROR)GetProcAddress(g_hOpenALDLL, "alcGetError"); + if (lpOALFnTable->alcGetError == NULL) + { + OutputDebugString("Failed to retrieve 'alcGetError' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcIsExtensionPresent = (LPALCISEXTENSIONPRESENT)GetProcAddress(g_hOpenALDLL, "alcIsExtensionPresent"); + if (lpOALFnTable->alcIsExtensionPresent == NULL) + { + OutputDebugString("Failed to retrieve 'alcIsExtensionPresent' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcGetProcAddress = (LPALCGETPROCADDRESS)GetProcAddress(g_hOpenALDLL, "alcGetProcAddress"); + if (lpOALFnTable->alcGetProcAddress == NULL) + { + OutputDebugString("Failed to retrieve 'alcGetProcAddress' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcGetEnumValue = (LPALCGETENUMVALUE)GetProcAddress(g_hOpenALDLL, "alcGetEnumValue"); + if (lpOALFnTable->alcGetEnumValue == NULL) + { + OutputDebugString("Failed to retrieve 'alcGetEnumValue' function address\n"); + return AL_FALSE; + } + + return AL_TRUE; +} + +ALvoid UnloadOAL10Library() +{ + // Unload the dll + if (g_hOpenALDLL) + { + FreeLibrary(g_hOpenALDLL); + g_hOpenALDLL = NULL; + } +} + +#endif diff --git a/src/win32/LoadOAL.h b/src/win32/LoadOAL.h new file mode 100644 index 0000000..59aa304 --- /dev/null +++ b/src/win32/LoadOAL.h @@ -0,0 +1,168 @@ +#ifndef NO_OAL + +#include +#include + +// Open AL Function table definition + +#ifndef _OPENALFNTABLE +#define _OPENALFNTABLE + +// AL 1.0 did not define the ALchar and ALCchar types, so define them here +// if they don't exist + +#ifndef ALchar +#define ALchar char +#endif + +#ifndef ALCchar +#define ALCchar char +#endif + +// Complete list of functions available in AL 1.0 implementations + +typedef void (ALAPIENTRY *LPALENABLE)( ALenum capability ); +typedef void (ALAPIENTRY *LPALDISABLE)( ALenum capability ); +typedef ALboolean (ALAPIENTRY *LPALISENABLED)( ALenum capability ); +typedef const ALchar* (ALAPIENTRY *LPALGETSTRING)( ALenum param ); +typedef void (ALAPIENTRY *LPALGETBOOLEANV)( ALenum param, ALboolean* data ); +typedef void (ALAPIENTRY *LPALGETINTEGERV)( ALenum param, ALint* data ); +typedef void (ALAPIENTRY *LPALGETFLOATV)( ALenum param, ALfloat* data ); +typedef void (ALAPIENTRY *LPALGETDOUBLEV)( ALenum param, ALdouble* data ); +typedef ALboolean (ALAPIENTRY *LPALGETBOOLEAN)( ALenum param ); +typedef ALint (ALAPIENTRY *LPALGETINTEGER)( ALenum param ); +typedef ALfloat (ALAPIENTRY *LPALGETFLOAT)( ALenum param ); +typedef ALdouble (ALAPIENTRY *LPALGETDOUBLE)( ALenum param ); +typedef ALenum (ALAPIENTRY *LPALGETERROR)( void ); +typedef ALboolean (ALAPIENTRY *LPALISEXTENSIONPRESENT)(const ALchar* extname ); +typedef void* (ALAPIENTRY *LPALGETPROCADDRESS)( const ALchar* fname ); +typedef ALenum (ALAPIENTRY *LPALGETENUMVALUE)( const ALchar* ename ); +typedef void (ALAPIENTRY *LPALLISTENERF)( ALenum param, ALfloat value ); +typedef void (ALAPIENTRY *LPALLISTENER3F)( ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 ); +typedef void (ALAPIENTRY *LPALLISTENERFV)( ALenum param, const ALfloat* values ); +typedef void (ALAPIENTRY *LPALLISTENERI)( ALenum param, ALint value ); +typedef void (ALAPIENTRY *LPALGETLISTENERF)( ALenum param, ALfloat* value ); +typedef void (ALAPIENTRY *LPALGETLISTENER3F)( ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3 ); +typedef void (ALAPIENTRY *LPALGETLISTENERFV)( ALenum param, ALfloat* values ); +typedef void (ALAPIENTRY *LPALGETLISTENERI)( ALenum param, ALint* value ); +typedef void (ALAPIENTRY *LPALGENSOURCES)( ALsizei n, ALuint* sources ); +typedef void (ALAPIENTRY *LPALDELETESOURCES)( ALsizei n, const ALuint* sources ); +typedef ALboolean (ALAPIENTRY *LPALISSOURCE)( ALuint sid ); +typedef void (ALAPIENTRY *LPALSOURCEF)( ALuint sid, ALenum param, ALfloat value); +typedef void (ALAPIENTRY *LPALSOURCE3F)( ALuint sid, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 ); +typedef void (ALAPIENTRY *LPALSOURCEFV)( ALuint sid, ALenum param, const ALfloat* values ); +typedef void (ALAPIENTRY *LPALSOURCEI)( ALuint sid, ALenum param, ALint value); +typedef void (ALAPIENTRY *LPALGETSOURCEF)( ALuint sid, ALenum param, ALfloat* value ); +typedef void (ALAPIENTRY *LPALGETSOURCE3F)( ALuint sid, ALenum param, ALfloat* value1, ALfloat* value2, ALfloat* value3); +typedef void (ALAPIENTRY *LPALGETSOURCEFV)( ALuint sid, ALenum param, ALfloat* values ); +typedef void (ALAPIENTRY *LPALGETSOURCEI)( ALuint sid, ALenum param, ALint* value ); +typedef void (ALAPIENTRY *LPALSOURCEPLAYV)( ALsizei ns, const ALuint *sids ); +typedef void (ALAPIENTRY *LPALSOURCESTOPV)( ALsizei ns, const ALuint *sids ); +typedef void (ALAPIENTRY *LPALSOURCEREWINDV)( ALsizei ns, const ALuint *sids ); +typedef void (ALAPIENTRY *LPALSOURCEPAUSEV)( ALsizei ns, const ALuint *sids ); +typedef void (ALAPIENTRY *LPALSOURCEPLAY)( ALuint sid ); +typedef void (ALAPIENTRY *LPALSOURCESTOP)( ALuint sid ); +typedef void (ALAPIENTRY *LPALSOURCEREWIND)( ALuint sid ); +typedef void (ALAPIENTRY *LPALSOURCEPAUSE)( ALuint sid ); +typedef void (ALAPIENTRY *LPALSOURCEQUEUEBUFFERS)(ALuint sid, ALsizei numEntries, const ALuint *bids ); +typedef void (ALAPIENTRY *LPALSOURCEUNQUEUEBUFFERS)(ALuint sid, ALsizei numEntries, ALuint *bids ); +typedef void (ALAPIENTRY *LPALGENBUFFERS)( ALsizei n, ALuint* buffers ); +typedef void (ALAPIENTRY *LPALDELETEBUFFERS)( ALsizei n, const ALuint* buffers ); +typedef ALboolean (ALAPIENTRY *LPALISBUFFER)( ALuint bid ); +typedef void (ALAPIENTRY *LPALBUFFERDATA)( ALuint bid, ALenum format, const ALvoid* data, ALsizei size, ALsizei freq ); +typedef void (ALAPIENTRY *LPALGETBUFFERF)( ALuint bid, ALenum param, ALfloat* value ); +typedef void (ALAPIENTRY *LPALGETBUFFERI)( ALuint bid, ALenum param, ALint* value ); +typedef void (ALAPIENTRY *LPALDOPPLERFACTOR)( ALfloat value ); +typedef void (ALAPIENTRY *LPALDOPPLERVELOCITY)( ALfloat value ); +typedef void (ALAPIENTRY *LPALDISTANCEMODEL)( ALenum distanceModel ); + +typedef ALCcontext * (ALCAPIENTRY *LPALCCREATECONTEXT) (ALCdevice *device, const ALCint *attrlist); +typedef ALCboolean (ALCAPIENTRY *LPALCMAKECONTEXTCURRENT)( ALCcontext *context ); +typedef void (ALCAPIENTRY *LPALCPROCESSCONTEXT)( ALCcontext *context ); +typedef void (ALCAPIENTRY *LPALCSUSPENDCONTEXT)( ALCcontext *context ); +typedef void (ALCAPIENTRY *LPALCDESTROYCONTEXT)( ALCcontext *context ); +typedef ALCcontext * (ALCAPIENTRY *LPALCGETCURRENTCONTEXT)( ALCvoid ); +typedef ALCdevice * (ALCAPIENTRY *LPALCGETCONTEXTSDEVICE)( ALCcontext *context ); +typedef ALCdevice * (ALCAPIENTRY *LPALCOPENDEVICE)( const ALCchar *devicename ); +typedef ALCboolean (ALCAPIENTRY *LPALCCLOSEDEVICE)( ALCdevice *device ); +typedef ALCenum (ALCAPIENTRY *LPALCGETERROR)( ALCdevice *device ); +typedef ALCboolean (ALCAPIENTRY *LPALCISEXTENSIONPRESENT)( ALCdevice *device, const ALCchar *extname ); +typedef void * (ALCAPIENTRY *LPALCGETPROCADDRESS)(ALCdevice *device, const ALCchar *funcname ); +typedef ALCenum (ALCAPIENTRY *LPALCGETENUMVALUE)(ALCdevice *device, const ALCchar *enumname ); +typedef const ALCchar* (ALCAPIENTRY *LPALCGETSTRING)( ALCdevice *device, ALCenum param ); +typedef void (ALCAPIENTRY *LPALCGETINTEGERV)( ALCdevice *device, ALCenum param, ALCsizei size, ALCint *dest ); + +typedef struct +{ + LPALENABLE alEnable; + LPALDISABLE alDisable; + LPALISENABLED alIsEnabled; + LPALGETBOOLEAN alGetBoolean; + LPALGETINTEGER alGetInteger; + LPALGETFLOAT alGetFloat; + LPALGETDOUBLE alGetDouble; + LPALGETBOOLEANV alGetBooleanv; + LPALGETINTEGERV alGetIntegerv; + LPALGETFLOATV alGetFloatv; + LPALGETDOUBLEV alGetDoublev; + LPALGETSTRING alGetString; + LPALGETERROR alGetError; + LPALISEXTENSIONPRESENT alIsExtensionPresent; + LPALGETPROCADDRESS alGetProcAddress; + LPALGETENUMVALUE alGetEnumValue; + LPALLISTENERI alListeneri; + LPALLISTENERF alListenerf; + LPALLISTENER3F alListener3f; + LPALLISTENERFV alListenerfv; + LPALGETLISTENERI alGetListeneri; + LPALGETLISTENERF alGetListenerf; + LPALGETLISTENER3F alGetListener3f; + LPALGETLISTENERFV alGetListenerfv; + LPALGENSOURCES alGenSources; + LPALDELETESOURCES alDeleteSources; + LPALISSOURCE alIsSource; + LPALSOURCEI alSourcei; + LPALSOURCEF alSourcef; + LPALSOURCE3F alSource3f; + LPALSOURCEFV alSourcefv; + LPALGETSOURCEI alGetSourcei; + LPALGETSOURCEF alGetSourcef; + LPALGETSOURCEFV alGetSourcefv; + LPALSOURCEPLAYV alSourcePlayv; + LPALSOURCESTOPV alSourceStopv; + LPALSOURCEPLAY alSourcePlay; + LPALSOURCEPAUSE alSourcePause; + LPALSOURCESTOP alSourceStop; + LPALGENBUFFERS alGenBuffers; + LPALDELETEBUFFERS alDeleteBuffers; + LPALISBUFFER alIsBuffer; + LPALBUFFERDATA alBufferData; + LPALGETBUFFERI alGetBufferi; + LPALGETBUFFERF alGetBufferf; + LPALSOURCEQUEUEBUFFERS alSourceQueueBuffers; + LPALSOURCEUNQUEUEBUFFERS alSourceUnqueueBuffers; + LPALDISTANCEMODEL alDistanceModel; + LPALDOPPLERFACTOR alDopplerFactor; + LPALDOPPLERVELOCITY alDopplerVelocity; + LPALCGETSTRING alcGetString; + LPALCGETINTEGERV alcGetIntegerv; + LPALCOPENDEVICE alcOpenDevice; + LPALCCLOSEDEVICE alcCloseDevice; + LPALCCREATECONTEXT alcCreateContext; + LPALCMAKECONTEXTCURRENT alcMakeContextCurrent; + LPALCPROCESSCONTEXT alcProcessContext; + LPALCGETCURRENTCONTEXT alcGetCurrentContext; + LPALCGETCONTEXTSDEVICE alcGetContextsDevice; + LPALCSUSPENDCONTEXT alcSuspendContext; + LPALCDESTROYCONTEXT alcDestroyContext; + LPALCGETERROR alcGetError; + LPALCISEXTENSIONPRESENT alcIsExtensionPresent; + LPALCGETPROCADDRESS alcGetProcAddress; + LPALCGETENUMVALUE alcGetEnumValue; +} OPENALFNTABLE, *LPOPENALFNTABLE; +#endif + +ALboolean LoadOAL10Library(char *szOALFullPathName, LPOPENALFNTABLE lpOALFnTable); +ALvoid UnloadOAL10Library(); + +#endif diff --git a/src/win32/Logging.cpp b/src/win32/Logging.cpp new file mode 100644 index 0000000..08c85dc --- /dev/null +++ b/src/win32/Logging.cpp @@ -0,0 +1,295 @@ +#include "stdafx.h" +#include "vba.h" + +#include "FileDlg.h" +#include "Logging.h" + +#include "../gba/Globals.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// Logging dialog + +Logging *Logging::instance = NULL; +CString Logging::text; + +Logging::Logging(CWnd* pParent /*=NULL*/) + : ResizeDlg(Logging::IDD, pParent) + , m_sound_output(FALSE) +{ + //{{AFX_DATA_INIT(Logging) + m_swi = FALSE; + m_unaligned_access = FALSE; + m_illegal_write = FALSE; + m_illegal_read = FALSE; + m_dma0 = FALSE; + m_dma1 = FALSE; + m_dma2 = FALSE; + m_dma3 = FALSE; + m_agbprint = FALSE; + m_undefined = FALSE; + //}}AFX_DATA_INIT +} + + +void Logging::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + DDX_Control(pDX, IDC_LOG, m_log); + DDX_Check(pDX, IDC_VERBOSE_SWI, m_swi); + DDX_Check(pDX, IDC_VERBOSE_UNALIGNED_ACCESS, m_unaligned_access); + DDX_Check(pDX, IDC_VERBOSE_ILLEGAL_WRITE, m_illegal_write); + DDX_Check(pDX, IDC_VERBOSE_ILLEGAL_READ, m_illegal_read); + DDX_Check(pDX, IDC_VERBOSE_DMA0, m_dma0); + DDX_Check(pDX, IDC_VERBOSE_DMA1, m_dma1); + DDX_Check(pDX, IDC_VERBOSE_DMA2, m_dma2); + DDX_Check(pDX, IDC_VERBOSE_DMA3, m_dma3); + DDX_Check(pDX, IDC_VERBOSE_AGBPRINT, m_agbprint); + DDX_Check(pDX, IDC_VERBOSE_UNDEFINED, m_undefined); + DDX_Check(pDX, IDC_VERBOSE_SOUNDOUTPUT, m_sound_output); +} + + +BEGIN_MESSAGE_MAP(Logging, CDialog) + ON_BN_CLICKED(ID_OK, OnOk) + ON_BN_CLICKED(IDC_CLEAR, OnClear) + ON_BN_CLICKED(IDC_VERBOSE_AGBPRINT, OnVerboseAgbprint) + ON_BN_CLICKED(IDC_VERBOSE_DMA0, OnVerboseDma0) + ON_BN_CLICKED(IDC_VERBOSE_DMA1, OnVerboseDma1) + ON_BN_CLICKED(IDC_VERBOSE_DMA2, OnVerboseDma2) + ON_BN_CLICKED(IDC_VERBOSE_DMA3, OnVerboseDma3) + ON_BN_CLICKED(IDC_VERBOSE_ILLEGAL_READ, OnVerboseIllegalRead) + ON_BN_CLICKED(IDC_VERBOSE_ILLEGAL_WRITE, OnVerboseIllegalWrite) + ON_BN_CLICKED(IDC_VERBOSE_SWI, OnVerboseSwi) + ON_BN_CLICKED(IDC_VERBOSE_UNALIGNED_ACCESS, OnVerboseUnalignedAccess) + ON_BN_CLICKED(IDC_VERBOSE_UNDEFINED, OnVerboseUndefined) + ON_BN_CLICKED(IDC_VERBOSE_SOUNDOUTPUT, OnVerboseSoundoutput) + ON_BN_CLICKED(IDC_SAVE, OnSave) + ON_EN_ERRSPACE(IDC_LOG, OnErrspaceLog) + ON_EN_MAXTEXT(IDC_LOG, OnMaxtextLog) + ON_WM_CLOSE() +END_MESSAGE_MAP() + + +///////////////////////////////////////////////////////////////////////////// +// Logging message handlers + +void Logging::OnOk() +{ + EndDialog(TRUE); + + instance = NULL; +} + +void Logging::OnClear() +{ + text = ""; + m_log.SetWindowText(""); +} + +void Logging::OnVerboseSwi() +{ + systemVerbose ^= VERBOSE_SWI; +} + +void Logging::OnVerboseUnalignedAccess() +{ + systemVerbose ^= VERBOSE_UNALIGNED_MEMORY; +} + +void Logging::OnVerboseIllegalWrite() +{ + systemVerbose ^= VERBOSE_ILLEGAL_WRITE; +} + +void Logging::OnVerboseIllegalRead() +{ + systemVerbose ^= VERBOSE_ILLEGAL_READ; +} + +void Logging::OnVerboseDma0() +{ + systemVerbose ^= VERBOSE_DMA0; +} + +void Logging::OnVerboseDma1() +{ + systemVerbose ^= VERBOSE_DMA1; +} + +void Logging::OnVerboseDma2() +{ + systemVerbose ^= VERBOSE_DMA2; +} + +void Logging::OnVerboseDma3() +{ + systemVerbose ^= VERBOSE_DMA3; +} + +void Logging::OnVerboseUndefined() +{ + systemVerbose ^= VERBOSE_UNDEFINED; +} + +void Logging::OnVerboseAgbprint() +{ + systemVerbose ^= VERBOSE_AGBPRINT; +} + +void Logging::OnVerboseSoundoutput() +{ + systemVerbose ^= VERBOSE_SOUNDOUTPUT; +} + +void Logging::OnSave() +{ + int len = m_log.GetWindowTextLength(); + + char *mem = (char *)malloc(len); + + if(mem) { + LPCTSTR exts[] = { ".txt" }; + m_log.GetWindowText(mem, len); + CString filter = "All Files|*.*||"; + FileDlg dlg(this, "", filter, 0, + NULL, exts, NULL, "Save output", true); + + if(dlg.DoModal() == IDOK) { + FILE *f = fopen(dlg.GetPathName(), "w"); + if(f) { + fwrite(mem, 1, len, f); + fclose(f); + } + } + } + + free(mem); +} + +void Logging::OnErrspaceLog() +{ + systemMessage(0, "Error allocating space"); +} + +void Logging::OnMaxtextLog() +{ + systemMessage(0, "Max text length reached %d", m_log.GetLimitText()); +} + +void Logging::PostNcDestroy() +{ + delete this; +} + +BOOL Logging::OnInitDialog() +{ + CDialog::OnInitDialog(); + + DIALOG_SIZER_START( sz ) + DIALOG_SIZER_ENTRY( IDC_LOG, DS_SizeY|DS_SizeX) + DIALOG_SIZER_ENTRY( ID_OK, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_CLEAR, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_SAVE, DS_MoveY) + DIALOG_SIZER_END() + SetData(sz, + TRUE, + HKEY_CURRENT_USER, + "Software\\Emulators\\VisualBoyAdvance\\Viewer\\LogView", + NULL); + m_swi = (systemVerbose & VERBOSE_SWI) != 0; + m_unaligned_access = (systemVerbose & VERBOSE_UNALIGNED_MEMORY) != 0; + m_illegal_write = (systemVerbose & VERBOSE_ILLEGAL_WRITE) != 0; + m_illegal_read = (systemVerbose & VERBOSE_ILLEGAL_READ) != 0; + m_dma0 = (systemVerbose & VERBOSE_DMA0) != 0; + m_dma1 = (systemVerbose & VERBOSE_DMA1) != 0; + m_dma2 = (systemVerbose & VERBOSE_DMA2) != 0; + m_dma3 = (systemVerbose & VERBOSE_DMA3) != 0; + m_undefined = (systemVerbose & VERBOSE_UNDEFINED) != 0; + m_agbprint = (systemVerbose & VERBOSE_AGBPRINT) != 0; + m_sound_output = (systemVerbose & VERBOSE_SOUNDOUTPUT) != 0; + UpdateData(FALSE); + + m_log.LimitText(-1); + m_log.SetWindowText(text); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void Logging::log(const char *s) +{ + CString text; + m_log.GetWindowText( text ); + text.Insert( 0, s ); + m_log.SetWindowText( text ); +} + +void Logging::clearLog() +{ + m_log.SetWindowText( "" ); +} + +void Logging::OnClose() +{ + EndDialog(FALSE); + + instance = NULL; + + CDialog::OnClose(); +} + +void toolsLogging() +{ + if(Logging::instance == NULL) { + Logging::instance = new Logging(); + Logging::instance->Create(IDD_LOGGING, AfxGetApp()->m_pMainWnd); + Logging::instance->ShowWindow(SW_SHOW); + } else { + Logging::instance->SetForegroundWindow(); + } +} + +void toolsLoggingClose() +{ + if(Logging::instance != NULL) { + Logging::instance->DestroyWindow(); + Logging::instance = NULL; + } +} + +void toolsLog(const char *s) +{ + CString str; + int state = 0; + if(s) { + char c = *s++; + while(c) { + if(c == '\n' && state == 0) + str += '\r'; + else if(c == '\r') + state = 1; + else + state = 0; + str += c; + c = *s++; + } + } + + Logging::text += str; + if(Logging::instance != NULL) { + Logging::instance->log(str); + } +} + +void toolsClearLog() +{ + if( Logging::instance ) { + Logging::instance->clearLog(); + } +} diff --git a/src/win32/Logging.h b/src/win32/Logging.h new file mode 100644 index 0000000..23ab2ec --- /dev/null +++ b/src/win32/Logging.h @@ -0,0 +1,65 @@ +#pragma once + +#include "ResizeDlg.h" + + +///////////////////////////////////////////////////////////////////////////// +// Logging dialog + +class Logging : public ResizeDlg +{ +public: + void log(const char *); + void clearLog(); + Logging(CWnd* pParent = NULL); // standard constructor + + // Dialog Data + enum { IDD = IDD_LOGGING }; + CEdit m_log; + BOOL m_swi; + BOOL m_unaligned_access; + BOOL m_illegal_write; + BOOL m_illegal_read; + BOOL m_dma0; + BOOL m_dma1; + BOOL m_dma2; + BOOL m_dma3; + BOOL m_agbprint; + BOOL m_undefined; + BOOL m_sound_output; + +// Overrides +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual void PostNcDestroy(); + +protected: + afx_msg void OnOk(); + afx_msg void OnClear(); + afx_msg void OnVerboseAgbprint(); + afx_msg void OnVerboseDma0(); + afx_msg void OnVerboseDma1(); + afx_msg void OnVerboseDma2(); + afx_msg void OnVerboseDma3(); + afx_msg void OnVerboseIllegalRead(); + afx_msg void OnVerboseIllegalWrite(); + afx_msg void OnVerboseSwi(); + afx_msg void OnVerboseUnalignedAccess(); + afx_msg void OnVerboseUndefined(); + afx_msg void OnVerboseSoundoutput(); + afx_msg void OnSave(); + afx_msg void OnClose(); + afx_msg void OnErrspaceLog(); + afx_msg void OnMaxtextLog(); + virtual BOOL OnInitDialog(); + + DECLARE_MESSAGE_MAP() +public: + static Logging *instance; + static CString text; +}; + +void toolsLogging(); +void toolsLoggingClose(); +void toolsLog(const char *s); +void toolsClearLog(); diff --git a/src/win32/MainWnd.cpp b/src/win32/MainWnd.cpp new file mode 100644 index 0000000..7341636 --- /dev/null +++ b/src/win32/MainWnd.cpp @@ -0,0 +1,1308 @@ +#include "stdafx.h" +#include "VBA.h" +#include "MainWnd.h" + +#include + +#include "FileDlg.h" +#include "Reg.h" +#include "WinResUtil.h" +#include "Logging.h" + +#include "../System.h" +#include "../AutoBuild.h" +#include "../gba/cheatSearch.h" +#include "../gba/GBA.h" +#include "../gba/Globals.h" +#include "../gba/Flash.h" +#include "../gba/Globals.h" +#include "../gb/GB.h" +#include "../gb/gbSound.h" +#include "../gb/gbCheats.h" +#include "../gb/gbGlobals.h" +#include "../gba/RTC.h" +#include "../gba/Sound.h" +#include "../Util.h" +#include "../gba/GBALink.h" +#include "../common/Patch.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +extern void remoteCleanUp(); +extern int gbHardware; + +///////////////////////////////////////////////////////////////////////////// +// MainWnd + +MainWnd::MainWnd() +{ + m_hAccelTable = NULL; + arrow = LoadCursor(NULL, IDC_ARROW); +} + +MainWnd::~MainWnd() +{ +} + +bool MainWnd::fileExists( LPCTSTR lpFileName ) +{ + // check if file exists + return GetFileAttributes( lpFileName ) != INVALID_FILE_ATTRIBUTES; +} + + +BEGIN_MESSAGE_MAP(MainWnd, CWnd) + //{{AFX_MSG_MAP(MainWnd) + ON_WM_CLOSE() + ON_COMMAND(ID_HELP_ABOUT, OnHelpAbout) + ON_COMMAND(ID_HELP_FAQ, OnHelpFaq) + ON_COMMAND(ID_FILE_OPEN_GBA, OnFileOpenGBA) + ON_COMMAND(ID_FILE_OPEN_GBC, OnFileOpenGBC) + ON_COMMAND(ID_FILE_OPEN_GB, OnFileOpenGB) + ON_WM_INITMENUPOPUP() + ON_COMMAND(ID_FILE_PAUSE, OnFilePause) + ON_UPDATE_COMMAND_UI(ID_FILE_PAUSE, OnUpdateFilePause) + ON_COMMAND(ID_FILE_RESET, OnFileReset) + ON_UPDATE_COMMAND_UI(ID_FILE_RESET, OnUpdateFileReset) + ON_UPDATE_COMMAND_UI(ID_FILE_RECENT_FREEZE, OnUpdateFileRecentFreeze) + ON_COMMAND(ID_FILE_RECENT_RESET, OnFileRecentReset) + ON_COMMAND(ID_FILE_RECENT_FREEZE, OnFileRecentFreeze) + ON_COMMAND(ID_FILE_EXIT, OnFileExit) + ON_COMMAND(ID_FILE_CLOSE, OnFileClose) + ON_UPDATE_COMMAND_UI(ID_FILE_CLOSE, OnUpdateFileClose) + ON_COMMAND(ID_FILE_LOAD, OnFileLoad) + ON_UPDATE_COMMAND_UI(ID_FILE_LOAD, OnUpdateFileLoad) + ON_COMMAND(ID_FILE_SAVE, OnFileSave) + ON_UPDATE_COMMAND_UI(ID_FILE_SAVE, OnUpdateFileSave) + ON_COMMAND(ID_FILE_IMPORT_BATTERYFILE, OnFileImportBatteryfile) + ON_UPDATE_COMMAND_UI(ID_FILE_IMPORT_BATTERYFILE, OnUpdateFileImportBatteryfile) + ON_COMMAND(ID_FILE_IMPORT_GAMESHARKCODEFILE, OnFileImportGamesharkcodefile) + ON_UPDATE_COMMAND_UI(ID_FILE_IMPORT_GAMESHARKCODEFILE, OnUpdateFileImportGamesharkcodefile) + ON_COMMAND(ID_FILE_IMPORT_GAMESHARKSNAPSHOT, OnFileImportGamesharksnapshot) + ON_UPDATE_COMMAND_UI(ID_FILE_IMPORT_GAMESHARKSNAPSHOT, OnUpdateFileImportGamesharksnapshot) + ON_COMMAND(ID_FILE_EXPORT_BATTERYFILE, OnFileExportBatteryfile) + ON_UPDATE_COMMAND_UI(ID_FILE_EXPORT_BATTERYFILE, OnUpdateFileExportBatteryfile) + ON_COMMAND(ID_FILE_EXPORT_GAMESHARKSNAPSHOT, OnFileExportGamesharksnapshot) + ON_UPDATE_COMMAND_UI(ID_FILE_EXPORT_GAMESHARKSNAPSHOT, OnUpdateFileExportGamesharksnapshot) + ON_COMMAND(ID_FILE_SCREENCAPTURE, OnFileScreencapture) + ON_UPDATE_COMMAND_UI(ID_FILE_SCREENCAPTURE, OnUpdateFileScreencapture) + ON_COMMAND(ID_FILE_ROMINFORMATION, OnFileRominformation) + ON_UPDATE_COMMAND_UI(ID_FILE_ROMINFORMATION, OnUpdateFileRominformation) + ON_COMMAND(ID_FILE_TOGGLEMENU, OnFileTogglemenu) + ON_UPDATE_COMMAND_UI(ID_FILE_TOGGLEMENU, OnUpdateFileTogglemenu) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_FRAMESKIP_THROTTLE_NOTHROTTLE, OnUpdateOptionsFrameskipThrottleNothrottle) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_FRAMESKIP_THROTTLE_25, OnUpdateOptionsFrameskipThrottle25) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_FRAMESKIP_THROTTLE_50, OnUpdateOptionsFrameskipThrottle50) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_FRAMESKIP_THROTTLE_100, OnUpdateOptionsFrameskipThrottle100) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_FRAMESKIP_THROTTLE_150, OnUpdateOptionsFrameskipThrottle150) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_FRAMESKIP_THROTTLE_200, OnUpdateOptionsFrameskipThrottle200) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_FRAMESKIP_THROTTLE_OTHER, OnUpdateOptionsFrameskipThrottleOther) + ON_COMMAND(ID_OPTIONS_FRAMESKIP_THROTTLE_NOTHROTTLE, OnOptionsFrameskipThrottleNothrottle) + ON_COMMAND(ID_OPTIONS_FRAMESKIP_THROTTLE_25, OnOptionsFrameskipThrottle25) + ON_COMMAND(ID_OPTIONS_FRAMESKIP_THROTTLE_50, OnOptionsFrameskipThrottle50) + ON_COMMAND(ID_OPTIONS_FRAMESKIP_THROTTLE_100, OnOptionsFrameskipThrottle100) + ON_COMMAND(ID_OPTIONS_FRAMESKIP_THROTTLE_150, OnOptionsFrameskipThrottle150) + ON_COMMAND(ID_OPTIONS_FRAMESKIP_THROTTLE_200, OnOptionsFrameskipThrottle200) + ON_COMMAND(ID_OPTIONS_FRAMESKIP_THROTTLE_OTHER, OnOptionsFrameskipThrottleOther) + ON_COMMAND(ID_OPTIONS_FRAMESKIP_AUTOMATIC, OnOptionsFrameskipAutomatic) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_FRAMESKIP_AUTOMATIC, OnUpdateOptionsFrameskipAutomatic) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_VIDEO_FRAMESKIP_0, OnUpdateOptionsVideoFrameskip0) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_VIDEO_FRAMESKIP_1, OnUpdateOptionsVideoFrameskip1) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_VIDEO_FRAMESKIP_2, OnUpdateOptionsVideoFrameskip2) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_VIDEO_FRAMESKIP_3, OnUpdateOptionsVideoFrameskip3) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_VIDEO_FRAMESKIP_4, OnUpdateOptionsVideoFrameskip4) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_VIDEO_FRAMESKIP_5, OnUpdateOptionsVideoFrameskip5) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_VIDEO_FRAMESKIP_6, OnUpdateOptionsVideoFrameskip6) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_VIDEO_FRAMESKIP_7, OnUpdateOptionsVideoFrameskip7) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_VIDEO_FRAMESKIP_8, OnUpdateOptionsVideoFrameskip8) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_VIDEO_FRAMESKIP_9, OnUpdateOptionsVideoFrameskip9) + ON_COMMAND(ID_OPTIONS_VIDEO_VSYNC, OnOptionsVideoVsync) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_VIDEO_VSYNC, OnUpdateOptionsVideoVsync) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_VIDEO_X1, OnUpdateOptionsVideoX1) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_VIDEO_X2, OnUpdateOptionsVideoX2) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_VIDEO_X3, OnUpdateOptionsVideoX3) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_VIDEO_X4, OnUpdateOptionsVideoX4) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_VIDEO_X5, OnUpdateOptionsVideoX5) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_VIDEO_X6, OnUpdateOptionsVideoX6) + ON_COMMAND(ID_OPTIONS_VIDEO_FULLSCREEN, OnOptionsVideoFullscreen) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_VIDEO_FULLSCREEN, OnUpdateOptionsVideoFullscreen) + ON_WM_MOVING() + ON_WM_MOVE() + ON_WM_SIZING() + ON_WM_SIZE() + ON_COMMAND(ID_OPTIONS_VIDEO_DISABLESFX, OnOptionsVideoDisablesfx) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_VIDEO_DISABLESFX, OnUpdateOptionsVideoDisablesfx) + ON_COMMAND(ID_OPTIONS_VIDEO_FULLSCREENSTRETCHTOFIT, OnOptionsVideoFullscreenstretchtofit) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_VIDEO_FULLSCREENSTRETCHTOFIT, OnUpdateOptionsVideoFullscreenstretchtofit) + ON_COMMAND(ID_OPTIONS_VIDEO_RENDERMETHOD_DIRECT3D, OnOptionsVideoRendermethodDirect3d) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_VIDEO_RENDERMETHOD_DIRECT3D, OnUpdateOptionsVideoRendermethodDirect3d) + ON_COMMAND(ID_OPTIONS_VIDEO_RENDERMETHOD_OPENGL, OnOptionsVideoRendermethodOpengl) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_VIDEO_RENDERMETHOD_OPENGL, OnUpdateOptionsVideoRendermethodOpengl) + ON_COMMAND(ID_OPTIONS_VIDEO_TRIPLEBUFFERING, OnOptionsVideoTriplebuffering) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_VIDEO_TRIPLEBUFFERING, OnUpdateOptionsVideoTriplebuffering) + ON_COMMAND(ID_OPTIONS_VIDEO_RENDEROPTIONS_D3DNOFILTER, OnOptionsVideoRenderoptionsD3dnofilter) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_VIDEO_RENDEROPTIONS_D3DNOFILTER, OnUpdateOptionsVideoRenderoptionsD3dnofilter) + ON_COMMAND(ID_OPTIONS_VIDEO_RENDEROPTIONS_D3DBILINEAR, OnOptionsVideoRenderoptionsD3dbilinear) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_VIDEO_RENDEROPTIONS_D3DBILINEAR, OnUpdateOptionsVideoRenderoptionsD3dbilinear) + ON_COMMAND(ID_OPTIONS_VIDEO_RENDEROPTIONS_GLNEAREST, OnOptionsVideoRenderoptionsGlnearest) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_VIDEO_RENDEROPTIONS_GLNEAREST, OnUpdateOptionsVideoRenderoptionsGlnearest) + ON_COMMAND(ID_OPTIONS_VIDEO_RENDEROPTIONS_GLBILINEAR, OnOptionsVideoRenderoptionsGlbilinear) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_VIDEO_RENDEROPTIONS_GLBILINEAR, OnUpdateOptionsVideoRenderoptionsGlbilinear) + + ON_WM_CONTEXTMENU() + ON_COMMAND(ID_OPTIONS_EMULATOR_ASSOCIATE, OnOptionsEmulatorAssociate) + ON_COMMAND(ID_OPTIONS_EMULATOR_DIRECTORIES, OnOptionsEmulatorDirectories) + ON_COMMAND(ID_OPTIONS_EMULATOR_DISABLESTATUSMESSAGES, OnOptionsEmulatorDisablestatusmessages) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_EMULATOR_DISABLESTATUSMESSAGES, OnUpdateOptionsEmulatorDisablestatusmessages) + ON_COMMAND(ID_OPTIONS_EMULATOR_SYNCHRONIZE, OnOptionsEmulatorSynchronize) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_EMULATOR_SYNCHRONIZE, OnUpdateOptionsEmulatorSynchronize) + ON_COMMAND(ID_OPTIONS_EMULATOR_PAUSEWHENINACTIVE, OnOptionsEmulatorPausewheninactive) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_EMULATOR_PAUSEWHENINACTIVE, OnUpdateOptionsEmulatorPausewheninactive) + ON_COMMAND(ID_OPTIONS_EMULATOR_SPEEDUPTOGGLE, OnOptionsEmulatorSpeeduptoggle) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_EMULATOR_SPEEDUPTOGGLE, OnUpdateOptionsEmulatorSpeeduptoggle) + ON_COMMAND(ID_OPTIONS_EMULATOR_AUTOMATICALLYAPPLYPATCHFILES, OnOptionsEmulatorAutomaticallyApplyPatchFiles) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_EMULATOR_AUTOMATICALLYAPPLYPATCHFILES, OnUpdateOptionsEmulatorAutomaticallyipspatch) + ON_COMMAND(ID_OPTIONS_EMULATOR_AGBPRINT, OnOptionsEmulatorAgbprint) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_EMULATOR_AGBPRINT, OnUpdateOptionsEmulatorAgbprint) + ON_COMMAND(ID_OPTIONS_EMULATOR_REALTIMECLOCK, OnOptionsEmulatorRealtimeclock) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_EMULATOR_REALTIMECLOCK, OnUpdateOptionsEmulatorRealtimeclock) + ON_COMMAND(ID_OPTIONS_EMULATOR_REWINDINTERVAL, OnOptionsEmulatorRewindinterval) + + ON_COMMAND(ID_OPTIONS_EMULATOR_SAVETYPE_AUTOMATIC, OnOptionsEmulatorSavetypeAutomatic) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_EMULATOR_SAVETYPE_AUTOMATIC, OnUpdateOptionsEmulatorSavetypeAutomatic) + ON_COMMAND(ID_OPTIONS_EMULATOR_SAVETYPE_EEPROM, OnOptionsEmulatorSavetypeEeprom) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_EMULATOR_SAVETYPE_EEPROM, OnUpdateOptionsEmulatorSavetypeEeprom) + ON_COMMAND(ID_OPTIONS_EMULATOR_SAVETYPE_SRAM, OnOptionsEmulatorSavetypeSram) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_EMULATOR_SAVETYPE_SRAM, OnUpdateOptionsEmulatorSavetypeSram) + ON_COMMAND(ID_OPTIONS_EMULATOR_SAVETYPE_FLASH, OnOptionsEmulatorSavetypeFlash) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_EMULATOR_SAVETYPE_FLASH, OnUpdateOptionsEmulatorSavetypeFlash) + ON_COMMAND(ID_OPTIONS_EMULATOR_SAVETYPE_EEPROMSENSOR, OnOptionsEmulatorSavetypeEepromsensor) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_EMULATOR_SAVETYPE_EEPROMSENSOR, OnUpdateOptionsEmulatorSavetypeEepromsensor) + ON_COMMAND(ID_OPTIONS_EMULATOR_SAVETYPE_NONE, OnOptionsEmulatorSavetypeNone) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_EMULATOR_SAVETYPE_NONE, OnUpdateOptionsEmulatorSavetypeNone) + ON_COMMAND(ID_OPTIONS_EMULATOR_SAVETYPE_FLASH512K, OnOptionsEmulatorSavetypeFlash512k) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_EMULATOR_SAVETYPE_FLASH512K, OnUpdateOptionsEmulatorSavetypeFlash512k) + ON_COMMAND(ID_OPTIONS_EMULATOR_SAVETYPE_FLASH1M, OnOptionsEmulatorSavetypeFlash1m) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_EMULATOR_SAVETYPE_FLASH1M, OnUpdateOptionsEmulatorSavetypeFlash1m) + ON_COMMAND(ID_OPTIONS_EMULATOR_SAVETYPE_DETECTNOW, OnOptionsEmulatorSavetypeDetectNow) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_EMULATOR_SAVETYPE_DETECTNOW, OnUpdateOptionsEmulatorSavetypeDetectNow) + + ON_COMMAND(ID_OPTIONS_EMULATOR_PNGFORMAT, OnOptionsEmulatorPngformat) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_EMULATOR_PNGFORMAT, OnUpdateOptionsEmulatorPngformat) + ON_COMMAND(ID_OPTIONS_EMULATOR_BMPFORMAT, OnOptionsEmulatorBmpformat) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_EMULATOR_BMPFORMAT, OnUpdateOptionsEmulatorBmpformat) + ON_COMMAND(ID_OPTIONS_SOUND_CHANNEL1, OnOptionsSoundChannel1) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_SOUND_CHANNEL1, OnUpdateOptionsSoundChannel1) + ON_COMMAND(ID_OPTIONS_SOUND_CHANNEL2, OnOptionsSoundChannel2) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_SOUND_CHANNEL2, OnUpdateOptionsSoundChannel2) + ON_COMMAND(ID_OPTIONS_SOUND_CHANNEL3, OnOptionsSoundChannel3) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_SOUND_CHANNEL3, OnUpdateOptionsSoundChannel3) + ON_COMMAND(ID_OPTIONS_SOUND_CHANNEL4, OnOptionsSoundChannel4) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_SOUND_CHANNEL4, OnUpdateOptionsSoundChannel4) + ON_COMMAND(ID_OPTIONS_SOUND_DIRECTSOUNDA, OnOptionsSoundDirectsounda) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_SOUND_DIRECTSOUNDA, OnUpdateOptionsSoundDirectsounda) + ON_COMMAND(ID_OPTIONS_SOUND_DIRECTSOUNDB, OnOptionsSoundDirectsoundb) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_SOUND_DIRECTSOUNDB, OnUpdateOptionsSoundDirectsoundb) + ON_COMMAND(ID_OPTIONS_GAMEBOY_BORDER, OnOptionsGameboyBorder) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_GAMEBOY_BORDER, OnUpdateOptionsGameboyBorder) + ON_COMMAND(ID_OPTIONS_GAMEBOY_PRINTER, OnOptionsGameboyPrinter) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_GAMEBOY_PRINTER, OnUpdateOptionsGameboyPrinter) + ON_COMMAND(ID_OPTIONS_GAMEBOY_BORDERAUTOMATIC, OnOptionsGameboyBorderAutomatic) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_GAMEBOY_BORDERAUTOMATIC, OnUpdateOptionsGameboyBorderAutomatic) + ON_COMMAND(ID_OPTIONS_GAMEBOY_AUTOMATIC, OnOptionsGameboyAutomatic) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_GAMEBOY_AUTOMATIC, OnUpdateOptionsGameboyAutomatic) + ON_COMMAND(ID_OPTIONS_GAMEBOY_GBA, OnOptionsGameboyGba) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_GAMEBOY_GBA, OnUpdateOptionsGameboyGba) + ON_COMMAND(ID_OPTIONS_GAMEBOY_CGB, OnOptionsGameboyCgb) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_GAMEBOY_CGB, OnUpdateOptionsGameboyCgb) + ON_COMMAND(ID_OPTIONS_GAMEBOY_SGB, OnOptionsGameboySgb) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_GAMEBOY_SGB, OnUpdateOptionsGameboySgb) + ON_COMMAND(ID_OPTIONS_GAMEBOY_SGB2, OnOptionsGameboySgb2) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_GAMEBOY_SGB2, OnUpdateOptionsGameboySgb2) + ON_COMMAND(ID_OPTIONS_GAMEBOY_GB, OnOptionsGameboyGb) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_GAMEBOY_GB, OnUpdateOptionsGameboyGb) + ON_COMMAND(ID_OPTIONS_GAMEBOY_REALCOLORS, OnOptionsGameboyRealcolors) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_GAMEBOY_REALCOLORS, OnUpdateOptionsGameboyRealcolors) + ON_COMMAND(ID_OPTIONS_GAMEBOY_GAMEBOYCOLORS, OnOptionsGameboyGameboycolors) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_GAMEBOY_GAMEBOYCOLORS, OnUpdateOptionsGameboyGameboycolors) + ON_COMMAND(ID_OPTIONS_GAMEBOY_COLORS, OnOptionsGameboyColors) + ON_COMMAND(ID_OPTIONS_FILTER_DISABLEMMX, OnOptionsFilterDisablemmx) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_FILTER_DISABLEMMX, OnUpdateOptionsFilterDisablemmx) + ON_COMMAND(ID_OPTIONS_LANGUAGE_SYSTEM, OnOptionsLanguageSystem) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_LANGUAGE_SYSTEM, OnUpdateOptionsLanguageSystem) + ON_COMMAND(ID_OPTIONS_LANGUAGE_ENGLISH, OnOptionsLanguageEnglish) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_LANGUAGE_ENGLISH, OnUpdateOptionsLanguageEnglish) + ON_COMMAND(ID_OPTIONS_LANGUAGE_OTHER, OnOptionsLanguageOther) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_LANGUAGE_OTHER, OnUpdateOptionsLanguageOther) + ON_COMMAND(ID_OPTIONS_JOYPAD_CONFIGURE_1, OnOptionsJoypadConfigure1) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_JOYPAD_CONFIGURE_1, OnUpdateOptionsJoypadConfigure1) + ON_COMMAND(ID_OPTIONS_JOYPAD_CONFIGURE_2, OnOptionsJoypadConfigure2) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_JOYPAD_CONFIGURE_2, OnUpdateOptionsJoypadConfigure2) + ON_COMMAND(ID_OPTIONS_JOYPAD_CONFIGURE_3, OnOptionsJoypadConfigure3) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_JOYPAD_CONFIGURE_3, OnUpdateOptionsJoypadConfigure3) + ON_COMMAND(ID_OPTIONS_JOYPAD_CONFIGURE_4, OnOptionsJoypadConfigure4) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_JOYPAD_CONFIGURE_4, OnUpdateOptionsJoypadConfigure4) + ON_COMMAND(ID_OPTIONS_JOYPAD_MOTIONCONFIGURE, OnOptionsJoypadMotionconfigure) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_JOYPAD_MOTIONCONFIGURE, OnUpdateOptionsJoypadMotionconfigure) + ON_COMMAND(ID_CHEATS_SEARCHFORCHEATS, OnCheatsSearchforcheats) + ON_UPDATE_COMMAND_UI(ID_CHEATS_SEARCHFORCHEATS, OnUpdateCheatsSearchforcheats) + ON_COMMAND(ID_CHEATS_CHEATLIST, OnCheatsCheatlist) + ON_UPDATE_COMMAND_UI(ID_CHEATS_CHEATLIST, OnUpdateCheatsCheatlist) + ON_COMMAND(ID_CHEATS_AUTOMATICSAVELOADCHEATS, OnCheatsAutomaticsaveloadcheats) + ON_COMMAND(ID_CHEATS_LOADCHEATLIST, OnCheatsLoadcheatlist) + ON_UPDATE_COMMAND_UI(ID_CHEATS_LOADCHEATLIST, OnUpdateCheatsLoadcheatlist) + ON_COMMAND(ID_CHEATS_SAVECHEATLIST, OnCheatsSavecheatlist) + ON_UPDATE_COMMAND_UI(ID_CHEATS_SAVECHEATLIST, OnUpdateCheatsSavecheatlist) + ON_COMMAND(ID_TOOLS_DISASSEMBLE, OnToolsDisassemble) + ON_UPDATE_COMMAND_UI(ID_TOOLS_DISASSEMBLE, OnUpdateToolsDisassemble) + ON_COMMAND(ID_TOOLS_LOGGING, OnToolsLogging) + ON_UPDATE_COMMAND_UI(ID_TOOLS_LOGGING, OnUpdateToolsLogging) + ON_COMMAND(ID_TOOLS_IOVIEWER, OnToolsIoviewer) + ON_UPDATE_COMMAND_UI(ID_TOOLS_IOVIEWER, OnUpdateToolsIoviewer) + ON_COMMAND(ID_TOOLS_MAPVIEW, OnToolsMapview) + ON_UPDATE_COMMAND_UI(ID_TOOLS_MAPVIEW, OnUpdateToolsMapview) + ON_COMMAND(ID_TOOLS_MEMORYVIEWER, OnToolsMemoryviewer) + ON_UPDATE_COMMAND_UI(ID_TOOLS_MEMORYVIEWER, OnUpdateToolsMemoryviewer) + ON_COMMAND(ID_TOOLS_OAMVIEWER, OnToolsOamviewer) + ON_UPDATE_COMMAND_UI(ID_TOOLS_OAMVIEWER, OnUpdateToolsOamviewer) + ON_COMMAND(ID_TOOLS_PALETTEVIEW, OnToolsPaletteview) + ON_UPDATE_COMMAND_UI(ID_TOOLS_PALETTEVIEW, OnUpdateToolsPaletteview) + ON_COMMAND(ID_TOOLS_TILEVIEWER, OnToolsTileviewer) + ON_UPDATE_COMMAND_UI(ID_TOOLS_TILEVIEWER, OnUpdateToolsTileviewer) + ON_COMMAND(ID_DEBUG_NEXTFRAME, OnDebugNextframe) + ON_UPDATE_COMMAND_UI(ID_CHEATS_AUTOMATICSAVELOADCHEATS, OnUpdateCheatsAutomaticsaveloadcheats) + ON_COMMAND(ID_TOOLS_DEBUG_GDB, OnToolsDebugGdb) + ON_UPDATE_COMMAND_UI(ID_TOOLS_DEBUG_GDB, OnUpdateToolsDebugGdb) + ON_COMMAND(ID_TOOLS_DEBUG_LOADANDWAIT, OnToolsDebugLoadandwait) + ON_UPDATE_COMMAND_UI(ID_TOOLS_DEBUG_LOADANDWAIT, OnUpdateToolsDebugLoadandwait) + ON_COMMAND(ID_TOOLS_DEBUG_BREAK, OnToolsDebugBreak) + ON_UPDATE_COMMAND_UI(ID_TOOLS_DEBUG_BREAK, OnUpdateToolsDebugBreak) + ON_COMMAND(ID_TOOLS_DEBUG_DISCONNECT, OnToolsDebugDisconnect) + ON_UPDATE_COMMAND_UI(ID_TOOLS_DEBUG_DISCONNECT, OnUpdateToolsDebugDisconnect) + ON_COMMAND(ID_OPTIONS_SOUND_STARTRECORDING, OnOptionsSoundStartrecording) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_SOUND_STARTRECORDING, OnUpdateOptionsSoundStartrecording) + ON_COMMAND(ID_OPTIONS_SOUND_STOPRECORDING, OnOptionsSoundStoprecording) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_SOUND_STOPRECORDING, OnUpdateOptionsSoundStoprecording) + ON_COMMAND(ID_TOOLS_RECORD_STARTAVIRECORDING, OnToolsRecordStartavirecording) + ON_UPDATE_COMMAND_UI(ID_TOOLS_RECORD_STARTAVIRECORDING, OnUpdateToolsRecordStartavirecording) + ON_COMMAND(ID_TOOLS_RECORD_STOPAVIRECORDING, OnToolsRecordStopavirecording) + ON_UPDATE_COMMAND_UI(ID_TOOLS_RECORD_STOPAVIRECORDING, OnUpdateToolsRecordStopavirecording) + ON_WM_PAINT() + ON_COMMAND(ID_TOOLS_RECORD_STARTMOVIERECORDING, OnToolsRecordStartmovierecording) + ON_UPDATE_COMMAND_UI(ID_TOOLS_RECORD_STARTMOVIERECORDING, OnUpdateToolsRecordStartmovierecording) + ON_COMMAND(ID_TOOLS_RECORD_STOPMOVIERECORDING, OnToolsRecordStopmovierecording) + ON_UPDATE_COMMAND_UI(ID_TOOLS_RECORD_STOPMOVIERECORDING, OnUpdateToolsRecordStopmovierecording) + ON_COMMAND(ID_TOOLS_PLAY_STARTMOVIEPLAYING, OnToolsPlayStartmovieplaying) + ON_UPDATE_COMMAND_UI(ID_TOOLS_PLAY_STARTMOVIEPLAYING, OnUpdateToolsPlayStartmovieplaying) + ON_COMMAND(ID_TOOLS_PLAY_STOPMOVIEPLAYING, OnToolsPlayStopmovieplaying) + ON_UPDATE_COMMAND_UI(ID_TOOLS_PLAY_STOPMOVIEPLAYING, OnUpdateToolsPlayStopmovieplaying) + ON_COMMAND(ID_TOOLS_REWIND, OnToolsRewind) + ON_UPDATE_COMMAND_UI(ID_TOOLS_REWIND, OnUpdateToolsRewind) + ON_COMMAND(ID_TOOLS_CUSTOMIZE, OnToolsCustomize) + ON_UPDATE_COMMAND_UI(ID_TOOLS_CUSTOMIZE, OnUpdateToolsCustomize) + ON_COMMAND(ID_HELP_BUGREPORT, OnHelpBugreport) + ON_WM_MOUSEMOVE() + ON_WM_INITMENU() + ON_WM_ACTIVATE() + ON_WM_DROPFILES() + ON_COMMAND(ID_FILE_SAVEGAME_OLDESTSLOT, OnFileSavegameOldestslot) + ON_UPDATE_COMMAND_UI(ID_FILE_SAVEGAME_OLDESTSLOT, OnUpdateFileSavegameOldestslot) + ON_COMMAND(ID_FILE_LOADGAME_MOSTRECENT, OnFileLoadgameMostrecent) + ON_UPDATE_COMMAND_UI(ID_FILE_LOADGAME_MOSTRECENT, OnUpdateFileLoadgameMostrecent) + ON_COMMAND(ID_FILE_LOADGAME_AUTOLOADMOSTRECENT, OnFileLoadgameAutoloadmostrecent) + ON_UPDATE_COMMAND_UI(ID_FILE_LOADGAME_AUTOLOADMOSTRECENT, OnUpdateFileLoadgameAutoloadmostrecent) + ON_COMMAND(ID_CHEATS_DISABLECHEATS, OnCheatsDisablecheats) + ON_UPDATE_COMMAND_UI(ID_CHEATS_DISABLECHEATS, OnUpdateCheatsDisablecheats) + ON_COMMAND(ID_OPTIONS_VIDEO_FULLSCREENMAXSCALE, OnOptionsVideoFullscreenmaxscale) + ON_COMMAND(ID_OPTIONS_EMULATOR_GAMEOVERRIDES, OnOptionsEmulatorGameoverrides) + ON_COMMAND(ID_OPTIONS_SELECT_PLUGIN, OnOptionsSelectPlugin) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_EMULATOR_GAMEOVERRIDES, OnUpdateOptionsEmulatorGameoverrides) + ON_COMMAND(ID_HELP_GNUPUBLICLICENSE, OnHelpGnupubliclicense) + ON_COMMAND(ID_OPTIONS_LINK_OPTIONS, OnLinkOptions) + ON_COMMAND(ID_OPTIONS_LINK_WIRELESSADAPTER, OnOptionsLinkRFU) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_LINK_WIRELESSADAPTER, OnUpdateOptionsLinkRFU) + ON_COMMAND(ID_OPTIONS_LINK_ENABLE, OnOptionsLinkEnable) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_LINK_ENABLE, OnUpdateOptionsLinkEnable) + ON_COMMAND(ID_OPTIONS_JOYBUS, &MainWnd::OnOptionsJoybus) + + //}}AFX_MSG_MAP + ON_COMMAND_EX_RANGE(ID_FILE_MRU_FILE1, ID_FILE_MRU_FILE10, OnFileRecentFile) + ON_COMMAND_EX_RANGE(ID_FILE_LOADGAME_SLOT1, ID_FILE_LOADGAME_SLOT10, OnFileLoadSlot) + ON_COMMAND_EX_RANGE(ID_FILE_SAVEGAME_SLOT1, ID_FILE_SAVEGAME_SLOT10, OnFileSaveSlot) + ON_UPDATE_COMMAND_UI_RANGE(ID_FILE_LOADGAME_SLOT1, ID_FILE_LOADGAME_SLOT10, OnUpdateFileLoadGameSlot) + ON_UPDATE_COMMAND_UI_RANGE(ID_FILE_SAVEGAME_SLOT1, ID_FILE_SAVEGAME_SLOT10, OnUpdateFileSaveGameSlot) + ON_COMMAND_EX_RANGE(ID_OPTIONS_VIDEO_FRAMESKIP_0, ID_OPTIONS_VIDEO_FRAMESKIP_5, OnOptionsFrameskip) + ON_COMMAND_EX_RANGE(ID_OPTIONS_VIDEO_FRAMESKIP_6, ID_OPTIONS_VIDEO_FRAMESKIP_9, OnOptionsFrameskip) + ON_COMMAND_EX_RANGE(ID_OPTIONS_VIDEO_X1, ID_OPTIONS_VIDEO_X6, OnOptionVideoSize) + ON_COMMAND_EX_RANGE(ID_OPTIONS_VIDEO_LAYERS_BG0, ID_OPTIONS_VIDEO_LAYERS_OBJWIN, OnVideoLayer) + ON_UPDATE_COMMAND_UI_RANGE(ID_OPTIONS_VIDEO_LAYERS_BG0, ID_OPTIONS_VIDEO_LAYERS_OBJWIN, OnUpdateVideoLayer) + ON_COMMAND(ID_OPTIONS_VIDEO_LAYERS_RESET, OnVideoLayerReset) + ON_COMMAND(ID_SYSTEM_MINIMIZE, OnSystemMinimize) + ON_COMMAND_EX_RANGE(ID_OPTIONS_EMULATOR_SHOWSPEED_NONE, ID_OPTIONS_EMULATOR_SHOWSPEED_TRANSPARENT, OnOptionsEmulatorShowSpeed) + ON_UPDATE_COMMAND_UI_RANGE(ID_OPTIONS_EMULATOR_SHOWSPEED_NONE, ID_OPTIONS_EMULATOR_SHOWSPEED_TRANSPARENT, OnUpdateOptionsEmulatorShowSpeed) + ON_COMMAND_EX_RANGE(ID_OPTIONS_PRIORITY_HIGHEST, ID_OPTIONS_PRIORITY_BELOWNORMAL, OnOptionsPriority) + ON_UPDATE_COMMAND_UI_RANGE(ID_OPTIONS_PRIORITY_HIGHEST, ID_OPTIONS_PRIORITY_BELOWNORMAL, OnUpdateOptionsPriority) + ON_COMMAND_EX_RANGE(ID_OPTIONS_FILTER_NORMAL, ID_OPTIONS_FILTER_TVMODE, OnOptionsFilter) + ON_COMMAND_EX_RANGE(ID_OPTIONS_FILTER16BIT_PIXELATEEXPERIMENTAL, ID_OPTIONS_FILTER16BIT_MOTIONBLUREXPERIMENTAL, OnOptionsFilter) + ON_COMMAND_EX_RANGE(ID_OPTIONS_FILTER16BIT_ADVANCEMAMESCALE2X, ID_OPTIONS_FILTER16BIT_SIMPLE2X, OnOptionsFilter) + ON_COMMAND_EX_RANGE(ID_OPTIONS_FILTER_BILINEAR, ID_OPTIONS_FILTER_BILINEARPLUS, OnOptionsFilter) + ON_COMMAND_EX_RANGE(ID_OPTIONS_FILTER_SCANLINES, ID_OPTIONS_FILTER_SCANLINES, OnOptionsFilter) + ON_COMMAND_EX_RANGE(ID_OPTIONS_FILTER_HQ2X, ID_OPTIONS_FILTER_LQ2X, OnOptionsFilter) + ON_COMMAND_EX(ID_OPTIONS_FILTER_PLUGIN, OnOptionsFilter) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_FILTER_PLUGIN, OnUpdateOptionsFilter) + ON_COMMAND_EX(ID_OPTIONS_FILTER_HQ3X, OnOptionsFilter) + ON_COMMAND_EX(ID_OPTIONS_FILTER_HQ4X, OnOptionsFilter) + ON_COMMAND_EX(ID_OPTIONS_FILTER_SIMPLE3X, OnOptionsFilter) + ON_COMMAND_EX(ID_OPTIONS_FILTER_SIMPLE4X, OnOptionsFilter) + ON_UPDATE_COMMAND_UI_RANGE(ID_OPTIONS_FILTER_NORMAL, ID_OPTIONS_FILTER_TVMODE, OnUpdateOptionsFilter) + ON_UPDATE_COMMAND_UI_RANGE(ID_OPTIONS_FILTER16BIT_PIXELATEEXPERIMENTAL, ID_OPTIONS_FILTER16BIT_MOTIONBLUREXPERIMENTAL, OnUpdateOptionsFilter) + ON_UPDATE_COMMAND_UI_RANGE(ID_OPTIONS_FILTER16BIT_ADVANCEMAMESCALE2X, ID_OPTIONS_FILTER16BIT_SIMPLE2X, OnUpdateOptionsFilter) + ON_UPDATE_COMMAND_UI_RANGE(ID_OPTIONS_FILTER_BILINEAR, ID_OPTIONS_FILTER_BILINEARPLUS, OnUpdateOptionsFilter) + ON_UPDATE_COMMAND_UI_RANGE(ID_OPTIONS_FILTER_SCANLINES, ID_OPTIONS_FILTER_SCANLINES, OnUpdateOptionsFilter) + ON_UPDATE_COMMAND_UI_RANGE(ID_OPTIONS_FILTER_HQ2X, ID_OPTIONS_FILTER_LQ2X, OnUpdateOptionsFilter) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_FILTER_SIMPLE3X, OnUpdateOptionsFilter) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_FILTER_SIMPLE4X, OnUpdateOptionsFilter) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_FILTER_HQ3X, OnUpdateOptionsFilter) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_FILTER_HQ4X, OnUpdateOptionsFilter) + ON_COMMAND_EX_RANGE(ID_OPTIONS_FILTER_INTERFRAMEBLENDING_NONE, ID_OPTIONS_FILTER_INTERFRAMEBLENDING_SMART, OnOptionsFilterIFB) + ON_UPDATE_COMMAND_UI_RANGE(ID_OPTIONS_FILTER_INTERFRAMEBLENDING_NONE, ID_OPTIONS_FILTER_INTERFRAMEBLENDING_SMART, OnUpdateOptionsFilterIFB) + ON_COMMAND_EX_RANGE(ID_OPTIONS_JOYPAD_DEFAULTJOYPAD_1, ID_OPTIONS_JOYPAD_DEFAULTJOYPAD_4, OnOptionsJoypadDefault) + ON_UPDATE_COMMAND_UI_RANGE(ID_OPTIONS_JOYPAD_DEFAULTJOYPAD_1, ID_OPTIONS_JOYPAD_DEFAULTJOYPAD_4, OnUpdateOptionsJoypadDefault) + ON_COMMAND_EX_RANGE(ID_OPTIONS_JOYPAD_AUTOFIRE_A, ID_OPTIONS_JOYPAD_AUTOFIRE_R, OnOptionsJoypadAutofire) + ON_UPDATE_COMMAND_UI_RANGE(ID_OPTIONS_JOYPAD_AUTOFIRE_A, ID_OPTIONS_JOYPAD_AUTOFIRE_R, OnUpdateOptionsJoypadAutofire) + ON_MESSAGE(WM_SYSCOMMAND, OnMySysCommand) + ON_COMMAND(ID_OPTIONS_SOUND_HARDWAREACCELERATION, &MainWnd::OnOptionsSoundHardwareacceleration) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_SOUND_HARDWAREACCELERATION, &MainWnd::OnUpdateOptionsSoundHardwareacceleration) + ON_COMMAND(ID_OUTPUTAPI_DIRECTSOUND, &MainWnd::OnOutputapiDirectsound) + ON_UPDATE_COMMAND_UI(ID_OUTPUTAPI_DIRECTSOUND, &MainWnd::OnUpdateOutputapiDirectsound) + ON_COMMAND(ID_OUTPUTAPI_OPENAL, &MainWnd::OnOutputapiOpenal) + ON_UPDATE_COMMAND_UI(ID_OUTPUTAPI_OPENAL, &MainWnd::OnUpdateOutputapiOpenal) + ON_COMMAND(ID_OUTPUTAPI_OALCONFIGURATION, &MainWnd::OnOutputapiOalconfiguration) + ON_UPDATE_COMMAND_UI(ID_OUTPUTAPI_OALCONFIGURATION, &MainWnd::OnUpdateOutputapiOalconfiguration) + ON_COMMAND(ID_RENDERAPI_D3DMOTIONBLUR, &MainWnd::OnRenderapiD3dmotionblur) + ON_UPDATE_COMMAND_UI(ID_RENDERAPI_D3DMOTIONBLUR, &MainWnd::OnUpdateRenderapiD3dmotionblur) + ON_WM_NCLBUTTONDOWN() + ON_WM_WINDOWPOSCHANGING() + ON_COMMAND(ID_EMULATOR_BIOSFILES, &MainWnd::OnEmulatorBiosfiles) + ON_WM_NCRBUTTONDOWN() + ON_COMMAND(ID_OUTPUTAPI_XAUDIO2, &MainWnd::OnOutputapiXaudio2) + ON_UPDATE_COMMAND_UI(ID_OUTPUTAPI_XAUDIO2, &MainWnd::OnUpdateOutputapiXaudio2) + ON_COMMAND(ID_PIXELFILTER_MULTI, &MainWnd::OnPixelfilterMultiThreading) + ON_UPDATE_COMMAND_UI(ID_PIXELFILTER_MULTI, &MainWnd::OnUpdatePixelfilterMultiThreading) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_SELECT_PLUGIN, &MainWnd::OnUpdateOptionsSelectPlugin) + + ON_COMMAND(ID_LOADGAME_DONOTCHANGEBATTERYSAVE, &MainWnd::OnLoadgameDonotchangebatterysave) + ON_UPDATE_COMMAND_UI(ID_LOADGAME_DONOTCHANGEBATTERYSAVE, &MainWnd::OnUpdateLoadgameDonotchangebatterysave) + + ON_COMMAND(ID_LOADGAME_DONOTCHANGECHEATLIST, &MainWnd::OnLoadgameDonotchangecheatlist) + ON_UPDATE_COMMAND_UI(ID_LOADGAME_DONOTCHANGECHEATLIST, &MainWnd::OnUpdateLoadgameDonotchangecheatlist) + + ON_COMMAND(ID_OUTPUTAPI_XAUDIO2CONFIG, &MainWnd::OnOutputapiXaudio2config) + ON_UPDATE_COMMAND_UI(ID_OUTPUTAPI_XAUDIO2CONFIG, &MainWnd::OnUpdateOutputapiXaudio2config) + ON_WM_ENTERSIZEMOVE() + ON_COMMAND(ID_AUDIO_CORE_SETTINGS, &MainWnd::OnAudioCoreSettings) + ON_UPDATE_COMMAND_UI(ID_AUDIO_CORE_SETTINGS, &MainWnd::OnUpdateAudioCoreSettings) + END_MESSAGE_MAP() + + + ///////////////////////////////////////////////////////////////////////////// +// MainWnd message handlers + +void MainWnd::OnClose() +{ + emulating = false; + CWnd::OnClose(); +} + +bool MainWnd::FileRun() +{ + // save battery file before we change the filename... + if(rom != NULL || gbRom != NULL) { + if(theApp.autoSaveLoadCheatList) + winSaveCheatListDefault(); + writeBatteryFile(); + cheatSearchCleanup(&cheatSearchData); + theApp.emulator.emuCleanUp(); + remoteCleanUp(); + emulating = false; +#ifdef APU_LOGGER_H + end_apu_log(); +#endif + } + char tempName[2048]; + char file[2048]; + CString oldFile = theApp.filename; + + utilStripDoubleExtension(theApp.szFile, tempName); + + _fullpath(file, tempName, 2048); + theApp.filename = file; + + int index = theApp.filename.ReverseFind('.'); + if(index != -1) + theApp.filename = theApp.filename.Left(index); + + if( theApp.filename != oldFile ) { + // clear cheat list when another game is loaded + cheatsDeleteAll( false ); + gbCheatRemoveAll(); + } + + CString patchName; + patchName.Format("%s.ips", theApp.filename); + if( !fileExists( patchName ) ) { + patchName.Format("%s.ups", theApp.filename); + if( !fileExists( patchName ) ) { + patchName.Format("%s.ppf", theApp.filename); + if( !fileExists( patchName ) ) { + // don't use any patches + patchName.Empty(); + } + } + } + + + if(!theApp.dir.GetLength()) { + int index = theApp.filename.ReverseFind('\\'); + if(index != -1) { + theApp.dir = theApp.filename.Left(index-1); + } + } + + IMAGE_TYPE type = utilFindType(theApp.szFile); + + if(type == IMAGE_UNKNOWN) { + systemMessage(IDS_UNSUPPORTED_FILE_TYPE, + "Unsupported file type: %s", theApp.szFile); + return false; + } + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + theApp.cartridgeType = type; + if(type == IMAGE_GB) { + if(!gbLoadRom(theApp.szFile)) + return false; + + gbGetHardwareType(); + + // used for the handling of the gb Boot Rom + if (gbHardware & 5) + { + skipBios = theApp.skipBiosFile; + gbCPUInit(theApp.biosFileNameGB, theApp.useBiosFileGB); + } + + + + gbReset(); + theApp.emulator = GBSystem; + gbBorderOn = theApp.winGbBorderOn; + theApp.romSize = gbRomSize; + + + if(theApp.autoPatch && !patchName.IsEmpty()) { + int size = gbRomSize; + applyPatch(patchName, &gbRom, &size); + if(size != gbRomSize) { + extern bool gbUpdateSizes(); + gbUpdateSizes(); + gbReset(); + theApp.romSize = size; + } + } + } else { + int size = CPULoadRom(theApp.szFile); + if(!size) + return false; + + theApp.romSize = size; + + flashSetSize(theApp.winFlashSize); + rtcEnable(theApp.winRtcEnable); + cpuSaveType = theApp.winSaveType; + + GetModuleFileName(NULL, tempName, 2048); + + char *p = strrchr(tempName, '\\'); + if(p) + *p = 0; + + char buffer[5]; + strncpy(buffer, (const char *)&rom[0xac], 4); + buffer[4] = 0; + + strcat(tempName, "\\vba-over.ini"); + + UINT i = GetPrivateProfileInt(buffer, + "rtcEnabled", + -1, + tempName); + if(i != (UINT)-1) + rtcEnable(i == 0 ? false : true); + + i = GetPrivateProfileInt(buffer, + "flashSize", + -1, + tempName); + if(i != (UINT)-1 && (i == 0x10000 || i == 0x20000)) + flashSetSize((int)i); + + i = GetPrivateProfileInt(buffer, + "saveType", + -1, + tempName); + if(i != (UINT)-1 && (i <= 5)) + cpuSaveType = (int)i; + i = GetPrivateProfileInt(buffer, + "mirroringEnabled", + -1, + tempName); + if(i != (UINT)-1) + doMirroring (i == 0 ? false : true); + + theApp.emulator = GBASystem; + /* disabled due to problems + if(theApp.removeIntros && rom != NULL) { + *((u32 *)rom)= 0xea00002e; + } + */ + + if(theApp.autoPatch && !patchName.IsEmpty()) { + int size = 0x2000000; + applyPatch(patchName, &rom, &size); + if(size != 0x2000000) { + CPUReset(); + } + } + } + + if(theApp.soundInitialized) { + if(theApp.cartridgeType == 1) + gbSoundReset(); + else + soundReset(); + } else { + soundInit(); + theApp.soundInitialized = true; + } + +#ifdef APU_LOGGER_H + begin_apu_log("apu_log.txt"); +#endif + + if(type == IMAGE_GBA) { + skipBios = theApp.skipBiosFile; + CPUInit(theApp.biosFileNameGBA.GetString(), theApp.useBiosFileGBA); + CPUReset(); + } + + readBatteryFile(); + + if(theApp.autoSaveLoadCheatList) + winLoadCheatListDefault(); + + theApp.addRecentFile(theApp.szFile); + + theApp.updateWindowSize(theApp.videoOption); + + theApp.updateFrameSkip(); + + emulating = true; + + if(theApp.autoLoadMostRecent) + OnFileLoadgameMostrecent(); + + theApp.frameskipadjust = 0; + theApp.renderedFrames = 0; + theApp.autoFrameSkipLastTime = systemGetClock(); + + theApp.rewindCount = 0; + theApp.rewindCounter = 0; + theApp.rewindSaveNeeded = false; + + toolsClearLog(); + + return true; +} + +void MainWnd::OnInitMenuPopup(CMenu* pMenu, UINT nIndex, BOOL bSysMenu) +{ + ASSERT(pMenu != NULL); + + CCmdUI state; + state.m_pMenu = pMenu; + ASSERT(state.m_pOther == NULL); + ASSERT(state.m_pParentMenu == NULL); + + // determine if menu is popup in top-level menu and set m_pOther to + // it if so (m_pParentMenu == NULL indicates that it is secondary popup) + HMENU hParentMenu; + if (AfxGetThreadState()->m_hTrackingMenu == pMenu->m_hMenu) + state.m_pParentMenu = pMenu; // parent == child for tracking popup + else if ((hParentMenu = ::GetMenu(m_hWnd)) != NULL) { + CWnd* pParent = GetTopLevelParent(); + // child windows don't have menus -- need to go to the top! + if (pParent != NULL && + (hParentMenu = ::GetMenu(pParent->m_hWnd)) != NULL) { + int nIndexMax = ::GetMenuItemCount(hParentMenu); + for (int nIndex = 0; nIndex < nIndexMax; nIndex++) { + if (::GetSubMenu(hParentMenu, nIndex) == pMenu->m_hMenu) { + // when popup is found, m_pParentMenu is containing menu + state.m_pParentMenu = CMenu::FromHandle(hParentMenu); + break; + } + } + } + } + + state.m_nIndexMax = pMenu->GetMenuItemCount(); + for (state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax; + state.m_nIndex++) { + state.m_nID = pMenu->GetMenuItemID(state.m_nIndex); + if (state.m_nID == 0) + continue; // menu separator or invalid cmd - ignore it + + ASSERT(state.m_pOther == NULL); + ASSERT(state.m_pMenu != NULL); + if (state.m_nID == (UINT)-1) { + // possibly a popup menu, route to first item of that popup + state.m_pSubMenu = pMenu->GetSubMenu(state.m_nIndex); + if (state.m_pSubMenu == NULL || + (state.m_nID = state.m_pSubMenu->GetMenuItemID(0)) == 0 || + state.m_nID == (UINT)-1) { + continue; // first item of popup can't be routed to + } + state.DoUpdate(this, FALSE); // popups are never auto disabled + } else { + // normal menu item + // Auto enable/disable if frame window has 'm_bAutoMenuEnable' + // set and command is _not_ a system command. + state.m_pSubMenu = NULL; + state.DoUpdate(this, state.m_nID < 0xF000); + } + + // adjust for menu deletions and additions + UINT nCount = pMenu->GetMenuItemCount(); + if (nCount < state.m_nIndexMax) { + state.m_nIndex -= (state.m_nIndexMax - nCount); + while (state.m_nIndex < nCount && + pMenu->GetMenuItemID(state.m_nIndex) == state.m_nID) { + state.m_nIndex++; + } + } + state.m_nIndexMax = nCount; + } +} + +void MainWnd::OnMoving(UINT fwSide, LPRECT pRect) +{ + CWnd::OnMoving(fwSide, pRect); + + if( emulating ) { + soundPause(); + } +} + +void MainWnd::OnMove(int x, int y) +{ + CWnd::OnMove(x, y); + + if(!theApp.changingVideoSize) { + if(this) { + if(!IsIconic()) { + RECT r; + + GetWindowRect(&r); + theApp.windowPositionX = r.left; + theApp.windowPositionY = r.top; + theApp.adjustDestRect(); + regSetDwordValue("windowX", theApp.windowPositionX); + regSetDwordValue("windowY", theApp.windowPositionY); + } + } + } +} + +void MainWnd::OnSizing(UINT fwSide, LPRECT pRect) +{ // the OnSizing event only occurs in windowed mode + CWnd::OnSizing(fwSide, pRect); + + // pause sound to prevent low sound buffers + if( emulating ) { + soundPause(); + } + + // maintain minimal window size + RECT size = { 0, 0, theApp.sizeX, theApp.sizeY }; + AdjustWindowRectEx( + &size, + WS_POPUP | WS_VISIBLE | WS_OVERLAPPEDWINDOW, + FALSE, + 0 ); + MENUBARINFO mbi; + mbi.cbSize = sizeof(MENUBARINFO); + GetMenuBarInfo( this->GetSafeHwnd(), OBJID_MENU, 0, &mbi ); + const LONG menuHeight = mbi.rcBar.bottom - mbi.rcBar.top + 1; + // +1 because of that white line, wherever it comes from + const LONG width = size.right - size.left; + const LONG height = size.bottom - size.top + menuHeight; + if( ( pRect->right - pRect->left ) < width ) { + pRect->right = pRect->left + width; + } + if( ( pRect->bottom - pRect->top ) < height ) { + pRect->bottom = pRect->top + height; + } +} + +void MainWnd::OnSize(UINT nType, int cx, int cy) +{ + CWnd::OnSize(nType, cx, cy); + + bool redraw = ( ( cx < theApp.surfaceSizeX ) || ( cy < theApp.surfaceSizeY ) ); + + if(!theApp.changingVideoSize) { + if(this) { + if(!IsIconic()) { + if(theApp.iconic) { + if(emulating) { + soundResume(); + theApp.paused = false; + } + } + if(theApp.videoOption <= VIDEO_6X) { + theApp.surfaceSizeX = cx; + theApp.surfaceSizeY = cy; + theApp.adjustDestRect(); + if(theApp.display) + theApp.display->resize(theApp.dest.right-theApp.dest.left, theApp.dest.bottom-theApp.dest.top); + if( redraw && emulating ) { + theApp.painting = true; + systemDrawScreen(); + theApp.painting = false; + theApp.renderedFrames--; + } + } + } else { + if(emulating) { + if(!theApp.paused) { + theApp.paused = true; + soundPause(); + } + } + theApp.iconic = true; + } + } + } +} + +void MainWnd::winSaveCheatListDefault() +{ + CString name; + CString filename; + + int index = theApp.filename.ReverseFind('\\'); + + if(index != -1) + name = theApp.filename.Right(theApp.filename.GetLength()-index-1); + else + name = theApp.filename; + CString dir = regQueryStringValue("saveDir", NULL); + if( dir[0] == '.' ) { + // handle as relative path + char baseDir[MAX_PATH+1]; + GetModuleFileName( NULL, baseDir, MAX_PATH ); + baseDir[MAX_PATH] = '\0'; // for security reasons + PathRemoveFileSpec( baseDir ); // removes the trailing file name and backslash + strcat( baseDir, "\\" ); + strcat( baseDir, dir ); + dir = baseDir; + } + + if(!dir.GetLength()) + dir = getDirFromFile(filename); + + if(isDriveRoot(dir)) + filename.Format("%s%s.clt", dir, name); + else + filename.Format("%s\\%s.clt", dir, name); + + winSaveCheatList(filename); +} + +void MainWnd::winSaveCheatList(const char *name) +{ + if(theApp.cartridgeType == 0) + cheatsSaveCheatList(name); + else + gbCheatsSaveCheatList(name); +} + +void MainWnd::winLoadCheatListDefault() +{ + CString name; + CString filename; + + int index = theApp.filename.ReverseFind('\\'); + + if(index != -1) + name = theApp.filename.Right(theApp.filename.GetLength()-index-1); + else + name = theApp.filename; + CString dir = regQueryStringValue("saveDir", NULL); + if( dir[0] == '.' ) { + // handle as relative path + char baseDir[MAX_PATH+1]; + GetModuleFileName( NULL, baseDir, MAX_PATH ); + baseDir[MAX_PATH] = '\0'; // for security reasons + PathRemoveFileSpec( baseDir ); // removes the trailing file name and backslash + strcat( baseDir, "\\" ); + strcat( baseDir, dir ); + dir = baseDir; + } + + if(!dir.GetLength()) + dir = getDirFromFile(filename); + + if(isDriveRoot(dir)) + filename.Format("%s%s.clt", dir, name); + else + filename.Format("%s\\%s.clt", dir, name); + + winLoadCheatList(filename); +} + +void MainWnd::winLoadCheatList(const char *name) +{ + bool res = false; + + if(theApp.cartridgeType == 0) + res = cheatsLoadCheatList(name); + else + res = gbCheatsLoadCheatList(name); + + if(res) + systemScreenMessage(winResLoadString(IDS_LOADED_CHEATS)); +} + +CString MainWnd::getDirFromFile(CString& file) +{ + CString temp = file; + int index = temp.ReverseFind('\\'); + + if(index != -1) { + temp = temp.Left(index); + if(temp.GetLength() == 2 && temp[1] == ':') + temp += "\\"; + } else { + temp = ""; + } + return temp; +} + +bool MainWnd::isDriveRoot(CString& file) +{ + if(file.GetLength() == 3) { + if(file[1] == ':' && file[2] == '\\') + return true; + } + return false; +} + +void MainWnd::writeBatteryFile() +{ + CString buffer; + CString filename; + + int index = theApp.filename.ReverseFind('\\'); + + if(index != -1) + buffer = theApp.filename.Right(theApp.filename.GetLength()-index-1); + else + buffer = theApp.filename; + + CString saveDir = regQueryStringValue("batteryDir", NULL); + if( saveDir[0] == '.' ) { + // handle as relative path + char baseDir[MAX_PATH+1]; + GetModuleFileName( NULL, baseDir, MAX_PATH ); + baseDir[MAX_PATH] = '\0'; // for security reasons + PathRemoveFileSpec( baseDir ); // removes the trailing file name and backslash + strcat( baseDir, "\\" ); + strcat( baseDir, saveDir ); + saveDir = baseDir; + } + + if(saveDir.IsEmpty()) + saveDir = getDirFromFile(theApp.filename); + + if(isDriveRoot(saveDir)) + filename.Format("%s%s.sav", saveDir, buffer); + else + filename.Format("%s\\%s.sav", saveDir, buffer); + + if(theApp.emulator.emuWriteBattery) + theApp.emulator.emuWriteBattery(MakeInstanceFilename((const char *)filename)); +} + + +void MainWnd::readBatteryFile() +{ + CString buffer; + CString filename; + + int index = theApp.filename.ReverseFind('\\'); + + if(index != -1) + buffer = theApp.filename.Right(theApp.filename.GetLength()-index-1); + else + buffer = theApp.filename; + + CString saveDir = regQueryStringValue("batteryDir", NULL); + if( saveDir[0] == '.' ) { + // handle as relative path + char baseDir[MAX_PATH+1]; + GetModuleFileName( NULL, baseDir, MAX_PATH ); + baseDir[MAX_PATH] = '\0'; // for security reasons + PathRemoveFileSpec( baseDir ); // removes the trailing file name and backslash + strcat( baseDir, "\\" ); + strcat( baseDir, saveDir ); + saveDir = baseDir; + } + + if(saveDir.IsEmpty()) + saveDir = getDirFromFile(theApp.filename); + + if(isDriveRoot(saveDir)) + filename.Format("%s%s.sav", saveDir, buffer); + else + filename.Format("%s\\%s.sav", saveDir, buffer); + + bool res = false; + + if(theApp.emulator.emuReadBattery) + res = theApp.emulator.emuReadBattery(MakeInstanceFilename(filename)); + + if(res) + systemScreenMessage(winResLoadString(IDS_LOADED_BATTERY)); +} + +CString MainWnd::winLoadFilter(UINT id) +{ + CString res = winResLoadString(id); + res.Replace('_','|'); + + return res; +} + +bool MainWnd::loadSaveGame(const char *name) +{ + if(theApp.emulator.emuReadState) + return theApp.emulator.emuReadState(name); + return false; +} + +bool MainWnd::writeSaveGame(const char *name) +{ + if(theApp.emulator.emuWriteState) + return theApp.emulator.emuWriteState(name); + return false; +} + +void MainWnd::OnContextMenu(CWnd* pWnd, CPoint point) +{ + winMouseOn(); +} + +void MainWnd::OnSystemMinimize() +{ + ShowWindow(SW_SHOWMINIMIZED); +} + + +bool MainWnd::fileOpenSelect( int system ) +{ + theApp.dir = _T(""); + CString initialDir; + int selectedFilter = 0; + LPCTSTR exts[] = { _T(""), _T(""), _T(""), _T("") }; + CString filter; + CString title; + + switch( system ) + { + case 0: + // GBA + initialDir = regQueryStringValue( _T("romdir"), _T(".") ); + selectedFilter = regQueryDwordValue( _T("selectedFilter"), 0); + if( (selectedFilter < 0) || (selectedFilter > 2) ) { + selectedFilter = 0; + } + filter = winLoadFilter( IDS_FILTER_GBAROM ); + break; + case 1: + // GBC + initialDir = regQueryStringValue( _T("gbcromdir"), _T(".") ); + // TODO: memorize selected filter for GBC as well + filter = winLoadFilter( IDS_FILTER_GBCROM ); + break; + case 2: + // GB + initialDir = regQueryStringValue( _T("gbromdir"), _T(".") ); + // TODO: memorize selected filter for GB as well + filter = winLoadFilter( IDS_FILTER_GBROM ); + break; + } + + title = winResLoadString( IDS_SELECT_ROM ); + + if( !initialDir.IsEmpty() ) { + theApp.dir = initialDir; + } + + if( initialDir[0] == '.' ) { + // handle as relative path + char baseDir[MAX_PATH+1]; + GetModuleFileName( NULL, baseDir, MAX_PATH ); + baseDir[MAX_PATH] = '\0'; // for security reasons + PathRemoveFileSpec( baseDir ); // removes the trailing file name and backslash + strcat( baseDir, "\\" ); + strcat( baseDir, initialDir ); + initialDir = baseDir; + } + + theApp.szFile = _T(""); + + + FileDlg dlg( this, _T(""), filter, selectedFilter, _T(""), exts, theApp.dir, title, false); + + if( dlg.DoModal() == IDOK ) { + if( system == 0 ) { + regSetDwordValue( _T("selectedFilter"), dlg.m_ofn.nFilterIndex ); + } + theApp.szFile = dlg.GetPathName(); + theApp.dir = theApp.szFile.Left( dlg.m_ofn.nFileOffset ); + if( (theApp.dir.GetLength() > 3) && (theApp.dir[theApp.dir.GetLength()-1] == _T('\\')) ) { + theApp.dir = theApp.dir.Left( theApp.dir.GetLength() - 1 ); + } + SetCurrentDirectory( theApp.dir ); + regSetStringValue( _T("lastDir"), theApp.dir ); + return true; + } + return false; +} + + +void MainWnd::OnPaint() +{ + CPaintDC dc(this); // device context for painting + + if(emulating) { + theApp.painting = true; + systemDrawScreen(); + theApp.painting = false; + theApp.renderedFrames--; + } +} + +BOOL MainWnd::PreTranslateMessage(MSG* pMsg) +{ + if (CWnd::PreTranslateMessage(pMsg)) + return TRUE; + + if(pMsg->message >= WM_KEYFIRST && pMsg->message <= WM_KEYLAST) { + return theApp.hAccel != NULL && ::TranslateAccelerator(m_hWnd, theApp.hAccel, pMsg); + } + + return FALSE; +} + +void MainWnd::screenCapture(int captureNumber) +{ + CString buffer; + + CString captureDir = regQueryStringValue("captureDir", ""); + if( captureDir[0] == '.' ) { + // handle as relative path + char baseDir[MAX_PATH+1]; + GetModuleFileName( NULL, baseDir, MAX_PATH ); + baseDir[MAX_PATH] = '\0'; // for security reasons + PathRemoveFileSpec( baseDir ); // removes the trailing file name and backslash + strcat( baseDir, "\\" ); + strcat( baseDir, captureDir ); + captureDir = baseDir; + } + + int index = theApp.filename.ReverseFind('\\'); + + CString name; + if(index != -1) + name = theApp.filename.Right(theApp.filename.GetLength()-index-1); + else + name = theApp.filename; + + if(captureDir.IsEmpty()) + captureDir = getDirFromFile(theApp.filename); + + LPCTSTR ext = "png"; + if(theApp.captureFormat != 0) + ext = "bmp"; + + if(isDriveRoot(captureDir)) + buffer.Format("%s%s_%02d.%s", + captureDir, + name, + captureNumber, + ext); + else + buffer.Format("%s\\%s_%02d.%s", + captureDir, + name, + captureNumber, + ext); + + if( fileExists( buffer ) ) { + // screenshot file already exists + screenCapture(++captureNumber); + // this will recursively use the first non-existent screenshot number + return; + } + + if(theApp.captureFormat == 0) + theApp.emulator.emuWritePNG(buffer); + else + theApp.emulator.emuWriteBMP(buffer); + + CString msg = winResLoadString(IDS_SCREEN_CAPTURE); + systemScreenMessage(msg); +} + +void MainWnd::winMouseOn() +{ + SetCursor(arrow); + if(theApp.videoOption > VIDEO_6X) { + theApp.mouseCounter = 10; + } else + theApp.mouseCounter = 0; +} + +void MainWnd::OnMouseMove(UINT nFlags, CPoint point) +{ + winMouseOn(); + + CWnd::OnMouseMove(nFlags, point); +} + +void MainWnd::OnInitMenu(CMenu* pMenu) +{ + CWnd::OnInitMenu(pMenu); + + soundPause(); +} + +void MainWnd::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized) +{ + CWnd::OnActivate(nState, pWndOther, bMinimized); + + bool a = (nState == WA_ACTIVE) || (nState == WA_CLICKACTIVE); + + if(a && theApp.input) { + theApp.active = a; + theApp.input->activate(); + if(!theApp.paused && emulating) { + soundResume(); + } + } else { + theApp.wasPaused = true; + if(theApp.pauseWhenInactive) { + if(emulating) { + soundPause(); + } + theApp.active = a; + } + + memset(theApp.delta,255,sizeof(theApp.delta)); + } + + if(theApp.paused && emulating) + { + theApp.painting = true; + systemDrawScreen(); + theApp.painting = false; + theApp.renderedFrames--; + } +} + +void MainWnd::OnDropFiles(HDROP hDropInfo) +{ + char szFile[1024]; + + if(DragQueryFile(hDropInfo, + 0, + szFile, + 1024)) { + theApp.szFile = szFile; + if(FileRun()) { + SetForegroundWindow(); + emulating = TRUE; + } else { + emulating = FALSE; + soundPause(); + } + } + DragFinish(hDropInfo); +} + +LRESULT MainWnd::OnMySysCommand(WPARAM wParam, LPARAM lParam) +{ + if(emulating && !theApp.paused) { + if((wParam&0xFFF0) == SC_SCREENSAVE || (wParam&0xFFF0) == SC_MONITORPOWER) + return 0; + } + return Default(); +} + +void MainWnd::OnNcLButtonDown(UINT nHitTest, CPoint point) +{ + // pause sound before process is halted + if( emulating ) { + soundPause(); + } + + CWnd::OnNcLButtonDown(nHitTest, point); +} + +void MainWnd::OnWindowPosChanging(WINDOWPOS* lpwndpos) +{ + CWnd::OnWindowPosChanging(lpwndpos); + + // pause sound before changing window position/size + if( emulating ) { + soundPause(); + } +} + +void MainWnd::OnNcRButtonDown(UINT nHitTest, CPoint point) +{ + // pause sound before process is halted + if( emulating ) { + soundPause(); + } + + CWnd::OnNcRButtonDown(nHitTest, point); +} + +void MainWnd::OnEnterSizeMove() +{ + // The WM_ENTERSIZEMOVE message is sent one time to a window after it enters the moving or sizing modal loop. + // Causes the emulator to stop when moving or resizing the window. + + if( emulating ) { + // pause sound before entering DefWindowProc + soundPause(); + } + + CWnd::OnEnterSizeMove(); +} diff --git a/src/win32/MainWnd.h b/src/win32/MainWnd.h new file mode 100644 index 0000000..4999332 --- /dev/null +++ b/src/win32/MainWnd.h @@ -0,0 +1,374 @@ +#pragma once + +#include "stdafx.h" +#include "VBA.h" + + +class MainWnd : public CWnd +{ +public: + MainWnd(); + virtual ~MainWnd(); + + HCURSOR arrow; + HACCEL m_hAccelTable; + + bool FileRun(); + bool BiosRun(); + void winMouseOn(); + void screenCapture(int captureNumber); + bool fileOpenSelect( int system ); + void updateSoundChannels(UINT nID); + bool fileImportGSACodeFile(CString& fileName); + bool writeSaveGame(const char *name); + bool loadSaveGame(const char *name); + CString winLoadFilter(UINT id); + void winLoadCheatList(const char *name); + void winLoadCheatListDefault(); + void readBatteryFile(); + void writeBatteryFile(); + bool isDriveRoot(CString& file); + CString getDirFromFile(CString& file); + void winSaveCheatList(const char *name); + void winSaveCheatListDefault(); + +private: + bool fileExists( LPCTSTR lpFileName ); + + + DECLARE_MESSAGE_MAP() + +protected: + virtual BOOL PreTranslateMessage(MSG* pMsg); + + afx_msg LRESULT OnMySysCommand(WPARAM, LPARAM); + afx_msg void OnUpdateFileLoadGameSlot(CCmdUI *pCmdUI); + afx_msg void OnUpdateFileSaveGameSlot(CCmdUI *pCmdUI); + afx_msg void OnUpdateOptionsJoypadAutofire(CCmdUI *pCmdUI); + afx_msg BOOL OnOptionsJoypadAutofire(UINT nID); + afx_msg void OnUpdateOptionsJoypadDefault(CCmdUI *pCmdUI); + afx_msg BOOL OnOptionsJoypadDefault(UINT nID); + afx_msg void OnUpdateOptionsFilterIFB(CCmdUI *pCmdUI); + afx_msg BOOL OnOptionsFilterIFB(UINT nID); + afx_msg void OnUpdateOptionsFilter(CCmdUI *pCmdUI); + afx_msg BOOL OnOptionsFilter(UINT nID); + afx_msg void OnUpdateOptionsPriority(CCmdUI *pCmdUI); + afx_msg BOOL OnOptionsPriority(UINT nID); + afx_msg void OnUpdateOptionsEmulatorShowSpeed(CCmdUI *pCmdUI); + afx_msg BOOL OnOptionsEmulatorShowSpeed(UINT nID); + afx_msg void OnSystemMinimize(); + afx_msg void OnUpdateVideoLayer(CCmdUI* pCmdUI); + afx_msg BOOL OnVideoLayer(UINT nID); + afx_msg void OnVideoLayerReset(); + afx_msg BOOL OnOptionVideoSize(UINT nID); + afx_msg BOOL OnOptionsFrameskip(UINT nID); + afx_msg void OnClose(); + afx_msg void OnHelpAbout(); + afx_msg void OnHelpFaq(); + afx_msg void OnFileOpenGBA(); + afx_msg void OnFileOpenGBC(); + afx_msg void OnFileOpenGB(); + afx_msg void OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu); + afx_msg void OnFilePause(); + afx_msg void OnUpdateFilePause(CCmdUI* pCmdUI); + afx_msg void OnFileReset(); + afx_msg void OnUpdateFileReset(CCmdUI* pCmdUI); + afx_msg void OnUpdateFileRecentFreeze(CCmdUI* pCmdUI); + afx_msg void OnFileRecentReset(); + afx_msg void OnFileRecentFreeze(); + afx_msg void OnFileExit(); + afx_msg void OnFileClose(); + afx_msg void OnUpdateFileClose(CCmdUI* pCmdUI); + afx_msg void OnFileLoad(); + afx_msg void OnUpdateFileLoad(CCmdUI* pCmdUI); + afx_msg void OnFileSave(); + afx_msg void OnUpdateFileSave(CCmdUI* pCmdUI); + afx_msg void OnFileImportBatteryfile(); + afx_msg void OnUpdateFileImportBatteryfile(CCmdUI* pCmdUI); + afx_msg void OnFileImportGamesharkcodefile(); + afx_msg void OnUpdateFileImportGamesharkcodefile(CCmdUI* pCmdUI); + afx_msg void OnFileImportGamesharksnapshot(); + afx_msg void OnUpdateFileImportGamesharksnapshot(CCmdUI* pCmdUI); + afx_msg void OnFileExportBatteryfile(); + afx_msg void OnUpdateFileExportBatteryfile(CCmdUI* pCmdUI); + afx_msg void OnFileExportGamesharksnapshot(); + afx_msg void OnUpdateFileExportGamesharksnapshot(CCmdUI* pCmdUI); + afx_msg void OnFileScreencapture(); + afx_msg void OnUpdateFileScreencapture(CCmdUI* pCmdUI); + afx_msg void OnFileRominformation(); + afx_msg void OnUpdateFileRominformation(CCmdUI* pCmdUI); + afx_msg void OnFileTogglemenu(); + afx_msg void OnUpdateFileTogglemenu(CCmdUI* pCmdUI); + afx_msg void OnUpdateOptionsFrameskipThrottleNothrottle(CCmdUI* pCmdUI); + afx_msg void OnUpdateOptionsFrameskipThrottle25(CCmdUI* pCmdUI); + afx_msg void OnUpdateOptionsFrameskipThrottle50(CCmdUI* pCmdUI); + afx_msg void OnUpdateOptionsFrameskipThrottle100(CCmdUI* pCmdUI); + afx_msg void OnUpdateOptionsFrameskipThrottle150(CCmdUI* pCmdUI); + afx_msg void OnUpdateOptionsFrameskipThrottle200(CCmdUI* pCmdUI); + afx_msg void OnUpdateOptionsFrameskipThrottleOther(CCmdUI* pCmdUI); + afx_msg void OnOptionsFrameskipThrottleNothrottle(); + afx_msg void OnOptionsFrameskipThrottle25(); + afx_msg void OnOptionsFrameskipThrottle50(); + afx_msg void OnOptionsFrameskipThrottle100(); + afx_msg void OnOptionsFrameskipThrottle150(); + afx_msg void OnOptionsFrameskipThrottle200(); + afx_msg void OnOptionsFrameskipThrottleOther(); + afx_msg void OnOptionsFrameskipAutomatic(); + afx_msg void OnUpdateOptionsFrameskipAutomatic(CCmdUI* pCmdUI); + afx_msg void OnUpdateOptionsVideoFrameskip0(CCmdUI* pCmdUI); + afx_msg void OnUpdateOptionsVideoFrameskip1(CCmdUI* pCmdUI); + afx_msg void OnUpdateOptionsVideoFrameskip2(CCmdUI* pCmdUI); + afx_msg void OnUpdateOptionsVideoFrameskip3(CCmdUI* pCmdUI); + afx_msg void OnUpdateOptionsVideoFrameskip4(CCmdUI* pCmdUI); + afx_msg void OnUpdateOptionsVideoFrameskip5(CCmdUI* pCmdUI); + afx_msg void OnUpdateOptionsVideoFrameskip6(CCmdUI* pCmdUI); + afx_msg void OnUpdateOptionsVideoFrameskip7(CCmdUI* pCmdUI); + afx_msg void OnUpdateOptionsVideoFrameskip8(CCmdUI* pCmdUI); + afx_msg void OnUpdateOptionsVideoFrameskip9(CCmdUI* pCmdUI); + afx_msg void OnOptionsVideoVsync(); + afx_msg void OnUpdateOptionsVideoVsync(CCmdUI* pCmdUI); + afx_msg void OnUpdateOptionsVideoX1(CCmdUI* pCmdUI); + afx_msg void OnUpdateOptionsVideoX2(CCmdUI* pCmdUI); + afx_msg void OnUpdateOptionsVideoX3(CCmdUI* pCmdUI); + afx_msg void OnUpdateOptionsVideoX4(CCmdUI* pCmdUI); + afx_msg void OnUpdateOptionsVideoX5(CCmdUI* pCmdUI); + afx_msg void OnUpdateOptionsVideoX6(CCmdUI* pCmdUI); + afx_msg void OnOptionsVideoFullscreen(); + afx_msg void OnUpdateOptionsVideoFullscreen(CCmdUI* pCmdUI); + afx_msg void OnMove(int x, int y); + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnOptionsVideoDisablesfx(); + afx_msg void OnUpdateOptionsVideoDisablesfx(CCmdUI* pCmdUI); + afx_msg void OnOptionsVideoFullscreenstretchtofit(); + afx_msg void OnUpdateOptionsVideoFullscreenstretchtofit(CCmdUI* pCmdUI); + afx_msg void OnOptionsVideoRendermethodDirectdraw(); + afx_msg void OnUpdateOptionsVideoRendermethodDirectdraw(CCmdUI* pCmdUI); + afx_msg void OnOptionsVideoRendermethodDirect3d(); + afx_msg void OnUpdateOptionsVideoRendermethodDirect3d(CCmdUI* pCmdUI); + afx_msg void OnOptionsVideoRendermethodOpengl(); + afx_msg void OnUpdateOptionsVideoRendermethodOpengl(CCmdUI* pCmdUI); + afx_msg void OnOptionsVideoTriplebuffering(); + afx_msg void OnUpdateOptionsVideoTriplebuffering(CCmdUI* pCmdUI); + afx_msg void OnOptionsVideoRenderoptionsD3dnofilter(); + afx_msg void OnUpdateOptionsVideoRenderoptionsD3dnofilter(CCmdUI* pCmdUI); + afx_msg void OnOptionsVideoRenderoptionsD3dbilinear(); + afx_msg void OnUpdateOptionsVideoRenderoptionsD3dbilinear(CCmdUI* pCmdUI); + afx_msg void OnOptionsVideoRenderoptionsGlnearest(); + afx_msg void OnUpdateOptionsVideoRenderoptionsGlnearest(CCmdUI* pCmdUI); + afx_msg void OnOptionsVideoRenderoptionsGlbilinear(); + afx_msg void OnUpdateOptionsVideoRenderoptionsGlbilinear(CCmdUI* pCmdUI); + afx_msg void OnContextMenu(CWnd* pWnd, CPoint point); + afx_msg void OnOptionsEmulatorAssociate(); + afx_msg void OnOptionsEmulatorDirectories(); + afx_msg void OnOptionsEmulatorDisablestatusmessages(); + afx_msg void OnUpdateOptionsEmulatorDisablestatusmessages(CCmdUI* pCmdUI); + afx_msg void OnOptionsEmulatorSynchronize(); + afx_msg void OnUpdateOptionsEmulatorSynchronize(CCmdUI* pCmdUI); + afx_msg void OnOptionsEmulatorPausewheninactive(); + afx_msg void OnUpdateOptionsEmulatorPausewheninactive(CCmdUI* pCmdUI); + afx_msg void OnOptionsEmulatorSpeeduptoggle(); + afx_msg void OnUpdateOptionsEmulatorSpeeduptoggle(CCmdUI* pCmdUI); + afx_msg void OnOptionsEmulatorAutomaticallyApplyPatchFiles(); + afx_msg void OnUpdateOptionsEmulatorAutomaticallyipspatch(CCmdUI* pCmdUI); + afx_msg void OnOptionsEmulatorAgbprint(); + afx_msg void OnUpdateOptionsEmulatorAgbprint(CCmdUI* pCmdUI); + afx_msg void OnOptionsEmulatorRealtimeclock(); + afx_msg void OnUpdateOptionsEmulatorRealtimeclock(CCmdUI* pCmdUI); + afx_msg void OnOptionsEmulatorRewindinterval(); + afx_msg void OnOptionsEmulatorSavetypeAutomatic(); + afx_msg void OnUpdateOptionsEmulatorSavetypeAutomatic(CCmdUI* pCmdUI); + afx_msg void OnOptionsEmulatorSavetypeEeprom(); + afx_msg void OnUpdateOptionsEmulatorSavetypeEeprom(CCmdUI* pCmdUI); + afx_msg void OnOptionsEmulatorSavetypeSram(); + afx_msg void OnUpdateOptionsEmulatorSavetypeSram(CCmdUI* pCmdUI); + afx_msg void OnOptionsEmulatorSavetypeFlash(); + afx_msg void OnUpdateOptionsEmulatorSavetypeFlash(CCmdUI* pCmdUI); + afx_msg void OnOptionsEmulatorSavetypeEepromsensor(); + afx_msg void OnUpdateOptionsEmulatorSavetypeEepromsensor(CCmdUI* pCmdUI); + afx_msg void OnOptionsEmulatorSavetypeNone(); + afx_msg void OnUpdateOptionsEmulatorSavetypeNone(CCmdUI* pCmdUI); + afx_msg void OnOptionsEmulatorSavetypeFlash512k(); + afx_msg void OnUpdateOptionsEmulatorSavetypeFlash512k(CCmdUI* pCmdUI); + afx_msg void OnOptionsEmulatorSavetypeFlash1m(); + afx_msg void OnUpdateOptionsEmulatorSavetypeFlash1m(CCmdUI* pCmdUI); + afx_msg void OnOptionsEmulatorSavetypeDetectNow(); + afx_msg void OnUpdateOptionsEmulatorSavetypeDetectNow(CCmdUI* pCmdUI); + afx_msg void OnOptionsEmulatorPngformat(); + afx_msg void OnUpdateOptionsEmulatorPngformat(CCmdUI* pCmdUI); + afx_msg void OnOptionsEmulatorBmpformat(); + afx_msg void OnUpdateOptionsEmulatorBmpformat(CCmdUI* pCmdUI); + afx_msg void OnOptionsSoundChannel1(); + afx_msg void OnUpdateOptionsSoundChannel1(CCmdUI* pCmdUI); + afx_msg void OnOptionsSoundChannel2(); + afx_msg void OnUpdateOptionsSoundChannel2(CCmdUI* pCmdUI); + afx_msg void OnOptionsSoundChannel3(); + afx_msg void OnUpdateOptionsSoundChannel3(CCmdUI* pCmdUI); + afx_msg void OnOptionsSoundChannel4(); + afx_msg void OnUpdateOptionsSoundChannel4(CCmdUI* pCmdUI); + afx_msg void OnOptionsSoundDirectsounda(); + afx_msg void OnUpdateOptionsSoundDirectsounda(CCmdUI* pCmdUI); + afx_msg void OnOptionsSoundDirectsoundb(); + afx_msg void OnUpdateOptionsSoundDirectsoundb(CCmdUI* pCmdUI); + afx_msg void OnOptionsGameboyBorder(); + afx_msg void OnUpdateOptionsGameboyBorder(CCmdUI* pCmdUI); + afx_msg void OnOptionsGameboyPrinter(); + afx_msg void OnUpdateOptionsGameboyPrinter(CCmdUI* pCmdUI); + afx_msg void OnOptionsGameboyBorderAutomatic(); + afx_msg void OnUpdateOptionsGameboyBorderAutomatic(CCmdUI* pCmdUI); + afx_msg void OnOptionsGameboyAutomatic(); + afx_msg void OnUpdateOptionsGameboyAutomatic(CCmdUI* pCmdUI); + afx_msg void OnOptionsGameboyGba(); + afx_msg void OnUpdateOptionsGameboyGba(CCmdUI* pCmdUI); + afx_msg void OnOptionsGameboyCgb(); + afx_msg void OnUpdateOptionsGameboyCgb(CCmdUI* pCmdUI); + afx_msg void OnOptionsGameboySgb(); + afx_msg void OnUpdateOptionsGameboySgb(CCmdUI* pCmdUI); + afx_msg void OnOptionsGameboySgb2(); + afx_msg void OnUpdateOptionsGameboySgb2(CCmdUI* pCmdUI); + afx_msg void OnOptionsGameboyGb(); + afx_msg void OnUpdateOptionsGameboyGb(CCmdUI* pCmdUI); + afx_msg void OnOptionsGameboyRealcolors(); + afx_msg void OnUpdateOptionsGameboyRealcolors(CCmdUI* pCmdUI); + afx_msg void OnOptionsGameboyGameboycolors(); + afx_msg void OnUpdateOptionsGameboyGameboycolors(CCmdUI* pCmdUI); + afx_msg void OnOptionsGameboyColors(); + afx_msg void OnOptionsFilterDisablemmx(); + afx_msg void OnUpdateOptionsFilterDisablemmx(CCmdUI* pCmdUI); + afx_msg void OnOptionsLanguageSystem(); + afx_msg void OnUpdateOptionsLanguageSystem(CCmdUI* pCmdUI); + afx_msg void OnOptionsLanguageEnglish(); + afx_msg void OnUpdateOptionsLanguageEnglish(CCmdUI* pCmdUI); + afx_msg void OnOptionsLanguageOther(); + afx_msg void OnUpdateOptionsLanguageOther(CCmdUI* pCmdUI); + afx_msg void OnOptionsJoypadConfigure1(); + afx_msg void OnUpdateOptionsJoypadConfigure1(CCmdUI* pCmdUI); + afx_msg void OnOptionsJoypadConfigure2(); + afx_msg void OnUpdateOptionsJoypadConfigure2(CCmdUI* pCmdUI); + afx_msg void OnOptionsJoypadConfigure3(); + afx_msg void OnUpdateOptionsJoypadConfigure3(CCmdUI* pCmdUI); + afx_msg void OnOptionsJoypadConfigure4(); + afx_msg void OnUpdateOptionsJoypadConfigure4(CCmdUI* pCmdUI); + afx_msg void OnOptionsJoypadMotionconfigure(); + afx_msg void OnUpdateOptionsJoypadMotionconfigure(CCmdUI* pCmdUI); + afx_msg void OnCheatsSearchforcheats(); + afx_msg void OnUpdateCheatsSearchforcheats(CCmdUI* pCmdUI); + afx_msg void OnCheatsCheatlist(); + afx_msg void OnUpdateCheatsCheatlist(CCmdUI* pCmdUI); + afx_msg void OnCheatsAutomaticsaveloadcheats(); + afx_msg void OnCheatsLoadcheatlist(); + afx_msg void OnUpdateCheatsLoadcheatlist(CCmdUI* pCmdUI); + afx_msg void OnCheatsSavecheatlist(); + afx_msg void OnUpdateCheatsSavecheatlist(CCmdUI* pCmdUI); + afx_msg void OnToolsDisassemble(); + afx_msg void OnUpdateToolsDisassemble(CCmdUI* pCmdUI); + afx_msg void OnToolsLogging(); + afx_msg void OnUpdateToolsLogging(CCmdUI* pCmdUI); + afx_msg void OnToolsIoviewer(); + afx_msg void OnUpdateToolsIoviewer(CCmdUI* pCmdUI); + afx_msg void OnToolsMapview(); + afx_msg void OnUpdateToolsMapview(CCmdUI* pCmdUI); + afx_msg void OnToolsMemoryviewer(); + afx_msg void OnUpdateToolsMemoryviewer(CCmdUI* pCmdUI); + afx_msg void OnToolsOamviewer(); + afx_msg void OnUpdateToolsOamviewer(CCmdUI* pCmdUI); + afx_msg void OnToolsPaletteview(); + afx_msg void OnUpdateToolsPaletteview(CCmdUI* pCmdUI); + afx_msg void OnToolsTileviewer(); + afx_msg void OnUpdateToolsTileviewer(CCmdUI* pCmdUI); + afx_msg void OnDebugNextframe(); + afx_msg void OnUpdateCheatsAutomaticsaveloadcheats(CCmdUI* pCmdUI); + afx_msg void OnToolsDebugGdb(); + afx_msg void OnUpdateToolsDebugGdb(CCmdUI* pCmdUI); + afx_msg void OnToolsDebugLoadandwait(); + afx_msg void OnUpdateToolsDebugLoadandwait(CCmdUI* pCmdUI); + afx_msg void OnToolsDebugBreak(); + afx_msg void OnUpdateToolsDebugBreak(CCmdUI* pCmdUI); + afx_msg void OnToolsDebugDisconnect(); + afx_msg void OnUpdateToolsDebugDisconnect(CCmdUI* pCmdUI); + afx_msg void OnOptionsSoundStartrecording(); + afx_msg void OnUpdateOptionsSoundStartrecording(CCmdUI* pCmdUI); + afx_msg void OnOptionsSoundStoprecording(); + afx_msg void OnUpdateOptionsSoundStoprecording(CCmdUI* pCmdUI); + afx_msg void OnToolsRecordStartavirecording(); + afx_msg void OnUpdateToolsRecordStartavirecording(CCmdUI* pCmdUI); + afx_msg void OnToolsRecordStopavirecording(); + afx_msg void OnUpdateToolsRecordStopavirecording(CCmdUI* pCmdUI); + afx_msg void OnPaint(); + afx_msg void OnToolsRecordStartmovierecording(); + afx_msg void OnUpdateToolsRecordStartmovierecording(CCmdUI* pCmdUI); + afx_msg void OnToolsRecordStopmovierecording(); + afx_msg void OnUpdateToolsRecordStopmovierecording(CCmdUI* pCmdUI); + afx_msg void OnToolsPlayStartmovieplaying(); + afx_msg void OnUpdateToolsPlayStartmovieplaying(CCmdUI* pCmdUI); + afx_msg void OnToolsPlayStopmovieplaying(); + afx_msg void OnUpdateToolsPlayStopmovieplaying(CCmdUI* pCmdUI); + afx_msg void OnToolsRewind(); + afx_msg void OnUpdateToolsRewind(CCmdUI* pCmdUI); + afx_msg void OnToolsCustomize(); + afx_msg void OnUpdateToolsCustomize(CCmdUI* pCmdUI); + afx_msg void OnHelpBugreport(); + afx_msg void OnMouseMove(UINT nFlags, CPoint point); + afx_msg void OnInitMenu(CMenu* pMenu); + afx_msg void OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized); + afx_msg void OnDropFiles(HDROP hDropInfo); + afx_msg void OnFileSavegameOldestslot(); + afx_msg void OnUpdateFileSavegameOldestslot(CCmdUI* pCmdUI); + afx_msg void OnFileLoadgameMostrecent(); + afx_msg void OnUpdateFileLoadgameMostrecent(CCmdUI* pCmdUI); + afx_msg void OnFileLoadgameAutoloadmostrecent(); + afx_msg void OnUpdateFileLoadgameAutoloadmostrecent(CCmdUI* pCmdUI); + afx_msg void OnCheatsDisablecheats(); + afx_msg void OnUpdateCheatsDisablecheats(CCmdUI* pCmdUI); + afx_msg void OnOptionsVideoFullscreenmaxscale(); + afx_msg void OnOptionsSelectPlugin(); + afx_msg void OnOptionsEmulatorGameoverrides(); + afx_msg void OnUpdateOptionsEmulatorGameoverrides(CCmdUI* pCmdUI); + afx_msg void OnHelpGnupubliclicense(); + afx_msg BOOL OnFileRecentFile(UINT nID); + afx_msg BOOL OnFileLoadSlot(UINT nID); + afx_msg BOOL OnFileSaveSlot(UINT nID); + afx_msg void OnOptionsSoundHardwareacceleration(); + afx_msg void OnUpdateOptionsSoundHardwareacceleration(CCmdUI *pCmdUI); + afx_msg void OnLinkOptions(); + afx_msg void OnOptionsLinkRFU(); + afx_msg void OnUpdateOptionsLinkRFU(CCmdUI* pCmdUI); + afx_msg void OnOptionsLinkEnable(); + afx_msg void OnUpdateOptionsLinkEnable(CCmdUI* pCmdUI); + afx_msg void OnOptionsJoybus(); + + afx_msg void OnOutputapiDirectsound(); + afx_msg void OnUpdateOutputapiDirectsound(CCmdUI *pCmdUI); + + afx_msg void OnOutputapiOpenal(); + afx_msg void OnUpdateOutputapiOpenal(CCmdUI *pCmdUI); + afx_msg void OnOutputapiOalconfiguration(); + afx_msg void OnUpdateOutputapiOalconfiguration(CCmdUI *pCmdUI); + + afx_msg void OnOutputapiXaudio2(); + afx_msg void OnUpdateOutputapiXaudio2(CCmdUI *pCmdUI); + afx_msg void OnOutputapiXaudio2config(); + afx_msg void OnUpdateOutputapiXaudio2config(CCmdUI *pCmdUI); + + afx_msg void OnRenderapiD3dmotionblur(); + afx_msg void OnUpdateRenderapiD3dmotionblur(CCmdUI *pCmdUI); + afx_msg void OnPixelfilterMultiThreading(); + + afx_msg void OnMoving(UINT fwSide, LPRECT pRect); + afx_msg void OnSizing(UINT fwSide, LPRECT pRect); + afx_msg void OnNcLButtonDown(UINT nHitTest, CPoint point); + afx_msg void OnWindowPosChanging(WINDOWPOS* lpwndpos); + afx_msg void OnEmulatorBiosfiles(); + afx_msg void OnNcRButtonDown(UINT nHitTest, CPoint point); + afx_msg void OnUpdatePixelfilterMultiThreading(CCmdUI *pCmdUI); + afx_msg void OnUpdateOptionsSelectPlugin(CCmdUI *pCmdUI); + + afx_msg void OnLoadgameDonotchangebatterysave(); + afx_msg void OnUpdateLoadgameDonotchangebatterysave(CCmdUI *pCmdUI); + afx_msg void OnLoadgameDonotchangecheatlist(); + afx_msg void OnUpdateLoadgameDonotchangecheatlist(CCmdUI *pCmdUI); + + afx_msg void OnEnterSizeMove(); + + afx_msg void OnAudioCoreSettings(); + afx_msg void OnUpdateAudioCoreSettings(CCmdUI *pCmdUI); +}; diff --git a/src/win32/MainWndCheats.cpp b/src/win32/MainWndCheats.cpp new file mode 100644 index 0000000..31114b8 --- /dev/null +++ b/src/win32/MainWndCheats.cpp @@ -0,0 +1,144 @@ +#include "stdafx.h" +#include "MainWnd.h" + +#include "FileDlg.h" +#include "GBACheats.h" +#include "GBCheatsDlg.h" +#include "Reg.h" +#include "WinResUtil.h" + +#include "../gba/GBA.h" +#include "../gba/Globals.h" +#include "../gb/gbCheats.h" + +extern int emulating; + +void MainWnd::OnCheatsSearchforcheats() +{ + if(theApp.cartridgeType == 0) { + GBACheatSearch dlg; + dlg.DoModal(); + } else { + GBCheatSearch dlg; + dlg.DoModal(); + } +} + +void MainWnd::OnUpdateCheatsSearchforcheats(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(emulating); +} + +void MainWnd::OnCheatsCheatlist() +{ + if(theApp.cartridgeType == 0) { + GBACheatList dlg; + dlg.DoModal(); + } else { + GBCheatList dlg; + dlg.DoModal(); + } +} + +void MainWnd::OnUpdateCheatsCheatlist(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(emulating); +} + +void MainWnd::OnCheatsAutomaticsaveloadcheats() +{ + theApp.autoSaveLoadCheatList = !theApp.autoSaveLoadCheatList; +} + +void MainWnd::OnUpdateCheatsAutomaticsaveloadcheats(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.autoSaveLoadCheatList); +} + +void MainWnd::OnCheatsLoadcheatlist() +{ + CString buffer; + CString filename; + + int index = theApp.filename.ReverseFind('\\'); + + if(index != -1) + buffer = theApp.filename.Right(theApp.filename.GetLength()-index-1); + else + buffer = theApp.filename; + + CString saveDir = regQueryStringValue("saveDir", NULL); + + if(saveDir.IsEmpty()) + saveDir = getDirFromFile(theApp.filename); + + if(isDriveRoot(saveDir)) + filename.Format("%s%s.clt", saveDir, buffer); + else + filename.Format("%s\\%s.clt", saveDir, buffer); + + LPCTSTR exts[] = { ".clt" }; + CString filter = winLoadFilter(IDS_FILTER_CHEAT_LIST); + CString title = winResLoadString(IDS_SELECT_CHEAT_LIST_NAME); + + FileDlg dlg(this, filename, filter, 0, "CLT", exts, saveDir, title, false); + + if(dlg.DoModal() == IDOK) { + winLoadCheatList(dlg.GetPathName()); + } +} + +void MainWnd::OnUpdateCheatsLoadcheatlist(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(emulating); +} + +void MainWnd::OnCheatsSavecheatlist() +{ + CString buffer; + CString filename; + + int index = theApp.filename.ReverseFind('\\'); + + if(index != -1) + buffer = theApp.filename.Right(theApp.filename.GetLength()-index-1); + else + buffer = theApp.filename; + + CString saveDir = regQueryStringValue("saveDir", NULL); + + if(saveDir.IsEmpty()) + saveDir = getDirFromFile(theApp.filename); + + if(isDriveRoot(saveDir)) + filename.Format("%s%s.clt", saveDir, buffer); + else + filename.Format("%s\\%s.clt", saveDir, buffer); + + LPCTSTR exts[] = { ".clt" }; + CString filter = winLoadFilter(IDS_FILTER_CHEAT_LIST); + CString title = winResLoadString(IDS_SELECT_CHEAT_LIST_NAME); + + FileDlg dlg(this, filename, filter, 0, "CLT", exts, saveDir, title, true); + + if(dlg.DoModal() == IDOK) { + winSaveCheatList(dlg.GetPathName()); + } +} + +void MainWnd::OnUpdateCheatsSavecheatlist(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(emulating); +} + +void MainWnd::OnCheatsDisablecheats() +{ + cheatsEnabled = !cheatsEnabled; + systemScreenMessage(winResLoadString(cheatsEnabled ? IDS_CHEATS_ENABLED : IDS_CHEATS_DISABLED)); +} + +void MainWnd::OnUpdateCheatsDisablecheats(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(!cheatsEnabled); +} + diff --git a/src/win32/MainWndFile.cpp b/src/win32/MainWndFile.cpp new file mode 100644 index 0000000..7ff97e5 --- /dev/null +++ b/src/win32/MainWndFile.cpp @@ -0,0 +1,996 @@ +#include "stdafx.h" +#include "MainWnd.h" + +#include + +#include "ExportGSASnapshot.h" +#include "FileDlg.h" +#include "GSACodeSelect.h" +#include "RomInfo.h" +#include "Reg.h" +#include "WinResUtil.h" +#include "Logging.h" + +#include "../gba/GBA.h" +#include "../gba/Globals.h" +#include "../NLS.h" +#include "../gba/Sound.h" +#include "../gb/GB.h" +#include "../gb/gbCheats.h" +#include "../gb/gbGlobals.h" + +#include "../version.h" + +extern void remoteCleanUp(); +extern void InterframeCleanup(); + + +void MainWnd::OnFileOpenGBA() +{ + if( fileOpenSelect( 0 ) ) { + FileRun(); + } +} + +void MainWnd::OnFileOpenGBC() +{ + if( fileOpenSelect( 1 ) ) { + FileRun(); + } +} + +void MainWnd::OnFileOpenGB() +{ + if( fileOpenSelect( 2 ) ) { + FileRun(); + } +} + +void MainWnd::OnFilePause() +{ + theApp.paused = !theApp.paused; + if(emulating) { + if(theApp.paused) { + theApp.wasPaused = true; + soundPause(); + } else { + soundResume(); + } + } +} + +void MainWnd::OnUpdateFilePause(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.paused); +} + +void MainWnd::OnFileReset() +{ + if(emulating) { + theApp.emulator.emuReset(); + systemScreenMessage(winResLoadString(IDS_RESET)); + } +} + +void MainWnd::OnUpdateFileReset(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(emulating); +} + +void MainWnd::OnUpdateFileRecentFreeze(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.recentFreeze); + + if(pCmdUI->m_pMenu == NULL) + return; + + CMenu *pMenu = pCmdUI->m_pMenu; + + int i; + for(i = 0; i < 10; i++) { + if(!pMenu->RemoveMenu(ID_FILE_MRU_FILE1+i, MF_BYCOMMAND)) + break; + } + + for(i = 0; i < 10; i++) { + CString p = theApp.recentFiles[i]; + if(p.GetLength() == 0) + break; + int index = p.ReverseFind('\\'); + + if(index != -1) + p = p.Right(p.GetLength()-index-1); + + pMenu->AppendMenu(MF_STRING, ID_FILE_MRU_FILE1+i, p); + } + theApp.winAccelMgr.UpdateMenu((HMENU)*pMenu); +} + +BOOL MainWnd::OnFileRecentFile(UINT nID) +{ + if(theApp.recentFiles[(nID&0xFFFF)-ID_FILE_MRU_FILE1].GetLength()) { + theApp.szFile = theApp.recentFiles[(nID&0xFFFF)-ID_FILE_MRU_FILE1]; + if(FileRun()) + emulating = true; + else { + emulating = false; + soundPause(); + } + } + return TRUE; +} + +void MainWnd::OnFileRecentReset() +{ + int i = 0; + for(i = 0; i < 10; i++) + theApp.recentFiles[i] = ""; +} + +void MainWnd::OnFileRecentFreeze() +{ + theApp.recentFreeze = !theApp.recentFreeze; +} + +void MainWnd::OnFileExit() +{ + SendMessage(WM_CLOSE); +} + +void MainWnd::OnFileClose() +{ + // save battery file before we change the filename... + if(rom != NULL || gbRom != NULL) { + if(theApp.autoSaveLoadCheatList) + winSaveCheatListDefault(); + writeBatteryFile(); + soundPause(); + theApp.emulator.emuCleanUp(); + remoteCleanUp(); + } + emulating = 0; + RedrawWindow(NULL,NULL,RDW_INVALIDATE|RDW_ERASE|RDW_ALLCHILDREN); + systemSetTitle(VBA_NAME_AND_SUBVERSION); +} + +void MainWnd::OnUpdateFileClose(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(emulating); +} + +void MainWnd::OnFileLoad() +{ + CString buffer; + CString filename; + + int index = theApp.filename.ReverseFind('\\'); + + if(index != -1) + buffer = theApp.filename.Right(theApp.filename.GetLength()-index-1); + else + buffer = theApp.filename; + + CString saveDir = regQueryStringValue("saveDir", NULL); + if( saveDir[0] == '.' ) { + // handle as relative path + char baseDir[MAX_PATH+1]; + GetModuleFileName( NULL, baseDir, MAX_PATH ); + baseDir[MAX_PATH] = '\0'; // for security reasons + PathRemoveFileSpec( baseDir ); // removes the trailing file name and backslash + strcat( baseDir, "\\" ); + strcat( baseDir, saveDir ); + saveDir = baseDir; + } + + if(saveDir.IsEmpty()) + saveDir = getDirFromFile(theApp.filename); + + if(isDriveRoot(saveDir)) + filename.Format("%s%s.sgm", saveDir, buffer); + else + filename.Format("%s\\%s.sgm", saveDir, buffer); + + LPCTSTR exts[] = { ".sgm" }; + CString filter = winLoadFilter(IDS_FILTER_SGM); + CString title = winResLoadString(IDS_SELECT_SAVE_GAME_NAME); + + FileDlg dlg(this, filename, filter, 0, "", exts, saveDir, title, false); + + if(dlg.DoModal() == IDOK) { + bool res = loadSaveGame(dlg.GetPathName()); + + theApp.rewindCount = 0; + theApp.rewindCounter = 0; + theApp.rewindSaveNeeded = false; + + if(res) + systemScreenMessage(winResLoadString(IDS_LOADED_STATE)); + } +} + +void MainWnd::OnUpdateFileLoad(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(emulating); +} + +BOOL MainWnd::OnFileLoadSlot(UINT nID) +{ + nID = nID + 1 - ID_FILE_LOADGAME_SLOT1; + + CString buffer; + CString filename; + + int index = theApp.filename.ReverseFind('\\'); + + if(index != -1) + buffer = theApp.filename.Right(theApp.filename.GetLength()-index-1); + else + buffer = theApp.filename; + + CString saveDir = regQueryStringValue("saveDir", NULL); + if( saveDir[0] == '.' ) { + // handle as relative path + char baseDir[MAX_PATH+1]; + GetModuleFileName( NULL, baseDir, MAX_PATH ); + baseDir[MAX_PATH] = '\0'; // for security reasons + PathRemoveFileSpec( baseDir ); // removes the trailing file name and backslash + strcat( baseDir, "\\" ); + strcat( baseDir, saveDir ); + saveDir = baseDir; + } + + if(saveDir.IsEmpty()) + saveDir = getDirFromFile(theApp.filename); + + if(isDriveRoot(saveDir)) + filename.Format("%s%s%d.sgm", saveDir, buffer, nID); + else + filename.Format("%s\\%s%d.sgm", saveDir, buffer, nID); + + CString format = winResLoadString(IDS_LOADED_STATE_N); + buffer.Format(format, nID); + + bool res = loadSaveGame(filename); + + if (theApp.paused) + InterframeCleanup(); + + systemScreenMessage(buffer); + + systemDrawScreen(); + + //theApp.rewindCount = 0; + //theApp.rewindCounter = 0; + //theApp.rewindSaveNeeded = false; + + return res; +} + +void MainWnd::OnFileSave() +{ + CString buffer; + CString filename; + + int index = theApp.filename.ReverseFind('\\'); + + if(index != -1) + buffer = theApp.filename.Right(theApp.filename.GetLength()-index-1); + else + buffer = theApp.filename; + + CString saveDir = regQueryStringValue("saveDir", NULL); + if( saveDir[0] == '.' ) { + // handle as relative path + char baseDir[MAX_PATH+1]; + GetModuleFileName( NULL, baseDir, MAX_PATH ); + baseDir[MAX_PATH] = '\0'; // for security reasons + PathRemoveFileSpec( baseDir ); // removes the trailing file name and backslash + strcat( baseDir, "\\" ); + strcat( baseDir, saveDir ); + saveDir = baseDir; + } + + if(saveDir.IsEmpty()) + saveDir = getDirFromFile(theApp.filename); + + if(isDriveRoot(saveDir)) + filename.Format("%s%s.sgm", saveDir, buffer); + else + filename.Format("%s\\%s.sgm", saveDir, buffer); + + LPCTSTR exts[] = { ".sgm" }; + CString filter = winLoadFilter(IDS_FILTER_SGM); + CString title = winResLoadString(IDS_SELECT_SAVE_GAME_NAME); + + FileDlg dlg(this, filename, filter, 0, "", exts, saveDir, title, true); + + if(dlg.DoModal() == IDOK) { + bool res = writeSaveGame(dlg.GetPathName()); + if(res) + systemScreenMessage(winResLoadString(IDS_WROTE_STATE)); + } +} + +void MainWnd::OnUpdateFileSave(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(emulating); +} + +BOOL MainWnd::OnFileSaveSlot(UINT nID) +{ + nID = nID + 1 - ID_FILE_SAVEGAME_SLOT1; + + CString buffer; + CString filename; + + int index = theApp.filename.ReverseFind('\\'); + + if(index != -1) + buffer = theApp.filename.Right(theApp.filename.GetLength()-index-1); + else + buffer = theApp.filename; + + CString saveDir = regQueryStringValue("saveDir", NULL); + if( saveDir[0] == '.' ) { + // handle as relative path + char baseDir[MAX_PATH+1]; + GetModuleFileName( NULL, baseDir, MAX_PATH ); + baseDir[MAX_PATH] = '\0'; // for security reasons + PathRemoveFileSpec( baseDir ); // removes the trailing file name and backslash + strcat( baseDir, "\\" ); + strcat( baseDir, saveDir ); + saveDir = baseDir; + } + + if(saveDir.IsEmpty()) + saveDir = getDirFromFile(theApp.filename); + + if(isDriveRoot(saveDir)) + filename.Format("%s%s%d.sgm", saveDir, buffer, nID); + else + filename.Format("%s\\%s%d.sgm", saveDir, buffer, nID); + + bool res = writeSaveGame(filename); + + CString format = winResLoadString(IDS_WROTE_STATE_N); + buffer.Format(format, nID); + + systemScreenMessage(buffer); + + systemDrawScreen(); + + return res; +} + +void MainWnd::OnFileImportBatteryfile() +{ + LPCTSTR exts[] = { ".sav", ".dat" }; + CString filter = winLoadFilter(IDS_FILTER_SAV); + CString title = winResLoadString(IDS_SELECT_BATTERY_FILE); + + CString saveDir = regQueryStringValue("batteryDir", NULL); + if( saveDir[0] == '.' ) { + // handle as relative path + char baseDir[MAX_PATH+1]; + GetModuleFileName( NULL, baseDir, MAX_PATH ); + baseDir[MAX_PATH] = '\0'; // for security reasons + PathRemoveFileSpec( baseDir ); // removes the trailing file name and backslash + strcat( baseDir, "\\" ); + strcat( baseDir, saveDir ); + saveDir = baseDir; + } + + if(saveDir.IsEmpty()) + saveDir = getDirFromFile(theApp.filename); + + FileDlg dlg(this, "", filter, 0, "", exts, saveDir, title, false); + + if(dlg.DoModal() == IDCANCEL) + return; + + CString str1 = winResLoadString(IDS_SAVE_WILL_BE_LOST); + CString str2 = winResLoadString(IDS_CONFIRM_ACTION); + + if(MessageBox(str1, + str2, + MB_OKCANCEL) == IDCANCEL) + return; + + bool res = false; + + res = theApp.emulator.emuReadBattery(dlg.GetPathName()); + + if(!res) + systemMessage(MSG_CANNOT_OPEN_FILE, "Cannot open file %s", dlg.GetPathName()); + else { + //Removed the reset to allow loading a battery file 'within' a save state. + //theApp.emulator.emuReset(); + } +} + +void MainWnd::OnUpdateFileImportBatteryfile(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(emulating); +} + +void MainWnd::OnFileImportGamesharkcodefile() +{ + LPCTSTR exts[] = { "" }; + CString filter = theApp.cartridgeType == 0 ? winLoadFilter(IDS_FILTER_SPC) : winLoadFilter(IDS_FILTER_GCF); + CString title = winResLoadString(IDS_SELECT_CODE_FILE); + + FileDlg dlg(this, "", filter, 0, "", exts, "", title, false); + + if(dlg.DoModal() == IDCANCEL) + return; + + CString str1 = winResLoadString(IDS_CODES_WILL_BE_LOST); + CString str2 = winResLoadString(IDS_CONFIRM_ACTION); + + if(MessageBox(str1, + str2, + MB_OKCANCEL) == IDCANCEL) + return; + + bool res = false; + CString file = dlg.GetPathName(); + if(theApp.cartridgeType == 1) + res = gbCheatReadGSCodeFile(file); + else { + res = fileImportGSACodeFile(file); + } +} + +void MainWnd::OnUpdateFileImportGamesharkcodefile(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(emulating); +} + +void MainWnd::OnFileImportGamesharksnapshot() +{ + LPCTSTR exts[] = { ".?ps", ".gsv" }; + CString filter = theApp.cartridgeType == 1 ? winLoadFilter(IDS_FILTER_GBS) : winLoadFilter(IDS_FILTER_GSVSPS); + CString title = winResLoadString(IDS_SELECT_SNAPSHOT_FILE); + + FileDlg dlg(this, "", filter, 0, "", exts, "", title, false); + + if(dlg.DoModal() == IDCANCEL) + return; + + CString str1 = winResLoadString(IDS_SAVE_WILL_BE_LOST); + CString str2 = winResLoadString(IDS_CONFIRM_ACTION); + + if(MessageBox(str1, + str2, + MB_OKCANCEL) == IDCANCEL) + return; + + if(theApp.cartridgeType == IMAGE_GB && dlg.getFilterIndex() == 1) + gbReadGSASnapshot(dlg.GetPathName()); + else if(theApp.cartridgeType == IMAGE_GB && dlg.getFilterIndex() == 2) { + /* gameboy .gsv saves? */ } + else if(theApp.cartridgeType == IMAGE_GBA && dlg.getFilterIndex() == 2) + CPUReadGSASPSnapshot(dlg.GetPathName()); + else + CPUReadGSASnapshot(dlg.GetPathName()); +} + +void MainWnd::OnUpdateFileImportGamesharksnapshot(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(emulating); +} + +void MainWnd::OnFileExportBatteryfile() +{ + CString name; + + int index = theApp.filename.ReverseFind('\\'); + + if(index != -1) + name = theApp.filename.Right(theApp.filename.GetLength()-index-1); + else + name = theApp.filename; + + LPCTSTR exts[] = {".sav", ".dat" }; + + CString filter = winLoadFilter(IDS_FILTER_SAV); + CString title = winResLoadString(IDS_SELECT_BATTERY_FILE); + + CString saveDir = regQueryStringValue("batteryDir", NULL); + if( saveDir[0] == '.' ) { + // handle as relative path + char baseDir[MAX_PATH+1]; + GetModuleFileName( NULL, baseDir, MAX_PATH ); + baseDir[MAX_PATH] = '\0'; // for security reasons + PathRemoveFileSpec( baseDir ); // removes the trailing file name and backslash + strcat( baseDir, "\\" ); + strcat( baseDir, saveDir ); + saveDir = baseDir; + } + + if(saveDir.IsEmpty()) + saveDir = getDirFromFile(theApp.filename); + + FileDlg dlg(this, + name, + filter, + 1, + "SAV", + exts, + saveDir, + title, + true); + + if(dlg.DoModal() == IDCANCEL) { + return; + } + + bool result = false; + + if(theApp.cartridgeType == 1) { + result = gbWriteBatteryFile(dlg.GetPathName(), false); + } else + result = theApp.emulator.emuWriteBattery(dlg.GetPathName()); + + if(!result) + systemMessage(MSG_ERROR_CREATING_FILE, "Error creating file %s", + dlg.GetPathName()); +} + +void MainWnd::OnUpdateFileExportBatteryfile(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(emulating); +} + +void MainWnd::OnFileExportGamesharksnapshot() +{ + if(eepromInUse) { + systemMessage(IDS_EEPROM_NOT_SUPPORTED, "EEPROM saves cannot be exported"); + return; + } + + CString name; + + int index = theApp.filename.ReverseFind('\\'); + + if(index != -1) + name = theApp.filename.Right(theApp.filename.GetLength()-index-1); + else + name = theApp.filename; + + LPCTSTR exts[] = {".sps" }; + + CString filter = winLoadFilter(IDS_FILTER_SPS); + CString title = winResLoadString(IDS_SELECT_SNAPSHOT_FILE); + + FileDlg dlg(this, + name, + filter, + 1, + "SPS", + exts, + "", + title, + true); + + if(dlg.DoModal() == IDCANCEL) + return; + + char buffer[16]; + strncpy(buffer, (const char *)&rom[0xa0], 12); + buffer[12] = 0; + + ExportGSASnapshot dlg2(dlg.GetPathName(), buffer, this); + dlg2.DoModal(); +} + +void MainWnd::OnUpdateFileExportGamesharksnapshot(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(emulating && theApp.cartridgeType == 0); +} + +void MainWnd::OnFileScreencapture() +{ + CString name; + CString filename; + + int index = theApp.filename.ReverseFind('\\'); + + if(index != -1) + name = theApp.filename.Right(theApp.filename.GetLength()-index-1); + else + name = theApp.filename; + + CString capdir = regQueryStringValue("captureDir", ""); + if( capdir[0] == '.' ) { + // handle as relative path + char baseDir[MAX_PATH+1]; + GetModuleFileName( NULL, baseDir, MAX_PATH ); + baseDir[MAX_PATH] = '\0'; // for security reasons + PathRemoveFileSpec( baseDir ); // removes the trailing file name and backslash + strcat( baseDir, "\\" ); + strcat( baseDir, capdir ); + capdir = baseDir; + } + + if(capdir.IsEmpty()) + capdir = getDirFromFile(theApp.filename); + + CString ext = "png"; + + if(theApp.captureFormat != 0) + ext = "bmp"; + + if(isDriveRoot(capdir)) + filename.Format("%s%s.%s", capdir, name, ext); + else + filename.Format("%s\\%s.%s", capdir, name, ext); + + LPCTSTR exts[] = {".png", ".bmp" }; + + CString filter = winLoadFilter(IDS_FILTER_PNG); + CString title = winResLoadString(IDS_SELECT_CAPTURE_NAME); + + FileDlg dlg(this, + filename, + filter, + theApp.captureFormat ? 2 : 1, + theApp.captureFormat ? "BMP" : "PNG", + exts, + capdir, + title, + true); + + if(dlg.DoModal() == IDCANCEL) + return; + + if(dlg.getFilterIndex() == 2) + theApp.emulator.emuWriteBMP(dlg.GetPathName()); + else + theApp.emulator.emuWritePNG(dlg.GetPathName()); + + systemScreenMessage(winResLoadString(IDS_SCREEN_CAPTURE)); +} + +void MainWnd::OnUpdateFileScreencapture(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(emulating); +} + +void MainWnd::OnFileRominformation() +{ + if(theApp.cartridgeType == 0) { + RomInfoGBA dlg(rom); + dlg.DoModal(); + } else { + RomInfoGB dlg(gbRom); + dlg.DoModal(); + } +} + +void MainWnd::OnUpdateFileRominformation(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(emulating); +} + +//OnFileToggleFullscreen +void MainWnd::OnFileTogglemenu() +{ + if( theApp.videoOption <= VIDEO_6X ) { + // switch to full screen + toolsLoggingClose(); // close log dialog + theApp.updateWindowSize( theApp.lastFullscreen ); + } else { + // switch to windowed mode + theApp.updateWindowSize( theApp.lastWindowed ); + } +} + +void MainWnd::OnUpdateFileTogglemenu(CCmdUI* pCmdUI) +{ + // HACK: when uncommented, Esc key will not be send to MainWnd + //pCmdUI->Enable(theApp.videoOption > VIDEO_6X); +} + +bool MainWnd::fileImportGSACodeFile(CString& fileName) +{ + FILE *f = fopen(fileName, "rb"); + + if(f == NULL) { + systemMessage(MSG_CANNOT_OPEN_FILE, "Cannot open file %s", fileName); + return false; + } + + u32 len; + fread(&len, 1, 4, f); + if(len != 14) { + fclose(f); + systemMessage(MSG_UNSUPPORTED_CODE_FILE, "Unsupported code file %s", + fileName); + return false; + } + char buffer[16]; + fread(buffer, 1, 14, f); + buffer[14] = 0; + if(memcmp(buffer, "SharkPortCODES", 14)) { + fclose(f); + systemMessage(MSG_UNSUPPORTED_CODE_FILE, "Unsupported code file %s", + fileName); + return false; + } + fseek(f, 0x1e, SEEK_SET); + fread(&len, 1, 4, f); + INT_PTR game = 0; + if(len > 1) { + GSACodeSelect dlg(f); + game = dlg.DoModal(); + } + fclose(f); + + bool v3 = false; + + int index = fileName.ReverseFind('.'); + + if(index != -1) { + if(fileName.Right(3).CompareNoCase("XPC") == 0) + v3 = true; + } + + if(game != -1) { + return cheatsImportGSACodeFile(fileName, (int)game, v3); + } + + return true; +} + +void MainWnd::OnFileSavegameOldestslot() +{ + if(!emulating) + return; + + CString filename; + + int index = theApp.filename.ReverseFind('\\'); + + if(index != -1) + filename = theApp.filename.Right(theApp.filename.GetLength()-index-1); + else + filename = theApp.filename; + + CString saveDir = regQueryStringValue("saveDir", NULL); + if( saveDir[0] == '.' ) { + // handle as relative path + char baseDir[MAX_PATH+1]; + GetModuleFileName( NULL, baseDir, MAX_PATH ); + baseDir[MAX_PATH] = '\0'; // for security reasons + PathRemoveFileSpec( baseDir ); // removes the trailing file name and backslash + strcat( baseDir, "\\" ); + strcat( baseDir, saveDir ); + saveDir = baseDir; + } + + if(saveDir.IsEmpty()) + saveDir = getDirFromFile(theApp.filename); + + if(!isDriveRoot(saveDir)) + saveDir += "\\"; + + CString name; + CFileStatus status; + CString str; + time_t time = 0; + int found = 0; + + for(int i = 0; i < 10; i++) { + name.Format("%s%s%d.sgm", saveDir, filename, i+1); + + if(emulating && CFile::GetStatus(name, status)) { + if( (status.m_mtime.GetTime() < time) || !time ) { + time = status.m_mtime.GetTime(); + found = i; + } + } else { + found = i; + break; + } + } + OnFileSaveSlot(ID_FILE_SAVEGAME_SLOT1+found); +} + +void MainWnd::OnUpdateFileSavegameOldestslot(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(emulating); + if(pCmdUI->m_pSubMenu != NULL) { + CMenu *pMenu = pCmdUI->m_pSubMenu; + CString filename; + + int index = theApp.filename.ReverseFind('\\'); + + if(index != -1) + filename = theApp.filename.Right(theApp.filename.GetLength()-index-1); + else + filename = theApp.filename; + + CString saveDir = regQueryStringValue("saveDir", NULL); + if( saveDir[0] == '.' ) { + // handle as relative path + char baseDir[MAX_PATH+1]; + GetModuleFileName( NULL, baseDir, MAX_PATH ); + baseDir[MAX_PATH] = '\0'; // for security reasons + PathRemoveFileSpec( baseDir ); // removes the trailing file name and backslash + strcat( baseDir, "\\" ); + strcat( baseDir, saveDir ); + saveDir = baseDir; + } + + if(saveDir.IsEmpty()) + saveDir = getDirFromFile(theApp.filename); + + if(!isDriveRoot(saveDir)) + saveDir += "\\"; + + CString name; + CFileStatus status; + CString str; + + for(int i = 0; i < 10; i++) { + name.Format("%s%s%d.sgm", saveDir, filename, i+1); + + if(emulating && CFile::GetStatus(name, status)) { + CString timestamp = status.m_mtime.Format("%Y/%m/%d %H:%M:%S"); + str.Format("%d %s", i+1, timestamp); + } else { + str.Format("%d ----/--/-- --:--:--", i+1); + } + pMenu->ModifyMenu(ID_FILE_SAVEGAME_SLOT1+i, MF_STRING|MF_BYCOMMAND, ID_FILE_SAVEGAME_SLOT1+i, str); + } + + theApp.winAccelMgr.UpdateMenu(pMenu->GetSafeHmenu()); + } +} + +void MainWnd::OnFileLoadgameMostrecent() +{ + if(!emulating) + return; + + CString filename; + + int index = theApp.filename.ReverseFind('\\'); + + if(index != -1) + filename = theApp.filename.Right(theApp.filename.GetLength()-index-1); + else + filename = theApp.filename; + + CString saveDir = regQueryStringValue("saveDir", NULL); + if( saveDir[0] == '.' ) { + // handle as relative path + char baseDir[MAX_PATH+1]; + GetModuleFileName( NULL, baseDir, MAX_PATH ); + baseDir[MAX_PATH] = '\0'; // for security reasons + PathRemoveFileSpec( baseDir ); // removes the trailing file name and backslash + strcat( baseDir, "\\" ); + strcat( baseDir, saveDir ); + saveDir = baseDir; + } + + if(saveDir.IsEmpty()) + saveDir = getDirFromFile(theApp.filename); + + if(!isDriveRoot(saveDir)) + saveDir += "\\"; + + CString name; + CFileStatus status; + CString str; + time_t time = 0; + int found = -1; + + for(int i = 0; i < 10; i++) { + name.Format("%s%s%d.sgm", saveDir, filename, i+1); + + if(emulating && CFile::GetStatus(name, status)) { +if(status.m_mtime.GetTime() > time) { + time = status.m_mtime.GetTime(); + found = i; + } + } + } + if(found != -1) { + OnFileLoadSlot(ID_FILE_LOADGAME_SLOT1+found); + } +} + +void MainWnd::OnUpdateFileLoadgameMostrecent(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(emulating); + + if(pCmdUI->m_pSubMenu != NULL) { + CMenu *pMenu = pCmdUI->m_pSubMenu; + CString filename; + + int index = theApp.filename.ReverseFind('\\'); + + if(index != -1) + filename = theApp.filename.Right(theApp.filename.GetLength()-index-1); + else + filename = theApp.filename; + + CString saveDir = regQueryStringValue("saveDir", NULL); + if( saveDir[0] == '.' ) { + // handle as relative path + char baseDir[MAX_PATH+1]; + GetModuleFileName( NULL, baseDir, MAX_PATH ); + baseDir[MAX_PATH] = '\0'; // for security reasons + PathRemoveFileSpec( baseDir ); // removes the trailing file name and backslash + strcat( baseDir, "\\" ); + strcat( baseDir, saveDir ); + saveDir = baseDir; + } + + if(saveDir.IsEmpty()) + saveDir = getDirFromFile(theApp.filename); + + if(!isDriveRoot(saveDir)) + saveDir += "\\"; + + CString name; + CFileStatus status; + CString str; + + for(int i = 0; i < 10; i++) { + name.Format("%s%s%d.sgm", saveDir, filename, i+1); + + if(emulating && CFile::GetStatus(name, status)) { + CString timestamp = status.m_mtime.Format("%Y/%m/%d %H:%M:%S"); + str.Format("%d %s", i+1, timestamp); + } else { + str.Format("%d ----/--/-- --:--:--", i+1); + } + pMenu->ModifyMenu(ID_FILE_LOADGAME_SLOT1+i, MF_STRING|MF_BYCOMMAND, ID_FILE_LOADGAME_SLOT1+i, str); + } + + theApp.winAccelMgr.UpdateMenu(pMenu->GetSafeHmenu()); + } +} + +void MainWnd::OnUpdateFileLoadGameSlot(CCmdUI *pCmdUI) +{ + pCmdUI->Enable(emulating); +} + +void MainWnd::OnUpdateFileSaveGameSlot(CCmdUI *pCmdUI) +{ + pCmdUI->Enable(emulating); +} + +void MainWnd::OnFileLoadgameAutoloadmostrecent() +{ + theApp.autoLoadMostRecent = !theApp.autoLoadMostRecent; +} + +void MainWnd::OnUpdateFileLoadgameAutoloadmostrecent(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.autoLoadMostRecent); +} + +void MainWnd::OnLoadgameDonotchangebatterysave() +{ + skipSaveGameBattery = !skipSaveGameBattery; +} + +void MainWnd::OnUpdateLoadgameDonotchangebatterysave(CCmdUI *pCmdUI) +{ + pCmdUI->SetCheck(skipSaveGameBattery); +} + +void MainWnd::OnLoadgameDonotchangecheatlist() +{ + skipSaveGameCheats = !skipSaveGameCheats; +} + +void MainWnd::OnUpdateLoadgameDonotchangecheatlist(CCmdUI *pCmdUI) +{ + pCmdUI->SetCheck(skipSaveGameCheats); +} diff --git a/src/win32/MainWndHelp.cpp b/src/win32/MainWndHelp.cpp new file mode 100644 index 0000000..b97596f --- /dev/null +++ b/src/win32/MainWndHelp.cpp @@ -0,0 +1,31 @@ +#include "stdafx.h" +#include "MainWnd.h" + +#include "AboutDialog.h" + +extern int emulating; + +void MainWnd::OnHelpAbout() +{ + AboutDialog dlg; + + dlg.DoModal(); +} + +void MainWnd::OnHelpFaq() +{ + ::ShellExecute(0, _T("open"), "http://vba-m.com/forum/", + 0, 0, SW_SHOWNORMAL); +} + +void MainWnd::OnHelpBugreport() +{ + ::ShellExecute(0, _T("open"), "http://sourceforge.net/tracker/?group_id=212795&atid=1023154", + 0, 0, SW_SHOWNORMAL); +} + +void MainWnd::OnHelpGnupubliclicense() +{ + ::ShellExecute(0, _T("open"), "http://www.gnu.org/licenses/old-licenses/gpl-2.0.html", + 0, 0, SW_SHOWNORMAL); +} diff --git a/src/win32/MainWndOptions.cpp b/src/win32/MainWndOptions.cpp new file mode 100644 index 0000000..ddb87c5 --- /dev/null +++ b/src/win32/MainWndOptions.cpp @@ -0,0 +1,1806 @@ +#include "stdafx.h" +#include "VBA.h" +#include "MainWnd.h" + +#include "Associate.h" +#include "Directories.h" +#include "FileDlg.h" +#include "GameOverrides.h" +#include "LinkOptions.h" +#include "JoybusOptions.h" +#include "GBColorDlg.h" +#include "Joypad.h" +#include "MaxScale.h" +#include "Reg.h" +#include "RewindInterval.h" +#include "Throttle.h" +#include "WinResUtil.h" +#include "SelectPlugin.h" +#include "OALConfig.h" +#include "XAudio2_Config.h" +#include "BIOSDialog.h" +#include "AudioCoreSettingsDlg.h" + +#include "../System.h" +#include "../gba/agbprint.h" +#include "../gba/GBA.h" +#include "../gba/Globals.h" +#include "../gba/Sound.h" +#include "../gb/GB.h" +#include "../gb/gbGlobals.h" +#include "../gb/gbPrinter.h" +#include "../gb/gbSound.h" +#include "../gba/GBALink.h" + +#include "../version.h" + +#include + + +void MainWnd::OnOptionsFrameskipThrottleNothrottle() +{ + theApp.updateThrottle( 0 ); // disable + theApp.autoFrameSkip = false; +} + +void MainWnd::OnUpdateOptionsFrameskipThrottleNothrottle(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck( theApp.throttle == 0 ); +} + + +void MainWnd::OnOptionsFrameskipThrottle25() +{ + theApp.updateThrottle( 25 ); + theApp.autoFrameSkip = false; +} + +void MainWnd::OnUpdateOptionsFrameskipThrottle25(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck( theApp.throttle == 25 ); +} + + +void MainWnd::OnOptionsFrameskipThrottle50() +{ + theApp.updateThrottle( 50 ); + theApp.autoFrameSkip = false; +} + +void MainWnd::OnUpdateOptionsFrameskipThrottle50(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck( theApp.throttle == 50 ); +} + + +void MainWnd::OnOptionsFrameskipThrottle100() +{ + theApp.updateThrottle( 100 ); + theApp.autoFrameSkip = false; +} + +void MainWnd::OnUpdateOptionsFrameskipThrottle100(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck( theApp.throttle == 100 ); +} + + +void MainWnd::OnOptionsFrameskipThrottle150() +{ + theApp.updateThrottle( 150 ); +theApp.autoFrameSkip = false; +} + +void MainWnd::OnUpdateOptionsFrameskipThrottle150(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck( theApp.throttle == 150 ); +} + + +void MainWnd::OnOptionsFrameskipThrottle200() +{ + theApp.updateThrottle( 200 ); + theApp.autoFrameSkip = false; +} + +void MainWnd::OnUpdateOptionsFrameskipThrottle200(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck( theApp.throttle == 200 ); +} + + +void MainWnd::OnOptionsFrameskipThrottleOther() +{ + Throttle dlg; + unsigned short v = (unsigned short)dlg.DoModal(); + + if( v ) { + theApp.updateThrottle( v ); + theApp.autoFrameSkip = false; + } +} + + +void MainWnd::OnUpdateOptionsFrameskipThrottleOther(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck( + ( theApp.throttle != 0 ) && + ( theApp.throttle != 25 ) && + ( theApp.throttle != 50 ) && + ( theApp.throttle != 100 ) && + ( theApp.throttle != 150 ) && + ( theApp.throttle != 200 ) ); +} + +void MainWnd::OnOptionsFrameskipAutomatic() +{ + theApp.autoFrameSkip = !theApp.autoFrameSkip; + if(!theApp.autoFrameSkip && emulating) + theApp.updateFrameSkip(); + else + { + theApp.throttle = false; + frameSkip = 0; + systemFrameSkip = 0; + } +} + +void MainWnd::OnUpdateOptionsFrameskipAutomatic(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.autoFrameSkip); +} + +BOOL MainWnd::OnOptionsFrameskip(UINT nID) +{ + switch(nID) { + case ID_OPTIONS_VIDEO_FRAMESKIP_0: + case ID_OPTIONS_VIDEO_FRAMESKIP_1: + case ID_OPTIONS_VIDEO_FRAMESKIP_2: + case ID_OPTIONS_VIDEO_FRAMESKIP_3: + case ID_OPTIONS_VIDEO_FRAMESKIP_4: + case ID_OPTIONS_VIDEO_FRAMESKIP_5: + if(theApp.cartridgeType == IMAGE_GBA) { + frameSkip = nID - ID_OPTIONS_VIDEO_FRAMESKIP_0; + } else { + gbFrameSkip = nID - ID_OPTIONS_VIDEO_FRAMESKIP_0; + } + if(emulating) + theApp.updateFrameSkip(); + theApp.updateThrottle( 0 ); + return TRUE; + break; + case ID_OPTIONS_VIDEO_FRAMESKIP_6: + case ID_OPTIONS_VIDEO_FRAMESKIP_7: + case ID_OPTIONS_VIDEO_FRAMESKIP_8: + case ID_OPTIONS_VIDEO_FRAMESKIP_9: + if(theApp.cartridgeType == IMAGE_GBA) { + frameSkip = 6 + nID - ID_OPTIONS_VIDEO_FRAMESKIP_6; + } else { + gbFrameSkip = 6 + nID - ID_OPTIONS_VIDEO_FRAMESKIP_6; + } + if(emulating) + theApp.updateFrameSkip(); + theApp.updateThrottle( 0 ); + return TRUE; + break; + } + return FALSE; +} + +void MainWnd::OnUpdateOptionsVideoFrameskip0(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.cartridgeType == IMAGE_GBA ? frameSkip == 0 : gbFrameSkip == 0); +} + +void MainWnd::OnUpdateOptionsVideoFrameskip1(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.cartridgeType == IMAGE_GBA ? frameSkip == 1 : gbFrameSkip == 1); +} + +void MainWnd::OnUpdateOptionsVideoFrameskip2(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.cartridgeType == IMAGE_GBA ? frameSkip == 2 : gbFrameSkip == 2); +} + +void MainWnd::OnUpdateOptionsVideoFrameskip3(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.cartridgeType == IMAGE_GBA ? frameSkip == 3 : gbFrameSkip == 3); +} + +void MainWnd::OnUpdateOptionsVideoFrameskip4(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.cartridgeType == IMAGE_GBA ? frameSkip == 4 : gbFrameSkip == 4); +} + +void MainWnd::OnUpdateOptionsVideoFrameskip5(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.cartridgeType == IMAGE_GBA ? frameSkip == 5 : gbFrameSkip == 5); +} + +void MainWnd::OnUpdateOptionsVideoFrameskip6(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.cartridgeType == IMAGE_GBA ? frameSkip == 6 : gbFrameSkip == 6); +} + +void MainWnd::OnUpdateOptionsVideoFrameskip7(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.cartridgeType == IMAGE_GBA ? frameSkip == 7 : gbFrameSkip == 7); +} + +void MainWnd::OnUpdateOptionsVideoFrameskip8(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.cartridgeType == IMAGE_GBA ? frameSkip == 8 : gbFrameSkip == 8); +} + +void MainWnd::OnUpdateOptionsVideoFrameskip9(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.cartridgeType == IMAGE_GBA ? frameSkip == 9 : gbFrameSkip == 9); +} + + +void MainWnd::OnOptionsVideoVsync() +{ + theApp.vsync = !theApp.vsync; + if( theApp.display ) { + theApp.display->setOption( _T("vsync"), theApp.vsync ); + } +} + + +void MainWnd::OnUpdateOptionsVideoVsync(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.vsync); +} + +void MainWnd::OnUpdateOptionsVideoX1(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.videoOption == VIDEO_1X); +} + +void MainWnd::OnUpdateOptionsVideoX2(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.videoOption == VIDEO_2X); +} + +void MainWnd::OnUpdateOptionsVideoX3(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.videoOption == VIDEO_3X); +} + +void MainWnd::OnUpdateOptionsVideoX4(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.videoOption == VIDEO_4X); +} + +void MainWnd::OnUpdateOptionsVideoX5(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.videoOption == VIDEO_5X); +} + +void MainWnd::OnUpdateOptionsVideoX6(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.videoOption == VIDEO_6X); +} + +BOOL MainWnd::OnOptionVideoSize(UINT nID) +{ + theApp.updateVideoSize(nID); + return TRUE; +} + +void MainWnd::OnOptionsVideoFullscreen() +{ + IDisplay::VIDEO_MODE mode; + ZeroMemory( &mode, sizeof(IDisplay::VIDEO_MODE) ); + + if( theApp.display->selectFullScreenMode( mode ) ) { + if( ( mode.width != theApp.fsWidth ) || + ( mode.height != theApp.fsHeight ) || + ( mode.bitDepth != theApp.fsColorDepth ) || + ( mode.frequency != theApp.fsFrequency ) || + ( mode.adapter != theApp.fsAdapter ) || + ( theApp.videoOption != VIDEO_OTHER ) ) + { + theApp.fsForceChange = true; + theApp.fsWidth = mode.width; + theApp.fsHeight = mode.height; + theApp.fsFrequency = mode.frequency; + theApp.fsColorDepth = mode.bitDepth; + theApp.fsAdapter = mode.adapter; + theApp.updateVideoSize( ID_OPTIONS_VIDEO_FULLSCREEN ); + } + } + theApp.winAccelMgr.UpdateMenu(theApp.menu); +} + + +void MainWnd::OnUpdateOptionsVideoFullscreen(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.videoOption == VIDEO_OTHER); +} + +void MainWnd::OnOptionsVideoDisablesfx() +{ + cpuDisableSfx = !cpuDisableSfx; + if(emulating && theApp.cartridgeType == IMAGE_GBA) + CPUUpdateRender(); +} + +void MainWnd::OnUpdateOptionsVideoDisablesfx(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(cpuDisableSfx); +} + + +void MainWnd::OnOptionsVideoFullscreenstretchtofit() +{ + theApp.fullScreenStretch = !theApp.fullScreenStretch; + if( theApp.display ) { + theApp.display->setOption( _T("fullScreenStretch"), theApp.fullScreenStretch ); + } +} + + +void MainWnd::OnUpdateOptionsVideoFullscreenstretchtofit(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.fullScreenStretch); +} + +BOOL MainWnd::OnVideoLayer(UINT nID) +{ + layerSettings ^= 0x0100 << + ((nID & 0xFFFF) - ID_OPTIONS_VIDEO_LAYERS_BG0); + layerEnable = DISPCNT & layerSettings; + CPUUpdateRenderBuffers(false); + + // inform the user about the change + CString msg; + int thisLayer = -1; + switch( nID ) { + case ID_OPTIONS_VIDEO_LAYERS_BG0: + msg = "BG0"; + thisLayer = 256; + break; + case ID_OPTIONS_VIDEO_LAYERS_BG1: + msg = "BG1"; + thisLayer = 512; + break; + case ID_OPTIONS_VIDEO_LAYERS_BG2: + msg = "BG2"; + thisLayer = 1024; + break; + case ID_OPTIONS_VIDEO_LAYERS_BG3: + msg = "BG3"; + thisLayer = 2048; + break; + case ID_OPTIONS_VIDEO_LAYERS_OBJ: + msg = "OBJ"; + thisLayer = 4096; + break; + case ID_OPTIONS_VIDEO_LAYERS_WIN0: + msg = "WIN0"; + thisLayer = 8192; + break; + case ID_OPTIONS_VIDEO_LAYERS_WIN1: + msg = "WIN1"; + thisLayer = 16384; + break; + case ID_OPTIONS_VIDEO_LAYERS_OBJWIN: + msg = "OBJWIN"; + thisLayer = 32768; + break; + default: + ASSERT( false ); + } + if( layerSettings & thisLayer ) { + msg.Append( " enabled" ); + } else { + msg.Append( " disabled" ); + } + systemScreenMessage( msg ); + + return TRUE; +} + +void MainWnd::OnVideoLayerReset() +{ + layerSettings = 0xFF00; + layerEnable = DISPCNT & layerSettings; + CPUUpdateRenderBuffers(false); + systemScreenMessage( "All layers enabled" ); +} + +void MainWnd::OnUpdateVideoLayer(CCmdUI *pCmdUI) +{ + pCmdUI->SetCheck((layerSettings >> (8 + pCmdUI->m_nID - ID_OPTIONS_VIDEO_LAYERS_BG0)) & 1); + switch(pCmdUI->m_nID) { + case ID_OPTIONS_VIDEO_LAYERS_BG1: + case ID_OPTIONS_VIDEO_LAYERS_BG2: + case ID_OPTIONS_VIDEO_LAYERS_BG3: + case ID_OPTIONS_VIDEO_LAYERS_WIN1: + case ID_OPTIONS_VIDEO_LAYERS_OBJWIN: + pCmdUI->Enable(theApp.cartridgeType == IMAGE_GBA); + break; + } +} + + +void MainWnd::OnOptionsVideoRendermethodDirect3d() +{ +#ifndef NO_D3D + theApp.renderMethod = DIRECT_3D; + theApp.updateRenderMethod(false); + theApp.winAccelMgr.UpdateMenu(theApp.menu); +#endif +} + +void MainWnd::OnUpdateOptionsVideoRendermethodDirect3d(CCmdUI* pCmdUI) +{ +#ifndef NO_D3D + pCmdUI->SetCheck(theApp.renderMethod == DIRECT_3D); +#else + pCmdUI->Enable( FALSE ); +#endif +} + +void MainWnd::OnOptionsVideoRendermethodOpengl() +{ +#ifndef NO_OGL + theApp.renderMethod = OPENGL; + theApp.updateRenderMethod(false); + theApp.winAccelMgr.UpdateMenu(theApp.menu); +#endif +} + +void MainWnd::OnUpdateOptionsVideoRendermethodOpengl(CCmdUI* pCmdUI) +{ +#ifndef NO_OGL + pCmdUI->SetCheck(theApp.renderMethod == OPENGL); +#else + pCmdUI->Enable( FALSE ); +#endif +} + + +void MainWnd::OnOptionsVideoTriplebuffering() +{ + theApp.tripleBuffering = !theApp.tripleBuffering; + if( theApp.display ) { + theApp.display->setOption( _T("tripleBuffering"), theApp.tripleBuffering ); + } +} + + +void MainWnd::OnUpdateOptionsVideoTriplebuffering(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.tripleBuffering); +} + + +void MainWnd::OnOptionsVideoRenderoptionsD3dnofilter() +{ +#ifndef NO_D3D + theApp.d3dFilter = 0; + if( theApp.display ) { + theApp.display->setOption( _T("d3dFilter"), theApp.d3dFilter ); + } +#endif +} + +void MainWnd::OnUpdateOptionsVideoRenderoptionsD3dnofilter(CCmdUI* pCmdUI) +{ +#ifndef NO_D3D + pCmdUI->SetCheck(theApp.d3dFilter == 0); +#else + pCmdUI->Enable( FALSE ); +#endif +} + + +void MainWnd::OnOptionsVideoRenderoptionsD3dbilinear() +{ +#ifndef NO_D3D + theApp.d3dFilter = 1; + if( theApp.display ) { + theApp.display->setOption( _T("d3dFilter"), theApp.d3dFilter ); + } +#endif +} + + +void MainWnd::OnUpdateOptionsVideoRenderoptionsD3dbilinear(CCmdUI* pCmdUI) +{ +#ifndef NO_D3D + pCmdUI->SetCheck(theApp.d3dFilter == 1); +#else + pCmdUI->Enable( FALSE ); +#endif +} + + +void MainWnd::OnOptionsVideoRenderoptionsGlnearest() +{ +#ifndef NO_OGL + theApp.glFilter = 0; + if( theApp.display ) { + theApp.display->setOption( _T("glFilter"), theApp.glFilter ); + } +#endif +} + + +void MainWnd::OnUpdateOptionsVideoRenderoptionsGlnearest(CCmdUI* pCmdUI) +{ +#ifndef NO_OGL + pCmdUI->SetCheck(theApp.glFilter == 0); +#else + pCmdUI->Enable( FALSE ); +#endif +} + + +void MainWnd::OnOptionsVideoRenderoptionsGlbilinear() +{ +#ifndef NO_OGL + theApp.glFilter = 1; + if( theApp.display ) { + theApp.display->setOption( _T("glFilter"), theApp.glFilter ); + } +#endif +} + + +void MainWnd::OnUpdateOptionsVideoRenderoptionsGlbilinear(CCmdUI* pCmdUI) +{ +#ifndef NO_OGL + pCmdUI->SetCheck(theApp.glFilter == 1); +#else + pCmdUI->Enable( FALSE ); +#endif +} + + +void MainWnd::OnOptionsEmulatorAssociate() +{ + Associate dlg; + dlg.DoModal(); +} + +void MainWnd::OnOptionsEmulatorDirectories() +{ + Directories dlg; + dlg.DoModal(); +} + +void MainWnd::OnOptionsEmulatorDisablestatusmessages() +{ + theApp.disableStatusMessage = !theApp.disableStatusMessage; +} + +void MainWnd::OnUpdateOptionsEmulatorDisablestatusmessages(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.disableStatusMessage); +} + +void MainWnd::OnOptionsEmulatorSynchronize() +{ + synchronize = !synchronize; + if (synchronize) + theApp.throttle = false; +} + +void MainWnd::OnUpdateOptionsEmulatorSynchronize(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(synchronize); +} + +void MainWnd::OnOptionsEmulatorPausewheninactive() +{ + theApp.pauseWhenInactive = !theApp.pauseWhenInactive; +} + +void MainWnd::OnUpdateOptionsEmulatorPausewheninactive(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.pauseWhenInactive); +} + +void MainWnd::OnOptionsEmulatorSpeeduptoggle() +{ + theApp.speedupToggle = !theApp.speedupToggle; +} + +void MainWnd::OnUpdateOptionsEmulatorSpeeduptoggle(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.speedupToggle); +} + +void MainWnd::OnOptionsEmulatorAutomaticallyApplyPatchFiles() +{ + theApp.autoPatch = !theApp.autoPatch; +} + +void MainWnd::OnUpdateOptionsEmulatorAutomaticallyipspatch(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.autoPatch); +} + +void MainWnd::OnOptionsEmulatorAgbprint() +{ + agbPrintEnable(!agbPrintIsEnabled()); +} + +void MainWnd::OnUpdateOptionsEmulatorAgbprint(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(agbPrintIsEnabled()); +} + +void MainWnd::OnOptionsEmulatorRealtimeclock() +{ + theApp.winRtcEnable = !theApp.winRtcEnable; +} + +void MainWnd::OnUpdateOptionsEmulatorRealtimeclock(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.winRtcEnable); +} + +void MainWnd::OnOptionsEmulatorRewindinterval() +{ + RewindInterval dlg(theApp.rewindTimer/6); + int v = (int)dlg.DoModal(); + + if( v >= 0 ) { + theApp.rewindTimer = v*6; // convert to a multiple of 10 frames + regSetDwordValue("rewindTimer", v); + if(v == 0) { + if(theApp.rewindMemory) { + free(theApp.rewindMemory); + theApp.rewindMemory = NULL; + } + theApp.rewindCount = 0; + theApp.rewindCounter = 0; + theApp.rewindSaveNeeded = false; + } else { + if(theApp.rewindMemory == NULL) { + theApp.rewindMemory = (char *)malloc(8*REWIND_SIZE); + } + } + } +} + + +BOOL MainWnd::OnOptionsEmulatorShowSpeed(UINT nID) +{ + switch(nID) { + case ID_OPTIONS_EMULATOR_SHOWSPEED_NONE: + theApp.showSpeed = 0; + systemSetTitle(VBA_NAME_AND_SUBVERSION); + break; + case ID_OPTIONS_EMULATOR_SHOWSPEED_PERCENTAGE: + theApp.showSpeed = 1; + break; + case ID_OPTIONS_EMULATOR_SHOWSPEED_DETAILED: + theApp.showSpeed = 2; + break; + case ID_OPTIONS_EMULATOR_SHOWSPEED_TRANSPARENT: + theApp.showSpeedTransparent = !theApp.showSpeedTransparent; + break; + default: + return FALSE; + } + return TRUE; +} + +void MainWnd::OnUpdateOptionsEmulatorShowSpeed(CCmdUI *pCmdUI) +{ + switch(pCmdUI->m_nID) { + case ID_OPTIONS_EMULATOR_SHOWSPEED_NONE: + pCmdUI->SetCheck(theApp.showSpeed == 0); + break; + case ID_OPTIONS_EMULATOR_SHOWSPEED_PERCENTAGE: + pCmdUI->SetCheck(theApp.showSpeed == 1); + break; + case ID_OPTIONS_EMULATOR_SHOWSPEED_DETAILED: + pCmdUI->SetCheck(theApp.showSpeed == 2); + break; + case ID_OPTIONS_EMULATOR_SHOWSPEED_TRANSPARENT: + pCmdUI->SetCheck(theApp.showSpeedTransparent); + break; + } +} + +void MainWnd::OnOptionsEmulatorSavetypeAutomatic() +{ + theApp.winSaveType = 0; +} + +void MainWnd::OnUpdateOptionsEmulatorSavetypeAutomatic(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.winSaveType == 0); +} + +void MainWnd::OnOptionsEmulatorSavetypeEeprom() +{ + theApp.winSaveType = 1; +} + +void MainWnd::OnUpdateOptionsEmulatorSavetypeEeprom(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.winSaveType == 1); +} + +void MainWnd::OnOptionsEmulatorSavetypeSram() +{ + theApp.winSaveType = 2; +} + +void MainWnd::OnUpdateOptionsEmulatorSavetypeSram(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.winSaveType == 2); +} + +void MainWnd::OnOptionsEmulatorSavetypeFlash() +{ + theApp.winSaveType = 3; +} + +void MainWnd::OnUpdateOptionsEmulatorSavetypeFlash(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.winSaveType == 3); +} + +void MainWnd::OnOptionsEmulatorSavetypeEepromsensor() +{ + theApp.winSaveType = 4; +} + +void MainWnd::OnUpdateOptionsEmulatorSavetypeEepromsensor(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.winSaveType == 4); +} + +void MainWnd::OnOptionsEmulatorSavetypeNone() +{ + theApp.winSaveType = 5; +} + +void MainWnd::OnUpdateOptionsEmulatorSavetypeNone(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.winSaveType == 5); +} + +void MainWnd::OnOptionsEmulatorSavetypeFlash512k() +{ + flashSetSize(0x10000); + theApp.winFlashSize = 0x10000; +} + +void MainWnd::OnUpdateOptionsEmulatorSavetypeFlash512k(CCmdUI* pCmdUI) +{ + // changed theApp.winFlashSize to flashSize to reflect the actual + // flashsize value used by the emu (it can change upon battery loading) + pCmdUI->SetCheck(flashSize == 0x10000); +} + +void MainWnd::OnOptionsEmulatorSavetypeFlash1m() +{ + flashSetSize(0x20000); + theApp.winFlashSize = 0x20000; +} + +void MainWnd::OnUpdateOptionsEmulatorSavetypeFlash1m(CCmdUI* pCmdUI) +{ + // changed theApp.winFlashSize to flashSize to reflect the actual + // flashsize value used by the emu (it can change upon battery loading) + pCmdUI->SetCheck(flashSize == 0x20000); +} + +void MainWnd::OnOptionsEmulatorSavetypeDetectNow() +{ + if( theApp.cartridgeType != IMAGE_GBA ) return; + const int address_max = theApp.romSize - 10; + char temp[11]; temp[10] = '\0'; + CString answer( _T( "This cartridge has probably no backup media." ) ); + + const u32 EEPR = 'E' | ( 'E' << 8 ) | ( 'P' << 16 ) | ( 'R' << 24 ); + const u32 SRAM = 'S' | ( 'R' << 8 ) | ( 'A' << 16 ) | ( 'M' << 24 ); + const u32 FLAS = 'F' | ( 'L' << 8 ) | ( 'A' << 16 ) | ( 'S' << 24 ); + + for( int address = 0; address < address_max; address += 4 ) { + const u32 check = *((u32*)&rom[address]); + + if( EEPR == check ) { + memcpy( temp, &rom[address], 10 ); + if( 0 == strncmp( temp, "EEPROM_V", 8 ) ) { + answer = _T( "This cartridge uses EEPROM." ); + break; + } + } + + if( SRAM == check ) { + memcpy( temp, &rom[address], 10 ); + if( ( 0 == strncmp( temp, "SRAM_V", 6 ) ) || ( 0 == strncmp( temp, "SRAM_F_V", 8 ) ) ) { + answer = _T( "This cartridge uses SRAM." ); + break; + } + } + + if( FLAS == check ) { + memcpy( temp, &rom[address], 10 ); + if( ( 0 == strncmp( temp, "FLASH_V", 7 ) ) || ( 0 == strncmp( temp, "FLASH512_V", 10 ) ) ) { + answer = _T( "This cartridge uses FLASH (64 KiB)." ); + break; + } + if( 0 == strncmp( temp, "FLASH1M_V", 9 ) ) { + answer = _T( "This cartridge uses FLASH (128 KiB)." ); + break; + } + } + } + + MessageBox( answer ); +} + +void MainWnd::OnUpdateOptionsEmulatorSavetypeDetectNow(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(emulating); +} + +void MainWnd::OnOptionsEmulatorPngformat() +{ + theApp.captureFormat = 0; +} + +void MainWnd::OnUpdateOptionsEmulatorPngformat(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.captureFormat == 0); +} + +void MainWnd::OnOptionsEmulatorBmpformat() +{ + theApp.captureFormat = 1; +} + +void MainWnd::OnUpdateOptionsEmulatorBmpformat(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.captureFormat == 1); +} + +void MainWnd::OnAudioCoreSettings() +{ + AudioCoreSettingsDlg dlg; + + dlg.m_enabled = gb_effects_config.enabled; + dlg.m_surround = gb_effects_config.surround; + dlg.m_echo = gb_effects_config.echo; + dlg.m_stereo = gb_effects_config.stereo; + dlg.m_volume = soundGetVolume(); + dlg.m_declicking = gbSoundGetDeclicking(); + dlg.m_sound_interpolation = soundInterpolation; + dlg.m_sound_filtering = soundFiltering; + dlg.m_sample_rate = soundGetSampleRate(); + + if( IDOK == dlg.DoModal() ) { + gb_effects_config_t _new; + + _new.enabled = dlg.m_enabled; + _new.surround = dlg.m_surround; + _new.echo = dlg.m_echo; + _new.stereo = dlg.m_stereo; + + gbSoundConfigEffects( _new ); + + soundSetVolume( dlg.m_volume ); + + gbSoundSetDeclicking( dlg.m_declicking ); + + soundInterpolation = dlg.m_sound_interpolation; + + soundFiltering = dlg.m_sound_filtering; + + if( theApp.cartridgeType == IMAGE_GBA ) { + soundSetSampleRate( dlg.m_sample_rate ); + } else if( theApp.cartridgeType == IMAGE_GB ) { + gbSoundSetSampleRate( dlg.m_sample_rate ); + } + } +} + +void MainWnd::OnUpdateAudioCoreSettings(CCmdUI *pCmdUI) +{ + pCmdUI->Enable( ( !theApp.aviRecording && !theApp.soundRecording ) ? TRUE : FALSE ); +} + +void MainWnd::updateSoundChannels(UINT id) +{ + int flag = 0; + + if(id == ID_OPTIONS_SOUND_CHANNEL1) + flag = 1; + + if(id == ID_OPTIONS_SOUND_CHANNEL2) + flag = 2; + + if(id == ID_OPTIONS_SOUND_CHANNEL3) + flag = 4; + + if(id == ID_OPTIONS_SOUND_CHANNEL4) + flag = 8; + + if(id == ID_OPTIONS_SOUND_DIRECTSOUNDA) + flag = 256; + + if(id == ID_OPTIONS_SOUND_DIRECTSOUNDB) + flag = 512; + + int active = soundGetEnable() & 0x30f; + + if(active & flag) + active &= (~flag); + else + active |= flag; + + soundSetEnable(active); +} + +void MainWnd::OnOptionsSoundChannel1() +{ + updateSoundChannels(ID_OPTIONS_SOUND_CHANNEL1); +} + +void MainWnd::OnUpdateOptionsSoundChannel1(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(soundGetEnable() & 1); +} + +void MainWnd::OnOptionsSoundChannel2() +{ + updateSoundChannels(ID_OPTIONS_SOUND_CHANNEL2); +} + +void MainWnd::OnUpdateOptionsSoundChannel2(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(soundGetEnable() & 2); +} + +void MainWnd::OnOptionsSoundChannel3() +{ + updateSoundChannels(ID_OPTIONS_SOUND_CHANNEL3); +} + +void MainWnd::OnUpdateOptionsSoundChannel3(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(soundGetEnable() & 4); +} + +void MainWnd::OnOptionsSoundChannel4() +{ + updateSoundChannels(ID_OPTIONS_SOUND_CHANNEL4); +} + +void MainWnd::OnUpdateOptionsSoundChannel4(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(soundGetEnable() & 8); +} + +void MainWnd::OnOptionsSoundDirectsounda() +{ + updateSoundChannels(ID_OPTIONS_SOUND_DIRECTSOUNDA); +} + +void MainWnd::OnUpdateOptionsSoundDirectsounda(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(soundGetEnable() & 256); + pCmdUI->Enable(theApp.cartridgeType == 0); +} + +void MainWnd::OnOptionsSoundDirectsoundb() +{ + updateSoundChannels(ID_OPTIONS_SOUND_DIRECTSOUNDB); +} + +void MainWnd::OnUpdateOptionsSoundDirectsoundb(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(soundGetEnable() & 512); + pCmdUI->Enable(theApp.cartridgeType == 0); +} + +void MainWnd::OnOptionsGameboyBorder() +{ + theApp.winGbBorderOn = !theApp.winGbBorderOn; + gbBorderOn = theApp.winGbBorderOn; + if(emulating && theApp.cartridgeType == 1 && gbBorderOn) { + gbSgbRenderBorder(); + } + theApp.updateWindowSize(theApp.videoOption); +} + +void MainWnd::OnUpdateOptionsGameboyBorder(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.winGbBorderOn); +} + +void MainWnd::OnOptionsGameboyPrinter() +{ + theApp.winGbPrinterEnabled = !theApp.winGbPrinterEnabled; + if(theApp.winGbPrinterEnabled) + gbSerialFunction = gbPrinterSend; + else + gbSerialFunction = NULL; +} + +void MainWnd::OnUpdateOptionsGameboyPrinter(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(gbSerialFunction == gbPrinterSend); +} + +void MainWnd::OnOptionsGameboyBorderAutomatic() +{ + gbBorderAutomatic = !gbBorderAutomatic; + if(emulating && theApp.cartridgeType == 1 && gbBorderOn) { + gbSgbRenderBorder(); + theApp.updateWindowSize(theApp.videoOption); + } +} + +void MainWnd::OnUpdateOptionsGameboyBorderAutomatic(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(gbBorderAutomatic); +} + +void MainWnd::OnOptionsGameboyAutomatic() +{ + gbEmulatorType = 0; +} + +void MainWnd::OnUpdateOptionsGameboyAutomatic(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(gbEmulatorType == 0); +} + +void MainWnd::OnOptionsGameboyGba() +{ + gbEmulatorType = 4; +} + +void MainWnd::OnUpdateOptionsGameboyGba(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(gbEmulatorType == 4); +} + +void MainWnd::OnOptionsGameboyCgb() +{ + gbEmulatorType = 1; +} + +void MainWnd::OnUpdateOptionsGameboyCgb(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(gbEmulatorType == 1); +} + +void MainWnd::OnOptionsGameboySgb() +{ + gbEmulatorType = 2; +} + +void MainWnd::OnUpdateOptionsGameboySgb(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(gbEmulatorType == 2); +} + +void MainWnd::OnOptionsGameboySgb2() +{ + gbEmulatorType = 5; +} + +void MainWnd::OnUpdateOptionsGameboySgb2(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(gbEmulatorType == 5); +} + +void MainWnd::OnOptionsGameboyGb() +{ + gbEmulatorType = 3; +} + +void MainWnd::OnUpdateOptionsGameboyGb(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(gbEmulatorType == 3); +} + +void MainWnd::OnOptionsGameboyRealcolors() +{ + gbColorOption = 0; + utilUpdateSystemColorMaps(theApp.cartridgeType == IMAGE_GBA && gbColorOption == 1); +} + +void MainWnd::OnUpdateOptionsGameboyRealcolors(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(gbColorOption == 0); +} + +void MainWnd::OnOptionsGameboyGameboycolors() +{ + gbColorOption = 1; + utilUpdateSystemColorMaps(theApp.cartridgeType == IMAGE_GBA && gbColorOption == 1); +} + +void MainWnd::OnUpdateOptionsGameboyGameboycolors(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(gbColorOption == 1); +} + + +void MainWnd::OnOptionsGameboyColors() +{ + GBColorDlg dlg; + if(dlg.DoModal()) { + gbPaletteOption = dlg.getWhich(); + memcpy(systemGbPalette, dlg.getColors(), 24*sizeof(u16)); + if(emulating && theApp.cartridgeType == 1) { + memcpy(gbPalette, &systemGbPalette[dlg.getWhich()*8], 8*sizeof(u16)); + } + } +} + +BOOL MainWnd::OnOptionsPriority(UINT nID) +{ + switch(nID) { + case ID_OPTIONS_PRIORITY_HIGHEST: + theApp.threadPriority = 0; + break; + case ID_OPTIONS_PRIORITY_ABOVENORMAL: + theApp.threadPriority = 1; + break; + case ID_OPTIONS_PRIORITY_NORMAL: + theApp.threadPriority = 2; + break; + case ID_OPTIONS_PRIORITY_BELOWNORMAL: + theApp.threadPriority = 3; + break; + default: + return FALSE; + } + theApp.updatePriority(); + + return TRUE; +} + +void MainWnd::OnUpdateOptionsPriority(CCmdUI *pCmdUI) +{ + switch(pCmdUI->m_nID) { + case ID_OPTIONS_PRIORITY_HIGHEST: + pCmdUI->SetCheck(theApp.threadPriority == 0); + break; + case ID_OPTIONS_PRIORITY_ABOVENORMAL: + pCmdUI->SetCheck(theApp.threadPriority == 1); + break; + case ID_OPTIONS_PRIORITY_NORMAL: + pCmdUI->SetCheck(theApp.threadPriority == 2); + break; + case ID_OPTIONS_PRIORITY_BELOWNORMAL: + pCmdUI->SetCheck(theApp.threadPriority == 3); + break; + } +} + +BOOL MainWnd::OnOptionsFilter(UINT nID) +{ + switch(nID) + { + case ID_OPTIONS_FILTER_NORMAL: + theApp.filterType = FILTER_NONE; + break; + case ID_OPTIONS_FILTER_TVMODE: + theApp.filterType = FILTER_TVMODE; + break; + case ID_OPTIONS_FILTER_PLUGIN: + theApp.filterType = FILTER_PLUGIN; + if( strcmp( theApp.pluginName, "" ) == 0 ) { + // open select plugin dialog when none selected + OnOptionsSelectPlugin(); + } + break; + case ID_OPTIONS_FILTER_2XSAI: + theApp.filterType = FILTER_2XSAI; + break; + case ID_OPTIONS_FILTER_SUPER2XSAI: + theApp.filterType = FILTER_SUPER2XSAI; + break; + case ID_OPTIONS_FILTER_SUPEREAGLE: + theApp.filterType = FILTER_SUPEREAGLE; + break; + case ID_OPTIONS_FILTER16BIT_PIXELATEEXPERIMENTAL: + theApp.filterType = FILTER_PIXELATE; + break; + case ID_OPTIONS_FILTER16BIT_ADVANCEMAMESCALE2X: + theApp.filterType = FILTER_MAMESCALE2X; + break; + case ID_OPTIONS_FILTER16BIT_SIMPLE2X: + theApp.filterType = FILTER_SIMPLE2X; + break; + case ID_OPTIONS_FILTER_BILINEAR: + theApp.filterType = FILTER_BILINEAR; + break; + case ID_OPTIONS_FILTER_BILINEARPLUS: + theApp.filterType = FILTER_BILINEARPLUS; + break; + case ID_OPTIONS_FILTER_SCANLINES: + theApp.filterType = FILTER_SCANLINES; + break; + case ID_OPTIONS_FILTER_HQ2X: + theApp.filterType = FILTER_HQ2X; + break; + case ID_OPTIONS_FILTER_LQ2X: + theApp.filterType = FILTER_LQ2X; + break; + case ID_OPTIONS_FILTER_SIMPLE3X: + theApp.filterType = FILTER_SIMPLE3X; + break; + case ID_OPTIONS_FILTER_SIMPLE4X: + theApp.filterType = FILTER_SIMPLE4X; + break; + case ID_OPTIONS_FILTER_HQ3X: + theApp.filterType = FILTER_HQ3X; + break; + case ID_OPTIONS_FILTER_HQ4X: + theApp.filterType = FILTER_HQ4X; + break; + default: + return FALSE; + } + theApp.updateFilter(); + return TRUE; +} + +void MainWnd::OnUpdateOptionsFilter(CCmdUI *pCmdUI) +{ + pCmdUI->Enable( systemColorDepth == 16 || systemColorDepth == 32 ); + + switch(pCmdUI->m_nID) { + case ID_OPTIONS_FILTER_NORMAL: + pCmdUI->SetCheck(theApp.filterType == FILTER_NONE); + break; + case ID_OPTIONS_FILTER_TVMODE: + pCmdUI->SetCheck(theApp.filterType == FILTER_TVMODE); + break; + case ID_OPTIONS_FILTER_2XSAI: + pCmdUI->SetCheck(theApp.filterType == FILTER_2XSAI); + break; + case ID_OPTIONS_FILTER_SUPER2XSAI: + pCmdUI->SetCheck(theApp.filterType == FILTER_SUPER2XSAI); + break; + case ID_OPTIONS_FILTER_PLUGIN: + pCmdUI->Enable( !theApp.filterMT ); + pCmdUI->SetCheck(theApp.filterType == FILTER_PLUGIN); + break; + case ID_OPTIONS_FILTER_SUPEREAGLE: + pCmdUI->SetCheck(theApp.filterType == FILTER_SUPEREAGLE); + break; + case ID_OPTIONS_FILTER16BIT_PIXELATEEXPERIMENTAL: + pCmdUI->SetCheck(theApp.filterType == FILTER_PIXELATE); + break; + case ID_OPTIONS_FILTER16BIT_ADVANCEMAMESCALE2X: + pCmdUI->SetCheck(theApp.filterType == FILTER_MAMESCALE2X); + break; + case ID_OPTIONS_FILTER16BIT_SIMPLE2X: + pCmdUI->SetCheck(theApp.filterType == FILTER_SIMPLE2X); + break; + case ID_OPTIONS_FILTER_BILINEAR: + pCmdUI->SetCheck(theApp.filterType == FILTER_BILINEAR); + break; + case ID_OPTIONS_FILTER_BILINEARPLUS: + pCmdUI->SetCheck(theApp.filterType == FILTER_BILINEARPLUS); + break; + case ID_OPTIONS_FILTER_SCANLINES: + pCmdUI->SetCheck(theApp.filterType == FILTER_SCANLINES); + break; + case ID_OPTIONS_FILTER_HQ2X: + pCmdUI->SetCheck(theApp.filterType == FILTER_HQ2X); + break; + case ID_OPTIONS_FILTER_LQ2X: + pCmdUI->SetCheck(theApp.filterType == FILTER_LQ2X); + break; + case ID_OPTIONS_FILTER_SIMPLE3X: + pCmdUI->SetCheck(theApp.filterType == FILTER_SIMPLE3X); + break; + case ID_OPTIONS_FILTER_SIMPLE4X: + pCmdUI->SetCheck(theApp.filterType == FILTER_SIMPLE4X); + break; + case ID_OPTIONS_FILTER_HQ3X: + pCmdUI->SetCheck(theApp.filterType == FILTER_HQ3X); + break; + case ID_OPTIONS_FILTER_HQ4X: + pCmdUI->SetCheck(theApp.filterType == FILTER_HQ4X); + break; + } +} + +void MainWnd::OnUpdateOptionsSelectPlugin(CCmdUI *pCmdUI) +{ + pCmdUI->Enable( !theApp.filterMT ); +} + +BOOL MainWnd::OnOptionsFilterIFB(UINT nID) +{ + switch(nID) { + case ID_OPTIONS_FILTER_INTERFRAMEBLENDING_NONE: + theApp.ifbType = 0; + break; + case ID_OPTIONS_FILTER_INTERFRAMEBLENDING_MOTIONBLUR: + theApp.ifbType = 1; + break; + case ID_OPTIONS_FILTER_INTERFRAMEBLENDING_SMART: + theApp.ifbType = 2; + break; + default: + return FALSE; + } + theApp.updateIFB(); + return TRUE; +} + +void MainWnd::OnUpdateOptionsFilterIFB(CCmdUI *pCmdUI) +{ + switch(pCmdUI->m_nID) { + case ID_OPTIONS_FILTER_INTERFRAMEBLENDING_NONE: + pCmdUI->SetCheck(theApp.ifbType == 0); + break; + case ID_OPTIONS_FILTER_INTERFRAMEBLENDING_MOTIONBLUR: + pCmdUI->SetCheck(theApp.ifbType == 1); + break; + case ID_OPTIONS_FILTER_INTERFRAMEBLENDING_SMART: + pCmdUI->SetCheck(theApp.ifbType == 2); + break; + } +} + +void MainWnd::OnOptionsFilterDisablemmx() +{ +#ifdef MMX + theApp.disableMMX = !theApp.disableMMX; + if(!theApp.disableMMX) + cpu_mmx = theApp.detectMMX(); + else + cpu_mmx = 0; +#endif +} + +void MainWnd::OnUpdateOptionsFilterDisablemmx(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.disableMMX); +} + +void MainWnd::OnOptionsLanguageSystem() +{ + theApp.winSetLanguageOption(0, false); + theApp.winAccelMgr.UpdateMenu(theApp.menu); +} + +void MainWnd::OnUpdateOptionsLanguageSystem(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.languageOption == 0); +} + +void MainWnd::OnOptionsLanguageEnglish() +{ + theApp.winSetLanguageOption(1, false); + theApp.winAccelMgr.UpdateMenu(theApp.menu); +} + +void MainWnd::OnUpdateOptionsLanguageEnglish(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.languageOption == 1); +} + +void MainWnd::OnOptionsLanguageOther() +{ + theApp.winSetLanguageOption(2, false); + theApp.winAccelMgr.UpdateMenu(theApp.menu); +} + +void MainWnd::OnUpdateOptionsLanguageOther(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.languageOption == 2); +} + + +void MainWnd::OnOptionsJoypadConfigure1() +{ + JoypadConfig dlg(0); + dlg.DoModal(); +} + +void MainWnd::OnUpdateOptionsJoypadConfigure1(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(theApp.videoOption != VIDEO_320x240); +} + +void MainWnd::OnOptionsJoypadConfigure2() +{ + JoypadConfig dlg(1); + dlg.DoModal(); +} + +void MainWnd::OnUpdateOptionsJoypadConfigure2(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(theApp.videoOption != VIDEO_320x240); +} + +void MainWnd::OnOptionsJoypadConfigure3() +{ + JoypadConfig dlg(2); + dlg.DoModal(); +} + +void MainWnd::OnUpdateOptionsJoypadConfigure3(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(theApp.videoOption != VIDEO_320x240); +} + +void MainWnd::OnOptionsJoypadConfigure4() +{ + JoypadConfig dlg(3); + dlg.DoModal(); +} + +void MainWnd::OnUpdateOptionsJoypadConfigure4(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(theApp.videoOption != VIDEO_320x240); +} + +BOOL MainWnd::OnOptionsJoypadDefault(UINT nID) +{ + theApp.joypadDefault = nID - ID_OPTIONS_JOYPAD_DEFAULTJOYPAD_1; + return TRUE; +} + +void MainWnd::OnUpdateOptionsJoypadDefault(CCmdUI *pCmdUI) +{ + pCmdUI->SetCheck(theApp.joypadDefault == (int)(pCmdUI->m_nID - ID_OPTIONS_JOYPAD_DEFAULTJOYPAD_1)); +} + +void MainWnd::OnOptionsJoypadMotionconfigure() +{ + MotionConfig dlg; + dlg.DoModal(); +} + +void MainWnd::OnUpdateOptionsJoypadMotionconfigure(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(theApp.videoOption != VIDEO_320x240); +} + +BOOL MainWnd::OnOptionsJoypadAutofire(UINT nID) +{ + switch(nID) { + case ID_OPTIONS_JOYPAD_AUTOFIRE_A: + if(theApp.autoFire & 1) { + theApp.autoFire &= ~1; + systemScreenMessage(winResLoadString(IDS_AUTOFIRE_A_DISABLED)); + } else { + theApp.autoFire |= 1; + systemScreenMessage(winResLoadString(IDS_AUTOFIRE_A)); + } + break; + case ID_OPTIONS_JOYPAD_AUTOFIRE_B: + if(theApp.autoFire & 2) { + theApp.autoFire &= ~2; + systemScreenMessage(winResLoadString(IDS_AUTOFIRE_B_DISABLED)); + } else { + theApp.autoFire |= 2; + systemScreenMessage(winResLoadString(IDS_AUTOFIRE_B)); + } + break; + case ID_OPTIONS_JOYPAD_AUTOFIRE_L: + if(theApp.autoFire & 512) { + theApp.autoFire &= ~512; + systemScreenMessage(winResLoadString(IDS_AUTOFIRE_L_DISABLED)); + } else { + theApp.autoFire |= 512; + systemScreenMessage(winResLoadString(IDS_AUTOFIRE_L)); + } + break; + case ID_OPTIONS_JOYPAD_AUTOFIRE_R: + if(theApp.autoFire & 256) { + theApp.autoFire &= ~256; + systemScreenMessage(winResLoadString(IDS_AUTOFIRE_R_DISABLED)); + } else { + theApp.autoFire |= 256; + systemScreenMessage(winResLoadString(IDS_AUTOFIRE_R)); + } + break; + default: + return FALSE; + } + return TRUE; +} + +void MainWnd::OnUpdateOptionsJoypadAutofire(CCmdUI *pCmdUI) +{ + bool check = true; + switch(pCmdUI->m_nID) { + case ID_OPTIONS_JOYPAD_AUTOFIRE_A: + check = (theApp.autoFire & 1) != 0; + break; + case ID_OPTIONS_JOYPAD_AUTOFIRE_B: + check = (theApp.autoFire & 2) != 0; + break; + case ID_OPTIONS_JOYPAD_AUTOFIRE_L: + check = (theApp.autoFire & 512) != 0; + break; + case ID_OPTIONS_JOYPAD_AUTOFIRE_R: + check = (theApp.autoFire & 256) != 0; + break; + } + pCmdUI->SetCheck(check); +} + +void MainWnd::OnOptionsVideoFullscreenmaxscale() +{ + MaxScale dlg; + + dlg.DoModal(); + + if( theApp.display ) { + theApp.display->setOption( _T("maxScale"), theApp.maxScale ); + } +} + +#ifndef NO_LINK +void MainWnd::OnLinkOptions() +{ + LinkOptions dlg; + + dlg.DoModal(); +} + +void MainWnd::OnOptionsLinkRFU() +{ + if(rfu_enabled) rfu_enabled = false; + else { + rfu_enabled = true; + MessageBox("Please note this is the first version\nof RFU emulation code and it's not 100% bug free.\nAlso only 2 players single computer are supported at this time.", "Warning", MB_OK); + } +} + +void MainWnd::OnUpdateOptionsLinkEnable(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(gba_link_enabled); +} + +void MainWnd::OnOptionsLinkEnable() +{ + gba_link_enabled = !gba_link_enabled; +} + +void MainWnd::OnUpdateOptionsLinkRFU(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(rfu_enabled); +} + +void MainWnd::OnOptionsJoybus() +{ + JoybusOptions dlg; + dlg.DoModal(); +} +#else +void MainWnd::OnLinkOptions() { } +void MainWnd::OnOptionsLinkRFU() { } +void MainWnd::OnUpdateOptionsLinkEnable(CCmdUI*) { } +void MainWnd::OnOptionsLinkEnable() { } +void MainWnd::OnUpdateOptionsLinkRFU(CCmdUI*) { } +void MainWnd::OnOptionsJoybus() { } +#endif + +void MainWnd::OnOptionsEmulatorGameoverrides() +{ + if(emulating && theApp.cartridgeType == 0) { + GameOverrides dlg(this); + dlg.DoModal(); + } +} + +void MainWnd::OnUpdateOptionsEmulatorGameoverrides(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(emulating && (theApp.cartridgeType == 0)); +} + +void MainWnd::OnOptionsSoundHardwareacceleration() +{ + theApp.dsoundDisableHardwareAcceleration = !theApp.dsoundDisableHardwareAcceleration; + soundShutdown(); + soundInit(); +} + +void MainWnd::OnUpdateOptionsSoundHardwareacceleration(CCmdUI *pCmdUI) +{ + pCmdUI->SetCheck(!theApp.dsoundDisableHardwareAcceleration); +} + +void MainWnd::OnOptionsSelectPlugin() +{ + SelectPlugin dlg; + + if (dlg.DoModal() == IDOK) + { + theApp.filterType = FILTER_PLUGIN; + theApp.updateFilter(); + } +} + + +void MainWnd::OnOutputapiDirectsound() +{ + if( theApp.audioAPI != DIRECTSOUND ) { + theApp.audioAPI = DIRECTSOUND; + soundShutdown(); + soundInit(); + } +} + +void MainWnd::OnUpdateOutputapiDirectsound(CCmdUI *pCmdUI) +{ + pCmdUI->SetCheck( ( theApp.audioAPI == DIRECTSOUND ) ? 1 : 0 ); + pCmdUI->Enable(!theApp.aviRecording && !theApp.soundRecording); +} + +void MainWnd::OnOutputapiXaudio2() +{ +#ifndef NO_XAUDIO2 + if( theApp.audioAPI != XAUDIO2 ) { + theApp.audioAPI = XAUDIO2; + soundShutdown(); + soundInit(); + } +#endif +} + +void MainWnd::OnUpdateOutputapiXaudio2(CCmdUI *pCmdUI) +{ +#ifndef NO_XAUDIO2 + pCmdUI->SetCheck( ( theApp.audioAPI == XAUDIO2 ) ? 1 : 0 ); + pCmdUI->Enable(!theApp.aviRecording && !theApp.soundRecording); +#else + pCmdUI->Enable( FALSE ); +#endif +} + +void MainWnd::OnOutputapiOpenal() +{ +#ifndef NO_OAL + if( theApp.audioAPI != OPENAL_SOUND ) { + theApp.audioAPI = OPENAL_SOUND; + soundShutdown(); + soundInit(); + } +#endif +} + +void MainWnd::OnUpdateOutputapiOpenal(CCmdUI *pCmdUI) +{ +#ifndef NO_OAL + pCmdUI->SetCheck( ( theApp.audioAPI == OPENAL_SOUND ) ? 1 : 0 ); + pCmdUI->Enable(!theApp.aviRecording && !theApp.soundRecording); +#else + pCmdUI->Enable( FALSE ); +#endif +} + +void MainWnd::OnOutputapiOalconfiguration() +{ +#ifndef NO_OAL + OALConfig dlg; + + dlg.selectedDevice = theApp.oalDevice; + dlg.bufferCount = theApp.oalBufferCount; + + if( dlg.DoModal() == IDOK ) { + soundShutdown(); + // do this before changing any values OpenAL + // might need for successful cleanup + + if( theApp.oalDevice ) { + free( theApp.oalDevice ); + theApp.oalDevice = NULL; + } + + theApp.oalDevice = (TCHAR*)malloc( (dlg.selectedDevice.GetLength() + 1) * sizeof( TCHAR ) ); + _tcscpy( theApp.oalDevice, dlg.selectedDevice.GetBuffer() ); + theApp.oalBufferCount = dlg.bufferCount; + + soundInit(); + } +#endif +} + +void MainWnd::OnUpdateOutputapiOalconfiguration(CCmdUI *pCmdUI) +{ +#ifndef NO_OAL + pCmdUI->Enable(!theApp.aviRecording && !theApp.soundRecording); +#else + pCmdUI->Enable( FALSE ); +#endif +} + +void MainWnd::OnOutputapiXaudio2config() +{ +#ifndef NO_XAUDIO2 + XAudio2_Config dlg; + + dlg.m_selected_device_index = theApp.xa2Device; + dlg.m_buffer_count = theApp.xa2BufferCount; + dlg.m_enable_upmixing = theApp.xa2Upmixing; + + if( dlg.DoModal() == IDOK ) { + soundShutdown(); + + theApp.xa2Device = dlg.m_selected_device_index; + theApp.xa2BufferCount = dlg.m_buffer_count; + theApp.xa2Upmixing = dlg.m_enable_upmixing; + + soundInit(); + } +#endif +} + +void MainWnd::OnUpdateOutputapiXaudio2config(CCmdUI *pCmdUI) +{ +#ifndef NO_XAUDIO2 + pCmdUI->Enable(!theApp.aviRecording && !theApp.soundRecording); +#else + pCmdUI->Enable( FALSE ); +#endif +} + +void MainWnd::OnRenderapiD3dmotionblur() +{ +#ifndef NO_D3D + theApp.d3dMotionBlur = !theApp.d3dMotionBlur; + if( theApp.display ) { + theApp.display->setOption( _T("motionBlur"), theApp.d3dMotionBlur ? 1 : 0 ); + } +#endif +} + +void MainWnd::OnUpdateRenderapiD3dmotionblur(CCmdUI *pCmdUI) +{ +#ifndef NO_D3D + pCmdUI->SetCheck( theApp.d3dMotionBlur ? 1 : 0 ); +#else + pCmdUI->Enable( FALSE ); +#endif +} + +void MainWnd::OnEmulatorBiosfiles() +{ + BIOSDialog dlg; + dlg.m_enableBIOS_GBA = theApp.useBiosFileGBA ? TRUE : FALSE; + dlg.m_enableBIOS_GBC = theApp.useBiosFileGBC ? TRUE : FALSE; + dlg.m_enableBIOS_GB = theApp.useBiosFileGB ? TRUE : FALSE; + dlg.m_skipLogo = theApp.skipBiosFile ? TRUE : FALSE; + dlg.m_pathGBA = theApp.biosFileNameGBA; + dlg.m_pathGBC = theApp.biosFileNameGBC; + dlg.m_pathGB = theApp.biosFileNameGB; + + if( IDOK == dlg.DoModal() ) { + theApp.useBiosFileGBA = dlg.m_enableBIOS_GBA == TRUE; + theApp.useBiosFileGBC = dlg.m_enableBIOS_GBC == TRUE; + theApp.useBiosFileGB = dlg.m_enableBIOS_GB == TRUE; + theApp.skipBiosFile = dlg.m_skipLogo == TRUE; + theApp.biosFileNameGBA = dlg.m_pathGBA; + theApp.biosFileNameGBC = dlg.m_pathGBC; + theApp.biosFileNameGB = dlg.m_pathGB; + } +} + +void MainWnd::OnPixelfilterMultiThreading() +{ + theApp.filterMT = !theApp.filterMT; +} + +void MainWnd::OnUpdatePixelfilterMultiThreading(CCmdUI *pCmdUI) +{ + pCmdUI->Enable( theApp.filterType != FILTER_PLUGIN ); + pCmdUI->SetCheck( theApp.filterMT ? 1 : 0 ); +} diff --git a/src/win32/MainWndTools.cpp b/src/win32/MainWndTools.cpp new file mode 100644 index 0000000..579191b --- /dev/null +++ b/src/win32/MainWndTools.cpp @@ -0,0 +1,636 @@ +#include "stdafx.h" +#include "MainWnd.h" + +#include "AccelEditor.h" +#include "AVIWrite.h" +#include "Disassemble.h" +#include "FileDlg.h" +#include "GBDisassemble.h" +#include "GBMapView.h" +#include "GBMemoryViewerDlg.h" +#include "GBOamView.h" +#include "GBPaletteView.h" +#include "GBTileView.h" +#include "GDBConnection.h" +#include "IOViewer.h" +#include "MapView.h" +#include "MemoryViewerDlg.h" +#include "OamView.h" +#include "PaletteView.h" +#include "Reg.h" +#include "TileView.h" +#include "WavWriter.h" +#include "WinResUtil.h" +#include "Logging.h" + +#include "../gba/GBA.h" +#include "../gba/Globals.h" +#include "../gba/Sound.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +extern bool debugger; +extern int emulating; +extern SOCKET remoteSocket; + +extern void remoteCleanUp(); +extern void remoteSetSockets(SOCKET, SOCKET); + +void MainWnd::OnToolsDisassemble() +{ + if(theApp.cartridgeType == 0) { + Disassemble *dlg = new Disassemble(); + dlg->Create(IDD_DISASSEMBLE, this); + dlg->ShowWindow(SW_SHOW); + } else { + GBDisassemble *dlg = new GBDisassemble(); + dlg->Create(IDD_GB_DISASSEMBLE, this); + dlg->ShowWindow(SW_SHOW); + } +} + +void MainWnd::OnUpdateToolsDisassemble(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(theApp.videoOption <= VIDEO_6X); +} + +void MainWnd::OnToolsLogging() +{ + toolsLogging(); +} + +void MainWnd::OnUpdateToolsLogging(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(theApp.videoOption <= VIDEO_6X); +} + +void MainWnd::OnToolsIoviewer() +{ + IOViewer *dlg = new IOViewer; + dlg->Create(IDD_IO_VIEWER,this); + dlg->ShowWindow(SW_SHOW); +} + +void MainWnd::OnUpdateToolsIoviewer(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(theApp.videoOption <= VIDEO_6X && theApp.cartridgeType == 0); +} + +void MainWnd::OnToolsMapview() +{ + if(theApp.cartridgeType == 0) { + MapView *dlg = new MapView; + dlg->Create(IDD_MAP_VIEW, this); + dlg->ShowWindow(SW_SHOW); + } else { + GBMapView *dlg = new GBMapView; + dlg->Create(IDD_GB_MAP_VIEW, this); + dlg->ShowWindow(SW_SHOW); + } +} + +void MainWnd::OnUpdateToolsMapview(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(theApp.videoOption <= VIDEO_6X); +} + +void MainWnd::OnToolsMemoryviewer() +{ + if(theApp.cartridgeType == 0) { + MemoryViewerDlg *dlg = new MemoryViewerDlg; + dlg->Create(IDD_MEM_VIEWER, this); + dlg->ShowWindow(SW_SHOW); + } else { + GBMemoryViewerDlg *dlg = new GBMemoryViewerDlg; + dlg->Create(IDD_MEM_VIEWER, this); + dlg->ShowWindow(SW_SHOW); + } +} + +void MainWnd::OnUpdateToolsMemoryviewer(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(theApp.videoOption <= VIDEO_6X); +} + +void MainWnd::OnToolsOamviewer() +{ + if(theApp.cartridgeType == 0) { + OamView *dlg = new OamView; + dlg->Create(IDD_OAM_VIEW, this); + dlg->ShowWindow(SW_SHOW); + } else { + GBOamView *dlg = new GBOamView; + dlg->Create(IDD_GB_OAM_VIEW, this); + dlg->ShowWindow(SW_SHOW); + } +} + +void MainWnd::OnUpdateToolsOamviewer(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(theApp.videoOption <= VIDEO_6X); +} + +void MainWnd::OnToolsPaletteview() +{ + if(theApp.cartridgeType == 0) { + PaletteView *dlg = new PaletteView; + dlg->Create(IDD_PALETTE_VIEW, this); + dlg->ShowWindow(SW_SHOW); + } else { + GBPaletteView *dlg = new GBPaletteView; + dlg->Create(IDD_GB_PALETTE_VIEW, this); + dlg->ShowWindow(SW_SHOW); + } +} + +void MainWnd::OnUpdateToolsPaletteview(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(theApp.videoOption <= VIDEO_6X); +} + +void MainWnd::OnToolsTileviewer() +{ + if(theApp.cartridgeType == 0) { + TileView *dlg = new TileView; + dlg->Create(IDD_TILE_VIEWER, this); + dlg->ShowWindow(SW_SHOW); + } else { + GBTileView *dlg = new GBTileView; + dlg->Create(IDD_GB_TILE_VIEWER, this); + dlg->ShowWindow(SW_SHOW); + } +} + +void MainWnd::OnUpdateToolsTileviewer(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(theApp.videoOption <= VIDEO_6X); +} + +void MainWnd::OnDebugNextframe() +{ + if(theApp.paused) + theApp.paused = false; + theApp.winPauseNextFrame = true; +} + +void MainWnd::OnToolsDebugGdb() +{ + GDBPortDlg dlg; + + if(dlg.DoModal()) { + GDBWaitingDlg wait(dlg.getSocket(), dlg.getPort()); + if(wait.DoModal()) { + remoteSetSockets(wait.getListenSocket(), wait.getSocket()); + debugger = true; + emulating = 1; + theApp.cartridgeType = IMAGE_GBA; + theApp.filename = "\\gnu_stub"; + rom = (u8 *)malloc(0x2000000); + workRAM = (u8 *)calloc(1, 0x40000); + bios = (u8 *)calloc(1,0x4000); + internalRAM = (u8 *)calloc(1,0x8000); + paletteRAM = (u8 *)calloc(1,0x400); + vram = (u8 *)calloc(1, 0x20000); + oam = (u8 *)calloc(1, 0x400); + pix = (u8 *)calloc(1, 4 * 241 * 162); + ioMem = (u8 *)calloc(1, 0x400); + + theApp.emulator = GBASystem; + + CPUInit(theApp.biosFileNameGBA, theApp.useBiosFileGBA); + CPUReset(); + } + } +} + +void MainWnd::OnUpdateToolsDebugGdb(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(theApp.videoOption <= VIDEO_6X && remoteSocket == -1); +} + +void MainWnd::OnToolsDebugLoadandwait() +{ + if(fileOpenSelect(0)) { + if(FileRun()) { + if(theApp.cartridgeType != 0) { + systemMessage(IDS_ERROR_NOT_GBA_IMAGE, "Error: not a GBA image"); + OnFileClose(); + return; + } + GDBPortDlg dlg; + + if(dlg.DoModal()) { + GDBWaitingDlg wait(dlg.getSocket(), dlg.getPort()); + if(wait.DoModal()) { + remoteSetSockets(wait.getListenSocket(), wait.getSocket()); + debugger = true; + emulating = 1; + } + } + } + } +} + +void MainWnd::OnUpdateToolsDebugLoadandwait(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(theApp.videoOption <= VIDEO_6X && remoteSocket == -1); +} + +void MainWnd::OnToolsDebugBreak() +{ + if(armState) { + armNextPC -= 4; + reg[15].I -= 4; + } else { + armNextPC -= 2; + reg[15].I -= 2; + } + debugger = true; +} + +void MainWnd::OnUpdateToolsDebugBreak(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(theApp.videoOption <= VIDEO_6X && remoteSocket != -1); +} + +void MainWnd::OnToolsDebugDisconnect() +{ + remoteCleanUp(); + debugger = false; +} + +void MainWnd::OnUpdateToolsDebugDisconnect(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(theApp.videoOption <= VIDEO_6X && remoteSocket != -1); +} + +void MainWnd::OnOptionsSoundStartrecording() +{ + CString captureBuffer; + + CString capdir = regQueryStringValue("soundRecordDir", NULL); + + if(capdir.IsEmpty()) + capdir = getDirFromFile(theApp.filename); + + CString filter = theApp.winLoadFilter(IDS_FILTER_WAV); + CString title = winResLoadString(IDS_SELECT_WAV_NAME); + + LPCTSTR exts[] = { ".WAV" }; + + FileDlg dlg(this, "", filter, 1, "WAV", exts, capdir, title, true); + + if(dlg.DoModal() == IDCANCEL) { + return; + } + + captureBuffer = theApp.soundRecordName = dlg.GetPathName(); + theApp.soundRecording = true; + + if(dlg.m_ofn.nFileOffset > 0) { + captureBuffer = captureBuffer.Left(dlg.m_ofn.nFileOffset); + } + + int len = captureBuffer.GetLength(); + + if(len > 3 && captureBuffer[len-1] == '\\') + captureBuffer = captureBuffer.Left(len-1); + regSetStringValue("soundRecordDir", captureBuffer); +} + +void MainWnd::OnUpdateOptionsSoundStartrecording(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(!theApp.soundRecording); +} + +void MainWnd::OnOptionsSoundStoprecording() +{ + if(theApp.soundRecorder) { + delete theApp.soundRecorder; + theApp.soundRecorder = NULL; + } + theApp.soundRecording = false; +} + +void MainWnd::OnUpdateOptionsSoundStoprecording(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(theApp.soundRecording); +} + + +void MainWnd::OnToolsRecordStartavirecording() +{ + CString captureBuffer; + CString capdir = regQueryStringValue( "aviRecordDir", NULL ); + + if( capdir.IsEmpty() ) { + capdir = getDirFromFile( theApp.filename ); + } + + CString filter = theApp.winLoadFilter( IDS_FILTER_AVI ); + CString title = winResLoadString( IDS_SELECT_AVI_NAME ); + + LPCTSTR exts[] = { ".AVI" }; + + FileDlg dlg( this, "", filter, 1, "AVI", exts, capdir, title, true ); + + if( dlg.DoModal() == IDCANCEL ) { + return; + } + + captureBuffer = theApp.soundRecordName = dlg.GetPathName(); + theApp.aviRecordName = captureBuffer; + theApp.aviRecording = true; + + if( dlg.m_ofn.nFileOffset > 0 ) { + captureBuffer = captureBuffer.Left( dlg.m_ofn.nFileOffset ); + } + + int len = captureBuffer.GetLength(); + + if( ( len > 3 ) && captureBuffer[ len - 1 ] == '\\' ) { + captureBuffer = captureBuffer.Left( len - 1 ); + } + + regSetStringValue( "aviRecordDir", captureBuffer ); + + + // create AVI file + bool ret; + + if( theApp.aviRecorder ) { + delete theApp.aviRecorder; + theApp.aviRecorder = NULL; + } + theApp.aviRecorder = new AVIWrite(); + + // create AVI file + ret = theApp.aviRecorder->CreateAVIFile( theApp.aviRecordName ); + if( !ret ) { + systemMessage( IDS_AVI_CANNOT_CREATE_AVI, "Cannot create AVI file." ); + delete theApp.aviRecorder; + theApp.aviRecorder = NULL; + theApp.aviRecording = false; + return; + } + + // add video stream + ret = theApp.aviRecorder->CreateVideoStream( + theApp.sizeX, + theApp.sizeY, + ( systemColorDepth == 32 ) ? 24 : 16, + 60, + this->GetSafeHwnd() + ); + if( !ret ) { + systemMessage( IDS_AVI_CANNOT_CREATE_VIDEO, "Cannot create video stream in AVI file. Make sure the selected codec supports input in RGB24 color space!" ); + delete theApp.aviRecorder; + theApp.aviRecorder = NULL; + theApp.aviRecording = false; + return; + } + + // add audio stream + ret = theApp.aviRecorder->CreateAudioStream( + 2, + soundGetSampleRate(), + 16, + this->GetSafeHwnd() + ); + if( !ret ) { + systemMessage( IDS_AVI_CANNOT_CREATE_AUDIO, "Cannot create audio stream in AVI file." ); + delete theApp.aviRecorder; + theApp.aviRecorder = NULL; + theApp.aviRecording = false; + return; + } +} + + +void MainWnd::OnUpdateToolsRecordStartavirecording(CCmdUI* pCmdUI) +{ + pCmdUI->Enable( !theApp.aviRecording && emulating ); +} + + +void MainWnd::OnToolsRecordStopavirecording() +{ + if( theApp.aviRecorder ) { + delete theApp.aviRecorder; + theApp.aviRecorder = NULL; + } + theApp.aviRecording = false; +} + + +void MainWnd::OnUpdateToolsRecordStopavirecording(CCmdUI* pCmdUI) +{ + pCmdUI->Enable( theApp.aviRecording ); +} + +void MainWnd::OnToolsRecordStartmovierecording() +{ + CString captureBuffer; + CString capdir = regQueryStringValue("movieRecordDir", ""); + + if(capdir.IsEmpty()) + capdir = getDirFromFile(theApp.filename); + + CString filter = theApp.winLoadFilter(IDS_FILTER_VMV); + CString title = winResLoadString(IDS_SELECT_MOVIE_NAME); + + LPCTSTR exts[] = { ".VMV" }; + + FileDlg dlg(this, "", filter, 1, "VMV", exts, capdir, title, true); + + if(dlg.DoModal() == IDCANCEL) { + return; + } + + CString movieName = dlg.GetPathName(); + captureBuffer = movieName; + + if(dlg.m_ofn.nFileOffset > 0) { + captureBuffer = captureBuffer.Left(dlg.m_ofn.nFileOffset); + } + + int len = captureBuffer.GetLength(); + + if(len > 3 && captureBuffer[len-1] == '\\') + captureBuffer = captureBuffer.Left(len-1); + + regSetStringValue("movieRecordDir", captureBuffer); + + theApp.movieFile = fopen(movieName, "wb"); + + if(!theApp.movieFile) { + systemMessage(IDS_CANNOT_OPEN_FILE, "Cannot open file %s", + (const char *)movieName); + return; + } + + int version = 1; + + fwrite(&version, 1, sizeof(int), theApp.movieFile); + + movieName = movieName.Left(movieName.GetLength()-3) + "VM0"; + + if(writeSaveGame(movieName)) { + theApp.movieFrame = 0; + theApp.movieLastJoypad = 0; + theApp.movieRecording = true; + theApp.moviePlaying = false; + } else { + systemMessage(IDS_CANNOT_OPEN_FILE, "Cannot open file %s", + (const char *)movieName); + } +} + +void MainWnd::OnUpdateToolsRecordStartmovierecording(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(!theApp.movieRecording); +} + +void MainWnd::OnToolsRecordStopmovierecording() +{ + if(theApp.movieRecording) { + if(theApp.movieFile != NULL) { + // record the last joypad change so that the correct time can be + // recorded + fwrite(&theApp.movieFrame, 1, sizeof(int), theApp.movieFile); + fwrite(&theApp.movieLastJoypad, 1, sizeof(u32), theApp.movieFile); + fclose(theApp.movieFile); + theApp.movieFile = NULL; + } + theApp.movieRecording = false; + theApp.moviePlaying = false; + theApp.movieLastJoypad = 0; + } +} + +void MainWnd::OnUpdateToolsRecordStopmovierecording(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(theApp.movieRecording); +} + +void MainWnd::OnToolsPlayStartmovieplaying() +{ + static bool moviePlayMessage = false; + + if(!moviePlayMessage) { + moviePlayMessage = true; + CString msg = winResLoadString(IDS_MOVIE_PLAY); + CString title = winResLoadString(IDS_CONFIRM_ACTION); + if(MessageBox(msg, + title, + MB_OKCANCEL) == IDCANCEL) + return; + } + + CString captureBuffer; + CString capdir = regQueryStringValue("movieRecordDir", ""); + + if(capdir.IsEmpty()) + capdir = getDirFromFile(theApp.filename); + + CString filter = theApp.winLoadFilter(IDS_FILTER_VMV); + CString title = winResLoadString(IDS_SELECT_MOVIE_NAME); + + LPCTSTR exts[] = { ".VMV" }; + + FileDlg dlg(this, "", filter, 1, "VMV", exts, capdir, title, false); + + if(dlg.DoModal() == IDCANCEL) { + return; + } + + CString movieName = dlg.GetPathName(); + captureBuffer = movieName; + + theApp.movieFile = fopen(movieName, "rb"); + + if(!theApp.movieFile) { + systemMessage(IDS_CANNOT_OPEN_FILE, "Cannot open file %s", + (const char *)movieName); + return; + } + int version = 0; + fread(&version, 1, sizeof(int), theApp.movieFile); + if(version != 1) { + systemMessage(IDS_UNSUPPORTED_MOVIE_VERSION, + "Unsupported movie version %d.", + version); + fclose(theApp.movieFile); + theApp.movieFile = NULL; + return; + } + movieName = movieName.Left(movieName.GetLength()-3)+"VM0"; + if(loadSaveGame(movieName)) { + theApp.moviePlaying = true; + theApp.movieFrame = 0; + theApp.moviePlayFrame = 0; + theApp.movieLastJoypad = 0; + theApp.movieReadNext(); + } else { + systemMessage(IDS_CANNOT_OPEN_FILE, "Cannot open file %s", + (const char *)movieName); + } +} + +void MainWnd::OnUpdateToolsPlayStartmovieplaying(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(!theApp.moviePlaying); +} + +void MainWnd::OnToolsPlayStopmovieplaying() +{ + if(theApp.moviePlaying) { + if(theApp.movieFile != NULL) { + fclose(theApp.movieFile); + theApp.movieFile = NULL; + } + theApp.moviePlaying = false; + theApp.movieLastJoypad = 0; + } +} + +void MainWnd::OnUpdateToolsPlayStopmovieplaying(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(theApp.moviePlaying); +} + +void MainWnd::OnToolsRewind() +{ + if(emulating && theApp.emulator.emuReadMemState && theApp.rewindMemory && theApp.rewindCount) { + theApp.rewindPos = --theApp.rewindPos & 7; + theApp.emulator.emuReadMemState(&theApp.rewindMemory[REWIND_SIZE*theApp.rewindPos], REWIND_SIZE); + theApp.rewindCount--; + theApp.rewindCounter = 0; + } +} + +void MainWnd::OnUpdateToolsRewind(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(theApp.rewindMemory != NULL && emulating && theApp.rewindCount); +} + +void MainWnd::OnToolsCustomize() +{ + AccelEditor dlg; + + if(dlg.DoModal()) { + theApp.winAccelMgr = dlg.mgr; + theApp.winAccelMgr.UpdateWndTable(); + theApp.winAccelMgr.Write(); + theApp.winAccelMgr.UpdateMenu(theApp.menu); + } +} + +void MainWnd::OnUpdateToolsCustomize(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(theApp.videoOption != VIDEO_320x240); +} diff --git a/src/win32/MapView.cpp b/src/win32/MapView.cpp new file mode 100644 index 0000000..dd14c4c --- /dev/null +++ b/src/win32/MapView.cpp @@ -0,0 +1,998 @@ +#include "stdafx.h" +#include "vba.h" +#include "FileDlg.h" +#include "MapView.h" +#include "Reg.h" +#include "WinResUtil.h" + +#include "../System.h" +#include "../gba/GBA.h" +#include "../gba/Globals.h" +#include "../NLS.h" +#include "../Util.h" + +extern "C" { +#include +} + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// MapView dialog + + +MapView::MapView(CWnd* pParent /*=NULL*/) + : ResizeDlg(MapView::IDD, pParent) +{ + //{{AFX_DATA_INIT(MapView) + //}}AFX_DATA_INIT + autoUpdate = false; + + memset(&bmpInfo.bmiHeader, 0, sizeof(bmpInfo.bmiHeader)); + + bmpInfo.bmiHeader.biSize = sizeof(bmpInfo.bmiHeader); + bmpInfo.bmiHeader.biWidth = 1024; + bmpInfo.bmiHeader.biHeight = -1024; + bmpInfo.bmiHeader.biPlanes = 1; + bmpInfo.bmiHeader.biBitCount = 24; + bmpInfo.bmiHeader.biCompression = BI_RGB; + data = (u8 *)calloc(1, 3 * 1024 * 1024); + + mapView.setData(data); + mapView.setBmpInfo(&bmpInfo); + + control = BG0CNT; + + bg = 0; + frame = 0; +} + +MapView::~MapView() +{ + free(data); + data = NULL; +} + +void MapView::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(MapView) + DDX_Control(pDX, IDC_NUMCOLORS, m_numcolors); + DDX_Control(pDX, IDC_MODE, m_mode); + DDX_Control(pDX, IDC_OVERFLOW, m_overflow); + DDX_Control(pDX, IDC_MOSAIC, m_mosaic); + DDX_Control(pDX, IDC_PRIORITY, m_priority); + DDX_Control(pDX, IDC_DIM, m_dim); + DDX_Control(pDX, IDC_CHARBASE, m_charbase); + DDX_Control(pDX, IDC_MAPBASE, m_mapbase); + //}}AFX_DATA_MAP + DDX_Control(pDX, IDC_MAP_VIEW, mapView); + DDX_Control(pDX, IDC_MAP_VIEW_ZOOM, mapViewZoom); + DDX_Control(pDX, IDC_COLOR, color); +} + + +BEGIN_MESSAGE_MAP(MapView, CDialog) + //{{AFX_MSG_MAP(MapView) + ON_BN_CLICKED(IDC_REFRESH, OnRefresh) + ON_BN_CLICKED(IDC_FRAME_0, OnFrame0) + ON_BN_CLICKED(IDC_FRAME_1, OnFrame1) + ON_BN_CLICKED(IDC_BG0, OnBg0) + ON_BN_CLICKED(IDC_BG1, OnBg1) + ON_BN_CLICKED(IDC_BG2, OnBg2) + ON_BN_CLICKED(IDC_BG3, OnBg3) + ON_BN_CLICKED(IDC_STRETCH, OnStretch) + ON_BN_CLICKED(IDC_AUTO_UPDATE, OnAutoUpdate) + ON_BN_CLICKED(IDC_CLOSE, OnClose) + ON_BN_CLICKED(IDC_SAVE, OnSave) + //}}AFX_MSG_MAP + ON_MESSAGE(WM_MAPINFO, OnMapInfo) + ON_MESSAGE(WM_COLINFO, OnColInfo) + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// MapView message handlers + +void MapView::renderTextScreen(u16 control) +{ + u16 *palette = (u16 *)paletteRAM; + u8 *charBase = &vram[((control >> 2) & 0x03) * 0x4000]; + u16 *screenBase = (u16 *)&vram[((control >> 8) & 0x1f) * 0x800]; + u8 *bmp = data; + + int sizeX = 256; + int sizeY = 256; + switch((control >> 14) & 3) { + case 0: + break; + case 1: + sizeX = 512; + break; + case 2: + sizeY = 512; + break; + case 3: + sizeX = 512; + sizeY = 512; + break; + } + + w = sizeX; + h = sizeY; + + if(control & 0x80) { + for(int y = 0; y < sizeY; y++) { + int yy = y & 255; + + if(y == 256 && sizeY > 256) { + screenBase += 0x400; + if(sizeX > 256) + screenBase += 0x400; + } + u16 *screenSource = screenBase + ((yy>>3)*32); + + for(int x = 0; x < sizeX; x++) { + u16 data = *screenSource; + + int tile = data & 0x3FF; + int tileX = (x & 7); + int tileY = y & 7; + + if(data & 0x0400) + tileX = 7 - tileX; + if(data & 0x0800) + tileY = 7 - tileY; + + u8 c = charBase[tile * 64 + tileY * 8 + tileX]; + + u16 color = palette[c]; + + *bmp++ = ((color >> 10) & 0x1f) << 3; + *bmp++ = ((color >> 5) & 0x1f) << 3; + *bmp++ = (color & 0x1f) << 3; + + if(data & 0x0400) { + if(tileX == 0) + screenSource++; + } else if(tileX == 7) + screenSource++; + if(x == 255 && sizeX > 256) { + screenSource = screenBase + 0x400 + ((yy>>3)*32); + } + } + } + } else { + for(int y = 0; y < sizeY; y++) { + int yy = y & 255; + + if(y == 256 && sizeY > 256) { + screenBase += 0x400; + if(sizeX > 256) + screenBase += 0x400; + } + u16 *screenSource = screenBase + ((yy>>3)*32); + + for(int x = 0; x < sizeX; x++) { + u16 data = *screenSource; + + int tile = data & 0x3FF; + int tileX = (x & 7); + int tileY = y & 7; + + if(data & 0x0400) + tileX = 7 - tileX; + if(data & 0x0800) + tileY = 7 - tileY; + + u8 color = charBase[tile * 32 + tileY * 4 + (tileX>>1)]; + + if(tileX & 1) { + color = (color >> 4); + } else { + color &= 0x0F; + } + + int pal = (*screenSource>>8) & 0xF0; + u16 color2 = palette[pal + color]; + + *bmp++ = ((color2 >> 10) & 0x1f) << 3; + *bmp++ = ((color2 >> 5) & 0x1f) << 3; + *bmp++ = (color2 & 0x1f) << 3; + + if(data & 0x0400) { + if(tileX == 0) + screenSource++; + } else if(tileX == 7) + screenSource++; + + if(x == 255 && sizeX > 256) { + screenSource = screenBase + 0x400 + ((yy>>3)*32); + } + } + } + } + /* + switch(bg) { + case 0: + renderView(BG0HOFS<<8, BG0VOFS<<8, + 0x100, 0x000, + 0x000, 0x100, + (sizeX -1) <<8, + (sizeY -1) << 8, + true); + break; + case 1: + renderView(BG1HOFS<<8, BG1VOFS<<8, + 0x100, 0x000, + 0x000, 0x100, + (sizeX -1) <<8, + (sizeY -1) << 8, + true); + break; + case 2: + renderView(BG2HOFS<<8, BG2VOFS<<8, + 0x100, 0x000, + 0x000, 0x100, + (sizeX -1) <<8, + (sizeY -1) << 8, + true); + break; + case 3: + renderView(BG3HOFS<<8, BG3VOFS<<8, + 0x100, 0x000, + 0x000, 0x100, + (sizeX -1) <<8, + (sizeY -1) << 8, + true); + break; + } + */ +} + +void MapView::renderRotScreen(u16 control) +{ + u16 *palette = (u16 *)paletteRAM; + u8 *charBase = &vram[((control >> 2) & 0x03) * 0x4000]; + u8 *screenBase = (u8 *)&vram[((control >> 8) & 0x1f) * 0x800]; + u8 *bmp = data; + + int sizeX = 128; + int sizeY = 128; + switch((control >> 14) & 3) { + case 0: + break; + case 1: + sizeX = sizeY = 256; + break; + case 2: + sizeX = sizeY = 512; + break; + case 3: + sizeX = sizeY = 1024; + break; + } + + w = sizeX; + h = sizeY; + + for(int y = 0; y < sizeY; y++) { + for(int x = 0; x < sizeX; x++) { + int tile = screenBase[(x>>3) + (y>>3)*(w>>3)]; + + int tileX = (x & 7); + int tileY = y & 7; + + u8 color = charBase[tile * 64 + tileY * 8 + tileX]; + u16 color2 = palette[color]; + + *bmp++ = ((color2 >> 10) & 0x1f) << 3; + *bmp++ = ((color2 >> 5) & 0x1f) << 3; + *bmp++ = (color2 & 0x1f) << 3; + } + } + + u32 xx; + u32 yy; + + switch(bg) { + case 2: + xx = BG2X_L | BG2X_H << 16; + yy = BG2Y_L | BG2Y_H << 16; + + /* + renderView(xx, yy, + BG2PA, BG2PC, + BG2PB, BG2PD, + (sizeX -1) <<8, + (sizeY -1) << 8, + (control & 0x2000) != 0); + */ + break; + case 3: + xx = BG3X_L | BG3X_H << 16; + yy = BG3Y_L | BG3Y_H << 16; + /* + renderView(xx, yy, + BG3PA, BG3PC, + BG3PB, BG3PD, + (sizeX -1) <<8, + (sizeY -1) << 8, + (control & 0x2000) != 0); + */ + break; + } +} + +void MapView::renderMode0() +{ + renderTextScreen(control); +} + +void MapView::renderMode1() +{ + switch(bg) { + case 0: + case 1: + renderTextScreen(control); + break; + case 2: + renderRotScreen(control); + break; + default: + bg = 0; + control = BG0CNT; + renderTextScreen(control); + break; + } +} + +void MapView::renderMode2() +{ + switch(bg) { + case 2: + case 3: + renderRotScreen(control); + break; + default: + bg = 2; + control = BG2CNT; + renderRotScreen(control); + break; + } +} + +void MapView::renderMode3() +{ + u8 *bmp = data; + u16 *src = (u16 *)&vram[0]; + + w = 240; + h = 160; + + for(int y = 0; y < 160; y++) { + for(int x = 0; x < 240; x++) { + u16 data = *src++; + *bmp++ = ((data >> 10) & 0x1f) << 3; + *bmp++ = ((data >> 5) & 0x1f) << 3; + *bmp++ = (data & 0x1f) << 3; + } + } + bg = 2; +} + + +void MapView::renderMode4() +{ + u8 *bmp = data; + u8 *src = frame ? &vram[0xa000] : &vram[0]; + u16 *pal = (u16 *)&paletteRAM[0]; + + w = 240; + h = 160; + + for(int y = 0; y < 160; y++) { + for(int x = 0; x < 240; x++) { + u8 c = *src++; + u16 data = pal[c]; + *bmp++ = ((data >> 10) & 0x1f) << 3; + *bmp++ = ((data >> 5) & 0x1f) << 3; + *bmp++ = (data & 0x1f) << 3; + } + } + bg = 2; +} + + +void MapView::renderMode5() +{ + u8 *bmp = data; + u16 *src = (u16 *)(frame ? &vram[0xa000] : &vram[0]); + + w = 160; + h = 128; + + for(int y = 0; y < 128; y++) { + for(int x = 0; x < 160; x++) { + u16 data = *src++; + *bmp++ = ((data >> 10) & 0x1f) << 3; + *bmp++ = ((data >> 5) & 0x1f) << 3; + *bmp++ = (data & 0x1f) << 3; + } + } + bg = 2; +} + + +void MapView::OnRefresh() +{ + paint(); +} + +void MapView::paint() +{ + if(vram == NULL) + return; + int mode = DISPCNT & 7; + + switch(bg) { + default: + case 0: + control = BG0CNT; + break; + case 1: + control = BG1CNT; + break; + case 2: + control = BG2CNT; + break; + case 3: + control = BG3CNT; + break; + } + + switch(mode) { + case 0: + renderMode0(); + break; + case 1: + renderMode1(); + break; + case 2: + renderMode2(); + break; + case 3: + renderMode3(); + break; + case 4: + renderMode4(); + break; + case 5: + renderMode5(); + break; + case 6: + renderMode5(); + break; + case 7: + renderMode5(); + break; + } + enableButtons(mode); + SIZE s; + + if(mapView.getStretch()) { + mapView.setSize(w, h); + s.cx = s.cy = 1; + mapView.SetScrollSizes(MM_TEXT, s); + } else { + mapView.setSize(w, h); + s.cx = w; + s.cy = h; + mapView.SetScrollSizes(MM_TEXT, s); + } + + mapView.refresh(); + + CString buffer; + + u32 charBase = ((control >> 2) & 0x03) * 0x4000 + 0x6000000; + u32 screenBase = ((control >> 8) & 0x1f) * 0x800 + 0x6000000; + + buffer.Format("%d", mode); + m_mode.SetWindowText(buffer); + + if(mode >= 3) { + m_mapbase.SetWindowText(""); + m_charbase.SetWindowText(""); + } else { + buffer.Format("0x%08X", screenBase); + m_mapbase.SetWindowText(buffer); + + buffer.Format("0x%08X", charBase); + m_charbase.SetWindowText(buffer); + } + + buffer.Format("%dx%d", w, h); + m_dim.SetWindowText(buffer); + + m_numcolors.SetWindowText(control & 0x80 ? "256" : "16"); + + buffer.Format("%d", control & 3); + m_priority.SetWindowText(buffer); + + m_mosaic.SetWindowText(control & 0x40 ? "1" : "0"); + + m_overflow.SetWindowText(bg <= 1 ? "" : + control & 0x2000 ? "1" : "0"); +} + +BOOL MapView::OnInitDialog() +{ + CDialog::OnInitDialog(); + + DIALOG_SIZER_START( sz ) + DIALOG_SIZER_ENTRY( IDC_MAP_VIEW, DS_SizeX | DS_SizeY ) + DIALOG_SIZER_ENTRY( IDC_REFRESH, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_CLOSE, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_SAVE, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_COLOR, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_R, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_G, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_B, DS_MoveY) + DIALOG_SIZER_END() + SetData(sz, + TRUE, + HKEY_CURRENT_USER, + "Software\\Emulators\\VisualBoyAdvance\\Viewer\\MapView", + NULL); + SIZE size; + size.cx = 1; + size.cy = 1; + mapView.SetScrollSizes(MM_TEXT,size); + int s = regQueryDwordValue("mapViewStretch", 0); + if(s) + mapView.setStretch(true); + ((CButton *)GetDlgItem(IDC_STRETCH))->SetCheck(s); + paint(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void MapView::PostNcDestroy() +{ + delete this; +} + +void MapView::enableButtons(int mode) +{ + bool enable[6] = { true, true, true, true, true, true }; + + switch(mode) { + case 0: + enable[4] = false; + enable[5] = false; + break; + case 1: + enable[3] = false; + enable[4] = false; + enable[5] = false; + break; + case 2: + enable[0] = false; + enable[1] = false; + enable[4] = false; + enable[5] = false; + break; + case 3: + enable[0] = false; + enable[1] = false; + enable[2] = false; + enable[3] = false; + enable[4] = false; + enable[5] = false; + break; + case 4: + enable[0] = false; + enable[1] = false; + enable[2] = false; + enable[3] = false; + break; + case 5: + enable[0] = false; + enable[1] = false; + enable[2] = false; + enable[3] = false; + break; + } + GetDlgItem(IDC_BG0)->EnableWindow(enable[0]); + GetDlgItem(IDC_BG1)->EnableWindow(enable[1]); + GetDlgItem(IDC_BG2)->EnableWindow(enable[2]); + GetDlgItem(IDC_BG3)->EnableWindow(enable[3]); + GetDlgItem(IDC_FRAME_0)->EnableWindow(enable[4]); + GetDlgItem(IDC_FRAME_1)->EnableWindow(enable[5]); + int id = IDC_BG0; + switch(bg) { + case 1: + id = IDC_BG1; + break; + case 2: + id = IDC_BG2; + break; + case 3: + id = IDC_BG3; + break; + } + CheckRadioButton(IDC_BG0, IDC_BG3, id); + id = IDC_FRAME_0; + if(frame != 0) + id = IDC_FRAME_1; + CheckRadioButton(IDC_FRAME_0, IDC_FRAME_1, id); +} + +void MapView::OnFrame0() +{ + frame = 0; + paint(); +} + +void MapView::OnFrame1() +{ + frame = 1; + paint(); +} + +void MapView::OnBg0() +{ + bg = 0; + control = BG0CNT; + paint(); +} + +void MapView::OnBg1() +{ + bg = 1; + control = BG1CNT; + paint(); +} + +void MapView::OnBg2() +{ + bg = 2; + control = BG2CNT; + paint(); +} + +void MapView::OnBg3() +{ + bg = 3; + control = BG3CNT; + paint(); +} + +void MapView::OnStretch() +{ + mapView.setStretch(!mapView.getStretch()); + paint(); + regSetDwordValue("mapViewStretch", mapView.getStretch()); +} + +void MapView::OnAutoUpdate() +{ + autoUpdate = !autoUpdate; + if(autoUpdate) { + theApp.winAddUpdateListener(this); + } else { + theApp.winRemoveUpdateListener(this); + } + (CButton*)GetDlgItem(IDC_REFRESH)->EnableWindow(autoUpdate ? FALSE : TRUE); +} + +void MapView::update() +{ + paint(); +} + +void MapView::OnClose() +{ + theApp.winRemoveUpdateListener(this); + + DestroyWindow(); +} + +u32 MapView::GetTextClickAddress(u32 base, int x, int y) +{ + if(y > 255 && h > 256) { + base += 0x800; + if(w > 256) + base += 0x800; + } + if(x >= 256) + base += 0x800; + x &= 255; + y &= 255; + base += (x>>3)*2 + 64*(y>>3); + + return base; +} + + + +u32 MapView::GetClickAddress(int x, int y) +{ + int mode = DISPCNT & 7; + + u32 base = ((control >> 8) & 0x1f) * 0x800 + 0x6000000; + + // all text bgs (16 bits) + if(mode == 0 ||(mode < 3 && bg < 2) || mode == 6 || mode == 7) { + return GetTextClickAddress(base, x, y); + } + // rot bgs (8 bits) + if(mode < 3) { + return base + (x>>3) + (w>>3)*(y>>3); + } + // mode 3/5 (16 bits) + if(mode != 4) { + return 0x6000000 + 0xa000*frame + 2*x + w*y*2; + } + // mode 4 (8 bits) + return 0x6000000 + 0xa000*frame + x + w*y; +} + +LRESULT MapView::OnMapInfo(WPARAM wParam, LPARAM lParam) +{ + u8 *colors = (u8 *)lParam; + mapViewZoom.setColors(colors); + + int x = (int)(wParam & 0xffff); + int y = (int)(wParam >> 16); + + CString buffer; + buffer.Format("(%d,%d)", x, y); + GetDlgItem(IDC_XY)->SetWindowText(buffer); + + u32 address = GetClickAddress(x,y); + buffer.Format("0x%08X", address); + GetDlgItem(IDC_ADDRESS)->SetWindowText(buffer); + + int mode = DISPCNT & 7; + if(mode >= 3 && mode <=5) { + // bitmap modes + GetDlgItem(IDC_TILE_NUM)->SetWindowText("---"); + GetDlgItem(IDC_FLIP)->SetWindowText("--"); + GetDlgItem(IDC_PALETTE_NUM)->SetWindowText("---"); + } else if(mode == 0 || bg < 2) { + // text bgs + u16 value = *((u16 *)&vram[address - 0x6000000]); + + int tile = value & 1023; + buffer.Format("%d", tile); + GetDlgItem(IDC_TILE_NUM)->SetWindowText(buffer); + buffer.Empty(); + buffer += value & 1024 ? 'H' : '-'; + buffer += value & 2048 ? 'V' : '-'; + GetDlgItem(IDC_FLIP)->SetWindowText(buffer); + + if(!(control & 0x80)) { + buffer.Format("%d", (value >> 12) & 15); + } else + buffer = "---"; + GetDlgItem(IDC_PALETTE_NUM)->SetWindowText(buffer); + } else { + // rot bgs + GetDlgItem(IDC_TILE_NUM)->SetWindowText("---"); + GetDlgItem(IDC_FLIP)->SetWindowText("--"); + GetDlgItem(IDC_PALETTE_NUM)->SetWindowText("---"); + } + + return TRUE; +} + +LRESULT MapView::OnColInfo(WPARAM wParam, LPARAM lParam) +{ + u16 c = (u16)wParam; + + color.setColor(c); + + int r = (c & 0x1f); + int g = (c & 0x3e0) >> 5; + int b = (c & 0x7c00) >> 10; + + CString buffer; + buffer.Format("R: %d", r); + GetDlgItem(IDC_R)->SetWindowText(buffer); + + buffer.Format("G: %d", g); + GetDlgItem(IDC_G)->SetWindowText(buffer); + + buffer.Format("B: %d", b); + GetDlgItem(IDC_B)->SetWindowText(buffer); + + return TRUE; +} + +void MapView::saveBMP(const char *name) +{ + u8 writeBuffer[1024 * 3]; + + FILE *fp = fopen(name,"wb"); + + if(!fp) { + systemMessage(MSG_ERROR_CREATING_FILE, "Error creating file %s", name); + return; + } + + struct { + u8 ident[2]; + u8 filesize[4]; + u8 reserved[4]; + u8 dataoffset[4]; + u8 headersize[4]; + u8 width[4]; + u8 height[4]; + u8 planes[2]; + u8 bitsperpixel[2]; + u8 compression[4]; + u8 datasize[4]; + u8 hres[4]; + u8 vres[4]; + u8 colors[4]; + u8 importantcolors[4]; + u8 pad[2]; + } bmpheader; + memset(&bmpheader, 0, sizeof(bmpheader)); + + bmpheader.ident[0] = 'B'; + bmpheader.ident[1] = 'M'; + + u32 fsz = sizeof(bmpheader) + w*h*3; + utilPutDword(bmpheader.filesize, fsz); + utilPutDword(bmpheader.dataoffset, 0x38); + utilPutDword(bmpheader.headersize, 0x28); + utilPutDword(bmpheader.width, w); + utilPutDword(bmpheader.height, h); + utilPutDword(bmpheader.planes, 1); + utilPutDword(bmpheader.bitsperpixel, 24); + utilPutDword(bmpheader.datasize, 3*w*h); + + fwrite(&bmpheader, 1, sizeof(bmpheader), fp); + + u8 *b = writeBuffer; + + int sizeX = w; + int sizeY = h; + + u8 *pixU8 = (u8 *)data+3*w*(h-1); + for(int y = 0; y < sizeY; y++) { + for(int x = 0; x < sizeX; x++) { + *b++ = *pixU8++; // B + *b++ = *pixU8++; // G + *b++ = *pixU8++; // R + } + pixU8 -= 2*3*w; + fwrite(writeBuffer, 1, 3*w, fp); + + b = writeBuffer; + } + + fclose(fp); +} + + + +void MapView::savePNG(const char *name) +{ + u8 writeBuffer[1024 * 3]; + + FILE *fp = fopen(name,"wb"); + + if(!fp) { + systemMessage(MSG_ERROR_CREATING_FILE, "Error creating file %s", name); + return; + } + + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, + NULL, + NULL, + NULL); + if(!png_ptr) { + fclose(fp); + return; + } + + png_infop info_ptr = png_create_info_struct(png_ptr); + + if(!info_ptr) { + png_destroy_write_struct(&png_ptr,NULL); + fclose(fp); + return; + } + + if(setjmp(png_ptr->jmpbuf)) { + png_destroy_write_struct(&png_ptr,NULL); + fclose(fp); + return; + } + + png_init_io(png_ptr,fp); + + png_set_IHDR(png_ptr, + info_ptr, + w, + h, + 8, + PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + png_write_info(png_ptr,info_ptr); + + u8 *b = writeBuffer; + + int sizeX = w; + int sizeY = h; + + u8 *pixU8 = (u8 *)data; + for(int y = 0; y < sizeY; y++) { + for(int x = 0; x < sizeX; x++) { + int blue = *pixU8++; + int green = *pixU8++; + int red = *pixU8++; + + *b++ = red; + *b++ = green; + *b++ = blue; + } + png_write_row(png_ptr,writeBuffer); + + b = writeBuffer; + } + + png_write_end(png_ptr, info_ptr); + + png_destroy_write_struct(&png_ptr, &info_ptr); + + fclose(fp); +} + +void MapView::OnSave() +{ + if(rom != NULL) + { + CString filename; + + if(theApp.captureFormat == 0) + filename = "map.png"; + else + filename = "map.bmp"; + + LPCTSTR exts[] = {".png", ".bmp" }; + + CString filter = theApp.winLoadFilter(IDS_FILTER_PNG); + CString title = winResLoadString(IDS_SELECT_CAPTURE_NAME); + + FileDlg dlg(this, + filename, + filter, + theApp.captureFormat ? 2 : 1, + theApp.captureFormat ? "BMP" : "PNG", + exts, + "", + title, + true); + + if(dlg.DoModal() == IDCANCEL) { + return; + } + + if(dlg.getFilterIndex() == 2) + saveBMP(dlg.GetPathName()); + else + savePNG(dlg.GetPathName()); + } +} diff --git a/src/win32/MapView.h b/src/win32/MapView.h new file mode 100644 index 0000000..a19af6b --- /dev/null +++ b/src/win32/MapView.h @@ -0,0 +1,103 @@ +#if !defined(AFX_MAPVIEW_H__20F40C77_8E10_44B7_BB49_7865F73C3E75__INCLUDED_) +#define AFX_MAPVIEW_H__20F40C77_8E10_44B7_BB49_7865F73C3E75__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// MapView.h : header file +// + +#include "BitmapControl.h" +#include "ColorControl.h" +#include "ZoomControl.h" +#include "ResizeDlg.h" +#include "IUpdate.h" +#include "../System.h" // Added by ClassView + +///////////////////////////////////////////////////////////////////////////// +// MapView dialog + +class MapView : public ResizeDlg, IUpdateListener +{ + private: + BITMAPINFO bmpInfo; + u8 *data; + int frame; + u16 control; + int bg; + int w; + int h; + BitmapControl mapView; + ZoomControl mapViewZoom; + ColorControl color; + bool autoUpdate; + + // Construction + public: + void savePNG(const char *name); + void saveBMP(const char *name); + afx_msg LRESULT OnColInfo(WPARAM wParam, LPARAM lParam); + afx_msg LRESULT OnMapInfo(WPARAM wParam, LPARAM lParam); + u32 GetClickAddress(int x, int y); + u32 GetTextClickAddress(u32 base, int x, int y); + void update(); + void enableButtons(int mode); + void paint(); + void renderMode5(); + void renderMode4(); + void renderMode3(); + void renderMode2(); + void renderMode1(); + void renderMode0(); + void renderRotScreen(u16 control); + void renderTextScreen(u16 control); + MapView(CWnd* pParent = NULL); // standard constructor + ~MapView(); + + // Dialog Data + //{{AFX_DATA(MapView) + enum { IDD = IDD_MAP_VIEW }; + CStatic m_numcolors; + CStatic m_mode; + CStatic m_overflow; + CStatic m_mosaic; + CStatic m_priority; + CStatic m_dim; + CStatic m_charbase; + CStatic m_mapbase; + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(MapView) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual void PostNcDestroy(); + //}}AFX_VIRTUAL + + // Implementation + protected: + + // Generated message map functions + //{{AFX_MSG(MapView) + afx_msg void OnRefresh(); + virtual BOOL OnInitDialog(); + afx_msg void OnFrame0(); + afx_msg void OnFrame1(); + afx_msg void OnBg0(); + afx_msg void OnBg1(); + afx_msg void OnBg2(); + afx_msg void OnBg3(); + afx_msg void OnStretch(); + afx_msg void OnAutoUpdate(); + afx_msg void OnClose(); + afx_msg void OnSave(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + + //{{AFX_INSERT_LOCATION}} + // Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_MAPVIEW_H__20F40C77_8E10_44B7_BB49_7865F73C3E75__INCLUDED_) diff --git a/src/win32/MaxScale.cpp b/src/win32/MaxScale.cpp new file mode 100644 index 0000000..45d51d0 --- /dev/null +++ b/src/win32/MaxScale.cpp @@ -0,0 +1,68 @@ +#include "stdafx.h" +#include "vba.h" +#include "MaxScale.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// MaxScale dialog + + +MaxScale::MaxScale(CWnd* pParent /*=NULL*/) + : CDialog(MaxScale::IDD, pParent) +{ + //{{AFX_DATA_INIT(MaxScale) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT +} + + +void MaxScale::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(MaxScale) + DDX_Control(pDX, IDC_VALUE, m_value); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(MaxScale, CDialog) + //{{AFX_MSG_MAP(MaxScale) + ON_BN_CLICKED(ID_OK, OnOk) + ON_BN_CLICKED(ID_CANCEL, OnCancel) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// MaxScale message handlers + +void MaxScale::OnCancel() +{ + EndDialog(FALSE); +} + +void MaxScale::OnOk() +{ + CString tmp; + m_value.GetWindowText(tmp); + theApp.maxScale = atoi(tmp); + EndDialog(TRUE); +} + +BOOL MaxScale::OnInitDialog() +{ + CDialog::OnInitDialog(); + + CString temp; + + temp.Format("%d", theApp.maxScale); + + m_value.SetWindowText(temp); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} diff --git a/src/win32/MaxScale.h b/src/win32/MaxScale.h new file mode 100644 index 0000000..ba8cbe9 --- /dev/null +++ b/src/win32/MaxScale.h @@ -0,0 +1,48 @@ +#if !defined(AFX_MAXSCALE_H__3F42C0CC_DD5E_4A96_A60D_33AB7CBDE406__INCLUDED_) +#define AFX_MAXSCALE_H__3F42C0CC_DD5E_4A96_A60D_33AB7CBDE406__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// MaxScale.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// MaxScale dialog + +class MaxScale : public CDialog +{ +// Construction +public: + MaxScale(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(MaxScale) + enum { IDD = IDD_MAX_SCALE }; + CEdit m_value; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(MaxScale) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(MaxScale) + afx_msg void OnCancel(); + afx_msg void OnOk(); + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_MAXSCALE_H__3F42C0CC_DD5E_4A96_A60D_33AB7CBDE406__INCLUDED_) diff --git a/src/win32/MemoryViewer.cpp b/src/win32/MemoryViewer.cpp new file mode 100644 index 0000000..eb321b1 --- /dev/null +++ b/src/win32/MemoryViewer.cpp @@ -0,0 +1,601 @@ +#include "stdafx.h" +#include "vba.h" +#include "MemoryViewer.h" + +#include "../System.h" +extern int emulating; + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// MemoryViewer + +bool MemoryViewer::isRegistered = false; + +MemoryViewer::MemoryViewer() +{ + address = 0; + addressSize = 0; + dataSize = 0; + editAddress = 0; + editNibble = 0; + displayedLines = 0; + hasCaret = false; + maxNibble = 0; + font = (HFONT)GetStockObject(SYSTEM_FIXED_FONT); + fontSize.cx = fontSize.cy = 0; + beginAscii = 0; + beginHex = 0; + dlg = NULL; + registerClass(); +} + +MemoryViewer::~MemoryViewer() +{ +} + + +BEGIN_MESSAGE_MAP(MemoryViewer, CWnd) + //{{AFX_MSG_MAP(MemoryViewer) + ON_WM_ERASEBKGND() + ON_WM_PAINT() + ON_WM_VSCROLL() + ON_WM_GETDLGCODE() + ON_WM_LBUTTONDOWN() + ON_WM_SETFOCUS() + ON_WM_KILLFOCUS() + ON_WM_KEYDOWN() + //}}AFX_MSG_MAP + ON_MESSAGE(WM_CHAR, OnWMChar) + END_MESSAGE_MAP() + + + ///////////////////////////////////////////////////////////////////////////// +// MemoryViewer message handlers + +void MemoryViewer::setDialog(IMemoryViewerDlg *d) +{ + dlg = d; +} + + +void MemoryViewer::setAddress(u32 a) +{ + address = a; + if(displayedLines) { + if(addressSize) { + u16 addr = address; + if((u16)(addr+(displayedLines<<4)) < addr) { + address = 0xffff - (displayedLines<<4) + 1; + } + } else { + if((address+(displayedLines<<4)) < address) { + address = 0xffffffff - (displayedLines<<4) + 1; + } + } + } + if(addressSize) + address &= 0xffff; + setCaretPos(); + InvalidateRect(NULL, TRUE); +} + + +void MemoryViewer::setSize(int s) +{ + dataSize = s; + if(s == 0) + maxNibble = 1; + else if(s == 1) + maxNibble = 3; + else + maxNibble = 7; + + InvalidateRect(NULL, TRUE); +} + +BOOL MemoryViewer::OnEraseBkgnd(CDC* pDC) +{ + return TRUE; +} + +void MemoryViewer::updateScrollInfo(int lines) +{ + int page = lines * 16; + SCROLLINFO si; + ZeroMemory(&si, sizeof(si)); + si.cbSize = sizeof(si); + si.fMask = SIF_PAGE | SIF_RANGE | SIF_DISABLENOSCROLL | SIF_POS; + si.nMin = 0; + if(addressSize) { + si.nMax = 0x10000/page; + si.nPage = 1; + } else { + si.nMax = 0xa000000 / page; + si.nPage = page; + } + + si.nPos = address / page; + SetScrollInfo(SB_VERT, + &si, + TRUE); +} + +void MemoryViewer::OnPaint() +{ + CPaintDC dc(this); // device context for painting + + RECT rect; + GetClientRect(&rect); + int w = rect.right - rect.left; + int h = rect.bottom - rect.top - 6; + + CDC memDC; + memDC.CreateCompatibleDC(&dc); + CBitmap bitmap, *pOldBitmap; + bitmap.CreateCompatibleBitmap(&dc, w, rect.bottom - rect.top); + pOldBitmap = memDC.SelectObject(&bitmap); + + memDC.FillRect(&rect, CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH))); + memDC.DrawEdge(&rect, EDGE_ETCHED, BF_RECT); + + CFont *oldFont = memDC.SelectObject(CFont::FromHandle(font)); + + fontSize = memDC.GetTextExtent("0", 1); + + int lines = h / fontSize.cy; + + displayedLines = lines; + + updateScrollInfo(lines); + + u32 addr = address; + + memDC.SetTextColor(RGB(0,0,0)); + + u8 data[32]; + + RECT r; + r.top = 3; + r.left = 3; + r.bottom = r.top+fontSize.cy; + r.right = rect.right-3; + + int line = 0; + + for(int i = 0; i < lines; i++) { + CString buffer; + if(addressSize) + buffer.Format("%04X", addr); + else + buffer.Format("%08X", addr); + memDC.DrawText(buffer, &r, DT_TOP | DT_LEFT | DT_NOPREFIX); + r.left += 10*fontSize.cx; + beginHex = r.left; + readData(addr, 16, data); + + int j; + + if(dataSize == 0) { + for(j = 0; j < 16; j++) { + buffer.Format("%02X", data[j]); + memDC.DrawText(buffer, &r, DT_TOP | DT_LEFT | DT_NOPREFIX); + r.left += 3*fontSize.cx; + } + } + if(dataSize == 1) { + for(j = 0; j < 16; j += 2) { + buffer.Format("%04X", data[j] | data[j+1]<<8); + memDC.DrawText(buffer, &r, DT_TOP | DT_LEFT | DT_NOPREFIX); + r.left += 5*fontSize.cx; + } + } + if(dataSize == 2) { + for(j = 0; j < 16; j += 4) { + buffer.Format("%08X", data[j] | data[j+1]<<8 | + data[j+2] << 16 | data[j+3] << 24); + memDC.DrawText(buffer, &r, DT_TOP | DT_LEFT | DT_NOPREFIX); + r.left += 9*fontSize.cx; + } + } + + line = r.left; + + r.left += fontSize.cx; + beginAscii = r.left; + buffer.Empty(); + for(j = 0; j < 16; j++) { + char c = data[j]; + if(c >= 32 && c <= 127) { + buffer += c; + } else + buffer += '.'; + } + + memDC.DrawText(buffer, &r, DT_TOP | DT_LEFT | DT_NOPREFIX); + addr += 16; + if(addressSize) + addr &= 0xffff; + r.top += fontSize.cy; + r.bottom += fontSize.cy; + r.left = 3; + } + CPen pen; + pen.CreatePen(PS_SOLID, 1, RGB(0,0,0)); + CPen *old = memDC.SelectObject(&pen); + + memDC.MoveTo(3+fontSize.cx*9, 3); + memDC.LineTo(3+fontSize.cx*9, 3+displayedLines*fontSize.cy); + + memDC.MoveTo(line, 3); + memDC.LineTo(line, 3+displayedLines*fontSize.cy); + + memDC.SelectObject(old); + pen.DeleteObject(); + + memDC.SelectObject(oldFont); + + dc.BitBlt(0, 0, w, rect.bottom - rect.top, &memDC, 0, 0, SRCCOPY); + + memDC.SelectObject(pOldBitmap); + memDC.DeleteDC(); + bitmap.DeleteObject(); +} + +void MemoryViewer::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) +{ + int address = this->address; + switch(nSBCode) { + case SB_BOTTOM: + address = 0xffffff00; + break; + case SB_LINEDOWN: + address += 0x10; + break; + case SB_LINEUP: + address -= 0x10; + break; + case SB_PAGEDOWN: + address += (displayedLines<<4); + break; + case SB_PAGEUP: + address -= (displayedLines<<4); + break; + case SB_TOP: + address = 0; + break; + case SB_THUMBTRACK: + { + int page = displayedLines * 16; + SCROLLINFO si; + ZeroMemory(&si, sizeof(si)); + si.cbSize = sizeof(si); + si.fMask = SIF_TRACKPOS; + GetScrollInfo(SB_VERT, &si); + address = page * si.nTrackPos; + } + break; + } + setAddress(address); +} + +UINT MemoryViewer::OnGetDlgCode() +{ + return DLGC_WANTALLKEYS; +} + +void MemoryViewer::createEditCaret(int w, int h) +{ + if(!hasCaret || caretWidth != w || caretHeight != h) { + hasCaret = true; + caretWidth = w; + caretHeight = h; + ::CreateCaret(m_hWnd, (HBITMAP)0, w, h); + } +} + + +void MemoryViewer::destroyEditCaret() +{ + hasCaret = false; + DestroyCaret(); +} + +void MemoryViewer::setCaretPos() +{ + if(GetFocus() != this) { + destroyEditCaret(); + return; + } + + if(dlg) + dlg->setCurrentAddress(editAddress); + + if(editAddress < address || editAddress > (address -1 + (displayedLines<<4))) { + destroyEditCaret(); + return; + } + + int subAddress = (editAddress - address); + + int x = 3+10*fontSize.cx+editNibble*fontSize.cx; + int y = 3+fontSize.cy*((editAddress-address)>>4); + + if(editAscii) { + x = beginAscii + fontSize.cx*(subAddress&15); + } else { + switch(dataSize) { + case 0: + x += 3*fontSize.cx*(subAddress & 15); + break; + case 1: + x += 5*fontSize.cx*((subAddress>>1) & 7); + break; + case 2: + x += 9*fontSize.cx*((subAddress>>2) & 3); + break; + } + } + + RECT r; + GetClientRect(&r); + r.right -= 3; + if(x >= r.right) { + destroyEditCaret(); + return; + } + int w = fontSize.cx; + if((x+fontSize.cx)>=r.right) + w = r.right - x; + createEditCaret(w, fontSize.cy); + ::SetCaretPos(x, y); + ShowCaret(); +} + +void MemoryViewer::OnLButtonDown(UINT nFlags, CPoint point) +{ + int x = point.x; + int y = point.y; + int line = (y-3)/fontSize.cy; + int beforeAscii = beginHex; + int inc = 1; + int sub = 3*fontSize.cx; + switch(dataSize) { + case 0: + beforeAscii += 47*fontSize.cx; + break; + case 1: + beforeAscii += 39*fontSize.cx; + inc = 2; + sub = 5*fontSize.cx; + break; + case 2: + beforeAscii += 35*fontSize.cx; + inc = 4; + sub = 9*fontSize.cx; + break; + } + + editAddress = address + (line<<4); + if(x >= beginHex && x < beforeAscii) { + x -= beginHex; + editNibble = 0; + while(x > 0) { + x -= sub; + if(x >= 0) + editAddress += inc; + else { + editNibble = (x + sub)/fontSize.cx; + } + } + editAscii = false; + } else if(x >= beginAscii) { + int afterAscii = beginAscii+16*fontSize.cx; + if(x >= afterAscii) + x = afterAscii-1; + editAddress += (x-beginAscii)/fontSize.cx; + editNibble = 0; + editAscii = true; + } else { + return; + } + + if(editNibble > maxNibble) + editNibble = maxNibble; + SetFocus(); + setCaretPos(); +} + +void MemoryViewer::OnSetFocus(CWnd* pOldWnd) +{ + setCaretPos(); + InvalidateRect(NULL, TRUE); +} + +void MemoryViewer::OnKillFocus(CWnd* pNewWnd) +{ + destroyEditCaret(); + InvalidateRect(NULL, TRUE); +} + +void MemoryViewer::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) +{ + bool isShift = (GetKeyState(VK_SHIFT) & 0x80000000) == 0x80000000; + + switch(nChar) { + case VK_RIGHT: + if(editAscii) + moveAddress(1,0); + else if(isShift) + moveAddress((maxNibble+1)>>1,0); + else + moveAddress(0, 1); + break; + case VK_LEFT: + if(editAscii) + moveAddress(-1, 0); + else if(isShift) + moveAddress(-((maxNibble+1)>>1),0); + else + moveAddress(0, -1); + break; + case VK_DOWN: + moveAddress(16, 0); + break; + case VK_UP: + moveAddress(-16, 0); + break; + case VK_TAB: + GetNextDlgTabItem(GetParent(), isShift)->SetFocus(); + break; + } +} + +void MemoryViewer::moveAddress(s32 offset, int nibbleOff) +{ + if(offset == 0) { + if(nibbleOff == -1) { + editNibble--; + if(editNibble == -1) { + editAddress -= (maxNibble + 1) >> 1; + editNibble = maxNibble; + } + if(address == 0 && (editAddress >= (u32)(displayedLines<<4))) { + editAddress = 0; + editNibble = 0; + beep(); + } + if(editAddress < address) + setAddress(address - 16); + } else { + editNibble++; + if(editNibble > maxNibble) { + editNibble = 0; + editAddress += (maxNibble + 1) >> 1; + } + if(editAddress < address) { + editAddress -= (maxNibble + 1) >> 1; + editNibble = maxNibble; + beep(); + } + if(editAddress >= (address+(displayedLines<<4))) + setAddress(address+16); + } + } else { + editAddress += offset; + if(offset < 0 && editAddress > (address-1+(displayedLines<<4))) { + editAddress -= offset; + beep(); + return; + } + if(offset > 0 && (editAddress < address)) { + editAddress -= offset; + beep(); + return; + } + if(editAddress < address) { + if(offset & 15) + setAddress((address+offset-16) & ~15); + else + setAddress(address+offset); + } else if(editAddress > (address - 1 + (displayedLines<<4))) { + if(offset & 15) + setAddress((address+offset+16) & ~15); + else + setAddress(address+offset); + } + } + + setCaretPos(); +} + +LRESULT MemoryViewer::OnWMChar(WPARAM wParam, LPARAM LPARAM) +{ // The WM_CHAR message uses Unicode Transformation Format (UTF)-16. + if(OnEditInput((UINT)(wParam & 0xFFFF))) + return 0; + return 1; +} + +bool MemoryViewer::OnEditInput(UINT c) +{ + if(c > 255 || !emulating) { + beep(); + return false; + } + + if(!editAscii) + c = tolower(c); + + u32 value = 256; + + if(c >= 'a' && c <= 'f') + value = 10 + (c - 'a'); + else if(c >= '0' && c <= '9') + value = (c - '0'); + if(editAscii) { + editData(editAddress, 8, 0, c); + moveAddress(1, 0); + InvalidateRect(NULL, TRUE); + } else { + if(value != 256) { + value <<= 4*(maxNibble-editNibble); + u32 mask = ~(15 << 4*(maxNibble - editNibble)); + switch(dataSize) { + case 0: + editData(editAddress, 8, mask, value); + break; + case 1: + editData(editAddress, 16, mask, value); + break; + case 2: + editData(editAddress, 32, mask, value); + break; + } + moveAddress(0, 1); + InvalidateRect(NULL, TRUE); + } + } + return true; +} + +void MemoryViewer::beep() +{ + MessageBeep((UINT)-1); +} + +void MemoryViewer::registerClass() +{ + if(!isRegistered) { + WNDCLASS wc; + ZeroMemory(&wc, sizeof(wc)); + wc.style = CS_PARENTDC | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS; + wc.lpfnWndProc = (WNDPROC)::DefWindowProc; + wc.hInstance = AfxGetInstanceHandle(); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH )GetStockObject(WHITE_BRUSH); + wc.lpszMenuName = NULL; + wc.lpszClassName = "VbaMemoryViewer"; + AfxRegisterClass(&wc); + isRegistered = true; + } +} + +void MemoryViewer::setAddressSize(int s) +{ + addressSize = s; +} + + +u32 MemoryViewer::getCurrentAddress() +{ + return editAddress; +} + +int MemoryViewer::getSize() +{ + return dataSize; +} diff --git a/src/win32/MemoryViewer.h b/src/win32/MemoryViewer.h new file mode 100644 index 0000000..6f5d06b --- /dev/null +++ b/src/win32/MemoryViewer.h @@ -0,0 +1,95 @@ +#if !defined(AFX_MEMORYVIEWER_H__52C50474_5399_4D0B_A3E4_4C52C4E0EAA0__INCLUDED_) +#define AFX_MEMORYVIEWER_H__52C50474_5399_4D0B_A3E4_4C52C4E0EAA0__INCLUDED_ + +#include "../System.h" // Added by ClassView +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// MemoryViewer.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// MemoryViewer window + +class IMemoryViewerDlg { + public: + virtual void setCurrentAddress(u32 address)=0; +}; + +class MemoryViewer : public CWnd +{ + u32 address; + int addressSize; + int dataSize; + bool hasCaret; + int caretWidth; + int caretHeight; + HFONT font; + CSize fontSize; + u32 editAddress; + int editNibble; + int maxNibble; + int displayedLines; + int beginAscii; + int beginHex; + bool editAscii; + IMemoryViewerDlg *dlg; + + static bool isRegistered; + // Construction + public: + MemoryViewer(); + + // Attributes + public: + + // Operations + public: + virtual void readData(u32,int,u8 *) = 0; + virtual void editData(u32,int,int,u32)=0; + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(MemoryViewer) + //}}AFX_VIRTUAL + + // Implementation + public: + int getSize(); + u32 getCurrentAddress(); + void setAddressSize(int s); + void registerClass(); + void beep(); + bool OnEditInput(UINT c); + void moveAddress(s32 offset, int nibbleOff); + void setCaretPos(); + void destroyEditCaret(); + void createEditCaret(int w, int h); + void updateScrollInfo(int lines); + void setSize(int s); + void setAddress(u32 a); + void setDialog(IMemoryViewerDlg *d); + virtual ~MemoryViewer(); + + // Generated message map functions + protected: + //{{AFX_MSG(MemoryViewer) + afx_msg BOOL OnEraseBkgnd(CDC* pDC); + afx_msg void OnPaint(); + afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); + afx_msg UINT OnGetDlgCode(); + afx_msg void OnLButtonDown(UINT nFlags, CPoint point); + afx_msg void OnSetFocus(CWnd* pOldWnd); + afx_msg void OnKillFocus(CWnd* pNewWnd); + afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + afx_msg LRESULT OnWMChar(WPARAM wParam, LPARAM lParam); +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_MEMORYVIEWER_H__52C50474_5399_4D0B_A3E4_4C52C4E0EAA0__INCLUDED_) diff --git a/src/win32/MemoryViewerAddressSize.cpp b/src/win32/MemoryViewerAddressSize.cpp new file mode 100644 index 0000000..00c3071 --- /dev/null +++ b/src/win32/MemoryViewerAddressSize.cpp @@ -0,0 +1,116 @@ +#include "stdafx.h" +#include "vba.h" +#include "MemoryViewerAddressSize.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// MemoryViewerAddressSize dialog + + +MemoryViewerAddressSize::MemoryViewerAddressSize(u32 a, int s, CWnd* pParent /*=NULL*/) + : CDialog(MemoryViewerAddressSize::IDD, pParent) +{ + //{{AFX_DATA_INIT(MemoryViewerAddressSize) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT + address = a; + size = s; +} + + +void MemoryViewerAddressSize::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(MemoryViewerAddressSize) + DDX_Control(pDX, IDC_SIZE_CONTROL, m_size); + DDX_Control(pDX, IDC_ADDRESS, m_address); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(MemoryViewerAddressSize, CDialog) + //{{AFX_MSG_MAP(MemoryViewerAddressSize) + ON_BN_CLICKED(ID_OK, OnOk) + ON_BN_CLICKED(ID_CANCEL, OnCancel) + //}}AFX_MSG_MAP + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// MemoryViewerAddressSize message handlers + +BOOL MemoryViewerAddressSize::OnInitDialog() +{ + CDialog::OnInitDialog(); + + CString buffer; + if(address != 0xFFFFFFFF) { + buffer.Format("%08X", address); + m_address.SetWindowText(buffer); + } + if(size != -1) { + buffer.Format("%08X", size); + m_size.SetWindowText(buffer); + m_size.EnableWindow(FALSE); + } + + if(size == -1 && address != 0xFFFFFFFF) + m_size.SetFocus(); + + m_address.LimitText(9); + m_size.LimitText(9); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void MemoryViewerAddressSize::OnOk() +{ + CString buffer; + + m_address.GetWindowText(buffer); + if(buffer.IsEmpty()) { + m_address.SetFocus(); + return; + } + sscanf(buffer, "%x", &address); + + m_size.GetWindowText(buffer); + if(buffer.IsEmpty()) { + m_size.SetFocus(); + return; + } + sscanf(buffer, "%x", &size); + EndDialog(TRUE); +} + +void MemoryViewerAddressSize::OnCancel() +{ + EndDialog(FALSE); +} + +void MemoryViewerAddressSize::setAddress(u32 a) +{ + address = a; +} + +void MemoryViewerAddressSize::setSize(int s) +{ + size = s; +} + + +u32 MemoryViewerAddressSize::getAddress() +{ + return address; +} +\ + +int MemoryViewerAddressSize::getSize() +{ + return size; +} diff --git a/src/win32/MemoryViewerAddressSize.h b/src/win32/MemoryViewerAddressSize.h new file mode 100644 index 0000000..51af50f --- /dev/null +++ b/src/win32/MemoryViewerAddressSize.h @@ -0,0 +1,56 @@ +#if !defined(AFX_MEMORYVIEWERADDRESSSIZE_H__04605262_2B1D_4EED_A467_B6C56AC2CACD__INCLUDED_) +#define AFX_MEMORYVIEWERADDRESSSIZE_H__04605262_2B1D_4EED_A467_B6C56AC2CACD__INCLUDED_ + +#include "../System.h" // Added by ClassView +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// MemoryViewerAddressSize.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// MemoryViewerAddressSize dialog + +class MemoryViewerAddressSize : public CDialog +{ + u32 address; + int size; + // Construction + public: + int getSize(); + u32 getAddress(); + void setSize(int s); + void setAddress(u32 a); + MemoryViewerAddressSize(u32 a=0xffffff, int s=-1, CWnd* pParent = NULL); // standard constructor + + // Dialog Data + //{{AFX_DATA(MemoryViewerAddressSize) + enum { IDD = IDD_ADDR_SIZE }; + CEdit m_size; + CEdit m_address; + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(MemoryViewerAddressSize) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + + // Implementation + protected: + + // Generated message map functions + //{{AFX_MSG(MemoryViewerAddressSize) + virtual BOOL OnInitDialog(); + afx_msg void OnOk(); + afx_msg void OnCancel(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + + //{{AFX_INSERT_LOCATION}} + // Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_MEMORYVIEWERADDRESSSIZE_H__04605262_2B1D_4EED_A467_B6C56AC2CACD__INCLUDED_) diff --git a/src/win32/MemoryViewerDlg.cpp b/src/win32/MemoryViewerDlg.cpp new file mode 100644 index 0000000..b331160 --- /dev/null +++ b/src/win32/MemoryViewerDlg.cpp @@ -0,0 +1,409 @@ +#include "stdafx.h" +#include "vba.h" +#include "FileDlg.h" +#include "MemoryViewerAddressSize.h" +#include "MemoryViewerDlg.h" +#include "Reg.h" +#include "WinResUtil.h" + +#include "../System.h" +#include "../gba/GBA.h" +#include "../gba/Globals.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +extern int emulating; + +#define CPUReadByteQuick(addr) \ + ::map[(addr)>>24].address[(addr) & ::map[(addr)>>24].mask] +#define CPUWriteByteQuick(addr, b) \ + ::map[(addr)>>24].address[(addr) & ::map[(addr)>>24].mask] = (b) +#define CPUReadHalfWordQuick(addr) \ + *((u16 *)&::map[(addr)>>24].address[(addr) & ::map[(addr)>>24].mask]) +#define CPUWriteHalfWordQuick(addr, b) \ + *((u16 *)&::map[(addr)>>24].address[(addr) & ::map[(addr)>>24].mask]) = (b) +#define CPUReadMemoryQuick(addr) \ + *((u32 *)&::map[(addr)>>24].address[(addr) & ::map[(addr)>>24].mask]) +#define CPUWriteMemoryQuick(addr, b) \ + *((u32 *)&::map[(addr)>>24].address[(addr) & ::map[(addr)>>24].mask]) = (b) + +///////////////////////////////////////////////////////////////////////////// +// GBAMemoryViewer control + + +GBAMemoryViewer::GBAMemoryViewer() + : MemoryViewer() +{ + setAddressSize(0); +} + +void GBAMemoryViewer::readData(u32 address, int len, u8 *data) +{ + if(emulating && rom != NULL) { + for(int i = 0; i < len; i++) { + *data++ = CPUReadByteQuick(address); + address++; + } + } else { + for(int i = 0; i < len; i++) { + *data++ = 0; + address++; + } + } +} + +void GBAMemoryViewer::editData(u32 address, int size, int mask, u32 value) +{ + u32 oldValue; + + switch(size) { + case 8: + oldValue = (CPUReadByteQuick(address) & mask) | value; + CPUWriteByteQuick(address, oldValue); + break; + case 16: + oldValue = (CPUReadHalfWordQuick(address) & mask) | value; + CPUWriteHalfWordQuick(address, oldValue); + break; + case 32: + oldValue = (CPUReadMemoryQuick(address) & mask) | value; + CPUWriteMemoryQuick(address, oldValue); + break; + } +} + + +///////////////////////////////////////////////////////////////////////////// +// MemoryViewerDlg dialog + + +MemoryViewerDlg::MemoryViewerDlg(CWnd* pParent /*=NULL*/) + : ResizeDlg(MemoryViewerDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(MemoryViewerDlg) + m_size = -1; + //}}AFX_DATA_INIT + autoUpdate = false; +} + + +void MemoryViewerDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(MemoryViewerDlg) + DDX_Control(pDX, IDC_CURRENT_ADDRESS, m_current); + DDX_Control(pDX, IDC_ADDRESS, m_address); + DDX_Control(pDX, IDC_ADDRESSES, m_addresses); + DDX_Radio(pDX, IDC_8_BIT, m_size); + //}}AFX_DATA_MAP + DDX_Control(pDX, IDC_VIEWER, m_viewer); +} + + +BEGIN_MESSAGE_MAP(MemoryViewerDlg, CDialog) + //{{AFX_MSG_MAP(MemoryViewerDlg) + ON_BN_CLICKED(IDC_CLOSE, OnClose) + ON_BN_CLICKED(IDC_REFRESH, OnRefresh) + ON_BN_CLICKED(IDC_8_BIT, On8Bit) + ON_BN_CLICKED(IDC_16_BIT, On16Bit) + ON_BN_CLICKED(IDC_32_BIT, On32Bit) + ON_BN_CLICKED(IDC_AUTO_UPDATE, OnAutoUpdate) + ON_BN_CLICKED(IDC_GO, OnGo) + ON_CBN_SELCHANGE(IDC_ADDRESSES, OnSelchangeAddresses) + ON_BN_CLICKED(IDC_SAVE, OnSave) + ON_BN_CLICKED(IDC_LOAD, OnLoad) + //}}AFX_MSG_MAP + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// MemoryViewerDlg message handlers + +BOOL MemoryViewerDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + + DIALOG_SIZER_START( sz ) + DIALOG_SIZER_ENTRY( IDC_VIEWER, DS_SizeX | DS_SizeY ) + DIALOG_SIZER_ENTRY( IDC_REFRESH, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_CLOSE, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_LOAD, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_SAVE, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_AUTO_UPDATE, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_CURRENT_ADDRESS_LABEL, DS_MoveY | DS_MoveX) + DIALOG_SIZER_ENTRY( IDC_CURRENT_ADDRESS, DS_MoveY | DS_MoveX) + DIALOG_SIZER_END() + SetData(sz, + TRUE, + HKEY_CURRENT_USER, + "Software\\Emulators\\VisualBoyAdvance\\Viewer\\MemoryView", + NULL); + + m_viewer.setDialog(this); + m_viewer.ShowScrollBar(SB_VERT, TRUE); + m_viewer.EnableScrollBar(SB_VERT, ESB_ENABLE_BOTH); + + LPCTSTR s[] = { + "0x00000000 - BIOS", + "0x02000000 - WRAM", + "0x03000000 - IRAM", + "0x04000000 - I/O", + "0x05000000 - PALETTE", + "0x06000000 - VRAM", + "0x07000000 - OAM", + "0x08000000 - ROM" + }; + + for(int i = 0; i < 8; i++) + m_addresses.AddString(s[i]); + + m_addresses.SetCurSel(0); + + RECT cbSize; + int Height; + + m_addresses.GetClientRect(&cbSize); + Height = m_addresses.GetItemHeight(-1); + Height += m_addresses.GetItemHeight(0) * (9); + + // Note: The use of SM_CYEDGE assumes that we're using Windows '95 + // Now add on the height of the border of the edit box + Height += GetSystemMetrics(SM_CYEDGE) * 2; // top & bottom edges + + // The height of the border of the drop-down box + Height += GetSystemMetrics(SM_CYEDGE) * 2; // top & bottom edges + + // now set the size of the window + m_addresses.SetWindowPos(NULL, + 0, 0, + cbSize.right, Height, + SWP_NOMOVE | SWP_NOZORDER); + + m_address.LimitText(8); + + m_size = regQueryDwordValue("memViewerDataSize", 0); + if(m_size < 0 || m_size > 2) + m_size = 0; + m_viewer.setSize(m_size); + UpdateData(FALSE); + + m_current.SetFont(CFont::FromHandle((HFONT)GetStockObject(SYSTEM_FIXED_FONT))); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void MemoryViewerDlg::OnClose() +{ + theApp.winRemoveUpdateListener(this); + + DestroyWindow(); +} + +void MemoryViewerDlg::OnRefresh() +{ + m_viewer.Invalidate(); +} + +void MemoryViewerDlg::update() +{ + OnRefresh(); +} + + +void MemoryViewerDlg::On8Bit() +{ + m_viewer.setSize(0); + regSetDwordValue("memViewerDataSize", 0); +} + +void MemoryViewerDlg::On16Bit() +{ + m_viewer.setSize(1); + regSetDwordValue("memViewerDataSize", 1); +} + +void MemoryViewerDlg::On32Bit() +{ + m_viewer.setSize(2); + regSetDwordValue("memViewerDataSize", 2); +} + +void MemoryViewerDlg::OnAutoUpdate() +{ + autoUpdate = !autoUpdate; + if(autoUpdate) { + theApp.winAddUpdateListener(this); + } else { + theApp.winRemoveUpdateListener(this); + } +} + +void MemoryViewerDlg::OnGo() +{ + CString buffer; + + m_address.GetWindowText(buffer); + + u32 address; + sscanf(buffer, "%x", &address); + if(m_viewer.getSize() == 1) + address &= ~1; + else if(m_viewer.getSize() == 2) + address &= ~3; + m_viewer.setAddress(address); +} + +void MemoryViewerDlg::OnSelchangeAddresses() +{ + int cur = m_addresses.GetCurSel(); + + switch(cur) { + case 0: + m_viewer.setAddress(0); + break; + case 1: + m_viewer.setAddress(0x2000000); + break; + case 2: + m_viewer.setAddress(0x3000000); + break; + case 3: + m_viewer.setAddress(0x4000000); + break; + case 4: + m_viewer.setAddress(0x5000000); + break; + case 5: + m_viewer.setAddress(0x6000000); + break; + case 6: + m_viewer.setAddress(0x7000000); + break; + case 7: + m_viewer.setAddress(0x8000000); + break; + } +} + +void MemoryViewerDlg::setCurrentAddress(u32 address) +{ + CString buffer; + + buffer.Format("0x%08X", address); + m_current.SetWindowText(buffer); +} + +void MemoryViewerDlg::OnSave() +{ + if(rom != NULL) + { + MemoryViewerAddressSize dlg; + CString buffer; + + dlg.setAddress(m_viewer.getCurrentAddress()); + + LPCTSTR exts[] = { ".dmp" }; + + if(dlg.DoModal() == IDOK) { + CString filter = theApp.winLoadFilter(IDS_FILTER_DUMP); + CString title = winResLoadString(IDS_SELECT_DUMP_FILE); + + FileDlg file(this, + buffer, + filter, + 0, + "DMP", + exts, + "", + title, + true); + if(file.DoModal() == IDOK) { + buffer = file.GetPathName(); + + FILE *f = fopen(buffer, "wb"); + + if(f == NULL) { + systemMessage(IDS_ERROR_CREATING_FILE, buffer); + return; + } + + int size = dlg.getSize(); + u32 addr = dlg.getAddress(); + + for(int i = 0; i < size; i++) { + fputc(CPUReadByteQuick(addr), f); + addr++; + } + + fclose(f); + } + } + } +} + +void MemoryViewerDlg::OnLoad() +{ + if(rom != NULL) + { + CString buffer; + LPCTSTR exts[] = { ".dmp" }; + + CString filter = theApp.winLoadFilter(IDS_FILTER_DUMP); + CString title = winResLoadString(IDS_SELECT_DUMP_FILE); + + FileDlg file(this, + buffer, + filter, + 0, + "DMP", + exts, + "", + title, + false); + + if(file.DoModal() == IDOK) { + buffer = file.GetPathName(); + FILE *f = fopen(buffer, "rb"); + if(f == NULL) { + systemMessage(IDS_CANNOT_OPEN_FILE, + "Cannot open file %s", + buffer); + return; + } + + MemoryViewerAddressSize dlg; + + fseek(f, 0, SEEK_END); + int size = ftell(f); + + fseek(f, 0, SEEK_SET); + + dlg.setAddress(m_viewer.getCurrentAddress()); + dlg.setSize(size); + + if(dlg.DoModal() == IDOK) { + int size = dlg.getSize(); + u32 addr = dlg.getAddress(); + + for(int i = 0; i < size; i++) { + int c = fgetc(f); + if(c == -1) + break; + CPUWriteByteQuick(addr, c); + addr++; + } + OnRefresh(); + } + fclose(f); + } + } +} + +void MemoryViewerDlg::PostNcDestroy() +{ + delete this; +} diff --git a/src/win32/MemoryViewerDlg.h b/src/win32/MemoryViewerDlg.h new file mode 100644 index 0000000..96559f6 --- /dev/null +++ b/src/win32/MemoryViewerDlg.h @@ -0,0 +1,75 @@ +#if !defined(AFX_MEMORYVIEWERDLG_H__15046D5B_D5A2_4C49_A969_2A77F803F2F1__INCLUDED_) +#define AFX_MEMORYVIEWERDLG_H__15046D5B_D5A2_4C49_A969_2A77F803F2F1__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// MemoryViewerDlg.h : header file +// + +#include "MemoryViewer.h" +#include "ResizeDlg.h" +#include "IUpdate.h" + +class GBAMemoryViewer : public MemoryViewer { + public: + GBAMemoryViewer(); + virtual void readData(u32, int, u8 *); + virtual void editData(u32,int,int,u32); +}; + +///////////////////////////////////////////////////////////////////////////// +// MemoryViewerDlg dialog + +class MemoryViewerDlg : public ResizeDlg, IUpdateListener, IMemoryViewerDlg +{ + GBAMemoryViewer m_viewer; + // Construction + public: + void setCurrentAddress(u32 address); + bool autoUpdate; + void update(); + MemoryViewerDlg(CWnd* pParent = NULL); // standard constructor + + // Dialog Data + //{{AFX_DATA(MemoryViewerDlg) + enum { IDD = IDD_MEM_VIEWER }; + CEdit m_current; + CEdit m_address; + CComboBox m_addresses; + int m_size; + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(MemoryViewerDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual void PostNcDestroy(); + //}}AFX_VIRTUAL + + // Implementation + protected: + + // Generated message map functions + //{{AFX_MSG(MemoryViewerDlg) + virtual BOOL OnInitDialog(); + afx_msg void OnClose(); + afx_msg void OnRefresh(); + afx_msg void On8Bit(); + afx_msg void On16Bit(); + afx_msg void On32Bit(); + afx_msg void OnAutoUpdate(); + afx_msg void OnGo(); + afx_msg void OnSelchangeAddresses(); + afx_msg void OnSave(); + afx_msg void OnLoad(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + + //{{AFX_INSERT_LOCATION}} + // Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_MEMORYVIEWERDLG_H__15046D5B_D5A2_4C49_A969_2A77F803F2F1__INCLUDED_) diff --git a/src/win32/OALConfig.cpp b/src/win32/OALConfig.cpp new file mode 100644 index 0000000..5c331f7 --- /dev/null +++ b/src/win32/OALConfig.cpp @@ -0,0 +1,89 @@ +#include "stdafx.h" +#ifndef NO_OAL +#include "VBA.h" + +#include "OALConfig.h" + +// OpenAL +#include +#include +#include "LoadOAL.h" + + +// OALConfig dialog + +IMPLEMENT_DYNAMIC(OALConfig, CDialog) + +OALConfig::OALConfig(CWnd* pParent /*=NULL*/) + : CDialog(OALConfig::IDD, pParent) + , selectedDevice(_T("")) + , bufferCount(0) +{ + if( !LoadOAL10Library( NULL, &ALFunction ) ) { + systemMessage( IDS_OAL_NODLL, "OpenAL32.dll could not be found on your system. Please install the runtime from http://openal.org" ); + } +} + +OALConfig::~OALConfig() +{ +} + +void OALConfig::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + DDX_Control(pDX, IDC_DEVICE, m_cbDevice); + + if( !pDX->m_bSaveAndValidate ) { + // enumerate devices + const ALchar *devices = NULL; + devices = ALFunction.alcGetString( NULL, ALC_DEVICE_SPECIFIER ); + if( strlen( devices ) ) { + while( *devices ) { + m_cbDevice.AddString( devices ); + devices += strlen( devices ) + 1; + } + } else { + systemMessage( IDS_OAL_NODEVICE, "There are no sound devices present on this system." ); + } + } + DDX_CBString(pDX, IDC_DEVICE, selectedDevice); + DDX_Control(pDX, IDC_SLIDER_BUFFERCOUNT, m_sliderBufferCount); + DDX_Slider(pDX, IDC_SLIDER_BUFFERCOUNT, bufferCount); + DDX_Control(pDX, IDC_BUFFERINFO, m_bufferInfo); +} + + +BOOL OALConfig::OnInitDialog() +{ + CDialog::OnInitDialog(); + m_sliderBufferCount.SetRange( 2, 10, FALSE ); + m_sliderBufferCount.SetTicFreq( 1 ); + m_sliderBufferCount.SetPos( bufferCount ); + + CString info; + int pos = m_sliderBufferCount.GetPos(); + info.Format( _T("%i frames = %.2f ms"), pos, (float)pos / 60.0f * 1000.0f ); + m_bufferInfo.SetWindowText( info ); + + return TRUE; +} + + +BEGIN_MESSAGE_MAP(OALConfig, CDialog) + ON_WM_HSCROLL() +END_MESSAGE_MAP() + + +// OALConfig message handlers + +void OALConfig::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) +{ + CString info; + int pos = m_sliderBufferCount.GetPos(); + info.Format( _T("%i frames = %.2f ms"), pos, (float)pos / 60.0f * 1000.0f ); + m_bufferInfo.SetWindowText( info ); + + CDialog::OnHScroll(nSBCode, nPos, pScrollBar); +} + +#endif diff --git a/src/win32/OALConfig.h b/src/win32/OALConfig.h new file mode 100644 index 0000000..4cdea3d --- /dev/null +++ b/src/win32/OALConfig.h @@ -0,0 +1,40 @@ +#ifndef NO_OAL + +#pragma once + +// OpenAL +#include +#include +#include "LoadOAL.h" + + +// OALConfig dialog + +class OALConfig : public CDialog +{ + DECLARE_DYNAMIC(OALConfig) + +public: + OALConfig(CWnd* pParent = NULL); // standard constructor + virtual ~OALConfig(); + +// Dialog Data + enum { IDD = IDD_OAL_CONFIG }; + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual BOOL OnInitDialog(); + + DECLARE_MESSAGE_MAP() +private: + OPENALFNTABLE ALFunction; + CComboBox m_cbDevice; + CSliderCtrl m_sliderBufferCount; + CStatic m_bufferInfo; +public: + CString selectedDevice; + int bufferCount; + afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); +}; + +#endif diff --git a/src/win32/OamView.cpp b/src/win32/OamView.cpp new file mode 100644 index 0000000..1dac474 --- /dev/null +++ b/src/win32/OamView.cpp @@ -0,0 +1,651 @@ +#include "stdafx.h" +#include "vba.h" +#include "FileDlg.h" +#include "OamView.h" +#include "Reg.h" +#include "WinResUtil.h" + +#include "../System.h" +#include "../gba/GBA.h" +#include "../gba/Globals.h" +#include "../NLS.h" +#include "../Util.h" + +extern "C" { +#include +} + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// OamView dialog + + +OamView::OamView(CWnd* pParent /*=NULL*/) + : ResizeDlg(OamView::IDD, pParent) +{ + //{{AFX_DATA_INIT(OamView) + m_stretch = FALSE; + //}}AFX_DATA_INIT + autoUpdate = false; + + memset(&bmpInfo.bmiHeader, 0, sizeof(bmpInfo.bmiHeader)); + + bmpInfo.bmiHeader.biSize = sizeof(bmpInfo.bmiHeader); + bmpInfo.bmiHeader.biWidth = 32; + bmpInfo.bmiHeader.biHeight = 32; + bmpInfo.bmiHeader.biPlanes = 1; + bmpInfo.bmiHeader.biBitCount = 24; + bmpInfo.bmiHeader.biCompression = BI_RGB; + data = (u8 *)calloc(1, 3 * 64 * 64); + + oamView.setData(data); + oamView.setBmpInfo(&bmpInfo); + + number = 0; +} + + +void OamView::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(OamView) + DDX_Control(pDX, IDC_SPRITE, m_sprite); + DDX_Check(pDX, IDC_STRETCH, m_stretch); + //}}AFX_DATA_MAP + DDX_Control(pDX, IDC_COLOR, color); + DDX_Control(pDX, IDC_OAM_VIEW, oamView); + DDX_Control(pDX, IDC_OAM_VIEW_ZOOM, oamZoom); +} + + +BEGIN_MESSAGE_MAP(OamView, CDialog) + //{{AFX_MSG_MAP(OamView) + ON_BN_CLICKED(IDC_SAVE, OnSave) + ON_BN_CLICKED(IDC_STRETCH, OnStretch) + ON_BN_CLICKED(IDC_AUTO_UPDATE, OnAutoUpdate) + ON_EN_CHANGE(IDC_SPRITE, OnChangeSprite) + ON_BN_CLICKED(IDC_CLOSE, OnClose) + ON_WM_HSCROLL() + //}}AFX_MSG_MAP + ON_MESSAGE(WM_MAPINFO, OnMapInfo) + ON_MESSAGE(WM_COLINFO, OnColInfo) + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// OamView message handlers + +OamView::~OamView() +{ + free(data); + data = NULL; +} + +void OamView::paint() +{ + if(oam == NULL || paletteRAM == NULL || vram == NULL) + return; + + render(); + oamView.setSize(w,h); + oamView.refresh(); +} + +void OamView::update() +{ + paint(); +} + + + +void OamView::setAttributes(u16 a0, u16 a1, u16 a2) +{ + CString buffer; + + int y = a0 & 255; + int rot = a0 & 512; + int mode = (a0 >> 10) & 3; + int mosaic = a0 & 4096; + int color = a0 & 8192; + int duple = a0 & 1024; + int shape = (a0 >> 14) & 3; + int x = a1 & 511; + int rotParam = (a1 >> 9) & 31; + int flipH = a1 & 4096; + int flipV = a1 & 8192; + int size = (a1 >> 14) & 3; + int tile = a2 & 1023; + int prio = (a2 >> 10) & 3; + int pal = (a2 >> 12) & 15; + + buffer.Format("%d,%d", x,y); + GetDlgItem(IDC_POS)->SetWindowText(buffer); + + buffer.Format("%d", mode); + GetDlgItem(IDC_MODE)->SetWindowText(buffer); + + GetDlgItem(IDC_COLORS)->SetWindowText(color ? "256" : "16"); + + buffer.Format("%d", pal); + GetDlgItem(IDC_PALETTE)->SetWindowText(buffer); + + buffer.Format("%d", tile); + GetDlgItem(IDC_TILE)->SetWindowText(buffer); + + buffer.Format("%d", prio); + GetDlgItem(IDC_PRIO)->SetWindowText(buffer); + + buffer.Format("%d,%d", w,h); + GetDlgItem(IDC_SIZE2)->SetWindowText(buffer); + + if(rot) { + buffer.Format("%d", rotParam); + } else + buffer.Empty(); + GetDlgItem(IDC_ROT)->SetWindowText(buffer); + + buffer.Empty(); + + if(rot) + buffer += 'R'; + else buffer += ' '; + if(!rot) { + if(flipH) + buffer += 'H'; + else + buffer += ' '; + if(flipV) + buffer += 'V'; + else + buffer += ' '; + } else { + buffer += ' '; + buffer += ' '; + } + if(mosaic) + buffer += 'M'; + else + buffer += ' '; + if(duple) + buffer += 'D'; + else + buffer += ' '; + + GetDlgItem(IDC_FLAGS)->SetWindowText(buffer); +} + +void OamView::render() +{ + int m=0; + if(oam == NULL || paletteRAM == NULL || vram == NULL) + return; + + u16 *sprites = &((u16 *)oam)[4*number]; + u16 *spritePalette = &((u16 *)paletteRAM)[0x100]; + u8 *bmp = data; + + u16 a0 = *sprites++; + u16 a1 = *sprites++; + u16 a2 = *sprites++; + + int sizeY = 8; + int sizeX = 8; + + switch(((a0 >>12) & 0x0c)|(a1>>14)) { + case 0: + break; + case 1: + sizeX = sizeY = 16; + break; + case 2: + sizeX = sizeY = 32; + break; + case 3: + sizeX = sizeY = 64; + break; + case 4: + sizeX = 16; + break; + case 5: + sizeX = 32; + break; + case 6: + sizeX = 32; + sizeY = 16; + break; + case 7: + sizeX = 64; + sizeY = 32; + break; + case 8: + sizeY = 16; + break; + case 9: + sizeY = 32; + break; + case 10: + sizeX = 16; + sizeY = 32; + break; + case 11: + sizeX = 32; + sizeY = 64; + break; + default: + return; + } + + w = sizeX; + h = sizeY; + + setAttributes(a0,a1,a2); + + int sy = (a0 & 255); + + if(a0 & 0x2000) { + int c = (a2 & 0x3FF); + // if((DISPCNT & 7) > 2 && (c < 512)) + // return; + int inc = 32; + if(DISPCNT & 0x40) + inc = sizeX >> 2; + else + c &= 0x3FE; + + for(int y = 0; y < sizeY; y++) { + for(int x = 0; x < sizeX; x++) { + u32 color = vram[0x10000 + (((c + (y>>3) * inc)* + 32 + (y & 7) * 8 + (x >> 3) * 64 + + (x & 7))&0x7FFF)]; + color = spritePalette[color]; + *bmp++ = ((color >> 10) & 0x1f) << 3; + *bmp++ = ((color >> 5) & 0x1f) << 3; + *bmp++ = (color & 0x1f) << 3; + } + } + } else { + int c = (a2 & 0x3FF); + // if((DISPCNT & 7) > 2 && (c < 512)) + // continue; + + int inc = 32; + if(DISPCNT & 0x40) + inc = sizeX >> 3; + int palette = (a2 >> 8) & 0xF0; + for(int y = 0; y < sizeY; y++) { + for(int x = 0; x < sizeX; x++) { + u32 color = vram[0x10000 + (((c + (y>>3) * inc)* + 32 + (y & 7) * 4 + (x >> 3) * 32 + + ((x & 7)>>1))&0x7FFF)]; + if(x & 1) + color >>= 4; + else + color &= 0x0F; + + color = spritePalette[palette+color]; + *bmp++ = ((color >> 10) & 0x1f) << 3; + *bmp++ = ((color >> 5) & 0x1f) << 3; + *bmp++ = (color & 0x1f) << 3; + } + } + } +} + +void OamView::saveBMP(const char *name) +{ + u8 writeBuffer[1024 * 3]; + + FILE *fp = fopen(name,"wb"); + + if(!fp) { + systemMessage(MSG_ERROR_CREATING_FILE, "Error creating file %s", name); + return; + } + + struct { + u8 ident[2]; + u8 filesize[4]; + u8 reserved[4]; + u8 dataoffset[4]; + u8 headersize[4]; + u8 width[4]; + u8 height[4]; + u8 planes[2]; + u8 bitsperpixel[2]; + u8 compression[4]; + u8 datasize[4]; + u8 hres[4]; + u8 vres[4]; + u8 colors[4]; + u8 importantcolors[4]; + u8 pad[2]; + } bmpheader; + memset(&bmpheader, 0, sizeof(bmpheader)); + + bmpheader.ident[0] = 'B'; + bmpheader.ident[1] = 'M'; + + u32 fsz = sizeof(bmpheader) + w*h*3; + utilPutDword(bmpheader.filesize, fsz); + utilPutDword(bmpheader.dataoffset, 0x38); + utilPutDword(bmpheader.headersize, 0x28); + utilPutDword(bmpheader.width, w); + utilPutDword(bmpheader.height, h); + utilPutDword(bmpheader.planes, 1); + utilPutDword(bmpheader.bitsperpixel, 24); + utilPutDword(bmpheader.datasize, 3*w*h); + + fwrite(&bmpheader, 1, sizeof(bmpheader), fp); + + u8 *b = writeBuffer; + + int sizeX = w; + int sizeY = h; + + u8 *pixU8 = (u8 *)data+3*w*(h-1); + for(int y = 0; y < sizeY; y++) { + for(int x = 0; x < sizeX; x++) { + *b++ = *pixU8++; // B + *b++ = *pixU8++; // G + *b++ = *pixU8++; // R + } + pixU8 -= 2*3*w; + fwrite(writeBuffer, 1, 3*w, fp); + + b = writeBuffer; + } + + fclose(fp); +} + + + +void OamView::savePNG(const char *name) +{ + u8 writeBuffer[1024 * 3]; + + FILE *fp = fopen(name,"wb"); + + if(!fp) { + systemMessage(MSG_ERROR_CREATING_FILE, "Error creating file %s", name); + return; + } + + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, + NULL, + NULL, + NULL); + if(!png_ptr) { + fclose(fp); + return; + } + + png_infop info_ptr = png_create_info_struct(png_ptr); + + if(!info_ptr) { + png_destroy_write_struct(&png_ptr,NULL); + fclose(fp); + return; + } + + if(setjmp(png_ptr->jmpbuf)) { + png_destroy_write_struct(&png_ptr,NULL); + fclose(fp); + return; + } + + png_init_io(png_ptr,fp); + + png_set_IHDR(png_ptr, + info_ptr, + w, + h, + 8, + PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + png_write_info(png_ptr,info_ptr); + + u8 *b = writeBuffer; + + int sizeX = w; + int sizeY = h; + + u8 *pixU8 = (u8 *)data; + for(int y = 0; y < sizeY; y++) { + for(int x = 0; x < sizeX; x++) { + int blue = *pixU8++; + int green = *pixU8++; + int red = *pixU8++; + + *b++ = red; + *b++ = green; + *b++ = blue; + } + png_write_row(png_ptr,writeBuffer); + + b = writeBuffer; + } + + png_write_end(png_ptr, info_ptr); + + png_destroy_write_struct(&png_ptr, &info_ptr); + + fclose(fp); +} + +void OamView::OnSave() +{ + if(rom != NULL) + { + CString captureBuffer; + + if(theApp.captureFormat == 0) + captureBuffer = "oam.png"; + else + captureBuffer = "oam.bmp"; + + LPCTSTR exts[] = {".png", ".bmp" }; + + CString filter = theApp.winLoadFilter(IDS_FILTER_PNG); + CString title = winResLoadString(IDS_SELECT_CAPTURE_NAME); + + FileDlg dlg(this, + captureBuffer, + filter, + theApp.captureFormat ? 2 : 1, + theApp.captureFormat ? "BMP" : "PNG", + exts, + "", + title, + true); + + if(dlg.DoModal() == IDCANCEL) { + return; + } + captureBuffer = dlg.GetPathName(); + + if(dlg.getFilterIndex() == 2) + saveBMP(captureBuffer); + else + savePNG(captureBuffer); + } +} + +BOOL OamView::OnInitDialog() +{ + CDialog::OnInitDialog(); + + DIALOG_SIZER_START( sz ) + DIALOG_SIZER_ENTRY( IDC_OAM_VIEW, DS_SizeX | DS_SizeY ) + DIALOG_SIZER_ENTRY( IDC_OAM_VIEW_ZOOM, DS_MoveX) + DIALOG_SIZER_ENTRY( IDC_REFRESH, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_SAVE, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_CLOSE, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_COLOR, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_R, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_G, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_B, DS_MoveY) + DIALOG_SIZER_END() + SetData(sz, + TRUE, + HKEY_CURRENT_USER, + "Software\\Emulators\\VisualBoyAdvance\\Viewer\\OamView", + NULL); + m_sprite.SetWindowText("0"); + + updateScrollInfo(); + + m_stretch = regQueryDwordValue("oamViewStretch", 0); + if(m_stretch) + oamView.setStretch(true); + UpdateData(FALSE); + + paint(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void OamView::OnStretch() +{ + oamView.setStretch(!oamView.getStretch()); + paint(); + regSetDwordValue("oamViewStretch", oamView.getStretch()); +} + + +void OamView::OnAutoUpdate() +{ + autoUpdate = !autoUpdate; + if(autoUpdate) { + theApp.winAddUpdateListener(this); + } else { + theApp.winRemoveUpdateListener(this); + } +} + +void OamView::OnChangeSprite() +{ + CString buffer; + m_sprite.GetWindowText(buffer); + int n = atoi(buffer); + if(n < 0 || n > 127) { + buffer.Format("%d", number); + m_sprite.SetWindowText(buffer); + return; + } + number = n; + paint(); + updateScrollInfo(); +} + +void OamView::OnClose() +{ + theApp.winRemoveUpdateListener(this); + + DestroyWindow(); +} + +LRESULT OamView::OnMapInfo(WPARAM wParam, LPARAM lParam) +{ + u8 *colors = (u8 *)lParam; + oamZoom.setColors(colors); + + return TRUE; +} + +LRESULT OamView::OnColInfo(WPARAM wParam, LPARAM lParam) +{ + u16 c = (u16)wParam; + + color.setColor(c); + + int r = (c & 0x1f); + int g = (c & 0x3e0) >> 5; + int b = (c & 0x7c00) >> 10; + + CString buffer; + buffer.Format("R: %d", r); + GetDlgItem(IDC_R)->SetWindowText(buffer); + + buffer.Format("G: %d", g); + GetDlgItem(IDC_G)->SetWindowText(buffer); + + buffer.Format("B: %d", b); + GetDlgItem(IDC_B)->SetWindowText(buffer); + + return TRUE; +} + +void OamView::updateScrollInfo() +{ + SCROLLINFO si; + ZeroMemory(&si, sizeof(si)); + si.cbSize = sizeof(si); + si.fMask = SIF_PAGE | SIF_RANGE | SIF_DISABLENOSCROLL | SIF_POS; + si.nMin = 0; + si.nMax = 127; + si.nPage = 1; + si.nPos = number; + GetDlgItem(IDC_SCROLLBAR)->SetScrollInfo(SB_CTL, + &si, + TRUE); +} + +void OamView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) +{ + switch(nSBCode) { + case SB_BOTTOM: + number = 127; + break; + case SB_LINEDOWN: + number++; + if(number > 127) + number = 127; + break; + case SB_LINEUP: + number--; + if(number < 0) + number = 0; + break; + case SB_PAGEDOWN: + number += 16; + if(number > 127) + number = 127; + break; + case SB_PAGEUP: + number -= 16; + if(number < 0) + number = 0; + break; + case SB_TOP: + number = 0; + break; + case SB_THUMBTRACK: + number = nPos; + if(number < 0) + number = 0; + if(number > 127) + number = 127; + break; + } + + updateScrollInfo(); + + CString buffer; + buffer.Format("%d", number); + m_sprite.SetWindowText(buffer); + paint(); +} + +void OamView::PostNcDestroy() +{ + delete this; +} diff --git a/src/win32/OamView.h b/src/win32/OamView.h new file mode 100644 index 0000000..9f5d639 --- /dev/null +++ b/src/win32/OamView.h @@ -0,0 +1,81 @@ +#if !defined(AFX_OAMVIEW_H__E5369352_80F8_49C4_9F23_05EB6FC1345B__INCLUDED_) +#define AFX_OAMVIEW_H__E5369352_80F8_49C4_9F23_05EB6FC1345B__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// OamView.h : header file +// +#include "BitmapControl.h" +#include "ZoomControl.h" +#include "ColorControl.h" + +#include "IUpdate.h" +#include "ResizeDlg.h" + +///////////////////////////////////////////////////////////////////////////// +// OamView dialog + +class OamView : public ResizeDlg, IUpdateListener +{ + private: + BITMAPINFO bmpInfo; + u8 *data; + int w; + int h; + int number; + bool autoUpdate; + BitmapControl oamView; + ZoomControl oamZoom; + ColorControl color; + + // Construction + public: + void updateScrollInfo(); + afx_msg LRESULT OnColInfo(WPARAM wParam, LPARAM lParam); + afx_msg LRESULT OnMapInfo(WPARAM wParam, LPARAM lParam); + void savePNG(const char *name); + void saveBMP(const char *name); + void render(); + void setAttributes(u16 a0, u16 a1, u16 a2); + void paint(); + ~OamView(); + OamView(CWnd* pParent = NULL); // standard constructor + + virtual void update(); + // Dialog Data + //{{AFX_DATA(OamView) + enum { IDD = IDD_OAM_VIEW }; + CEdit m_sprite; + BOOL m_stretch; + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(OamView) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual void PostNcDestroy(); + //}}AFX_VIRTUAL + + // Implementation + protected: + + // Generated message map functions + //{{AFX_MSG(OamView) + afx_msg void OnSave(); + virtual BOOL OnInitDialog(); + afx_msg void OnStretch(); + afx_msg void OnAutoUpdate(); + afx_msg void OnChangeSprite(); + afx_msg void OnClose(); + afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + + //{{AFX_INSERT_LOCATION}} + // Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_OAMVIEW_H__E5369352_80F8_49C4_9F23_05EB6FC1345B__INCLUDED_) diff --git a/src/win32/OpenAL.cpp b/src/win32/OpenAL.cpp new file mode 100644 index 0000000..08107c2 --- /dev/null +++ b/src/win32/OpenAL.cpp @@ -0,0 +1,329 @@ +// === LOGALL writes very detailed informations to vba-trace.log === +//#define LOGALL + +#include "stdafx.h" +#include "VBA.h" // for 'theApp.throttle' + +#ifndef NO_OAL + +// Interface +#include "../common/SoundDriver.h" + +// OpenAL +#include +#include +#include "LoadOAL.h" + +// Windows +#include // for 'Sleep' function + +// Internals +#include "../gba/Sound.h" +#include "../gba/Globals.h" // for 'speedup' and 'synchronize' + +// Debug +#include +#define ASSERT_SUCCESS assert( AL_NO_ERROR == ALFunction.alGetError() ) + +#ifndef LOGALL +// replace logging functions with comments +#define winlog // +#define debugState() // +#endif + +class OpenAL : public SoundDriver +{ +public: + OpenAL(); + virtual ~OpenAL(); + + 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: + 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( theApp.oalBufferCount * sizeof( ALuint ) ); + memset( buffer, 0, theApp.oalBufferCount * 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( theApp.oalBufferCount, buffer ); + ASSERT_SUCCESS; + + free( buffer ); + + ALFunction.alcMakeContextCurrent( NULL ); + ASSERT_SUCCESS; + + ALFunction.alcDestroyContext( context ); + ASSERT_SUCCESS; + + ALFunction.alcCloseDevice( device ); + ASSERT_SUCCESS; +} + +#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( !LoadOAL10Library( NULL, &ALFunction ) ) { + systemMessage( IDS_OAL_NODLL, "OpenAL32.dll could not be found on your system. Please install the runtime from http://openal.org" ); + return false; + } + + if( theApp.oalDevice ) { + device = ALFunction.alcOpenDevice( theApp.oalDevice ); + } 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( theApp.oalBufferCount, 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 < theApp.oalBufferCount ; 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, theApp.oalBufferCount, buffer ); + ASSERT_SUCCESS; + + buffersLoaded = true; + } else { + // ==normal buffer refreshing== + nBuffersProcessed = 0; + ALFunction.alGetSourcei( source, AL_BUFFERS_PROCESSED, &nBuffersProcessed ); + ASSERT_SUCCESS; + + if( nBuffersProcessed == theApp.oalBufferCount ) { + // we only want to know about it when we are emulating at full speed or faster: + if( ( theApp.throttle >= 100 ) || ( theApp.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 && !theApp.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 + Sleep( 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(); +} + +#endif diff --git a/src/win32/OpenGL.cpp b/src/win32/OpenGL.cpp new file mode 100644 index 0000000..ba6cc54 --- /dev/null +++ b/src/win32/OpenGL.cpp @@ -0,0 +1,689 @@ +#ifndef NO_OGL + +//OpenGL library +#pragma comment( lib, "OpenGL32" ) + +// MFC +#include "stdafx.h" + +//GUI +#include "MainWnd.h" +#include "FullscreenSettings.h" + +// Internals +#include "../System.h" +#include "../gba/GBA.h" +#include "../gba/Globals.h" +#include "../Util.h" +#include "../gb/gbGlobals.h" +#include "../common/memgzio.h" + +//Math +#include +#include + +// OpenGL +#include // main include file +#include +#include "glFont.h" +#include +typedef BOOL (APIENTRY *PFNWGLSWAPINTERVALFARPROC)( int ); +extern int Init_2xSaI(u32); +extern void winlog(const char *,...); +extern int systemSpeed; + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +#ifdef MMX +extern "C" bool cpu_mmx; +extern bool detectMMX(); +#endif + + +class OpenGLDisplay : public IDisplay { +private: + HDC hDC; + HGLRC hRC; + GLuint texture; + int width,height; + float size; + u8 *filterData; + RECT destRect; + bool failed; + GLFONT font; + int pitch; + u8 *data; + DWORD currentAdapter; + + void initializeMatrices( int w, int h ); + bool initializeTexture( int w, int h ); + void updateFiltering( int value ); + void setVSync( int interval = 1 ); + void calculateDestRect( int w, int h ); + void initializeFont(); + +public: + OpenGLDisplay(); + virtual ~OpenGLDisplay(); + virtual DISPLAY_TYPE getType() { return OPENGL; }; + + virtual void EnableOpenGL(); + virtual void DisableOpenGL(); + virtual bool initialize(); + virtual void cleanup(); + virtual void clear(); + virtual void render(); + virtual bool changeRenderSize( int w, int h ); + virtual void resize( int w, int h ); + virtual void setOption( const char *, int ); + virtual bool selectFullScreenMode( VIDEO_MODE &mode ); +}; + +#include "gzglfont.h" +//Load GL font +void OpenGLDisplay::initializeFont() +{ + int ret; + z_stream strm; + char *buf = (char *)malloc(GZGLFONT_SIZE); + + /* allocate inflate state */ + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = 0; + strm.next_in = Z_NULL; + ret = inflateInit2(&strm, 16+MAX_WBITS); + if (ret != Z_OK) + return; + + strm.avail_in = sizeof(gzglfont); + strm.next_in = gzglfont; + strm.avail_out = GZGLFONT_SIZE; + strm.next_out = (Bytef *)buf; + ret = inflate(&strm, Z_NO_FLUSH); + if (ret==Z_STREAM_END) + { + glGenTextures( 1, &texture ); + glFontCreate(&font, (char *)buf, texture); + texture=0; + } + free(buf); + (void)inflateEnd(&strm); +} + +//OpenGL class constructor +OpenGLDisplay::OpenGLDisplay() +{ + hDC = NULL; + hRC = NULL; + texture = 0; + width = 0; + height = 0; + size = 0.0f; + failed = false; + filterData = NULL; + currentAdapter = 0; +} + +//OpenGL class destroyer +OpenGLDisplay::~OpenGLDisplay() +{ + cleanup(); +} + +//Set OpenGL PFD and contexts +void OpenGLDisplay::EnableOpenGL() +{ + PIXELFORMATDESCRIPTOR pfd; + // get the device context (DC) + hDC = GetDC( theApp.m_pMainWnd->GetSafeHwnd() ); + // set the pixel format for the DC + ZeroMemory( &pfd, sizeof( pfd ) ); + pfd.nSize = sizeof( pfd ); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.cColorBits = 24; + pfd.cDepthBits = 16; + pfd.iLayerType = PFD_MAIN_PLANE; + SetPixelFormat (GetDC (theApp.m_pMainWnd->GetSafeHwnd()), ChoosePixelFormat ( GetDC (theApp.m_pMainWnd->GetSafeHwnd()), &pfd), &pfd); + wglMakeCurrent (GetDC (theApp.m_pMainWnd->GetSafeHwnd()), wglCreateContext(GetDC (theApp.m_pMainWnd->GetSafeHwnd()) ) ); +} +//Remove contexts +void OpenGLDisplay::DisableOpenGL() +{ + wglMakeCurrent( NULL, NULL ); + wglDeleteContext( hRC ); + ReleaseDC( theApp.m_pMainWnd->GetSafeHwnd(), hDC ); +} +//Remove resources used +void OpenGLDisplay::cleanup() +{ + if(texture != 0) { + glDeleteTextures(1, &texture); + texture = 0; + } + + DisableOpenGL(); + if(filterData) { + free(filterData); + filterData = NULL; + } + width = 0; + height = 0; + size = 0.0f; + + DISPLAY_DEVICE dev; + ZeroMemory( &dev, sizeof(dev) ); + dev.cb = sizeof(dev); + EnumDisplayDevices( NULL, currentAdapter, &dev, 0 ); + // restore default video mode + ChangeDisplaySettingsEx( dev.DeviceName, NULL, NULL, 0, NULL ); +} + +//init renderer +bool OpenGLDisplay::initialize() +{ + switch( theApp.cartridgeType ) + { + case IMAGE_GBA: + theApp.sizeX = 240; + theApp.sizeY = 160; + break; + case IMAGE_GB: + if ( gbBorderOn ) + { + theApp.sizeX = 256; + theApp.sizeY = 224; + } + else + { + theApp.sizeX = 160; + theApp.sizeY = 144; + } + break; + } + + + switch(theApp.videoOption) + { + case VIDEO_1X: + theApp.surfaceSizeX = theApp.sizeX; + theApp.surfaceSizeY = theApp.sizeY; + break; + case VIDEO_2X: + theApp.surfaceSizeX = theApp.sizeX * 2; + theApp.surfaceSizeY = theApp.sizeY * 2; + break; + case VIDEO_3X: + theApp.surfaceSizeX = theApp.sizeX * 3; + theApp.surfaceSizeY = theApp.sizeY * 3; + break; + case VIDEO_4X: + theApp.surfaceSizeX = theApp.sizeX * 4; + theApp.surfaceSizeY = theApp.sizeY * 4; + break; + case VIDEO_5X: + theApp.surfaceSizeX = theApp.sizeX * 5; + theApp.surfaceSizeY = theApp.sizeY * 5; + break; + case VIDEO_6X: + theApp.surfaceSizeX = theApp.sizeX * 6; + theApp.surfaceSizeY = theApp.sizeY * 6; + break; + case VIDEO_320x240: + case VIDEO_640x480: + case VIDEO_800x600: + case VIDEO_1024x768: + case VIDEO_1280x1024: + case VIDEO_OTHER: + { + if( theApp.fullScreenStretch ) { + theApp.surfaceSizeX = theApp.fsWidth; + theApp.surfaceSizeY = theApp.fsHeight; + } else { + float scaleX = (float)theApp.fsWidth / (float)theApp.sizeX; + float scaleY = (float)theApp.fsHeight / (float)theApp.sizeY; + float min = ( scaleX < scaleY ) ? scaleX : scaleY; + if( theApp.maxScale ) + min = ( min > (float)theApp.maxScale ) ? (float)theApp.maxScale : min; + theApp.surfaceSizeX = (int)((float)theApp.sizeX * min); + theApp.surfaceSizeY = (int)((float)theApp.sizeY * min); + } + } + break; + } + + theApp.rect.left = 0; + theApp.rect.top = 0; + theApp.rect.right = theApp.sizeX; + theApp.rect.bottom = theApp.sizeY; + + theApp.dest.left = 0; + theApp.dest.top = 0; + theApp.dest.right = theApp.surfaceSizeX; + theApp.dest.bottom = theApp.surfaceSizeY; + + DWORD style = WS_POPUP | WS_VISIBLE; + DWORD styleEx = 0; + + if( theApp.videoOption <= VIDEO_6X ) + style |= WS_OVERLAPPEDWINDOW; + else + styleEx = 0; + + if( theApp.videoOption <= VIDEO_6X ) + AdjustWindowRectEx( &theApp.dest, style, TRUE, styleEx ); + else + AdjustWindowRectEx( &theApp.dest, style, FALSE, styleEx ); + + int winSizeX = theApp.dest.right - theApp.dest.left; + int winSizeY = theApp.dest.bottom - theApp.dest.top; + int x = 0, y = 0; + + if( theApp.videoOption <= VIDEO_6X ) { + x = theApp.windowPositionX; + y = theApp.windowPositionY; + } else { + winSizeX = theApp.fsWidth; + winSizeY = theApp.fsHeight; + } + + + + theApp.updateMenuBar(); + + theApp.adjustDestRect(); + + currentAdapter = theApp.fsAdapter; + DISPLAY_DEVICE dev; + ZeroMemory( &dev, sizeof(dev) ); + dev.cb = sizeof(dev); + EnumDisplayDevices( NULL, currentAdapter, &dev, 0 ); + if( theApp.videoOption >= VIDEO_320x240 ) { + // enter full screen mode + DEVMODE mode; + ZeroMemory( &mode, sizeof(mode) ); + mode.dmSize = sizeof(mode); + mode.dmBitsPerPel = theApp.fsColorDepth; + mode.dmPelsWidth = theApp.fsWidth; + mode.dmPelsHeight = theApp.fsHeight; + mode.dmDisplayFrequency = theApp.fsFrequency; + mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY; + LONG ret = ChangeDisplaySettingsEx( dev.DeviceName, &mode, NULL, CDS_FULLSCREEN, NULL ); + if( ret != DISP_CHANGE_SUCCESSFUL ) { + systemMessage( 0, "Can not change display mode!" ); + failed = true; + } + } else { + // restore default mode + ChangeDisplaySettingsEx( dev.DeviceName, NULL, NULL, 0, NULL ); + } + + EnableOpenGL(); + initializeFont(); + glPushAttrib( GL_ENABLE_BIT ); + glDisable( GL_DEPTH_TEST ); + glDisable( GL_CULL_FACE ); + glEnable( GL_TEXTURE_2D ); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + initializeMatrices( theApp.surfaceSizeX, theApp.surfaceSizeY ); + + setVSync( theApp.vsync ); + +#ifdef MMX + if(!theApp.disableMMX) + cpu_mmx = theApp.detectMMX(); + else + cpu_mmx = 0; +#endif + + systemRedShift = 19; + systemGreenShift = 11; + systemBlueShift = 3; + systemColorDepth = 32; + theApp.fsColorDepth = 32; + + Init_2xSaI(32); + + utilUpdateSystemColorMaps(theApp.cartridgeType == IMAGE_GBA && gbColorOption == 1); + theApp.updateFilter(); + theApp.updateIFB(); + pitch = theApp.filterWidth * (systemColorDepth>>3) + 4; + data = pix + ( theApp.sizeX + 1 ) * 4; + + if(failed) + return false; + + return true; +} + +//clear colour buffer +void OpenGLDisplay::clear() +{ + glClearColor(0.0,0.0,0.0,1.0); + glClear( GL_COLOR_BUFFER_BIT ); +} + +//main render func +void OpenGLDisplay::render() +{ + clear(); + + pitch = theApp.filterWidth * (systemColorDepth>>3) + 4; + data = pix + ( theApp.sizeX + 1 ) * 4; + + // apply pixel filter + if(theApp.filterFunction) { + data = filterData; + theApp.filterFunction( + pix + pitch, + pitch, + (u8*)theApp.delta, + (u8*)filterData, + width * 4 , + theApp.filterWidth, + theApp.filterHeight); + } + + // Texturemap complete texture to surface + // so we have free scaling and antialiasing + + if( theApp.filterFunction ) { + glPixelStorei( GL_UNPACK_ROW_LENGTH, width); + } else { + glPixelStorei( GL_UNPACK_ROW_LENGTH, theApp.sizeX + 1 ); + } + glTexSubImage2D(GL_TEXTURE_2D,0,0,0,width,height,GL_BGRA,GL_UNSIGNED_BYTE,data ); + + + glBegin( GL_QUADS ); + + glTexCoord2f( 0.0f, 0.0f ); + glVertex3i( 0, 0, 0 ); + + glTexCoord2f( (float)(width) / size, 0.0f ); + glVertex3i( theApp.surfaceSizeX, 0, 0 ); + + glTexCoord2f( (float)(width) / size, (float)(height) / size ); + glVertex3i( theApp.surfaceSizeX, theApp.surfaceSizeY, 0 ); + + glTexCoord2f( 0.0f, (float)(height) / size ); + glVertex3i( 0, theApp.surfaceSizeY, 0 ); + glEnd(); + + + if( theApp.showSpeed ) { // && ( theApp.videoOption > VIDEO_6X ) ) { + char buffer[30]; + if( theApp.showSpeed == 1 ) { + sprintf( buffer, "%3d%%", systemSpeed ); + } else { + sprintf( buffer, "%3d%%(%d, %d fps)", systemSpeed, systemFrameSkip, theApp.showRenderedFrames ); + } + glFontBegin(&font); + glPushMatrix(); + float fontscale = (float)theApp.surfaceSizeX / 100.0f; + glScalef(fontscale, fontscale, fontscale); + glColor4f(1.0f, 0.25f, 0.25f, 1.0f); + glFontTextOut(buffer, (theApp.surfaceSizeX-(strlen(buffer)*11))/(fontscale*2), (theApp.surfaceSizeY-20)/fontscale, 0); + glPopMatrix(); + glFontEnd(); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glBindTexture( GL_TEXTURE_2D, texture ); + } + if( theApp.screenMessage ) { + if( ( ( GetTickCount() - theApp.screenMessageTime ) < 3000 ) && !theApp.disableStatusMessage ) { + glFontBegin(&font); + glPushMatrix(); + + float fontscale = (float)theApp.surfaceSizeX / 100.0f; + glScalef(fontscale, fontscale, fontscale); + glColor4f(1.0f, 0.25f, 0.25f, 1.0f); + glFontTextOut((char *)((const char *)theApp.screenMessageBuffer), (theApp.surfaceSizeX-(theApp.screenMessageBuffer.GetLength()*11))/(fontscale*2), (theApp.surfaceSizeY-40)/fontscale, 0); + glPopMatrix(); + glFontEnd(); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glBindTexture( GL_TEXTURE_2D, texture ); + } else { + theApp.screenMessage = false; + } + } + + glFlush(); + SwapBuffers( hDC ); + // since OpenGL draws on the back buffer, + // we have to swap it to the front buffer to see the content + +} + +//resize screen +void OpenGLDisplay::resize( int w, int h ) +{ + initializeMatrices( w, h ); +} + +//update filtering methods +void OpenGLDisplay::updateFiltering( int value ) +{ + switch( value ) + { + case 0: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + break; + case 1: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + break; + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP ); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP ); +} + +//init projection matrixes and viewports +void OpenGLDisplay::initializeMatrices( int w, int h ) +{ + if( theApp.fullScreenStretch ) { + glViewport( 0, 0, w, h ); + } else { + calculateDestRect( w, h ); + glViewport( + destRect.left, + destRect.top, + destRect.right - destRect.left, + destRect.bottom - destRect.top ); + } + + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho( + /* left */ 1.0f, + /* right */ (GLdouble)(w - 1), + /* bottom */ (GLdouble)(h - 1), + /* top */ 1.0f, + 0.0f, + 1.0f ); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + +} + +//init font texture +bool OpenGLDisplay::initializeTexture( int w, int h ) +{ + // size = 2^n + // w = 24 > size = 256 = 2^8 + // w = 255 > size = 256 = 2^8 + // w = 256 > size = 512 = 2^9 + // w = 300 > size = 512 = 2^9 + // OpenGL textures have to be square and a power of 2 + // We could use methods that allow tex's to not be powers of two + // but that requires extra OGL extensions + + float n1 = log10( (float)w ) / log10( 2.0f ); + float n2 = log10( (float)h ) / log10( 2.0f ); + float n = ( n1 > n2 ) ? n1 : n2; + + if( ((float)((int)n)) != n ) { + // round up + n = ((float)((int)n)) + 1.0f; + } + + size = pow( 2.0f, n ); + + glGenTextures( 1, &texture ); + glBindTexture( GL_TEXTURE_2D, texture ); + updateFiltering( theApp.glFilter ); + + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGBA, + (GLsizei)size, + (GLsizei)size, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + NULL ); + + width = w; + height = h; + + //return ( glGetError() == GL_NO_ERROR) ? true : false; + // Workaround: We usually get GL_INVALID_VALUE, but somehow it works nevertheless + // In consequence, we must not treat it as an error or else the app behaves as if an error occured. + // This in the end results in theApp->input not being created = no input when switching from D3D to OGL + return true; +} + +//turn vsync on or off +void OpenGLDisplay::setVSync( int interval ) +{ + const char *extensions = (const char *)glGetString( GL_EXTENSIONS ); + + if( strstr( extensions, "WGL_EXT_swap_control" ) == 0 ) { + winlog( "Error: WGL_EXT_swap_control extension not supported on your computer.\n" ); + return; + } else { + PFNWGLSWAPINTERVALFARPROC wglSwapIntervalEXT = NULL; + wglSwapIntervalEXT = (PFNWGLSWAPINTERVALFARPROC)wglGetProcAddress( "wglSwapIntervalEXT" ); + if( wglSwapIntervalEXT ) { + wglSwapIntervalEXT( interval ); + } + } +} + +//change render size for fonts and filter data +bool OpenGLDisplay::changeRenderSize( int w, int h ) +{ + if( (width != w) || (height != h) ) { + if( texture != 0 ) { + glDeleteTextures( 1, &texture ); + texture = 0; + } + + if( !initializeTexture( w, h ) ) { + failed = true; + return false; + } + if (filterData) + free(filterData); + filterData = (u8 *)malloc(4*w*h); + } + + return true; +} + +//calculate RECTs +void OpenGLDisplay::calculateDestRect( int w, int h ) +{ + float scaleX = (float)w / (float)width; + float scaleY = (float)h / (float)height; + float min = (scaleX < scaleY) ? scaleX : scaleY; + if( theApp.maxScale && (min > theApp.maxScale) ) { + min = (float)theApp.maxScale; + } + destRect.left = 0; + destRect.top = 0; + destRect.right = (LONG)(width * min); + destRect.bottom = (LONG)(height * min); + if( destRect.right != w ) { + LONG diff = (w - destRect.right) / 2; + destRect.left += diff; + destRect.right += diff; + } + if( destRect.bottom != h ) { + LONG diff = (h - destRect.bottom) / 2; + destRect.top += diff; + destRect.bottom += diff; + } + +} + +//config options +void OpenGLDisplay::setOption( const char *option, int value ) +{ + if( !_tcscmp( option, _T("vsync") ) ) { + setVSync( value ); + } + + if( !_tcscmp( option, _T("glFilter") ) ) { + updateFiltering( value ); + } + + if( !_tcscmp( option, _T("maxScale") ) ) { + initializeMatrices( theApp.dest.right-theApp.dest.left, theApp.dest.bottom-theApp.dest.top ); + } + + if( !_tcscmp( option, _T("fullScreenStretch") ) ) { + initializeMatrices( theApp.dest.right-theApp.dest.left, theApp.dest.bottom-theApp.dest.top ); + } +} + +//set fullscreen mode +bool OpenGLDisplay::selectFullScreenMode( VIDEO_MODE &mode ) +{ + FullscreenSettings dlg; + dlg.setAPI( this->getType() ); + INT_PTR ret = dlg.DoModal(); + if( ret == IDOK ) { + mode.adapter = dlg.m_device; + switch( dlg.m_colorDepth ) + { + case 30: + // TODO: support + return false; + break; + case 24: + mode.bitDepth = 32; + break; + case 16: + case 15: + mode.bitDepth = 16; + break; + } + mode.width = dlg.m_width; + mode.height = dlg.m_height; + mode.frequency = dlg.m_refreshRate; + return true; + } else { + return false; + } +} + + +IDisplay *newOpenGLDisplay() +{ + return new OpenGLDisplay(); +} + +#endif // #ifndef NO_OGL diff --git a/src/win32/PaletteView.cpp b/src/win32/PaletteView.cpp new file mode 100644 index 0000000..db6c9a7 --- /dev/null +++ b/src/win32/PaletteView.cpp @@ -0,0 +1,244 @@ +#include "stdafx.h" +#include "vba.h" +#include "FileDlg.h" +#include "PaletteView.h" +#include "WinResUtil.h" + +#include "../System.h" +#include "../gba/GBA.h" +#include "../gba/Globals.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +void GBAPaletteViewControl::updatePalette() +{ + if(paletteRAM != NULL) + memcpy(palette, &paletteRAM[paletteAddress], 512); +} + +///////////////////////////////////////////////////////////////////////////// +// PaletteView dialog + + +PaletteView::PaletteView(CWnd* pParent /*=NULL*/) + : ResizeDlg(PaletteView::IDD, pParent) +{ + //{{AFX_DATA_INIT(PaletteView) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT + autoUpdate = false; +} + +PaletteView::~PaletteView() +{ +} + + +void PaletteView::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(PaletteView) + // NOTE: the ClassWizard will add DDX and DDV calls here + //}}AFX_DATA_MAP + DDX_Control(pDX, IDC_PALETTE_VIEW, paletteView); + DDX_Control(pDX, IDC_PALETTE_VIEW_OBJ, paletteViewOBJ); + DDX_Control(pDX, IDC_COLOR, colorControl); +} + + +BEGIN_MESSAGE_MAP(PaletteView, CDialog) + //{{AFX_MSG_MAP(PaletteView) + ON_BN_CLICKED(IDC_SAVE_BG, OnSaveBg) + ON_BN_CLICKED(IDC_SAVE_OBJ, OnSaveObj) + ON_BN_CLICKED(IDC_REFRESH2, OnRefresh2) + ON_BN_CLICKED(IDC_AUTO_UPDATE, OnAutoUpdate) + ON_BN_CLICKED(IDC_CLOSE, OnClose) + //}}AFX_MSG_MAP + ON_MESSAGE(WM_PALINFO, OnPalInfo) + ON_BN_CLICKED(IDC_CHANGE_BACKDROP, &PaletteView::OnBnClickedChangeBackdrop) +END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// PaletteView message handlers + +BOOL PaletteView::OnInitDialog() +{ + CDialog::OnInitDialog(); + + DIALOG_SIZER_START( sz ) + DIALOG_SIZER_END() + SetData(sz, + FALSE, + HKEY_CURRENT_USER, + "Software\\Emulators\\VisualBoyAdvance\\Viewer\\PaletteView", + NULL); + + paletteView.setPaletteAddress(0); + paletteView.refresh(); + + paletteViewOBJ.setPaletteAddress(0x200); + paletteViewOBJ.refresh(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void PaletteView::save(int which) +{ + if(rom != NULL) + { + CString captureBuffer; + + if(which == 0) + captureBuffer = "bg.pal"; + else + captureBuffer = "obj.pal"; + + LPCTSTR exts[] = {".pal", ".pal", ".act" }; + + CString filter = theApp.winLoadFilter(IDS_FILTER_PAL); + CString title = winResLoadString(IDS_SELECT_PALETTE_NAME); + FileDlg dlg(this, + captureBuffer, + filter, + 1, + "PAL", + exts, + "", + title, + true); + + if(dlg.DoModal() == IDCANCEL) { + return; + } + + captureBuffer = dlg.GetPathName(); + + PaletteViewControl *p = NULL; + + if(which == 0) + p = &paletteView; + else + p = &paletteViewOBJ; + + switch(dlg.getFilterIndex()) { + case 0: + case 1: + p->saveMSPAL(captureBuffer); + break; + case 2: + p->saveJASCPAL(captureBuffer); + break; + case 3: + p->saveAdobe(captureBuffer); + break; + } + } +} + +void PaletteView::OnSaveBg() +{ + save(0); +} + +void PaletteView::OnSaveObj() +{ + save(1); +} + +void PaletteView::OnRefresh2() +{ + paletteView.refresh(); + paletteViewOBJ.refresh(); +} + +void PaletteView::update() +{ + OnRefresh2(); +} + + +void PaletteView::OnAutoUpdate() +{ + autoUpdate = !autoUpdate; + if(autoUpdate) { + theApp.winAddUpdateListener(this); + } else { + theApp.winRemoveUpdateListener(this); + } + (CButton*)GetDlgItem(IDC_REFRESH2)->EnableWindow(!autoUpdate); +} + +void PaletteView::OnClose() +{ + theApp.winRemoveUpdateListener(this); + + DestroyWindow(); +} + +LRESULT PaletteView::OnPalInfo(WPARAM wParam, LPARAM lParam) +{ + u16 color = (u16)wParam; + u32 address = (u32)lParam; + CString buffer; + + if(address >= 0x200) + address = 0x5000200 + 2*(address & 255); + else + address = 0x5000000 + 2*(address & 255); + + buffer.Format("0x%08X", address); + GetDlgItem(IDC_ADDRESS)->SetWindowText(buffer); + + int r = (color & 0x1f); + int g = (color & 0x3e0) >> 5; + int b = (color & 0x7c00) >> 10; + + buffer.Format("%d", r); + GetDlgItem(IDC_R)->SetWindowText(buffer); + + buffer.Format("%d", g); + GetDlgItem(IDC_G)->SetWindowText(buffer); + + buffer.Format("%d", b); + GetDlgItem(IDC_B)->SetWindowText(buffer); + + buffer.Format("0x%04X", color); + GetDlgItem(IDC_VALUE)->SetWindowText(buffer); + + colorControl.setColor(color); + + if(address >= 0x5000200) { + paletteView.setSelected(-1); + } else + paletteViewOBJ.setSelected(-1); + + return TRUE; +} + +void PaletteView::PostNcDestroy() +{ + delete this; +} + +void PaletteView::OnBnClickedChangeBackdrop() +{ + CColorDialog cdlg; + COLORREF color; + switch( cdlg.DoModal() ) { + case IDOK: + color = cdlg.GetColor(); + customBackdropColor = + ( ( GetBValue( color ) >> 3 ) << 10 ) | + ( ( GetGValue( color ) >> 3 ) << 5 ) | + ( GetRValue( color ) >> 3 ); + break; + case IDCANCEL: + customBackdropColor = -1; // disable hack + break; + } +} diff --git a/src/win32/PaletteView.h b/src/win32/PaletteView.h new file mode 100644 index 0000000..7122da9 --- /dev/null +++ b/src/win32/PaletteView.h @@ -0,0 +1,73 @@ +#if !defined(AFX_PALETTEVIEW1_H__0873E3FF_9486_4B2C_8EF0_59C3B4F47162__INCLUDED_) +#define AFX_PALETTEVIEW1_H__0873E3FF_9486_4B2C_8EF0_59C3B4F47162__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// PaletteView.h : header file +// + +#include "ColorControl.h" +#include "PaletteViewControl.h" +#include "ResizeDlg.h" + +class GBAPaletteViewControl : public PaletteViewControl { + public: + virtual void updatePalette(); +}; + +///////////////////////////////////////////////////////////////////////////// +// PaletteView dialog + +class PaletteView : public ResizeDlg, IUpdateListener +{ + private: + GBAPaletteViewControl paletteView; + GBAPaletteViewControl paletteViewOBJ; + ColorControl colorControl; + bool autoUpdate; + // Construction + public: + void save(int which); + PaletteView(CWnd* pParent = NULL); // standard constructor + ~PaletteView(); + afx_msg LRESULT OnPalInfo(WPARAM wParam, LPARAM lParam); + + // Dialog Data + //{{AFX_DATA(PaletteView) + enum { IDD = IDD_PALETTE_VIEW }; + // NOTE: the ClassWizard will add data members here + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(PaletteView) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual void PostNcDestroy(); + //}}AFX_VIRTUAL + + virtual void update(); + + // Implementation + protected: + + // Generated message map functions + //{{AFX_MSG(PaletteView) + virtual BOOL OnInitDialog(); + afx_msg void OnSaveBg(); + afx_msg void OnSaveObj(); + afx_msg void OnRefresh2(); + afx_msg void OnAutoUpdate(); + afx_msg void OnClose(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +public: + afx_msg void OnBnClickedChangeBackdrop(); +}; + + //{{AFX_INSERT_LOCATION}} + // Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_PALETTEVIEW1_H__0873E3FF_9486_4B2C_8EF0_59C3B4F47162__INCLUDED_) diff --git a/src/win32/PaletteViewControl.cpp b/src/win32/PaletteViewControl.cpp new file mode 100644 index 0000000..03dec09 --- /dev/null +++ b/src/win32/PaletteViewControl.cpp @@ -0,0 +1,388 @@ +#include "stdafx.h" +#include "vba.h" +#include "PaletteViewControl.h" + +#include "../Util.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +bool PaletteViewControl::isRegistered = false; + +///////////////////////////////////////////////////////////////////////////// +// PaletteViewControl + +PaletteViewControl::PaletteViewControl() +{ + memset(&bmpInfo.bmiHeader, 0, sizeof(bmpInfo.bmiHeader)); + + bmpInfo.bmiHeader.biSize = sizeof(bmpInfo.bmiHeader); + bmpInfo.bmiHeader.biWidth = 256; + bmpInfo.bmiHeader.biHeight = -256; + bmpInfo.bmiHeader.biPlanes = 1; + bmpInfo.bmiHeader.biBitCount = 24; + bmpInfo.bmiHeader.biCompression = BI_RGB; + data = (u8 *)malloc(3 * 256 * 256); + + w = 256; + h = 256; + + colors = 256; + + paletteAddress = 0; + + ZeroMemory(palette, 512); + + selected = -1; + registerClass(); +} + +PaletteViewControl::~PaletteViewControl() +{ + if(data) + free(data); +} + + +BEGIN_MESSAGE_MAP(PaletteViewControl, CWnd) + //{{AFX_MSG_MAP(PaletteViewControl) + ON_WM_LBUTTONDOWN() + ON_WM_ERASEBKGND() + ON_WM_PAINT() + //}}AFX_MSG_MAP + END_MESSAGE_MAP() + + + ///////////////////////////////////////////////////////////////////////////// +// PaletteViewControl message handlers + +void PaletteViewControl::init(int c, int w, int h) +{ + this->w = w; + this->h = h; + this->colors = c; + + bmpInfo.bmiHeader.biWidth = w; + bmpInfo.bmiHeader.biHeight = -h; +} + + +bool PaletteViewControl::saveAdobe(const char *name) +{ + FILE *f = fopen(name, "wb"); + + if(!f) + return false; + + for(int i = 0; i < colors; i++) { + u16 c = palette[i]; + int r = (c & 0x1f) << 3; + int g = (c & 0x3e0) >> 2; + int b = (c & 0x7c00) >> 7; + + u8 data[3] = { r, g, b }; + fwrite(data, 1, 3, f); + } + if(colors < 256) { + for(int i = colors; i < 256; i++) { + u8 data[3] = { 0, 0, 0 }; + fwrite(data, 1, 3, f); + } + } + fclose(f); + + return true; +} + + +bool PaletteViewControl::saveMSPAL(const char *name) +{ + FILE *f = fopen(name, "wb"); + + if(!f) + return false; + + u8 data[4] = { 'R', 'I', 'F', 'F' }; + + fwrite(data, 1, 4, f); + utilPutDword(data, 256 * 4 + 16); + fwrite(data, 1, 4, f); + u8 data3[4] = { 'P', 'A', 'L', ' ' }; + fwrite(data3, 1, 4, f); + u8 data4[4] = { 'd', 'a', 't', 'a' }; + fwrite(data4, 1, 4, f); + utilPutDword(data, 256*4+4); + fwrite(data, 1, 4, f); + utilPutWord(&data[0], 0x0300); + utilPutWord(&data[2], 256); // causes problems if not 16 or 256 + fwrite(data, 1, 4, f); + + for(int i = 0; i < colors; i++) { + u16 c = palette[i]; + int r = (c & 0x1f) << 3; + int g = (c & 0x3e0) >> 2; + int b = (c & 0x7c00) >> 7; + + u8 data7[4] = { r, g, b, 0 }; + fwrite(data7, 1, 4, f); + } + if(colors < 256) { + for(int i = colors; i < 256; i++) { + u8 data7[4] = { 0, 0, 0, 0 }; + fwrite(data7, 1, 4, f); + } + } + fclose(f); + + return true; +} + + +bool PaletteViewControl::saveJASCPAL(const char *name) +{ + FILE *f = fopen(name, "wb"); + + if(!f) + return false; + + fprintf(f, "JASC-PAL\r\n0100\r\n256\r\n"); + + for(int i = 0; i < colors; i++) { + u16 c = palette[i]; + int r = (c & 0x1f) << 3; + int g = (c & 0x3e0) >> 2; + int b = (c & 0x7c00) >> 7; + + fprintf(f, "%d %d %d\r\n", r, g, b); + } + if(colors < 256) { + for(int i = colors; i < 256; i++) + fprintf(f, "0 0 0\r\n"); + } + fclose(f); + + return true; +} + +void PaletteViewControl::setPaletteAddress(int address) +{ + paletteAddress = address; +} + + +void PaletteViewControl::setSelected(int s) +{ + selected = s; + InvalidateRect(NULL, FALSE); +} + + +void PaletteViewControl::render(u16 color, int x, int y) +{ + u8 *start = data + y*16*w*3 + x*16*3; + int skip = w*3-16*3; + + int r = (color & 0x1f) << 3; + int g = (color & 0x3e0) >> 2; + int b = (color & 0x7c00) >> 7; + + for(int i = 0; i < 16; i++) { + *start++ = b; + *start++ = g; + *start++ = r; + + *start++ = b; + *start++ = g; + *start++ = r; + + *start++ = b; + *start++ = g; + *start++ = r; + + *start++ = b; + *start++ = g; + *start++ = r; + + *start++ = b; + *start++ = g; + *start++ = r; + + *start++ = b; + *start++ = g; + *start++ = r; + + *start++ = b; + *start++ = g; + *start++ = r; + + *start++ = b; + *start++ = g; + *start++ = r; + + *start++ = b; + *start++ = g; + *start++ = r; + + *start++ = b; + *start++ = g; + *start++ = r; + + *start++ = b; + *start++ = g; + *start++ = r; + + *start++ = b; + *start++ = g; + *start++ = r; + + *start++ = b; + *start++ = g; + *start++ = r; + + *start++ = b; + *start++ = g; + *start++ = r; + + *start++ = b; + *start++ = g; + *start++ = r; + + *start++ = b; + *start++ = g; + *start++ = r; + + start += skip; + } +} + +void PaletteViewControl::refresh() +{ + updatePalette(); + int sw = w/16; + int sh = h/16; + for(int i = 0; i < colors; i++) { + render(palette[i], i & (sw-1), i/sw); + } + InvalidateRect(NULL, FALSE); +} + +void PaletteViewControl::OnLButtonDown(UINT nFlags, CPoint point) +{ + int x = point.x; + int y = point.y; + RECT rect; + GetClientRect(&rect); + int h = rect.bottom - rect.top; + int w = rect.right - rect.left; + int sw = (this->w/16); + int sh = (this->h/16); + int mult = w / sw; + int multY = h / sh; + + setSelected(x/mult + (y/multY)*sw); + + GetParent()->SendMessage(WM_PALINFO, + palette[x/mult+(y/multY)*sw], + paletteAddress+(x/mult+(y/multY)*sw)); +} + +BOOL PaletteViewControl::OnEraseBkgnd(CDC* pDC) +{ + return TRUE; +} + + +void PaletteViewControl::OnPaint() +{ + CPaintDC dc(this); // device context for painting + + RECT rect; + GetClientRect(&rect); + int w = rect.right - rect.left; + int h = rect.bottom - rect.top; + + CDC memDC; + memDC.CreateCompatibleDC(&dc); + CBitmap bitmap, *pOldBitmap; + bitmap.CreateCompatibleBitmap(&dc, w, h); + pOldBitmap = memDC.SelectObject(&bitmap); + + StretchDIBits(memDC.GetSafeHdc(), + 0, + 0, + w, + h, + 0, + 0, + this->w, + this->h, + data, + &bmpInfo, + DIB_RGB_COLORS, + SRCCOPY); + int sw = this->w / 16; + int sh = this->h / 16; + int mult = w / sw; + int multY = h / sh; + CPen pen; + pen.CreatePen(PS_SOLID, 1, RGB(192,192,192)); + CPen *old = memDC.SelectObject(&pen); + int i; + for(i = 1; i < sh; i++) { + memDC.MoveTo(0, i * multY); + memDC.LineTo(w, i * multY); + } + for(i = 1; i < sw; i++) { + memDC.MoveTo(i * mult, 0); + memDC.LineTo(i * mult, h); + } + memDC.DrawEdge(&rect, EDGE_SUNKEN, BF_RECT); + memDC.SelectObject(old); + pen.DeleteObject(); + + if(selected != -1) { + pen.CreatePen(PS_SOLID, 2, RGB(255, 0, 0)); + old = memDC.SelectObject(&pen); + + int startX = (selected & (sw-1))*mult+1; + int startY = (selected / sw)*multY+1; + int endX = startX + mult-2; + int endY = startY + multY-2; + + memDC.MoveTo(startX, startY); + memDC.LineTo(endX, startY); + memDC.LineTo(endX, endY); + memDC.LineTo(startX, endY); + memDC.LineTo(startX, startY-1); + + memDC.SelectObject(old); + pen.DeleteObject(); + } + + dc.BitBlt(0,0,w,h, + &memDC,0,0,SRCCOPY); + + memDC.SelectObject(pOldBitmap); + bitmap.DeleteObject(); + memDC.DeleteDC(); +} + +void PaletteViewControl::registerClass() +{ + if(!isRegistered) { + WNDCLASS wc; + ZeroMemory(&wc, sizeof(wc)); + wc.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS; + wc.lpfnWndProc = (WNDPROC)::DefWindowProc; + wc.hInstance = AfxGetInstanceHandle(); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH )GetStockObject(BLACK_BRUSH); + wc.lpszMenuName = NULL; + wc.lpszClassName = "VbaPaletteViewControl"; + AfxRegisterClass(&wc); + isRegistered = true; + } +} diff --git a/src/win32/PaletteViewControl.h b/src/win32/PaletteViewControl.h new file mode 100644 index 0000000..ac84e12 --- /dev/null +++ b/src/win32/PaletteViewControl.h @@ -0,0 +1,71 @@ +#if !defined(AFX_PALETTEVIEWCONTROL_H__31F600AE_B7E5_4F6C_80B6_55E4B61FBD57__INCLUDED_) +#define AFX_PALETTEVIEWCONTROL_H__31F600AE_B7E5_4F6C_80B6_55E4B61FBD57__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// PaletteViewControl.h : header file +// +#define WM_PALINFO WM_APP+1 + +///////////////////////////////////////////////////////////////////////////// +// PaletteViewControl window + +class PaletteViewControl : public CWnd +{ + int w; + int h; + int colors; + u8 *data; + BITMAPINFO bmpInfo; + static bool isRegistered; + int selected; + protected: + u16 palette[256]; + int paletteAddress; + // Construction + public: + PaletteViewControl(); + + virtual void updatePalette()=0; + + // Attributes + public: + + // Operations + public: + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(PaletteViewControl) + //}}AFX_VIRTUAL + + // Implementation + public: + void registerClass(); + void refresh(); + void render(u16 color, int x, int y); + void setSelected(int s); + void setPaletteAddress(int address); + bool saveJASCPAL(const char *name); + bool saveMSPAL(const char *name); + bool saveAdobe(const char *name); + void init(int c, int w, int h); + virtual ~PaletteViewControl(); + + // Generated message map functions + protected: + //{{AFX_MSG(PaletteViewControl) + afx_msg void OnLButtonDown(UINT nFlags, CPoint point); + afx_msg BOOL OnEraseBkgnd(CDC* pDC); + afx_msg void OnPaint(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + + ///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_PALETTEVIEWCONTROL_H__31F600AE_B7E5_4F6C_80B6_55E4B61FBD57__INCLUDED_) diff --git a/src/win32/Reg.cpp b/src/win32/Reg.cpp new file mode 100644 index 0000000..39dd7d4 --- /dev/null +++ b/src/win32/Reg.cpp @@ -0,0 +1,394 @@ +#include "stdafx.h" +#include "VBA.h" +#include "..\gba\GBALink.h" + +static char buffer[2048]; +static HKEY vbKey = NULL; +static CString *regVbaPath = NULL; + +#define VBA_PREF "preferences" + +bool regEnabled = true; + +void regInit(const char *path, bool force) +{ + if( regEnabled ) { + DWORD disp = 0; + LONG res = RegCreateKeyEx(HKEY_CURRENT_USER, + "Software\\Emulators\\VisualBoyAdvance", + 0, + "", + REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, + NULL, + &vbKey, + &disp); + } + if( regVbaPath != NULL ) { + delete regVbaPath; + regVbaPath = NULL; + } + regVbaPath = new CString(); + regVbaPath->Format("%s\\vbam.ini", path); +} + +void regShutdown() +{ + LONG res = RegCloseKey(vbKey); +} + +const char *regGetINIPath() +{ + return *regVbaPath; +} + +char *regQueryStringValue(const char * key, char *def) +{ + if(regEnabled) { + DWORD type = 0; + DWORD size = 2048; + + LONG res = RegQueryValueEx(vbKey, + key, + NULL, + &type, + (UCHAR *)buffer, + &size); + + if(res == ERROR_SUCCESS && type == REG_SZ) + return buffer; + + return def; + } + + DWORD res = GetPrivateProfileString(VBA_PREF, + key, + def, + (LPTSTR)buffer, + 2048, + *regVbaPath); + + if(res) + return buffer; + + return def; +} + +DWORD regQueryDwordValue(const char * key, DWORD def, bool force) +{ + if(regEnabled || force) { + DWORD type = 0; + DWORD size = sizeof(DWORD); + DWORD result = 0; + + LONG res = RegQueryValueEx(vbKey, + key, + NULL, + &type, + (UCHAR *)&result, + &size); + + if(res == ERROR_SUCCESS && type == REG_DWORD) + return result; + + return def; + } + + return GetPrivateProfileInt(VBA_PREF, + key, + def, + *regVbaPath); +} + +BOOL regQueryBinaryValue(const char * key, char *value, int count) +{ + if(regEnabled) { + DWORD type = 0; + DWORD size = count; + DWORD result = 0; + + + LONG res = RegQueryValueEx(vbKey, + key, + NULL, + &type, + (UCHAR *)value, + &size); + + if(res == ERROR_SUCCESS && type == REG_BINARY) + return TRUE; + + return FALSE; + } + CString k = key; + k += "Count"; + int size = GetPrivateProfileInt(VBA_PREF, + k, + -1, + *regVbaPath); + if(size >= 0 && size < count) + count = size; + return GetPrivateProfileStruct(VBA_PREF, + key, + value, + count, + *regVbaPath); +} + +void regSetStringValue(const char * key, const char * value) +{ + if(regEnabled) { + LONG res = RegSetValueEx(vbKey, + key, + NULL, + REG_SZ, + (const UCHAR *)value, + lstrlen(value)+1); + } else { + WritePrivateProfileString(VBA_PREF, + key, + value, + *regVbaPath); + } +} + +void regSetDwordValue(const char * key, DWORD value, bool force) +{ + if(regEnabled || force) { + LONG res = RegSetValueEx(vbKey, + key, + NULL, + REG_DWORD, + (const UCHAR *)&value, + sizeof(DWORD)); + } else { + wsprintf(buffer, "%u", value); + WritePrivateProfileString(VBA_PREF, + key, + buffer, + *regVbaPath); + } +} + +void regSetBinaryValue(const char *key, char *value, int count) +{ + if(regEnabled) { + LONG res = RegSetValueEx(vbKey, + key, + NULL, + REG_BINARY, + (const UCHAR *)value, + count); + } else { + CString k = key; + k += "Count"; + wsprintf(buffer, "%u", count); + + WritePrivateProfileString(VBA_PREF, + k, + buffer, + *regVbaPath); + + WritePrivateProfileStruct(VBA_PREF, + key, + value, + count, + *regVbaPath); + } +} + +void regDeleteValue(char *key) +{ + if(regEnabled) { + LONG res = RegDeleteValue(vbKey, + key); + } else { + WritePrivateProfileString(VBA_PREF, + key, + NULL, + *regVbaPath); + } +} + +bool regCreateFileType( const char *ext, const char *type ) +{ + HKEY key; + CString temp; + temp.Format( "Software\\Classes\\%s", ext ); + LONG res = RegCreateKeyEx( + HKEY_CURRENT_USER, + temp, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, + NULL, + &key, + NULL ); + if( res == ERROR_SUCCESS ) { + RegSetValueEx( + key, + "", + 0, + REG_SZ, + (const BYTE *)type, + lstrlen(type) + 1 ); + RegCloseKey( key ); + // Notify Windows that extensions have changed + SHChangeNotify( SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL ); + return true; + } + return false; +} + +bool regAssociateType( const char *type, const char *desc, const char *application, const char *icon ) +{ + HKEY key; + CString temp; + temp.Format( "Software\\Classes\\%s", type ); + LONG res = RegCreateKeyEx( + HKEY_CURRENT_USER, + temp, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, + NULL, + &key, + NULL ); + if( res == ERROR_SUCCESS ) { + res = RegSetValueEx( + key, + "",//"FriendlyTypeName", + 0, + REG_SZ, + (const BYTE *)desc, + lstrlen(desc) + 1 ); + HKEY key2; + res = RegCreateKeyEx( + key, + "Shell\\Open\\Command", + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, + NULL, + &key2, + NULL ); + if( res == ERROR_SUCCESS ) { + RegSetValueEx( + key2, + "", + 0, + REG_SZ, + (const BYTE *)application, + lstrlen(application) + 1 ); + if( icon != NULL ) { + HKEY key3; + res = RegCreateKeyEx( + key, + "DefaultIcon", + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, + NULL, + &key3, + NULL ); + if( res == ERROR_SUCCESS ) { + RegSetValueEx( + key3, + "", + 0, + REG_SZ, + (const BYTE *)icon, + lstrlen(icon) + 1 ); + } + RegCloseKey(key3); + } + RegCloseKey(key2); + RegCloseKey(key); + // Notify Windows that extensions have changed + SHChangeNotify( SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL ); + return true; + } + RegCloseKey(key); + } + return false; +} + +static void regExportSettingsToINI(HKEY key, const char *section) +{ + char valueName[256]; + int index = 0; + while(1) { + DWORD nameSize = 256; + DWORD size = 2048; + DWORD type; + LONG res = RegEnumValue(key, + index, + valueName, + &nameSize, + NULL, + &type, + (LPBYTE)buffer, + &size); + + if(res == ERROR_SUCCESS) { + switch(type) { + case REG_DWORD: + { + char temp[256]; + wsprintf(temp, "%u", *((DWORD *)buffer)); + WritePrivateProfileString(section, + valueName, + temp, + *regVbaPath); + } + break; + case REG_SZ: + WritePrivateProfileString(section, + valueName, + buffer, + *regVbaPath); + break; + case REG_BINARY: + { + char temp[256]; + + wsprintf(temp, "%u", size); + CString k = valueName; + k += "Count"; + WritePrivateProfileString(section, + k, + temp, + *regVbaPath); + WritePrivateProfileStruct(section, + valueName, + buffer, + size, + *regVbaPath); + } + break; + } + index++; + } else + break; + } +} + +void regExportSettingsToINI() +{ + if(vbKey != NULL) { + regExportSettingsToINI(vbKey, VBA_PREF); + } + + HKEY key; + + if(RegOpenKey(HKEY_CURRENT_USER, + "Software\\Emulators\\VisualBoyAdvance\\Viewer", &key) == + ERROR_SUCCESS) { + regExportSettingsToINI(key, "Viewer"); + RegCloseKey(key); + } +} diff --git a/src/win32/Reg.h b/src/win32/Reg.h new file mode 100644 index 0000000..e095c74 --- /dev/null +++ b/src/win32/Reg.h @@ -0,0 +1,18 @@ +#ifndef VBA_REG_H +#define VBA_REG_H + +extern bool regEnabled; + +char *regQueryStringValue(const char *key, char *def); +DWORD regQueryDwordValue(const char *key, DWORD def, bool force=false); +BOOL regQueryBinaryValue(const char *key, char *value, int count); +void regSetStringValue(const char *key,const char *value); +void regSetDwordValue(const char *key,DWORD value,bool force=false); +void regSetBinaryValue(const char *key, char *value, int count); +void regDeleteValue(char *key); +void regInit(const char *, bool force = false); +void regShutdown(); +bool regCreateFileType( const char *ext, const char *type ); +bool regAssociateType( const char *type, const char *desc, const char *application, const char *icon = NULL ); +void regExportSettingsToINI(); +#endif // VBA_REG_H diff --git a/src/win32/ResizeDlg.cpp b/src/win32/ResizeDlg.cpp new file mode 100644 index 0000000..7027996 --- /dev/null +++ b/src/win32/ResizeDlg.cpp @@ -0,0 +1,561 @@ +/*---------------------------------------------------------------------- + Copyright (c) Gipsysoft. All Rights Reserved. + File: DialogSizer_Set.cpp + Web site: http://gipsysoft.com + + This software is provided 'as-is', without any express or implied warranty. + + In no event will the author be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, including + commercial applications, and to alter it and redistribute it freely, subject + to the following restrictions: + + 1) The origin of this software must not be misrepresented; you must not claim + that you wrote the original software. If you use this software in a product, + an acknowledgment in the product documentation is requested but not required. + 2) Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. Altered source is encouraged + to be submitted back to the original author so it can be shared with the + community. Please share your changes. + 3) This notice may not be removed or altered from any source distribution. + + Owner: russf@gipsysoft.com + Purpose: Main functionality for sizeable dialogs + + Store a local copy of the user settings + Subclass the window + Respond to various messages withinn the subclassed window. + + ----------------------------------------------------------------------*/ +#include "stdafx.h" +#include "VBA.h" +#include "ResizeDlg.h" +#undef ASSERT +#include "WinHelper.h" + +// moved functions to this file to reduce number of files + +struct RegistryData +{ + WINDOWPLACEMENT m_wpl; +}; + + +struct DialogData // dd +{ + HKEY hkRootSave; + LPCTSTR pcszName; + + // + // The number of items contained in the psd member. + // Used in the DeferWindowPos structure and in allocating memory + int nItemCount; + DialogSizerSizingItem *psd; + + // + // We need the smallest to respond to the WM_GETMINMAXINFO message + POINT m_ptSmallest; + + // + // We don't strictly speaking need to say how big the biggest can be but + POINT m_ptLargest; + bool m_bLargestSet; + + // + // we need this to decide how much the window has changed size when we get a WM_SIZE message + SIZE m_sizeClient; + + // + // Draw the sizing grip...or not + bool m_bMaximised; + BOOL m_bShowSizingGrip; + + WinHelper::CRect m_rcGrip; +}; + +extern bool regEnabled; +extern const char *regGetINIPath(); + +void AssertFailed(char *file, int line, char *exp) +{ + char buffer[1024]; + + sprintf(buffer, "File %s\nLine %d\nExpression %s\nPress Retry to debug", + file, line, exp); + int res = MessageBox(*theApp.m_pMainWnd, buffer, "Assertion failed!", + MB_ICONHAND | MB_SETFOREGROUND | MB_TASKMODAL | + MB_ABORTRETRYIGNORE); + + if(res == IDRETRY) { + DebugBreak(); + } else if(res == IDABORT) + SendMessage(*theApp.m_pMainWnd, WM_QUIT, 0, 0); +} + +void ApiFailure(char *pcszFilename, int nLine, char *pcszExpression ) +{ + const DWORD dwLastError = ::GetLastError(); + LPCTSTR lpMsgBuf; + (void)::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwLastError, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpMsgBuf, 0, NULL ); + + char szExeName[ MAX_PATH ]; + + if( !GetModuleFileName( NULL, szExeName, countof( szExeName ) ) ) + strcpy( szExeName, "" ); + + + char szMessage[ 1024 ]; + _snprintf( szMessage, countof( szMessage ) + , "API VERIFY Failure!" + "\nProgram: %s" + "\n" + "\nFile %s" + "\nLine %d" + "\n" + "\nExpression %s" + "\n" + "\nLast Error %d" + "\n %s" + "\n\nPress Retry to debug the application" + , szExeName + , pcszFilename + , nLine + , pcszExpression + , dwLastError + , lpMsgBuf + ); + + (void)LocalFree( (LPVOID)lpMsgBuf ); + HWND hwndParent = ::GetActiveWindow(); + hwndParent = ::GetLastActivePopup( hwndParent ); + int nCode = ::MessageBoxA( hwndParent, + szMessage, + "Debug Helper", + MB_TASKMODAL | MB_ICONHAND | MB_ABORTRETRYIGNORE | + MB_SETFOREGROUND ); + if(nCode == IDABORT) { + ::SendMessage(*theApp.m_pMainWnd, WM_QUIT, 0, 0); + } else if(nCode == IDRETRY) + DebugBreak(); +} + +long FASTCALL RegQueryValueExRecursive( HKEY hKey, LPCTSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData ) +{ + TCHAR szBuffer[ 256 ]; + R_ASSERT( lstrlen( lpValueName ) < countof( szBuffer ) ); + (void)lstrcpy( szBuffer, lpValueName ); + + LPTSTR pszBuffer = szBuffer; + LPTSTR pszLast = szBuffer; + while( *pszBuffer ) + { + if( *pszBuffer == _T('\\') || *pszBuffer == _T('/') ) + { + pszLast = pszBuffer; + lpValueName = pszLast + 1; + } + pszBuffer++; + } + + if(!regEnabled) { + if(GetPrivateProfileStruct("Viewer", + lpValueName, + lpData, + *lpcbData, + regGetINIPath())) { + *lpType = REG_BINARY; + return ERROR_SUCCESS; + } + return -1; + } + + bool m_bNeedToCloseKey = false; + if( pszLast != szBuffer ) + { + *pszLast = _T('\000'); + HKEY hkeyTemp; + long lRet = RegOpenKey( hKey, szBuffer, &hkeyTemp ); + if( lRet != ERROR_SUCCESS ) + { + return lRet; + } + hKey = hkeyTemp; + m_bNeedToCloseKey = true; + } + + long lRet = RegQueryValueEx( hKey, lpValueName, lpReserved, lpType, lpData, lpcbData ); + if( m_bNeedToCloseKey ) + { + R_VERIFY( RegCloseKey( hKey ) == ERROR_SUCCESS ); + } + return lRet; +} + +long FASTCALL RegSetValueExRecursive( HKEY hKey, LPCTSTR lpValueName, DWORD Reserved, DWORD dwType, CONST BYTE* lpData, DWORD cbData ) +{ + TCHAR szBuffer[ 256 ]; + R_ASSERT( lstrlen( lpValueName ) < countof( szBuffer ) ); + (void)lstrcpy( szBuffer, lpValueName ); + + LPTSTR pszBuffer = szBuffer; + LPTSTR pszLast = szBuffer; + while( *pszBuffer ) + { + if( *pszBuffer == _T('\\') || *pszBuffer == _T('/') ) + { + pszLast = pszBuffer; + lpValueName = pszLast + 1; + } + pszBuffer++; + } + + if(!regEnabled) { + if(WritePrivateProfileStruct("Viewer", + lpValueName, + (LPVOID)lpData, + cbData, + regGetINIPath())) { + return ERROR_SUCCESS; + } + return -1; + } + + bool m_bNeedToCloseKey = false; + if( pszLast != szBuffer ) + { + *pszLast = _T('\000'); + HKEY hkeyTemp; + long lRet = RegOpenKey( hKey, szBuffer, &hkeyTemp ); + if( lRet != ERROR_SUCCESS ) + { + lRet = RegCreateKey( hKey, szBuffer, &hkeyTemp ); + if( lRet != ERROR_SUCCESS ) + return lRet; + } + hKey = hkeyTemp; + m_bNeedToCloseKey = true; + } + + long lRet = RegSetValueEx( hKey, lpValueName, Reserved, dwType, lpData, cbData ); + if( m_bNeedToCloseKey ) + { + R_VERIFY( RegCloseKey( hKey ) == ERROR_SUCCESS ); + } + return lRet; +} + + +int ResizeDlgGetItemCount(const DialogSizerSizingItem *psd) +{ + R_ASSERT( psd ); + int nCount = 0; + while( psd->uSizeInfo != 0xFFFFFFFF ) + { + nCount++; + psd++; + } + return nCount; +} + +void ResizeDlgUpdateGripperRect( const int cx, const int cy, WinHelper::CRect &rcGrip ) +{ + const int nGripWidth = GetSystemMetrics( SM_CYVSCROLL ); + const int nGripHeight = GetSystemMetrics( SM_CXVSCROLL ); + rcGrip.left = cx - nGripWidth; + rcGrip.top = cy - nGripHeight; + rcGrip.right = cx; + rcGrip.bottom = cy; +} + +void ResizeDlgUpdateGripper( HWND hwnd, DialogData *pdd ) +{ + if( pdd->m_bShowSizingGrip ) + { + WinHelper::CRect rcOld( pdd->m_rcGrip ); + + ResizeDlgUpdateGripperRect( pdd->m_sizeClient.cx, pdd->m_sizeClient.cy, pdd->m_rcGrip ); + + // + // We also need to invalidate the combined area of the old and new rectangles + // otherwise we would have trail of grippers when we sized the dialog larger + // in any axis + (void)UnionRect( &rcOld, &rcOld, &pdd->m_rcGrip ); + (void)InvalidateRect( hwnd, &rcOld, TRUE ); + } +} + +void ResizeDlgCopyItems( DialogSizerSizingItem *psdDest, const DialogSizerSizingItem *psdSource ) + // + // Will copy all of the items in psdSource into psdDest. +{ + // + // Loop til we reach the end + while( psdSource->uSizeInfo != 0xFFFFFFFF ) + { + *psdDest = *psdSource; + psdDest++; + psdSource++; + } + // And when we do copy the last item + *psdDest = *psdSource; +} + + +ResizeDlg::ResizeDlg(UINT id, CWnd *parent) + : CDialog(id, parent) +{ + dd = NULL; +} + +void *ResizeDlg::AddDialogData() + // + // Firstly determine if the data already exists, if it does then return that, if not then we will + // create and initialise a brand new structure. +{ + DialogData *pdd = (DialogData*)dd; + if( !pdd ) { + pdd = (DialogData *)calloc(1, sizeof(DialogData)); + } + + if( pdd ) { + // + // Store some sizes etc. for later. + CRect rc; + GetWindowRect( rc ); + pdd->m_ptSmallest.x = rc.Width(); + pdd->m_ptSmallest.y = rc.Height(); + + + GetClientRect( rc ); + pdd->m_sizeClient = rc.Size(); + dd = pdd; + ResizeDlgUpdateGripperRect( pdd->m_sizeClient.cx, pdd->m_sizeClient.cy, pdd->m_rcGrip ); + } + return pdd; +} + +BOOL ResizeDlg::SetData(const DialogSizerSizingItem *psd, + BOOL bShowSizingGrip, + HKEY hkRootSave, + LPCTSTR pcszName, + SIZE *psizeMax) + // + // Setting a dialog sizeable involves subclassing the window and handling it's + // WM_SIZE messages, if we have a hkRootSave and pcszName then we will also be loading/saving + // the size and position of the window from the registry. We load from the registry when we + // subclass the window and we save to the registry when we get a WM_DESTROY. + // + // It will return non-zero for success and zero if it fails +{ + R_ASSERT( psd ); + R_ASSERT( ( hkRootSave != NULL && pcszName != NULL ) + || ( hkRootSave == NULL && pcszName == NULL ) ); + // + // Make sure all of the parameters are valid. + if( ::IsWindow( *this ) + && psd + && ( ( hkRootSave != NULL && pcszName != NULL && + !IsBadStringPtr( pcszName, 0xFFFF ) ) || + ( hkRootSave == NULL && pcszName == NULL ) ) + && ( psizeMax == NULL || !IsBadReadPtr( psizeMax, sizeof( SIZE ) ) ) + ) { + DialogData *pdd = (DialogData *)AddDialogData(); + if( pdd ) { + pdd->hkRootSave = hkRootSave; + pdd->pcszName = pcszName; + pdd->m_bShowSizingGrip = bShowSizingGrip; + pdd->nItemCount = ResizeDlgGetItemCount( psd ) + 1; + pdd->psd = (DialogSizerSizingItem *) + calloc(pdd->nItemCount, + sizeof(DialogSizerSizingItem )); + if( pdd->psd ) { + // + // Copy all of the user controls etc. for later, this way the user can quite happily + // let the structure go out of scope. + ResizeDlgCopyItems( pdd->psd, psd ); + if( psizeMax ) { + pdd->m_ptLargest.x = psizeMax->cx; + pdd->m_ptLargest.y = psizeMax->cy; + pdd->m_bLargestSet = true; + } + + // + // If the there was save info passed in then we need to make damn good use of it + // by attempting to load the RegistryData structure + if( hkRootSave && pcszName ) { + RegistryData rd; + DWORD dwSize = sizeof( RegistryData ); + DWORD dwType = REG_BINARY; + if( RegQueryValueExRecursive( hkRootSave, pcszName, NULL, &dwType, reinterpret_cast( &rd ), &dwSize ) == ERROR_SUCCESS && dwSize == sizeof( rd ) ) { + if( !(GetWindowLong( *this, GWL_STYLE ) & WS_VISIBLE) ) + rd.m_wpl.showCmd = SW_HIDE; + + VAPI( SetWindowPlacement( &rd.m_wpl ) ); + } + } + return TRUE; + } else { + free(pdd); + } + } + } + return FALSE; +} + +void ResizeDlg::UpdateWindowSize(const int cx, const int cy, HWND hwnd) +{ + DialogData *pdd = (DialogData*)dd; + if( pdd ) { + const int nDeltaX = cx - pdd->m_sizeClient.cx; + const int nDeltaY = cy - pdd->m_sizeClient.cy; + WinHelper::CDeferWindowPos def( pdd->nItemCount ); + WinHelper::CRect rc; + const DialogSizerSizingItem *psd = pdd->psd; + while( psd->uSizeInfo != 0xFFFFFFFF ) { + HWND hwndChild = ::GetDlgItem( *this, psd->uControlID ); + if( ::IsWindow( hwndChild ) ) { + VAPI( ::GetWindowRect( hwndChild, rc ) ); + (void)::MapWindowPoints( ::GetDesktopWindow(), hwnd, + (LPPOINT)&rc, 2 ); + + // + // Adjust the window horizontally + if( psd->uSizeInfo & DS_MoveX ) { + rc.left += nDeltaX; + rc.right += nDeltaX; + } + + // + // Adjust the window vertically + if( psd->uSizeInfo & DS_MoveY ) { + rc.top += nDeltaY; + rc.bottom += nDeltaY; + } + + // + // Size the window horizontally + if( psd->uSizeInfo & DS_SizeX ) { + rc.right += nDeltaX; + } + + // + // Size the window vertically + if( psd->uSizeInfo & DS_SizeY ) { + rc.bottom += nDeltaY; + } + + (void)def.DeferWindowPos( hwndChild, NULL, rc, + SWP_NOACTIVATE | SWP_NOZORDER ); + } + psd++; + } + + pdd->m_sizeClient.cx = cx; + pdd->m_sizeClient.cy = cy; + + // + // If we have a sizing grip enabled then adjust it's position + ResizeDlgUpdateGripper( hwnd, pdd ); + } +} + +BOOL ResizeDlg::OnWndMsg(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT *res ) + // Actual window procedure that will handle saving window size/position and moving + // the controls whilst the window sizes. +{ + if(dd == NULL) { + return CDialog::OnWndMsg(msg, wParam, lParam, res); + } + switch( msg ) { + case WM_ERASEBKGND: + { + BOOL r = CDialog::OnWndMsg(msg, wParam, lParam, res); + DialogData *pdd = (DialogData*)dd; + if( pdd && pdd->m_bShowSizingGrip && !pdd->m_bMaximised ) { + VAPI( ::DrawFrameControl( reinterpret_cast( wParam ), + pdd->m_rcGrip, + DFC_SCROLL, DFCS_SCROLLSIZEGRIP ) ); + } + return r; + } + case WM_SIZE: + { + DialogData *pdd = (DialogData*)dd; + if( pdd && wParam != SIZE_MINIMIZED ) { + pdd->m_bMaximised = ( wParam == SIZE_MAXIMIZED ? true : false ); + UpdateWindowSize( LOWORD( lParam ), HIWORD( lParam ), *this); + } + } + break; + case WM_NCHITTEST: + { + // + // If the gripper is enabled then perform a simple hit test on our gripper area. + DialogData *pdd = (DialogData*)dd; + if( pdd && pdd->m_bShowSizingGrip ) { + POINT pt = { LOWORD(lParam), HIWORD(lParam) }; + (void)ScreenToClient( &pt ); + if( PtInRect( pdd->m_rcGrip, pt ) ) + return (BOOL)HTBOTTOMRIGHT; + } + } + break; + case WM_GETMINMAXINFO: + { + // + // Our opportunity to say that we do not want the dialog to grow or shrink any more. + DialogData *pdd = (DialogData*)dd; + LPMINMAXINFO lpmmi = reinterpret_cast( lParam ); + lpmmi->ptMinTrackSize = pdd->m_ptSmallest; + if( pdd->m_bLargestSet ) { + lpmmi->ptMaxTrackSize = pdd->m_ptLargest; + } + } + return (BOOL)0; + case WM_NOTIFY: + { + if( reinterpret_cast(lParam)->code == PSN_SETACTIVE ) { + CRect rc; + VAPI( ::GetClientRect( *GetParent( ), &rc ) ); + UpdateWindowSize( rc.Width(), rc.Height(), *GetParent( ) ); + } + } + break; + case WM_DESTROY: + { + // + // Our opportunty for cleanup. + // Simply acquire all of our objects, free the appropriate memory and remove the + // properties from the window. If we do not remove the properties then they will constitute + // a resource leak. + DialogData *pdd = (DialogData*)dd; + if( pdd ) { + RegistryData rd; + rd.m_wpl.length = sizeof( rd.m_wpl ); + VAPI( GetWindowPlacement( &rd.m_wpl ) ); + + if( pdd->hkRootSave && pdd->pcszName ) { + (void)RegSetValueExRecursive( pdd->hkRootSave, pdd->pcszName, + NULL, REG_BINARY, + reinterpret_cast( &rd ), + sizeof( rd ) ); + } + + if( pdd->psd ) { + free(pdd->psd); + } + free(pdd); + } + + } + break; + } + return CDialog::OnWndMsg(msg, wParam, lParam, res); +} diff --git a/src/win32/ResizeDlg.h b/src/win32/ResizeDlg.h new file mode 100644 index 0000000..eeb6158 --- /dev/null +++ b/src/win32/ResizeDlg.h @@ -0,0 +1,40 @@ +#ifndef VBA_WIN32_RESIZEDLG_H +#define VBA_WIN32_RESIZEDLG_H + +#ifndef _INC_TCHAR +#include +#endif // _INC_TCHAR + +// +// Predefined sizing information +#define DS_MoveX 1 +#define DS_MoveY 2 +#define DS_SizeX 4 +#define DS_SizeY 8 + +typedef struct DialogSizerSizingItem // sdi +{ + UINT uControlID; + UINT uSizeInfo; +} DialogSizerSizingItem; + +#define DIALOG_SIZER_START( name ) DialogSizerSizingItem name[] = { +#define DIALOG_SIZER_ENTRY( controlID, flags ) { controlID, flags }, +#define DIALOG_SIZER_END() { 0xFFFFFFFF, 0xFFFFFFFF } }; + +class ResizeDlg : public CDialog { + void *dd; + public: + ResizeDlg(UINT id, CWnd *parent = NULL); + + void *AddDialogData(); + BOOL SetData(const DialogSizerSizingItem *psd, + BOOL bShowSizingGrip, + HKEY hkRootSave, + LPCTSTR pcszName, + SIZE *psizeMax); + void UpdateWindowSize(const int cx, const int cy, HWND); + + virtual BOOL OnWndMsg(UINT, WPARAM, LPARAM, LRESULT *); +}; +#endif diff --git a/src/win32/RewindInterval.cpp b/src/win32/RewindInterval.cpp new file mode 100644 index 0000000..7bb514b --- /dev/null +++ b/src/win32/RewindInterval.cpp @@ -0,0 +1,79 @@ +#include "stdafx.h" +#include "vba.h" +#include "RewindInterval.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// RewindInterval dialog + + +RewindInterval::RewindInterval(int interval, CWnd* pParent /*=NULL*/) + : CDialog(RewindInterval::IDD, pParent) +{ + //{{AFX_DATA_INIT(RewindInterval) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT + this->interval = interval; +} + + +void RewindInterval::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(RewindInterval) + DDX_Control(pDX, IDC_INTERVAL, m_interval); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(RewindInterval, CDialog) + //{{AFX_MSG_MAP(RewindInterval) + ON_BN_CLICKED(ID_CANCEL, OnCancel) + ON_BN_CLICKED(ID_OK, OnOk) + //}}AFX_MSG_MAP + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// RewindInterval message handlers + +void RewindInterval::OnCancel() +{ + EndDialog(-1); +} + +void RewindInterval::OnOk() +{ + CString buffer; + + m_interval.GetWindowText(buffer); + + int v = atoi(buffer); + + if(v >= 0 && v <= 600) { + EndDialog(v); + } else + systemMessage(IDS_INVALID_INTERVAL_VALUE, + "Invalid rewind interval value. Please enter a number " + "between 0 and 600 seconds"); +} + +BOOL RewindInterval::OnInitDialog() +{ + CDialog::OnInitDialog(); + + m_interval.LimitText(3); + + CString buffer; + buffer.Format("%d", interval); + m_interval.SetWindowText(buffer); + + CenterWindow(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} diff --git a/src/win32/RewindInterval.h b/src/win32/RewindInterval.h new file mode 100644 index 0000000..8cbe244 --- /dev/null +++ b/src/win32/RewindInterval.h @@ -0,0 +1,49 @@ +#if !defined(AFX_REWINDINTERVAL_H__C95AFF44_1F64_44C8_BAAB_A54B982D28EA__INCLUDED_) +#define AFX_REWINDINTERVAL_H__C95AFF44_1F64_44C8_BAAB_A54B982D28EA__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// RewindInterval.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// RewindInterval dialog + +class RewindInterval : public CDialog +{ + // Construction + public: + int interval; + RewindInterval(int interval, CWnd* pParent = NULL); // standard constructor + + // Dialog Data + //{{AFX_DATA(RewindInterval) + enum { IDD = IDD_REWIND_INTERVAL }; + CEdit m_interval; + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(RewindInterval) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + + // Implementation + protected: + + // Generated message map functions + //{{AFX_MSG(RewindInterval) + afx_msg void OnCancel(); + afx_msg void OnOk(); + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + + //{{AFX_INSERT_LOCATION}} + // Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_REWINDINTERVAL_H__C95AFF44_1F64_44C8_BAAB_A54B982D28EA__INCLUDED_) diff --git a/src/win32/RomInfo.cpp b/src/win32/RomInfo.cpp new file mode 100644 index 0000000..91d65f2 --- /dev/null +++ b/src/win32/RomInfo.cpp @@ -0,0 +1,578 @@ +#include "stdafx.h" +#include "vba.h" +#include "RomInfo.h" +#include "WinResUtil.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +extern int gbRomSize; + +struct WinGBACompanyName { + LPCTSTR code; + LPCTSTR name; +}; + +static WinGBACompanyName winGBARomInfoCompanies[] = { + { "01", "Nintendo" }, + { "02", "Rocket Games" }, + { "08", "Capcom" }, + { "09", "Hot B Co." }, + { "0A", "Jaleco" }, + { "0B", "Coconuts Japan" }, + { "0C", "Coconuts Japan/G.X.Media" }, + { "0H", "Starfish" }, + { "0L", "Warashi Inc." }, + { "0N", "Nowpro" }, + { "0P", "Game Village" }, + { "13", "Electronic Arts Japan" }, + { "18", "Hudson Soft Japan" }, + { "19", "S.C.P." }, + { "1A", "Yonoman" }, + { "1G", "SMDE" }, + { "1P", "Creatures Inc." }, + { "1Q", "TDK Deep Impresion" }, + { "20", "Destination Software" }, + { "22", "VR 1 Japan" }, + { "25", "San-X" }, + { "28", "Kemco Japan" }, + { "29", "Seta" }, + { "2H", "Ubisoft Japan" }, + { "2K", "NEC InterChannel" }, + { "2L", "Tam" }, + { "2M", "Jordan" }, + { "2N", "Smilesoft" }, + { "2Q", "Mediakite" }, + { "36", "Codemasters" }, + { "37", "GAGA Communications" }, + { "38", "Laguna" }, + { "39", "Telstar Fun and Games" }, + { "41", "Ubi Soft Entertainment" }, + { "42", "Sunsoft" }, + { "47", "Spectrum Holobyte" }, + { "49", "IREM" }, + { "4D", "Malibu Games" }, + { "4F", "Eidos/U.S. Gold" }, + { "4J", "Fox Interactive" }, + { "4K", "Time Warner Interactive" }, + { "4Q", "Disney" }, + { "4S", "Black Pearl" }, + { "4X", "GT Interactive" }, + { "4Y", "RARE" }, + { "4Z", "Crave Entertainment" }, + { "50", "Absolute Entertainment" }, + { "51", "Acclaim" }, + { "52", "Activision" }, + { "53", "American Sammy Corp." }, + { "54", "Take 2 Interactive" }, + { "55", "Hi Tech" }, + { "56", "LJN LTD." }, + { "58", "Mattel" }, + { "5A", "Mindscape/Red Orb Ent." }, + { "5C", "Taxan" }, + { "5D", "Midway" }, + { "5F", "American Softworks" }, + { "5G", "Majesco Sales Inc" }, + { "5H", "3DO" }, + { "5K", "Hasbro" }, + { "5L", "NewKidCo" }, + { "5M", "Telegames" }, + { "5N", "Metro3D" }, + { "5P", "Vatical Entertainment" }, + { "5Q", "LEGO Media" }, + { "5S", "Xicat Interactive" }, + { "5T", "Cryo Interactive" }, + { "5W", "Red Storm Ent./BKN Ent." }, + { "5X", "Microids" }, + { "5Z", "Conspiracy Entertainment Corp." }, + { "60", "Titus Interactive Studios" }, + { "61", "Virgin Interactive" }, + { "62", "Maxis" }, + { "64", "LucasArts Entertainment" }, + { "67", "Ocean" }, + { "69", "Electronic Arts" }, + { "6E", "Elite Systems Ltd." }, + { "6F", "Electro Brain" }, + { "6G", "The Learning Company" }, + { "6H", "BBC" }, + { "6J", "Software 2000" }, + { "6L", "BAM! Entertainment" }, + { "6M", "Studio 3" }, + { "6Q", "Classified Games" }, + { "6S", "TDK Mediactive" }, + { "6U", "DreamCatcher" }, + { "6V", "JoWood Productions" }, + { "6W", "SEGA" }, + { "6X", "Wannado Edition" }, + { "6Y", "LSP" }, + { "6Z", "ITE Media" }, + { "70", "Infogrames" }, + { "71", "Interplay" }, + { "72", "JVC Musical Industries Inc" }, + { "73", "Parker Brothers" }, + { "75", "SCI" }, + { "78", "THQ" }, + { "79", "Accolade" }, + { "7A", "Triffix Ent. Inc." }, + { "7C", "Microprose Software" }, + { "7D", "Universal Interactive Studios" }, + { "7F", "Kemco" }, + { "7G", "Rage Software" }, + { "7H", "Encore" }, + { "7J", "Zoo" }, + { "7K", "BVM" }, + { "7L", "Simon & Schuster Interactive" }, + { "7M", "Asmik Ace Entertainment Inc./AIA" }, + { "7N", "Empire Interactive" }, + { "7Q", "Jester Interactive" }, + { "7T", "Scholastic" }, + { "7U", "Ignition Entertainment" }, + { "7W", "Stadlbauer" }, + { "80", "Misawa" }, + { "83", "LOZC" }, + { "8B", "Bulletproof Software" }, + { "8C", "Vic Tokai Inc." }, + { "8J", "General Entertainment" }, + { "8N", "Success" }, + { "8P", "SEGA Japan" }, + { "91", "Chun Soft" }, + { "92", "Video System" }, + { "93", "BEC" }, + { "96", "Yonezawa/S'pal" }, + { "97", "Kaneko" }, + { "99", "Victor Interactive Software" }, + { "9A", "Nichibutsu/Nihon Bussan" }, + { "9B", "Tecmo" }, + { "9C", "Imagineer" }, + { "9F", "Nova" }, + { "9H", "Bottom Up" }, + { "9L", "Hasbro Japan" }, + { "9N", "Marvelous Entertainment" }, + { "9P", "Keynet Inc." }, + { "9Q", "Hands-On Entertainment" }, + { "A0", "Telenet" }, + { "A1", "Hori" }, + { "A4", "Konami" }, + { "A6", "Kawada" }, + { "A7", "Takara" }, + { "A9", "Technos Japan Corp." }, + { "AA", "JVC" }, + { "AC", "Toei Animation" }, + { "AD", "Toho" }, + { "AF", "Namco" }, + { "AG", "Media Rings Corporation" }, + { "AH", "J-Wing" }, + { "AK", "KID" }, + { "AL", "MediaFactory" }, + { "AP", "Infogrames Hudson" }, + { "AQ", "Kiratto. Ludic Inc" }, + { "B0", "Acclaim Japan" }, + { "B1", "ASCII" }, + { "B2", "Bandai" }, + { "B4", "Enix" }, + { "B6", "HAL Laboratory" }, + { "B7", "SNK" }, + { "B9", "Pony Canyon Hanbai" }, + { "BA", "Culture Brain" }, + { "BB", "Sunsoft" }, + { "BD", "Sony Imagesoft" }, + { "BF", "Sammy" }, + { "BG", "Magical" }, + { "BJ", "Compile" }, + { "BL", "MTO Inc." }, + { "BN", "Sunrise Interactive" }, + { "BP", "Global A Entertainment" }, + { "BQ", "Fuuki" }, + { "C0", "Taito" }, + { "C2", "Kemco" }, + { "C3", "Square Soft" }, + { "C5", "Data East" }, + { "C6", "Tonkin House" }, + { "C8", "Koei" }, + { "CA", "Konami/Palcom/Ultra" }, + { "CB", "Vapinc/NTVIC" }, + { "CC", "Use Co.,Ltd." }, + { "CD", "Meldac" }, + { "CE", "FCI/Pony Canyon" }, + { "CF", "Angel" }, + { "CM", "Konami Computer Entertainment Osaka" }, + { "CP", "Enterbrain" }, + { "D1", "Sofel" }, + { "D2", "Quest" }, + { "D3", "Sigma Enterprises" }, + { "D4", "Ask Kodansa" }, + { "D6", "Naxat" }, + { "D7", "Copya System" }, + { "D9", "Banpresto" }, + { "DA", "TOMY" }, + { "DB", "LJN Japan" }, + { "DD", "NCS" }, + { "DF", "Altron Corporation" }, + { "DH", "Gaps Inc." }, + { "DN", "ELF" }, + { "E2", "Yutaka" }, + { "E3", "Varie" }, + { "E5", "Epoch" }, + { "E7", "Athena" }, + { "E8", "Asmik Ace Entertainment Inc." }, + { "E9", "Natsume" }, + { "EA", "King Records" }, + { "EB", "Atlus" }, + { "EC", "Epic/Sony Records" }, + { "EE", "IGS" }, + { "EL", "Spike" }, + { "EM", "Konami Computer Entertainment Tokyo" }, + { "EN", "Alphadream Corporation" }, + { "F0", "A Wave" }, + { "G1", "PCCW" }, + { "G4", "KiKi Co Ltd" }, + { "G5", "Open Sesame Inc." }, + { "G6", "Sims" }, + { "G7", "Broccoli" }, + { "G8", "Avex" }, + { "G9", "D3 Publisher" }, + { "GB", "Konami Computer Entertainment Japan" }, + { "GD", "Square-Enix" }, + { "HY", "Sachen" }, + { NULL, NULL } +}; + +static LPCTSTR winGBARomInfoFindMakerCode(LPCTSTR code) +{ + int i = 0; + while(winGBARomInfoCompanies[i].code) { + if(!strcmp(winGBARomInfoCompanies[i].code, code)) + return winGBARomInfoCompanies[i].name; + i++; + } + return (LPCTSTR)winResLoadString(IDS_UNKNOWN); +} + + +///////////////////////////////////////////////////////////////////////////// +// RomInfoGB dialog + + +RomInfoGB::RomInfoGB(u8 *rom, CWnd* pParent /*=NULL*/) + : CDialog(RomInfoGB::IDD, pParent) +{ + //{{AFX_DATA_INIT(RomInfoGB) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT + this->rom = rom; +} + + +void RomInfoGB::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(RomInfoGB) + // NOTE: the ClassWizard will add DDX and DDV calls here + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(RomInfoGB, CDialog) + //{{AFX_MSG_MAP(RomInfoGB) + ON_BN_CLICKED(ID_OK, OnOk) + //}}AFX_MSG_MAP + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// RomInfoGB message handlers + +void RomInfoGB::OnOk() +{ + EndDialog(TRUE); +} + +BOOL RomInfoGB::OnInitDialog() +{ + CDialog::OnInitDialog(); + + char buffer[128]; + + strncpy(buffer, (const char *)&rom[0x134], 15); + buffer[15] = 0; + GetDlgItem(IDC_ROM_TITLE)->SetWindowText(buffer); + + sprintf(buffer, "%02x", rom[0x143]); + GetDlgItem(IDC_ROM_COLOR)->SetWindowText(buffer); + + strncpy(buffer, (const char *)&rom[0x144],2); + buffer[2] = 0; + GetDlgItem(IDC_ROM_MAKER_CODE)->SetWindowText(buffer); + + if(rom[0x14b] != 0x33) { + sprintf(buffer, "%02X", rom[0x14b]); + GetDlgItem(IDC_ROM_MAKER_CODE)->SetWindowText(buffer); + } + GetDlgItem(IDC_ROM_MAKER_NAME2)->SetWindowText(winGBARomInfoFindMakerCode(buffer)); + + sprintf(buffer, "%02x", rom[0x146]); + GetDlgItem(IDC_ROM_UNIT_CODE)->SetWindowText(buffer); + + CString type = winResLoadString(IDS_UNKNOWN); + switch(rom[0x147]) { + case 0x00: + type = "ROM"; + break; + case 0x01: + type = "ROM+MBC1"; + break; + case 0x02: + type = "ROM+MBC1+RAM"; + break; + case 0x03: + type = "ROM+MBC1+RAM+BATT"; + break; + case 0x05: + type = "ROM+MBC2"; + break; + case 0x06: + type = "ROM+MBC2+BATT"; + break; + case 0x0b: + type = "ROM+MMM01"; + break; + case 0x0c: + type = "ROM+MMM01+RAM"; + break; + case 0x0d: + type = "ROM+MMM01+RAM+BATT"; + break; + case 0x0f: + type = "ROM+MBC3+TIMER+BATT"; + break; + case 0x10: + type = "ROM+MBC3+TIMER+RAM+BATT"; + break; + case 0x11: + type = "ROM+MBC3"; + break; + case 0x12: + type = "ROM+MBC3+RAM"; + break; + case 0x13: + type = "ROM+MBC3+RAM+BATT"; + break; + case 0x19: + type = "ROM+MBC5"; + break; + case 0x1a: + type = "ROM+MBC5+RAM"; + break; + case 0x1b: + type = "ROM+MBC5+RAM+BATT"; + break; + case 0x1c: + type = "ROM+MBC5+RUMBLE"; + break; + case 0x1d: + type = "ROM+MBC5+RUMBLE+RAM"; + break; + case 0x1e: + type = "ROM+MBC5+RUMBLE+RAM+BATT"; + break; + case 0x22: + type = "ROM+MBC7+BATT"; + break; + case 0x55: + type = "GameGenie"; + break; + case 0x56: + type = "GameShark V3.0"; + break; + case 0xfc: + type = "ROM+POCKET CAMERA"; + break; + case 0xfd: + type = "ROM+BANDAI TAMA5"; + break; + case 0xfe: + type = "ROM+HuC-3"; + break; + case 0xff: + type = "ROM+HuC-1"; + break; + } + sprintf(buffer, "%02x (%s)", rom[0x147], (const char *)type); + GetDlgItem(IDC_ROM_DEVICE_TYPE)->SetWindowText(buffer); + + type = winResLoadString(IDS_UNKNOWN); + switch(rom[0x148]) { + case 0: + type = "32K"; + break; + case 1: + type = "64K"; + break; + case 2: + type = "128K"; + break; + case 3: + type = "256K"; + break; + case 4: + type = "512K"; + break; + case 5: + type = "1M"; + break; + case 6: + type = "2M"; + break; + case 7: + type = "4M"; + break; + } + + sprintf(buffer, "%02x (%s)", rom[0x148], (const char *)type); + GetDlgItem(IDC_ROM_SIZE)->SetWindowText(buffer); + + type = winResLoadString(IDS_UNKNOWN); + switch(rom[0x149]) { + case 0: + type = winResLoadString(IDS_NONE); + break; + case 1: + type = "2K"; + break; + case 2: + type = "8K"; + break; + case 3: + type = "32K"; + break; + case 4: + type = "128K"; + break; + case 5: + type = "64K"; + break; + } + + sprintf(buffer, "%02x (%s)", rom[0x149], (const char *)type); + GetDlgItem(IDC_ROM_RAM_SIZE)->SetWindowText(buffer); + + sprintf(buffer, "%02x", rom[0x14a]); + GetDlgItem(IDC_ROM_DEST_CODE)->SetWindowText(buffer); + + sprintf(buffer, "%02x", rom[0x14b]); + GetDlgItem(IDC_ROM_LIC_CODE)->SetWindowText(buffer); + + sprintf(buffer, "%02x", rom[0x14c]); + GetDlgItem(IDC_ROM_VERSION)->SetWindowText(buffer); + + u8 crc = 25; + int i; + for(i = 0x134; i < 0x14d; i++) { + crc += rom[i]; + } + + crc = 256 - crc; + + sprintf(buffer, "%02x (%02x)", crc, rom[0x14d]); + GetDlgItem(IDC_ROM_CRC)->SetWindowText(buffer); + + u16 crc16 = 0; + for(i = 0; i < gbRomSize; i++) { + crc16 += rom[i]; + } + + crc16 -= rom[0x14e]; + crc16 -= rom[0x14f]; + sprintf(buffer, "%04x (%04x)", crc16, (rom[0x14e]<<8)|rom[0x14f]); + GetDlgItem(IDC_ROM_CHECKSUM)->SetWindowText(buffer); + + CenterWindow(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} +///////////////////////////////////////////////////////////////////////////// +// RomInfoGBA dialog + + +RomInfoGBA::RomInfoGBA(u8 *rom, CWnd* pParent /*=NULL*/) + : CDialog(RomInfoGBA::IDD, pParent) +{ + //{{AFX_DATA_INIT(RomInfoGBA) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT + this->rom = rom; +} + + +void RomInfoGBA::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(RomInfoGBA) + // NOTE: the ClassWizard will add DDX and DDV calls here + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(RomInfoGBA, CDialog) + //{{AFX_MSG_MAP(RomInfoGBA) + ON_BN_CLICKED(ID_OK, OnOk) + //}}AFX_MSG_MAP + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// RomInfoGBA message handlers + +void RomInfoGBA::OnOk() +{ + EndDialog(TRUE); +} + +BOOL RomInfoGBA::OnInitDialog() +{ + CDialog::OnInitDialog(); + + char buffer[13]; + + strncpy(buffer, (const char *)&rom[0xa0], 12); + buffer[12] = 0; + GetDlgItem(IDC_ROM_TITLE)->SetWindowText(buffer); + + strncpy(buffer, (const char *)&rom[0xac], 4); + buffer[4] = 0; + GetDlgItem(IDC_ROM_GAME_CODE)->SetWindowText(buffer); + + strncpy(buffer, (const char *)&rom[0xb0],2); + buffer[2] = 0; + GetDlgItem(IDC_ROM_MAKER_CODE)->SetWindowText(buffer); + + GetDlgItem(IDC_ROM_MAKER_NAME)->SetWindowText(winGBARomInfoFindMakerCode(buffer)); + + sprintf(buffer, "%02x", rom[0xb3]); + GetDlgItem(IDC_ROM_UNIT_CODE)->SetWindowText(buffer); + + sprintf(buffer, "%02x", rom[0xb4]); + if( rom[0xb4] & 0x80 ) { + strcat(buffer, " (DACS)"); + } + GetDlgItem(IDC_ROM_DEVICE_TYPE)->SetWindowText(buffer); + + sprintf(buffer, "%02x", rom[0xbc]); + GetDlgItem(IDC_ROM_VERSION)->SetWindowText(buffer); + + u8 crc = 0x19; + for(int i = 0xa0; i < 0xbd; i++) { + crc += rom[i]; + } + + crc = (-crc) & 255; + + sprintf(buffer, "%02x (%02x)", crc, rom[0xbd]); + GetDlgItem(IDC_ROM_CRC)->SetWindowText(buffer); + CenterWindow(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} diff --git a/src/win32/RomInfo.h b/src/win32/RomInfo.h new file mode 100644 index 0000000..804dbcb --- /dev/null +++ b/src/win32/RomInfo.h @@ -0,0 +1,82 @@ +#if !defined(AFX_ROMINFO_H__9888A45C_3E71_4C0F_B119_EFC74DFF8CD3__INCLUDED_) +#define AFX_ROMINFO_H__9888A45C_3E71_4C0F_B119_EFC74DFF8CD3__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// RomInfo.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// RomInfoGB dialog + +class RomInfoGB : public CDialog +{ + // Construction + public: + RomInfoGB(u8 *rom, CWnd* pParent = NULL); // standard constructor + + // Dialog Data + //{{AFX_DATA(RomInfoGB) + enum { IDD = IDD_GB_ROM_INFO }; + // NOTE: the ClassWizard will add data members here + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(RomInfoGB) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + u8 *rom; + + // Implementation + protected: + + // Generated message map functions + //{{AFX_MSG(RomInfoGB) + afx_msg void OnOk(); + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + + ///////////////////////////////////////////////////////////////////////////// +// RomInfoGBA dialog + +class RomInfoGBA : public CDialog +{ + // Construction + public: + RomInfoGBA(u8 *rom, CWnd* pParent = NULL); // standard constructor + + // Dialog Data + //{{AFX_DATA(RomInfoGBA) + enum { IDD = IDD_GBA_ROM_INFO }; + // NOTE: the ClassWizard will add data members here + //}}AFX_DATA + u8 *rom; + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(RomInfoGBA) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + + // Implementation + protected: + + // Generated message map functions + //{{AFX_MSG(RomInfoGBA) + afx_msg void OnOk(); + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + //{{AFX_INSERT_LOCATION}} + // Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_ROMINFO_H__9888A45C_3E71_4C0F_B119_EFC74DFF8CD3__INCLUDED_) diff --git a/src/win32/SelectPlugin.cpp b/src/win32/SelectPlugin.cpp new file mode 100644 index 0000000..6ac2504 --- /dev/null +++ b/src/win32/SelectPlugin.cpp @@ -0,0 +1,173 @@ +#include "stdafx.h" +#include "vba.h" +#include "SelectPlugin.h" +#include "rpi.h" +#include "reg.h" +#include +#include + +using namespace std; + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +vector rpiPool; + +///////////////////////////////////////////////////////////////////////////// +// SelectPlugin dialog + + +SelectPlugin::SelectPlugin(CWnd* pParent /*=NULL*/) + : CDialog(SelectPlugin::IDD, pParent) +{ + //{{AFX_DATA_INIT(SelectPlugin) + //}}AFX_DATA_INIT +} + + +void SelectPlugin::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(SelectPlugin) + DDX_Control(pDX, IDC_COMBO_PLUGIN, m_comboPlugin); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(SelectPlugin, CDialog) + //{{AFX_MSG_MAP(SelectPlugin) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// SelectPlugin message handlers + +void SelectPlugin::OnOK() +{ + // TODO: Add extra validation here + if (m_comboPlugin.GetCount() > 0) + { + int nSel = m_comboPlugin.GetCurSel(); + if (nSel >= 0 && nSel < (int)rpiPool.size()) + strcpy(theApp.pluginName, rpiPool[nSel].sFile); + } + + CDialog::OnOK(); +} + +void SelectPlugin::OnCancel() +{ + // TODO: Add extra cleanup here + + CDialog::OnCancel(); +} + +BOOL SelectPlugin::OnInitDialog() +{ + CDialog::OnInitDialog(); + + m_comboPlugin.ResetContent(); + + size_t nPluginCnt = EnumPlugins(); + if (nPluginCnt > 0) + { + for (size_t i = 0; i < rpiPool.size(); i++) + m_comboPlugin.AddString(rpiPool[i].sDesc); + + for (size_t ii = 0; ii < rpiPool.size(); ii++) + { + if (_stricmp(theApp.pluginName, rpiPool[ii].sFile) == 0) + { + m_comboPlugin.SetCurSel(ii); + break; + } + } + } + + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +size_t SelectPlugin::EnumPlugins() +{ + rpiPool.clear(); + + char sFindFile[MAX_PATH]; + char *ptr; + + GetModuleFileName(NULL, sFindFile, sizeof(sFindFile)); + ptr = strrchr(sFindFile, '\\'); + if (ptr) + *ptr = '\0'; + strcat(sFindFile, "\\plugins\\*.rpi"); + + PluginDesc plugDesc; + WIN32_FIND_DATA FindFileData; + HANDLE hFind; + + memset(&FindFileData, 0, sizeof(FindFileData)); + hFind = FindFirstFile(sFindFile, &FindFileData); + if (hFind != INVALID_HANDLE_VALUE) + { + if (GetPluginDesc(FindFileData.cFileName, &plugDesc)) + rpiPool.push_back(plugDesc); + + while (true) + { + memset(&FindFileData, 0, sizeof(FindFileData)); + if (!FindNextFile(hFind, &FindFileData)) + break; + + if (GetPluginDesc(FindFileData.cFileName, &plugDesc)) + rpiPool.push_back(plugDesc); + } + FindClose(hFind); + } + + return rpiPool.size(); +} + +bool SelectPlugin::GetPluginDesc(const char *sRpi, PluginDesc *pDesc) +{ + HINSTANCE rpiDLL = NULL; + char sFile[MAX_PATH]; + char *ptr; + + GetModuleFileName(NULL, sFile, sizeof(sFile)); + ptr = strrchr(sFile, '\\'); + if (ptr) + *ptr = '\0'; + strcat(sFile, "\\plugins\\"); + strcat(sFile, sRpi); + + rpiDLL = LoadLibrary(sFile); + if (!rpiDLL) + return false; + + RENDPLUG_GetInfo fnGetInfo = (RENDPLUG_GetInfo) GetProcAddress(rpiDLL, "RenderPluginGetInfo"); + RENDPLUG_Output fnOutput = (RENDPLUG_Output) GetProcAddress(rpiDLL, "RenderPluginOutput"); + if (fnGetInfo == NULL || fnOutput == NULL) + { + FreeLibrary(rpiDLL); + rpiDLL = NULL; + return false; + } + + RENDER_PLUGIN_INFO *pRPI = fnGetInfo(); + if (pRPI == NULL) + { + FreeLibrary(rpiDLL); + return false; + } + + memset(pDesc, 0, sizeof(PluginDesc)); + strcpy(pDesc->sFile, sRpi); + strcpy(pDesc->sDesc, pRPI->Name); + FreeLibrary(rpiDLL); + + return true; +} diff --git a/src/win32/SelectPlugin.h b/src/win32/SelectPlugin.h new file mode 100644 index 0000000..005fb60 --- /dev/null +++ b/src/win32/SelectPlugin.h @@ -0,0 +1,55 @@ +#if !defined(AFX_SELECTPLUGIN_H__A097B9D0_7C23_4C1A_A2D3_1AEC07501BBC__INCLUDED_) +#define AFX_SELECTPLUGIN_H__A097B9D0_7C23_4C1A_A2D3_1AEC07501BBC__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// SelectPlugin.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// SelectPlugin dialog +struct PluginDesc +{ + char sFile[MAX_PATH]; + char sDesc[60]; +}; + +class SelectPlugin : public CDialog +{ +// Construction +public: + SelectPlugin(CWnd* pParent = NULL); // standard constructor + size_t EnumPlugins(); + bool GetPluginDesc(const char *sRpi, PluginDesc *pDesc); + +// Dialog Data + //{{AFX_DATA(SelectPlugin) + enum { IDD = IDD_SELECT_PLUGIN }; + CComboBox m_comboPlugin; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(SelectPlugin) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(SelectPlugin) + virtual void OnOK(); + virtual void OnCancel(); + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_SELECTPLUGIN_H__A097B9D0_7C23_4C1A_A2D3_1AEC07501BBC__INCLUDED_) diff --git a/src/win32/StringTokenizer.cpp b/src/win32/StringTokenizer.cpp new file mode 100644 index 0000000..59ff007 --- /dev/null +++ b/src/win32/StringTokenizer.cpp @@ -0,0 +1,47 @@ +#include "stdafx.h" +#include "vba.h" +#include "StringTokenizer.h" + +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +StringTokenizer::StringTokenizer(CString str, CString del) +{ + m_right = str; + m_delim = del; +} + +StringTokenizer::~StringTokenizer() +{ + +} + + +const char *StringTokenizer::next() +{ + int index = m_right.FindOneOf(m_delim); + + while(index == 0) { + m_right = m_right.Right(m_right.GetLength()-1); + index = m_right.FindOneOf(m_delim); + } + if(index == -1) { + if(m_right.IsEmpty()) + return NULL; + m_token = m_right; + m_right.Empty(); + return m_token; + } + + m_token = m_right.Left(index); + m_right = m_right.Right(m_right.GetLength()-(1+index)); + + return m_token; +} diff --git a/src/win32/StringTokenizer.h b/src/win32/StringTokenizer.h new file mode 100644 index 0000000..6b8eb13 --- /dev/null +++ b/src/win32/StringTokenizer.h @@ -0,0 +1,21 @@ +#if !defined(AFX_STRINGTOKENIZER_H__1AB4CD12_6B7A_49E4_A87F_75D3DC3FF20F__INCLUDED_) +#define AFX_STRINGTOKENIZER_H__1AB4CD12_6B7A_49E4_A87F_75D3DC3FF20F__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +class StringTokenizer +{ + public: + const char * next(); + StringTokenizer(CString str, CString token); + virtual ~StringTokenizer(); + + private: + CString m_token; + CString m_delim; + CString m_right; +}; + +#endif // !defined(AFX_STRINGTOKENIZER_H__1AB4CD12_6B7A_49E4_A87F_75D3DC3FF20F__INCLUDED_) diff --git a/src/win32/Throttle.cpp b/src/win32/Throttle.cpp new file mode 100644 index 0000000..2cb2dc4 --- /dev/null +++ b/src/win32/Throttle.cpp @@ -0,0 +1,66 @@ +#include "stdafx.h" +#include "vba.h" +#include "Throttle.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// Throttle dialog + + +Throttle::Throttle(CWnd* pParent /*=NULL*/) + : CDialog(Throttle::IDD, pParent) +{ + //{{AFX_DATA_INIT(Throttle) + m_throttle = 0; + //}}AFX_DATA_INIT +} + + +void Throttle::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(Throttle) + DDX_Text(pDX, IDC_THROTTLE, m_throttle); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(Throttle, CDialog) + //{{AFX_MSG_MAP(Throttle) + ON_BN_CLICKED(ID_CANCEL, OnCancel) + ON_BN_CLICKED(ID_OK, OnOk) + //}}AFX_MSG_MAP + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// Throttle message handlers + +BOOL Throttle::OnInitDialog() +{ + CDialog::OnInitDialog(); + + CenterWindow(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void Throttle::OnCancel() +{ + EndDialog(false); +} + +void Throttle::OnOk() +{ + UpdateData(); + + if(m_throttle < 5 || m_throttle > 1000) + systemMessage(IDS_INVALID_THROTTLE_VALUE, "Invalid throttle value. Please enter a number between 5 and 1000"); + else + EndDialog(m_throttle); +} diff --git a/src/win32/Throttle.h b/src/win32/Throttle.h new file mode 100644 index 0000000..0a9dfd2 --- /dev/null +++ b/src/win32/Throttle.h @@ -0,0 +1,48 @@ +#if !defined(AFX_THROTTLE_H__5F03B6E9_0C43_4933_A7BC_1618428C2B7F__INCLUDED_) +#define AFX_THROTTLE_H__5F03B6E9_0C43_4933_A7BC_1618428C2B7F__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// Throttle.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// Throttle dialog + +class Throttle : public CDialog +{ + // Construction + public: + Throttle(CWnd* pParent = NULL); // standard constructor + + // Dialog Data + //{{AFX_DATA(Throttle) + enum { IDD = IDD_THROTTLE }; + int m_throttle; + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(Throttle) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + + // Implementation + protected: + + // Generated message map functions + //{{AFX_MSG(Throttle) + virtual BOOL OnInitDialog(); + afx_msg void OnCancel(); + afx_msg void OnOk(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + + //{{AFX_INSERT_LOCATION}} + // Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_THROTTLE_H__5F03B6E9_0C43_4933_A7BC_1618428C2B7F__INCLUDED_) diff --git a/src/win32/TileView.cpp b/src/win32/TileView.cpp new file mode 100644 index 0000000..7e3128d --- /dev/null +++ b/src/win32/TileView.cpp @@ -0,0 +1,569 @@ +#include "stdafx.h" +#include "vba.h" +#include "FileDlg.h" +#include "Reg.h" +#include "TileView.h" +#include "WinResUtil.h" + +#include "../System.h" +#include "../gba/GBA.h" +#include "../gba/Globals.h" +#include "../NLS.h" +#include "../Util.h" + +extern "C" { +#include +} + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// TileView dialog + + +TileView::TileView(CWnd* pParent /*=NULL*/) + : ResizeDlg(TileView::IDD, pParent) +{ + //{{AFX_DATA_INIT(TileView) + m_colors = -1; + m_charBase = -1; + m_stretch = FALSE; + //}}AFX_DATA_INIT + autoUpdate = false; + + memset(&bmpInfo, 0, sizeof(bmpInfo)); + + bmpInfo.bmiHeader.biSize = sizeof(bmpInfo.bmiHeader); + bmpInfo.bmiHeader.biWidth = 32*8; + bmpInfo.bmiHeader.biHeight = 32*8; + bmpInfo.bmiHeader.biPlanes = 1; + bmpInfo.bmiHeader.biBitCount = 24; + bmpInfo.bmiHeader.biCompression = BI_RGB; + data = (u8 *)calloc(1, 3 * 32*32 * 64); + + tileView.setData(data); + tileView.setBmpInfo(&bmpInfo); + + charBase = 0; + is256Colors = 0; + palette = 0; + w = h = 0; +} + +TileView::~TileView() +{ + free(data); + data = NULL; +} + +void TileView::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(TileView) + DDX_Control(pDX, IDC_PALETTE_SLIDER, m_slider); + DDX_Radio(pDX, IDC_16_COLORS, m_colors); + DDX_Radio(pDX, IDC_CHARBASE_0, m_charBase); + DDX_Check(pDX, IDC_STRETCH, m_stretch); + //}}AFX_DATA_MAP + DDX_Control(pDX, IDC_TILE_VIEW, tileView); + DDX_Control(pDX, IDC_MAP_VIEW_ZOOM, zoom); + DDX_Control(pDX, IDC_COLOR, color); +} + + +BEGIN_MESSAGE_MAP(TileView, CDialog) + //{{AFX_MSG_MAP(TileView) + ON_BN_CLICKED(IDC_SAVE, OnSave) + ON_BN_CLICKED(IDC_CLOSE, OnClose) + ON_BN_CLICKED(IDC_AUTO_UPDATE, OnAutoUpdate) + ON_BN_CLICKED(IDC_16_COLORS, On16Colors) + ON_BN_CLICKED(IDC_256_COLORS, On256Colors) + ON_BN_CLICKED(IDC_CHARBASE_0, OnCharbase0) + ON_BN_CLICKED(IDC_CHARBASE_1, OnCharbase1) + ON_BN_CLICKED(IDC_CHARBASE_2, OnCharbase2) + ON_BN_CLICKED(IDC_CHARBASE_3, OnCharbase3) + ON_BN_CLICKED(IDC_CHARBASE_4, OnCharbase4) + ON_BN_CLICKED(IDC_STRETCH, OnStretch) + ON_WM_HSCROLL() + //}}AFX_MSG_MAP + ON_MESSAGE(WM_MAPINFO, OnMapInfo) + ON_MESSAGE(WM_COLINFO, OnColInfo) + END_MESSAGE_MAP() + + ///////////////////////////////////////////////////////////////////////////// +// TileView message handlers + +void TileView::saveBMP(const char *name) +{ + u8 writeBuffer[1024 * 3]; + + FILE *fp = fopen(name,"wb"); + + if(!fp) { + systemMessage(MSG_ERROR_CREATING_FILE, "Error creating file %s", name); + return; + } + + struct { + u8 ident[2]; + u8 filesize[4]; + u8 reserved[4]; + u8 dataoffset[4]; + u8 headersize[4]; + u8 width[4]; + u8 height[4]; + u8 planes[2]; + u8 bitsperpixel[2]; + u8 compression[4]; + u8 datasize[4]; + u8 hres[4]; + u8 vres[4]; + u8 colors[4]; + u8 importantcolors[4]; + u8 pad[2]; + } bmpheader; + memset(&bmpheader, 0, sizeof(bmpheader)); + + bmpheader.ident[0] = 'B'; + bmpheader.ident[1] = 'M'; + + u32 fsz = sizeof(bmpheader) + w*h*3; + utilPutDword(bmpheader.filesize, fsz); + utilPutDword(bmpheader.dataoffset, 0x38); + utilPutDword(bmpheader.headersize, 0x28); + utilPutDword(bmpheader.width, w); + utilPutDword(bmpheader.height, h); + utilPutDword(bmpheader.planes, 1); + utilPutDword(bmpheader.bitsperpixel, 24); + utilPutDword(bmpheader.datasize, 3*w*h); + + fwrite(&bmpheader, 1, sizeof(bmpheader), fp); + + u8 *b = writeBuffer; + + int sizeX = w; + int sizeY = h; + + u8 *pixU8 = (u8 *)data+3*w*(h-1); + for(int y = 0; y < sizeY; y++) { + for(int x = 0; x < sizeX; x++) { + *b++ = *pixU8++; // B + *b++ = *pixU8++; // G + *b++ = *pixU8++; // R + } + pixU8 -= 2*3*w; + fwrite(writeBuffer, 1, 3*w, fp); + + b = writeBuffer; + } + + fclose(fp); +} + + +void TileView::savePNG(const char *name) +{ + u8 writeBuffer[1024 * 3]; + + FILE *fp = fopen(name,"wb"); + + if(!fp) { + systemMessage(MSG_ERROR_CREATING_FILE, "Error creating file %s", name); + return; + } + + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, + NULL, + NULL, + NULL); + if(!png_ptr) { + fclose(fp); + return; + } + + png_infop info_ptr = png_create_info_struct(png_ptr); + + if(!info_ptr) { + png_destroy_write_struct(&png_ptr,NULL); + fclose(fp); + return; + } + + if(setjmp(png_ptr->jmpbuf)) { + png_destroy_write_struct(&png_ptr,NULL); + fclose(fp); + return; + } + + png_init_io(png_ptr,fp); + + png_set_IHDR(png_ptr, + info_ptr, + w, + h, + 8, + PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + png_write_info(png_ptr,info_ptr); + + u8 *b = writeBuffer; + + int sizeX = w; + int sizeY = h; + + u8 *pixU8 = (u8 *)data; + for(int y = 0; y < sizeY; y++) { + for(int x = 0; x < sizeX; x++) { + int blue = *pixU8++; + int green = *pixU8++; + int red = *pixU8++; + + *b++ = red; + *b++ = green; + *b++ = blue; + } + png_write_row(png_ptr,writeBuffer); + + b = writeBuffer; + } + + png_write_end(png_ptr, info_ptr); + + png_destroy_write_struct(&png_ptr, &info_ptr); + + fclose(fp); +} + + +void TileView::OnSave() +{ + if(rom != NULL) + { + CString captureBuffer; + + if(theApp.captureFormat == 0) + captureBuffer = "tiles.png"; + else + captureBuffer = "tiles.bmp"; + + LPCTSTR exts[] = {".png", ".bmp" }; + + CString filter = theApp.winLoadFilter(IDS_FILTER_PNG); + CString title = winResLoadString(IDS_SELECT_CAPTURE_NAME); + + FileDlg dlg(this, + captureBuffer, + filter, + theApp.captureFormat ? 2 : 1, + theApp.captureFormat ? "BMP" : "PNG", + exts, + "", + title, + true); + + if(dlg.DoModal() == IDCANCEL) { + return; + } + + captureBuffer = dlg.GetPathName(); + + if(dlg.getFilterIndex() == 2) + saveBMP(captureBuffer); + else + savePNG(captureBuffer); + } +} + +void TileView::renderTile256(int tile, int x, int y, u8 *charBase, u16 *palette) +{ + u8 *bmp = &data[24*x + 8*32*24*y]; + + for(int j = 0; j < 8; j++) { + for(int i = 0; i < 8; i++) { + u8 c = charBase[tile*64 + j * 8 + i]; + + u16 color = palette[c]; + + *bmp++ = ((color >> 10) & 0x1f) << 3; + *bmp++ = ((color >> 5) & 0x1f) << 3; + *bmp++ = (color & 0x1f) << 3; + + } + bmp += 31*24; // advance line + } +} + +void TileView::renderTile16(int tile, int x, int y, u8 *charBase, u16 *palette) +{ + u8 *bmp = &data[24*x + 8*32*24*y]; + + int pal = this->palette; + + if(this->charBase == 4) + pal += 16; + + for(int j = 0; j < 8; j++) { + for(int i = 0; i < 8; i++) { + u8 c = charBase[tile*32 + j * 4 + (i>>1)]; + + if(i & 1) + c = c>>4; + else + c = c & 15; + + u16 color = palette[pal*16+c]; + + *bmp++ = ((color >> 10) & 0x1f) << 3; + *bmp++ = ((color >> 5) & 0x1f) << 3; + *bmp++ = (color & 0x1f) << 3; + } + bmp += 31*24; // advance line + } +} + + +void TileView::render() +{ + u16 *palette = (u16 *)paletteRAM; + u8 *charBase = &vram[this->charBase * 0x4000]; + + int maxY; + + if(is256Colors) { + int tile = 0; + maxY = 16; + for(int y = 0; y < maxY; y++) { + for(int x = 0; x < 32; x++) { + if(this->charBase == 4) + renderTile256(tile, x, y, charBase, &palette[256]); + else + renderTile256(tile, x, y, charBase, palette); + tile++; + } + } + tileView.setSize(32*8, maxY*8); + w = 32*8; + h = maxY*8; + SIZE s; + s.cx = 32*8; + s.cy = maxY*8; + if(tileView.getStretch()) { + s.cx = s.cy = 1; + } + tileView.SetScrollSizes(MM_TEXT,s); + } else { + int tile = 0; + maxY = 32; + if(this->charBase == 3) + maxY = 16; + for(int y = 0; y < maxY; y++) { + for(int x = 0; x < 32; x++) { + renderTile16(tile, x, y, charBase, palette); + tile++; + } + } + tileView.setSize(32*8, maxY*8); + w = 32*8; + h = maxY*8; + SIZE s; + s.cx = 32*8; + s.cy = maxY*8; + if(tileView.getStretch()) { + s.cx = s.cy = 1; + } + tileView.SetScrollSizes(MM_TEXT, s); + } +} + +void TileView::update() +{ + paint(); +} + + +BOOL TileView::OnInitDialog() +{ + CDialog::OnInitDialog(); + + DIALOG_SIZER_START( sz ) + DIALOG_SIZER_ENTRY( IDC_TILE_VIEW, DS_SizeX | DS_SizeY ) + DIALOG_SIZER_ENTRY( IDC_COLOR, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_R, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_G, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_B, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_REFRESH, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_CLOSE, DS_MoveY) + DIALOG_SIZER_ENTRY( IDC_SAVE, DS_MoveY) + DIALOG_SIZER_END() + SetData(sz, + TRUE, + HKEY_CURRENT_USER, + "Software\\Emulators\\VisualBoyAdvance\\Viewer\\TileView", + NULL); + + m_colors = is256Colors; + m_charBase = charBase; + + m_slider.SetRange(0, 15); + m_slider.SetPageSize(4); + m_slider.SetTicFreq(1); + + paint(); + + m_stretch = regQueryDwordValue("tileViewStretch", 0); + if(m_stretch) + tileView.setStretch(true); + UpdateData(FALSE); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void TileView::OnClose() +{ + theApp.winRemoveUpdateListener(this); + + DestroyWindow(); +} + + +void TileView::OnAutoUpdate() +{ + autoUpdate = !autoUpdate; + if(autoUpdate) { + theApp.winAddUpdateListener(this); + } else { + theApp.winRemoveUpdateListener(this); + } +} + + +void TileView::paint() +{ + if(vram != NULL && paletteRAM != NULL) { + render(); + tileView.refresh(); + } +} + + +void TileView::On16Colors() +{ + is256Colors = 0; + paint(); +} + +void TileView::On256Colors() +{ + is256Colors = 1; + paint(); +} + +void TileView::OnCharbase0() +{ + charBase = 0; + paint(); +} + +void TileView::OnCharbase1() +{ + charBase = 1; + paint(); +} + +void TileView::OnCharbase2() +{ + charBase = 2; + paint(); +} + +void TileView::OnCharbase3() +{ + charBase = 3; + paint(); +} + +void TileView::OnCharbase4() +{ + charBase = 4; + paint(); +} + +void TileView::OnStretch() +{ + tileView.setStretch(!tileView.getStretch()); + paint(); + regSetDwordValue("tileViewStretch", tileView.getStretch()); +} + +LRESULT TileView::OnMapInfo(WPARAM wParam, LPARAM lParam) +{ + u8 *colors = (u8 *)lParam; + zoom.setColors(colors); + + int x = (int)((wParam & 0xFFFF) / 8); + int y = (int)(((wParam >> 16) & 0xFFFF) / 8); + + u32 address = 0x6000000 + 0x4000 * charBase; + int tile = 32 * y + x; + if(is256Colors) + tile *= 2; + address += 32 * tile; + + CString buffer; + buffer.Format("%d", tile); + GetDlgItem(IDC_TILE_NUMBER)->SetWindowText(buffer); + + buffer.Format("%08x", address); + GetDlgItem(IDC_ADDRESS)->SetWindowText(buffer); + + return TRUE; +} + +LRESULT TileView::OnColInfo(WPARAM wParam, LPARAM) +{ + u16 c = (u16)wParam; + + color.setColor(c); + + int r = (c & 0x1f); + int g = (c & 0x3e0) >> 5; + int b = (c & 0x7c00) >> 10; + + CString buffer; + buffer.Format("R: %d", r); + GetDlgItem(IDC_R)->SetWindowText(buffer); + + buffer.Format("G: %d", g); + GetDlgItem(IDC_G)->SetWindowText(buffer); + + buffer.Format("B: %d", b); + GetDlgItem(IDC_B)->SetWindowText(buffer); + + return TRUE; +} + +void TileView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) +{ + switch(nSBCode) { + case TB_THUMBPOSITION: + palette = nPos; + break; + default: + palette = m_slider.GetPos(); + break; + } + paint(); +} + +void TileView::PostNcDestroy() +{ + delete this; +} diff --git a/src/win32/TileView.h b/src/win32/TileView.h new file mode 100644 index 0000000..0c082ef --- /dev/null +++ b/src/win32/TileView.h @@ -0,0 +1,90 @@ +#if !defined(AFX_TILEVIEW_H__055751EC_2DF3_495B_B643_29025465CD2E__INCLUDED_) +#define AFX_TILEVIEW_H__055751EC_2DF3_495B_B643_29025465CD2E__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// TileView.h : header file +// + +#include "BitmapControl.h" +#include "ColorControl.h" +#include "IUpdate.h" +#include "ResizeDlg.h" +#include "ZoomControl.h" + +///////////////////////////////////////////////////////////////////////////// +// TileView dialog + +class TileView : public ResizeDlg, IUpdateListener +{ + int charBase; + int is256Colors; + int palette; + BitmapControl tileView; + BITMAPINFO bmpInfo; + u8 *data; + ZoomControl zoom; + ColorControl color; + int w; + int h; + bool autoUpdate; + // Construction + public: + void paint(); + void render(); + void renderTile16(int tile, int x, int y, u8 *charBase, u16 *palette); + void renderTile256(int tile, int x, int y, u8 *charBase, u16 *palette); + void savePNG(const char *name); + void saveBMP(const char *name); + TileView(CWnd* pParent = NULL); // standard constructor + virtual ~TileView(); + + virtual void update(); + + // Dialog Data + //{{AFX_DATA(TileView) + enum { IDD = IDD_TILE_VIEWER }; + CSliderCtrl m_slider; + int m_colors; + int m_charBase; + BOOL m_stretch; + //}}AFX_DATA + + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(TileView) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual void PostNcDestroy(); + //}}AFX_VIRTUAL + + // Implementation + protected: + virtual afx_msg LRESULT OnMapInfo(WPARAM wParam, LPARAM lParam); + virtual afx_msg LRESULT OnColInfo(WPARAM wParam, LPARAM lParam); + + // Generated message map functions + //{{AFX_MSG(TileView) + afx_msg void OnSave(); + virtual BOOL OnInitDialog(); + afx_msg void OnClose(); + afx_msg void OnAutoUpdate(); + afx_msg void On16Colors(); + afx_msg void On256Colors(); + afx_msg void OnCharbase0(); + afx_msg void OnCharbase1(); + afx_msg void OnCharbase2(); + afx_msg void OnCharbase3(); + afx_msg void OnCharbase4(); + afx_msg void OnStretch(); + afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + }; + + //{{AFX_INSERT_LOCATION}} + // Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_TILEVIEW_H__055751EC_2DF3_495B_B643_29025465CD2E__INCLUDED_) diff --git a/src/win32/VBA.cpp b/src/win32/VBA.cpp new file mode 100644 index 0000000..0977987 --- /dev/null +++ b/src/win32/VBA.cpp @@ -0,0 +1,2693 @@ +#if (_MSC_VER == 1600 && _M_IX86_FP >= 0 && (_MSC_FULL_VER < 160040219)) +#error The version of Visual Studio 2010 you are using generates SSE2 instructions when /arch:SSE is specified. Please update to Service Pack 1. +#endif + +#ifdef NO_D3D +#ifdef NO_OGL +#error NO_D3D and NO_OGL must not be defined at the same time. +#endif +#endif + +#include "stdafx.h" +#include "VBA.h" + +#include + +#include "AVIWrite.h" +#include "LangSelect.h" +#include "MainWnd.h" +#include "Reg.h" +#include "resource.h" +#include "WavWriter.h" +#include "WinResUtil.h" +#include "Logging.h" +#include "rpi.h" + +#include "../System.h" +#include "../gba/agbprint.h" +#include "../gba/cheatSearch.h" +#include "../gba/GBA.h" +#include "../gba/Globals.h" +#include "../gba/RTC.h" +#include "../gba/Sound.h" +#include "../Util.h" +#include "../gb/gbGlobals.h" +#include "../gb/gbPrinter.h" +#include "../gb/gbSound.h" +#include "../common/SoundDriver.h" + +#include "../version.h" + +/* Link +---------------------*/ +#include "../gba/GBALink.h" +/* ---------------- */ + +extern void Pixelate(u8*,u32,u8*,u8*,u32,int,int); +extern void Pixelate32(u8*,u32,u8*,u8*,u32,int,int); +extern void _2xSaI(u8*,u32,u8*,u8*,u32,int,int); +extern void _2xSaI32(u8*,u32,u8*,u8*,u32,int,int); +extern void Super2xSaI(u8*,u32,u8*,u8*,u32,int,int); +extern void Super2xSaI32(u8*,u32,u8*,u8*,u32,int,int); +extern void SuperEagle(u8*,u32,u8*,u8*,u32,int,int); +extern void SuperEagle32(u8*,u32,u8*,u8*,u32,int,int); +extern void AdMame2x(u8*,u32,u8*,u8*,u32,int,int); +extern void AdMame2x32(u8*,u32,u8*,u8*,u32,int,int); +extern void Bilinear(u8*,u32,u8*,u8*,u32,int,int); +extern void Bilinear32(u8*,u32,u8*,u8*,u32,int,int); +extern void BilinearPlus(u8*,u32,u8*,u8*,u32,int,int); +extern void BilinearPlus32(u8*,u32,u8*,u8*,u32,int,int); +extern void Scanlines(u8*,u32,u8*,u8*,u32,int,int); +extern void Scanlines32(u8*,u32,u8*,u8*,u32,int,int); +extern void ScanlinesTV(u8*,u32,u8*,u8*,u32,int,int); +extern void ScanlinesTV32(u8*,u32,u8*,u8*,u32,int,int); +extern void hq2x(u8*,u32,u8*,u8*,u32,int,int); +extern void hq2x32(u8*,u32,u8*,u8*,u32,int,int); +extern void lq2x(u8*,u32,u8*,u8*,u32,int,int); +extern void lq2x32(u8*,u32,u8*,u8*,u32,int,int); +extern void Simple2x16(u8*,u32,u8*,u8*,u32,int,int); +extern void Simple2x32(u8*,u32,u8*,u8*,u32,int,int); +extern void Simple3x16(u8*,u32,u8*,u8*,u32,int,int); +extern void Simple3x32(u8*,u32,u8*,u8*,u32,int,int); +extern void Simple4x16(u8*,u32,u8*,u8*,u32,int,int); +extern void Simple4x32(u8*,u32,u8*,u8*,u32,int,int); +#ifndef WIN64 +extern void hq3x16(u8*,u32,u8*,u8*,u32,int,int); +extern void hq4x16(u8*,u32,u8*,u8*,u32,int,int); +extern void hq3x32(u8*,u32,u8*,u8*,u32,int,int); +extern void hq4x32(u8*,u32,u8*,u8*,u32,int,int); +#endif + +extern void SmartIB(u8*,u32,int,int); +extern void SmartIB32(u8*,u32,int,int); +extern void MotionBlurIB(u8*,u32,int,int); +extern void MotionBlurIB32(u8*,u32,int,int); + +extern IDisplay *newGDIDisplay(); +extern IDisplay *newDirectDrawDisplay(); +#ifndef NO_OGL +extern IDisplay *newOpenGLDisplay(); +#endif +#ifndef NO_D3D +extern IDisplay *newDirect3DDisplay(); +#endif + +extern Input *newDirectInput(); + +extern SoundDriver *newDirectSound(); +#ifndef NO_OAL +extern SoundDriver *newOpenAL(); +#endif +#ifndef NO_XAUDIO2 +extern SoundDriver *newXAudio2_Output(); +#endif + +extern void remoteStubSignal(int, int); +extern void remoteOutput(char *, u32); +extern void remoteStubMain(); +extern void remoteSetProtocol(int); +extern void remoteCleanUp(); +extern int remoteSocket; + +extern void InterframeCleanup(); + +void winlog(const char *msg, ...); + +/* Link +---------------------*/ +extern bool InitLink(void); +extern void CloseLink(void); +//extern int linkid; +extern char inifile[]; +/* ------------------- */ +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +int emulating = 0; +bool debugger = false; +int RGB_LOW_BITS_MASK = 0; +bool b16to32Video = false; +int systemFrameSkip = 0; +int systemSpeed = 0; +u32 systemColorMap32[0x10000]; +u16 systemColorMap16[0x10000]; +u16 systemGbPalette[24]; +int systemRedShift = 0; +int systemBlueShift = 0; +int systemGreenShift = 0; +int systemColorDepth = 16; +int realsystemRedShift = 0; +int realsystemBlueShift = 0; +int realsystemGreenShift = 0; +int realsystemColorDepth = 16; +int systemVerbose = 0; +int systemDebug = 0; +int systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; +bool soundBufferLow = 0; +void winSignal(int,int); +void winOutput(const char *, u32); + +void (*dbgSignal)(int,int) = winSignal; +void (*dbgOutput)(const char *, u32) = winOutput; + +#ifdef MMX +extern "C" bool cpu_mmx; +#endif + +namespace Sm60FPS +{ + float K_fCpuSpeed = 100.0f; // was 98.0f before, but why? + float K_fTargetFps = 60.0f * K_fCpuSpeed / 100; + float K_fDT = 1000.0f / K_fTargetFps; + + u32 dwTimeElapse; + u32 dwTime0; + u32 dwTime1; + u32 nFrameCnt; + float fWantFPS; + float fCurFPS; + bool bLastSkip; + int nCurSpeed; + int bSaveMoreCPU; +}; + +#ifdef LOG_PERFORMANCE +#ifndef PERFORMANCE_INTERVAL +#define PERFORMANCE_INTERVAL 3600 +#endif +int systemSpeedTable[PERFORMANCE_INTERVAL]; +unsigned int systemSpeedCounter; +#endif + +void directXMessage(const char *msg) +{ + systemMessage(IDS_DIRECTX_7_REQUIRED, + "DirectX 7.0 or greater is required to run.\nDownload at http://www.microsoft.com/directx.\n\nError found at: %s", + msg); +} + +///////////////////////////////////////////////////////////////////////////// +// VBA + +VBA::VBA() +{ + // COINIT_MULTITHREADED is not supported by SHBrowseForFolder with BIF_USENEWUI + // OpenAL also causes trouble when COINIT_MULTITHREADED is used + if( S_OK != CoInitializeEx( NULL, COINIT_APARTMENTTHREADED ) ) { + systemMessage( IDS_COM_FAILURE, NULL ); + } + + // ! keep in mind that many of the following values will be really initialized in loadSettings() + maxCpuCores = 1; + windowPositionX = 0; + windowPositionY = 0; + filterFunction = NULL; + ifbFunction = NULL; + ifbType = 0; + filterType = FILTER_NONE; + filterWidth = 0; + filterHeight = 0; + filterMT = false; + fsAdapter = 0; + fsWidth = 0; + fsHeight = 0; + fsColorDepth = 0; + fsFrequency = 0; + fsForceChange = false; + surfaceSizeX = 0; + surfaceSizeY = 0; + sizeX = 0; + sizeY = 0; + videoOption = 0; + fullScreenStretch = false; + disableStatusMessage = false; + showSpeed = 0; + showSpeedTransparent = true; + showRenderedFrames = 0; + screenMessage = false; + screenMessageTime = 0; + display = NULL; + menu = NULL; + popup = NULL; + cartridgeType = IMAGE_GBA; + soundInitialized = false; + useBiosFileGBA = false; + useBiosFileGBC = false; + useBiosFileGB = false; + skipBiosFile = false; + biosFileNameGBA = _T(""); + biosFileNameGBC = _T(""); + biosFileNameGB = _T(""); + active = true; + paused = false; + recentFreeze = false; + autoSaveLoadCheatList = true; + winout = NULL; + removeIntros = false; + autoPatch = true; + winGbBorderOn = 0; + winFlashSize = 0x20000; + winRtcEnable = false; + winSaveType = 0; + rewindMemory = NULL; + rewindPos = 0; + rewindTopPos = 0; + rewindCounter = 0; + rewindCount = 0; + rewindSaveNeeded = false; + rewindTimer = 0; + captureFormat = 0; + tripleBuffering = true; + throttle = 0; + autoFrameSkipLastTime = 0; + autoFrameSkip = false; + vsync = false; + changingVideoSize = false; + renderMethod = DIRECT_3D; + audioAPI = DIRECTSOUND; +#ifndef NO_OAL + oalDevice = NULL; + oalBufferCount = 5; +#endif +#ifndef NO_XAUDIO2 + audioAPI = XAUDIO2; + xa2Device = 0; + xa2BufferCount = 4; + xa2Upmixing = false; +#endif +#ifndef NO_D3D + d3dFilter = 0; + d3dMotionBlur = false; +#endif + iconic = false; + glFilter = 0; + regEnabled = false; + pauseWhenInactive = true; + speedupToggle = false; + winGbPrinterEnabled = false; + threadPriority = 2; + disableMMX = false; + languageOption = 0; + languageModule = NULL; + languageName = ""; + renderedFrames = 0; + input = NULL; + joypadDefault = 0; + autoFire = 0; + autoFireToggle = false; + winPauseNextFrame = false; + soundRecording = false; + soundRecorder = NULL; + dsoundDisableHardwareAcceleration = true; + aviRecording = false; + aviRecorder = NULL; + painting = false; + skipAudioFrames = 0; + movieRecording = false; + moviePlaying = false; + movieFrame = 0; + moviePlayFrame = 0; + movieFile = NULL; + movieLastJoypad = 0; + movieNextJoypad = 0; + sensorX = 2047; + sensorY = 2047; + mouseCounter = 0; + wasPaused = false; + frameskipadjust = 0; + autoLoadMostRecent = false; + maxScale = 0; + romSize = 0; + lastWindowed = VIDEO_3X; + lastFullscreen = VIDEO_1024x768; + + updateCount = 0; + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + ZeroMemory(&emulator, sizeof(emulator)); + + hAccel = NULL; + + for(int i = 0; i < 24;) { + systemGbPalette[i++] = (0x1f) | (0x1f << 5) | (0x1f << 10); + systemGbPalette[i++] = (0x15) | (0x15 << 5) | (0x15 << 10); + systemGbPalette[i++] = (0x0c) | (0x0c << 5) | (0x0c << 10); + systemGbPalette[i++] = 0; + } +} + +VBA::~VBA() +{ + rpiCleanup(); + InterframeCleanup(); + + char winBuffer[2048]; + + GetModuleFileName(NULL, winBuffer, 2048); + char *p = strrchr(winBuffer, '\\'); + if(p) + *p = 0; + + regInit(winBuffer); + + JoyBusShutdown(); + + saveSettings(); + + if(moviePlaying) { + if(movieFile != NULL) { + fclose(movieFile); + movieFile = NULL; + } + moviePlaying = false; + movieLastJoypad = 0; + } + + if(movieRecording) { + if(movieFile != NULL) { + // record the last joypad change so that the correct time can be + // recorded + fwrite(&movieFrame, 1, sizeof(int), movieFile); + fwrite(&movieLastJoypad, 1, sizeof(u32), movieFile); + fclose(movieFile); + movieFile = NULL; + } + movieRecording = false; + moviePlaying = false; + movieLastJoypad = 0; + } + + soundPause(); + soundShutdown(); + + if(gbRom != NULL || rom != NULL) { + if(autoSaveLoadCheatList) + ((MainWnd *)m_pMainWnd)->winSaveCheatListDefault(); + ((MainWnd *)m_pMainWnd)->writeBatteryFile(); + cheatSearchCleanup(&cheatSearchData); + emulator.emuCleanUp(); + } + + if(input) + delete input; + + shutdownDisplay(); + + if(rewindMemory) + free(rewindMemory); + +#ifndef NO_OAL + if( oalDevice ) { + free( oalDevice ); + } +#endif + + CoUninitialize(); +} + +///////////////////////////////////////////////////////////////////////////// +// The one and only VBA object + +VBA theApp; +///////////////////////////////////////////////////////////////////////////// +// VBA initialization + +BOOL VBA::InitInstance() +{ +#if _MSC_VER < 1400 +#ifdef _AFXDLL + Enable3dControls(); // Call this when using MFC in a shared DLL +#else + Enable3dControlsStatic(); // Call this when linking to MFC statically +#endif +#endif + + SetRegistryKey(_T("VBA")); + + remoteSetProtocol(0); + + systemVerbose = GetPrivateProfileInt("config", + "verbose", + 0, + MakeInstanceFilename("vbam.ini")); + + systemDebug = GetPrivateProfileInt("config", + "debug", + 0, + MakeInstanceFilename("vbam.ini")); + + wndClass = AfxRegisterWndClass(0, LoadCursor(IDC_ARROW), (HBRUSH)GetStockObject(BLACK_BRUSH), LoadIcon(IDI_MAINICON)); + + char winBuffer[2048]; + + GetModuleFileName(NULL, winBuffer, 2048); + char *p = strrchr(winBuffer, '\\'); + if(p) + *p = 0; + + if(!InitLink()) + return FALSE; + + bool force = false; + + if (m_lpCmdLine[0]) + { + if(__argc > 0) { + if( 0 == strcmp( __argv[1], "--configpath" ) ) { + if( __argc > 2 ) { + strcpy( winBuffer, __argv[2] ); + force = true; + if( __argc > 3 ) { + szFile = __argv[3]; filename = szFile; + int index = filename.ReverseFind('.'); + if(index != -1) + filename = filename.Left(index); + } + } + } else { + szFile = __argv[1]; filename = szFile; + int index = filename.ReverseFind('.'); + if(index != -1) + filename = filename.Left(index); + } + } + } + + regInit(winBuffer, force); + + loadSettings(); + + if(!initDisplay()) { + if(videoOption >= VIDEO_320x240) { + regSetDwordValue("video", VIDEO_2X); + } + return FALSE; + } + + if(!initInput()) + return FALSE; + + hAccel = LoadAccelerators(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_ACCELERATOR)); + + winAccelMgr.Connect((MainWnd *)m_pMainWnd); + + winAccelMgr.SetRegKey(HKEY_CURRENT_USER, "Software\\Emulators\\VisualBoyAdvance"); + + extern void winAccelAddCommands(CAcceleratorManager&); + + winAccelAddCommands(winAccelMgr); + + winAccelMgr.CreateDefaultTable(); + + winAccelMgr.Load(); + + winAccelMgr.UpdateWndTable(); + + winAccelMgr.UpdateMenu(menu); + + if( !filename.IsEmpty() ) { + if(((MainWnd*)m_pMainWnd)->FileRun()) + emulating = true; + else + emulating = false; + } + + return TRUE; +} + +void VBA::adjustDestRect() +{ + POINT point; + + point.x = 0; + point.y = 0; + + m_pMainWnd->ClientToScreen(&point); + dest.top = point.y; + dest.left = point.x; + + point.x = surfaceSizeX; + point.y = surfaceSizeY; + + m_pMainWnd->ClientToScreen(&point); + dest.bottom = point.y; + dest.right = point.x; + + // make sure that dest rect lies in the monitor + if(videoOption >= VIDEO_320x240) { + dest.top -= windowPositionY; + dest.left -= windowPositionX; + dest.bottom-= windowPositionY; + dest.right -= windowPositionX; + } + + if(videoOption > VIDEO_6X) { + if(fullScreenStretch) { + dest.top = 0; + dest.left = 0; + dest.right = fsWidth; + dest.bottom = fsHeight; + } else { + int top = (fsHeight - surfaceSizeY) / 2; + int left = (fsWidth - surfaceSizeX) / 2; + dest.top += top; + dest.bottom += top; + dest.left += left; + dest.right += left; + } + } +} + + +void VBA::updateIFB() +{ + if(systemColorDepth == 16) { + switch(ifbType) { + case 0: + default: + ifbFunction = NULL; + break; + case 1: + ifbFunction = MotionBlurIB; + break; + case 2: + ifbFunction = SmartIB; + break; + } + } else if(systemColorDepth == 32) { + switch(ifbType) { + case 0: + default: + ifbFunction = NULL; + break; + case 1: + ifbFunction = MotionBlurIB32; + break; + case 2: + ifbFunction = SmartIB32; + break; + } + } else + ifbFunction = NULL; +} + +void VBA::updateFilter() +{ + // BEGIN hacky ugly code + + // HQ3X asm wants 16 bit input. When we switch + // away from 16 bits we need to restore the driver values + + // This hack is also necessary for Kega Fusion filter plugins + + if ( b16to32Video ) + { + b16to32Video = false; + systemColorDepth = realsystemColorDepth; + systemRedShift = realsystemRedShift; + systemGreenShift = realsystemGreenShift; + systemBlueShift = realsystemBlueShift; + utilUpdateSystemColorMaps(theApp.cartridgeType == IMAGE_GBA && gbColorOption == 1); + } + // END hacky ugly code + + filterWidth = sizeX; + filterHeight = sizeY; + filterMagnification = 1; + + + if ( videoOption == VIDEO_1X || videoOption == VIDEO_320x240 ) + { + filterFunction = NULL; + filterMagnification = 1; + } + else + { + if ( systemColorDepth == 16 ) + { + switch(filterType) + { + default: + case FILTER_NONE: + filterFunction = NULL; + filterMagnification = 1; + break; + case FILTER_PLUGIN: + if( rpiInit( pluginName ) ) { + filterFunction = rpiFilter; + filterMagnification = rpiScaleFactor(); + } else { + filterType = FILTER_NONE; + updateFilter(); + return; + } + break; + case FILTER_TVMODE: + filterFunction = ScanlinesTV; + filterMagnification = 2; + break; + case FILTER_2XSAI: + filterFunction = _2xSaI; + filterMagnification = 2; + break; + case FILTER_SUPER2XSAI: + filterFunction = Super2xSaI; + filterMagnification = 2; + break; + case FILTER_SUPEREAGLE: + filterFunction = SuperEagle; + filterMagnification = 2; + break; + case FILTER_PIXELATE: + filterFunction = Pixelate; + filterMagnification = 2; + break; + case FILTER_MAMESCALE2X: + filterFunction = AdMame2x; + filterMagnification = 2; + break; + case FILTER_SIMPLE2X: + filterFunction = Simple2x16; + filterMagnification = 2; + break; + case FILTER_BILINEAR: + filterFunction = Bilinear; + filterMagnification = 2; + break; + case FILTER_BILINEARPLUS: + filterFunction = BilinearPlus; + filterMagnification = 2; + break; + case FILTER_SCANLINES: + filterFunction = Scanlines; + filterMagnification = 2; + break; + case FILTER_HQ2X: + filterFunction = hq2x; + filterMagnification = 2; + break; + case FILTER_LQ2X: + filterFunction = lq2x; + filterMagnification = 2; + break; + case FILTER_SIMPLE3X: + filterFunction = Simple3x16; + filterMagnification = 3; + break; + case FILTER_SIMPLE4X: + filterFunction = Simple4x16; + filterMagnification = 4; + break; +#ifndef WIN64 + case FILTER_HQ3X: + filterFunction = hq3x16; + filterMagnification = 3; + break; + case FILTER_HQ4X: + filterFunction = hq4x16; + filterMagnification = 4; + break; +#endif + } + } + + if ( systemColorDepth == 32 ) + { + switch(filterType) + { + default: + case FILTER_NONE: + filterFunction = NULL; + filterMagnification = 1; + break; + case FILTER_PLUGIN: + if( rpiInit( pluginName ) ) { + filterFunction = rpiFilter; + filterMagnification = rpiScaleFactor(); + b16to32Video=true; + } else { + filterType = FILTER_NONE; + updateFilter(); + return; + } + break; + case FILTER_TVMODE: + filterFunction = ScanlinesTV32; + filterMagnification = 2; + break; + case FILTER_2XSAI: + filterFunction = _2xSaI32; + filterMagnification = 2; + break; + case FILTER_SUPER2XSAI: + filterFunction = Super2xSaI32; + filterMagnification = 2; + break; + case FILTER_SUPEREAGLE: + filterFunction = SuperEagle32; + filterMagnification = 2; + break; + case FILTER_PIXELATE: + filterFunction = Pixelate32; + filterMagnification = 2; + break; + case FILTER_MAMESCALE2X: + filterFunction = AdMame2x32; + filterMagnification = 2; + break; + case FILTER_SIMPLE2X: + filterFunction = Simple2x32; + filterMagnification = 2; + break; + case FILTER_BILINEAR: + filterFunction = Bilinear32; + filterMagnification = 2; + break; + case FILTER_BILINEARPLUS: + filterFunction = BilinearPlus32; + filterMagnification = 2; + break; + case FILTER_SCANLINES: + filterFunction = Scanlines32; + filterMagnification = 2; + break; + case FILTER_HQ2X: + filterFunction = hq2x32; + filterMagnification = 2; + break; + case FILTER_LQ2X: + filterFunction = lq2x32; + filterMagnification = 2; + break; + case FILTER_SIMPLE3X: + filterFunction = Simple3x32; + filterMagnification = 3; + break; + case FILTER_SIMPLE4X: + filterFunction = Simple4x32; + filterMagnification = 4; + break; +#ifndef WIN64 + case FILTER_HQ3X: + filterFunction = hq3x32; + filterMagnification = 3; +#ifndef NO_ASM + b16to32Video=true; +#endif + break; + case FILTER_HQ4X: + filterFunction = hq4x32; + filterMagnification = 4; +#ifndef NO_ASM + b16to32Video=true; +#endif + break; +#endif + } + } + } + + rect.right = sizeX * filterMagnification; + rect.bottom = sizeY * filterMagnification; + + + if( filterType != FILTER_NONE ) + memset(delta, 0xFF, sizeof(delta)); + + if( display ) + display->changeRenderSize(rect.right, rect.bottom); + + if (b16to32Video && systemColorDepth!=16) + { + realsystemColorDepth = systemColorDepth; + systemColorDepth = 16; + realsystemRedShift = systemRedShift; + systemRedShift = 11; + realsystemGreenShift = systemGreenShift; + systemGreenShift = 6; + realsystemBlueShift = systemBlueShift; + systemBlueShift = 0; + utilUpdateSystemColorMaps(theApp.cartridgeType == IMAGE_GBA && gbColorOption == 1); + } + +#ifdef LOG_PERFORMANCE + memset( systemSpeedTable, 0x00, sizeof(systemSpeedTable) ); + systemSpeedCounter = 0; +#endif +} + + +void VBA::updateThrottle( unsigned short throttle ) +{ + this->throttle = throttle; + + if( throttle ) { + Sm60FPS::K_fCpuSpeed = (float)throttle; + Sm60FPS::K_fTargetFps = 60.0f * Sm60FPS::K_fCpuSpeed / 100; + Sm60FPS::K_fDT = 1000.0f / Sm60FPS::K_fTargetFps; + autoFrameSkip = false; + frameSkip = 0; + systemFrameSkip = 0; + } + + soundSetThrottle(throttle); +} + + +void VBA::updateMenuBar() +{ + if(menu != NULL) { + if(m_pMainWnd) + m_pMainWnd->SetMenu(NULL); + m_menu.Detach(); + DestroyMenu(menu); + } + + if(popup != NULL) { + // force popup recreation if language changed + DestroyMenu(popup); + popup = NULL; + } + + if( ( videoOption >= VIDEO_320x240 ) ) { + return; + } + + m_menu.Attach(winResLoadMenu(MAKEINTRESOURCE(IDR_MENU))); + menu = (HMENU)m_menu; + + if(m_pMainWnd) + m_pMainWnd->SetMenu(&m_menu); +} + +void winlog(const char *msg, ...) +{ + CString buffer; + va_list valist; + + va_start(valist, msg); + buffer.FormatV(msg, valist); + + if(theApp.winout == NULL) { + theApp.winout = fopen("vba-trace.log","w"); + } + + fputs(buffer, theApp.winout); + + va_end(valist); +} + +void log(const char *msg, ...) +{ + CString buffer; + va_list valist; + + va_start(valist, msg); + buffer.FormatV(msg, valist); + + toolsLog(buffer); + + va_end(valist); +} + +bool systemReadJoypads() +{ + if(theApp.input) + return theApp.input->readDevices(); + return false; +} + +u32 systemReadJoypad(int which) +{ + if(theApp.input) + return theApp.input->readDevice(which); + return 0; +} + +// TODO: implement +void systemCartridgeRumble(bool) { } +void systemPossibleCartridgeRumble(bool) { } +void updateRumbleFrame() { } +int systemGetSensorZ() { return 0; } +u8 systemGetSensorDarkness() { return 0; } + +void systemDrawScreen() +{ + if(theApp.display == NULL) + return; + + theApp.renderedFrames++; + + if(theApp.updateCount) { + POSITION pos = theApp.updateList.GetHeadPosition(); + while(pos) { + IUpdateListener *up = theApp.updateList.GetNext(pos); + up->update(); + } + } + + if (Sm60FPS_CanSkipFrame()) + return; + + if( theApp.aviRecording ) { + if( theApp.painting ) { + theApp.skipAudioFrames++; + } else { + unsigned char *bmp; + unsigned short srcPitch = theApp.sizeX * ( systemColorDepth >> 3 ) + 4; + switch( systemColorDepth ) + { + case 16: + bmp = new unsigned char[ theApp.sizeX * theApp.sizeY * 2 ]; + cpyImg16bmp( bmp, pix + srcPitch, srcPitch, theApp.sizeX, theApp.sizeY ); + break; + case 32: + // use 24 bit colors to reduce video size + bmp = new unsigned char[ theApp.sizeX * theApp.sizeY * 3 ]; + cpyImg32bmp( bmp, pix + srcPitch, srcPitch, theApp.sizeX, theApp.sizeY ); + break; + } + if( false == theApp.aviRecorder->AddVideoFrame( bmp ) ) { + systemMessage( IDS_AVI_CANNOT_WRITE_VIDEO, "Cannot write video frame to AVI file." ); + delete theApp.aviRecorder; + theApp.aviRecorder = NULL; + theApp.aviRecording = false; + } + delete [] bmp; + } + } + + if( theApp.ifbFunction ) { + theApp.ifbFunction( pix + (theApp.filterWidth * (systemColorDepth>>3)) + 4, + (theApp.filterWidth * (systemColorDepth>>3)) + 4, + theApp.filterWidth, theApp.filterHeight ); + } + + if(!soundBufferLow) + { + theApp.display->render(); + Sm60FPS_Sleep(); + } + else + soundBufferLow = false; + +} + +void systemScreenCapture(int captureNumber) +{ + if(theApp.m_pMainWnd) + ((MainWnd *)theApp.m_pMainWnd)->screenCapture(captureNumber); +} + +u32 systemGetClock() +{ + return GetTickCount(); +} + +void systemMessage(int number, const char *defaultMsg, ...) +{ + CString buffer; + va_list valist; + CString msg = defaultMsg; + if(number) + msg = winResLoadString(number); + + va_start(valist, defaultMsg); + buffer.FormatV(msg, valist); + + CWnd *win = AfxGetApp()->GetMainWnd(); + if (win) + win = win->GetLastActivePopup(); // possible modal dialog + if (!win) + win = AfxGetApp()->m_pMainWnd; + win->MessageBox(buffer, winResLoadString(IDS_ERROR), MB_OK|MB_ICONERROR); + + va_end(valist); +} + +void systemSetTitle(const char *title) +{ + if(theApp.m_pMainWnd != NULL) { + AfxGetApp()->m_pMainWnd->SetWindowText(title); + } +} + +void systemShowSpeed(int speed) +{ + systemSpeed = speed; + theApp.showRenderedFrames = theApp.renderedFrames; + theApp.renderedFrames = 0; + if(theApp.videoOption <= VIDEO_6X && theApp.showSpeed) { + CString buffer; + if(theApp.showSpeed == 1) + buffer.Format(VBA_NAME_AND_SUBVERSION "-%3d%%", systemSpeed); + else + buffer.Format(VBA_NAME_AND_SUBVERSION "-%3d%%(%d, %d fps)", systemSpeed, + systemFrameSkip, + theApp.showRenderedFrames); + + systemSetTitle(buffer); + } +} + + +void systemFrame() +{ + if( theApp.movieRecording || theApp.moviePlaying ) { + theApp.movieFrame++; + } + +#ifdef LOG_PERFORMANCE + systemSpeedTable[systemSpeedCounter++ % PERFORMANCE_INTERVAL] = systemSpeed; +#endif +} + + +void system10Frames(int rate) +{ + + if( theApp.autoFrameSkip ) + { + u32 time = systemGetClock(); + u32 diff = time - theApp.autoFrameSkipLastTime; + theApp.autoFrameSkipLastTime = time; + if( diff ) { + // countermeasure against div/0 when debugging + Sm60FPS::nCurSpeed = (1000000/rate)/diff; + } else { + Sm60FPS::nCurSpeed = 100; + } + } + + + if(theApp.rewindMemory) { + if(++theApp.rewindCounter >= (theApp.rewindTimer)) { + theApp.rewindSaveNeeded = true; + theApp.rewindCounter = 0; + } + } + if(systemSaveUpdateCounter) { + if(--systemSaveUpdateCounter <= SYSTEM_SAVE_NOT_UPDATED) { + ((MainWnd *)theApp.m_pMainWnd)->writeBatteryFile(); + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + } + } + + theApp.wasPaused = false; + +// Old autoframeskip crap... might be useful later. autoframeskip Ifdef above might be useless as well now +// theApp.autoFrameSkipLastTime = time; + +#ifdef LOG_PERFORMANCE + if( systemSpeedCounter >= PERFORMANCE_INTERVAL ) { + // log performance every PERFORMANCE_INTERVAL frames + float a = 0.0f; + for( unsigned short i = 0 ; i < PERFORMANCE_INTERVAL ; i++ ) { + a += (float)systemSpeedTable[i]; + } + a /= (float)PERFORMANCE_INTERVAL; + log( _T("Speed: %f\n"), a ); + systemSpeedCounter = 0; + } +#endif +} + +void systemScreenMessage(const char *msg) +{ + theApp.screenMessage = true; + theApp.screenMessageTime = GetTickCount(); + theApp.screenMessageBuffer = msg; + + if(theApp.screenMessageBuffer.GetLength() > 40) + theApp.screenMessageBuffer = theApp.screenMessageBuffer.Left(40); +} + +void systemUpdateMotionSensor() +{ + if(theApp.input) + theApp.input->checkMotionKeys(); +} + +int systemGetSensorX() +{ + return theApp.sensorX; +} + +int systemGetSensorY() +{ + return theApp.sensorY; +} + + +SoundDriver * systemSoundInit() +{ + SoundDriver * drv = 0; + soundShutdown(); + + switch( theApp.audioAPI ) + { + case DIRECTSOUND: + drv = newDirectSound(); + break; +#ifndef NO_OAL + case OPENAL_SOUND: + drv = newOpenAL(); + break; +#endif +#ifndef NO_XAUDIO2 + case XAUDIO2: + drv = newXAudio2_Output(); + break; +#endif + } + + if( drv ) { + if (theApp.throttle) + drv->setThrottle( theApp.throttle ); + } + + return drv; +} + + +void systemOnSoundShutdown() +{ + if( theApp.aviRecorder ) { + delete theApp.aviRecorder; + theApp.aviRecorder = NULL; + } + theApp.aviRecording = false; + + + if( theApp.soundRecorder ) { + delete theApp.soundRecorder; + theApp.soundRecorder = NULL; + } + theApp.soundRecording = false; +} + +void systemOnWriteDataToSoundBuffer(const u16 * finalWave, int length) +{ + if( theApp.soundRecording ) { + if( theApp.soundRecorder ) { + theApp.soundRecorder->AddSound( (const u8 *)finalWave, length ); + } else { + WAVEFORMATEX format; + format.cbSize = 0; + format.wFormatTag = WAVE_FORMAT_PCM; + format.nChannels = 2; + format.nSamplesPerSec = soundGetSampleRate(); + format.wBitsPerSample = 16; + format.nBlockAlign = format.nChannels * ( format.wBitsPerSample >> 3 ); + format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; + theApp.soundRecorder = new WavWriter; + if( theApp.soundRecorder->Open( theApp.soundRecordName ) ) { + theApp.soundRecorder->SetFormat( &format ); + } + } + } + + if( theApp.aviRecording && theApp.aviRecorder ) { + if( theApp.skipAudioFrames ) { + theApp.skipAudioFrames--; + } else { + if( false == theApp.aviRecorder->AddAudioFrame( ( u8 *)finalWave ) ) { + systemMessage( IDS_AVI_CANNOT_WRITE_AUDIO, "Cannot write audio frame to AVI file." ); + delete theApp.aviRecorder; + theApp.aviRecorder = NULL; + theApp.aviRecording = false; + } + } + } +} + +bool systemCanChangeSoundQuality() +{ + return true; +} + +bool systemPauseOnFrame() +{ + if(theApp.winPauseNextFrame) { + theApp.paused = true; + theApp.winPauseNextFrame = false; + return true; + } + return false; +} + +void systemGbBorderOn() +{ + if(emulating && theApp.cartridgeType == IMAGE_GB && gbBorderOn) { + theApp.updateWindowSize(theApp.videoOption); + } +} + +BOOL VBA::OnIdle(LONG lCount) +{ + if(emulating && debugger) { + MSG msg; + remoteStubMain(); + if(debugger) + return TRUE; // continue loop + return !::PeekMessage(&msg, NULL, NULL, NULL, PM_NOREMOVE); + } else if(emulating && active && !paused) { + for(int i = 0; i < 2; i++) { + emulator.emuMain(emulator.emuCount); + +#ifndef NO_LINK + if (lanlink.connected && linkid && lc.numtransfers == 0) + lc.CheckConn(); +#endif + + if(rewindSaveNeeded && rewindMemory && emulator.emuWriteMemState) { + rewindCount++; + if(rewindCount > 8) + rewindCount = 8; + if(emulator.emuWriteMemState(&rewindMemory[rewindPos*REWIND_SIZE], + REWIND_SIZE)) { + rewindPos = ++rewindPos & 7; + if(rewindCount == 8) + rewindTopPos = ++rewindTopPos & 7; + } + } + + rewindSaveNeeded = false; + } + + if(mouseCounter) { + if(--mouseCounter == 0) { + SetCursor(NULL); + } + } + return TRUE; + } + return FALSE; + + // return CWinApp::OnIdle(lCount); +} + +void VBA::addRecentFile(CString file) +{ + // Do not change recent list if frozen + if(recentFreeze) + return; + int i = 0; + for(i = 0; i < 10; i++) { + if(recentFiles[i].GetLength() == 0) + break; + + if(recentFiles[i].Compare(file) == 0) { + if(i == 0) + return; + CString p = recentFiles[i]; + for(int j = i; j > 0; j--) { + recentFiles[j] = recentFiles[j-1]; + } + recentFiles[0] = p; + return; + } + } + int num = 0; + for(i = 0; i < 10; i++) { + if(recentFiles[i].GetLength() != 0) + num++; + } + if(num == 10) { + num--; + } + + for(i = num; i >= 1; i--) { + recentFiles[i] = recentFiles[i-1]; + } + recentFiles[0] = file; +} + +void VBA::loadSettings() +{ + CString buffer; + + lastFullscreen = (VIDEO_SIZE)regQueryDwordValue("lastFullscreen", VIDEO_1024x768); + + languageOption = regQueryDwordValue("language", 1); + if(languageOption < 0 || languageOption > 2) + languageOption = 1; + + buffer = regQueryStringValue("languageName", ""); + if(!buffer.IsEmpty()) { + languageName = buffer.Left(3); + } else + languageName = ""; + + winSetLanguageOption(languageOption, true); + + frameSkip = regQueryDwordValue("frameSkip", 0); + if(frameSkip < 0 || frameSkip > 9) + frameSkip = 0; + + gbFrameSkip = regQueryDwordValue("gbFrameSkip", 0); + if(gbFrameSkip < 0 || gbFrameSkip > 9) + gbFrameSkip = 0; + + autoFrameSkip = regQueryDwordValue("autoFrameSkip", FALSE) ? TRUE : FALSE; + + vsync = regQueryDwordValue("vsync", false) ? true : false ; + synchronize = regQueryDwordValue("synchronize", 1) ? true : false; + fullScreenStretch = regQueryDwordValue("stretch", 0) ? true : false; + + videoOption = regQueryDwordValue("video", VIDEO_3X); + + strcpy(pluginName, regQueryStringValue("pluginName", "")); + + if(videoOption < VIDEO_1X || videoOption > VIDEO_OTHER) + videoOption = VIDEO_3X; + + fsAdapter = regQueryDwordValue("fsAdapter", 0); + fsWidth = regQueryDwordValue("fsWidth", 800); + fsHeight = regQueryDwordValue("fsHeight", 600); + fsColorDepth = regQueryDwordValue("fsColorDepth", 32); + fsFrequency = regQueryDwordValue("fsFrequency", 60); + + if(videoOption == VIDEO_OTHER) { + if(fsWidth < 0 || fsWidth > 4095 || fsHeight < 0 || fsHeight > 4095) + videoOption = 0; + if(fsColorDepth != 16 && fsColorDepth != 24 && fsColorDepth != 32) + videoOption = 0; + } + + renderMethod = (DISPLAY_TYPE)regQueryDwordValue("renderMethod", DIRECT_3D); +#ifdef NO_OGL + if( renderMethod == OPENGL ) { + renderMethod = DIRECT_3D; + } +#endif +#ifdef NO_D3D + if( renderMethod == DIRECT_3D ) { + renderMethod = OPENGL; + } +#endif +#ifndef NO_XAUDIO2 + audioAPI = (AUDIO_API)regQueryDwordValue( "audioAPI", XAUDIO2 ); +#else + audioAPI = (AUDIO_API)regQueryDwordValue( "audioAPI", DIRECTSOUND ); +#endif + if( ( audioAPI != DIRECTSOUND ) +#ifndef NO_OAL + && ( audioAPI != OPENAL_SOUND ) +#endif +#ifndef NO_XAUDIO2 + && ( audioAPI != XAUDIO2 ) +#endif + ) { +#ifndef NO_XAUDIO2 + audioAPI = XAUDIO2; +#else + audioAPI = DIRECTSOUND; +#endif + } + + windowPositionX = regQueryDwordValue("windowX", 0); + if(windowPositionX < 0) + windowPositionX = 0; + windowPositionY = regQueryDwordValue("windowY", 0); + if(windowPositionY < 0) + windowPositionY = 0; + + maxCpuCores = regQueryDwordValue("maxCpuCores", 0); + if(maxCpuCores == 0) { + maxCpuCores = detectCpuCores(); + } + + useBiosFileGBA = ( regQueryDwordValue("useBiosGBA", 0) == 1 ) ? true : false; + + useBiosFileGBC = ( regQueryDwordValue("useBiosGBC", 0) == 1 ) ? true : false; + + useBiosFileGB = ( regQueryDwordValue("useBiosGB", 0) == 1 ) ? true : false; + + skipBiosFile = regQueryDwordValue("skipBios", 0) ? true : false; + + buffer = regQueryStringValue("biosFileGBA", ""); + + if(!buffer.IsEmpty()) { + biosFileNameGBA = buffer; + } + + buffer = regQueryStringValue("biosFileGBC", ""); + + if(!buffer.IsEmpty()) { + biosFileNameGBC = buffer; + } + + buffer = regQueryStringValue("biosFileGB", ""); + + if(!buffer.IsEmpty()) { + biosFileNameGB = buffer; + } + + int res = regQueryDwordValue("soundEnable", 0x30f); + soundSetEnable(res); + + long soundQuality = regQueryDwordValue("soundQuality", 1); + soundSetSampleRate(44100 / soundQuality); + + soundSetVolume( (float)(regQueryDwordValue("soundVolume", 100)) / 100.0f ); + + gb_effects_config.enabled = 1 == regQueryDwordValue( "gbSoundEffectsEnabled", 0 ); + gb_effects_config.surround = 1 == regQueryDwordValue( "gbSoundEffectsSurround", 0 ); + gb_effects_config.echo = (float)regQueryDwordValue( "gbSoundEffectsEcho", 20 ) / 100.0f; + gb_effects_config.stereo = (float)regQueryDwordValue( "gbSoundEffectsStereo", 15 ) / 100.0f; + + gbSoundSetDeclicking( 1 == regQueryDwordValue( "gbSoundDeclicking", 1 ) ); + + soundInterpolation = 1 == regQueryDwordValue( "gbaSoundInterpolation", 1 ); + soundFiltering = (float)regQueryDwordValue( "gbaSoundFiltering", 50 ) / 100.0f; + + tripleBuffering = regQueryDwordValue("tripleBuffering", false) ? true : false; + +#ifndef NO_D3D + d3dFilter = regQueryDwordValue("d3dFilter", 1); + if(d3dFilter < 0 || d3dFilter > 1) + d3dFilter = 1; + + d3dMotionBlur = ( regQueryDwordValue("d3dMotionBlur", 0) == 1 ) ? true : false; +#endif + + glFilter = regQueryDwordValue("glFilter", 1); + if(glFilter < 0 || glFilter > 1) + glFilter = 1; + + + filterType = regQueryDwordValue("filter", 0); + if(filterType < 0 || filterType > 17) + filterType = 0; + + filterMT = ( 1 == regQueryDwordValue("filterEnableMultiThreading", 0) ); + + disableMMX = regQueryDwordValue("disableMMX", false) ? true: false; + + disableStatusMessage = regQueryDwordValue("disableStatus", 0) ? true : false; + + showSpeed = regQueryDwordValue("showSpeed", 0); + if(showSpeed < 0 || showSpeed > 2) + showSpeed = 0; + + showSpeedTransparent = regQueryDwordValue("showSpeedTransparent", TRUE) ? + TRUE : FALSE; + + winGbPrinterEnabled = regQueryDwordValue("gbPrinter", false) ? true : false; + + if(winGbPrinterEnabled) + gbSerialFunction = gbPrinterSend; + else + gbSerialFunction = NULL; + + pauseWhenInactive = regQueryDwordValue("pauseWhenInactive", 1) ? + true : false; + captureFormat = regQueryDwordValue("captureFormat", 0); + + removeIntros = regQueryDwordValue("removeIntros", false) ? true : false; + + recentFreeze = regQueryDwordValue("recentFreeze", false) ? true : false; + + autoPatch = regQueryDwordValue("autoPatch", 1) == 1 ? true : false; + + cpuDisableSfx = regQueryDwordValue("disableSfx", 0) ? true : false; + + winSaveType = regQueryDwordValue("saveType", 0); + if(winSaveType < 0 || winSaveType > 5) + winSaveType = 0; + + ifbType = regQueryDwordValue("ifbType", 0); + if(ifbType < 0 || ifbType > 2) + ifbType = 0; + + winFlashSize = regQueryDwordValue("flashSize", 0x10000); + if(winFlashSize != 0x10000 && winFlashSize != 0x20000) + winFlashSize = 0x10000; + flashSize = winFlashSize; + + agbPrintEnable(regQueryDwordValue("agbPrint", 0) ? true : false); + + winRtcEnable = regQueryDwordValue("rtcEnabled", 0) ? true : false; + rtcEnable(winRtcEnable); + + switch(videoOption) { + case VIDEO_320x240: + fsWidth = 320; + fsHeight = 240; + fsColorDepth = 16; + fsFrequency = 60; + break; + case VIDEO_640x480: + fsWidth = 640; + fsHeight = 480; + fsColorDepth = 16; + fsFrequency = 60; + break; + case VIDEO_800x600: + fsWidth = 800; + fsHeight = 600; + fsColorDepth = 16; + fsFrequency = 60; + break; + case VIDEO_1024x768: + fsWidth = 1024; + fsHeight = 768; + fsColorDepth = 16; + fsFrequency = 60; + break; + case VIDEO_1280x1024: + fsWidth = 1280; + fsHeight = 1024; + fsColorDepth = 16; + fsFrequency = 60; + break; + } + + winGbBorderOn = regQueryDwordValue("borderOn", 0); + gbBorderAutomatic = regQueryDwordValue("borderAutomatic", 0); + gbEmulatorType = regQueryDwordValue("emulatorType", 1); + if(gbEmulatorType < 0 || gbEmulatorType > 5) + gbEmulatorType = 1; + gbColorOption = regQueryDwordValue("colorOption", 0); + + threadPriority = regQueryDwordValue("priority", 2); + + if(threadPriority < 0 || threadPriority >3) + threadPriority = 2; + updatePriority(); + + autoSaveLoadCheatList = ( 1 == regQueryDwordValue( "autoSaveCheatList", 1 ) ) ? true : false; + + gbPaletteOption = regQueryDwordValue("gbPaletteOption", 0); + if(gbPaletteOption < 0) + gbPaletteOption = 0; + if(gbPaletteOption > 2) + gbPaletteOption = 2; + + regQueryBinaryValue("gbPalette", (char *)systemGbPalette, + 24*sizeof(u16)); + + rewindTimer = regQueryDwordValue("rewindTimer", 0); + + if(rewindTimer < 0 || rewindTimer > 600) + rewindTimer = 0; + + rewindTimer *= 6; // convert to 10 frames multiple + + if(rewindTimer != 0) + rewindMemory = (char *)malloc(8*REWIND_SIZE); + + for(int i = 0; i < 10; i++) { + buffer.Format("recent%d", i); + char *s = regQueryStringValue(buffer, NULL); + if(s == NULL) + break; + recentFiles[i] = s; + } + + joypadDefault = regQueryDwordValue("joypadDefault", 0); + if(joypadDefault < 0 || joypadDefault > 3) + joypadDefault = 0; + + autoLoadMostRecent = ( 1 == regQueryDwordValue("autoLoadMostRecent", 0) ) ? true : false; + + skipSaveGameBattery = ( 1 == regQueryDwordValue("skipSaveGameBattery", 0) ) ? true : false; + + skipSaveGameCheats = ( 1 == regQueryDwordValue("skipSaveGameCheats", 0) ) ? true : false; + + cheatsEnabled = regQueryDwordValue("cheatsEnabled", false) ? true : false; + + maxScale = regQueryDwordValue("maxScale", 0); + + updateThrottle( (unsigned short)regQueryDwordValue( "throttle", 0 ) ); + +#ifndef NO_LINK + linktimeout = regQueryDwordValue("LinkTimeout", 1000); + + rfu_enabled = regQueryDwordValue("RFU", false) ? true : false; + gba_link_enabled = regQueryDwordValue("linkEnabled", false) ? true : false; + gba_joybus_enabled = regQueryDwordValue("joybusEnabled", false) ? true : false; + buffer = regQueryStringValue("joybusHostAddr", ""); + + if(!buffer.IsEmpty()) { + joybusHostAddr = std::string(buffer); + } + + lanlink.active = regQueryDwordValue("LAN", 0) ? true : false; +#endif + + Sm60FPS::bSaveMoreCPU = regQueryDwordValue("saveMoreCPU", 0); + +#ifndef NO_OAL + buffer = regQueryStringValue( "oalDevice", "Generic Software" ); + if( oalDevice ) { + free( oalDevice ); + } + oalDevice = (TCHAR*)malloc( ( buffer.GetLength() + 1 ) * sizeof( TCHAR ) ); + _tcscpy( oalDevice, buffer.GetBuffer() ); + + oalBufferCount = regQueryDwordValue( "oalBufferCount", 5 ); +#endif + +#ifndef NO_XAUDIO2 + xa2Device = regQueryDwordValue( "xa2Device", 0 ); + xa2BufferCount = regQueryDwordValue( "xa2BufferCount", 4 ); + xa2Upmixing = ( 1 == regQueryDwordValue( "xa2Upmixing", 0 ) ); +#endif + + if( ( maxCpuCores == 1 ) && filterMT ) { + // multi-threading use useless for just one core + filterMT = false; + } +} + +void VBA::updateFrameSkip() +{ + switch(cartridgeType) { + case 0: + systemFrameSkip = frameSkip; + break; + case 1: + systemFrameSkip = gbFrameSkip; + break; + } +} + +void VBA::updateVideoSize(UINT id) +{ + int value = 0; + + switch(id) { + case ID_OPTIONS_VIDEO_X1: + value = VIDEO_1X; + break; + case ID_OPTIONS_VIDEO_X2: + value = VIDEO_2X; + break; + case ID_OPTIONS_VIDEO_X3: + value = VIDEO_3X; + break; + case ID_OPTIONS_VIDEO_X4: + value = VIDEO_4X; + break; + case ID_OPTIONS_VIDEO_X5: + value = VIDEO_5X; + break; + case ID_OPTIONS_VIDEO_X6: + value = VIDEO_6X; + break; + case ID_OPTIONS_VIDEO_FULLSCREEN320X240: + value = VIDEO_320x240; + fsWidth = 320; + fsHeight = 240; + fsColorDepth = 16; + break; + case ID_OPTIONS_VIDEO_FULLSCREEN640X480: + value = VIDEO_640x480; + fsWidth = 640; + fsHeight = 480; + fsColorDepth = 16; + break; + case ID_OPTIONS_VIDEO_FULLSCREEN800X600: + value = VIDEO_800x600; + fsWidth = 800; + fsHeight = 600; + fsColorDepth = 16; + break; + case ID_OPTIONS_VIDEO_FULLSCREEN1024X768: + value = VIDEO_1024x768; + fsWidth = 1024; + fsHeight = 768; + fsColorDepth = 32; + break; + case ID_OPTIONS_VIDEO_FULLSCREEN1280X1024: + value = VIDEO_1280x1024; + fsWidth = 1280; + fsHeight = 1024; + fsColorDepth = 32; + break; + case ID_OPTIONS_VIDEO_FULLSCREEN: + value = VIDEO_OTHER; + break; + } + + updateWindowSize(value); +} + + +void VBA::updateWindowSize(int value) +{ + regSetDwordValue("video", value); + + if(value == VIDEO_OTHER) { + regSetDwordValue("fsWidth", fsWidth); + regSetDwordValue("fsHeight", fsHeight); + regSetDwordValue("fsColorDepth", fsColorDepth); + } + + if(((value >= VIDEO_320x240) && + (videoOption != value)) || + (videoOption >= VIDEO_320x240 && + value <= VIDEO_6X) || + fsForceChange) { + fsForceChange = false; + changingVideoSize = true; + if( videoOption <= VIDEO_6X ) { + lastWindowed = (VIDEO_SIZE)videoOption; // save for when leaving full screen + } else { + lastFullscreen = (VIDEO_SIZE)videoOption; // save for when using quick switch to fullscreen + } + shutdownDisplay(); + if(input) { + delete input; + input = NULL; + } + m_pMainWnd->DragAcceptFiles(FALSE); + CWnd *pWnd = m_pMainWnd; + m_pMainWnd = NULL; + pWnd->DestroyWindow(); + delete pWnd; + videoOption = value; + if(!initDisplay()) { + if(videoOption == VIDEO_320x240 || + videoOption == VIDEO_640x480 || + videoOption == VIDEO_800x600 || + videoOption == VIDEO_1024x768 || + videoOption == VIDEO_1280x1024 || + videoOption == VIDEO_OTHER) { + regSetDwordValue("video", VIDEO_1X); + } + changingVideoSize = false; + AfxPostQuitMessage(0); + return; + } + if(!initInput()) { + changingVideoSize = false; + AfxPostQuitMessage(0); + return; + } + input->checkKeys(); + + + changingVideoSize = FALSE; + updateWindowSize(videoOption); + return; + } + + sizeX = 240; + sizeY = 160; + + videoOption = value; + + if(cartridgeType == IMAGE_GB) { + if(gbBorderOn) { + sizeX = 256; + sizeY = 224; + gbBorderLineSkip = 256; + gbBorderColumnSkip = 48; + gbBorderRowSkip = 40; + } else { + sizeX = 160; + sizeY = 144; + gbBorderLineSkip = 160; + gbBorderColumnSkip = 0; + gbBorderRowSkip = 0; + } + } + + surfaceSizeX = sizeX; + surfaceSizeY = sizeY; + + switch(videoOption) { + case VIDEO_1X: + surfaceSizeX = sizeX; + surfaceSizeY = sizeY; + break; + case VIDEO_2X: + surfaceSizeX = sizeX * 2; + surfaceSizeY = sizeY * 2; + break; + case VIDEO_3X: + surfaceSizeX = sizeX * 3; + surfaceSizeY = sizeY * 3; + break; + case VIDEO_4X: + surfaceSizeX = sizeX * 4; + surfaceSizeY = sizeY * 4; + break; + case VIDEO_5X: + surfaceSizeX = sizeX * 5; + surfaceSizeY = sizeY * 5; + break; + case VIDEO_6X: + surfaceSizeX = sizeX * 6; + surfaceSizeY = sizeY * 6; + break; + case VIDEO_320x240: + case VIDEO_640x480: + case VIDEO_800x600: + case VIDEO_1024x768: + case VIDEO_1280x1024: + case VIDEO_OTHER: + { + int scaleX = 1; + int scaleY = 1; + scaleX = (fsWidth / sizeX); + scaleY = (fsHeight / sizeY); + int min = scaleX < scaleY ? scaleX : scaleY; + if(maxScale) + min = min > maxScale ? maxScale : min; + surfaceSizeX = min * sizeX; + surfaceSizeY = min * sizeY; + if((fullScreenStretch && (display != NULL && + (display->getType() != DIRECT_3D))) + || (display != NULL && display->getType() >= DIRECT_3D)) { + surfaceSizeX = fsWidth; + surfaceSizeY = fsHeight; + } + } + break; + } + + rect.right = sizeX; + rect.bottom = sizeY; + + int winSizeX = sizeX; + int winSizeY = sizeY; + + if(videoOption <= VIDEO_6X) { + dest.left = 0; + dest.top = 0; + dest.right = surfaceSizeX; + dest.bottom = surfaceSizeY; + + DWORD style = WS_POPUP | WS_VISIBLE; + + style |= WS_OVERLAPPEDWINDOW; + + AdjustWindowRectEx(&dest, style, TRUE, 0); //WS_EX_TOPMOST); + + winSizeX = dest.right-dest.left; + winSizeY = dest.bottom-dest.top; + + m_pMainWnd->SetWindowPos(0, //HWND_TOPMOST, + windowPositionX, + windowPositionY, + winSizeX, + winSizeY, + SWP_NOMOVE | SWP_SHOWWINDOW); + + // content of old seperate 'winCheckMenuBarInfo' function: + MENUBARINFO info; + info.cbSize = sizeof(MENUBARINFO); + GetMenuBarInfo( theApp.m_pMainWnd->GetSafeHwnd(), OBJID_MENU, 0, &info ); + int menuHeight = GetSystemMetrics(SM_CYMENU); // includes white line + if((info.rcBar.bottom - info.rcBar.top) > menuHeight) // check for double height menu + { + winSizeY += (info.rcBar.bottom - info.rcBar.top) - menuHeight + 1; + m_pMainWnd->SetWindowPos( + 0, //HWND_TOPMOST, + theApp.windowPositionX, + theApp.windowPositionY, + winSizeX, + winSizeY, + SWP_NOMOVE | SWP_SHOWWINDOW); + } + } + + adjustDestRect(); + + updateIFB(); + updateFilter(); + + if(display) + display->resize(theApp.dest.right-theApp.dest.left, theApp.dest.bottom-theApp.dest.top); + + m_pMainWnd->RedrawWindow(NULL,NULL,RDW_INVALIDATE|RDW_ERASE|RDW_ALLCHILDREN); +} + + +bool VBA::initDisplay() +{ + return updateRenderMethod(false); +} + + +bool VBA::preInitialize() +{ + switch( cartridgeType ) + { + case IMAGE_GBA: + sizeX = 240; + sizeY = 160; + break; + case IMAGE_GB: + if( gbBorderOn ) { + sizeX = 256; + sizeY = 224; + } else { + sizeX = 160; + sizeY = 144; + } + break; + } + + switch( videoOption ) + { + case VIDEO_1X: + surfaceSizeX = sizeX; + surfaceSizeY = sizeY; + break; + case VIDEO_2X: + surfaceSizeX = sizeX * 2; + surfaceSizeY = sizeY * 2; + break; + case VIDEO_3X: + surfaceSizeX = sizeX * 3; + surfaceSizeY = sizeY * 3; + break; + case VIDEO_4X: + surfaceSizeX = sizeX * 4; + surfaceSizeY = sizeY * 4; + break; + case VIDEO_5X: + surfaceSizeX = sizeX * 5; + surfaceSizeY = sizeY * 5; + break; + case VIDEO_6X: + surfaceSizeX = sizeX * 6; + surfaceSizeY = sizeY * 6; + break; + case VIDEO_320x240: + case VIDEO_640x480: + case VIDEO_800x600: + case VIDEO_1024x768: + case VIDEO_1280x1024: + case VIDEO_OTHER: + float scaleX = (float)fsWidth / sizeX; + float scaleY = (float)fsHeight / sizeY; + float min = ( scaleX < scaleY ) ? scaleX : scaleY; + if( fullScreenStretch ) { + surfaceSizeX = fsWidth; + surfaceSizeY = fsHeight; + } else { + surfaceSizeX = (int)( sizeX * min ); + surfaceSizeY = (int)( sizeY * min ); + } + break; + } + + rect.left = 0; + rect.top = 0; + rect.right = sizeX; + rect.bottom = sizeY; + + dest.left = 0; + dest.top = 0; + dest.right = surfaceSizeX; + dest.bottom = surfaceSizeY; + + + DWORD style = WS_POPUP | WS_VISIBLE; + DWORD styleEx = 0; + + if( videoOption <= VIDEO_6X ) { + style |= WS_OVERLAPPEDWINDOW; + } else { + styleEx = 0; + } + + if( videoOption <= VIDEO_6X ) { + AdjustWindowRectEx( &dest, style, TRUE, styleEx ); + } else { + AdjustWindowRectEx( &dest, style, FALSE, styleEx ); + } + + int winSizeX = dest.right-dest.left; + int winSizeY = dest.bottom-dest.top; + + if( videoOption > VIDEO_6X ) { + winSizeX = fsWidth; + winSizeY = fsHeight; + } + + int x = 0, y = 0; + + if( videoOption <= VIDEO_6X ) { + x = windowPositionX; + y = windowPositionY; + } + + + // Create a window + MainWnd *pWnd = new MainWnd; + m_pMainWnd = pWnd; + + pWnd->CreateEx( + styleEx, + wndClass, + _T(VBA_NAME_AND_SUBVERSION), + style, + x, y, + winSizeX, winSizeY, + NULL, + 0 + ); + + if( !((HWND)*pWnd) ) { + winlog( "Error creating Window %08x\n", GetLastError() ); + return false; + } + pWnd->DragAcceptFiles( TRUE ); + updateMenuBar(); + adjustDestRect(); + + return true; +} + + +bool VBA::updateRenderMethod(bool force) +{ + bool ret = true; + + Sm60FPS_Init(); + + if( !updateRenderMethod0( force ) ) { + // fall back to safe configuration + renderMethod = DIRECT_3D; + fsAdapter = 0; + videoOption = VIDEO_1X; + ret = updateRenderMethod( true ); + } + + return ret; +} + + +bool VBA::updateRenderMethod0(bool force) +{ + bool initInput = false; + b16to32Video = false; + + if(display) { + if(display->getType() != renderMethod || force) { + toolsLoggingClose(); // close log dialog + initInput = true; + changingVideoSize = true; + shutdownDisplay(); + if(input) { + delete input; + input = NULL; + } + CWnd *pWnd = m_pMainWnd; + + m_pMainWnd = NULL; + pWnd->DragAcceptFiles(FALSE); + pWnd->DestroyWindow(); + delete pWnd; + + display = NULL; + regSetDwordValue("renderMethod", renderMethod); + } + } + if(display == NULL) { + switch(renderMethod) { +#ifndef NO_OGL + case OPENGL: + display = newOpenGLDisplay(); + break; +#endif +#ifndef NO_D3D + case DIRECT_3D: + display = newDirect3DDisplay(); + break; +#endif + } + + if( preInitialize() ) { + if( display->initialize() ) { + if( initInput ) { + if( !this->initInput() ) { + changingVideoSize = false; + AfxPostQuitMessage(0); + return false; + } + input->checkKeys(); + updateMenuBar(); + changingVideoSize = false; + updateWindowSize(videoOption); + + m_pMainWnd->ShowWindow(SW_SHOW); + m_pMainWnd->UpdateWindow(); + m_pMainWnd->SetFocus(); + + return true; + } else { + changingVideoSize = false; + return true; + } + } + } + changingVideoSize = false; + } + return true; +} + + +void VBA::shutdownDisplay() +{ + if(display != NULL) { + display->cleanup(); + delete display; + display = NULL; + } +} + +void VBA::directXMessage(const char *msg) +{ + systemMessage(IDS_DIRECTX_7_REQUIRED, + "DirectX 7.0 or greater is required to run.\nDownload at http://www.microsoft.com/directx.\n\nError found at: %s", + msg); +} + +void VBA::updatePriority() +{ + switch(threadPriority) { + case 0: + SetThreadPriority(THREAD_PRIORITY_HIGHEST); + break; + case 1: + SetThreadPriority(THREAD_PRIORITY_ABOVE_NORMAL); + break; + case 3: + SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL); + break; + default: + SetThreadPriority(THREAD_PRIORITY_NORMAL); + } +} + +#ifdef MMX +bool VBA::detectMMX() +{ + bool support = false; + char brand[12]; // not zero terminated + + // check for Intel chip + __try { + __asm { + mov eax, 0; + cpuid; + mov [dword ptr brand+0], ebx; + mov [dword ptr brand+4], edx; + mov [dword ptr brand+8], ecx; + } + } + __except(EXCEPTION_EXECUTE_HANDLER) { + if(_exception_code() == STATUS_ILLEGAL_INSTRUCTION) { + return false; + } + return false; + } + // Check for Intel or AMD CPUs + if(strncmp(brand, "GenuineIntel", 12)) { + if(strncmp(brand, "AuthenticAMD", 12)) { + return false; + } + } + + __asm { + mov eax, 1; + cpuid; + test edx, 00800000h; + jz NotFound; + mov [support], 1; + NotFound: + } + return support; +} +#endif + +void VBA::winSetLanguageOption(int option, bool force) +{ + if(((option == languageOption) && option != 2) && !force) + return; + switch(option) { + case 0: + { + char lbuffer[10]; + + if(GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_SABBREVLANGNAME, + lbuffer, 10)) { + HINSTANCE l = winLoadLanguage(lbuffer); + if(l == NULL) { + LCID locIdBase = MAKELCID( MAKELANGID( PRIMARYLANGID( GetSystemDefaultLangID() ), SUBLANG_NEUTRAL ), SORT_DEFAULT ); + if(GetLocaleInfo(locIdBase, LOCALE_SABBREVLANGNAME, + lbuffer, 10)) { + l = winLoadLanguage(lbuffer); + if(l == NULL) { + systemMessage(IDS_FAILED_TO_LOAD_LIBRARY, + "Failed to load library %s", + lbuffer); + return; + } + } + } + AfxSetResourceHandle(l); + if(languageModule != NULL) +#ifdef _AFXDLL + AfxFreeLibrary( languageModule ); +#else + FreeLibrary( languageModule ); +#endif + languageModule = l; + } else { + systemMessage(IDS_FAILED_TO_GET_LOCINFO, + "Failed to get locale information"); + return; + } + } + break; + case 1: + if(languageModule != NULL) +#ifdef _AFXDLL + AfxFreeLibrary( languageModule ); +#else + FreeLibrary( languageModule ); +#endif + languageModule = NULL; + AfxSetResourceHandle(AfxGetInstanceHandle()); + break; + case 2: + { + if(!force) { + LangSelect dlg; + if(dlg.DoModal()) { + HINSTANCE l = winLoadLanguage(languageName); + if(l == NULL) { + systemMessage(IDS_FAILED_TO_LOAD_LIBRARY, + "Failed to load library %s", + languageName); + return; + } + AfxSetResourceHandle(l); + if(languageModule != NULL) + { +#ifdef _AFXDLL + AfxFreeLibrary( languageModule ); +#else + FreeLibrary( languageModule ); +#endif + } + languageModule = l; + } + } else { + if(languageName.IsEmpty()) + return; + HINSTANCE l = winLoadLanguage(languageName); + if(l == NULL) { + systemMessage(IDS_FAILED_TO_LOAD_LIBRARY, + "Failed to load library %s", + languageName); + return; + } + AfxSetResourceHandle(l); + if(languageModule != NULL) + FreeLibrary(languageModule); + languageModule = l; + } + } + break; + } + languageOption = option; + updateMenuBar(); +} + +HINSTANCE VBA::winLoadLanguage(const char *name) +{ + CString buffer; + + buffer.Format( _T("vba_%s.dll"), name); + +#ifdef _AFXDLL + HINSTANCE l = AfxLoadLibrary( buffer ); +#else + HMODULE l = LoadLibrary( buffer ); +#endif + + if(l == NULL) { + if(strlen(name) == 3) { + char buffer2[3]; + buffer2[0] = name[0]; + buffer2[1] = name[1]; + buffer2[2] = 0; + buffer.Format("vba_%s.dll", buffer2); + +#ifdef _AFXDLL + return AfxLoadLibrary( buffer ); +#else + return LoadLibrary( buffer ); +#endif + } + } + return l; +} + + +bool VBA::initInput() +{ + if(input) + delete input; + input = newDirectInput(); + if(input->initialize()) { + input->loadSettings(); + input->checkKeys(); + return true; + } + delete input; + return false; +} + +void VBA::winAddUpdateListener(IUpdateListener *l) +{ + updateList.AddTail(l); + updateCount++; +} + +void VBA::winRemoveUpdateListener(IUpdateListener *l) +{ + POSITION pos = updateList.Find(l); + if(pos) { + updateList.RemoveAt(pos); + updateCount--; + if(updateCount < 0) + updateCount = 0; + } +} + +CString VBA::winLoadFilter(UINT id) +{ + CString res = winResLoadString(id); + res.Replace('_','|'); + + return res; +} + +void VBA::movieReadNext() +{ + if(movieFile) { + bool movieEnd = false; + + if(fread(&moviePlayFrame, 1, sizeof(int), movieFile) == sizeof(int)) { + if(fread(&movieNextJoypad, 1, sizeof(u32), movieFile) == sizeof(int)) { + // make sure we don't have spurious entries on the movie that can + // cause us to play it forever + if(moviePlayFrame <= movieFrame) + movieEnd = true; + } else + movieEnd = true; + } else + movieEnd = true; + if(movieEnd) { + CString string = winResLoadString(IDS_END_OF_MOVIE); + systemScreenMessage(string); + moviePlaying = false; + fclose(movieFile); + movieFile = NULL; + return; + } + } else + moviePlaying = false; +} + +void VBA::saveSettings() +{ + regSetDwordValue("language", languageOption); + + regSetStringValue("languageName", languageName); + + regSetDwordValue("frameSkip", frameSkip); + + regSetDwordValue("gbFrameSkip", gbFrameSkip); + + regSetDwordValue("autoFrameSkip", autoFrameSkip); + regSetDwordValue("vsync", vsync); + regSetDwordValue("synchronize", synchronize); + regSetDwordValue("stretch", fullScreenStretch); + + regSetDwordValue("video", videoOption); + + regSetDwordValue("fsAdapter", fsAdapter); + regSetDwordValue("fsWidth", fsWidth); + regSetDwordValue("fsHeight", fsHeight); + regSetDwordValue("fsColorDepth", fsColorDepth); + regSetDwordValue("fsFrequency", fsFrequency); + + regSetDwordValue("renderMethod", renderMethod); + regSetDwordValue( "audioAPI", audioAPI ); + + regSetDwordValue("windowX", windowPositionX); + regSetDwordValue("windowY", windowPositionY); + + regSetDwordValue("maxCpuCores", maxCpuCores); + + regSetDwordValue("useBiosGBA", useBiosFileGBA); + + regSetDwordValue("useBiosGBC", useBiosFileGBC); + + regSetDwordValue("useBiosGB", useBiosFileGB); + + regSetDwordValue("skipBios", skipBiosFile); + + if(!biosFileNameGBA.IsEmpty()) + regSetStringValue("biosFileGBA", biosFileNameGBA); + + if(!biosFileNameGBC.IsEmpty()) + regSetStringValue("biosFileGBC", biosFileNameGBC); + + if(!biosFileNameGB.IsEmpty()) + regSetStringValue("biosFileGB", biosFileNameGB); + + regSetDwordValue("soundEnable", soundGetEnable() & 0x30f); + + regSetDwordValue("soundQuality", 44100 / soundGetSampleRate() ); + + regSetDwordValue("soundVolume", (DWORD)(soundGetVolume() * 100.0f)); + + regSetDwordValue( "gbSoundEffectsEnabled", gb_effects_config.enabled ? 1 : 0 ); + regSetDwordValue( "gbSoundEffectsSurround", gb_effects_config.surround ? 1 : 0 ); + regSetDwordValue( "gbSoundEffectsEcho", (DWORD)( gb_effects_config.echo * 100.0f ) ); + regSetDwordValue( "gbSoundEffectsStereo", (DWORD)( gb_effects_config.stereo * 100.0f ) ); + + regSetDwordValue( "gbSoundDeclicking", gbSoundGetDeclicking() ? 1 : 0 ); + + regSetDwordValue( "gbaSoundInterpolation", soundInterpolation ? 1 : 0 ); + regSetDwordValue( "gbaSoundFiltering", (DWORD)( soundFiltering * 100.0f ) ); + + regSetDwordValue("tripleBuffering", tripleBuffering); + +#ifndef NO_D3D + regSetDwordValue("d3dFilter", d3dFilter); + regSetDwordValue("d3dMotionBlur", d3dMotionBlur ? 1 : 0); +#endif + + regSetDwordValue("glFilter", glFilter); + + regSetDwordValue("filter", filterType); + + regSetDwordValue("filterEnableMultiThreading", filterMT ? 1 : 0); + + regSetDwordValue("disableMMX", disableMMX); + + regSetDwordValue("disableStatus", disableStatusMessage); + + regSetDwordValue("showSpeed", showSpeed); + + regSetDwordValue("showSpeedTransparent", showSpeedTransparent); + + regSetDwordValue("gbPrinter", winGbPrinterEnabled); + + regSetDwordValue("captureFormat", captureFormat); + + regSetDwordValue("removeIntros", removeIntros); + + regSetDwordValue("recentFreeze", recentFreeze); + + regSetDwordValue("autoPatch", autoPatch ? 1 : 0); + + regSetDwordValue("disableSfx", cpuDisableSfx); + + regSetDwordValue("saveType", winSaveType); + + regSetDwordValue("ifbType", ifbType); + + regSetDwordValue("flashSize", winFlashSize); + + regSetDwordValue("agbPrint", agbPrintIsEnabled()); + + regSetDwordValue("rtcEnabled", winRtcEnable); + + regSetDwordValue("borderOn", winGbBorderOn); + regSetDwordValue("borderAutomatic", gbBorderAutomatic); + regSetDwordValue("emulatorType", gbEmulatorType); + regSetDwordValue("colorOption", gbColorOption); + + regSetDwordValue("priority", threadPriority); + + regSetDwordValue("autoSaveCheatList", autoSaveLoadCheatList); + + regSetDwordValue("gbPaletteOption", gbPaletteOption); + + regSetBinaryValue("gbPalette", (char *)systemGbPalette, + 24*sizeof(u16)); + + regSetDwordValue("rewindTimer", rewindTimer/6); + + CString buffer; + for(int i = 0; i < 10; i++) { + buffer.Format("recent%d", i); + regSetStringValue(buffer, recentFiles[i]); + } + + regSetDwordValue("joypadDefault", joypadDefault); + regSetDwordValue("autoLoadMostRecent", autoLoadMostRecent); + regSetDwordValue("skipSaveGameBattery", skipSaveGameBattery); + regSetDwordValue("skipSaveGameCheats", skipSaveGameCheats); + regSetDwordValue("cheatsEnabled", cheatsEnabled); + regSetDwordValue("maxScale", maxScale); + regSetDwordValue("throttle", throttle); + regSetStringValue("pluginName", pluginName); + regSetDwordValue("saveMoreCPU", Sm60FPS::bSaveMoreCPU); + +#ifndef NO_LINK + regSetDwordValue("LinkTimeout", linktimeout); + regSetDwordValue("RFU", rfu_enabled); + regSetDwordValue("linkEnabled", gba_link_enabled); + regSetDwordValue("joybusEnabled", gba_joybus_enabled); + regSetStringValue("joybusHostAddr", joybusHostAddr.ToString().c_str()); +#endif + + regSetDwordValue("lastFullscreen", lastFullscreen); + regSetDwordValue("pauseWhenInactive", pauseWhenInactive); + +#ifndef NO_OAL + regSetStringValue( "oalDevice", oalDevice ); + regSetDwordValue( "oalBufferCount", oalBufferCount ); +#endif + +#ifndef NO_XAUDIO2 + regSetDwordValue( "xa2Device", xa2Device ); + regSetDwordValue( "xa2BufferCount", xa2BufferCount ); + regSetDwordValue( "xa2Upmixing", xa2Upmixing ? 1 : 0 ); +#endif +} + +unsigned int VBA::detectCpuCores() +{ + SYSTEM_INFO info; + + GetSystemInfo( &info ); + + return info.dwNumberOfProcessors; +} + +void winSignal(int, int) +{ +} + +#define CPUReadByteQuick(addr) \ + map[(addr)>>24].address[(addr) & map[(addr)>>24].mask] + +void winOutput(const char *s, u32 addr) +{ + if(s) { + toolsLog(s); + } else { + CString str; + char c; + + c = CPUReadByteQuick(addr); + addr++; + while(c) { + str += c; + c = CPUReadByteQuick(addr); + addr++; + } + toolsLog(str); + } +} + + +void Sm60FPS_Init() +{ + Sm60FPS::dwTimeElapse = 0; + Sm60FPS::fWantFPS = 60.f; + Sm60FPS::fCurFPS = 0.f; + Sm60FPS::nFrameCnt = 0; + Sm60FPS::bLastSkip = false; + Sm60FPS::nCurSpeed = 100; +} + + +bool Sm60FPS_CanSkipFrame() +{ + if( theApp.autoFrameSkip ) { + if( Sm60FPS::nFrameCnt == 0 ) { + Sm60FPS::nFrameCnt = 0; + Sm60FPS::dwTimeElapse = 0; + Sm60FPS::dwTime0 = GetTickCount(); + } else { + if( Sm60FPS::nFrameCnt >= 10 ) { + Sm60FPS::nFrameCnt = 0; + Sm60FPS::dwTimeElapse = 0; + + if( Sm60FPS::nCurSpeed > Sm60FPS::K_fCpuSpeed ) { + Sm60FPS::fWantFPS += 1; + if( Sm60FPS::fWantFPS > Sm60FPS::K_fTargetFps ){ + Sm60FPS::fWantFPS = Sm60FPS::K_fTargetFps; + } + } else { + if( Sm60FPS::nCurSpeed < (Sm60FPS::K_fCpuSpeed - 5) ) { + Sm60FPS::fWantFPS -= 1; + if( Sm60FPS::fWantFPS < 30.f ) { + Sm60FPS::fWantFPS = 30.f; + } + } + } + } else { // between frame 1-10 + Sm60FPS::dwTime1 = GetTickCount(); + Sm60FPS::dwTimeElapse += (Sm60FPS::dwTime1 - Sm60FPS::dwTime0); + Sm60FPS::dwTime0 = Sm60FPS::dwTime1; + if( !Sm60FPS::bLastSkip && + ( (Sm60FPS::fWantFPS < Sm60FPS::K_fTargetFps) || Sm60FPS::bSaveMoreCPU) ) { + Sm60FPS::fCurFPS = (float)Sm60FPS::nFrameCnt * 1000 / Sm60FPS::dwTimeElapse; + if( (Sm60FPS::fCurFPS < Sm60FPS::K_fTargetFps) || Sm60FPS::bSaveMoreCPU ) { + Sm60FPS::bLastSkip = true; + Sm60FPS::nFrameCnt++; + return true; + } + } + } + } + Sm60FPS::bLastSkip = false; + Sm60FPS::nFrameCnt++; + } + return false; +} + + +void Sm60FPS_Sleep() +{ + if( theApp.autoFrameSkip ) { + u32 dwTimePass = Sm60FPS::dwTimeElapse + (GetTickCount() - Sm60FPS::dwTime0); + u32 dwTimeShould = (u32)(Sm60FPS::nFrameCnt * Sm60FPS::K_fDT); + if( dwTimeShould > dwTimePass ) { + Sleep(dwTimeShould - dwTimePass); + } + } +} diff --git a/src/win32/VBA.h b/src/win32/VBA.h new file mode 100644 index 0000000..0f8c3a8 --- /dev/null +++ b/src/win32/VBA.h @@ -0,0 +1,260 @@ +#pragma once + +#ifndef __AFXWIN_H__ +#error include 'stdafx.h' before including this file for PCH +#endif + +#include "resource.h" + +#include "AcceleratorManager.h" +#include "Display.h" +#include "Input.h" +#include "IUpdate.h" +#include "../System.h" +#include "../Util.h" + +///////////////////////////////////////////////////////////////////////////// +// VBA: +// See VBA.cpp for the implementation of this class +// + +enum VIDEO_SIZE{ + VIDEO_1X, VIDEO_2X, VIDEO_3X, VIDEO_4X, VIDEO_5X, VIDEO_6X, + VIDEO_320x240, VIDEO_640x480, VIDEO_800x600, VIDEO_1024x768, VIDEO_1280x1024, + VIDEO_OTHER +}; + +enum pixelFilterType +{ + FILTER_NONE, + + FILTER_SIMPLE2X, FILTER_PIXELATE, FILTER_TVMODE, FILTER_SCANLINES, + FILTER_PLUGIN, + FILTER_BILINEAR, FILTER_BILINEARPLUS, FILTER_MAMESCALE2X, + FILTER_2XSAI, FILTER_SUPER2XSAI, FILTER_SUPEREAGLE, FILTER_LQ2X, FILTER_HQ2X, + + FILTER_SIMPLE3X, FILTER_HQ3X, + + FILTER_SIMPLE4X, FILTER_HQ4X +}; + +enum AUDIO_API { + DIRECTSOUND = 0 +#ifndef NO_OAL + , OPENAL_SOUND = 1 +#endif +#ifndef NO_XAUDIO2 + , XAUDIO2 = 2 +#endif +}; + +#define REWIND_SIZE 400000 + +class AVIWrite; +class WavWriter; + +class VBA : public CWinApp +{ + public: + CMenu m_menu; + HMENU menu; + HMENU popup; + unsigned int maxCpuCores; // maximum number of CPU cores VBA should use, 0 means auto-detect + int windowPositionX; + int windowPositionY; + void (*filterFunction)(u8*,u32,u8*,u8*,u32,int,int); + void (*ifbFunction)(u8*,u32,int,int); + int ifbType; + int filterType; + char pluginName[MAX_PATH]; + int filterWidth; + int filterHeight; + int filterMagnification; + bool filterMT; // enable multi-threading for pixel filters + int fsWidth; + int fsHeight; + int fsColorDepth; + int fsFrequency; + int fsAdapter; + bool fsForceChange; + int sizeX; + int sizeY; + int surfaceSizeX; + int surfaceSizeY; + int videoOption; + bool fullScreenStretch; + bool disableStatusMessage; + int showSpeed; + BOOL showSpeedTransparent; + int showRenderedFrames; + bool screenMessage; + CString screenMessageBuffer; + DWORD screenMessageTime; + u8 *delta[257*244*4]; + IDisplay *display; + IMAGE_TYPE cartridgeType; + bool soundInitialized; + bool useBiosFileGBA; + bool useBiosFileGBC; + bool useBiosFileGB; + bool skipBiosFile; + CString biosFileNameGBA; + CString biosFileNameGBC; + CString biosFileNameGB; + bool active; + bool paused; + CString recentFiles[10]; + bool recentFreeze; + bool autoSaveLoadCheatList; + FILE *winout; + bool removeIntros; + bool autoPatch; + int winGbBorderOn; + int winFlashSize; + bool winRtcEnable; + int winSaveType; + char *rewindMemory; + int rewindPos; + int rewindTopPos; + int rewindCounter; + int rewindCount; + bool rewindSaveNeeded; + int rewindTimer; + int captureFormat; + bool tripleBuffering; + unsigned short throttle; + u32 autoFrameSkipLastTime; + bool autoFrameSkip; + bool vsync; + bool changingVideoSize; + DISPLAY_TYPE renderMethod; + AUDIO_API audioAPI; +#ifndef NO_OAL + TCHAR *oalDevice; + int oalBufferCount; +#endif +#ifndef NO_XAUDIO2 + UINT32 xa2Device; + UINT32 xa2BufferCount; + bool xa2Upmixing; +#endif +#ifndef NO_D3D + int d3dFilter; + bool d3dMotionBlur; +#endif + bool iconic; + int glFilter; + bool dinputKeyFocus; + bool pauseWhenInactive; + bool speedupToggle; + bool winGbPrinterEnabled; + int threadPriority; + bool disableMMX; + int languageOption; + CString languageName; + HMODULE languageModule; + int renderedFrames; + Input *input; + int joypadDefault; + int autoFire; + bool autoFireToggle; + bool winPauseNextFrame; + bool soundRecording; + WavWriter *soundRecorder; + CString soundRecordName; + bool dsoundDisableHardwareAcceleration; + bool aviRecording; + AVIWrite *aviRecorder; + CString aviRecordName; + bool painting; + unsigned int skipAudioFrames; + bool movieRecording; + bool moviePlaying; + int movieFrame; + int moviePlayFrame; + FILE *movieFile; + u32 movieLastJoypad; + u32 movieNextJoypad; + int sensorX; + int sensorY; + int mouseCounter; + bool wasPaused; + int frameskipadjust; + bool autoLoadMostRecent; + int maxScale; + int romSize; + VIDEO_SIZE lastWindowed; + VIDEO_SIZE lastFullscreen; + + CList updateList; + int updateCount; + + CAcceleratorManager winAccelMgr; + HACCEL hAccel; + + RECT rect; + RECT dest; + + struct EmulatedSystem emulator; + + CString szFile; + CString filename; + CString dir; + + CString wndClass; + + public: + VBA(); + ~VBA(); + + void adjustDestRect(); + void updateIFB(); + void updateFilter(); + void updateThrottle( unsigned short throttle ); + void updateMenuBar(); + void winAddUpdateListener(IUpdateListener *l); + void winRemoveUpdateListener(IUpdateListener *l); + CString winLoadFilter(UINT id); + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(VBA) + public: + virtual BOOL InitInstance(); + virtual BOOL OnIdle(LONG lCount); + //}}AFX_VIRTUAL + + // Implementation + + public: + void saveSettings(); + void movieReadNext(); + bool initInput(); + HMODULE winLoadLanguage(const char *name); + void winSetLanguageOption(int option, bool force); +#ifdef MMX + bool detectMMX(); +#endif + void updatePriority(); + void directXMessage(const char *msg); + void shutdownDisplay(); + bool preInitialize(); + bool updateRenderMethod0(bool force); + bool updateRenderMethod(bool force); + bool initDisplay(); + void updateWindowSize(int value); + void updateVideoSize(UINT id); + void updateFrameSkip(); + void loadSettings(); + void addRecentFile(CString file); + + private: + unsigned int detectCpuCores(); +}; + + extern VBA theApp; + extern int emulating; + +#ifdef MMX + extern "C" bool cpu_mmx; +#endif diff --git a/src/win32/VBA.rc b/src/win32/VBA.rc new file mode 100644 index 0000000..29d4a98 --- /dev/null +++ b/src/win32/VBA.rc @@ -0,0 +1,2356 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#ifndef APSTUDIO_INVOKED +#include "targetver.h" +#endif +#include "afxres.h" +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// German (Germany) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU) +LANGUAGE LANG_GERMAN, SUBLANG_GERMAN +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#ifndef APSTUDIO_INVOKED\r\n" + "#include ""targetver.h""\r\n" + "#endif\r\n" + "#include ""afxres.h""\0" +END + +3 TEXTINCLUDE +BEGIN + "#include ""res\\VBA.rc2"" // non-Microsoft Visual C++ edited resource\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // German (Germany) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_MAINICON ICON "res\\VBA.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_XAUDIO2_CONFIG DIALOGEX 0, 0, 160, 144 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "XAudio2" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,42,126,54,14 + PUSHBUTTON "Cancel",IDCANCEL,102,126,54,14 + COMBOBOX IDC_COMBO_DEV,6,18,150,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + CONTROL "Enable stereo upmixing",IDC_CHECK_UPMIX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,108,150,8 + LTEXT "Select device:",IDC_STATIC,6,6,150,8 + GROUPBOX "Number of sound buffers:",IDC_STATIC,6,42,150,54 + CONTROL "",IDC_SLIDER_BUFFER,"msctls_trackbar32",TBS_AUTOTICKS | TBS_BOTH | WS_TABSTOP,12,54,138,24 + CTEXT "bufferInfo",IDC_INFO_BUFFER,12,78,138,12,0,WS_EX_DLGMODALFRAME +END + +IDD_OAL_CONFIG DIALOGEX 0, 0, 167, 114 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "OpenAL configuration" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,66,96,48,12 + PUSHBUTTON "Cancel",IDCANCEL,114,96,48,12 + COMBOBOX IDC_DEVICE,6,18,156,36,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "Select device:",IDC_STATIC,6,6,156,8 + GROUPBOX "Sound Buffer Count",IDC_STATIC,6,36,156,54 + CONTROL "",IDC_SLIDER_BUFFERCOUNT,"msctls_trackbar32",TBS_AUTOTICKS | TBS_BOTH | WS_TABSTOP,12,48,144,24 + CTEXT "bufferInfo",IDC_BUFFERINFO,12,72,144,12,0,WS_EX_DLGMODALFRAME +END + +IDD_SELECT_PLUGIN DIALOGEX 0, 0, 172, 54 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Filter Plugin" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + DEFPUSHBUTTON "OK",IDOK,66,36,48,12 + PUSHBUTTON "Cancel",IDCANCEL,120,36,48,12 + COMBOBOX IDC_COMBO_PLUGIN,6,18,162,48,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP + LTEXT "Please select filter plugin:",IDC_STATIC,6,6,162,8 +END + +IDD_LINKTAB DIALOGEX 0, 0, 254, 203 +STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Link Options" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + CONTROL "Tab1",IDC_TAB1,"SysTabControl32",0x0,9,7,240,162 + PUSHBUTTON "OK",ID_OK,57,180,60,15 + PUSHBUTTON "Cancel",ID_CANCEL,140,180,57,15 +END + +IDD_LINKTAB1 DIALOGEX 0, 0, 184, 79 +STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + LTEXT "Link timeout (in milliseconds)",IDC_STATIC,17,12,92,16 + EDITTEXT IDC_LINKTIMEOUT,116,10,53,14,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Single Computer",IDC_LINK_SINGLE,"Button",BS_AUTORADIOBUTTON | WS_GROUP,17,27,71,16 + CONTROL "Network",IDC_LINK_LAN,"Button",BS_AUTORADIOBUTTON,17,43,70,16 +END + +IDD_LINKTAB2 DIALOGEX 0, 0, 210, 113 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + CONTROL "2",IDC_LINK2P,"Button",BS_AUTORADIOBUTTON | WS_GROUP,46,16,21,13 + CONTROL "3",IDC_LINK3P,"Button",BS_AUTORADIOBUTTON,94,16,21,13 + CONTROL "4",IDC_LINK4P,"Button",BS_AUTORADIOBUTTON,142,16,21,13 + CONTROL "TCP/IP",IDC_LINKTCP,"Button",BS_AUTORADIOBUTTON | WS_GROUP,54,47,42,14 + CONTROL "UDP",IDC_LINKUDP,"Button",BS_AUTORADIOBUTTON | WS_DISABLED,121,47,33,14 + PUSHBUTTON "Start!",IDC_SERVERSTART,79,89,50,17,WS_DISABLED + LTEXT "Select number of players:",IDC_STATIC,60,7,89,10 + LTEXT "Select protocol:",IDC_STATIC,78,33,53,11 + CONTROL "Speed hacks",IDC_SSPEED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,76,70,57,12 +END + +IDD_LINKTAB3 DIALOGEX 0, 0, 188, 108 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + CONTROL "TCP/IP",IDC_CLINKTCP,"Button",BS_AUTORADIOBUTTON | WS_GROUP,58,20,39,12 + CONTROL "UDP",IDC_CLINKUDP,"Button",BS_AUTORADIOBUTTON | WS_DISABLED,118,20,32,12 + EDITTEXT IDC_SERVERIP,84,39,79,12,ES_AUTOHSCROLL | WS_GROUP + PUSHBUTTON "Connect",IDC_LINKCONNECT,75,81,59,16,WS_DISABLED + LTEXT "Select protocol:",IDC_STATIC,78,7,53,9 + LTEXT "Server IP address or hostname:",IDC_STATIC,7,37,62,18 + LTEXT "Speed hacks:",IDC_STATIC,7,64,47,10 + CONTROL "Off (accurate)",IDC_SPEEDOFF,"Button",BS_AUTORADIOBUTTON | WS_GROUP,60,63,57,12 + CONTROL "On (fast)",IDC_SPEEDON,"Button",BS_AUTORADIOBUTTON,128,63,48,12 +END + +IDD_SERVERWAIT DIALOG 0, 0, 186, 90 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Waiting for players" +FONT 8, "MS Sans Serif" +BEGIN + PUSHBUTTON "Cancel",IDCANCEL,63,69,50,14 + CONTROL "Progress1",IDC_SERVERWAIT,"msctls_progress32",WS_BORDER,33,50,120,13 + LTEXT "",IDC_STATIC1,7,7,154,8 + LTEXT "",IDC_STATIC2,7,17,105,8 + LTEXT "",IDC_STATIC3,7,25,105,8 + LTEXT "",IDC_STATIC4,7,33,105,8 +END + +IDD_OPENDLG DIALOG 36, 24, 202, 117 +STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_CONTEXTHELP | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Open" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "File &name:",1090,2,1,81,8 + EDITTEXT 1152,0,10,104,12,ES_AUTOHSCROLL | ES_OEMCONVERT + LISTBOX 1120,1,24,104,53,LBS_SORT | LBS_OWNERDRAWFIXED | LBS_HASSTRINGS | LBS_DISABLENOSCROLL | WS_VSCROLL | WS_TABSTOP + LTEXT "&Folders:",-1,112,0,53,9 + LTEXT "",1088,113,10,86,9,SS_NOPREFIX + LISTBOX 1121,112,24,88,52,LBS_SORT | LBS_OWNERDRAWFIXED | LBS_HASSTRINGS | LBS_DISABLENOSCROLL | WS_VSCROLL | WS_TABSTOP + LTEXT "List files of &type:",1089,1,75,81,9 + COMBOBOX 1136,1,87,104,13,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP + LTEXT "Dri&ves:",1091,113,76,70,9 + COMBOBOX 1137,112,87,71,68,CBS_DROPDOWNLIST | CBS_OWNERDRAWFIXED | CBS_AUTOHSCROLL | CBS_SORT | CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP + DEFPUSHBUTTON "OK",IDOK,24,102,50,14,WS_GROUP + PUSHBUTTON "Cancel",IDCANCEL,90,102,50,14,WS_GROUP +END + +IDD_ABOUT DIALOGEX 0, 0, 179, 153 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_TOOLWINDOW +CAPTION "About" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + ICON IDI_MAINICON,IDC_STATIC,6,6,20,20 + CTEXT "VisualBoyAdvance-M Emulator",IDC_STATIC,36,6,138,8 + CTEXT "Copyright © 2008 VBA-M development team",IDC_STATIC,6,48,168,8 + CTEXT "http://vba-m.com",IDC_URL,7,138,168,8 + RTEXT "Version:",IDC_STATIC,36,18,54,8 + LTEXT "",IDC_VERSION,96,18,78,8,SS_NOPREFIX + GROUPBOX "VBA-M dev team:",IDC_STATIC,7,65,90,69 + CTEXT "mudlord\nNach\nJonas Quinn\nDJRobX\nSpacy\nSquall Leonhart\nNormmatt",IDC_STATIC,15,73,76,56 + RTEXT "Date compiled:",IDC_STATIC,36,30,54,8 + LTEXT "",IDC_DATE,96,30,78,8,SS_NOPREFIX + GROUPBOX "Thanks go to:",IDC_STATIC,100,65,72,69 + CTEXT "blargg\nCostis\nchrono\nxKiv\nbgK\nOrig. VBA team",IDC_STATIC,108,77,55,49 +END + +IDD_DIRECTORIES DIALOGEX 0, 0, 222, 270 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Directories" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + GROUPBOX "Game Boy Advance ROMs",IDC_STATIC,6,6,210,30 + EDITTEXT IDC_ROM_PATH,12,18,180,12,ES_AUTOHSCROLL + PUSHBUTTON "...",IDC_ROM_DIR,192,18,18,12 + GROUPBOX "Game Boy Color ROMs",IDC_STATIC,6,42,210,30 + EDITTEXT IDC_GBCROM_PATH,12,54,180,12,ES_AUTOHSCROLL + PUSHBUTTON "...",IDC_GBCROM_DIR,192,54,18,12 + GROUPBOX "Game Boy ROMs",IDC_STATIC,6,78,210,30 + EDITTEXT IDC_GBROM_PATH,12,90,180,12,ES_AUTOHSCROLL + PUSHBUTTON "...",IDC_GBROM_DIR,192,90,18,12 + GROUPBOX "Native Saves",IDC_STATIC,6,114,210,30 + EDITTEXT IDC_BATTERY_PATH,12,126,180,12,ES_AUTOHSCROLL + PUSHBUTTON "...",IDC_BATTERY_DIR,192,126,18,12 + GROUPBOX "Emulator Saves",IDC_STATIC,6,150,210,30 + EDITTEXT IDC_SAVE_PATH,12,162,180,12,ES_AUTOHSCROLL + PUSHBUTTON "...",IDC_SAVE_DIR,192,162,18,12 + GROUPBOX "Screenshots",IDC_STATIC,6,186,210,30 + EDITTEXT IDC_CAPTURE_PATH,12,198,180,12,ES_AUTOHSCROLL + PUSHBUTTON "...",IDC_CAPTURE_DIR,192,198,18,12 + GROUPBOX "Relative Paths",IDC_STATIC,6,222,102,42 + CONTROL "Example:\n .\\battery\n ..\\screenshots\\vba",IDC_STATIC, + "Static",SS_LEFTNOWORDWRAP | WS_GROUP,12,234,90,24 + DEFPUSHBUTTON "OK",IDOK,120,246,48,18 + PUSHBUTTON "Cancel",IDCANCEL,168,246,48,18 +END + +IDD_CONFIG DIALOGEX 0, 0, 448, 102 +STYLE DS_SYSMODAL | DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Joypad configuration" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + RTEXT "Up",IDC_STATIC,6,6,36,12 + EDITTEXT IDC_EDIT_UP,48,6,96,12,ES_AUTOHSCROLL + RTEXT "Down",IDC_STATIC,6,24,36,12 + EDITTEXT IDC_EDIT_DOWN,48,24,96,12,ES_AUTOHSCROLL + RTEXT "Left",IDC_STATIC,6,42,36,12 + EDITTEXT IDC_EDIT_LEFT,48,42,96,12,ES_AUTOHSCROLL + RTEXT "Right",IDC_STATIC,6,60,36,12 + EDITTEXT IDC_EDIT_RIGHT,48,60,96,12,ES_AUTOHSCROLL + RTEXT "A",IDC_STATIC,156,6,36,12 + EDITTEXT IDC_EDIT_BUTTON_A,198,6,96,12,ES_AUTOHSCROLL + RTEXT "B",IDC_STATIC,156,24,36,12 + EDITTEXT IDC_EDIT_BUTTON_B,198,24,96,12,ES_AUTOHSCROLL + RTEXT "L",IDC_STATIC,156,42,36,12 + EDITTEXT IDC_EDIT_BUTTON_L,198,42,96,12,ES_AUTOHSCROLL + RTEXT "R",IDC_STATIC,156,60,36,12 + EDITTEXT IDC_EDIT_BUTTON_R,198,60,96,12,ES_AUTOHSCROLL + RTEXT "Select",IDC_STATIC,6,84,36,12 + EDITTEXT IDC_EDIT_BUTTON_SELECT,48,84,96,12,ES_AUTOHSCROLL + RTEXT "Start",IDC_STATIC,156,84,36,12 + EDITTEXT IDC_EDIT_BUTTON_START,198,84,96,12,ES_AUTOHSCROLL + RTEXT "Speed Up",IDC_STATIC,306,6,36,12 + EDITTEXT IDC_EDIT_SPEED,348,6,96,12,ES_AUTOHSCROLL + RTEXT "Screenshot",IDC_STATIC,306,24,36,12 + EDITTEXT IDC_EDIT_CAPTURE,348,24,96,12,ES_AUTOHSCROLL + RTEXT "GS",IDC_STATIC,306,42,36,12 + EDITTEXT IDC_EDIT_BUTTON_GS,348,42,96,12,ES_AUTOHSCROLL + CONTROL "Multiple key assignments",IDC_APPENDMODE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,348,60,96,12 + DEFPUSHBUTTON "OK",ID_OK,348,78,48,18 + PUSHBUTTON "Cancel",ID_CANCEL,396,78,48,18 + PUSHBUTTON "Clear all",IDC_CLEAR_ALL,306,60,36,12 +END + +IDD_CHEATS DIALOG 0, 0, 276, 253 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Search for cheats" +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "",IDC_CHEAT_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | WS_BORDER | WS_TABSTOP,3,5,265,111 + CONTROL "Ol&d value",IDC_OLD_VALUE,"Button",BS_AUTORADIOBUTTON | WS_GROUP,11,129,46,10 + CONTROL "Specifi&c value",IDC_SPECIFIC_VALUE,"Button",BS_AUTORADIOBUTTON,11,141,61,10 + CONTROL "&8 bits",IDC_SIZE_8,"Button",BS_AUTORADIOBUTTON | WS_GROUP,11,167,33,10 + CONTROL "&16 bits",IDC_SIZE_16,"Button",BS_AUTORADIOBUTTON,11,179,37,10 + CONTROL "&32 bits",IDC_SIZE_32,"Button",BS_AUTORADIOBUTTON,11,191,37,10 + CONTROL "&Equal",IDC_EQ,"Button",BS_AUTORADIOBUTTON | WS_GROUP,100,128,34,10 + CONTROL "&Not equal",IDC_NE,"Button",BS_AUTORADIOBUTTON,100,140,47,10 + CONTROL "&Less than",IDC_LT,"Button",BS_AUTORADIOBUTTON,100,152,47,10 + CONTROL "Le&ss or equal",IDC_LE,"Button",BS_AUTORADIOBUTTON,100,164,58,10 + CONTROL "&Greather than",IDC_GT,"Button",BS_AUTORADIOBUTTON,100,176,59,10 + CONTROL "G&reater or equal",IDC_GE,"Button",BS_AUTORADIOBUTTON,100,188,67,10 + CONTROL "S&igned",IDC_SIGNED,"Button",BS_AUTORADIOBUTTON | WS_GROUP,202,130,38,10 + CONTROL "&Unsigned",IDC_UNSIGNED,"Button",BS_AUTORADIOBUTTON,202,142,46,10 + CONTROL "&Hexadecimal",IDC_HEXADECIMAL,"Button",BS_AUTORADIOBUTTON,202,154,57,10 + CONTROL "U&pdate values",IDC_UPDATE,"Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,192,192,62,10 + EDITTEXT IDC_VALUE,95,211,172,14,ES_AUTOHSCROLL + PUSHBUTTON "&Start",IDC_START,15,237,50,14,WS_GROUP + PUSHBUTTON "S&earch",IDC_SEARCH,80,236,50,14 + PUSHBUTTON "&Add cheat",IDC_ADD_CHEAT,145,236,50,14 + DEFPUSHBUTTON "OK",ID_OK,210,236,50,14 + GROUPBOX "&Search type",IDC_STATIC,3,118,84,36 + GROUPBOX "&Data size",IDC_STATIC,3,158,84,44 + GROUPBOX "Compare type",IDC_STATIC,95,118,92,84 + GROUPBOX "Signed/Unsigned",IDC_STATIC,192,118,76,50 + LTEXT "Enter &value:",IDC_STATIC,3,214,69,8 +END + +IDD_ADD_CHEAT DIALOG 0, 0, 186, 137 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Add cheat" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_ADDRESS,60,6,123,14,ES_AUTOHSCROLL + EDITTEXT IDC_VALUE,60,24,123,14,ES_AUTOHSCROLL + EDITTEXT IDC_DESC,60,42,123,14,ES_AUTOHSCROLL + CONTROL "8-bit",IDC_SIZE_8,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,11,70,29,10 + CONTROL "16-bit",IDC_SIZE_16,"Button",BS_AUTORADIOBUTTON,62,70,33,10 + CONTROL "32-bit",IDC_SIZE_32,"Button",BS_AUTORADIOBUTTON,117,70,33,10 + CONTROL "&Signed",IDC_SIGNED,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,11,97,38,10 + CONTROL "&Unsigned",IDC_UNSIGNED,"Button",BS_AUTORADIOBUTTON,62,98,46,10 + CONTROL "&Hexadecimal",IDC_HEXADECIMAL,"Button",BS_AUTORADIOBUTTON,117,98,57,10 + DEFPUSHBUTTON "&OK",ID_OK,36,116,50,14,WS_GROUP + PUSHBUTTON "&Cancel",ID_CANCEL,99,116,50,14 + LTEXT "&Value:",IDC_STATIC,3,27,54,8 + GROUPBOX "Number format",IDC_STATIC,3,88,180,24 + LTEXT "&Address:",IDC_STATIC,3,9,54,8 + GROUPBOX "Size",IDC_STATIC,3,60,180,24 + LTEXT "&Description:",IDC_STATIC,3,45,55,8 +END + +IDD_CHEAT_LIST DIALOG 0, 0, 280, 250 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Cheat list" +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Restore &previous values",IDC_RESTORE,"Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,5,183,92,10 + PUSHBUTTON "&Code...",IDC_ADD_CODE,9,208,64,14,WS_GROUP + PUSHBUTTON "C&heat...",IDC_ADD_CHEAT,75,208,64,14 + PUSHBUTTON "&Gameshark...",IDC_ADD_GAMESHARK,141,208,64,14 + PUSHBUTTON "CodeBreaker...",IDC_ADD_CODEBREAKER,206,208,64,14 + PUSHBUTTON "&Remove",IDC_REMOVE,9,230,64,14 + PUSHBUTTON "Remove A&ll",IDC_REMOVE_ALL,75,230,64,14 + PUSHBUTTON "&Enable/Dis.",IDC_ENABLE,141,230,64,14 + DEFPUSHBUTTON "&OK",ID_OK,206,230,64,14,WS_GROUP + CONTROL "",IDC_CHEAT_LIST,"SysListView32",LVS_REPORT | WS_BORDER | WS_GROUP | WS_TABSTOP,5,15,269,156 + LTEXT "Status legend:",IDC_STATIC,6,3,46,8 + LTEXT "E: Enabled",IDC_STATIC,188,3,36,8 + LTEXT "D: Disabled",IDC_STATIC,234,3,38,8 + GROUPBOX "Add",IDC_STATIC,5,199,268,27 +END + +IDD_ASSOCIATIONS DIALOGEX 0, 0, 116, 110 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Associations" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + CONTROL ".gb",IDC_GB,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,13,26,10 + CONTROL ".dmg",IDC_DMG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,27,30,10 + CONTROL ".sgb",IDC_SGB,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,41,29,10 + CONTROL ".cgb",IDC_CGB,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,55,30,10 + CONTROL ".gbc",IDC_GBC,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,69,30,10 + CONTROL ".gba",IDC_GBA,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,75,13,30,10 + CONTROL ".agb",IDC_AGB,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,75,27,30,10 + CONTROL ".bin",IDC_BIN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,75,41,27,10 + DEFPUSHBUTTON "OK",ID_OK,3,89,50,14 + PUSHBUTTON "Cancel",ID_CANCEL,63,89,50,14 + GROUPBOX "GBA Types",IDC_STATIC,63,3,50,51 + GROUPBOX "GB Types",IDC_STATIC,3,3,50,79 +END + +IDD_GBA_ROM_INFO DIALOGEX 0, 0, 220, 142 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "ROM Information" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",ID_OK,84,121,50,14 + LTEXT "Game title:",IDC_STATIC,7,10,60,8 + LTEXT "Game code:",IDC_STATIC,7,24,60,8 + LTEXT "Maker code:",IDC_STATIC,7,38,60,8 + LTEXT "Main unit code:",IDC_STATIC,7,66,60,8 + LTEXT "Device type:",IDC_STATIC,7,80,60,8 + LTEXT "ROM version:",IDC_STATIC,7,94,60,8 + LTEXT "CRC:",IDC_STATIC,7,108,60,8 + LTEXT "Maker name:",IDC_STATIC,7,52,60,8 + LTEXT "",IDC_ROM_TITLE,80,10,133,8,SS_NOPREFIX + LTEXT "",IDC_ROM_GAME_CODE,80,24,133,8,SS_NOPREFIX + LTEXT "",IDC_ROM_MAKER_CODE,80,38,133,8,SS_NOPREFIX + LTEXT "",IDC_ROM_UNIT_CODE,80,66,133,8,SS_NOPREFIX + LTEXT "",IDC_ROM_DEVICE_TYPE,80,80,133,8,SS_NOPREFIX + LTEXT "",IDC_ROM_VERSION,80,94,133,8,SS_NOPREFIX + LTEXT "",IDC_ROM_CRC,80,108,133,8,SS_NOPREFIX + LTEXT "",IDC_ROM_MAKER_NAME,80,52,133,8,SS_NOPREFIX +END + +IDD_GB_ROM_INFO DIALOGEX 0, 0, 220, 225 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "ROM Information" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",ID_OK,40,204,50,14 + LTEXT "Game title:",IDC_STATIC,7,10,60,8 + LTEXT "Maker code:",IDC_STATIC,7,38,60,8 + LTEXT "Unit code:",IDC_STATIC,7,68,60,8 + LTEXT "Cartridge type:",IDC_STATIC,7,82,60,8 + LTEXT "ROM version:",IDC_STATIC,7,152,60,8 + LTEXT "CRC:",IDC_STATIC,7,166,60,8 + LTEXT "",IDC_ROM_TITLE,80,10,133,8,SS_NOPREFIX + LTEXT "",IDC_ROM_MAKER_CODE,80,38,133,8,SS_NOPREFIX + LTEXT "",IDC_ROM_UNIT_CODE,80,68,133,8,SS_NOPREFIX + LTEXT "",IDC_ROM_DEVICE_TYPE,80,82,133,8,SS_NOPREFIX + LTEXT "",IDC_ROM_VERSION,80,152,133,8,SS_NOPREFIX + LTEXT "",IDC_ROM_CRC,80,166,133,8,SS_NOPREFIX + LTEXT "Color:",IDC_STATIC,7,24,60,8 + LTEXT "",IDC_ROM_COLOR,80,24,133,8,SS_NOPREFIX + LTEXT "ROM size:",IDC_STATIC,7,96,60,8 + LTEXT "",IDC_ROM_SIZE,80,96,133,8,SS_NOPREFIX + LTEXT "RAM size:",IDC_STATIC,7,110,60,8 + LTEXT "",IDC_ROM_RAM_SIZE,80,110,133,8,SS_NOPREFIX + LTEXT "Dest. code:",IDC_STATIC,7,124,60,8 + LTEXT "",IDC_ROM_DEST_CODE,80,124,133,8,SS_NOPREFIX + LTEXT "License code:",IDC_STATIC,7,138,60,8 + LTEXT "",IDC_ROM_LIC_CODE,80,138,133,8,SS_NOPREFIX + LTEXT "Checksum:",IDC_STATIC,7,180,60,8 + LTEXT "",IDC_ROM_CHECKSUM,80,180,133,8,SS_NOPREFIX + LTEXT "",IDC_ROM_MAKER_NAME2,80,52,133,8,SS_NOPREFIX + LTEXT "Maker name:",IDC_STATIC,7,52,60,8 +END + +IDD_GB_CHEAT_LIST DIALOG 0, 0, 286, 221 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Gameboy Cheat List" +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "",IDC_CHEAT_LIST,"SysListView32",LVS_REPORT | WS_BORDER | WS_GROUP | WS_TABSTOP,9,20,269,156 + PUSHBUTTON "Add &GameGenie...",IDC_ADD_GG_CHEAT,9,183,80,14,WS_GROUP + PUSHBUTTON "&Add GameShark...",IDC_ADD_GS_CHEAT,103,183,80,14,WS_GROUP + PUSHBUTTON "&Remove",IDC_REMOVE,197,183,80,14 + PUSHBUTTON "Remove A&ll",IDC_REMOVE_ALL,9,202,80,14 + PUSHBUTTON "&Enable/Dis.",IDC_ENABLE,103,202,80,14 + DEFPUSHBUTTON "&OK",ID_OK,197,202,80,14 + LTEXT "Status legend:",IDC_STATIC,10,9,46,8 + LTEXT "E: Enabled",IDC_STATIC,195,9,36,8 + LTEXT "D: Disabled",IDC_STATIC,241,9,38,8 +END + +IDD_ADD_CHEAT_DLG DIALOG 0, 0, 182, 107 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Title" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_DESC,60,7,120,14,ES_AUTOHSCROLL + EDITTEXT IDC_CODE,60,23,120,58,ES_MULTILINE | ES_UPPERCASE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN + DEFPUSHBUTTON "OK",ID_OK,33,86,50,14 + PUSHBUTTON "Cancel",ID_CANCEL,99,86,50,14 + LTEXT "&Description:",IDC_STATIC,3,10,54,8 + LTEXT "&Code:",IDC_STATIC,3,29,54,8 +END + +IDD_GB_PRINTER DIALOG 0, 0, 178, 209 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "GB Printer" +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "&1x",IDC_1X,"Button",BS_AUTORADIOBUTTON | WS_GROUP,13,166,22,10 + CONTROL "&2x",IDC_2X,"Button",BS_AUTORADIOBUTTON,55,166,23,10 + CONTROL "&3x",IDC_3X,"Button",BS_AUTORADIOBUTTON,98,166,23,10 + CONTROL "&4x",IDC_4X,"Button",BS_AUTORADIOBUTTON,141,166,23,10 + DEFPUSHBUTTON "&Print...",ID_PRINT,7,190,50,14,WS_GROUP + PUSHBUTTON "&Save...",ID_SAVE,64,190,50,14 + PUSHBUTTON "&Close",ID_OK,121,190,50,14 + CONTROL "",IDC_GB_PRINTER,"Static",SS_BLACKFRAME | WS_GROUP,7,6,162,146 + GROUPBOX "Print Size",IDC_STATIC,7,156,162,25 +END + +IDD_MOTION_CONFIG DIALOGEX 0, 0, 234, 107 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Motion Sensor" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + EDITTEXT IDC_EDIT_UP,41,2,186,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_DOWN,41,16,186,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_LEFT,41,30,186,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_RIGHT,41,44,186,12,ES_AUTOHSCROLL + PUSHBUTTON "OK",ID_OK,64,86,40,14 + PUSHBUTTON "Cancel",ID_CANCEL,118,86,40,14 + LTEXT "Up:",IDC_STATIC,5,2,35,10 + LTEXT "Down:",IDC_STATIC,5,16,35,10 + LTEXT "Left:",IDC_STATIC,5,30,35,10 + LTEXT "Right:",IDC_STATIC,5,44,35,10 + CONTROL "Assign additional keys to functions",IDC_APPENDMODE, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,46,66,135,10 +END + +IDD_LANG_SELECT DIALOG 0, 0, 186, 68 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Language selection" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_LANG_STRING,140,25,40,14,ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",ID_OK,30,49,50,14 + PUSHBUTTON "Cancel",ID_CANCEL,104,49,50,14 + LTEXT "Current system language is:",IDC_STATIC,6,9,123,8 + LTEXT "Enter language name (3 letter):",IDC_STATIC,6,30,127,8 + LTEXT "",IDC_LANG_NAME,140,9,40,8,SS_NOPREFIX +END + +IDD_CODE_SELECT DIALOG 0, 0, 316, 235 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Select codes to import" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",ID_OK,91,214,50,14 + PUSHBUTTON "Cancel",ID_CANCEL,174,214,50,14 + LISTBOX IDC_GAME_LIST,7,7,302,205,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP +END + +IDD_MAP_VIEW DIALOG 0, 0, 322, 238 +STYLE DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Map view" +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Frame 0",IDC_FRAME_0,"Button",BS_AUTORADIOBUTTON | WS_GROUP,13,22,41,10 + CONTROL "Frame 1",IDC_FRAME_1,"Button",BS_AUTORADIOBUTTON,13,36,41,10 + CONTROL "BG0",IDC_BG0,"Button",BS_AUTORADIOBUTTON | WS_GROUP,13,63,30,10 + CONTROL "BG1",IDC_BG1,"Button",BS_AUTORADIOBUTTON,13,77,30,10 + CONTROL "BG2",IDC_BG2,"Button",BS_AUTORADIOBUTTON,13,91,30,10 + CONTROL "BG3",IDC_BG3,"Button",BS_AUTORADIOBUTTON,13,105,30,10 + CONTROL "Stretch to fit",IDC_STRETCH,"Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,7,122,68,10 + CONTROL "Auto update",IDC_AUTO_UPDATE,"Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,7,134,55,10 + PUSHBUTTON "&Refresh",IDC_REFRESH,25,217,50,14,WS_GROUP + PUSHBUTTON "&Save...",IDC_SAVE,88,217,50,14,WS_GROUP + PUSHBUTTON "&Close",IDC_CLOSE,155,217,50,14 + CONTROL "MapView",IDC_MAP_VIEW,"VbaBitmapControl",WS_GROUP | WS_TABSTOP,187,15,128,128 + CONTROL "Zoom",IDC_MAP_VIEW_ZOOM,"VbaZoomControl",WS_GROUP | WS_TABSTOP,7,148,64,64 + CONTROL "Color",IDC_COLOR,"VbaColorControl",WS_TABSTOP,187,164,48,47 + LTEXT "",IDC_R,245,173,50,8,SS_NOPREFIX + LTEXT "",IDC_G,245,185,50,8,SS_NOPREFIX + LTEXT "",IDC_B,245,197,50,8,SS_NOPREFIX + GROUPBOX "Frame",IDC_STATIC,7,11,63,37 + GROUPBOX "Background",IDC_STATIC,7,52,63,67 + LTEXT "",IDC_XY,129,95,53,8,SS_NOPREFIX + LTEXT "Mode:",IDC_STATIC,80,15,34,8 + LTEXT "",IDC_MODE,130,15,53,8,SS_NOPREFIX + LTEXT "Map Base:",IDC_STATIC,80,25,35,8 + LTEXT "",IDC_MAPBASE,130,25,53,8,SS_NOPREFIX + LTEXT "Char Base:",IDC_STATIC,80,35,36,8 + LTEXT "",IDC_CHARBASE,130,35,53,8,SS_NOPREFIX + LTEXT "Size:",IDC_STATIC,80,45,37,8 + LTEXT "",IDC_DIM,130,45,53,8,SS_NOPREFIX + LTEXT "Colors:",IDC_STATIC,80,55,37,8 + LTEXT "",IDC_NUMCOLORS,130,55,53,8,SS_NOPREFIX + LTEXT "Priority:",IDC_STATIC,80,65,37,8 + LTEXT "",IDC_PRIORITY,130,65,53,8,SS_NOPREFIX + LTEXT "Mosaic:",IDC_STATIC,80,75,37,8 + LTEXT "",IDC_MOSAIC,130,75,53,8,SS_NOPREFIX + LTEXT "Overflow:",IDC_STATIC,80,85,37,8 + LTEXT "",IDC_OVERFLOW,130,85,53,8,SS_NOPREFIX + LTEXT "Address:",IDC_STATIC,80,105,37,8 + LTEXT "",IDC_ADDRESS,130,105,53,8,SS_NOPREFIX + LTEXT "Tile:",IDC_STATIC,80,115,37,8 + LTEXT "",IDC_TILE_NUM,130,115,53,8,SS_NOPREFIX + LTEXT "Flip:",IDC_STATIC,80,125,37,8 + LTEXT "",IDC_FLIP,130,125,53,8,SS_NOPREFIX + LTEXT "Palette:",IDC_STATIC,80,135,37,8 + LTEXT "",IDC_PALETTE_NUM,130,135,53,8,SS_NOPREFIX +END + +IDD_PALETTE_VIEW DIALOGEX 0, 0, 316, 266 +STYLE DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Palette View" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + PUSHBUTTON "Save BG...",IDC_SAVE_BG,102,246,50,14 + PUSHBUTTON "Save OBJ...",IDC_SAVE_OBJ,162,246,50,14 + PUSHBUTTON "&Refresh",IDC_REFRESH2,6,246,50,14 + PUSHBUTTON "&Close",IDC_CLOSE,260,246,50,14 + LTEXT "",IDC_ADDRESS,53,168,50,8,SS_NOPREFIX + LTEXT "",IDC_R,53,180,50,8,SS_NOPREFIX + LTEXT "",IDC_G,53,192,50,8,SS_NOPREFIX + LTEXT "",IDC_B,53,204,50,8,SS_NOPREFIX + LTEXT "",IDC_VALUE,53,216,50,8,SS_NOPREFIX + CONTROL "Custom1",IDC_COLOR,"VbaColorControl",WS_TABSTOP,161,168,50,50 + CONTROL "PaletteViewBG",IDC_PALETTE_VIEW,"VbaPaletteViewControl",WS_TABSTOP,12,30,128,128 + CONTROL "PaletteViewBG",IDC_PALETTE_VIEW_OBJ, + "VbaPaletteViewControl",WS_TABSTOP,166,30,128,128 + GROUPBOX "Background",IDC_STATIC,7,20,137,143 + GROUPBOX "Sprite",IDC_STATIC,161,20,137,143 + LTEXT "Address:",IDC_STATIC,7,168,38,8 + LTEXT "R:",IDC_STATIC,7,180,41,8 + LTEXT "G:",IDC_STATIC,7,192,43,8 + LTEXT "B:",IDC_STATIC,7,204,38,8 + LTEXT "Value:",IDC_STATIC,7,216,38,8 + LTEXT "Click on a color for more information",IDC_STATIC,7,7,302,8 + CONTROL "Automatic update",IDC_AUTO_UPDATE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,229,71,10 + PUSHBUTTON "Change backdrop color...",IDC_CHANGE_BACKDROP,102,228,108,14 +END + +IDD_MEM_VIEWER DIALOG 0, 0, 380, 178 +STYLE DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Memory viewer" +FONT 8, "MS Sans Serif" +BEGIN + COMBOBOX IDC_ADDRESSES,7,7,109,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + CONTROL "8-bit",IDC_8_BIT,"Button",BS_AUTORADIOBUTTON | WS_GROUP,120,9,29,10 + CONTROL "16-bit",IDC_16_BIT,"Button",BS_AUTORADIOBUTTON,154,9,33,10 + CONTROL "32-bit",IDC_32_BIT,"Button",BS_AUTORADIOBUTTON,192,9,33,10 + EDITTEXT IDC_ADDRESS,238,7,82,14,ES_UPPERCASE | ES_AUTOHSCROLL | ES_WANTRETURN | WS_GROUP + DEFPUSHBUTTON "&Go",IDC_GO,323,7,50,14,WS_GROUP + CONTROL "Viewer",IDC_VIEWER,"VbaMemoryViewer",WS_TABSTOP,7,22,366,112 + CONTROL "Automatic update",IDC_AUTO_UPDATE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,139,71,10 + PUSHBUTTON "&Refresh",IDC_REFRESH,67,157,50,14 + PUSHBUTTON "&Load...",IDC_LOAD,132,157,50,14 + PUSHBUTTON "&Save...",IDC_SAVE,197,157,50,14 + PUSHBUTTON "&Close",IDC_CLOSE,262,157,50,14 + LTEXT "Current address:",IDC_CURRENT_ADDRESS_LABEL,210,142,77,8 + EDITTEXT IDC_CURRENT_ADDRESS,291,139,82,14,ES_RIGHT | ES_AUTOHSCROLL | WS_DISABLED +END + +IDD_OAM_VIEW DIALOGEX 0, 0, 234, 185 +STYLE DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "OAM Viewer" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + EDITTEXT IDC_SPRITE,7,19,76,14,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER + SCROLLBAR IDC_SCROLLBAR,7,33,76,11 + CONTROL "Stretch to fit",IDC_STRETCH,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,138,79,10 + PUSHBUTTON "&Refresh",IDC_REFRESH,7,164,50,14,WS_GROUP + PUSHBUTTON "&Save...",IDC_SAVE,91,164,50,14,WS_GROUP + PUSHBUTTON "&Close",IDC_CLOSE,177,164,50,14 + CONTROL "MapView",IDC_OAM_VIEW,"VbaBitmapControl",WS_GROUP | WS_TABSTOP,87,7,64,64 + CONTROL "Zoom",IDC_OAM_VIEW_ZOOM,"VbaZoomControl",WS_GROUP | WS_TABSTOP,163,7,64,64 + CONTROL "Color",IDC_COLOR,"VbaColorControl",WS_TABSTOP,87,79,48,47 + LTEXT "",IDC_POS,31,47,50,8,SS_NOPREFIX + LTEXT "",IDC_MODE,31,57,50,8,SS_NOPREFIX + LTEXT "",IDC_COLORS,31,67,50,8,SS_NOPREFIX + LTEXT "",IDC_PALETTE,31,77,50,8,SS_NOPREFIX + LTEXT "",IDC_TILE,31,87,50,8,SS_NOPREFIX + LTEXT "",IDC_PRIO,31,97,50,8,SS_NOPREFIX + LTEXT "",IDC_SIZE2,31,107,50,8,SS_NOPREFIX + LTEXT "",IDC_ROT,31,117,50,8,SS_NOPREFIX + LTEXT "",IDC_FLAGS,31,127,50,8,SS_NOPREFIX + LTEXT "",IDC_R,145,88,50,8,SS_NOPREFIX + LTEXT "",IDC_G,145,100,50,8,SS_NOPREFIX + LTEXT "",IDC_B,145,112,50,8,SS_NOPREFIX + LTEXT "Pos:",IDC_STATIC,7,47,24,8 + LTEXT "Mode:",IDC_STATIC,7,57,24,8 + LTEXT "Colors:",IDC_STATIC,7,67,24,8 + LTEXT "Pal:",IDC_STATIC,7,77,24,8 + LTEXT "Tile:",IDC_STATIC,7,87,24,8 + LTEXT "Prio:",IDC_STATIC,7,97,24,8 + LTEXT "Size:",IDC_STATIC,7,107,24,8 + LTEXT "Sprite:",IDC_STATIC,7,7,50,8 + LTEXT "Rot.:",IDC_STATIC,7,117,24,8 + LTEXT "Flags:",IDC_STATIC,7,127,24,8 + CONTROL "Automatic update",IDC_AUTO_UPDATE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,150,71,10 +END + +IDD_ACCEL_EDITOR DIALOGEX 0, 0, 399, 121 +STYLE DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Accelerator editor" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + LTEXT "&Commands:",IDC_STATIC,7,7,38,8 + LISTBOX IDC_COMMANDS,7,18,153,67,LBS_SORT | LBS_NOINTEGRALHEIGHT | LBS_DISABLENOSCROLL | WS_VSCROLL | WS_TABSTOP + LTEXT "Current &Keys:",IDC_STATIC1,176,7,43,8 + LISTBOX IDC_CURRENTS,176,17,153,67,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + DEFPUSHBUTTON "OK",ID_OK,342,9,50,14 + PUSHBUTTON "Cancel",ID_CANCEL,342,25,50,14 + LTEXT "Select &New Shortcut Key:",IDC_STATIC3,175,86,82,8 + EDITTEXT IDC_EDIT_KEY,176,95,100,12,ES_AUTOHSCROLL + PUSHBUTTON "&Assign",IDC_ASSIGN,342,70,50,14 + PUSHBUTTON "&Remove",IDC_REMOVE,342,86,50,14 + PUSHBUTTON "Re&set All",IDC_RESET,342,102,50,14 + LTEXT "Static",IDC_ALREADY_AFFECTED,7,96,105,9,SS_CENTERIMAGE + LTEXT "Currently assigned to :",IDC_STATIC2,7,87,73,10 +END + +IDD_TILE_VIEWER DIALOG 0, 0, 326, 266 +STYLE DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Tile Viewer" +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "16",IDC_16_COLORS,"Button",BS_AUTORADIOBUTTON | WS_GROUP,13,20,24,10 + CONTROL "256",IDC_256_COLORS,"Button",BS_AUTORADIOBUTTON,13,30,28,10 + CONTROL "0x6000000",IDC_CHARBASE_0,"Button",BS_AUTORADIOBUTTON | WS_GROUP,13,57,51,10 + CONTROL "0x6004000",IDC_CHARBASE_1,"Button",BS_AUTORADIOBUTTON,13,67,51,10 + CONTROL "0x6008000",IDC_CHARBASE_2,"Button",BS_AUTORADIOBUTTON,13,77,51,10 + CONTROL "0x600C000",IDC_CHARBASE_3,"Button",BS_AUTORADIOBUTTON,13,87,52,10 + CONTROL "0x6010000",IDC_CHARBASE_4,"Button",BS_AUTORADIOBUTTON,13,97,49,10 + CONTROL "Slider1",IDC_PALETTE_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_GROUP | WS_TABSTOP,1,124,76,22 + CONTROL "Stretch to fit",IDC_STRETCH,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,149,79,10 + PUSHBUTTON "Refresh",IDC_REFRESH,7,245,50,14,WS_GROUP + PUSHBUTTON "Save...",IDC_SAVE,138,245,50,14 + PUSHBUTTON "Close",IDC_CLOSE,269,245,50,14 + CONTROL "Custom1",IDC_TILE_VIEW,"VbaBitmapControl",WS_GROUP | WS_TABSTOP,191,7,128,128 + GROUPBOX "Colors",IDC_STATIC,7,7,66,38 + GROUPBOX "Char Base",IDC_STATIC,7,46,65,64 + CONTROL "Zoom",IDC_MAP_VIEW_ZOOM,"VbaZoomControl",WS_GROUP | WS_TABSTOP,7,174,64,64 + CONTROL "Color",IDC_COLOR,"VbaColorControl",WS_TABSTOP,98,183,48,47 + LTEXT "",IDC_R,156,192,50,8,SS_NOPREFIX + LTEXT "",IDC_G,156,204,50,8,SS_NOPREFIX + LTEXT "",IDC_B,156,216,50,8,SS_NOPREFIX + LTEXT "Palette:",IDC_STATIC,7,113,65,8 + CONTROL "Automatic update",IDC_AUTO_UPDATE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,160,71,10 + LTEXT "Tile:",IDC_STATIC,79,14,41,8 + LTEXT "Address:",IDC_STATIC,79,26,41,8 + LTEXT "",IDC_TILE_NUMBER,135,14,50,8,SS_NOPREFIX + LTEXT "",IDC_ADDRESS,135,26,50,8,SS_NOPREFIX +END + +IDD_GB_COLORS DIALOG 0, 0, 169, 121 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Gameboy Mono Colors" +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Default",IDC_DEFAULT,"Button",BS_AUTORADIOBUTTON | WS_GROUP,7,7,39,10 + CONTROL "User 1",IDC_USER1,"Button",BS_AUTORADIOBUTTON,67,7,37,10 + CONTROL "User 2",IDC_USER2,"Button",BS_AUTORADIOBUTTON,125,7,37,10 + COMBOBOX IDC_PREDEFINED,7,21,155,30,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "",IDC_COLOR_BG0,15,47,28,14,WS_GROUP + PUSHBUTTON "",IDC_COLOR_BG1,52,47,28,14 + PUSHBUTTON "",IDC_COLOR_BG2,89,47,28,14 + PUSHBUTTON "",IDC_COLOR_BG3,126,47,28,14 + PUSHBUTTON "",IDC_COLOR_OB0,15,78,28,14 + PUSHBUTTON "",IDC_COLOR_OB1,52,78,28,14 + PUSHBUTTON "",IDC_COLOR_OB2,89,78,28,14 + PUSHBUTTON "",IDC_COLOR_OB3,126,78,28,14 + PUSHBUTTON "Reset",IDC_RESET,7,100,50,14 + DEFPUSHBUTTON "OK",ID_OK,59,100,50,14 + PUSHBUTTON "Cancel",ID_CANCEL,111,100,50,14 + GROUPBOX "Background",IDC_STATIC,8,37,154,29 + GROUPBOX "Sprite",IDC_STATIC,8,67,154,30 +END + +IDD_DISASSEMBLE DIALOG 0, 0, 402, 225 +STYLE DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Disassemble" +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Automatic",IDC_AUTOMATIC,"Button",BS_AUTORADIOBUTTON | WS_GROUP,7,9,47,10 + CONTROL "ARM",IDC_ARM,"Button",BS_AUTORADIOBUTTON,62,9,32,10 + CONTROL "THUMB",IDC_THUMB,"Button",BS_AUTORADIOBUTTON,103,9,42,10 + EDITTEXT IDC_ADDRESS,158,7,65,14,ES_UPPERCASE | ES_AUTOHSCROLL | WS_GROUP + DEFPUSHBUTTON "Go",IDC_GO,232,7,50,14 + LISTBOX IDC_DISASSEMBLE,7,25,276,161,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_TABSTOP + CONTROL "Automatic update",IDC_AUTO_UPDATE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,191,71,10 + PUSHBUTTON "Refresh",IDC_REFRESH,120,204,50,14 + PUSHBUTTON "Next",IDC_NEXT,233,204,50,14 + PUSHBUTTON "Close",IDC_CLOSE,346,204,50,14 + LTEXT "R0:",IDC_STATIC,309,7,18,8 + LTEXT "R1:",IDC_STATIC,309,15,18,8 + LTEXT "R2:",IDC_STATIC,309,23,18,8 + LTEXT "R3:",IDC_STATIC,309,31,18,8 + LTEXT "R4:",IDC_STATIC,309,39,18,8 + LTEXT "R5:",IDC_STATIC,309,47,18,8 + LTEXT "R6:",IDC_STATIC,309,55,18,8 + LTEXT "R7:",IDC_STATIC,309,63,18,8 + LTEXT "",IDC_R0,344,7,52,8,SS_NOPREFIX + LTEXT "",IDC_R1,344,15,52,8,SS_NOPREFIX + LTEXT "",IDC_R2,344,23,52,8,SS_NOPREFIX + LTEXT "",IDC_R3,344,31,52,8,SS_NOPREFIX + LTEXT "",IDC_R4,344,39,52,8,SS_NOPREFIX + LTEXT "",IDC_R5,344,47,52,8,SS_NOPREFIX + LTEXT "",IDC_R6,344,55,52,8,SS_NOPREFIX + LTEXT "",IDC_R7,344,63,52,8,SS_NOPREFIX + LTEXT "",IDC_R8,344,71,52,8,SS_NOPREFIX + LTEXT "",IDC_R9,344,79,52,8,SS_NOPREFIX + LTEXT "",IDC_R10,344,87,52,8,SS_NOPREFIX + LTEXT "",IDC_R11,344,95,52,8,SS_NOPREFIX + LTEXT "",IDC_R12,344,103,52,8,SS_NOPREFIX + LTEXT "",IDC_R13,344,111,52,8,SS_NOPREFIX + LTEXT "",IDC_R14,344,119,52,8,SS_NOPREFIX + LTEXT "",IDC_R15,344,127,52,8,SS_NOPREFIX + LTEXT "R8:",IDC_STATIC,309,71,18,8 + LTEXT "R9:",IDC_STATIC,309,79,18,8 + LTEXT "R10:",IDC_STATIC,309,87,18,8 + LTEXT "R11:",IDC_STATIC,309,95,18,8 + LTEXT "R12:",IDC_STATIC,309,103,18,8 + LTEXT "R13:",IDC_STATIC,309,111,18,8 + LTEXT "R14:",IDC_STATIC,309,119,18,8 + LTEXT "R15:",IDC_STATIC,309,127,18,8 + LTEXT "",IDC_R16,344,135,52,8,SS_NOPREFIX + LTEXT "R16:",IDC_STATIC,309,135,20,8 + CONTROL "N",IDC_N,"Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,309,146,21,10 + CONTROL "Z",IDC_Z,"Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,309,156,21,10 + CONTROL "C",IDC_C,"Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,309,166,21,10 + CONTROL "V",IDC_V,"Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,309,176,21,10 + CONTROL "F",IDC_F,"Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,342,156,20,10 + CONTROL "I",IDC_I,"Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,342,146,18,10 + CONTROL "T",IDC_T,"Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,342,166,21,10 + LTEXT "Mode:",IDC_STATIC,341,176,21,8 + LTEXT "",IDC_MODE,376,176,20,8,SS_NOPREFIX + SCROLLBAR IDC_VSCROLL,283,25,10,161,SBS_VERT + PUSHBUTTON "Goto R15",IDC_GOPC,7,204,50,14 +END + +IDD_GDB_PORT DIALOG 0, 0, 186, 51 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "GDB connection" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",ID_OK,34,30,50,14 + PUSHBUTTON "Cancel",ID_CANCEL,102,30,50,14 + LTEXT "Port to wait for connection:",IDC_STATIC,7,10,105,8 + EDITTEXT IDC_PORT,125,7,54,14,ES_RIGHT | ES_AUTOHSCROLL +END + +IDD_GDB_WAITING DIALOG 0, 0, 186, 44 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Waiting..." +FONT 8, "MS Sans Serif" +BEGIN + PUSHBUTTON "Cancel",ID_CANCEL,67,23,50,14 + LTEXT "Waiting for connection on port:",IDC_STATIC,7,7,117,8 + LTEXT "",IDC_PORT,143,7,36,8,SS_NOPREFIX +END + +IDD_LOGGING DIALOGEX 0, 0, 382, 220 +STYLE DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +EXSTYLE WS_EX_TOOLWINDOW +CAPTION "Logging" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + CONTROL "SWI",IDC_VERBOSE_SWI,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,18,90,12 + CONTROL "Unaligned memory",IDC_VERBOSE_UNALIGNED_ACCESS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,36,90,12 + CONTROL "Illegal write",IDC_VERBOSE_ILLEGAL_WRITE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,54,90,12 + CONTROL "Illegal read",IDC_VERBOSE_ILLEGAL_READ,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,72,90,12 + CONTROL "DMA 0",IDC_VERBOSE_DMA0,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,90,90,12 + CONTROL "DMA 1",IDC_VERBOSE_DMA1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,108,90,12 + CONTROL "DMA 2",IDC_VERBOSE_DMA2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,126,90,12 + CONTROL "DMA 3",IDC_VERBOSE_DMA3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,144,90,12 + CONTROL "Undefined instruction",IDC_VERBOSE_UNDEFINED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,162,90,12 + CONTROL "AGBPrint",IDC_VERBOSE_AGBPRINT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,180,90,12 + EDITTEXT IDC_LOG,114,6,258,192,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL | WS_HSCROLL + PUSHBUTTON "Save...",IDC_SAVE,114,204,48,12 + PUSHBUTTON "Clear",IDC_CLEAR,168,204,48,12 + DEFPUSHBUTTON "OK",ID_OK,324,204,48,12 + GROUPBOX "Verbose",IDC_STATIC,6,6,102,210 + CONTROL "Sound output",IDC_VERBOSE_SOUNDOUTPUT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,198,90,12 +END + +IDD_EXPORT_SPS DIALOGEX 0, 0, 248, 148 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Export GameShark Snapshot" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + EDITTEXT IDC_TITLE,84,7,157,14,ES_AUTOHSCROLL + EDITTEXT IDC_DESC,84,27,157,14,ES_AUTOHSCROLL + EDITTEXT IDC_NOTES,84,47,157,73,ES_MULTILINE | ES_AUTOHSCROLL | ES_WANTRETURN + DEFPUSHBUTTON "OK",ID_OK,67,127,50,14 + PUSHBUTTON "Cancel",ID_CANCEL,130,127,50,14 + LTEXT "Title:",IDC_STATIC,7,8,62,8 + LTEXT "Description:",IDC_STATIC,7,28,63,8 + LTEXT "Notes:",IDC_STATIC,7,48,60,8 +END + +IDD_ADDR_SIZE DIALOG 0, 0, 186, 67 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Enter address and size" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_ADDRESS,99,6,80,14,ES_AUTOHSCROLL + EDITTEXT IDC_SIZE_CONTROL,99,26,80,14,ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",ID_OK,34,46,50,14 + PUSHBUTTON "Cancel",ID_CANCEL,102,46,50,14 + LTEXT "Address:",IDC_STATIC,7,11,65,8 + LTEXT "Size:",IDC_STATIC,7,29,65,8 +END + +IDD_THROTTLE DIALOGEX 0, 0, 126, 60 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Throttle" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + EDITTEXT IDC_THROTTLE,6,18,114,12,ES_CENTER | ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",ID_OK,6,36,54,18 + PUSHBUTTON "Cancel",ID_CANCEL,66,36,54,18 + CTEXT "Enter desired throttle (5%...1000%):",IDC_STATIC,6,6,114,8 +END + +IDD_GB_DISASSEMBLE DIALOG 0, 0, 344, 225 +STYLE DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "GB Disassemble" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_ADDRESS,7,7,65,14,ES_UPPERCASE | ES_AUTOHSCROLL | WS_GROUP + DEFPUSHBUTTON "Go",IDC_GO,81,7,50,14 + LISTBOX IDC_DISASSEMBLE,7,25,222,161,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_TABSTOP + CONTROL "Automatic update",IDC_AUTO_UPDATE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,191,71,10 + PUSHBUTTON "Refresh",IDC_REFRESH,100,204,50,14 + PUSHBUTTON "Next",IDC_NEXT,193,204,50,14 + PUSHBUTTON "Close",IDC_CLOSE,287,204,50,14 + LTEXT "AF:",IDC_STATIC,250,25,18,8 + LTEXT "BC:",IDC_STATIC,250,35,18,8 + LTEXT "DE:",IDC_STATIC,250,45,18,8 + LTEXT "HL:",IDC_STATIC,250,55,18,8 + LTEXT "IFF:",IDC_STATIC,250,85,18,8 + LTEXT "LY:",IDC_STATIC,272,95,18,8 + LTEXT "",IDC_R0,285,25,52,8,SS_NOPREFIX + LTEXT "",IDC_R1,285,35,52,8,SS_NOPREFIX + LTEXT "",IDC_R2,285,45,52,8,SS_NOPREFIX + LTEXT "",IDC_R3,285,55,52,8,SS_NOPREFIX + LTEXT "",IDC_R6,285,85,52,8,SS_NOPREFIX + LTEXT "",IDC_LY,285,95,52,8,SS_NOPREFIX + CONTROL "N",IDC_N,"Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,250,109,21,10 + CONTROL "Z",IDC_Z,"Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,250,97,21,10 + CONTROL "C",IDC_C,"Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,250,133,21,10 + CONTROL "H",IDC_H,"Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,250,121,21,10 + SCROLLBAR IDC_VSCROLL,229,25,10,161,SBS_VERT + PUSHBUTTON "Goto PC",IDC_GOPC,7,204,50,14 + LTEXT "SP:",IDC_STATIC,250,65,18,8 + LTEXT "",IDC_R4,285,65,52,8,SS_NOPREFIX + LTEXT "PC:",IDC_STATIC,250,75,18,8 + LTEXT "",IDC_R5,285,75,52,8,SS_NOPREFIX +END + +IDD_GB_OAM_VIEW DIALOG 0, 0, 234, 185 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "GB Oam Viewer" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_SPRITE,7,19,76,14,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER + SCROLLBAR IDC_SCROLLBAR,7,33,76,11 + CONTROL "Stretch to fit",IDC_STRETCH,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,138,79,10 + PUSHBUTTON "&Refresh",IDC_REFRESH,7,164,50,14,WS_GROUP + PUSHBUTTON "&Save...",IDC_SAVE,91,164,50,14,WS_GROUP + PUSHBUTTON "&Close",IDC_CLOSE,177,164,50,14 + CONTROL "MapView",IDC_OAM_VIEW,"VbaBitmapControl",WS_GROUP | WS_TABSTOP,87,7,64,64 + CONTROL "Zoom",IDC_OAM_VIEW_ZOOM,"VbaZoomControl",WS_GROUP | WS_TABSTOP,163,7,64,64 + CONTROL "Color",IDC_COLOR,"VbaColorControl",WS_TABSTOP,87,79,48,47 + LTEXT "",IDC_POS,31,47,50,8,SS_NOPREFIX + LTEXT "",IDC_PALETTE,31,87,50,8,SS_NOPREFIX + LTEXT "",IDC_TILE,31,57,50,8,SS_NOPREFIX + LTEXT "",IDC_PRIO,31,67,50,8,SS_NOPREFIX + LTEXT "",IDC_OAP,31,77,50,8,SS_NOPREFIX + LTEXT "",IDC_FLAGS,31,97,50,8,SS_NOPREFIX + LTEXT "",IDC_R,145,88,50,8,SS_NOPREFIX + LTEXT "",IDC_G,145,100,50,8,SS_NOPREFIX + LTEXT "",IDC_B,145,112,50,8,SS_NOPREFIX + LTEXT "Pos:",IDC_STATIC,7,47,24,8 + LTEXT "Pal:",IDC_STATIC,7,87,24,8 + LTEXT "Tile:",IDC_STATIC,7,57,24,8 + LTEXT "Prio:",IDC_STATIC,7,67,24,8 + LTEXT "OAP:",IDC_STATIC,7,77,24,8 + LTEXT "Sprite:",IDC_STATIC,7,7,50,8 + LTEXT "Flags:",IDC_STATIC,7,97,24,8 + CONTROL "Automatic update",IDC_AUTO_UPDATE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,150,71,10 + LTEXT "",IDC_BANK,31,107,50,8,SS_NOPREFIX + LTEXT "Bank:",IDC_STATIC,7,107,24,8 +END + +IDD_GB_TILE_VIEWER DIALOG 0, 0, 326, 238 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "GB Tile Viewer" +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "0",IDC_BANK_0,"Button",BS_AUTORADIOBUTTON | WS_GROUP,13,20,20,10 + CONTROL "1",IDC_BANK_1,"Button",BS_AUTORADIOBUTTON,13,30,20,10 + CONTROL "0x8000",IDC_CHARBASE_0,"Button",BS_AUTORADIOBUTTON | WS_GROUP,13,57,39,10 + CONTROL "0x8800",IDC_CHARBASE_1,"Button",BS_AUTORADIOBUTTON,13,67,39,10 + CONTROL "Stretch to fit",IDC_STRETCH,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,122,79,10 + PUSHBUTTON "Refresh",IDC_REFRESH,7,217,50,14,WS_GROUP + PUSHBUTTON "Save...",IDC_SAVE,138,217,50,14 + PUSHBUTTON "Close",IDC_CLOSE,269,217,50,14 + CONTROL "Custom1",IDC_TILE_VIEW,"VbaBitmapControl",WS_GROUP | WS_TABSTOP,191,7,128,128 + GROUPBOX "VRAM Bank",IDC_STATIC,7,7,66,38 + GROUPBOX "Char Base",IDC_STATIC,7,46,65,35 + CONTROL "Zoom",IDC_MAP_VIEW_ZOOM,"VbaZoomControl",WS_GROUP | WS_TABSTOP,7,147,64,64 + CONTROL "Color",IDC_COLOR,"VbaColorControl",WS_TABSTOP,98,156,48,47 + LTEXT "",IDC_R,156,164,50,8,SS_NOPREFIX + LTEXT "",IDC_G,156,176,50,8,SS_NOPREFIX + LTEXT "",IDC_B,156,188,50,8,SS_NOPREFIX + LTEXT "Palette:",IDC_STATIC,7,86,65,8 + CONTROL "Automatic update",IDC_AUTO_UPDATE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,133,71,10 + LTEXT "Tile:",IDC_STATIC,79,14,41,8 + LTEXT "Address:",IDC_STATIC,79,26,41,8 + LTEXT "",IDC_TILE_NUMBER,135,14,50,8,SS_NOPREFIX + LTEXT "",IDC_ADDRESS,135,26,50,8,SS_NOPREFIX + CONTROL "Slider1",IDC_PALETTE_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_GROUP | WS_TABSTOP,1,98,76,22 +END + +IDD_GB_MAP_VIEW DIALOG 0, 0, 322, 238 +STYLE DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "GB Map Viewer" +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "0x8000",IDC_BANK_0,"Button",BS_AUTORADIOBUTTON | WS_GROUP,13,22,39,10 + CONTROL "0x8800",IDC_BANK_1,"Button",BS_AUTORADIOBUTTON,13,36,39,10 + CONTROL "0x9800",IDC_BG0,"Button",BS_AUTORADIOBUTTON | WS_GROUP,13,63,39,10 + CONTROL "0x9C00",IDC_BG1,"Button",BS_AUTORADIOBUTTON,13,77,40,10 + CONTROL "Stretch to fit",IDC_STRETCH,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,122,68,10 + PUSHBUTTON "&Refresh",IDC_REFRESH,25,217,50,14,WS_GROUP + PUSHBUTTON "&Save...",IDC_SAVE,88,217,50,14,WS_GROUP + PUSHBUTTON "&Close",IDC_CLOSE,155,217,50,14 + CONTROL "MapView",IDC_MAP_VIEW,"VbaBitmapControl",WS_GROUP | WS_TABSTOP,187,15,128,128 + CONTROL "Zoom",IDC_MAP_VIEW_ZOOM,"VbaZoomControl",WS_GROUP | WS_TABSTOP,7,148,64,64 + CONTROL "Color",IDC_COLOR,"VbaColorControl",WS_TABSTOP,187,164,48,47 + LTEXT "",IDC_R,245,173,50,8,SS_NOPREFIX + LTEXT "",IDC_G,245,185,50,8,SS_NOPREFIX + LTEXT "",IDC_B,245,197,50,8,SS_NOPREFIX + GROUPBOX "Char Base",IDC_STATIC,7,11,63,37 + GROUPBOX "Map Base",IDC_STATIC,7,52,63,41 + CONTROL "Auto update",IDC_AUTO_UPDATE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,134,55,10 + LTEXT "",IDC_XY,129,18,53,8,SS_NOPREFIX + LTEXT "Priority:",IDC_STATIC,80,68,37,8 + LTEXT "",IDC_PRIORITY,130,68,53,8,SS_NOPREFIX + LTEXT "Address:",IDC_STATIC,80,28,37,8 + LTEXT "",IDC_ADDRESS,130,28,53,8,SS_NOPREFIX + LTEXT "Tile:",IDC_STATIC,80,38,37,8 + LTEXT "",IDC_TILE_NUM,130,38,53,8,SS_NOPREFIX + LTEXT "Flip:",IDC_STATIC,80,48,37,8 + LTEXT "",IDC_FLIP,130,48,53,8,SS_NOPREFIX + LTEXT "Palette:",IDC_STATIC,80,58,37,8 + LTEXT "",IDC_PALETTE_NUM,130,58,53,8,SS_NOPREFIX +END + +IDD_GB_PALETTE_VIEW DIALOG 0, 0, 196, 234 +STYLE DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "GB Palette Viewer" +FONT 8, "MS Sans Serif" +BEGIN + PUSHBUTTON "Save BG...",IDC_SAVE_BG,7,191,50,14 + PUSHBUTTON "Save OBJ...",IDC_SAVE_OBJ,73,191,50,14 + PUSHBUTTON "&Refresh",IDC_REFRESH2,139,191,50,14 + PUSHBUTTON "&Close",IDC_CLOSE,73,213,50,14 + LTEXT "",IDC_ADDRESS,53,117,50,8,SS_NOPREFIX + LTEXT "",IDC_R,53,129,50,8,SS_NOPREFIX + LTEXT "",IDC_G,53,141,50,8,SS_NOPREFIX + LTEXT "",IDC_B,53,153,50,8,SS_NOPREFIX + LTEXT "",IDC_VALUE,53,165,50,8,SS_NOPREFIX + CONTROL "Custom1",IDC_COLOR,"VbaColorControl",WS_TABSTOP,119,117,50,50 + CONTROL "PaletteViewBG",IDC_PALETTE_VIEW,"VbaPaletteViewControl",WS_TABSTOP,11,30,64,64 + CONTROL "PaletteViewBG",IDC_PALETTE_VIEW_OBJ, + "VbaPaletteViewControl",WS_TABSTOP,120,30,64,64 + GROUPBOX "BG",IDC_STATIC,6,20,74,81 + GROUPBOX "Sprite",IDC_STATIC,115,20,74,81 + LTEXT "Index:",IDC_STATIC,7,117,38,8 + LTEXT "R:",IDC_STATIC,7,129,41,8 + LTEXT "G:",IDC_STATIC,7,141,43,8 + LTEXT "B:",IDC_STATIC,7,153,38,8 + LTEXT "Value:",IDC_STATIC,7,165,38,8 + LTEXT "Click on a color for more information",IDC_STATIC,7,7,182,8 + CONTROL "Automatic update",IDC_AUTO_UPDATE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,178,71,10 +END + +IDD_REWIND_INTERVAL DIALOG 0, 0, 186, 68 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Select rewind interval" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_INTERVAL,7,28,172,14,ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",ID_OK,37,47,50,14 + PUSHBUTTON "Cancel",ID_CANCEL,99,47,50,14 + LTEXT "Enter rewind interval (0...600) seconds:",IDC_STATIC,7,7,172,8 + LTEXT "Enter 0 to disable rewind.",IDC_STATIC,7,17,172,8 +END + +IDD_IO_VIEWER DIALOG 0, 0, 269, 238 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "I/O Viewer" +FONT 8, "MS Sans Serif" +BEGIN + COMBOBOX IDC_ADDRESSES,7,7,255,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + RTEXT "",IDC_VALUE,103,23,159,8 + CONTROL "",IDC_BIT_15,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,34,255,10 + CONTROL "",IDC_BIT_14,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,44,255,10 + CONTROL "",IDC_BIT_13,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,54,255,8 + CONTROL "",IDC_BIT_12,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,64,255,10 + CONTROL "",IDC_BIT_11,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,74,255,10 + CONTROL "",IDC_BIT_10,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,84,255,10 + CONTROL "",IDC_BIT_9,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,94,255,10 + CONTROL "",IDC_BIT_8,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,104,255,10 + CONTROL "",IDC_BIT_7,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,114,255,10 + CONTROL "",IDC_BIT_6,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,124,255,10 + CONTROL "",IDC_BIT_5,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,134,255,10 + CONTROL "",IDC_BIT_4,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,144,255,10 + CONTROL "",IDC_BIT_3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,154,255,10 + CONTROL "",IDC_BIT_2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,164,255,10 + CONTROL "",IDC_BIT_1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,174,255,10 + CONTROL "",IDC_BIT_0,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,184,255,10 + CONTROL "Automatic update",IDC_AUTO_UPDATE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,201,71,10 + DEFPUSHBUTTON "&Refresh",IDC_REFRESH,54,221,50,14 + DEFPUSHBUTTON "&Apply",IDC_APPLY,110,221,50,14 + PUSHBUTTON "&Close",IDC_CLOSE,166,221,50,14 + LTEXT "Value:",IDC_STATIC,7,23,72,8 +END + +IDD_MAX_SCALE DIALOG 0, 0, 186, 68 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Fullscreen scale" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_VALUE,7,28,172,14,ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",ID_OK,37,47,50,14 + PUSHBUTTON "Cancel",ID_CANCEL,99,47,50,14 + LTEXT "Enter the maxium fullscreen scale:",IDC_STATIC,7,7,172,8 + LTEXT "Enter 0 to use maximum scale.",IDC_STATIC,7,17,172,8 +END + +IDD_GAME_OVERRIDES DIALOGEX 0, 0, 268, 132 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Game overrides" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + COMBOBOX IDC_RTC,84,42,180,12,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_SAVE_TYPE,84,60,180,12,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_FLASH_SIZE,84,78,180,12,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_MIRRORING,84,96,180,12,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + DEFPUSHBUTTON "OK",IDOK,6,114,72,12 + PUSHBUTTON "Defaults",IDC_DEFAULTS,108,114,54,12 + PUSHBUTTON "Cancel",IDCANCEL,192,114,72,12 + LTEXT "Game Code",IDC_STATIC,6,6,72,12 + EDITTEXT IDC_NAME,84,6,180,12,ES_AUTOHSCROLL | WS_DISABLED + LTEXT "Real Time Clock:",IDC_STATIC,6,42,72,12 + LTEXT "Save Type:",IDC_STATIC,6,60,72,12 + LTEXT "Flash Size:",IDC_STATIC,6,78,72,12 + LTEXT "Mirroring:",IDC_STATIC,6,96,72,12 + LTEXT "Comment",IDC_STATIC,6,24,72,12 + EDITTEXT IDC_COMMENT,84,24,180,12,ES_AUTOHSCROLL +END + +IDD_BIOS DIALOGEX 0, 0, 220, 159 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "BIOS Files" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,84,132,60,18 + PUSHBUTTON "Cancel",IDCANCEL,150,132,60,18 + GROUPBOX "Game Boy mono",IDC_STATIC,6,6,210,30 + GROUPBOX "Game Boy Advance",IDC_STATIC,6,78,210,30 + EDITTEXT IDC_GB_BIOS_PATH,12,18,180,12,ES_AUTOHSCROLL + EDITTEXT IDC_GBC_BIOS_PATH,12,53,180,12,ES_AUTOHSCROLL + EDITTEXT IDC_GBA_BIOS_PATH,12,90,180,12,ES_AUTOHSCROLL + GROUPBOX "Options",IDC_STATIC,6,121,72,30 + CONTROL "Skip boot logo",IDC_SKIP_BOOT_LOGO,"Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,12,132,60,12 + CONTROL "Enable",IDC_ENABLE_GB_BIOS,"Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,174,6,36,8 + CONTROL "Enable",IDC_ENABLE_GBC_BIOS,"Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,174,42,36,8 + CONTROL "Enable",IDC_ENABLE_GBA_BIOS,"Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,174,78,36,8 + PUSHBUTTON "...",IDC_SELECT_GB_BIOS_PATH,192,18,18,12 + PUSHBUTTON "...",IDC_SELECT_GBC_BIOS_PATH,192,53,18,12 + PUSHBUTTON "...",IDC_SELECT_GBA_BIOS_PATH,192,90,18,12 + GROUPBOX "Game Boy Color",IDC_STATIC,6,42,210,30 +END + +IDD_FULLSCREEN DIALOGEX 0, 0, 167, 96 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Fullscreen Settings" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,60,78,48,12 + PUSHBUTTON "Cancel",IDCANCEL,114,78,48,12 + COMBOBOX IDC_COMBO_RESOLUTION,60,42,102,12,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + RTEXT "Resolution:",IDC_STATIC,6,42,48,12 + RTEXT "Color depth:",IDC_STATIC,6,24,48,12 + COMBOBOX IDC_COMBO_COLOR_DEPTH,60,24,102,12,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + RTEXT "Refresh rate:",IDC_STATIC,6,60,48,12 + COMBOBOX IDC_COMBO_REFRESH_RATE,60,60,102,12,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_COMBO_DEVICE,60,6,102,12,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + RTEXT "Device:",IDC_STATIC,6,6,48,12 +END + +IDD_AUDIO_CORE_SETTINGS DIALOGEX 0, 0, 330, 170 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Audio Core Settings" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,222,150,48,14 + PUSHBUTTON "Cancel",IDCANCEL,276,150,48,14 + GROUPBOX "Game Boy only",IDC_STATIC,168,6,156,138 + CONTROL "Enhance sound:",IDC_ENHANCE_SOUND,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,174,30,67,10 + CONTROL "Surround",IDC_SURROUND,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,186,42,48,10 + GROUPBOX "Echo",IDC_STATIC,186,54,132,36 + CONTROL "",IDC_ECHO,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | TBS_TOOLTIPS | WS_TABSTOP,192,66,120,12 + LTEXT "None",IDC_STATIC,192,78,36,8,SS_CENTERIMAGE + RTEXT "Lots",IDC_STATIC,276,78,36,8,SS_CENTERIMAGE + GROUPBOX "Stereo",IDC_STATIC,186,96,132,36 + CONTROL "",IDC_STEREO,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | TBS_TOOLTIPS | WS_TABSTOP,192,109,120,12 + LTEXT "Center",IDC_STATIC,192,120,36,8,SS_CENTERIMAGE + RTEXT "Left/Right",IDC_STATIC,276,120,36,8,SS_CENTERIMAGE + GROUPBOX "Shared",IDC_STATIC,6,6,156,66 + CONTROL "",IDC_VOLUME,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | TBS_TOOLTIPS | WS_TABSTOP,42,18,114,12 + LTEXT "Mute",IDC_STATIC,42,30,36,12,SS_CENTERIMAGE + RTEXT "Maximum",IDC_STATIC,120,30,36,12,SS_CENTERIMAGE + PUSHBUTTON "Default",IDC_DEFAULT_VOLUME,78,30,42,12,BS_NOTIFY + CONTROL "Declicking",IDC_DECLICKING,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,174,18,47,10 + GROUPBOX "Game Boy Advance only",IDC_STATIC,6,78,156,66 + CONTROL "Sound interpolation",IDC_SOUND_INTERPOLATION,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,90,78,10 + GROUPBOX "Sound filtering",IDC_STATIC,12,102,144,36 + CONTROL "",IDC_SOUND_FILTERING,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | TBS_TOOLTIPS | WS_TABSTOP,18,115,132,12 + LTEXT "None",IDC_STATIC,18,126,36,8,SS_CENTERIMAGE + RTEXT "Maximum",IDC_STATIC,114,126,36,8,SS_CENTERIMAGE + LTEXT "Volume:",IDC_STATIC,12,18,30,12,SS_CENTERIMAGE + LTEXT "Sample rate:",IDC_STATIC,12,54,48,12,SS_CENTERIMAGE + COMBOBOX IDC_SAMPLE_RATE,66,54,66,12,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP +END + +IDD_JOYBUS_DIALOG DIALOGEX 0, 0, 209, 57 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION +EXSTYLE WS_EX_TOOLWINDOW +CAPTION "Joybus Options" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,152,36,50,14 + CONTROL "Enable Joybus Connection",IDC_JOYBUS_ENABLE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,7,195,10 + EDITTEXT IDC_JOYBUS_HOSTNAME,7,20,195,14,ES_AUTOHSCROLL +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_XAUDIO2_CONFIG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 153 + TOPMARGIN, 7 + BOTTOMMARGIN, 137 + END + + IDD_OAL_CONFIG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 160 + TOPMARGIN, 7 + BOTTOMMARGIN, 107 + END + + IDD_LINKTAB, DIALOG + BEGIN + END + + IDD_LINKTAB1, DIALOG + BEGIN + END + + IDD_LINKTAB2, DIALOG + BEGIN + END + + IDD_LINKTAB3, DIALOG + BEGIN + END + + IDD_OPENDLG, DIALOG + BEGIN + RIGHTMARGIN, 165 + END + + IDD_ABOUT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 172 + TOPMARGIN, 7 + BOTTOMMARGIN, 146 + END + + IDD_DIRECTORIES, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 215 + TOPMARGIN, 7 + BOTTOMMARGIN, 263 + END + + IDD_CONFIG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 441 + TOPMARGIN, 7 + BOTTOMMARGIN, 95 + END + + IDD_CHEATS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 269 + TOPMARGIN, 7 + BOTTOMMARGIN, 246 + END + + IDD_ADD_CHEAT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 130 + END + + IDD_CHEAT_LIST, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 273 + TOPMARGIN, 7 + BOTTOMMARGIN, 243 + END + + IDD_ASSOCIATIONS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 109 + TOPMARGIN, 7 + BOTTOMMARGIN, 103 + END + + IDD_GBA_ROM_INFO, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 213 + TOPMARGIN, 7 + BOTTOMMARGIN, 135 + END + + IDD_GB_ROM_INFO, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 213 + TOPMARGIN, 7 + BOTTOMMARGIN, 218 + END + + IDD_GB_CHEAT_LIST, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 279 + TOPMARGIN, 7 + BOTTOMMARGIN, 214 + END + + IDD_ADD_CHEAT_DLG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 175 + TOPMARGIN, 7 + BOTTOMMARGIN, 100 + END + + IDD_GB_PRINTER, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 171 + TOPMARGIN, 7 + BOTTOMMARGIN, 202 + END + + IDD_MOTION_CONFIG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 227 + TOPMARGIN, 7 + BOTTOMMARGIN, 100 + END + + IDD_LANG_SELECT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 61 + END + + IDD_CODE_SELECT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 309 + TOPMARGIN, 7 + BOTTOMMARGIN, 228 + END + + IDD_MAP_VIEW, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 315 + TOPMARGIN, 7 + BOTTOMMARGIN, 231 + END + + IDD_PALETTE_VIEW, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 309 + TOPMARGIN, 7 + BOTTOMMARGIN, 259 + END + + IDD_MEM_VIEWER, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 373 + TOPMARGIN, 7 + BOTTOMMARGIN, 171 + END + + IDD_OAM_VIEW, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 227 + TOPMARGIN, 7 + BOTTOMMARGIN, 178 + END + + IDD_ACCEL_EDITOR, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 392 + TOPMARGIN, 7 + BOTTOMMARGIN, 114 + END + + IDD_TILE_VIEWER, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 319 + TOPMARGIN, 7 + BOTTOMMARGIN, 259 + END + + IDD_GB_COLORS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 162 + TOPMARGIN, 7 + BOTTOMMARGIN, 114 + END + + IDD_DISASSEMBLE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 396 + TOPMARGIN, 7 + BOTTOMMARGIN, 218 + END + + IDD_GDB_PORT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 44 + END + + IDD_GDB_WAITING, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 37 + END + + IDD_LOGGING, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 375 + TOPMARGIN, 7 + BOTTOMMARGIN, 213 + END + + IDD_EXPORT_SPS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 241 + TOPMARGIN, 7 + BOTTOMMARGIN, 141 + END + + IDD_ADDR_SIZE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 60 + END + + IDD_THROTTLE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 119 + TOPMARGIN, 7 + BOTTOMMARGIN, 53 + END + + IDD_GB_DISASSEMBLE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 337 + TOPMARGIN, 7 + BOTTOMMARGIN, 218 + END + + IDD_GB_OAM_VIEW, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 227 + TOPMARGIN, 7 + BOTTOMMARGIN, 178 + END + + IDD_GB_TILE_VIEWER, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 319 + TOPMARGIN, 7 + BOTTOMMARGIN, 231 + END + + IDD_GB_MAP_VIEW, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 315 + TOPMARGIN, 7 + BOTTOMMARGIN, 231 + END + + IDD_GB_PALETTE_VIEW, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 189 + TOPMARGIN, 7 + BOTTOMMARGIN, 227 + END + + IDD_REWIND_INTERVAL, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 61 + END + + IDD_IO_VIEWER, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 262 + TOPMARGIN, 7 + BOTTOMMARGIN, 235 + END + + IDD_MAX_SCALE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 61 + END + + IDD_GAME_OVERRIDES, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 261 + TOPMARGIN, 7 + BOTTOMMARGIN, 105 + END + + IDD_BIOS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 213 + TOPMARGIN, 7 + BOTTOMMARGIN, 152 + END + + IDD_FULLSCREEN, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 160 + TOPMARGIN, 7 + BOTTOMMARGIN, 89 + END + + IDD_AUDIO_CORE_SETTINGS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 323 + TOPMARGIN, 7 + BOTTOMMARGIN, 163 + END + + IDD_JOYBUS_DIALOG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 202 + TOPMARGIN, 7 + BOTTOMMARGIN, 50 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// 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 + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_MENU MENU +BEGIN + POPUP "&File" + BEGIN + MENUITEM "Open GBA...", ID_FILE_OPEN_GBA + MENUITEM "Open GBC...", ID_FILE_OPEN_GBC + MENUITEM "Open GB...", ID_FILE_OPEN_GB + MENUITEM "Close", ID_FILE_CLOSE + MENUITEM SEPARATOR + POPUP "Recent" + BEGIN + MENUITEM "&Reset", ID_FILE_RECENT_RESET + MENUITEM "&Freeze", ID_FILE_RECENT_FREEZE + MENUITEM SEPARATOR + END + MENUITEM SEPARATOR + MENUITEM "Load...", ID_FILE_LOAD + MENUITEM "Save...", ID_FILE_SAVE + POPUP "Load Game" + BEGIN + MENUITEM "Most recent", ID_FILE_LOADGAME_MOSTRECENT + MENUITEM "Auto load most recent", ID_FILE_LOADGAME_AUTOLOADMOSTRECENT + MENUITEM SEPARATOR + MENUITEM "Slot #1", ID_FILE_LOADGAME_SLOT1 + MENUITEM "Slot #2", ID_FILE_LOADGAME_SLOT2 + MENUITEM "Slot #3", ID_FILE_LOADGAME_SLOT3 + MENUITEM "Slot #4", ID_FILE_LOADGAME_SLOT4 + MENUITEM "Slot #5", ID_FILE_LOADGAME_SLOT5 + MENUITEM "Slot #6", ID_FILE_LOADGAME_SLOT6 + MENUITEM "Slot #7", ID_FILE_LOADGAME_SLOT7 + MENUITEM "Slot #8", ID_FILE_LOADGAME_SLOT8 + MENUITEM "Slot #9", ID_FILE_LOADGAME_SLOT9 + MENUITEM "Slot #10", ID_FILE_LOADGAME_SLOT10 + MENUITEM SEPARATOR + MENUITEM "Do not change battery save", ID_LOADGAME_DONOTCHANGEBATTERYSAVE + MENUITEM "Do not change cheat list", ID_LOADGAME_DONOTCHANGECHEATLIST + END + POPUP "Save Game" + BEGIN + MENUITEM "Oldest slot", ID_FILE_SAVEGAME_OLDESTSLOT + MENUITEM SEPARATOR + MENUITEM "Slot #1", ID_FILE_SAVEGAME_SLOT1 + MENUITEM "Slot #2", ID_FILE_SAVEGAME_SLOT2 + MENUITEM "Slot #3", ID_FILE_SAVEGAME_SLOT3 + MENUITEM "Slot #4", ID_FILE_SAVEGAME_SLOT4 + MENUITEM "Slot #5", ID_FILE_SAVEGAME_SLOT5 + MENUITEM "Slot #6", ID_FILE_SAVEGAME_SLOT6 + MENUITEM "Slot #7", ID_FILE_SAVEGAME_SLOT7 + MENUITEM "Slot #8", ID_FILE_SAVEGAME_SLOT8 + MENUITEM "Slot #9", ID_FILE_SAVEGAME_SLOT9 + MENUITEM "Slot #10", ID_FILE_SAVEGAME_SLOT10 + END + MENUITEM SEPARATOR + MENUITEM "Pause", ID_FILE_PAUSE + MENUITEM "Reset", ID_FILE_RESET + MENUITEM SEPARATOR + POPUP "Import" + BEGIN + MENUITEM "&Battery file...", ID_FILE_IMPORT_BATTERYFILE + MENUITEM "Gameshark &code file...", ID_FILE_IMPORT_GAMESHARKCODEFILE + MENUITEM "&Gameshark/Pro Action Replay Snapshot...", ID_FILE_IMPORT_GAMESHARKSNAPSHOT + END + POPUP "Export" + BEGIN + MENUITEM "&Battery file...", ID_FILE_EXPORT_BATTERYFILE + MENUITEM "&Gameshark Snapshot...", ID_FILE_EXPORT_GAMESHARKSNAPSHOT + END + MENUITEM SEPARATOR + MENUITEM "Screen Capture...", ID_FILE_SCREENCAPTURE + MENUITEM "ROM Information...", ID_FILE_ROMINFORMATION + MENUITEM "Toggle Fullscreen", ID_FILE_TOGGLEMENU + MENUITEM SEPARATOR + MENUITEM "Exit", ID_FILE_EXIT + END + POPUP "&Options" + BEGIN + POPUP "&Video" + BEGIN + POPUP "Render API" + BEGIN + MENUITEM "Direct3D 9", ID_OPTIONS_VIDEO_RENDERMETHOD_DIRECT3D + MENUITEM " Filter: Nearest", ID_OPTIONS_VIDEO_RENDEROPTIONS_D3DNOFILTER + MENUITEM " Filter: Bilinear", ID_OPTIONS_VIDEO_RENDEROPTIONS_D3DBILINEAR + MENUITEM " Motion Blur", ID_RENDERAPI_D3DMOTIONBLUR + MENUITEM SEPARATOR + MENUITEM "OpenGL", ID_OPTIONS_VIDEO_RENDERMETHOD_OPENGL + MENUITEM " Filter: Nearest", ID_OPTIONS_VIDEO_RENDEROPTIONS_GLNEAREST + MENUITEM " Filter: Bilinear", ID_OPTIONS_VIDEO_RENDEROPTIONS_GLBILINEAR + MENUITEM SEPARATOR + MENUITEM "VSync", ID_OPTIONS_VIDEO_VSYNC + MENUITEM "Triple Buffering", ID_OPTIONS_VIDEO_TRIPLEBUFFERING + END + MENUITEM SEPARATOR + MENUITEM "1x window size", ID_OPTIONS_VIDEO_X1 + MENUITEM "2x window size", ID_OPTIONS_VIDEO_X2 + MENUITEM "3x window size", ID_OPTIONS_VIDEO_X3 + MENUITEM "4x window size", ID_OPTIONS_VIDEO_X4 + MENUITEM "5x window size", ID_OPTIONS_VIDEO_X5 + MENUITEM "6x window size", ID_OPTIONS_VIDEO_X6 + MENUITEM SEPARATOR + MENUITEM "Select fullscreen mode...", ID_OPTIONS_VIDEO_FULLSCREEN + MENUITEM SEPARATOR + MENUITEM "Ignore aspect ratio", ID_OPTIONS_VIDEO_FULLSCREENSTRETCHTOFIT + MENUITEM "Maximum magnification...", ID_OPTIONS_VIDEO_FULLSCREENMAXSCALE + MENUITEM SEPARATOR + POPUP "&Layers" + BEGIN + MENUITEM "BG 0", ID_OPTIONS_VIDEO_LAYERS_BG0 + MENUITEM "BG 1", ID_OPTIONS_VIDEO_LAYERS_BG1 + MENUITEM "BG 2", ID_OPTIONS_VIDEO_LAYERS_BG2 + MENUITEM "BG 3", ID_OPTIONS_VIDEO_LAYERS_BG3 + MENUITEM "OBJ", ID_OPTIONS_VIDEO_LAYERS_OBJ + MENUITEM "WIN 0", ID_OPTIONS_VIDEO_LAYERS_WIN0 + MENUITEM "WIN 1", ID_OPTIONS_VIDEO_LAYERS_WIN1 + MENUITEM "OBJ WIN", ID_OPTIONS_VIDEO_LAYERS_OBJWIN + END + MENUITEM SEPARATOR + MENUITEM "Disable status messages", ID_OPTIONS_EMULATOR_DISABLESTATUSMESSAGES + END + POPUP "&Pixel Filter" + BEGIN + MENUITEM "&Disable Filters", ID_OPTIONS_FILTER_NORMAL + POPUP "&Magnification" + BEGIN + POPUP "&2X" + BEGIN + MENUITEM "&Simple 2x", ID_OPTIONS_FILTER16BIT_SIMPLE2X + MENUITEM SEPARATOR + MENUITEM "&Pixelate", ID_OPTIONS_FILTER16BIT_PIXELATEEXPERIMENTAL + MENUITEM "&TV Mode", ID_OPTIONS_FILTER_TVMODE + MENUITEM "Scan&lines", ID_OPTIONS_FILTER_SCANLINES + MENUITEM SEPARATOR + MENUITEM "&Bilinear", ID_OPTIONS_FILTER_BILINEAR + MENUITEM "B&ilinear Plus", ID_OPTIONS_FILTER_BILINEARPLUS + MENUITEM SEPARATOR + MENUITEM "AdvanceMAME Scale2x", ID_OPTIONS_FILTER16BIT_ADVANCEMAMESCALE2X + MENUITEM "&2xSaI", ID_OPTIONS_FILTER_2XSAI + MENUITEM "S&uper 2xSaI", ID_OPTIONS_FILTER_SUPER2XSAI + MENUITEM "Super &Eagle", ID_OPTIONS_FILTER_SUPEREAGLE + MENUITEM "&LQ2x", ID_OPTIONS_FILTER_LQ2X + MENUITEM "&HQ2x", ID_OPTIONS_FILTER_HQ2X + END + POPUP "&3X" + BEGIN + MENUITEM "&Simple 3x", ID_OPTIONS_FILTER_SIMPLE3X + MENUITEM SEPARATOR + MENUITEM "&HQ3x", ID_OPTIONS_FILTER_HQ3X + END + POPUP "&4X" + BEGIN + MENUITEM "&Simple 4x", ID_OPTIONS_FILTER_SIMPLE4X + MENUITEM SEPARATOR + MENUITEM "&HQ4x", ID_OPTIONS_FILTER_HQ4X + END + END + MENUITEM SEPARATOR + MENUITEM "&Use Filter Plugin", ID_OPTIONS_FILTER_PLUGIN + MENUITEM "Select Filter &Plugin ...", ID_OPTIONS_SELECT_PLUGIN + MENUITEM SEPARATOR + POPUP "&Interframe Blending" + BEGIN + MENUITEM "&None", ID_OPTIONS_FILTER_INTERFRAMEBLENDING_NONE + MENUITEM "&Motion Blur", ID_OPTIONS_FILTER_INTERFRAMEBLENDING_MOTIONBLUR + MENUITEM "&Smart", ID_OPTIONS_FILTER_INTERFRAMEBLENDING_SMART + END + MENUITEM SEPARATOR + MENUITEM "Multi-Threading (D3D)", ID_PIXELFILTER_MULTI + MENUITEM "Disable &MMX", ID_OPTIONS_FILTER_DISABLEMMX + END + POPUP "&Audio" + BEGIN + POPUP "Output API" + BEGIN + MENUITEM "XAudio2", ID_OUTPUTAPI_XAUDIO2 + MENUITEM " Configuration...", ID_OUTPUTAPI_XAUDIO2CONFIG + MENUITEM SEPARATOR + MENUITEM "OpenAL", ID_OUTPUTAPI_OPENAL + MENUITEM " Configuration...", ID_OUTPUTAPI_OALCONFIGURATION + MENUITEM SEPARATOR + MENUITEM "DirectSound 8", ID_OUTPUTAPI_DIRECTSOUND + MENUITEM SEPARATOR + MENUITEM "&Sync game to audio", ID_OPTIONS_EMULATOR_SYNCHRONIZE + END + MENUITEM SEPARATOR + MENUITEM "Core Settings...", ID_AUDIO_CORE_SETTINGS + MENUITEM SEPARATOR + POPUP "Sound Channels" + BEGIN + MENUITEM "Channel &1", ID_OPTIONS_SOUND_CHANNEL1, CHECKED + MENUITEM "Channel &2", ID_OPTIONS_SOUND_CHANNEL2, CHECKED + MENUITEM "Channel &3", ID_OPTIONS_SOUND_CHANNEL3, CHECKED + MENUITEM "Channel &4", ID_OPTIONS_SOUND_CHANNEL4, CHECKED + MENUITEM "Direct Sound &A", ID_OPTIONS_SOUND_DIRECTSOUNDA, CHECKED + MENUITEM "Direct Sound &B", ID_OPTIONS_SOUND_DIRECTSOUNDB, CHECKED + END + END + POPUP "&Input" + BEGIN + POPUP "&Set" + BEGIN + MENUITEM "Config &1...", ID_OPTIONS_JOYPAD_CONFIGURE_1 + MENUITEM "Config &2...", ID_OPTIONS_JOYPAD_CONFIGURE_2 + MENUITEM "Config &3...", ID_OPTIONS_JOYPAD_CONFIGURE_3 + MENUITEM "Config &4...", ID_OPTIONS_JOYPAD_CONFIGURE_4 + MENUITEM SEPARATOR + MENUITEM "&Motion...", ID_OPTIONS_JOYPAD_MOTIONCONFIGURE + END + POPUP "&Use" + BEGIN + MENUITEM "Config &1", ID_OPTIONS_JOYPAD_DEFAULTJOYPAD_1 + MENUITEM "Config &2", ID_OPTIONS_JOYPAD_DEFAULTJOYPAD_2 + MENUITEM "Config &3", ID_OPTIONS_JOYPAD_DEFAULTJOYPAD_3 + MENUITEM "Config &4", ID_OPTIONS_JOYPAD_DEFAULTJOYPAD_4 + END + MENUITEM SEPARATOR + POPUP "&Autofire" + BEGIN + MENUITEM "&A", ID_OPTIONS_JOYPAD_AUTOFIRE_A + MENUITEM "&B", ID_OPTIONS_JOYPAD_AUTOFIRE_B + MENUITEM "&L", ID_OPTIONS_JOYPAD_AUTOFIRE_L + MENUITEM "&R", ID_OPTIONS_JOYPAD_AUTOFIRE_R + END + END + POPUP "&Speed" + BEGIN + POPUP "&Throttle" + BEGIN + MENUITEM "No throttle", ID_OPTIONS_FRAMESKIP_THROTTLE_NOTHROTTLE + MENUITEM "25%", ID_OPTIONS_FRAMESKIP_THROTTLE_25 + MENUITEM "50%", ID_OPTIONS_FRAMESKIP_THROTTLE_50 + MENUITEM "100%", ID_OPTIONS_FRAMESKIP_THROTTLE_100 + MENUITEM "150%", ID_OPTIONS_FRAMESKIP_THROTTLE_150 + MENUITEM "200%", ID_OPTIONS_FRAMESKIP_THROTTLE_200 + MENUITEM "&Other...", ID_OPTIONS_FRAMESKIP_THROTTLE_OTHER + END + POPUP "&Frame Skip" + BEGIN + MENUITEM "&Automatic", ID_OPTIONS_FRAMESKIP_AUTOMATIC + MENUITEM SEPARATOR + MENUITEM "&No frame skip", ID_OPTIONS_VIDEO_FRAMESKIP_0 + MENUITEM "&1 frame", ID_OPTIONS_VIDEO_FRAMESKIP_1 + MENUITEM "&2 frames", ID_OPTIONS_VIDEO_FRAMESKIP_2 + MENUITEM "&3 frames", ID_OPTIONS_VIDEO_FRAMESKIP_3 + MENUITEM "&4 frames", ID_OPTIONS_VIDEO_FRAMESKIP_4 + MENUITEM "&5 frames", ID_OPTIONS_VIDEO_FRAMESKIP_5 + MENUITEM "&6 frames", ID_OPTIONS_VIDEO_FRAMESKIP_6 + MENUITEM "&7 frames", ID_OPTIONS_VIDEO_FRAMESKIP_7 + MENUITEM "&8 frames", ID_OPTIONS_VIDEO_FRAMESKIP_8 + MENUITEM "&9 frames", ID_OPTIONS_VIDEO_FRAMESKIP_9 + END + MENUITEM SEPARATOR + MENUITEM "Turbo mode", ID_OPTIONS_EMULATOR_SPEEDUPTOGGLE + END + MENUITEM SEPARATOR + POPUP "&Emulator" + BEGIN + MENUITEM "&Associate...", ID_OPTIONS_EMULATOR_ASSOCIATE + MENUITEM "&Directories...", ID_OPTIONS_EMULATOR_DIRECTORIES + MENUITEM "BIOS Files...", ID_EMULATOR_BIOSFILES + POPUP "&Priority" + BEGIN + MENUITEM "&Highest", ID_OPTIONS_PRIORITY_HIGHEST + MENUITEM "&Above Normal", ID_OPTIONS_PRIORITY_ABOVENORMAL + MENUITEM "&Normal", ID_OPTIONS_PRIORITY_NORMAL + MENUITEM "&Below Normal", ID_OPTIONS_PRIORITY_BELOWNORMAL + END + MENUITEM "&Remove intros (GBA)", ID_OPTIONS_EMULATOR_REMOVEINTROSGBA + MENUITEM "Auto-apply IPS/UPS/PPF", ID_OPTIONS_EMULATOR_AUTOMATICALLYAPPLYPATCHFILES + MENUITEM "Pause when inactive", ID_OPTIONS_EMULATOR_PAUSEWHENINACTIVE + MENUITEM "AGB Print", ID_OPTIONS_EMULATOR_AGBPRINT + MENUITEM "Real Time Clock", ID_OPTIONS_EMULATOR_REALTIMECLOCK + MENUITEM "&Game Overrides...", ID_OPTIONS_EMULATOR_GAMEOVERRIDES + POPUP "Show speed" + BEGIN + MENUITEM "None", ID_OPTIONS_EMULATOR_SHOWSPEED_NONE + MENUITEM "Percentage", ID_OPTIONS_EMULATOR_SHOWSPEED_PERCENTAGE + MENUITEM "Detailed", ID_OPTIONS_EMULATOR_SHOWSPEED_DETAILED + MENUITEM SEPARATOR + MENUITEM "Transparent", ID_OPTIONS_EMULATOR_SHOWSPEED_TRANSPARENT + END + POPUP "Save Type" + BEGIN + MENUITEM "&Automatic", ID_OPTIONS_EMULATOR_SAVETYPE_AUTOMATIC + MENUITEM "EEPROM", ID_OPTIONS_EMULATOR_SAVETYPE_EEPROM + MENUITEM "SRAM", ID_OPTIONS_EMULATOR_SAVETYPE_SRAM + MENUITEM "Flash", ID_OPTIONS_EMULATOR_SAVETYPE_FLASH + MENUITEM "EEPROM+Sensor", ID_OPTIONS_EMULATOR_SAVETYPE_EEPROMSENSOR + MENUITEM "None", ID_OPTIONS_EMULATOR_SAVETYPE_NONE + MENUITEM SEPARATOR + MENUITEM "Flash 64 KB", ID_OPTIONS_EMULATOR_SAVETYPE_FLASH512K + MENUITEM "Flash 128 KB", ID_OPTIONS_EMULATOR_SAVETYPE_FLASH1M + MENUITEM SEPARATOR + MENUITEM "Detect now...", ID_OPTIONS_EMULATOR_SAVETYPE_DETECTNOW + END + POPUP "Screenshot &Format" + BEGIN + MENUITEM "&PNG", ID_OPTIONS_EMULATOR_PNGFORMAT + MENUITEM "&BMP", ID_OPTIONS_EMULATOR_BMPFORMAT + END + MENUITEM SEPARATOR + POPUP "UI &Language" + BEGIN + MENUITEM "&System", ID_OPTIONS_LANGUAGE_SYSTEM + MENUITEM "&English", ID_OPTIONS_LANGUAGE_ENGLISH + MENUITEM "&Other...", ID_OPTIONS_LANGUAGE_OTHER + END + END + POPUP "&Gameboy" + BEGIN + MENUITEM "&Border", ID_OPTIONS_GAMEBOY_BORDER + MENUITEM "&Printer", ID_OPTIONS_GAMEBOY_PRINTER + MENUITEM "Border Automatic", ID_OPTIONS_GAMEBOY_BORDERAUTOMATIC + MENUITEM SEPARATOR + MENUITEM "&Automatic", ID_OPTIONS_GAMEBOY_AUTOMATIC + MENUITEM "&GBA", ID_OPTIONS_GAMEBOY_GBA + MENUITEM "&CGB/GBC", ID_OPTIONS_GAMEBOY_CGB + MENUITEM "&SGB", ID_OPTIONS_GAMEBOY_SGB + MENUITEM "SGB&2", ID_OPTIONS_GAMEBOY_SGB2 + MENUITEM "G&B", ID_OPTIONS_GAMEBOY_GB + MENUITEM SEPARATOR + MENUITEM "&Real Colors", ID_OPTIONS_GAMEBOY_REALCOLORS + MENUITEM "G&ameboy Colors", ID_OPTIONS_GAMEBOY_GAMEBOYCOLORS + MENUITEM SEPARATOR + MENUITEM "&Colors...", ID_OPTIONS_GAMEBOY_COLORS + END + POPUP "&Link" + BEGIN + MENUITEM "Enable GBA Linking", ID_OPTIONS_LINK_ENABLE + MENUITEM "&Wireless Adapter", ID_OPTIONS_LINK_WIRELESSADAPTER + MENUITEM "&Options...", ID_OPTIONS_LINK_OPTIONS + MENUITEM SEPARATOR + MENUITEM "&Joybus Options...", ID_OPTIONS_JOYBUS + END + END + POPUP "&Cheats" + BEGIN + MENUITEM "Create...", ID_CHEATS_SEARCHFORCHEATS + MENUITEM "List...", ID_CHEATS_CHEATLIST + MENUITEM SEPARATOR + MENUITEM "Save cheat list to...", ID_CHEATS_SAVECHEATLIST + MENUITEM "Load cheat list from...", ID_CHEATS_LOADCHEATLIST + MENUITEM "Save/Load automatically", ID_CHEATS_AUTOMATICSAVELOADCHEATS + MENUITEM SEPARATOR + MENUITEM "Disabled", ID_CHEATS_DISABLECHEATS + END + POPUP "&Tools" + BEGIN + MENUITEM "Disassemble...", ID_TOOLS_DISASSEMBLE + MENUITEM "Logging...", ID_TOOLS_LOGGING + MENUITEM "&IO Viewer...", ID_TOOLS_IOVIEWER + MENUITEM "&Map Viewer...", ID_TOOLS_MAPVIEW + MENUITEM "&Memory viewer...", ID_TOOLS_MEMORYVIEWER + MENUITEM "OAM Viewer...", ID_TOOLS_OAMVIEWER + MENUITEM "&Palette Viewer...", ID_TOOLS_PALETTEVIEW + MENUITEM "Tile Viewer...", ID_TOOLS_TILEVIEWER + MENUITEM SEPARATOR + MENUITEM "&Next frame", ID_DEBUG_NEXTFRAME + POPUP "GDB" + BEGIN + MENUITEM "Wait for connection...", ID_TOOLS_DEBUG_GDB + MENUITEM "Load and wait...", ID_TOOLS_DEBUG_LOADANDWAIT + MENUITEM "Break into GDB", ID_TOOLS_DEBUG_BREAK + MENUITEM "Disconnect", ID_TOOLS_DEBUG_DISCONNECT + END + MENUITEM SEPARATOR + POPUP "Record" + BEGIN + MENUITEM "Start sound recording...", ID_OPTIONS_SOUND_STARTRECORDING + MENUITEM "Stop sound recording", ID_OPTIONS_SOUND_STOPRECORDING + MENUITEM "Start AVI recording...", ID_TOOLS_RECORD_STARTAVIRECORDING + MENUITEM "Stop AVI recording", ID_TOOLS_RECORD_STOPAVIRECORDING + MENUITEM "Start movie recording...", ID_TOOLS_RECORD_STARTMOVIERECORDING + MENUITEM "Stop movie recording", ID_TOOLS_RECORD_STOPMOVIERECORDING + END + POPUP "Play" + BEGIN + MENUITEM "Start playing movie...", ID_TOOLS_PLAY_STARTMOVIEPLAYING + MENUITEM "Stop playing movie", ID_TOOLS_PLAY_STOPMOVIEPLAYING + END + MENUITEM SEPARATOR + MENUITEM "Rewind", ID_TOOLS_REWIND + MENUITEM "Rewind interval...", ID_OPTIONS_EMULATOR_REWINDINTERVAL + MENUITEM SEPARATOR + MENUITEM "Customize...", ID_TOOLS_CUSTOMIZE + END + POPUP "&Help" + BEGIN + MENUITEM "Report Bugs", ID_HELP_BUGREPORT + MENUITEM "VBA-M Support Forum", ID_HELP_FAQ + MENUITEM "License...", ID_HELP_GNUPUBLICLICENSE + MENUITEM SEPARATOR + MENUITEM "&About VBA-M...", ID_HELP_ABOUT + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDR_ACCELERATOR ACCELERATORS +BEGIN + "C", ID_CHEATS_SEARCHFORCHEATS, VIRTKEY, CONTROL, NOINVERT + "N", ID_DEBUG_NEXTFRAME, VIRTKEY, CONTROL, NOINVERT + "X", ID_FILE_EXIT, VIRTKEY, CONTROL, NOINVERT + "L", ID_FILE_LOAD, VIRTKEY, CONTROL, NOINVERT + VK_F1, ID_FILE_LOADGAME_SLOT1, VIRTKEY, NOINVERT + VK_F10, ID_FILE_LOADGAME_SLOT10, VIRTKEY, NOINVERT + VK_F2, ID_FILE_LOADGAME_SLOT2, VIRTKEY, NOINVERT + VK_F3, ID_FILE_LOADGAME_SLOT3, VIRTKEY, NOINVERT + VK_F4, ID_FILE_LOADGAME_SLOT4, VIRTKEY, NOINVERT + VK_F5, ID_FILE_LOADGAME_SLOT5, VIRTKEY, NOINVERT + VK_F6, ID_FILE_LOADGAME_SLOT6, VIRTKEY, NOINVERT + VK_F7, ID_FILE_LOADGAME_SLOT7, VIRTKEY, NOINVERT + VK_F8, ID_FILE_LOADGAME_SLOT8, VIRTKEY, NOINVERT + VK_F9, ID_FILE_LOADGAME_SLOT9, VIRTKEY, NOINVERT + VK_F1, ID_FILE_MRU_FILE1, VIRTKEY, CONTROL, NOINVERT + VK_F10, ID_FILE_MRU_FILE10, VIRTKEY, CONTROL, NOINVERT + VK_F2, ID_FILE_MRU_FILE2, VIRTKEY, CONTROL, NOINVERT + VK_F3, ID_FILE_MRU_FILE3, VIRTKEY, CONTROL, NOINVERT + VK_F4, ID_FILE_MRU_FILE4, VIRTKEY, CONTROL, NOINVERT + VK_F5, ID_FILE_MRU_FILE5, VIRTKEY, CONTROL, NOINVERT + VK_F6, ID_FILE_MRU_FILE6, VIRTKEY, CONTROL, NOINVERT + VK_F7, ID_FILE_MRU_FILE7, VIRTKEY, CONTROL, NOINVERT + VK_F8, ID_FILE_MRU_FILE8, VIRTKEY, CONTROL, NOINVERT + VK_F9, ID_FILE_MRU_FILE9, VIRTKEY, CONTROL, NOINVERT + "P", ID_FILE_PAUSE, VIRTKEY, CONTROL, NOINVERT + VK_PAUSE, ID_FILE_PAUSE, VIRTKEY, NOINVERT + "R", ID_FILE_RESET, VIRTKEY, CONTROL, NOINVERT + "S", ID_FILE_SAVE, VIRTKEY, CONTROL, NOINVERT + VK_F1, ID_FILE_SAVEGAME_SLOT1, VIRTKEY, SHIFT, NOINVERT + VK_F10, ID_FILE_SAVEGAME_SLOT10, VIRTKEY, SHIFT, NOINVERT + VK_F2, ID_FILE_SAVEGAME_SLOT2, VIRTKEY, SHIFT, NOINVERT + VK_F3, ID_FILE_SAVEGAME_SLOT3, VIRTKEY, SHIFT, NOINVERT + VK_F4, ID_FILE_SAVEGAME_SLOT4, VIRTKEY, SHIFT, NOINVERT + VK_F5, ID_FILE_SAVEGAME_SLOT5, VIRTKEY, SHIFT, NOINVERT + VK_F6, ID_FILE_SAVEGAME_SLOT6, VIRTKEY, SHIFT, NOINVERT + VK_F7, ID_FILE_SAVEGAME_SLOT7, VIRTKEY, SHIFT, NOINVERT + VK_F8, ID_FILE_SAVEGAME_SLOT8, VIRTKEY, SHIFT, NOINVERT + VK_F9, ID_FILE_SAVEGAME_SLOT9, VIRTKEY, SHIFT, NOINVERT + VK_ESCAPE, ID_FILE_TOGGLEMENU, VIRTKEY, NOINVERT + "1", ID_OPTIONS_JOYPAD_AUTOFIRE_A, VIRTKEY, ALT, NOINVERT + "2", ID_OPTIONS_JOYPAD_AUTOFIRE_B, VIRTKEY, ALT, NOINVERT + "3", ID_OPTIONS_JOYPAD_AUTOFIRE_L, VIRTKEY, ALT, NOINVERT + "4", ID_OPTIONS_JOYPAD_AUTOFIRE_R, VIRTKEY, ALT, NOINVERT + "1", ID_OPTIONS_VIDEO_LAYERS_BG0, VIRTKEY, CONTROL, NOINVERT + "2", ID_OPTIONS_VIDEO_LAYERS_BG1, VIRTKEY, CONTROL, NOINVERT + "3", ID_OPTIONS_VIDEO_LAYERS_BG2, VIRTKEY, CONTROL, NOINVERT + "4", ID_OPTIONS_VIDEO_LAYERS_BG3, VIRTKEY, CONTROL, NOINVERT + "5", ID_OPTIONS_VIDEO_LAYERS_OBJ, VIRTKEY, CONTROL, NOINVERT + "8", ID_OPTIONS_VIDEO_LAYERS_OBJWIN, VIRTKEY, CONTROL, NOINVERT + "6", ID_OPTIONS_VIDEO_LAYERS_WIN0, VIRTKEY, CONTROL, NOINVERT + "7", ID_OPTIONS_VIDEO_LAYERS_WIN1, VIRTKEY, CONTROL, NOINVERT + "B", ID_TOOLS_REWIND, VIRTKEY, CONTROL, NOINVERT + "0", ID_OPTIONS_VIDEO_LAYERS_RESET, VIRTKEY, CONTROL, NOINVERT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_UNSUPPORTED_VBA_SGM "Unsupported VisualBoyAdvance save game version %d" + IDS_CANNOT_LOAD_SGM "Cannot load save game for %s" + IDS_SAVE_GAME_NOT_USING_BIOS "Save game is not using the BIOS file" + IDS_SAVE_GAME_USING_BIOS "Save game is using the BIOS file" + IDS_UNSUPPORTED_SAVE_TYPE "Unsupported save type %d" + IDS_CANNOT_OPEN_FILE "Cannot open file %s" + IDS_BAD_ZIP_FILE "Bad ZIP file %s" + IDS_NO_IMAGE_ON_ZIP "No image found on ZIP file %s" + IDS_ERROR_OPENING_IMAGE "Error opening image %s" + IDS_ERROR_READING_IMAGE "Error reading image %s" + IDS_UNSUPPORTED_BIOS_FUNCTION + "Unsupported BIOS function %02x called from %08x. A BIOS file is needed in order to get correct behaviour." + IDS_INVALID_BIOS_FILE_SIZE "Invalid BIOS file size" + IDS_INVALID_CHEAT_CODE "Invalid cheat code '%s'. Supported formats are:\nXXXXXXXX:YY, XXXXXXXX:YYYY, XXXXXXXX:YYYYYYYY." + IDS_UNKNOWN_ARM_OPCDOE "Unimplemented opcode %08x from %08x" + IDS_UNKNOWN_THUMB_OPCODE "Unknown opcode %04x from %08x" +END + +STRINGTABLE +BEGIN + IDS_ERROR_CREATING_FILE "Error creating file %s" + IDS_FAILED_TO_READ_SGM "Battery file's size incompatible with the ROM settings %s (%d).\nWarning : save of the battery file is now disabled !" + IDS_FAILED_TO_READ_RTC "Failed to read RTC from save game %s (continuing)" + IDS_UNSUPPORTED_VB_SGM "Unsupported VisualBoy save game version %d" + IDS_CANNOT_LOAD_SGM_FOR "Cannot load save game for %s. Playing %s" + IDS_ERROR_OPENING_IMAGE_FROM "Error opening image %s from zip file %s" + IDS_ERROR_READING_IMAGE_FROM "Error reading image %s from zip file %s" + IDS_UNSUPPORTED_ROM_SIZE "Unsupported rom size %02x" + IDS_UNSUPPORTED_RAM_SIZE "Unsupported ram size %02x" + IDS_UNKNOWN_CARTRIDGE_TYPE "Unknown cartridge type %02x" + IDS_MAXIMUM_NUMBER_OF_CHEATS "Maximum number of cheats reached." + IDS_INVALID_GAMESHARK_CODE "Invalid GameShark code: %s" + IDS_INVALID_GAMEGENIE_CODE "Invalid GameGenie code: %s" + IDS_INVALID_CHEAT_TO_REMOVE "Invalid cheat to remove %d" + IDS_INVALID_CHEAT_CODE_ADDRESS "Invalid cheat code address: %08x" + IDS_UNSUPPORTED_CHEAT_LIST_VERSION "Unsupported cheat list version %d" +END + +STRINGTABLE +BEGIN + IDS_DIRECTX_7_REQUIRED "DirectX 7.0 or greater is required to run.\nDownload at http://www.microsoft.com/directx.\n\nError found at: %s" + IDS_DISABLING_VIDEO_MEMORY "Disabling Use Video Memory setting" + IDS_SETTING_WILL_BE_EFFECTIVE + "Setting will be effective the next time you start the emulator" + IDS_DISABLING_EMULATION_ONLY "Disabling Emulation Only setting" + IDS_FAILED_TO_OPEN_FILE "Failed to open file %s" + IDS_FAILED_TO_READ_ZIP_DIR "Failed to read zip directory for file %s" + IDS_UNSUPPORTED_FILE_TYPE "Unsupported file type: %s" + IDS_CANNOT_CREATE_DIRECTSOUND "Cannot create DirectSound %08x" + IDS_CANNOT_SETCOOPERATIVELEVEL "Cannot SetCooperativeLevel %08x" + IDS_CANNOT_CREATESOUNDBUFFER "Cannot CreateSoundBuffer %08x" + IDS_CANNOT_SETFORMAT_PRIMARY "Cannot SetFormat for primary %08x" + IDS_CANNOT_CREATESOUNDBUFFER_SEC "Cannot CreateSoundBuffer secondary %08x" + IDS_CANNOT_PLAY_PRIMARY "Cannot Play primary %08x" + IDS_SEARCH_PRODUCED_TOO_MANY + "Search produced %d results. Please refine better" + IDS_NUMBER_CANNOT_BE_EMPTY "Number cannot be empty" + IDS_INVALID_ADDRESS "Invalid address: %08x" +END + +STRINGTABLE +BEGIN + IDS_MISALIGNED_HALFWORD "Misaligned half-word address: %08x" + IDS_MISALIGNED_WORD "Misaligned word address: %08x" + IDS_VALUE_CANNOT_BE_EMPTY "Value cannot be empty" + IDS_ERROR_ON_STARTDOC "Error on StartDoc" + IDS_ERROR_ON_STARTPAGE "Error on StartPage" + IDS_ERROR_PRINTING_ON_STRETCH "Error printing on StretchDIBits" + IDS_ERROR_ON_ENDPAGE "Error on EndPage" + IDS_ERROR_ON_ENDDOC "Error on EndDoc" + IDS_ERROR "Error" + IDS_JOY_LEFT "Joy %d Left" + IDS_JOY_RIGHT "Joy %d Right" + IDS_JOY_UP "Joy %d Up" + IDS_JOY_DOWN "Joy %d Down" + IDS_JOY_BUTTON "Joy %d %s" + IDS_SELECT_ROM_DIR "Select ROM directory:" + IDS_SELECT_BATTERY_DIR "Select Battery directory:" +END + +STRINGTABLE +BEGIN + IDS_SELECT_SAVE_DIR "Select Save Directory:" + IDS_SELECT_CAPTURE_DIR "Select Capture directory:" + IDS_RESET "Reset" + IDS_AUTOFIRE_A_DISABLED "autofire A disabled" + IDS_AUTOFIRE_A "autofire A" + IDS_AUTOFIRE_B_DISABLED "autofire B disabled" + IDS_AUTOFIRE_B "autofire B" + IDS_AUTOFIRE_L_DISABLED "autofire L disabled" + IDS_AUTOFIRE_L "autofire L" + IDS_AUTOFIRE_R_DISABLED "autofire R disabled" + IDS_AUTOFIRE_R "autofire R" + IDS_SELECT_ROM "Select ROM" + IDS_SELECT_SAVE_GAME_NAME "Select save game name" + IDS_LOADED_STATE "Loaded state" + IDS_LOADED_STATE_N "Loaded state %d" +END + +STRINGTABLE +BEGIN + IDS_WROTE_STATE "Wrote state" + IDS_WROTE_STATE_N "Wrote state %d" + IDS_LOADED_BATTERY "Loaded battery" + IDS_SELECT_CAPTURE_NAME "Select screen capture name" + IDS_SCREEN_CAPTURE "Screen capture" + IDS_ADDRESS "Address" + IDS_OLD_VALUE "Old Value" + IDS_NEW_VALUE "New Value" + IDS_ADD_CHEAT_CODE "Add cheat code" + IDS_CODE "Code" + IDS_DESCRIPTION "Description" + IDS_STATUS "Status" + IDS_ADD_GG_CODE "Add GameGenie code" + IDS_ADD_GS_CODE "Add GameShark code" + IDS_POCKET_PRINTER "Pocket Printer" + IDS_UNKNOWN "Unknown" +END + +STRINGTABLE +BEGIN + IDS_NONE "None" + IDS_FAILED_TO_LOAD_LIBRARY "Failed to load library %s" + IDS_FAILED_TO_GET_LOCINFO "Failed to get locale information" + IDS_SELECT_CHEAT_LIST_NAME "Select cheat list name" + IDS_FILTER_GBAROM "Game Boy Advance ROMs (*.GBA;*.AGB;*.BIN;*.ELF;*.MB;*.ZIP;*.7Z;*.Z;*.GZ)_*.GBA;*.AGB;*.BIN;*.ELF;*.MB;*.ZIP;*.7Z;*.Z;*.GZ__" + IDS_FILTER_SGM "VisualBoyAdvance Save Game_*.SGM__" + IDS_FILTER_CHEAT_LIST "VisualBoyAdvance Cheat List_*.CLT__" + IDS_FILTER_PNG "PNG Image_*.PNG_BMP Image_*.BMP__" + IDS_LOADED_CHEATS "Loaded cheats" + IDS_ERROR_DISP_COLOR "Unsupported display setting for color depth: %d bits. \nWindows desktop must be in either 16-bit, 24-bit or 32-bit mode for this program to work in window mode." + IDS_ADD_GSA_CODE "Add GameSharkAdvance code" + IDS_FILTER_SPS "Gameshark Snapshot_*.SPS__" + IDS_SELECT_SNAPSHOT_FILE "Select snapshot file" + IDS_FILTER_SAV "Battery file_*.SAV_Flash save_*.DAT__" + IDS_SELECT_BATTERY_FILE "Select battery file" +END + +STRINGTABLE +BEGIN + IDS_INVALID_INTERVAL_VALUE + "Invalid rewind interval value. Please enter a number between 0 and 600 seconds." + IDS_REGISTRY "VisualBoyAdvance no longer uses the registry to store its settings. Your previous settings have been exported into the file: %s" + IDS_MOVIE_PLAY "Playing a movie will load a save state which may erase your previous battery saves. Please be sure to have a saved state if you don't want to loose any previous data." + IDS_FILTER_GSVSPS "GS & PAC Snapshots (*.SPS;*.XPS)_*.SPS;*.XPS_GameShark SP Snapshots (*.GSV)_*.gsv__" +END + +STRINGTABLE +BEGIN + IDS_UNSUPPORTED_CHEAT_LIST_TYPE "Unsupported cheat list type %d" + IDS_INVALID_GSA_CODE "Invalid GSA code. Format is XXXXXXXXYYYYYYYY." + IDS_CANNOT_IMPORT_SNAPSHOT_FOR + "Cannot import snapshot for %s. Current game is %s" + IDS_UNSUPPORTED_SNAPSHOT_FILE "Unsupported snapshot file %s" + IDS_UNSUPPORTED_ARM_MODE "Unsupported ARM mode %02x" + IDS_UNSUPPORTED_CODE_FILE "Unsupported code file %s" + IDS_GSA_CODE_WARNING "Warning: cheats are for game %s. Current game is %s.\nCodes may not work correctly." + IDS_INVALID_CBA_CODE "Invalid CBA code. Format is XXXXXXXX YYYY." + IDS_CBA_CODE_WARNING "Warning: Codes seem to be for a different game.\nCodes may not work correctly." + IDS_OUT_OF_MEMORY "Failed to allocate memory for %s" + IDS_TOOLTIP_DEFAULT_VOLUME "Reset to default volume (100%)." + IDS_TOOLTIP_ENHANCE_SOUND "Enable the following sound enhancements." + IDS_TOOLTIP_SURROUND "Inverts the phase of some channels, making it sound as if sound is coming from behind." + IDS_TOOLTIP_DECLICKING "When enabled, clicks are reduced by using GBA sound hardware. Note that clicks are normal for GB and GBC sound hardware." +END + +STRINGTABLE +BEGIN + IDS_FILTER_GBS "Gameboy Snapshot_*.GBS__" + IDS_FILTER_GCF "Gameshark Code File_*.GCF__" + IDS_SELECT_CODE_FILE "Select code file" + IDS_SAVE_WILL_BE_LOST "Importing a snapshot file will erase any saved games. Do you want to continue?" + IDS_CONFIRM_ACTION "Please confirm action" + IDS_CODES_WILL_BE_LOST "Importing a code file will erase any entered codes. Do you want to continue?" + IDS_FILTER_SPC "Gameshark Code File_*.SPC;*.XPC__" + IDS_ADD_CBA_CODE "Add CodeBreakerAdvance code" + IDS_FILTER_WAV "Wave file_*.WAV__" + IDS_SELECT_WAV_NAME "Select wave file name" + IDS_FILTER_GBROM "Game Boy ROMs (*.GB;*.DMG;*.SGB;*.ZIP;*.7Z;*.Z;*.GZ)_*.GB;*.DMG;*.SGB;*.ZIP;*.7Z;*.Z;*.GZ__" + IDS_FILTER_PAL "Windows Palette (*.PAL)_*.PAL_PaintShop Palette (*.PAL)_*.PAL_Adobe Color Table (*.ACT)_*.ACT__" + IDS_SELECT_PALETTE_NAME "Select palette name:" + IDS_SEARCH_PRODUCED_NO_RESULTS "Search produced no results." + IDS_ERROR_BINDING "Error binding socket. Port probably in use." + IDS_ERROR_LISTENING "Error listening on socket." +END + +STRINGTABLE +BEGIN + IDS_ERROR_CREATING_SOCKET "Error creating socket." + IDS_ACK_NOT_RECEIVED "ACK not received from GDB." + IDS_ERROR_NOT_GBA_IMAGE "Error: not a GBA image." + IDS_EEPROM_NOT_SUPPORTED "EEPROM saves cannot be exported." + IDS_FILTER_DUMP "Memory Dump_*.DMP__" + IDS_SELECT_DUMP_FILE "Select dump file name" + IDS_FILTER_AVI "AVI File_*.AVI__" + IDS_SELECT_AVI_NAME "Select AVI file name" + IDS_INVALID_THROTTLE_VALUE + "Invalid throttle value. Please enter a number between 5 and 1000." + IDS_FILTER_INI "Skin INI File_*.INI__" + IDS_SELECT_SKIN_FILE "Select the skin file name" + IDS_FILTER_VMV "VisualBoyAdvance Movie_*.VMV__" + IDS_SELECT_MOVIE_NAME "Select movie name" + IDS_BUG_REPORT "The bug report information is now available on the Windows Clipboard. Please paste it into any bug report made by email or on forums to help solve problems more easily." + IDS_UNSUPPORTED_MOVIE_VERSION "Unsupported movie version %d." + IDS_END_OF_MOVIE "end of movie" +END + +STRINGTABLE +BEGIN + IDS_OAL_NODEVICE "There are no sound devices present on this system." + IDS_OAL_NODLL "OpenAL32.dll could not be found on your system. Please install the runtime from http://openal.org" + IDS_AVI_CANNOT_CREATE_AVI "Cannot create AVI file." + IDS_AVI_CANNOT_CREATE_VIDEO + "Cannot create video stream in AVI file. Make sure the selected codec supports input in RGB24 color space!" + IDS_AVI_CANNOT_CREATE_AUDIO "Cannot create audio stream in AVI file." + IDS_AVI_CANNOT_WRITE_VIDEO "Cannot write video frame to AVI file." + IDS_AVI_CANNOT_WRITE_AUDIO "Cannot write audio frame to AVI file." + IDS_FILTER_GBCROM "Game Boy Color ROMs (*.GBC;*.CGB;*.ZIP;*.7Z;*.Z;*.GZ)_*.GBC;*.CGB;*.ZIP;*.7Z;*.Z;*.GZ__" + IDS_COM_FAILURE "The COM (Component Object Model) failed to initialize!" + IDS_XAUDIO2_FAILURE "The XAudio2 interface failed to initialize!" + IDS_XAUDIO2_CANNOT_CREATE_MASTERINGVOICE + "XAudio2: Creating mastering voice failed!" + IDS_XAUDIO2_CANNOT_CREATE_SOURCEVOICE + "XAudio2: Creating source voice failed!" + IDS_XAUDIO2_CANNOT_ENUMERATE_DEVICES + "XAudio2: Enumerating devices failed!" + IDS_CHEATS_DISABLED "Cheats disabled" + IDS_CHEATS_ENABLED "Cheats enabled" +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "res\VBA.rc2" // non-Microsoft Visual C++ edited resource +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/win32/WavWriter.cpp b/src/win32/WavWriter.cpp new file mode 100644 index 0000000..0e9a0f6 --- /dev/null +++ b/src/win32/WavWriter.cpp @@ -0,0 +1,95 @@ +#include "stdafx.h" +#include "vba.h" +#include "WavWriter.h" + +#include "../System.h" +#include "../Util.h" + +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +WavWriter::WavWriter() +{ + m_file = NULL; + m_len = 0; + m_posSize = 0; +} + +WavWriter::~WavWriter() +{ + if(m_file) + Close(); +} + +void WavWriter::Close() +{ + // calculate the total file length + u32 len = ftell(m_file)-8; + fseek(m_file, 4, SEEK_SET); + u8 data[4]; + utilPutDword(data, len); + fwrite(data, 1, 4, m_file); + // write out the size of the data section + fseek(m_file, m_posSize, SEEK_SET); + utilPutDword(data, m_len); + fwrite(data, 1, 4, m_file); + fclose(m_file); + m_file = NULL; +} + +bool WavWriter::Open(const char *name) +{ + if(m_file) + Close(); + m_file = fopen(name, "wb"); + + if(!m_file) + return false; + // RIFF header + u8 data[4] = { 'R', 'I', 'F', 'F' }; + fwrite(data, 1, 4, m_file); + utilPutDword(data, 0); + // write 0 for now. Will get filled during close + fwrite(data, 1, 4, m_file); + // write WAVE header + u8 data2[4] = { 'W', 'A', 'V', 'E' }; + fwrite(data2, 1, 4, m_file); + return true; +} + +void WavWriter::SetFormat(const WAVEFORMATEX *format) +{ + if(m_file == NULL) + return; + // write fmt header + u8 data[4] = { 'f', 'm', 't', ' ' }; + fwrite(data, 1, 4, m_file); + u32 value = sizeof(WAVEFORMATEX); + utilPutDword(data, value); + fwrite(data, 1, 4, m_file); + fwrite(format, 1, sizeof(WAVEFORMATEX), m_file); + // start data header + u8 data2[4] = { 'd', 'a', 't', 'a' }; + fwrite(data2, 1, 4, m_file); + + m_posSize = ftell(m_file); + // write 0 for data chunk size. Filled out during Close() + utilPutDword(data, 0); + fwrite(data, 1, 4, m_file); +} + +void WavWriter::AddSound(const u8 *data, int len) +{ + if(m_file == NULL) + return; + // write a block of sound data + fwrite(data, 1, len, m_file); + m_len += len; +} diff --git a/src/win32/WavWriter.h b/src/win32/WavWriter.h new file mode 100644 index 0000000..1738c57 --- /dev/null +++ b/src/win32/WavWriter.h @@ -0,0 +1,29 @@ +#if !defined(AFX_WAVWRITER_H__BE6C9DE9_60E7_4192_9797_8C7F55B3CE46__INCLUDED_) +#define AFX_WAVWRITER_H__BE6C9DE9_60E7_4192_9797_8C7F55B3CE46__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include + +class WavWriter +{ + private: + FILE *m_file; + int m_len; + long m_posSize; + + public: + WavWriter(); + ~WavWriter(); + + bool Open(const char *name); + void SetFormat(const WAVEFORMATEX *format); + void AddSound(const u8 *data, int len); + + private: + void Close(); +}; + +#endif // !defined(AFX_WAVWRITER_H__BE6C9DE9_60E7_4192_9797_8C7F55B3CE46__INCLUDED_) diff --git a/src/win32/WinHelper.h b/src/win32/WinHelper.h new file mode 100644 index 0000000..a4ae88a --- /dev/null +++ b/src/win32/WinHelper.h @@ -0,0 +1,232 @@ +/*---------------------------------------------------------------------- + Copyright (c) 1998 Gipsysoft. All Rights Reserved. + Please see the file "licence.txt" for licencing details. + File: WinHelper.h + Owner: russf@gipsysoft.com + Purpose: Windows helper functions, classes, structures and macros + that make life a little easier + These should all be zero impact classes etc. that is they + should *not* have a cpp file associated with them. + ----------------------------------------------------------------------*/ +#ifndef WINHELPER_H +#define WINHELPER_H + +//#ifndef DEBUGHLP_H +// #include +//#endif // DEBUGHLP_H + +#ifndef FASTCALL +#define FASTCALL +#endif // FASTCALL + +extern void AssertFailed(char *, int, char *); +extern void ApiFailure(char *, int, char *); + +#define R_VERIFY(a) R_ASSERT(a) +#define R_ASSERT(a) \ + do {\ + if(!(a)) {\ + AssertFailed(__FILE__, __LINE__, #a);\ + }\ + } while(0); + +#define VAPI(a) \ + do { \ + if(!(a)) {\ + ApiFailure(__FILE__, __LINE__, #a); \ + }\ + } while (0); + +#define ASSERT_VALID_HWND(a) ASSERT( ::IsWindow(a) ) + +namespace WinHelper +{ + + class CSize : public tagSIZE + // + // Wrapper for the SIZE structure + { + public: + inline CSize() {}; + inline explicit CSize( const SIZE &size ) { cx = size.cx; cy = size.cy; } + inline explicit CSize( long nSizeX, long nSizeY ) { cx = nSizeX; cy = nSizeY; } + inline void Set( long nSizeX, long nSizeY ) { cx = nSizeX; cy = nSizeY; } + inline operator LPSIZE() { return this; }; + + inline bool operator !=( const SIZE &size ) const { return cx != size.cx || cy != size.cy;} + inline CSize & operator =( const SIZE &size ) { cx = size.cx; cy = size.cy; return *this; } + inline void Empty() { cx = cy = 0; } + }; + + + class CRect : public tagRECT + // + // Wrapper for a RECT structure + { + public: + inline CRect() {} + // Initialisation constructor + inline explicit CRect( const RECT& rhs ) { Set( rhs.left, rhs.top, rhs.right, rhs.bottom );} + inline CRect(int xLeft, int yTop, int xRight, int yBottom) { Set( xLeft, yTop, xRight, yBottom ); } + // Get the width of the rectangle + inline int Width() const { return right - left; } + // Get the height of the rectangle + inline int Height() const { return bottom - top; } + // overloaded operator so you don't have to do &rc anymore + inline operator LPCRECT() const { return this; }; + inline operator LPRECT() { return this; }; + // Return the SIZE of the rectangle; + inline CSize Size() const { CSize s( Width(), Height() ); return s; } + // Return the top left of the rectangle + inline POINT TopLeft() const { POINT pt = { left, top }; return pt; } + // Return the bottom right of the rectangle + inline POINT BottomRight() const { POINT pt = { right, bottom }; return pt; } + // Set the rectangles left, top, right and bottom + inline void Set( int xLeft, int yTop, int xRight, int yBottom) { top = yTop; bottom = yBottom; right = xRight; left = xLeft; } + // Return true if the rectangle contains all zeros + inline bool IsEmpty() const { return left == 0 && right == 0 && top == 0 && bottom == 0 ? true : false; } + // Zero out our rectangle + inline void Empty() { left = right = top = bottom = 0; } + // Set the size of the rect but leave the top left position untouched. + inline void SetSize( const CSize &size ) { bottom = top + size.cy; right = left + size.cx; } + inline void SetSize( const SIZE &size ) { bottom = top + size.cy; right = left + size.cx; } + inline void SetSize( int cx, int cy ) { bottom = top + cy; right = left + cx; } + // Move the rectangle by an offset + inline void Offset( int cx, int cy ) + { + top+=cy; + bottom+=cy; + right+=cx; + left+=cx; + } + // Inflate the rectangle by the cx and cy, use negative to shrink the rectangle + inline void Inflate( int cx, int cy ) + { + top-=cy; + bottom+=cy; + right+=cx; + left-=cx; + } + // Assignment from a RECT + inline CRect &operator = ( const RECT&rhs ) + { + left = rhs.left; top = rhs.top; + right = rhs.right; bottom = rhs.bottom; + return *this; + } + + // Return true if the point passed is within the rectangle + inline bool PtInRect( const POINT &pt ) const { return ( pt.x >= left && pt.x < right && pt.y >=top && pt.y < bottom ); } + // Return true if the rectangle passed overlaps this rectangle + inline bool Intersect( const RECT &rc ) const { return ( rc.left < right && rc.right > left && rc.top < bottom && rc.bottom > top ); } + }; + + + class CPoint : public tagPOINT + // + // Wrapper for the POINT structure + { + public: + inline CPoint() {}; + inline CPoint( LPARAM lParam ) { x = LOWORD( lParam ); y = HIWORD(lParam); } + inline CPoint( int nX, int nY ) { x = nX; y = nY; } + inline CPoint( const POINT &pt ) { x = pt.x; y = pt.y; } + inline bool operator == ( const CPoint &rhs ) const { return x == rhs.x && y == rhs.y; } + inline bool operator != ( const CPoint &rhs ) const { return x != rhs.x || y != rhs.y; } + inline operator LPPOINT () { return this; } + }; + + + class CScrollInfo : public tagSCROLLINFO + { + public: + CScrollInfo( UINT fPassedMask ) { cbSize = sizeof( tagSCROLLINFO ); fMask = fPassedMask; } + }; + + + class CCriticalSection + // + // Simple crtical section handler/wrapper + { + public: + inline CCriticalSection() { ::InitializeCriticalSection(&m_sect); } + inline ~CCriticalSection() { ::DeleteCriticalSection(&m_sect); } + + // Blocking lock. + inline void Lock() { ::EnterCriticalSection(&m_sect); } + // Unlock + inline void Unlock() { ::LeaveCriticalSection(&m_sect); } + + class CLock + // + // Simple lock class for the critcal section + { + public: + inline CLock( CCriticalSection § ) : m_sect( sect ) { m_sect.Lock(); } + inline ~CLock() { m_sect.Unlock(); } + private: + CCriticalSection &m_sect; + + CLock(); + CLock( const CLock &); + CLock& operator =( const CLock &); + }; + + private: + CRITICAL_SECTION m_sect; + + CCriticalSection( const CCriticalSection & ); + CCriticalSection& operator =( const CCriticalSection & ); + }; + + +#define ZeroStructure( t ) ZeroMemory( &t, sizeof( t ) ) +#define countof( t ) (sizeof( (t) ) / sizeof( (t)[0] ) ) +#define UNREF(P) UNREFERENCED_PARAMETER(P) + + inline bool IsShiftPressed() + { + return GetKeyState(VK_SHIFT) & 0x8000 ? true : false; + } + + inline bool IsAltPressed() + { + return GetKeyState(VK_MENU) & 0x8000 ? true : false; + } + + inline bool IsControlPressed() + { + return GetKeyState(VK_CONTROL) & 0x8000 ? true : false; + } + + inline HICON LoadIcon16x16( HINSTANCE hInst, UINT uID ) + // + // Load a 16x16 icon from the same resource as the other size icons. + { + return reinterpret_cast( ::LoadImage( hInst, MAKEINTRESOURCE( uID ), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR ) ); + } + + + class CDeferWindowPos + // + // Wrapper for the Begin, Defer and End WindowPos functions. Nothing glamorous. + { + public: + inline CDeferWindowPos( const int nWindows = 1 ) : m_hdlDef( ::BeginDeferWindowPos( nWindows ) ) {} + inline ~CDeferWindowPos() { R_VERIFY( ::EndDeferWindowPos( m_hdlDef ) ); } + inline HDWP DeferWindowPos( HWND hWnd, HWND hWndInsertAfter , int x, int y, int cx, int cy, UINT uFlags ) + { + return ::DeferWindowPos( m_hdlDef, hWnd, hWndInsertAfter, x, y, cx, cy, uFlags ); + } + inline HDWP DeferWindowPos( HWND hWnd, HWND hWndInsertAfter, const CRect &rc, UINT uFlags ) + { + return ::DeferWindowPos( m_hdlDef, hWnd, hWndInsertAfter, rc.left, rc.top, rc.Width(), rc.Height(), uFlags ); + } + + private: + HDWP m_hdlDef; + }; + +} // WinHelper + +#endif //WINHELPER_H diff --git a/src/win32/WinResUtil.cpp b/src/win32/WinResUtil.cpp new file mode 100644 index 0000000..e432875 --- /dev/null +++ b/src/win32/WinResUtil.cpp @@ -0,0 +1,91 @@ +#include "stdafx.h" + +static HINSTANCE winResGetInstance(LPCTSTR resType, LPCTSTR resName) +{ + // TODO: make language DLL first + return AfxFindResourceHandle(resName, resType); +} + + +UCHAR *winResGetResource(LPCTSTR resType, LPCTSTR resName) +{ + HINSTANCE winResInstance = winResGetInstance(resType, resName); + + HRSRC hRsrc = FindResourceEx(winResInstance, resType, resName, 0); + + if(hRsrc != NULL) { + HGLOBAL hGlobal = LoadResource(winResInstance, hRsrc); + + if(hGlobal != NULL) { + UCHAR * b = (UCHAR *)LockResource(hGlobal); + + return b; + } + } + return NULL; +} + +HMENU winResLoadMenu(LPCTSTR menuName) +{ + UCHAR * b = winResGetResource(RT_MENU, menuName); + + if(b != NULL) { + HMENU menu = LoadMenuIndirect((CONST MENUTEMPLATE *)b); + + if(menu != NULL) + return menu; + } + + return LoadMenu(NULL, menuName); +} + +int winResDialogBox(LPCTSTR boxName, + HWND parent, + DLGPROC dlgProc, + LPARAM lParam) +{ + /* + UCHAR * b = winResGetResource(RT_DIALOG, boxName); + + if(b != NULL) { + + return DialogBoxIndirectParam(hInstance, + (LPCDLGTEMPLATE)b, + parent, + dlgProc, + lParam); + } + + return DialogBoxParam(hInstance, + boxName, + parent, + dlgProc, + lParam); + */ + return 0; +} + +int winResDialogBox(LPCTSTR boxName, + HWND parent, + DLGPROC dlgProc) +{ + return winResDialogBox(boxName, + parent, + dlgProc, + 0); +} + +CString winResLoadString(UINT id) +{ + int stId = id / 16 + 1; + HINSTANCE inst = winResGetInstance(RT_STRING, MAKEINTRESOURCE(stId)); + + CString res; + if(res.LoadString(id)) + return res; + + // TODO: handle case where string is only in the default English + res = ""; + + return res; +} diff --git a/src/win32/WinResUtil.h b/src/win32/WinResUtil.h new file mode 100644 index 0000000..5b79cbd --- /dev/null +++ b/src/win32/WinResUtil.h @@ -0,0 +1,12 @@ +extern HMENU winResLoadMenu(LPCTSTR menuName); + +extern int winResDialogBox(LPCTSTR boxName, + HWND parent, + DLGPROC dlgProc); + +extern int winResDialogBox(LPCTSTR boxName, + HWND parent, + DLGPROC dlgProc, + LPARAM lParam); + +extern CString winResLoadString(UINT id); diff --git a/src/win32/XAudio2.cpp b/src/win32/XAudio2.cpp new file mode 100644 index 0000000..0541d7f --- /dev/null +++ b/src/win32/XAudio2.cpp @@ -0,0 +1,539 @@ +#ifndef NO_XAUDIO2 + +// MFC +#include "stdafx.h" + +// Application +#include "VBA.h" + +// Interface +#include "../common/SoundDriver.h" + +// XAudio2 +#include + +// MMDevice API +#include +#include +#include + +// Internals +#include "../System.h" // for systemMessage() +#include "../gba/Globals.h" + + +class XAudio2_Output; + +static void xaudio2_device_changed( XAudio2_Output * ); + +class XAudio2_Device_Notifier : public IMMNotificationClient +{ + volatile LONG registered; + IMMDeviceEnumerator *pEnumerator; + + std::wstring last_device; + + CRITICAL_SECTION lock; + std::vector instances; + +public: + XAudio2_Device_Notifier() : registered( 0 ) + { + InitializeCriticalSection( &lock ); + } + ~XAudio2_Device_Notifier() + { + DeleteCriticalSection( &lock ); + } + + ULONG STDMETHODCALLTYPE AddRef() + { + return 1; + } + + ULONG STDMETHODCALLTYPE Release() + { + return 1; + } + + HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, VOID **ppvInterface ) + { + if (IID_IUnknown == riid) + { + *ppvInterface = (IUnknown*)this; + } + else if (__uuidof(IMMNotificationClient) == riid) + { + *ppvInterface = (IMMNotificationClient*)this; + } + else + { + *ppvInterface = NULL; + return E_NOINTERFACE; + } + return S_OK; + } + + HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged( EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId ) + { + if ( flow == eRender && last_device.compare( pwstrDeviceId ) != 0 ) + { + last_device = pwstrDeviceId; + + EnterCriticalSection( &lock ); + for ( auto it = instances.begin(); it < instances.end(); ++it ) + { + xaudio2_device_changed( *it ); + } + LeaveCriticalSection( &lock ); + } + + return S_OK; + } + + HRESULT STDMETHODCALLTYPE OnDeviceAdded( LPCWSTR pwstrDeviceId ) { return S_OK; } + HRESULT STDMETHODCALLTYPE OnDeviceRemoved( LPCWSTR pwstrDeviceId ) { return S_OK; } + HRESULT STDMETHODCALLTYPE OnDeviceStateChanged( LPCWSTR pwstrDeviceId, DWORD dwNewState ) { return S_OK; } + HRESULT STDMETHODCALLTYPE OnPropertyValueChanged( LPCWSTR pwstrDeviceId, const PROPERTYKEY key ) { return S_OK; } + + void do_register(XAudio2_Output * p_instance) + { + if ( InterlockedIncrement( ®istered ) == 1 ) + { + pEnumerator = NULL; + HRESULT hr = CoCreateInstance( __uuidof( MMDeviceEnumerator ), NULL, CLSCTX_INPROC_SERVER, __uuidof( IMMDeviceEnumerator ), ( void** ) &pEnumerator ); + if ( SUCCEEDED( hr ) ) + { + pEnumerator->RegisterEndpointNotificationCallback( this ); + } + } + + EnterCriticalSection( &lock ); + instances.push_back( p_instance ); + LeaveCriticalSection( &lock ); + } + + void do_unregister( XAudio2_Output * p_instance ) + { + if ( InterlockedDecrement( ®istered ) == 0 ) + { + if (pEnumerator) + { + pEnumerator->UnregisterEndpointNotificationCallback( this ); + pEnumerator->Release(); + pEnumerator = NULL; + } + } + + EnterCriticalSection( &lock ); + for ( auto it = instances.begin(); it < instances.end(); ++it ) + { + if ( *it == p_instance ) + { + instances.erase( it ); + break; + } + } + LeaveCriticalSection( &lock ); + } +} g_notifier; + +// 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(); + void close(); + void device_change(); + + // Configuration Changes + void setThrottle( unsigned short throttle ); + +private: + bool failed; + bool initialized; + bool playing; + UINT32 freq; + UINT32 bufferCount; + BYTE *buffers; + int currentBuffer; + int soundBufferLen; + + volatile bool device_changed; + + 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 = theApp.xa2BufferCount; + buffers = NULL; + currentBuffer = 0; + device_changed = false; + + xaud = NULL; + mVoice = NULL; + sVoice = NULL; + ZeroMemory( &buf, sizeof( buf ) ); + ZeroMemory( &vState, sizeof( vState ) ); + + g_notifier.do_register( this ); +} + + +XAudio2_Output::~XAudio2_Output() +{ + g_notifier.do_unregister( this ); + close(); +} + +void XAudio2_Output::close() +{ + initialized = false; + + if( sVoice ) { + if( playing ) { + HRESULT hr = sVoice->Stop( 0 ); + ASSERT( hr == S_OK ); + } + sVoice->DestroyVoice(); + sVoice = NULL; + } + + if( buffers ) { + free( buffers ); + buffers = NULL; + } + + if( mVoice ) { + mVoice->DestroyVoice(); + mVoice = NULL; + } + + if( xaud ) { + xaud->Release(); + xaud = NULL; + } +} + +void XAudio2_Output::device_change() +{ + device_changed = true; +} + + +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 ) { + systemMessage( IDS_XAUDIO2_FAILURE, NULL ); + 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, + theApp.xa2Device, + NULL ); + if( hr != S_OK ) { + systemMessage( IDS_XAUDIO2_CANNOT_CREATE_MASTERINGVOICE, NULL ); + failed = true; + return false; + } + + + // create sound emitter + hr = xaud->CreateSourceVoice( &sVoice, &wfx, 0, 4.0f, ¬ify ); + if( hr != S_OK ) { + systemMessage( IDS_XAUDIO2_CANNOT_CREATE_SOURCEVOICE, NULL ); + failed = true; + return false; + } + + + if( theApp.xa2Upmixing ) { + // 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; + + currentBuffer = 0; + device_changed = false; + + initialized = true; + return true; +} + + +void XAudio2_Output::write(u16 * finalWave, int length) +{ + if( !initialized || failed ) return; + + while( true ) { + if ( device_changed ) { + close(); + if (!init(freq)) return; + } + + 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 && !theApp.throttle ) { + // wait for one buffer to finish playing + if (WaitForSingleObject( notify.hBufferEndEvent, 10000 ) == WAIT_TIMEOUT) { + device_changed = true; + } + } 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 ); +} + +void xaudio2_device_changed( XAudio2_Output * instance ) +{ + instance->device_change(); +} + +SoundDriver *newXAudio2_Output() +{ + return new XAudio2_Output(); +} + + +#endif // #ifndef NO_XAUDIO2 diff --git a/src/win32/XAudio2_Config.cpp b/src/win32/XAudio2_Config.cpp new file mode 100644 index 0000000..4a8b45d --- /dev/null +++ b/src/win32/XAudio2_Config.cpp @@ -0,0 +1,151 @@ +#include "stdafx.h" +#ifndef NO_XAUDIO2 +#include "VBA.h" + +#include "XAudio2_Config.h" + +#include + + +// XAudio2_Config dialog + +IMPLEMENT_DYNAMIC(XAudio2_Config, CDialog) + +XAudio2_Config::XAudio2_Config(CWnd* pParent /*=NULL*/) + : CDialog(XAudio2_Config::IDD, pParent) + , m_selected_device_index(0) + , m_enable_upmixing(false) + , m_buffer_count(0) +{ +} + +XAudio2_Config::~XAudio2_Config() +{ +} + +void XAudio2_Config::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + DDX_Control(pDX, IDC_COMBO_DEV, m_combo_dev); + DDX_Control(pDX, IDC_SLIDER_BUFFER, m_slider_buffer); + DDX_Control(pDX, IDC_INFO_BUFFER, m_info_buffer); + DDX_Control(pDX, IDC_CHECK_UPMIX, m_check_upmix); + + if( pDX->m_bSaveAndValidate == TRUE ) { + if( CB_ERR != m_combo_dev.GetCurSel() ) { + if( CB_ERR != m_combo_dev.GetItemData( m_combo_dev.GetCurSel() ) ) { + m_selected_device_index = m_combo_dev.GetItemData( m_combo_dev.GetCurSel() ); + } + } + + m_enable_upmixing = ( m_check_upmix.GetCheck() == BST_CHECKED ); + + m_buffer_count = (UINT32)m_slider_buffer.GetPos(); + } else { + m_check_upmix.SetCheck( m_enable_upmixing ? BST_CHECKED : BST_UNCHECKED ); + } +} + + +BEGIN_MESSAGE_MAP(XAudio2_Config, CDialog) + ON_WM_HSCROLL() +END_MESSAGE_MAP() + + +// XAudio2_Config message handlers + +BOOL XAudio2_Config::OnInitDialog() +{ + CDialog::OnInitDialog(); + + m_combo_dev.ResetContent(); + + m_slider_buffer.SetRange( 2, 10, FALSE ); + m_slider_buffer.SetTicFreq( 1 ); + m_slider_buffer.SetPos( (int)m_buffer_count ); + + CString info; + int pos = m_slider_buffer.GetPos(); + info.Format( _T("%i frames = %.2f ms"), pos, (float)pos / 60.0f * 1000.0f ); + m_info_buffer.SetWindowText( info ); + + HRESULT hr; + IXAudio2 *xa = NULL; + UINT32 flags = 0; +#ifdef _DEBUG + flags = XAUDIO2_DEBUG_ENGINE; +#endif + + hr = XAudio2Create( &xa, flags ); + if( hr != S_OK ) { + systemMessage( IDS_XAUDIO2_FAILURE, NULL ); + } else { + UINT32 dev_count = 0; + hr = xa->GetDeviceCount( &dev_count ); + if( hr != S_OK ) { + systemMessage( IDS_XAUDIO2_CANNOT_ENUMERATE_DEVICES, NULL ); + } else { + XAUDIO2_DEVICE_DETAILS dd; + for( UINT32 i = 0; i < dev_count; i++ ) { + hr = xa->GetDeviceDetails( i, &dd ); + if( hr != S_OK ) { + continue; + } else { +#ifdef _UNICODE + int id = m_combo_dev.AddString( dd.DisplayName ); +#else + CHAR temp[256]; + ZeroMemory( temp, sizeof( temp ) ); + WideCharToMultiByte( + CP_ACP, + WC_NO_BEST_FIT_CHARS, + dd.DisplayName, + -1, + temp, + sizeof( temp ) - 1, + NULL, + NULL ); + + int id = m_combo_dev.AddString( temp ); +#endif + if( id < 0 ) { + systemMessage( IDS_XAUDIO2_CANNOT_ENUMERATE_DEVICES, NULL ); + break; + } else { + m_combo_dev.SetItemData( id, i ); + } + } + } + + // select the currently configured device { + int count = m_combo_dev.GetCount(); + if( count > 0 ) { + for( int i = 0; i < count; i++ ) { + if( m_combo_dev.GetItemData( i ) == m_selected_device_index ) { + m_combo_dev.SetCurSel( i ); + break; + } + } + } + // } + + } + xa->Release(); + xa = NULL; + } + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void XAudio2_Config::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) +{ + CString info; + int pos = m_slider_buffer.GetPos(); + info.Format( _T("%i frames = %.2f ms"), pos, (float)pos / 60.0f * 1000.0f ); + m_info_buffer.SetWindowText( info ); + + CDialog::OnHScroll(nSBCode, nPos, pScrollBar); +} + +#endif diff --git a/src/win32/XAudio2_Config.h b/src/win32/XAudio2_Config.h new file mode 100644 index 0000000..4614baf --- /dev/null +++ b/src/win32/XAudio2_Config.h @@ -0,0 +1,37 @@ +#ifndef NO_XAUDIO2 + +#pragma once + + +class XAudio2_Config : public CDialog +{ + DECLARE_DYNAMIC(XAudio2_Config) + DECLARE_MESSAGE_MAP() + +public: + UINT32 m_selected_device_index; + UINT32 m_buffer_count; + bool m_enable_upmixing; + +private: + CComboBox m_combo_dev; + CSliderCtrl m_slider_buffer; + CStatic m_info_buffer; + CButton m_check_upmix; + + +public: + XAudio2_Config(CWnd* pParent = NULL); // standard constructor + virtual ~XAudio2_Config(); + +// Dialog Data + enum { IDD = IDD_XAUDIO2_CONFIG }; + + virtual BOOL OnInitDialog(); + afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support +}; + +#endif diff --git a/src/win32/ZoomControl.cpp b/src/win32/ZoomControl.cpp new file mode 100644 index 0000000..d5c9bb1 --- /dev/null +++ b/src/win32/ZoomControl.cpp @@ -0,0 +1,169 @@ +#include "stdafx.h" +#include "vba.h" +#include "ZoomControl.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +bool ZoomControl::isRegistered = false; + +///////////////////////////////////////////////////////////////////////////// +// ZoomControl + +ZoomControl::ZoomControl() +{ + ZeroMemory(colors, 3*64); + selected = -1; + registerClass(); +} + +ZoomControl::~ZoomControl() +{ +} + + +BEGIN_MESSAGE_MAP(ZoomControl, CWnd) + //{{AFX_MSG_MAP(ZoomControl) + ON_WM_PAINT() + ON_WM_LBUTTONDOWN() + ON_WM_ERASEBKGND() + //}}AFX_MSG_MAP + END_MESSAGE_MAP() + + + ///////////////////////////////////////////////////////////////////////////// +// ZoomControl message handlers + +void ZoomControl::registerClass() +{ + if(!isRegistered) { + WNDCLASS wc; + ZeroMemory(&wc, sizeof(wc)); + wc.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS; + wc.lpfnWndProc = (WNDPROC)::DefWindowProc; + wc.hInstance = AfxGetInstanceHandle(); + wc.hIcon = NULL; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH )GetStockObject(BLACK_BRUSH); + wc.lpszMenuName = NULL; + wc.lpszClassName = "VbaZoomControl"; + AfxRegisterClass(&wc); + isRegistered = true; + } +} + +void ZoomControl::OnPaint() +{ + CPaintDC dc(this); // device context for painting + + RECT rect; + GetClientRect(&rect); + + int w = rect.right - rect.left; + int h = rect.bottom - rect.top; + + CDC memDC ; + memDC.CreateCompatibleDC(&dc); + CBitmap bitmap, *pOldBitmap; + bitmap.CreateCompatibleBitmap(&dc, w, h); + + pOldBitmap = memDC.SelectObject(&bitmap); + + int multX = w / 8; + int multY = h / 8; + + int i; + for(i = 0; i < 64; i++) { + CBrush b; + b.CreateSolidBrush(RGB(colors[i*3+2], colors[i*3+1], colors[i*3])); + + RECT r; + int x = i & 7; + int y = i / 8; + r.top = y*multY; + r.left = x*multX; + r.bottom = r.top + multY; + r.right = r.left + multX; + memDC.FillRect(&r, &b); + b.DeleteObject(); + } + + CPen pen; + pen.CreatePen(PS_SOLID, 1, RGB(192,192,192)); + CPen *old = (CPen *)memDC.SelectObject(&pen); + + for(i = 0; i < 8; i++) { + memDC.MoveTo(0, i * multY); + memDC.LineTo(w, i * multY); + memDC.MoveTo(i * multX, 0); + memDC.LineTo(i * multX, h); + } + + if(selected != -1) { + CPen pen2; + pen2.CreatePen(PS_SOLID, 2, RGB(255, 0, 0)); + CPen *old2 = (CPen*)memDC.SelectObject(&pen2); + + int startX = (selected & 7)*multX+1; + int startY = (selected / 8)*multY+1; + int endX = startX + multX-2; + int endY = startY + multY-2; + + memDC.MoveTo(startX, startY); + memDC.LineTo(endX, startY); + memDC.LineTo(endX, endY); + memDC.LineTo(startX, endY); + memDC.LineTo(startX, startY-1); + memDC.SelectObject(old2); + pen2.DeleteObject(); + } + memDC.SelectObject(old); + pen.DeleteObject(); + + dc.BitBlt(0,0,w,h, + &memDC,0,0, SRCCOPY); + + memDC.SelectObject(pOldBitmap); + bitmap.DeleteObject(); + memDC.DeleteDC(); +} + +void ZoomControl::OnLButtonDown(UINT nFlags, CPoint point) +{ + RECT rect; + GetClientRect(&rect); + + int height = rect.bottom - rect.top; + int width = rect.right - rect.left; + + int multX = width / 8; + int multY = height / 8; + + selected = point.x / multX + 8 * (point.y / multY); + + int c = point.x / multX + 8 * (point.y/multY); + u16 color = colors[c*3] << 7 | + colors[c*3+1] << 2 | + (colors[c*3+2] >> 3); + + GetParent()->PostMessage(WM_COLINFO, + color, + 0); + + Invalidate(); +} + +BOOL ZoomControl::OnEraseBkgnd(CDC* pDC) +{ + return TRUE; +} + +void ZoomControl::setColors(const u8 *c) +{ + memcpy(colors, c, 3*64); + selected = -1; + Invalidate(); +} diff --git a/src/win32/ZoomControl.h b/src/win32/ZoomControl.h new file mode 100644 index 0000000..fedb548 --- /dev/null +++ b/src/win32/ZoomControl.h @@ -0,0 +1,59 @@ +#if !defined(AFX_ZOOMCONTROL_H__BC193230_D2D6_4240_93AE_28C2EF2C641A__INCLUDED_) +#define AFX_ZOOMCONTROL_H__BC193230_D2D6_4240_93AE_28C2EF2C641A__INCLUDED_ + +#include "../System.h" // Added by ClassView +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// ZoomControl.h : header file +// +#ifndef WM_COLINFO +#define WM_COLINFO WM_APP+100 +#endif + +///////////////////////////////////////////////////////////////////////////// +// ZoomControl window + +class ZoomControl : public CWnd +{ + // Construction + public: + ZoomControl(); + + // Attributes + public: + + // Operations + public: + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(ZoomControl) + //}}AFX_VIRTUAL + + // Implementation + public: + void setColors(const u8 *c); + static bool isRegistered; + virtual ~ZoomControl(); + + // Generated message map functions + protected: + //{{AFX_MSG(ZoomControl) + afx_msg void OnPaint(); + afx_msg void OnLButtonDown(UINT nFlags, CPoint point); + afx_msg BOOL OnEraseBkgnd(CDC* pDC); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + private: + int selected; + u8 colors[3*64]; + void registerClass(); +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_ZOOMCONTROL_H__BC193230_D2D6_4240_93AE_28C2EF2C641A__INCLUDED_) diff --git a/src/win32/glfont.c b/src/win32/glfont.c new file mode 100644 index 0000000..4cf7bbb --- /dev/null +++ b/src/win32/glfont.c @@ -0,0 +1,156 @@ +#ifndef NO_OGL + +//********************************************************* +//GLFONT.CPP -- glFont routines +//Copyright (c) 1998 Brad Fish +//See glFont.txt for terms of use +//November 10, 1998 +//********************************************************* + +#include +#include +#include +#include +#include +#include "glfont.h" + +//********************************************************* +//Variables +//********************************************************* + +//Current font +GLFONT *glFont; + +//********************************************************* +//Functions +//********************************************************* +int glFontCreate (GLFONT *Font, char *Buffer, int Tex) +{ + char *TexBytes; + int Num; + + //Read glFont structure + memcpy(Font, Buffer, sizeof(GLFONT)); + Buffer+=sizeof(GLFONT); + + //Save texture number + Font->Tex = Tex; + + //Get number of characters + Num = Font->IntEnd - Font->IntStart + 1; + + //Allocate memory for characters + if ((Font->Char = (GLFONTCHAR *)malloc( + sizeof(GLFONTCHAR) * Num)) == NULL) + return FALSE; + + //Read glFont characters + memcpy(Font->Char, Buffer, sizeof(GLFONTCHAR)*Num); + Buffer+=sizeof(GLFONTCHAR)*Num; + + //Get texture size + Num = Font->TexWidth * Font->TexHeight * 2; + + //Allocate memory for texture data + if ((TexBytes = (char *)malloc(Num)) == NULL) + return FALSE; + + //Read texture data + memcpy(TexBytes, Buffer, sizeof(char)*Num); + Buffer+=sizeof(char)*Num; + + //Set texture attributes + glBindTexture(GL_TEXTURE_2D, Font->Tex); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, + GL_CLAMP); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, + GL_CLAMP); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, + GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + GL_LINEAR); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, + GL_MODULATE); + + //Create texture + glTexImage2D(GL_TEXTURE_2D, 0, 2, Font->TexWidth, + Font->TexHeight, 0, GL_LUMINANCE_ALPHA, + GL_UNSIGNED_BYTE, (void *)TexBytes); + + //Clean up + free(TexBytes); + + //Return pointer to new font + return TRUE; +} +//********************************************************* +void glFontDestroy (GLFONT *Font) +{ + //Free character memory + free(Font->Char); +} +//********************************************************* +void glFontBegin (GLFONT *Font) +{ + //Save pointer to font structure + if (Font->Char != NULL) + glFont = Font; + else + glFont = NULL; + + //Bind to font texture + glBindTexture(GL_TEXTURE_2D, Font->Tex); +} +//********************************************************* +void glFontEnd (void) +{ + //Font no longer current + glFont = NULL; +} +//********************************************************* +void glFontTextOut (char *String, float x, float y, + float z) +{ + int Length, i; + GLFONTCHAR *Char; + + //Return if we don't have a valid glFont + if (glFont == NULL) + return; + + //Get length of string + Length = strlen(String); + + //Begin rendering quads + glBegin(GL_QUADS); + + //Loop through characters + for (i = 0; i < Length; i++) + { + //Get pointer to glFont character + Char = &glFont->Char[(int)String[i] - + glFont->IntStart]; + + //Specify vertices and texture coordinates + glTexCoord2f(Char->tx1, Char->ty1); + glVertex3f(x, y - Char->dy, z); + glTexCoord2f(Char->tx1, Char->ty2); + glVertex3f(x, y, z); + glTexCoord2f(Char->tx2, Char->ty2); + glVertex3f(x + Char->dx, y, z); + glTexCoord2f(Char->tx2, Char->ty1); + glVertex3f(x + Char->dx, y - Char->dy, z); + + //Move to next character + x += Char->dx; + } + + //Stop rendering quads + glEnd(); +} +//********************************************************* + +//End of file + + +#endif // NO_OGL diff --git a/src/win32/glfont.h b/src/win32/glfont.h new file mode 100644 index 0000000..a477ec2 --- /dev/null +++ b/src/win32/glfont.h @@ -0,0 +1,68 @@ +#pragma once + +#ifndef NO_OGL + +//********************************************************* +//GLFONT.H -- Header for GLFONT.CPP +//Copyright (c) 1998 Brad Fish +//See glFont.txt for terms of use +//November 10, 1998 +//********************************************************* + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +//********************************************************* +//Structures +//********************************************************* + +//glFont character structure +typedef struct +{ + float dx, dy; + float tx1, ty1; + float tx2, ty2; +} GLFONTCHAR; + +//glFont structure +typedef struct +{ + int Tex; + int TexWidth, TexHeight; + int IntStart, IntEnd; + GLFONTCHAR *Char; +} GLFONT; + +//********************************************************* +//Function Declarations +//********************************************************* +#ifdef __cplusplus +extern "C" { +#endif +//Creates a glFont +int glFontCreate(GLFONT *Font, char *Buffer, int Tex); + +//Deletes a glFont +void glFontDestroy (GLFONT *Font); + +//Needs to be called before text output +void glFontBegin (GLFONT *Font); + +//Needs to be called after text output +void glFontEnd (void); + +//Draws text with a glFont +void glFontTextOut (char *String, float x, float y, + float z); +//********************************************************* +#ifdef __cplusplus +} +#endif +//End of file + +#endif // NO_OGL diff --git a/src/win32/gzglfont.h b/src/win32/gzglfont.h new file mode 100644 index 0000000..30cc19e --- /dev/null +++ b/src/win32/gzglfont.h @@ -0,0 +1,263 @@ +#pragma once + +#ifndef NO_OGL + +#define GZGLFONT_SIZE 35096 + + +unsigned char gzglfont[]= + { + '\x1F', '\x8B', '\x08', '\x08', '\x72', '\x0F', '\x39', '\x47', + '\x00', '\x0B', '\x76', '\x65', '\x72', '\x64', '\x61', '\x6E', + '\x61', '\x00', '\xED', '\x97', '\xBF', '\x6B', '\x5C', '\x47', + '\x10', '\xC7', '\x2F', '\x21', '\x04', '\x27', '\x90', '\x20', + '\x82', '\x08', '\x21', '\x08', '\x21', '\x82', '\x62', '\x5C', + '\x19', '\x21', '\x8C', '\xAB', '\xDC', '\x7B', '\x42', '\x11', + '\x21', '\x0E', '\xC6', '\x1C', '\xAA', '\x0E', '\x05', '\x82', + '\x30', '\x42', '\xA8', '\x88', '\x85', '\x50', '\x11', '\x4C', + '\x9A', '\xB8', '\x70', '\x91', '\xC2', '\x85', '\x50', '\x95', + '\xC2', '\x85', '\x0A', '\x15', '\x29', '\x55', '\x08', '\xBB', + '\x49', '\xE1', '\x22', '\x4D', '\x7A', '\x17', '\x02', '\xBB', + '\x50', '\xE1', '\x22', '\x85', '\x0A', '\xFD', '\x01', '\x81', + '\xCB', '\xED', '\x7B', '\x73', '\x7E', '\xBB', '\xB3', '\x33', + '\xB3', '\xF3', '\x7E', '\xDC', '\xE9', '\x7C', '\xB7', '\x03', + '\xB2', '\xDF', '\x7D', '\x6E', '\xDE', '\xCE', '\x7C', '\x67', + '\x67', '\x7F', '\x5C', '\xAB', '\xD5', '\x6A', '\x3D', '\x82', + '\xBF', '\x85', '\xFE', '\xDF', '\x6F', '\xFD', '\xBF', '\x87', + '\x9F', '\x7C', '\xFB', '\xDE', '\xD7', '\x77', '\x3E', '\x4B', + '\xFB', '\x8F', '\x2B', '\xAD', '\xC2', '\xDA', '\xAD', '\xD6', + '\x79', '\x7B', '\xF7', '\xD5', '\x6D', '\xE0', '\x0B', '\xED', + '\x1C', '\x1F', '\x65', '\xBC', '\x3F', '\x02', '\xF0', '\x13', + '\xE0', '\x33', '\x49', '\xEE', '\xFF', '\x0C', '\xF8', '\x17', + '\x49', '\xCE', '\x77', '\x32', '\x7E', '\xE7', '\xF0', '\x18', + '\xF8', '\x1E', '\xF0', '\xA7', '\x19', '\x3F', '\x5C', '\xFC', + '\x68', '\xA5', '\xD7', '\xEB', '\xF5', '\xBF', '\x3B', '\x02', + '\xFE', '\x1A', '\x8D', '\x73', '\x0E', '\xFC', '\x83', '\xD4', + '\xF0', '\x22', '\xCF', '\x0F', '\xD3', '\x9C', '\x7F', '\x9A', + '\xBA', '\xF9', '\xCC', '\x00', '\x9F', '\x47', '\x7C', '\x01', + '\xF8', '\xCD', '\xD4', '\xCD', '\x67', '\x09', '\xF8', '\x6A', + '\xEA', '\xC6', '\x5D', '\x03', '\xDE', '\x45', '\x71', '\x37', + '\x80', '\xDF', '\xCF', '\xF8', '\xF3', '\x07', '\x3F', '\x02', + '\xDF', '\x02', '\xBE', '\x87', '\xFC', '\xF7', '\x81', '\xFF', + '\x0A', '\xFC', '\xD0', '\xAA', '\xF3', '\xA5', '\xC9', '\xAF', + '\x6D', '\x6A', '\x54', '\xE4', '\x73', '\xD0', '\xCE', '\xB9', + '\xA9', '\xA7', '\xCD', '\x4D', '\x3D', '\x0D', '\xDF', '\x44', + '\x7C', '\x07', '\xF8', '\x1F', '\x88', '\x3F', '\x05', '\xFE', + '\x02', '\xF1', '\xBF', '\x81', '\xFF', '\x8B', '\xF8', '\x05', + '\xF0', '\x6B', '\xA9', '\xCB', '\x3F', '\x4E', '\x73', '\x3E', + '\x87', '\xF8', '\x3C', '\xF0', '\x25', '\xC4', '\x97', '\x81', + '\xAF', '\x21', '\xFE', '\x1D', '\xF0', '\x6E', '\xC6', '\x8B', + '\xBE', '\xDA', '\x00', '\xBE', '\x85', '\xF8', '\x36', '\xF0', + '\x3D', '\xE0', '\xCF', '\xAC', '\xBA', '\x99', '\x1E', '\x32', + '\x7D', '\x78', '\x62', '\x71', '\xD3', '\x87', '\x86', '\xDF', + '\x48', '\x5C', '\xBE', '\x94', '\xE4', '\xFC', '\x51', '\xC6', + '\x8B', '\x7E', '\x78', '\x0C', '\xFC', '\x28', '\xE3', '\xCF', + '\x1F', '\xBC', '\x02', '\x7E', '\x0C', '\xFC', '\x65', '\xC6', + '\x8B', '\xFC', '\xCF', '\x80', '\xFF', '\x87', '\x78', '\x2B', + '\xCD', '\xB9', '\x99', '\x6F', '\x9B', '\xCF', '\x02', '\xBF', + '\x9E', '\xBA', '\xF9', '\xDC', '\x00', '\xFE', '\x4D', '\xC6', + '\x8B', '\x7E', '\x48', '\x80', '\xDF', '\x43', '\xBC', '\x03', + '\x7C', '\x13', '\x8D', '\x73', '\x1F', '\xF8', '\x2F', '\x88', + '\x1B', '\x3B', '\x85', '\xFA', '\x5C', '\x24', '\x45', '\x7F', + '\x9A', '\xFA', '\x18', '\x7E', '\x2D', '\xC9', '\xE7', '\xFA', + '\xED', '\x7A', '\x49', '\x72', '\xFE', '\x7D', '\xC6', '\x8B', + '\xFC', '\x3B', '\xC0', '\x9F', '\x64', '\xBC', '\xC8', '\xE7', + '\x00', '\xF8', '\x49', '\xC6', '\x0F', '\x17', '\xFF', '\x01', + '\x7E', '\x0A', '\xFC', '\x3C', '\xE3', '\x45', '\x3E', '\x6F', + '\x80', '\x9B', '\xF5', '\x6A', '\xFB', '\x9B', '\x3E', '\x33', + '\x7C', '\x3E', '\x75', '\xE3', '\x2E', '\x00', '\x5F', '\x46', + '\xFE', '\xB7', '\x80', '\xFF', '\x80', '\xFC', '\xEF', '\x02', + '\xFF', '\x09', '\xF1', '\x4D', '\xE0', '\xFB', '\xA9', '\x9B', + '\x7F', '\xB6', '\xEE', '\x92', '\x7C', '\xDD', '\xCD', '\xA6', + '\x45', '\x9E', '\xD9', '\xBA', '\x4B', '\xF2', '\x75', '\x36', + '\x9B', '\x5A', '\xF9', '\x24', '\x39', '\xDF', '\x49', '\xF2', + '\x39', '\xCD', '\xC6', '\x58', '\xC9', '\x6B', '\x7F', '\x09', + '\x75', '\xB0', '\xFD', '\x4F', '\x81', '\x9F', '\x25', '\xEE', + '\xF8', '\xAF', '\x81', '\xBF', '\x9F', '\x1A', '\x5E', '\xE4', + '\x63', '\xF6', '\xB5', '\x4B', '\x18', '\x7B', '\x36', '\x2D', + '\xE6', '\xEB', '\x73', '\xE0', '\x5F', '\x21', '\xFF', '\x45', + '\xE0', '\xB7', '\x90', '\xFF', '\x6D', '\xE0', '\xAB', '\xA9', + '\x1B', '\x77', '\x0D', '\x78', '\x37', '\x75', '\xF3', '\xDC', + '\x00', '\xFE', '\x33', '\xE2', '\xC6', '\x4C', '\x6C', '\x53', + '\x8F', '\x65', '\x2B', '\xAE', '\xE9', '\x27', '\xC3', '\x67', + '\x12', '\x97', '\x9B', '\x7A', '\x19', '\xDE', '\x85', '\x39', + '\x1B', '\xF4', '\xD5', '\x26', '\xF0', '\x27', '\xC8', '\xFF', + '\x00', '\xF8', '\x09', '\xE2', '\xA7', '\xC0', '\x5F', '\x66', + '\xBC', '\xD8', '\x07', '\xCE', '\x80', '\x5F', '\x20', '\xFF', + '\x4B', '\xE0', '\xA6', '\x8F', '\x6C', '\x6E', '\xF6', '\x2B', + '\xC3', '\xBF', '\x04', '\x3E', '\xD8', '\x87', '\xE7', '\x80', + '\x2F', '\xA6', '\xEE', '\xF8', '\xD7', '\x81', '\x2F', '\xA1', + '\x71', '\x96', '\x81', '\xAF', '\xA2', '\x71', '\xD6', '\x80', + '\xDF', '\xCD', '\x78', '\xB1', '\x6F', '\xDC', '\x03', '\xBE', + '\x8D', '\xC6', '\xD9', '\x01', '\xFE', '\x10', '\x71', '\x63', + '\x66', '\x0E', '\x4D', '\x1F', '\x76', '\xEC', '\xFA', '\xB4', + '\x73', '\x6E', '\xD6', '\xA9', '\xCD', '\x67', '\x60', '\xCE', + '\x3B', '\x19', '\x2F', '\xE6', '\xBD', '\x0B', '\xFC', '\x71', + '\x92', '\xEF', '\x15', '\x83', '\xFA', '\xFF', '\x0E', '\xFC', + '\x38', '\xE3', '\x85', '\xDE', '\x3F', '\x81', '\xFF', '\x85', + '\xC6', '\x7F', '\x01', '\xFC', '\x1C', '\xF1', '\x37', '\xC0', + '\x4D', '\xDF', '\x76', '\x2C', '\xBD', '\xA6', '\x6F', '\x0D', + '\x9F', '\x4B', '\x5D', '\xFF', '\x79', '\xE0', '\x37', '\x11', + '\x5F', '\x02', '\xBE', '\x92', '\xBA', '\x79', '\xAE', '\x02', + '\xEF', '\x64', '\x7F', '\x45', '\x1F', '\xAE', '\x03', '\xDF', + '\x4A', '\x5D', '\xBD', '\xDB', '\xC0', '\xF7', '\x91', '\xBF', + '\xB1', '\x75', '\xE8', '\xDB', '\x5D', '\xAB', '\xFF', '\x4D', + '\xDF', '\x1A', '\x6E', '\xD6', '\xF1', '\xAE', '\x95', '\xBF', + '\x39', '\x27', '\xD6', '\xA1', '\x6E', '\xBB', '\x69', '\x2B', + '\x5A', '\x34', '\xC2', '\xFA', '\x77', '\xD3', '\xDE', '\xE0', + '\xDF', '\xB0', '\x6F', '\x53', '\xF1', '\x34', '\x9F', '\x9A', + '\x8B', '\x29', '\xE5', '\x40', '\xEB', '\xCF', '\x3F', '\xE1', + '\xD8', '\xFE', '\x67', '\x77', '\x24', '\x2A', '\x0E', '\xF5', + '\x0E', '\xF5', '\x4C', '\x91', '\x3C', '\xB3', '\xE6', '\x6B', + '\x50', '\x45', '\x7F', '\xEF', '\xAD', '\x71', '\x6F', '\xD2', + '\x5A', '\xFC', '\x91', '\xF5', '\xFA', '\xE9', '\x0A', '\xD6', + '\xAF', '\x87', '\xDC', '\xED', '\x3D', '\xC7', '\xEC', '\x37', + '\xDC', '\xAA', '\xB8', '\xFA', '\xE8', '\xD9', '\x97', '\x62', + '\xD1', '\xDF', '\xF8', '\x2B', '\x62', '\xDC', '\xF4', '\xBB', + '\xA3', '\xD8', '\xFA', '\xE5', '\xFE', '\xD0', '\x65', '\x81', + '\x57', '\x14', '\xD5', '\xFF', '\xC3', '\xD5', '\x2F', '\x77', + '\x3F', '\xAD', '\xCF', '\x7E', '\x72', '\x3D', '\x06', '\x63', + '\x71', '\xFD', '\x11', '\xA2', '\xD4', '\x7A', '\xC0', '\x99', + '\xF8', '\xDF', '\x4B', '\xEA', '\x64', '\xFD', '\x5C', '\x04', + '\xAE', '\x02', '\xF6', '\xBA', '\xC0', '\xFB', '\x81', '\x5B', + '\x1F', '\x5D', '\x16', '\xE1', '\xEC', '\x87', '\x73', '\x26', + '\xD8', '\xF1', '\xB9', '\x0A', '\x73', '\xFD', '\x2C', '\xE9', + '\xE7', '\x15', '\x5D', '\x9D', '\x7E', '\x39', '\x06', '\xB5', + '\x9F', '\xB9', '\x6B', '\x9C', '\xCE', '\x86', '\xD3', '\x5F', + '\x4E', '\x69', '\x15', '\xFD', '\xE5', '\x2A', '\x5C', '\x45', + '\x3F', '\x75', '\xFE', '\xF9', '\xD9', '\x70', '\x6B', '\x46', + '\xAF', '\x55', '\xA3', '\x5E', '\x33', '\x4E', '\x9D', '\xD1', + '\xA4', '\x38', '\x4D', '\x8E', '\x36', '\x7C', '\xFD', '\x4D', + '\x5B', '\xF3', '\x51', '\xFC', '\x7E', '\xA9', '\x36', '\x4A', + '\x53', '\xF9', '\x44', '\x8B', '\x36', '\xE9', '\x36', '\xAA', + '\xDD', '\x62', '\x5C', '\xCD', '\xBF', '\xC1', '\x85', '\xF7', + '\x61', '\xCA', '\x4F', '\x3A', '\x11', '\x43', '\x37', '\x47', + '\xFF', '\xA6', '\xDA', '\xEB', '\x51', '\x7E', '\xE5', '\xE2', + '\x6A', '\x0C', '\x8F', '\x4E', '\x53', '\x6E', '\x5F', '\x96', + '\x9F', '\xEC', '\xF7', '\xE9', '\x88', '\x12', '\x2B', '\x1B', + '\xAD', '\xFE', '\x59', '\x29', '\x6B', '\x08', '\xBD', '\x4D', + '\x33', '\xFF', '\xB6', '\x13', '\xEE', '\xAE', '\x32', '\xF9', + '\x85', '\x2A', '\x11', '\xAA', '\xCA', '\xD5', '\xE8', '\xE7', + '\x7B', '\xBD', '\x6C', '\x7E', '\xF5', '\xF4', '\x87', '\xFB', + '\x5A', '\x1A', '\x85', '\xEE', '\x74', '\xAC', '\x3E', '\xFC', + '\xB6', '\x3E', '\x2E', '\x5D', '\x3B', '\x29', '\x47', '\xD9', + '\xEA', '\xE8', '\x0F', '\xF7', '\x84', '\xF6', '\xF7', '\x8E', + '\x36', '\x2E', '\x3F', '\xC3', '\x5C', '\xF7', '\xC8', '\xC6', + '\x47', '\x0B', '\xEB', '\xE7', '\xA3', '\xB9', '\xA3', '\x68', + '\x3A', '\x9B', '\x8B', '\xAB', '\xCF', '\x4F', '\xCE', '\x88', + '\xB3', '\xEA', '\xFA', '\xA5', '\x7A', '\xC8', '\xB5', '\x92', + '\x55', '\x57', '\xD7', '\x4F', '\xC7', '\xD0', '\xEF', '\xAD', + '\x05', '\xC1', '\xB3', '\xE5', '\xCF', '\x21', '\x3D', '\xAF', + '\x9A', '\x99', '\x0E', '\xC7', '\x90', '\xB2', '\x93', '\xDF', + '\xA5', '\xDF', '\xE4', '\xF5', '\x97', '\xEF', '\x97', '\x77', + '\xD1', '\xA2', '\xFE', '\xAB', '\xCE', '\x20', '\x5A', '\xB4', + '\x68', '\x93', '\x69', '\xD4', '\x79', '\x27', '\x9D', '\x82', + '\xA1', '\x33', '\x8F', '\x7A', '\x9F', '\x3A', '\x6B', '\x43', + '\xA7', '\x72', '\x38', '\x0A', '\x8E', '\x19', '\xD2', '\xC9', + '\x53', '\xEE', '\xB4', '\xC5', '\x31', '\xB9', '\x53', '\x9A', + '\x3B', '\xBB', '\xCB', '\xFE', '\xAE', '\xE1', '\x6A', '\x2E', + '\x6B', '\xD0', '\x9E', '\x83', '\xBA', '\x73', '\x74', '\xF0', + '\x6B', '\x2F', '\x67', '\xBE', '\x2E', '\x4A', '\x41', '\xE8', + '\xB6', '\x42', '\x7F', '\xAF', '\xD1', '\x1F', '\x56', '\xC6', + '\x77', '\x1F', '\xD7', '\x99', '\x72', '\x76', '\x58', '\x27', + '\xCE', '\x7C', '\x9C', '\xF4', '\xEB', '\xE6', '\x5D', '\x7E', + '\x0B', '\x7F', '\x2A', '\xAB', '\x1F', '\xD7', '\xB9', '\x9C', + '\x7E', '\xF7', '\x6D', '\x9A', '\x68', '\x95', '\x68', '\xAD', + '\x59', '\xFD', '\xFC', '\x13', '\x1D', '\xA3', '\xDA', '\xFC', + '\xD3', '\xDD', '\xCC', '\xBF', '\xE3', '\xCF', '\x0B', '\x9F', + '\x9D', '\xFB', '\x3D', '\xA7', '\x9F', '\x1A', '\x75', '\x74', + '\xFA', '\xE5', '\xDC', '\x75', '\x7D', '\xA0', '\xAB', '\x9F', + '\xBD', '\xFF', '\xE9', '\xE7', '\x9A', '\xF3', '\xD4', '\xF9', + '\x49', '\x19', '\xEA', '\xD7', '\x02', '\xFF', '\x9D', '\xF4', + '\xBD', '\xAF', '\x9F', '\x3B', '\x29', '\x69', '\x35', '\x54', + '\x9F', '\x51', '\xB5', '\xF3', '\xFD', '\xA4', '\x7E', '\xA2', + '\x46', '\xD3', '\xCD', '\x72', '\x13', '\x36', '\xDA', '\x68', + '\xD1', '\xA2', '\x45', '\x8B', '\x16', '\xAD', '\xAC', '\xF1', + '\xE7', '\x33', '\x75', '\x46', '\x51', '\x7E', '\xFE', '\x38', + '\x14', '\xE1', '\x3C', '\xE9', '\xD3', '\x8C', '\x62', '\xF4', + '\xF9', '\x18', '\xF6', '\x95', '\x4F', '\x3E', '\x5A', '\xBF', + '\xAC', '\x67', '\xF0', '\x14', '\xF6', '\x77', '\x2B', '\xE8', + '\x3F', '\xFB', '\x77', '\x63', '\x7E', '\xDC', '\x6A', '\x94', + '\x8E', '\xD5', '\x84', '\x7E', '\x6E', '\x7E', '\xC3', '\xFA', + '\xE5', '\xFB', '\x5D', '\x58', '\x49', '\x75', '\x5F', '\xDF', + '\xAA', '\xEA', '\xA7', '\xFA', '\x11', '\xFB', '\x17', '\x95', + '\xC2', '\x8A', '\x47', '\xAB', '\x5F', '\xBE', '\xF3', '\xBA', + '\xA6', '\xD7', '\xCF', '\x7D', '\xAB', '\xD1', '\x6F', '\xAF', + '\xA1', '\x66', '\xF5', '\xFB', '\x73', '\x43', '\x31', '\x3A', + '\xEF', '\xE6', '\xE7', '\x9F', '\xDA', '\x2B', '\xF0', '\x2A', + '\x2A', '\xAF', '\x49', '\xAE', '\x15', '\xAD', '\x94', '\xAB', + '\x40', '\x55', '\xFD', '\x9A', '\xF5', '\xDF', '\x94', '\x7E', + '\x29', '\xE7', '\xD0', '\xBE', '\x2C', '\x65', '\x88', '\x69', + '\x39', '\xFD', '\x61', '\x7F', '\xEE', '\x6D', '\x9B', '\xE9', + '\xF7', '\x74', '\x39', '\x17', '\x8E', '\xEA', '\x47', '\xE2', + '\x3A', '\x5C', '\xEA', '\x3F', '\xBA', '\x4F', '\xDD', '\x95', + '\xCD', '\x67', '\x44', '\xCD', '\x24', '\xEE', '\x53', '\x3A', + '\xEB', '\xB0', '\x2F', '\x95', '\x19', '\x35', '\xCE', '\xBB', + '\x6C', '\x93', '\xA8', '\xA9', '\x9C', '\x4D', '\xEA', '\xCC', + '\x46', '\x8B', '\x36', '\xCD', '\xE6', '\xAE', '\x6B', '\xFA', + '\x2C', '\x6E', '\x62', '\xDD', '\xF7', '\x90', '\xE1', '\x68', + '\xDC', '\x49', '\x57', '\xFF', '\x49', '\xBE', '\xE3', '\x69', + '\xCE', '\x40', '\x4C', '\xAB', '\xD7', '\x43', '\x7B', '\x8A', + '\x36', '\xF9', '\x24', '\xE7', '\xAF', '\xD1', '\xE6', '\x67', + '\x5D', '\xB5', '\x02', '\xC3', '\xD1', '\x2F', '\xDF', '\x93', + '\xE9', '\x9E', '\xA6', '\x33', '\x0A', '\xEB', '\xE7', '\x47', + '\xD5', '\x98', '\x46', '\xBF', '\x4E', '\x0D', '\x95', '\xB7', + '\x6E', '\xED', '\xCA', '\x1D', '\x2F', '\xEB', '\x97', '\x2A', + '\xA7', '\xB1', '\xBA', '\xFA', '\x0B', '\x45', '\x1A', '\xFD', + '\xE5', '\x7E', '\xD3', '\x94', '\x21', '\x55', '\xAD', '\xBE', + '\xFE', '\xFC', '\xB9', '\xBA', '\xFE', '\xF0', '\x7C', '\x5F', + '\xBD', '\x7E', '\xA9', '\x9B', '\xE9', '\x6F', '\xE9', '\x9E', + '\xA8', '\xA6', '\xBF', '\xC9', '\xFD', '\x5E', '\x1E', '\xB9', + '\xBC', '\xFE', '\xD0', '\x38', '\xA1', '\xCC', '\x75', '\x73', + '\x3B', '\x2A', '\xFD', '\xD4', '\x79', '\x6C', '\x73', '\xFE', + '\xDD', '\x3A', '\xFA', '\xDD', '\x78', '\xA3', '\xD5', '\x3F', + '\x7E', '\x46', '\x6B', '\xE3', '\xFB', '\x74', '\xD2', '\x8C', + '\xEA', '\x3E', '\xEA', '\xC6', '\x18', '\x1E', '\x81', '\xEE', + '\xE4', '\x68', '\xD1', '\xA2', '\xE5', '\xA6', '\x59', '\x1B', + '\xFA', '\xF5', '\xA3', '\x59', '\x95', '\xDA', '\x37', '\xE9', + '\xB3', '\x45', '\x77', '\x5A', '\x96', '\xB1', '\xF0', '\xFB', + '\xCD', '\xE8', '\x0F', '\xD7', '\x86', '\x27', '\xC3', '\xD4', + '\x1F', '\xB6', '\xAB', '\xD1', '\xAF', '\xF5', '\x6B', '\x5E', + '\x3F', '\x3E', '\x33', '\xA8', '\x53', '\x24', '\xEC', '\xE3', + '\xDF', '\x69', '\xCA', '\x7C', '\xF6', '\xCF', '\x3B', '\x97', + '\x52', '\x1D', '\x91', '\x3F', '\xF9', '\x1E', '\xB4', '\x02', + '\x4D', '\x15', '\x34', '\x31', '\x65', '\x0F', '\xBA', '\x73', + '\xAB', '\xFA', '\x53', '\xBF', '\x2A', '\xFC', '\x3A', '\xD9', + '\xFF', '\x73', '\xA3', '\x87', '\x94', '\x6B', '\x6B', '\x5E', + '\x4F', '\x4F', '\x35', '\xFD', '\x6E', '\x74', '\x7E', '\x8C', + '\x6A', '\xFA', '\xCB', '\xE6', '\xC6', '\x7B', '\xD0', '\x55', + '\x9C', '\x1E', '\xFD', '\xF4', '\xB8', '\xF5', '\xF5', '\x87', + '\xCE', '\x86', '\x51', '\xEA', '\x0F', '\x45', '\x1C', '\x78', + '\x34', '\xAB', '\x5F', '\xDA', '\xFF', '\xEA', '\xEA', '\xD7', + '\xEC', '\xCD', '\x7E', '\x1F', '\xFA', '\x3E', '\xD4', '\x67', + '\x39', '\x46', '\x19', '\xFD', '\x1A', '\x95', '\x55', '\xF5', + '\x8F', '\xCE', '\xB4', '\xF9', '\x8C', '\x5B', '\xDE', '\xF5', + '\xCD', '\xEF', '\x28', '\xD9', '\x73', '\xF8', '\x19', '\x45', + '\x8B', '\x16', '\x6D', '\x32', '\x8C', '\x3A', '\xAD', '\x46', + '\x19', '\x7B', '\x54', '\xB1', '\xB4', '\x19', '\x44', '\xFD', + '\x5C', '\x4E', '\xE1', '\x7B', '\x0C', '\x77', '\x57', '\xF1', + '\xEF', '\x47', '\xBE', '\xAF', '\xE6', '\x26', '\x52', '\xC7', + '\x97', '\xCF', '\x57', '\xA7', '\xBF', '\x89', '\x98', '\x78', + '\x6C', '\x5C', '\xCD', '\xE1', '\xEA', '\xA7', '\xF4', '\x8D', + '\x42', '\x3F', '\x3F', '\x76', '\x73', '\xFA', '\xF9', '\x3B', + '\x34', '\x9F', '\x01', '\xEE', '\x85', '\xE1', '\xEB', '\xC7', + '\x31', '\x43', '\xF9', '\xD2', '\x9A', '\x06', '\x7E', '\x9A', + '\xEA', '\xFA', '\x1E', '\x52', '\x37', '\x0C', '\x5F', '\x3F', + '\xFF', '\xAD', '\x46', '\xBF', '\xAD', '\x80', '\xFE', '\x26', + '\x4C', '\x42', '\x6A', '\xC7', '\x5F', '\xBF', '\xF4', '\x1C', + '\xEE', '\x9E', '\xAA', '\xFA', '\xEB', '\xEC', '\xFF', '\xB2', + '\x9E', '\x32', '\xFB', '\x73', '\x19', '\xFD', '\x5C', '\xF4', + '\x6A', '\xFB', '\x9F', '\x6F', '\x72', '\xA5', '\x74', '\x63', + '\x0C', '\xD7', '\xC2', '\x79', '\x4C', '\xB2', '\x7E', '\x5D', + '\x16', '\xE1', '\x8E', '\x88', '\x16', '\x2D', '\x1A', '\x77', + '\x5F', '\x9A', '\x2E', '\x9B', '\xF6', '\x0A', '\x44', '\xFD', + '\x51', '\x3F', '\xF7', '\x79', '\x3A', '\x2A', '\xE3', '\xDE', + '\x58', '\x8B', '\x7F', '\xA7', '\x43', '\x3F', '\xBE', '\x2B', + '\xD9', '\xFF', '\x4F', '\xAF', '\xFE', '\xE9', '\xD9', '\x17', + '\xA6', '\x45', '\x27', '\x67', '\x51', '\xFF', '\xF4', '\xEA', + '\x8F', '\xF7', '\xDF', '\x68', '\xD1', '\xA2', '\x45', '\x8B', + '\x16', '\x2D', '\x5A', '\xB4', '\xE9', '\xB1', '\xFF', '\x01', + '\xF1', '\xAA', '\xBA', '\x4E', '\x18', '\x89', '\x00', '\x00' + }; + +#endif // NO_OGL diff --git a/src/win32/res/VBA.ico b/src/win32/res/VBA.ico new file mode 100644 index 0000000..06c07c9 Binary files /dev/null and b/src/win32/res/VBA.ico differ diff --git a/src/win32/res/VBA.rc2 b/src/win32/res/VBA.rc2 new file mode 100644 index 0000000..b1e617e --- /dev/null +++ b/src/win32/res/VBA.rc2 @@ -0,0 +1,13 @@ +// +// VBA.RC2 - resources Microsoft Visual C++ does not edit directly +// + +#ifdef APSTUDIO_INVOKED +#error this file is not editable by Microsoft Visual C++ +#endif //APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// Add manually edited resources here... + +///////////////////////////////////////////////////////////////////////////// diff --git a/src/win32/resource.h b/src/win32/resource.h new file mode 100644 index 0000000..493aaba --- /dev/null +++ b/src/win32/resource.h @@ -0,0 +1,896 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by VBA.rc +// +#define IDS_UNSUPPORTED_VBA_SGM 1 +#define IDS_CANNOT_LOAD_SGM 2 +#define IDS_SAVE_GAME_NOT_USING_BIOS 3 +#define IDC_DEFAULTS 3 +#define IDS_SAVE_GAME_USING_BIOS 4 +#define IDS_UNSUPPORTED_SAVE_TYPE 5 +#define IDS_CANNOT_OPEN_FILE 6 +#define IDS_BAD_ZIP_FILE 7 +#define IDS_NO_IMAGE_ON_ZIP 8 +#define IDS_ERROR_OPENING_IMAGE 9 +#define IDS_ERROR_READING_IMAGE 10 +#define IDS_UNSUPPORTED_BIOS_FUNCTION 11 +#define IDS_INVALID_BIOS_FILE_SIZE 12 +#define IDS_INVALID_CHEAT_CODE 13 +#define IDS_UNKNOWN_ARM_OPCDOE 14 +#define IDS_UNKNOWN_THUMB_OPCODE 15 +#define IDS_ERROR_CREATING_FILE 16 +#define IDS_FAILED_TO_READ_SGM 17 +#define IDS_FAILED_TO_READ_RTC 18 +#define IDS_UNSUPPORTED_VB_SGM 19 +#define IDS_CANNOT_LOAD_SGM_FOR 20 +#define IDS_ERROR_OPENING_IMAGE_FROM 21 +#define IDS_ERROR_READING_IMAGE_FROM 22 +#define IDS_UNSUPPORTED_ROM_SIZE 23 +#define IDS_UNSUPPORTED_RAM_SIZE 24 +#define IDS_UNKNOWN_CARTRIDGE_TYPE 25 +#define IDS_MAXIMUM_NUMBER_OF_CHEATS 26 +#define IDS_INVALID_GAMESHARK_CODE 27 +#define IDS_INVALID_GAMEGENIE_CODE 28 +#define IDS_INVALID_CHEAT_TO_REMOVE 29 +#define IDS_INVALID_CHEAT_CODE_ADDRESS 30 +#define IDS_UNSUPPORTED_CHEAT_LIST_VERSION 31 +#define IDS_UNSUPPORTED_CHEAT_LIST_TYPE 32 +#define IDS_INVALID_GSA_CODE 33 +#define IDS_CANNOT_IMPORT_SNAPSHOT_FOR 34 +#define IDS_UNSUPPORTED_SNAPSHOT_FILE 35 +#define IDS_UNSUPPORTED_ARM_MODE 36 +#define IDS_UNSUPPORTED_CODE_FILE 37 +#define IDS_GSA_CODE_WARNING 38 +#define IDS_INVALID_CBA_CODE 39 +#define IDS_CBA_CODE_WARNING 40 +#define IDS_OUT_OF_MEMORY 41 +#define IDS_WRONG_GAMESHARK_CODE 42 +#define IDS_TOOLTIP_DEFAULT_VOLUME 42 +#define IDS_UNSUPPORTED_GAMESHARK_CODE 43 +#define IDS_TOOLTIP_ENHANCE_SOUND 43 +#define IDS_TOOLTIP_SURROUND 44 +#define IDS_TOOLTIP_DECLICKING 45 +#define IDS_FILTER_COMPRESSED_FILES 46 +#define IDI_MAINICON 101 +#define IDD_REGISTERS 102 +#define IDD_DEBUG 103 +#define IDR_MENU 104 +#define IDD_ABOUT 105 +#define IDR_ACCELERATOR 106 +#define IDD_CHEATS 107 +#define IDD_ADD_CHEAT 108 +#define IDD_DIRECTORIES 109 +#define IDD_CONFIG 110 +#define IDD_GS 111 +#define IDD_GG 112 +#define IDD_CHEAT_LIST 113 +#define IDD_ASSOCIATIONS 114 +#define IDR_GB_PRINTER 115 +#define IDD_GBA_ROM_INFO 116 +#define IDD_GB_ROM_INFO 117 +#define IDD_GB_CHEAT_LIST 118 +#define IDD_ADD_CHEAT_DLG 119 +#define IDD_GB_PRINTER 120 +#define IDD_MOTION_CONFIG 121 +#define IDD_LANG_SELECT 122 +#define IDD_CODE_SELECT 123 +#define IDD_OPENDLG 124 +#define IDD_MAP_VIEW 126 +#define IDD_PALETTE_VIEW 127 +#define IDD_MEM_VIEWER 128 +#define IDD_OAM_VIEW 130 +#define IDD_ACCEL_EDITOR 131 +#define IDD_TILE_VIEWER 132 +#define IDD_GB_COLORS 133 +#define IDD_DISASSEMBLE 134 +#define IDD_GDB_PORT 135 +#define IDD_GDB_WAITING 136 +#define IDD_LOGGING 137 +#define IDD_EXPORT_SPS 138 +#define IDD_ADDR_SIZE 139 +#define IDD_MODES 140 +#define IDD_THROTTLE 143 +#define IDD_GB_DISASSEMBLE 144 +#define IDD_GB_OAM_VIEW 145 +#define IDD_GB_TILE_VIEWER 146 +#define IDD_GB_MAP_VIEW 147 +#define IDD_GB_PALETTE_VIEW 148 +#define IDD_MODE_CONFIRM 149 +#define IDD_REWIND_INTERVAL 150 +#define IDD_IO_VIEWER 151 +#define IDD_MAX_SCALE 154 +#define IDD_GAME_OVERRIDES 156 +#define IDD_SELECT 159 +#define IDD_SELECT_PLUGIN 159 +#define IDD_OAL_CONFIG 160 +#define IDD_BIOS 161 +#define IDD_FULLSCREEN 162 +#define IDD_XAUDIO2_CONFIG 163 +#define IDD_AUDIO_CORE_SETTINGS 164 +#define IDD_JOYBUS_DIALOG 165 +#define IDC_R0 1000 +#define IDC_EDIT_UP 1000 +#define IDC_R1 1001 +#define IDC_EDIT_DOWN 1001 +#define IDC_R2 1002 +#define IDC_EDIT_LEFT 1002 +#define IDC_R3 1003 +#define IDC_EDIT_RIGHT 1003 +#define IDC_R4 1004 +#define IDC_EDIT_BUTTON_A 1004 +#define IDC_R5 1005 +#define IDC_EDIT_BUTTON_B 1005 +#define IDC_R6 1006 +#define IDC_EDIT_BUTTON_SELECT 1006 +#define IDC_R7 1007 +#define IDC_EDIT_BUTTON_START 1007 +#define IDC_R8 1008 +#define ID_OK 1008 +#define IDC_R9 1009 +#define ID_CANCEL 1009 +#define ID_SAVE 1009 +#define IDC_R10 1010 +#define IDC_EDIT_SPEED 1010 +#define IDC_R11 1011 +#define IDC_EDIT_CAPTURE 1011 +#define IDC_R12 1012 +#define IDC_EDIT_BUTTON_L 1012 +#define IDC_R13 1013 +#define IDC_EDIT_BUTTON_GS 1013 +#define IDC_R14 1014 +#define IDC_EDIT_BUTTON_R 1014 +#define IDC_R15 1015 +#define IDC_R16 1016 +#define IDC_R17 1017 +#define IDC_N_FLAG 1018 +#define IDC_ROM_DIR 1018 +#define IDC_Z_FLAG 1019 +#define IDC_NEXT 1019 +#define IDC_BATTERY_DIR 1019 +#define IDC_C_FLAG 1020 +#define IDC_CONTINUE 1020 +#define IDC_SAVE_DIR 1020 +#define IDC_V_FLAG 1021 +#define IDC_CAPTURE_DIR 1021 +#define IDC_CHEAT_LIST 1021 +#define IDC_IRQ 1022 +#define IDC_ROM_PATH 1022 +#define IDC_START 1022 +#define IDC_T_FLAG 1023 +#define IDC_BATTERY_PATH 1023 +#define IDC_SEARCH 1023 +#define IDS_DIRECTX_7_REQUIRED 1024 +#define IDC_SAVE_PATH 1024 +#define IDC_ADD_CHEAT 1024 +#define IDC_M4 1025 +#define IDC_CAPTURE_PATH 1025 +#define IDC_OLD_VALUE 1025 +#define IDC_ADD_GS_CHEAT 1025 +#define IDS_DISABLING_VIDEO_MEMORY 1025 +#define IDC_ADD_GAMESHARK 1025 +#define IDC_M3 1026 +#define IDC_SPECIFIC_VALUE 1026 +#define IDS_SETTING_WILL_BE_EFFECTIVE 1026 +#define IDC_GBROM_DIR 1026 +#define IDC_M2 1027 +#define IDS_DISABLING_EMULATION_ONLY 1027 +#define IDC_GBROM_PATH 1027 +#define IDC_M1 1028 +#define IDC_SIZE_8 1028 +#define IDS_FAILED_TO_OPEN_FILE 1028 +#define IDC_ROM_DIR_RESET 1028 +#define IDC_M0 1029 +#define IDC_SIZE_16 1029 +#define IDS_FAILED_TO_READ_ZIP_DIR 1029 +#define IDC_GBROM_DIR_RESET 1029 +#define IDC_SIZE_32 1030 +#define IDS_UNSUPPORTED_FILE_TYPE 1030 +#define IDC_BATTERY_DIR_RESET 1030 +#define IDC_EQ 1031 +#define IDS_CANNOT_CREATE_DIRECTSOUND 1031 +#define IDC_SAVE_DIR_RESET 1031 +#define IDC_NE 1032 +#define IDS_CANNOT_SETCOOPERATIVELEVEL 1032 +#define IDC_CAPTURE_DIR_RESET 1032 +#define IDC_LT 1033 +#define IDS_CANNOT_CREATESOUNDBUFFER 1033 +#define IDC_GBCROM_PATH 1033 +#define IDC_LE 1034 +#define IDS_CANNOT_SETFORMAT_PRIMARY 1034 +#define IDC_GBCROM_DIR 1034 +#define IDC_GT 1035 +#define IDS_CANNOT_CREATESOUNDBUFFER_SEC 1035 +#define IDC_GE 1036 +#define IDS_CANNOT_PLAY_PRIMARY 1036 +#define IDC_SIGNED 1037 +#define IDS_SEARCH_PRODUCED_TOO_MANY 1037 +#define IDC_UNSIGNED 1038 +#define IDS_NUMBER_CANNOT_BE_EMPTY 1038 +#define IDS_INVALID_ADDRESS 1039 +#define IDC_HEXADECIMAL 1040 +#define IDS_MISALIGNED_HALFWORD 1040 +#define IDC_VALUE 1041 +#define IDS_MISALIGNED_WORD 1041 +#define IDC_ADDRESS 1042 +#define IDS_VALUE_CANNOT_BE_EMPTY 1042 +#define IDS_ERROR_ON_STARTDOC 1043 +#define IDC_R 1043 +#define IDS_ERROR_ON_STARTPAGE 1044 +#define IDC_G 1044 +#define IDS_ERROR_PRINTING_ON_STRETCH 1045 +#define IDC_B 1045 +#define IDC_UPDATE 1046 +#define IDS_ERROR_ON_ENDPAGE 1046 +#define IDC_TILE_NUM 1046 +#define IDC_GGDESC 1047 +#define IDS_ERROR_ON_ENDDOC 1047 +#define IDC_FLIP 1047 +#define IDC_GGCODE 1048 +#define IDS_ERROR 1048 +#define IDC_PALETTE_NUM 1048 +#define IDC_GGADD 1049 +#define IDS_JOY_LEFT 1049 +#define IDC_GGDEL 1050 +#define IDS_JOY_RIGHT 1050 +#define IDC_GGLIST 1051 +#define IDS_JOY_UP 1051 +#define IDC_GGRES 1052 +#define IDS_JOY_DOWN 1052 +#define IDC_GGQUIT 1053 +#define IDS_JOY_BUTTON 1053 +#define IDC_GSDESC 1054 +#define IDS_SELECT_ROM_DIR 1054 +#define IDC_GSCODE 1055 +#define IDS_SELECT_BATTERY_DIR 1055 +#define IDC_GSADD 1056 +#define IDS_SELECT_SAVE_DIR 1056 +#define IDC_GSDEL 1057 +#define IDS_SELECT_CAPTURE_DIR 1057 +#define IDC_GSLIST 1058 +#define IDS_SELECT_BIOS_FILE 1058 +#define IDC_GSRES 1059 +#define IDS_RESET 1059 +#define IDC_GSQUIT 1060 +#define IDS_AUTOFIRE_A_DISABLED 1060 +#define IDC_FREEZE 1061 +#define IDS_AUTOFIRE_A 1061 +#define IDS_AUTOFIRE_B_DISABLED 1062 +#define IDS_AUTOFIRE_B 1063 +#define IDS_AUTOFIRE_L_DISABLED 1064 +#define IDS_AUTOFIRE_L 1065 +#define IDS_AUTOFIRE_R_DISABLED 1066 +#define IDC_REMOVE 1067 +#define IDS_AUTOFIRE_R 1067 +#define IDC_REMOVE_ALL 1068 +#define IDS_SELECT_ROM 1068 +#define IDS_SELECT_SAVE_GAME_NAME 1069 +#define IDC_ENABLE 1070 +#define IDS_LOADED_STATE 1070 +#define IDS_LOADED_STATE_N 1071 +#define IDS_WROTE_STATE 1072 +#define IDS_WROTE_STATE_N 1073 +#define IDC_RESTORE 1074 +#define IDS_LOADED_BATTERY 1074 +#define IDC_GBA 1075 +#define IDS_SELECT_CAPTURE_NAME 1075 +#define IDC_AGB 1076 +#define IDS_SCREEN_CAPTURE 1076 +#define IDC_BIN 1077 +#define IDS_ADDRESS 1077 +#define IDC_GB 1078 +#define IDS_OLD_VALUE 1078 +#define IDC_SGB 1079 +#define IDC_ROM_TITLE 1079 +#define IDS_NEW_VALUE 1079 +#define IDC_CGB 1080 +#define IDC_ROM_GAME_CODE 1080 +#define IDS_ADD_CHEAT_CODE 1080 +#define IDC_GBC 1081 +#define IDC_ROM_MAKER_CODE 1081 +#define IDS_CODE 1081 +#define IDC_DMG 1082 +#define IDC_ROM_UNIT_CODE 1082 +#define IDS_DESCRIPTION 1082 +#define IDC_ROM_DEVICE_TYPE 1083 +#define IDS_STATUS 1083 +#define IDC_ROM_VERSION 1084 +#define IDS_ADD_GG_CODE 1084 +#define IDC_ROM_CRC 1085 +#define IDS_ADD_GS_CODE 1085 +#define IDC_ROM_COLOR 1086 +#define IDC_CODE 1086 +#define IDS_POCKET_PRINTER 1086 +#define IDC_ROM_MAKER_NAME 1086 +#define IDC_ROM_SIZE 1087 +#define IDC_DESC 1087 +#define IDS_UNKNOWN 1087 +#define IDC_ROM_RAM_SIZE 1088 +#define IDC_ADD_GG_CHEAT 1088 +#define IDS_NONE 1088 +#define IDC_ROM_DEST_CODE 1089 +#define IDC_GB_PRINTER 1089 +#define IDS_FAILED_TO_LOAD_LIBRARY 1089 +#define IDC_ROM_LIC_CODE 1090 +#define IDC_1X 1090 +#define IDS_FAILED_TO_GET_LOCINFO 1090 +#define IDC_ROM_CHECKSUM 1091 +#define IDC_2X 1091 +#define IDS_SELECT_CHEAT_LIST_NAME 1091 +#define IDC_3X 1092 +#define IDS_FILTER_BIOS 1092 +#define IDC_4X 1093 +#define IDS_FILTER_GBAROM 1093 +#define IDC_ROM_MAKER_NAME2 1093 +#define ID_PRINT 1094 +#define IDS_FILTER_SGM 1094 +#define IDC_ADD_GSA 1095 +#define IDC_ADD_CODE 1095 +#define IDS_FILTER_CHEAT_LIST 1095 +#define IDC_TRANSLATION_BY 1096 +#define IDS_FILTER_PNG 1096 +#define IDC_LANG_STRING 1097 +#define IDS_LOADED_CHEATS 1097 +#define IDC_LANG_NAME 1098 +#define IDS_ERROR_DISP_COLOR 1098 +#define IDS_ADD_GSA_CODE 1099 +#define IDC_GAME_LIST 1099 +#define IDS_FILTER_SPS 1100 +#define IDS_SELECT_SNAPSHOT_FILE 1101 +#define IDC_ADD_CODEBREAKER 1101 +#define IDS_FILTER_SAV 1102 +#define IDS_SELECT_BATTERY_FILE 1103 +#define IDS_FILTER_GBS 1104 +#define IDS_FILTER_GCF 1105 +#define IDS_SELECT_CODE_FILE 1106 +#define IDS_SAVE_WILL_BE_LOST 1107 +#define IDS_CONFIRM_ACTION 1108 +#define IDS_CODES_WILL_BE_LOST 1109 +#define IDS_FILTER_SPC 1110 +#define IDS_ADD_CBA_CODE 1111 +#define IDS_FILTER_WAV 1112 +#define IDS_SELECT_WAV_NAME 1113 +#define IDC_FRAME_0 1113 +#define IDS_FILTER_GBROM 1114 +#define IDC_FRAME_1 1114 +#define IDC_BG0 1115 +#define IDS_FILTER_PAL 1115 +#define IDC_BG1 1116 +#define IDS_SELECT_PALETTE_NAME 1116 +#define IDC_BG2 1117 +#define IDS_SEARCH_PRODUCED_NO_RESULTS 1117 +#define IDC_BG3 1118 +#define IDS_ERROR_BINDING 1118 +#define IDS_ERROR_LISTENING 1119 +#define IDS_ERROR_CREATING_SOCKET 1120 +#define IDS_ACK_NOT_RECEIVED 1121 +#define IDS_ERROR_NOT_GBA_IMAGE 1122 +#define IDS_EEPROM_NOT_SUPPORTED 1123 +#define IDC_MAP_VIEW 1124 +#define IDS_FILTER_DUMP 1124 +#define IDC_PALETTE_VIEW 1125 +#define IDS_SELECT_DUMP_FILE 1125 +#define IDC_PALETTE_VIEW_OBJ 1126 +#define IDC_REFRESH 1126 +#define IDS_FILTER_AVI 1126 +#define IDC_SAVE 1127 +#define IDC_GOPC 1127 +#define IDS_SELECT_AVI_NAME 1127 +#define IDC_APPLY 1127 +#define IDS_INVALID_THROTTLE_VALUE 1128 +#define IDC_REFRESH2 1129 +#define IDS_FILTER_INI 1129 +#define IDS_SELECT_SKIN_FILE 1130 +#define IDC_CLOSE 1131 +#define IDS_FILTER_VMV 1131 +#define IDS_SELECT_MOVIE_NAME 1132 +#define IDS_BUG_REPORT 1133 +#define IDS_UNSUPPORTED_MOVIE_VERSION 1134 +#define IDS_END_OF_MOVIE 1135 +#define IDC_COLOR 1136 +#define IDS_INVALID_INTERVAL_VALUE 1136 +#define IDC_SAVE_BG 1137 +#define IDS_REGISTRY 1137 +#define IDC_SAVE_OBJ 1138 +#define IDC_MAP_VIEW_ZOOM 1138 +#define IDS_MOVIE_PLAY 1138 +#define IDS_FILTER_GSVSPS 1139 +#define IDC_VIEWER 1140 +#define IDC_CHANGE_BACKDROP 1140 +#define IDC_ADDRESSES 1141 +#define IDC_GO 1143 +#define IDC_8_BIT 1144 +#define IDC_16_BIT 1145 +#define IDC_32_BIT 1146 +#define IDC_OAM_VIEW 1147 +#define IDC_OAM_VIEW_ZOOM 1148 +#define IDC_SPRITE 1150 +#define IDC_POS 1151 +#define IDC_MODE 1152 +#define IDC_COLORS 1153 +#define IDC_MAPBASE 1153 +#define IDC_PALETTE 1154 +#define IDC_CHARBASE 1154 +#define IDC_TILE 1155 +#define IDC_DIM 1155 +#define IDC_PRIO 1156 +#define IDC_NUMCOLORS 1156 +#define IDC_SCROLLBAR 1157 +#define IDC_PRIORITY 1157 +#define IDC_MOSAIC 1158 +#define IDC_SIZE2 1159 +#define IDC_OVERFLOW 1159 +#define IDC_ROT 1160 +#define IDC_FLAGS 1161 +#define IDC_COMMANDS 1162 +#define IDC_BANK 1162 +#define IDC_CURRENTS 1163 +#define IDC_ASSIGN 1164 +#define IDC_RESET 1165 +#define IDC_EDIT_KEY 1166 +#define IDC_ALREADY_AFFECTED 1167 +#define IDC_TILE_VIEW 1168 +#define IDC_16_COLORS 1169 +#define IDC_256_COLORS 1170 +#define IDC_CHARBASE_0 1173 +#define IDC_CHARBASE_1 1174 +#define IDC_CHARBASE_2 1175 +#define IDC_CHARBASE_3 1176 +#define IDC_PALETTE_SLIDER 1177 +#define IDC_CHARBASE_4 1178 +#define IDC_COLOR_BG0 1178 +#define IDC_COLOR_BG1 1179 +#define IDC_URL 1179 +#define IDC_COLOR_BG2 1180 +#define IDC_STRETCH 1180 +#define IDC_COLOR_BG3 1181 +#define IDC_COLOR_OB0 1182 +#define IDC_COLOR_OB1 1183 +#define IDC_COLOR_OB2 1184 +#define IDC_COLOR_OB3 1185 +#define IDC_TRANSLATOR_URL 1186 +#define IDC_STATIC1 1187 +#define IDC_STATIC2 1188 +#define IDC_STATIC3 1189 +#define IDC_STATIC4 1190 +#define IDC_DEFAULT 1191 +#define IDC_USER1 1192 +#define IDC_USER2 1193 +#define IDC_DISASSEMBLE 1196 +#define IDC_AUTOMATIC 1199 +#define IDC_ARM 1200 +#define IDC_THUMB 1201 +#define IDC_AUTO_UPDATE 1204 +#define IDC_N 1210 +#define IDC_Z 1211 +#define IDC_C 1212 +#define IDC_V 1213 +#define IDC_F 1214 +#define IDC_I 1215 +#define IDC_T 1216 +#define IDC_PORT 1217 +#define IDC_VSCROLL 1218 +#define IDC_VERSION 1219 +#define IDC_VERSION2 1220 +#define IDC_DATE 1220 +#define IDC_VERBOSE_SWI 1223 +#define IDC_VERBOSE_UNALIGNED_ACCESS 1224 +#define IDC_VERBOSE_ILLEGAL_WRITE 1225 +#define IDC_VERBOSE_ILLEGAL_READ 1226 +#define IDC_LOG 1227 +#define IDC_CLEAR 1228 +#define IDC_VERBOSE_DMA0 1229 +#define IDC_VERBOSE_DMA1 1230 +#define IDC_TILE_NUMBER 1230 +#define IDC_VERBOSE_DMA2 1231 +#define IDC_XY 1231 +#define IDC_VERBOSE_DMA3 1232 +#define IDC_VERBOSE_UNDEFINED 1233 +#define IDC_TITLE 1234 +#define IDC_VERBOSE_AGBPRINT 1234 +#define IDC_CURRENT_ADDRESS 1235 +#define IDC_VERBOSE_AGBPRINT2 1235 +#define IDC_VERBOSE_SOUNDOUTPUT 1235 +#define IDC_NOTES 1236 +#define IDC_CURRENT_ADDRESS_LABEL 1236 +#define IDC_LOAD 1238 +#define IDC_SIZE_CONTROL 1240 +#define IDC_MODES 1240 +#define IDC_DRIVERS 1241 +#define IDC_THROTTLE 1242 +#define IDC_H 1243 +#define IDC_OAP 1244 +#define IDC_BANK_0 1245 +#define IDC_BANK_1 1246 +#define IDC_TIMER 1247 +#define IDC_INTERVAL 1248 +#define IDC_BIT_0 1250 +#define IDC_BIT_1 1251 +#define IDC_PREDEFINED 1251 +#define IDC_BIT_2 1252 +#define IDC_BIT_3 1253 +#define IDC_BIT_4 1254 +#define IDC_NAME 1254 +#define IDC_BIT_5 1255 +#define IDC_RTC 1255 +#define IDC_BIT_6 1256 +#define IDC_SAVE_TYPE 1256 +#define IDC_BIT_7 1257 +#define IDC_FLASH_SIZE 1257 +#define IDC_BIT_8 1258 +#define IDC_COMMENT 1258 +#define IDC_BIT_9 1259 +#define IDC_BIT_10 1260 +#define IDC_BIT_11 1261 +#define IDC_BIT_12 1262 +#define IDC_BIT_13 1263 +#define IDC_BIT_14 1264 +#define IDC_BIT_15 1265 +#define IDC_MIRRORING 1266 +#define IDC_LY 1267 +#define IDC_APPENDMODE 1268 +#define IDC_DEVICE 1269 +#define IDC_SLIDER_BUFFERCOUNT 1270 +#define IDC_BUFFERINFO 1271 +#define IDC_GB_BIOS_PATH 1272 +#define IDC_GBA_BIOS_PATH 1273 +#define IDC_SKIP_BOOT_LOGO 1274 +#define IDC_ENABLE_GB_BIOS 1275 +#define IDC_ENABLE_GBA_BIOS 1276 +#define IDC_SELECT_GB_BIOS_PATH 1277 +#define IDC_SELECT_GBA_BIOS_PATH 1278 +#define IDC_CLEAR_ALL 1278 +#define IDC_GBC_BIOS_PATH 1279 +#define IDC_COMBO_RESOLUTION 1280 +#define IDC_ENABLE_GBC_BIOS 1280 +#define IDC_COMBO_COLOR_DEPTH 1281 +#define IDC_SELECT_GB_BIOS_PATH2 1281 +#define IDC_SELECT_GBC_BIOS_PATH 1281 +#define IDC_COMBO_REFRESH_RATE 1282 +#define IDC_COMBO_DEVICE 1283 +#define IDC_CHECK1 1284 +#define IDC_CHECK_UPMIX 1284 +#define IDC_ENHANCE_SOUND 1284 +#define IDC_COMBO_DEV 1285 +#define IDC_SOUND_INTERPOLATION 1285 +#define IDC_SLIDER_BUFFER 1286 +#define IDC_INFO_BUFFER 1287 +#define IDC_SURROUND 1288 +#define IDC_ECHO 1289 +#define IDC_STEREO 1290 +#define IDC_SLIDER2 1291 +#define IDC_VOLUME 1291 +#define IDC_DEFAULT_VOLUME 1292 +#define IDC_DECLICKING 1293 +#define IDC_SOUND_FILTERING 1294 +#define IDC_COMBO1 1296 +#define IDC_SAMPLE_RATE 1296 +#define IDC_JOYBUS_HOSTNAME 1297 +#define IDC_JOYBUS_ENABLE 1298 +#define IDS_OAL_NODEVICE 2000 +#define IDS_OAL_NODLL 2001 +#define IDS_AVI_CANNOT_CREATE_AVI 2002 +#define IDS_AVI_CANNOT_CREATE_VIDEO 2003 +#define IDS_AVI_CANNOT_CREATE_AUDIO 2004 +#define IDS_AVI_CANNOT_WRITE_VIDEO 2005 +#define IDS_AVI_CANNOT_WRITE_AUDIO 2006 +#define IDS_FILTER_GBCROM 2007 +#define IDS_COM_FAILURE 2008 +#define IDS_XAUDIO2_FAILURE 2009 +#define IDS_XAUDIO2_CANNOT_CREATE_MASTERINGVOICE 2010 +#define IDS_XAUDIO2_CANNOT_CREATE_SOURCEVOICE 2011 +#define IDS_XAUDIO2_CANNOT_ENUMERATE_DEVICES 2012 +#define IDS_CHEATS_DISABLED 2013 +#define IDS_CHEATS_ENABLED 2014 +#define ID_HELP_ABOUT 40001 +#define ID_FILE_EXIT 40002 +#define ID_OPTIONS_VIDEO_FRAMESKIP_0 40003 +#define ID_OPTIONS_VIDEO_FRAMESKIP_1 40004 +#define ID_OPTIONS_VIDEO_FRAMESKIP_2 40005 +#define ID_OPTIONS_VIDEO_FRAMESKIP_3 40006 +#define ID_OPTIONS_VIDEO_FRAMESKIP_4 40007 +#define ID_OPTIONS_VIDEO_FRAMESKIP_5 40008 +#define ID_OPTIONS_VIDEO_VSYNC 40009 +#define ID_OPTIONS_VIDEO_X1 40010 +#define ID_OPTIONS_VIDEO_X2 40011 +#define ID_OPTIONS_VIDEO_X3 40012 +#define ID_OPTIONS_VIDEO_X4 40013 +#define ID_OPTIONS_VIDEO_X5 40014 +#define ID_OPTIONS_VIDEO_X6 40015 +#define ID_OPTIONS_JOYPAD 40016 +#define ID_OPTIONS_EMULATOR_SYNCHRONIZE 40017 +#define ID_FILE_RESET 40018 +#define ID_FILE_LOAD 40019 +#define ID_OPTIONS_SOUND_DIRECTSOUNDA 40020 +#define ID_OPTIONS_SOUND_DIRECTSOUNDB 40021 +#define ID_OPTIONS_SOUND_CHANNEL1 40025 +#define ID_OPTIONS_SOUND_CHANNEL2 40026 +#define ID_OPTIONS_SOUND_CHANNEL3 40027 +#define ID_OPTIONS_SOUND_CHANNEL4 40028 +#define ID_OPTIONS_EMULATOR_USEBIOSFILE 40029 +#define ID_OPTIONS_EMULATOR_SELECTBIOSFILE 40030 +#define ID_CHEATS_SEARCHFORCHEATS 40031 +#define ID_CHEATS_ADDCHEAT 40032 +#define ID_OPTIONS_VIDEO_DISABLESFX 40033 +#define ID_OPTIONS_GAMEBOY_BORDER 40034 +#define ID_FILE_SAVEGAME_SLOT1 40035 +#define ID_FILE_SAVEGAME_SLOT2 40036 +#define ID_FILE_SAVEGAME_SLOT3 40037 +#define ID_FILE_SAVEGAME_SLOT4 40038 +#define ID_FILE_SAVEGAME_SLOT5 40039 +#define ID_FILE_SAVEGAME_SLOT6 40040 +#define ID_FILE_SAVEGAME_SLOT7 40041 +#define ID_FILE_SAVEGAME_SLOT8 40042 +#define ID_FILE_SAVEGAME_SLOT9 40043 +#define ID_FILE_SAVEGAME_SLOT10 40044 +#define ID_FILE_LOADGAME_SLOT1 40045 +#define ID_FILE_LOADGAME_SLOT2 40046 +#define ID_FILE_LOADGAME_SLOT3 40047 +#define ID_FILE_LOADGAME_SLOT4 40048 +#define ID_FILE_LOADGAME_SLOT5 40049 +#define ID_FILE_LOADGAME_SLOT6 40050 +#define ID_FILE_LOADGAME_SLOT7 40051 +#define ID_FILE_LOADGAME_SLOT8 40052 +#define ID_FILE_LOADGAME_SLOT9 40053 +#define ID_FILE_LOADGAME_SLOT10 40054 +#define ID_OPTIONS_GAMEBOY_AUTOMATIC 40057 +#define ID_OPTIONS_GAMEBOY_CGB 40058 +#define ID_OPTIONS_GAMEBOY_GBA 40059 +#define ID_OPTIONS_GAMEBOY_SGB 40060 +#define ID_OPTIONS_GAMEBOY_GB 40062 +#define ID_OPTIONS_GAMEBOY_REALCOLORS 40063 +#define ID_OPTIONS_GAMEBOY_GAMEBOYCOLORS 40064 +#define ID_CHEATS_GAMEBOY 40065 +#define ID_OPTIONS_SOUND_11KHZ 40067 +#define ID_OPTIONS_SOUND_22KHZ 40068 +#define ID_OPTIONS_SOUND_44KHZ 40069 +#define ID_OPTIONS_VIDEO_DDRAWEMULATIONONLY 40070 +#define ID_OPTIONS_VIDEO_DDRAWUSEVIDEOMEMORY 40071 +#define ID_OPTIONS_PRIORITY_HIGHEST 40072 +#define ID_OPTIONS_PRIORITY_ABOVENORMAL 40073 +#define ID_OPTIONS_PRIORITY_NORMAL 40074 +#define ID_OPTIONS_PRIORITY_BELOWNORMAL 40075 +#define ID_OPTIONS_VIDEO_FULLSCREEN320X240 40076 +#define ID_OPTIONS_VIDEO_FULLSCREEN640X480 40077 +#define ID_OPTIONS_FILTER_NORMAL 40078 +#define ID_OPTIONS_FILTER_2XSAI 40079 +#define ID_OPTIONS_FILTER_SUPER2XSAI 40081 +#define ID_OPTIONS_FILTER_SUPEREAGLE 40082 +#define ID_OPTIONS_FILTER_TVMODE 40083 +#define ID_CHEATS_CHEATLIST 40084 +#define ID_OPTIONS_JOYPAD_AUTOFIRE_A 40085 +#define ID_OPTIONS_JOYPAD_AUTOFIRE_B 40086 +#define ID_OPTIONS_JOYPAD_AUTOFIRE_L 40087 +#define ID_OPTIONS_JOYPAD_AUTOFIRE_R 40088 +#define ID_OPTIONS_VIDEO_FULLSCREENSTRETCHTOFIT 40089 +#define ID_OPTIONS_EMULATOR_ASSOCIATE 40091 +#define ID_OPTIONS_FILTER_DISABLEMMX 40093 +#define ID_FILE_ROMINFORMATION 40100 +#define ID_CHEATS_ADDCHEATCODE 40101 +#define ID_OPTIONS_EMULATOR_DISABLESTATUSMESSAGES 40102 +#define ID_OPTIONS_JOYPAD_MOTIONCONFIGURE 40103 +#define ID_FILE_SCREENCAPTURE 40104 +#define ID_OPTIONS_LANGUAGE_SYSTEM 40105 +#define ID_OPTIONS_LANGUAGE_ENGLISH 40106 +#define ID_OPTIONS_LANGUAGE_OTHER 40107 +#define ID_OPTIONS_GAMEBOY_PRINTER 40108 +#define ID_FILE_RECENT_RESET 40109 +#define ID_CHEATS_SAVECHEATLIST 40110 +#define ID_CHEATS_LOADCHEATLIST 40111 +#define ID_CHEATS_AUTOMATICSAVELOADCHEATS 40112 +#define ID_FILE_IMPORT_GAMESHARKSNAPSHOT 40115 +#define ID_FILE_IMPORT_BATTERYFILE 40116 +#define ID_FILE_IMPORT_GAMESHARKCODEFILE 40117 +#define ID_FILE_EXPORT_BATTERYFILE 40118 +#define ID_OPTIONS_FILTER16BIT_PIXELATEEXPERIMENTAL 40121 +#define ID_OPTIONS_FILTER16BIT_MOTIONBLUREXPERIMENTAL 40122 +#define ID_OPTIONS_EMULATOR_PAUSEWHENINACTIVE 40124 +#define ID_OPTIONS_SOUND_STARTRECORDING 40125 +#define ID_OPTIONS_SOUND_STOPRECORDING 40126 +#define ID_OPTIONS_VIDEO_LAYERS_BG0 40127 +#define ID_OPTIONS_VIDEO_LAYERS_BG1 40128 +#define ID_OPTIONS_VIDEO_LAYERS_BG2 40129 +#define ID_OPTIONS_VIDEO_LAYERS_BG3 40130 +#define ID_OPTIONS_VIDEO_LAYERS_OBJ 40131 +#define ID_OPTIONS_VIDEO_LAYERS_WIN0 40132 +#define ID_OPTIONS_VIDEO_LAYERS_WIN1 40133 +#define ID_OPTIONS_VIDEO_LAYERS_OBJWIN 40134 +#define ID_FILE_OPEN_GB 40135 +#define ID_DEBUG_NEXTFRAME 40137 +#define ID_TOOLS_MAPVIEW 40138 +#define ID_TOOLS_PALETTEVIEW 40139 +#define ID_OPTIONS_EMULATOR_PNGFORMAT 40140 +#define ID_OPTIONS_EMULATOR_BMPFORMAT 40141 +#define ID_TOOLS_MEMORYVIEWER 40143 +#define ID_TOOLS_OAMVIEWER 40144 +#define ID_TOOLS_CUSTOMIZE 40145 +#define ID_TOOLS_TILEVIEWER 40146 +#define ID_OPTIONS_GAMEBOY_COLORS 40147 +#define ID_TOOLS_DISASSEMBLE 40151 +#define ID_TOOLS_DEBUG_GDB 40152 +#define ID_TOOLS_DEBUG_LOADANDWAIT 40153 +#define ID_TOOLS_DEBUG_DISCONNECT 40154 +#define ID_TOOLS_DEBUG_BREAK 40155 +#define ID_TOOLS_LOGGING 40156 +#define ID_OPTIONS_EMULATOR_SPEEDHACK 40157 +#define ID_OPTIONS_EMULATOR_SPEEDUPTOGGLE 40158 +#define ID_OPTIONS_FILTER16BIT_ADVANCEMAMESCALE2X 40160 +#define ID_OPTIONS_FILTER16BIT_SIMPLE2X 40161 +#define ID_FILE_RECENT_FREEZE 40162 +#define ID_FILE_EXPORT_GAMESHARKSNAPSHOT 40163 +#define ID_OPTIONS_VIDEO_FULLSCREEN800X600 40164 +#define ID_OPTIONS_VIDEO_FRAMESKIP_6 40165 +#define ID_OPTIONS_VIDEO_FRAMESKIP_7 40166 +#define ID_OPTIONS_VIDEO_FRAMESKIP_8 40167 +#define ID_OPTIONS_VIDEO_FRAMESKIP_9 40168 +#define ID_OPTIONS_EMULATOR_SAVETYPE_AUTOMATIC 40169 +#define ID_OPTIONS_EMULATOR_SAVETYPE_EEPROM 40170 +#define ID_OPTIONS_EMULATOR_SAVETYPE_SRAM 40171 +#define ID_OPTIONS_EMULATOR_SAVETYPE_FLASH 40172 +#define ID_OPTIONS_EMULATOR_SAVETYPE_EEPROMSENSOR 40173 +#define ID_OPTIONS_EMULATOR_SAVETYPE_FLASH512K 40174 +#define ID_OPTIONS_EMULATOR_SAVETYPE_FLASH1M 40175 +#define ID_OPTIONS_EMULATOR_AUTOMATICALLYAPPLYPATCHFILES 40176 +#define ID_TOOLS_RECORD_STARTAVIRECORDING 40178 +#define ID_TOOLS_RECORD_STOPAVIRECORDING 40179 +#define ID_OPTIONS_FILTER_BILINEAR 40186 +#define ID_OPTIONS_FILTER_BILINEARPLUS 40187 +#define ID_OPTIONS_FILTER_INTERFRAMEBLENDING_NONE 40188 +#define ID_OPTIONS_FILTER_INTERFRAMEBLENDING_MOTIONBLUR 40189 +#define ID_OPTIONS_FILTER_INTERFRAMEBLENDING_SMART 40190 +#define ID_OPTIONS_VIDEO_FULLSCREEN 40191 +#define ID_OPTIONS_VIDEO_TRIPLEBUFFERING 40192 +#define ID_OPTIONS_FRAMESKIP_AUTOMATIC 40194 +#define ID_OPTIONS_EMULATOR_SHOWSPEED_NONE 40195 +#define ID_OPTIONS_EMULATOR_SHOWSPEED_PERCENTAGE 40196 +#define ID_OPTIONS_EMULATOR_SHOWSPEED_DETAILED 40197 +#define ID_OPTIONS_EMULATOR_SHOWSPEED_TRANSPARENT 40198 +#define ID_OPTIONS_JOYPAD_CONFIGURE_1 40199 +#define ID_OPTIONS_JOYPAD_CONFIGURE_2 40200 +#define ID_OPTIONS_JOYPAD_CONFIGURE_3 40201 +#define ID_OPTIONS_JOYPAD_CONFIGURE_4 40202 +#define ID_OPTIONS_JOYPAD_DEFAULTJOYPAD_1 40208 +#define ID_OPTIONS_JOYPAD_DEFAULTJOYPAD_2 40209 +#define ID_OPTIONS_JOYPAD_DEFAULTJOYPAD_3 40210 +#define ID_OPTIONS_JOYPAD_DEFAULTJOYPAD_4 40211 +#define ID_OPTIONS_EMULATOR_STORESETTINGSINREGISTRY 40214 +#define ID_FILE_EXPORT_SETTINGSTOINI 40215 +#define ID_OPTIONS_FRAMESKIP_THROTTLE_NOTHROTTLE 40216 +#define ID_OPTIONS_FRAMESKIP_THROTTLE_50 40217 +#define ID_OPTIONS_FRAMESKIP_THROTTLE_150 40218 +#define ID_OPTIONS_FRAMESKIP_THROTTLE_200 40219 +#define ID_OPTIONS_FRAMESKIP_THROTTLE_25 40220 +#define ID_OPTIONS_FRAMESKIP_THROTTLE_OTHER 40221 +#define ID_OPTIONS_FRAMESKIP_THROTTLE_100 40222 +#define ID_OPTIONS_FILTER_SCANLINES 40223 +#define ID_OPTIONS_VIDEO_RENDERMETHOD_GDI 40228 +#define ID_OPTIONS_VIDEO_RENDERMETHOD_DIRECTDRAW 40229 +#define ID_OPTIONS_VIDEO_RENDERMETHOD_DIRECT3D 40230 +#define ID_OPTIONS_VIDEO_RENDERMETHOD_OPENGL 40231 +#define ID_OPTIONS_VIDEO_RENDEROPTIONS_D3DNOFILTER 40233 +#define ID_OPTIONS_VIDEO_RENDEROPTIONS_D3DBILINEAR 40234 +#define ID_OPTIONS_VIDEO_RENDEROPTIONS_D3DTRILINEAR 40235 +#define ID_OPTIONS_VIDEO_RENDEROPTIONS_D3DANISOTROPIC 40236 +#define ID_OPTIONS_VIDEO_RENDEROPTIONS_GLNEAREST 40237 +#define ID_OPTIONS_VIDEO_RENDEROPTIONS_GLBILINEAR 40238 +#define ID_OPTIONS_VIDEO_RENDEROPTIONS_GLTRIANGLE 40239 +#define ID_OPTIONS_VIDEO_RENDEROPTIONS_GLQUADS 40240 +#define ID_OPTIONS_EMULATOR_AGBPRINT 40247 +#define ID_OPTIONS_EMULATOR_REALTIMECLOCK 40248 +#define ID_OPTIONS_GAMEBOY_SGB2 40249 +#define ID_SYSTEM_MINIMIZE 40250 +#define ID_TOOLS_RECORD_STARTMOVIERECORDING 40251 +#define ID_TOOLS_RECORD_STOPMOVIERECORDING 40252 +#define ID_TOOLS_PLAY_STARTMOVIEPLAYING 40253 +#define ID_TOOLS_PLAY_STOPMOVIEPLAYING 40254 +#define ID_OPTIONS_GAMEBOY_BORDERAUTOMATIC 40256 +#define ID_OPTIONS_EMULATOR_REWIND 40257 +#define ID_TOOLS_REWIND 40258 +#define ID_OPTIONS_EMULATOR_SKIPBIOS 40259 +#define ID_HELP_BUGREPORT 40260 +#define ID_HELP_FAQ 40261 +#define ID_OPTIONS_EMULATOR_REWINDINTERVAL 40262 +#define ID_FILE_TOGGLEMENU 40263 +#define ID_OPTIONS_EMULATOR_SAVETYPE_NONE 40264 +#define ID_TOOLS_IOVIEWER 40266 +#define ID_FILE_LOADGAME_MOSTRECENT 40267 +#define ID_FILE_SAVEGAME_OLDESTSLOT 40268 +#define ID_FILE_LOADGAME_AUTOLOADMOSTRECENT 40269 +#define ID_CHEATS_DISABLECHEATS 40272 +#define ID_OPTIONS_VIDEO_FULLSCREENMAXSCALE 40273 +#define ID_OPTIONS_FILTER_HQ2X 40274 +#define ID_OPTIONS_FILTER_LQ2X 40275 +#define ID_OPTIONS_EMULATOR_GAMEOVERRIDES 40276 +#define ID_HELP_GNUPUBLICLICENSE 40277 +#define ID_OPTIONS_SOUND_HARDWAREACCELERATION 40281 +#define ID_OPTIONS_VIDEO_FULLSCREEN1024X768 40282 +#define ID_OPTIONS_VIDEO_FULLSCREEN1280X1024 40283 +#define ID_OPTIONS_FILTER_SIMPLE3X 40287 +#define ID_OPTIONS_FILTER_SIMPLE4X 40288 +#define ID_OPTIONS_FILTER_HQ3X 40290 +#define ID_OPTIONS_FILTER_HQ4X 40291 +#define ID_VIDEO_WINDOWED 40292 +#define ID_VIDEO_FULL 40293 +#define ID_OPTIONS_SOUND_PCMINTERPOLATION_NONE 40294 +#define ID_OPTIONS_SOUND_PCMINTERPOLATION_LINEAR 40295 +#define ID_OPTIONS_SOUND_PCMINTERPOLATION_CUBIC 40296 +#define ID_OPTIONS_SOUND_PCMINTERPOLATION_FIR 40297 +#define ID_OPTIONS_SOUND_PCMINTERPOLATION_LIBRESAMPLE 40298 +#define IDD_LINKTAB1 40300 +#define IDD_LINKTAB 40301 +#define IDD_LINKTAB2 40302 +#define IDD_LINKTAB3 40303 +#define IDD_SERVERWAIT 40304 +#define IDC_TAB1 40305 +#define IDC_LINK_SINGLE 40306 +#define IDC_LINK_TIMEOUT 40307 +#define IDC_LINK_LAN 40308 +#define IDC_LINK2P 40309 +#define IDC_LINKTCP 40310 +#define IDC_SSPEED 40311 +#define IDC_SERVERSTART 40312 +#define IDC_SERVERIP 40313 +#define IDC_CLINKIP 40314 +#define IDC_SPEEDOFF 40315 +#define IDC_LINKCONNECT 40316 +#define ID_OPTIONS_LINK_OPTIONS 40318 +#define ID_OPTIONS_LINK_LOG 40319 +#define ID_OPTIONS_LINK_WIRELESSADAPTER 40320 +#define IDC_LINKTIMEOUT 40321 +#define IDC_CLINKTCP 40322 +#define IDC_SERVERWAIT 40323 +#define IDC_LINKUDP 40324 +#define IDC_LINK3P 40325 +#define IDC_LINK4P 40326 +#define IDC_CLINKUDP 40327 +#define IDC_SPEEDON 40328 +#define ID_OPTIONS_EMULATOR_REMOVEINTROSGBA 40331 +#define ID_Menu 40332 +#define ID_OPTIONS_VIDEO_RENDEROPTIONS_GLANISOTROPIC 40333 +#define ID_OPTIONS_LINK_ENABLE 40335 +#define ID_RENDERAPI_VERTEX 40336 +#define ID_OPTIONS_VIDEO_RENDEROPTIONS_GLPOLYGONS 40337 +#define ID_VIDEO_SKIN 40338 +#define ID_OPTIONS_UI 40339 +#define ID_USERINTERFACE_SKIN 40340 +#define ID_SKIN_USE 40341 +#define ID_SKIN_SELECT 40342 +#define ID_OPTIONS_FILTER_PLUGIN 40343 +#define ID_OPTIONS_SELECT_PLUGIN 40344 +#define IDC_COMBO_PLUGIN 40345 +#define ID_OUTPUTAPI_DIRECTSOUND 40346 +#define ID_OUTPUTAPI_OPENAL 40347 +#define ID_OUTPUTAPI_SOFTWAREMIXING 40348 +#define ID_OUTPUTAPI_CONFIGURATION 40349 +#define ID_OUTPUTAPI_OALCONFIGURATION 40350 +#define ID_RENDERAPI_FILTER 40351 +#define ID_OPTIONS_SPEED 40353 +#define ID_RENDERAPI_MOTIONBLUR 40354 +#define ID_RENDERAPI_D3DMOTIONBLUR 40355 +#define ID_EMULATOR_BIOSFILES 40356 +#define ID_FILE_OPEN_GBC 40358 +#define ID_OUTPUTAPI_XAUDIO2 40359 +#define ID_PIXELFILTER_MULTI 40360 +#define ID_EMULATOR_LOADSTATESHOULD 40361 +#define ID_LOADGAME_DONOTCHANGEBATTERYSAVE 40362 +#define ID_OUTPUTAPI_CONFIGURATION40363 40363 +#define ID_OUTPUTAPI_XAUDIO2CONFIG 40364 +#define ID_AUDIO_CORE_SETTINGS 40365 +#define ID_FILE_OPEN_GBA 40366 +#define ID_OPTIONS_VIDEO_LAYERS_RESET 40367 +#define ID_LOADGAME_DONOTCHANGECHEATLIST 40371 +#define ID_OPTIONS_EMULATOR_SAVETYPE_DETECTNOW 40372 +#define ID_FILE_PAUSE 40373 +#define ID_OPTIONS_EMULATOR_DIRECTORIES 40374 +#define ID_Menu40375 40375 +#define ID_OPTIONS_JOYBUS 40376 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 166 +#define _APS_NEXT_COMMAND_VALUE 40377 +#define _APS_NEXT_CONTROL_VALUE 1299 +#define _APS_NEXT_SYMED_VALUE 103 +#endif +#endif diff --git a/src/win32/rpi.cpp b/src/win32/rpi.cpp new file mode 100644 index 0000000..c5ae774 --- /dev/null +++ b/src/win32/rpi.cpp @@ -0,0 +1,188 @@ +#include "stdafx.h" +#include "VBA.h" +#include "rpi.h" + +extern void SuperEagle(u8*,u32,u8*,u8*,u32,int,int); + +static HINSTANCE rpiDLL = NULL; +static RENDPLUG_Output fnOutput = NULL; +static RENDPLUG_GetInfo fnGetInfo = NULL; +static RENDER_PLUGIN_INFO MyPlugInfo; +static RENDER_PLUGIN_OUTP MyPlugOutput; +static int nScaleFactor; + +extern int systemRedShift, systemGreenShift, systemBlueShift; +extern int realsystemRedShift, realsystemGreenShift, realsystemBlueShift; +extern int realsystemColorDepth; +u8 *pBuffer16 = NULL; +u32 Buffer16Size = 0; + +bool rpiInit(const char *sPluginName) +{ + rpiCleanup(); + + char sBuffer[256]; + char *ptr; + + GetModuleFileName(NULL, sBuffer, sizeof(sBuffer)); + ptr = strrchr(sBuffer, '\\'); + if (ptr) + *ptr = '\0'; + strcat(sBuffer, "\\plugins\\"); + strcat(sBuffer, sPluginName); + + rpiDLL = LoadLibrary(sBuffer); + if (!rpiDLL) + return false; + + fnGetInfo = (RENDPLUG_GetInfo) GetProcAddress(rpiDLL, "RenderPluginGetInfo"); + fnOutput = (RENDPLUG_Output) GetProcAddress(rpiDLL, "RenderPluginOutput"); + if (fnGetInfo == NULL || fnOutput == NULL) + { + FreeLibrary(rpiDLL); + rpiDLL = NULL; + return false; + } + + RENDER_PLUGIN_INFO *pRPI = fnGetInfo(); + if (pRPI == NULL) + { + FreeLibrary(rpiDLL); + rpiDLL = NULL; + return false; + } + + memcpy(&MyPlugInfo, pRPI, sizeof(MyPlugInfo)); + + unsigned long Flags = MyPlugInfo.Flags & 0x0000F0000; + + if (Flags == RPI_OUT_SCL2) + { + nScaleFactor = 2; + } + else if (Flags == RPI_OUT_SCL3) + { + nScaleFactor = 3; + } + else if (Flags == RPI_OUT_SCL4) + { + nScaleFactor = 4; + } + else + { + nScaleFactor = 2; + } + + return true; +} + +void rpiFilter(u8 *srcPtr, u32 srcPitch, u8 *deltaPtr, u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u8 *pBuff; + + if (realsystemColorDepth == 32) + { + // Kega filters are 16 bit only. Assumes we've forced 16 bit input + ASSERT(systemColorDepth == 16); + u32 bufferNeeded = dstPitch * (height + nScaleFactor) * nScaleFactor; + if (Buffer16Size < bufferNeeded) + { + Buffer16Size = bufferNeeded; + if (pBuffer16) + free(pBuffer16); + pBuffer16 = (u8 *)malloc(Buffer16Size); + } + pBuff = pBuffer16; + } + else + pBuff = dstPtr; + + MyPlugOutput.Size = sizeof(MyPlugOutput); + MyPlugOutput.Flags = MyPlugInfo.Flags; + MyPlugOutput.SrcPtr = srcPtr; + MyPlugOutput.SrcPitch = srcPitch; + MyPlugOutput.SrcW = width; + // Without this funky math on the height value, the RPI filter isn't fully + // rendering the frame. I don't like passing in values that seem + // to be greater than the buffer size, but it's the only way to get + // proper results. + MyPlugOutput.SrcH = height+(nScaleFactor/2); + MyPlugOutput.DstPtr = pBuff; + MyPlugOutput.DstPitch = dstPitch; + MyPlugOutput.DstW = width * nScaleFactor; + MyPlugOutput.DstH = (height+(nScaleFactor/2)) * nScaleFactor; + MyPlugOutput.OutW = width * nScaleFactor; + MyPlugOutput.OutH = (height+(nScaleFactor/2)) * nScaleFactor; + + fnOutput(&MyPlugOutput); + + if (realsystemColorDepth == 32) + { + register int i,j; + int rshiftDiff = realsystemRedShift - systemRedShift; + int gshiftDiff = realsystemGreenShift - systemGreenShift; + int bshiftDiff = realsystemBlueShift - systemBlueShift; + + u16 *pI, *pICur; + u32 *pO, *pOCur; + + pI = pICur = (u16 *)pBuff; + pO = pOCur = (u32 *)dstPtr; + + if (rshiftDiff >= 0) + { + for(j=0;j> rshiftDiff) | + ((*pICur & 0x07E0) << gshiftDiff) | + ((*pICur & 0x001F) << bshiftDiff); + *(pICur++); + } + pI = pICur = (u16 *)((char *)pI + dstPitch); + pO = pOCur = (u32 *)((char *)pO + dstPitch); + } + + } + } +} + +int rpiScaleFactor() +{ + return nScaleFactor; +} + +void rpiCleanup() +{ + if (rpiDLL != NULL) + { + FreeLibrary(rpiDLL); + rpiDLL = NULL; + } + if (pBuffer16) + { + free(pBuffer16); + pBuffer16 = NULL; + Buffer16Size = 0; + } +} diff --git a/src/win32/rpi.h b/src/win32/rpi.h new file mode 100644 index 0000000..c519567 --- /dev/null +++ b/src/win32/rpi.h @@ -0,0 +1,76 @@ +#pragma once + +//--------------------------------------------------------------------------------------------------------------------------- +// hq2x plugin example - Steve Snake 2004. +// This plugin uses (modified) code by Maxim Stepin - see "hq2x16.asm" for info +// The original code and description of the algorithm can be found at: +// http://www.hiend3d.com/hq2x.html +// Modified by suanyuan +//--------------------------------------------------------------------------------------------------------------------------- +#if defined(_WIN32) +#include +#else +#define HMODULE void * +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +typedef struct +{ + unsigned long Size; + unsigned long Flags; + void *SrcPtr; + unsigned long SrcPitch; + unsigned long SrcW; + unsigned long SrcH; + void *DstPtr; + unsigned long DstPitch; + unsigned long DstW; + unsigned long DstH; + unsigned long OutW; + unsigned long OutH; +} RENDER_PLUGIN_OUTP; + +//--------------------------------------------------------------------------------------------------------------------------- + +typedef void (*RENDPLUG_Output)(RENDER_PLUGIN_OUTP *); + +//--------------------------------------------------------------------------------------------------------------------------- + +typedef struct +{ + char Name[60]; + unsigned long Flags; + HMODULE Handle; + RENDPLUG_Output Output; +} RENDER_PLUGIN_INFO; + +//--------------------------------------------------------------------------------------------------------------------------- + +typedef RENDER_PLUGIN_INFO *(*RENDPLUG_GetInfo)(void); + +//--------------------------------------------------------------------------------------------------------------------------- + +#define RPI_VERSION 0x02 + +#define RPI_MMX_USED 0x000000100 +#define RPI_MMX_REQD 0x000000200 +#define RPI_555_SUPP 0x000000400 +#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(); +bool rpiInit(const char *sPluginName); +void rpiFilter(u8 *srcPtr, u32 srcPitch, u8 *deltaPtr, u8 *dstPtr, u32 dstPitch, int width, int height); +void rpiCleanup(); diff --git a/src/win32/stdafx.cpp b/src/win32/stdafx.cpp new file mode 100644 index 0000000..fd4f341 --- /dev/null +++ b/src/win32/stdafx.cpp @@ -0,0 +1 @@ +#include "stdafx.h" diff --git a/src/win32/stdafx.h b/src/win32/stdafx.h new file mode 100644 index 0000000..c0430d9 --- /dev/null +++ b/src/win32/stdafx.h @@ -0,0 +1,30 @@ +#pragma once + +// make windows controls look newer / enable visual styles: +#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' ""version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") + +#ifndef VC_EXTRALEAN +#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers +#endif + +#ifndef NO_STRICT +#define STRICT +#endif + +#include "targetver.h" + +#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit + +// turns off MFC's hiding of some common and often safely ignored warning messages +#define _AFX_ALL_WARNINGS + +#include // MFC core and standard components +#include // MFC extensions + + +#ifndef _AFX_NO_OLE_SUPPORT +#include // MFC support for Internet Explorer 4 Common Controls +#endif +#ifndef _AFX_NO_AFXCMN_SUPPORT +#include // MFC support for Windows Common Controls +#endif // _AFX_NO_AFXCMN_SUPPORT diff --git a/src/win32/targetver.h b/src/win32/targetver.h new file mode 100644 index 0000000..4f07adf --- /dev/null +++ b/src/win32/targetver.h @@ -0,0 +1,24 @@ +#pragma once + +// The following macros define the minimum required platform. The minimum required platform +// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run +// your application. The macros work by enabling all features available on platform versions up to and +// including the version specified. + +// Modify the following defines if you have to target a platform prior to the ones specified below. +// Refer to MSDN for the latest info on corresponding values for different platforms. +#ifndef WINVER // Specifies that the minimum required platform is Windows 2000. +#define WINVER 0x0500 // Change this to the appropriate value to target other versions of Windows. +#endif + +#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows 2000. +#define _WIN32_WINNT 0x0500 // Change this to the appropriate value to target other versions of Windows. +#endif + +#ifndef _WIN32_WINDOWS // Specifies that the minimum required platform is Windows 98. +#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later. +#endif + +#ifndef _WIN32_IE // Specifies that the minimum required platform is Internet Explorer 5.0. +#define _WIN32_IE 0x0500 // Change this to the appropriate value to target other versions of IE. +#endif // Actually, we don't care diff --git a/src/wx/CMakeLists.txt b/src/wx/CMakeLists.txt new file mode 100644 index 0000000..4723b26 --- /dev/null +++ b/src/wx/CMakeLists.txt @@ -0,0 +1,238 @@ +# 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 ) + option( ENABLE_XAUDIO2 "Enable xaudio2 sound output for the wxWidgets port" OFF ) +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_XAUDIO2 ) + ADD_DEFINITIONS (-DNO_XAUDIO2) +endif( NOT ENABLE_XAUDIO2 ) + +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 +# xml, html is for xrc +SET( wxWidgets_USE_LIBS xrc xml html adv gl net core base ) +FIND_PACKAGE ( wxWidgets REQUIRED ) +EXECUTE_PROCESS(COMMAND sh "${wxWidgets_CONFIG_EXECUTABLE}" --cxxflags) +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(ENABLE_XAUDIO2) + SET( SRC_WX ${SRC_WX} xaudio2.cpp ) +ENDIF(ENABLE_XAUDIO2) + +IF( WIN32 ) + SET( SRC_WX ${SRC_WX} wxvbam.rc dsound.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) diff --git a/src/wx/bin2c.cmake b/src/wx/bin2c.cmake new file mode 100644 index 0000000..6aaa17e --- /dev/null +++ b/src/wx/bin2c.cmake @@ -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}) diff --git a/src/wx/cmdevents.cpp b/src/wx/cmdevents.cpp new file mode 100644 index 0000000..90fe589 --- /dev/null +++ b/src/wx/cmdevents.cpp @@ -0,0 +1,2316 @@ +#ifndef NO_FFMPEG +#define __STDC_LIMIT_MACROS // required for ffmpeg +#define __STDC_CONSTANT_MACROS // required for ffmpeg +#endif +#include "wxvbam.h" +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef NO_FFMPEG +extern "C" { +#include +} +#endif +#include "../gb/gbPrinter.h" +#include "../gba/agbprint.h" + +#define GetXRCDialog(n) \ + wxStaticCast(FindWindow(XRCID(n)), wxDialog) + +bool cmditem_lt(const struct cmditem &cmd1, const struct cmditem &cmd2) +{ + return wxStrcmp(cmd1.cmd, cmd2.cmd) < 0; +} + +#define update_bcheck(s, f) do { \ + f = !f; \ + int id = XRCID(s); \ + for(int i = 0; i < checkable_mi.size(); i++) { \ + if(checkable_mi[i].cmd != id) \ + continue; \ + f = checkable_mi[i].mi->IsChecked(); \ + break; \ + } \ +} while(0) + +#define update_icheck(s, f, m, v) do { \ + bool is_checked = ((f) & (m)) != (v); \ + int id = XRCID(s); \ + for(int i = 0; i < checkable_mi.size(); i++) { \ + if(checkable_mi[i].cmd != id) \ + continue; \ + is_checked = checkable_mi[i].mi->IsChecked(); \ + break; \ + } \ + f = ((f) & ~(m)) | (is_checked ? (v) : 0); \ +} while(0) +#define update_icheck1(s, f, m) update_icheck(s, f, m, m) + +#define update_check(s, v) do { \ + int id = XRCID(s); \ + for(int i = 0; i < checkable_mi.size(); i++) { \ + if(checkable_mi[i].cmd != id) \ + continue; \ + checkable_mi[i].mi->Check(v); \ + break; \ + } \ +} while(0) + +//// File menu + +// formerly OpenGBA, OpenGBC, OpenGB +// having just one means separate ROM dirs only make sense on the cmd line + +static int open_ft = 0; +static wxString open_dir; + +EVT_HANDLER(wxID_OPEN, "Open ROM...") +{ + if(!open_dir.size()) + open_dir = gopts.gba_rom_dir; + // FIXME: ignore if non-existent or not a dir + wxString pats = _("GameBoy Advance Files (*.agb;*.gba;*.bin;*.elf;*.mb)|" + "*.agb;*.gba;*.bin;*.elf;*.mb" + "*.agb.gz;*.gba.gz;*.bin.gz;*.elf.gz;*.mb.gz" + "*.agb.z;*.gba.z;*.bin.z;*.elf.z;*.mb.z" + "|GameBoy Files (*.dmg;*.gb;*.gbc;*.cgb;*.sgb)|" + "*.dmg;*.gb;*.gbc;*.cgb;*.sgb" + "*.dmg.gz;*.gb.gz;*.gbc.gz;*.cgb.gz;*.sgb.gz" + "*.dmg.z;*.gb.z;*.gbc.z;*.cgb.z;*.sgb.z" + "|Archives (*.zip;*.7z;*.rar)" + "|*.zip;*.7z;*.rar|"); + pats.append(wxALL_FILES); + wxFileDialog dlg(this, _("Open ROM file"), open_dir, wxT(""), + pats, + wxFD_OPEN|wxFD_FILE_MUST_EXIST); + dlg.SetFilterIndex(open_ft); + if(ShowModal(&dlg) == wxID_OK) + wxGetApp().pending_load = dlg.GetPath(); + open_ft = dlg.GetFilterIndex(); + open_dir = dlg.GetDirectory(); +} + +EVT_HANDLER(RecentReset, "Reset recent ROM list") +{ + // only save config if there were items to remove + if(gopts.recent->GetCount()) { + while(gopts.recent->GetCount()) + gopts.recent->RemoveFileFromHistory(0); + wxConfig *cfg = wxGetApp().cfg; + cfg->SetPath(wxT("/Recent")); + gopts.recent->Save(*cfg); + cfg->SetPath(wxT("/")); + cfg->Flush(); + } +} + +EVT_HANDLER(RecentFreeze, "Freeze recent ROM list (toggle)") +{ + update_bcheck("RecentFreeze", gopts.recent_freeze); + update_opts(); +} + +// following 10 should really be a single ranged handler +// former names: Recent01 .. Recent10 +EVT_HANDLER(wxID_FILE1, "Load recent ROM 1") +{ + panel->LoadGame(gopts.recent->GetHistoryFile(0)); +} + +EVT_HANDLER(wxID_FILE2, "Load recent ROM 2") +{ + panel->LoadGame(gopts.recent->GetHistoryFile(1)); +} + +EVT_HANDLER(wxID_FILE3, "Load recent ROM 3") +{ + panel->LoadGame(gopts.recent->GetHistoryFile(2)); +} + +EVT_HANDLER(wxID_FILE4, "Load recent ROM 4") +{ + panel->LoadGame(gopts.recent->GetHistoryFile(3)); +} + +EVT_HANDLER(wxID_FILE5, "Load recent ROM 5") +{ + panel->LoadGame(gopts.recent->GetHistoryFile(4)); +} + +EVT_HANDLER(wxID_FILE6, "Load recent ROM 6") +{ + panel->LoadGame(gopts.recent->GetHistoryFile(5)); +} + +EVT_HANDLER(wxID_FILE7, "Load recent ROM 7") +{ + panel->LoadGame(gopts.recent->GetHistoryFile(6)); +} + +EVT_HANDLER(wxID_FILE8, "Load recent ROM 8") +{ + panel->LoadGame(gopts.recent->GetHistoryFile(7)); +} + +EVT_HANDLER(wxID_FILE9, "Load recent ROM 9") +{ + panel->LoadGame(gopts.recent->GetHistoryFile(8)); +} + +EVT_HANDLER(wxID_FILE10, "Load recent ROM 10") +{ + panel->LoadGame(gopts.recent->GetHistoryFile(9)); +} + +static const struct rom_maker { + const wxChar *code, *name; +} makers[] = { + { wxT("01"), wxT("Nintendo") }, + { wxT("02"), wxT("Rocket Games") }, + { wxT("08"), wxT("Capcom") }, + { wxT("09"), wxT("Hot B Co.") }, + { wxT("0A"), wxT("Jaleco") }, + { wxT("0B"), wxT("Coconuts Japan") }, + { wxT("0C"), wxT("Coconuts Japan/G.X.Media") }, + { wxT("0H"), wxT("Starfish") }, + { wxT("0L"), wxT("Warashi Inc.") }, + { wxT("0N"), wxT("Nowpro") }, + { wxT("0P"), wxT("Game Village") }, + { wxT("13"), wxT("Electronic Arts Japan") }, + { wxT("18"), wxT("Hudson Soft Japan") }, + { wxT("19"), wxT("S.C.P.") }, + { wxT("1A"), wxT("Yonoman") }, + { wxT("1G"), wxT("SMDE") }, + { wxT("1P"), wxT("Creatures Inc.") }, + { wxT("1Q"), wxT("TDK Deep Impresion") }, + { wxT("20"), wxT("Destination Software") }, + { wxT("22"), wxT("VR 1 Japan") }, + { wxT("25"), wxT("San-X") }, + { wxT("28"), wxT("Kemco Japan") }, + { wxT("29"), wxT("Seta") }, + { wxT("2H"), wxT("Ubisoft Japan") }, + { wxT("2K"), wxT("NEC InterChannel") }, + { wxT("2L"), wxT("Tam") }, + { wxT("2M"), wxT("Jordan") }, + { wxT("2N"), wxT("Smilesoft") }, + { wxT("2Q"), wxT("Mediakite") }, + { wxT("36"), wxT("Codemasters") }, + { wxT("37"), wxT("GAGA Communications") }, + { wxT("38"), wxT("Laguna") }, + { wxT("39"), wxT("Telstar Fun and Games") }, + { wxT("41"), wxT("Ubi Soft Entertainment") }, + { wxT("42"), wxT("Sunsoft") }, + { wxT("47"), wxT("Spectrum Holobyte") }, + { wxT("49"), wxT("IREM") }, + { wxT("4D"), wxT("Malibu Games") }, + { wxT("4F"), wxT("Eidos/U.S. Gold") }, + { wxT("4J"), wxT("Fox Interactive") }, + { wxT("4K"), wxT("Time Warner Interactive") }, + { wxT("4Q"), wxT("Disney") }, + { wxT("4S"), wxT("Black Pearl") }, + { wxT("4X"), wxT("GT Interactive") }, + { wxT("4Y"), wxT("RARE") }, + { wxT("4Z"), wxT("Crave Entertainment") }, + { wxT("50"), wxT("Absolute Entertainment") }, + { wxT("51"), wxT("Acclaim") }, + { wxT("52"), wxT("Activision") }, + { wxT("53"), wxT("American Sammy Corp.") }, + { wxT("54"), wxT("Take 2 Interactive") }, + { wxT("55"), wxT("Hi Tech") }, + { wxT("56"), wxT("LJN LTD.") }, + { wxT("58"), wxT("Mattel") }, + { wxT("5A"), wxT("Mindscape/Red Orb Ent.") }, + { wxT("5C"), wxT("Taxan") }, + { wxT("5D"), wxT("Midway") }, + { wxT("5F"), wxT("American Softworks") }, + { wxT("5G"), wxT("Majesco Sales Inc") }, + { wxT("5H"), wxT("3DO") }, + { wxT("5K"), wxT("Hasbro") }, + { wxT("5L"), wxT("NewKidCo") }, + { wxT("5M"), wxT("Telegames") }, + { wxT("5N"), wxT("Metro3D") }, + { wxT("5P"), wxT("Vatical Entertainment") }, + { wxT("5Q"), wxT("LEGO Media") }, + { wxT("5S"), wxT("Xicat Interactive") }, + { wxT("5T"), wxT("Cryo Interactive") }, + { wxT("5W"), wxT("Red Storm Ent./BKN Ent.") }, + { wxT("5X"), wxT("Microids") }, + { wxT("5Z"), wxT("Conspiracy Entertainment Corp.") }, + { wxT("60"), wxT("Titus Interactive Studios") }, + { wxT("61"), wxT("Virgin Interactive") }, + { wxT("62"), wxT("Maxis") }, + { wxT("64"), wxT("LucasArts Entertainment") }, + { wxT("67"), wxT("Ocean") }, + { wxT("69"), wxT("Electronic Arts") }, + { wxT("6E"), wxT("Elite Systems Ltd.") }, + { wxT("6F"), wxT("Electro Brain") }, + { wxT("6G"), wxT("The Learning Company") }, + { wxT("6H"), wxT("BBC") }, + { wxT("6J"), wxT("Software 2000") }, + { wxT("6L"), wxT("BAM! Entertainment") }, + { wxT("6M"), wxT("Studio 3") }, + { wxT("6Q"), wxT("Classified Games") }, + { wxT("6S"), wxT("TDK Mediactive") }, + { wxT("6U"), wxT("DreamCatcher") }, + { wxT("6V"), wxT("JoWood Productions") }, + { wxT("6W"), wxT("SEGA") }, + { wxT("6X"), wxT("Wannado Edition") }, + { wxT("6Y"), wxT("LSP") }, + { wxT("6Z"), wxT("ITE Media") }, + { wxT("70"), wxT("Infogrames") }, + { wxT("71"), wxT("Interplay") }, + { wxT("72"), wxT("JVC Musical Industries Inc") }, + { wxT("73"), wxT("Parker Brothers") }, + { wxT("75"), wxT("SCI") }, + { wxT("78"), wxT("THQ") }, + { wxT("79"), wxT("Accolade") }, + { wxT("7A"), wxT("Triffix Ent. Inc.") }, + { wxT("7C"), wxT("Microprose Software") }, + { wxT("7D"), wxT("Universal Interactive Studios") }, + { wxT("7F"), wxT("Kemco") }, + { wxT("7G"), wxT("Rage Software") }, + { wxT("7H"), wxT("Encore") }, + { wxT("7J"), wxT("Zoo") }, + { wxT("7K"), wxT("BVM") }, + { wxT("7L"), wxT("Simon & Schuster Interactive") }, + { wxT("7M"), wxT("Asmik Ace Entertainment Inc./AIA") }, + { wxT("7N"), wxT("Empire Interactive") }, + { wxT("7Q"), wxT("Jester Interactive") }, + { wxT("7T"), wxT("Scholastic") }, + { wxT("7U"), wxT("Ignition Entertainment") }, + { wxT("7W"), wxT("Stadlbauer") }, + { wxT("80"), wxT("Misawa") }, + { wxT("83"), wxT("LOZC") }, + { wxT("8B"), wxT("Bulletproof Software") }, + { wxT("8C"), wxT("Vic Tokai Inc.") }, + { wxT("8J"), wxT("General Entertainment") }, + { wxT("8N"), wxT("Success") }, + { wxT("8P"), wxT("SEGA Japan") }, + { wxT("91"), wxT("Chun Soft") }, + { wxT("92"), wxT("Video System") }, + { wxT("93"), wxT("BEC") }, + { wxT("96"), wxT("Yonezawa/S'pal") }, + { wxT("97"), wxT("Kaneko") }, + { wxT("99"), wxT("Victor Interactive Software") }, + { wxT("9A"), wxT("Nichibutsu/Nihon Bussan") }, + { wxT("9B"), wxT("Tecmo") }, + { wxT("9C"), wxT("Imagineer") }, + { wxT("9F"), wxT("Nova") }, + { wxT("9H"), wxT("Bottom Up") }, + { wxT("9L"), wxT("Hasbro Japan") }, + { wxT("9N"), wxT("Marvelous Entertainment") }, + { wxT("9P"), wxT("Keynet Inc.") }, + { wxT("9Q"), wxT("Hands-On Entertainment") }, + { wxT("A0"), wxT("Telenet") }, + { wxT("A1"), wxT("Hori") }, + { wxT("A4"), wxT("Konami") }, + { wxT("A6"), wxT("Kawada") }, + { wxT("A7"), wxT("Takara") }, + { wxT("A9"), wxT("Technos Japan Corp.") }, + { wxT("AA"), wxT("JVC") }, + { wxT("AC"), wxT("Toei Animation") }, + { wxT("AD"), wxT("Toho") }, + { wxT("AF"), wxT("Namco") }, + { wxT("AG"), wxT("Media Rings Corporation") }, + { wxT("AH"), wxT("J-Wing") }, + { wxT("AK"), wxT("KID") }, + { wxT("AL"), wxT("MediaFactory") }, + { wxT("AP"), wxT("Infogrames Hudson") }, + { wxT("AQ"), wxT("Kiratto. Ludic Inc") }, + { wxT("B0"), wxT("Acclaim Japan") }, + { wxT("B1"), wxT("ASCII") }, + { wxT("B2"), wxT("Bandai") }, + { wxT("B4"), wxT("Enix") }, + { wxT("B6"), wxT("HAL Laboratory") }, + { wxT("B7"), wxT("SNK") }, + { wxT("B9"), wxT("Pony Canyon Hanbai") }, + { wxT("BA"), wxT("Culture Brain") }, + { wxT("BB"), wxT("Sunsoft") }, + { wxT("BD"), wxT("Sony Imagesoft") }, + { wxT("BF"), wxT("Sammy") }, + { wxT("BG"), wxT("Magical") }, + { wxT("BJ"), wxT("Compile") }, + { wxT("BL"), wxT("MTO Inc.") }, + { wxT("BN"), wxT("Sunrise Interactive") }, + { wxT("BP"), wxT("Global A Entertainment") }, + { wxT("BQ"), wxT("Fuuki") }, + { wxT("C0"), wxT("Taito") }, + { wxT("C2"), wxT("Kemco") }, + { wxT("C3"), wxT("Square Soft") }, + { wxT("C5"), wxT("Data East") }, + { wxT("C6"), wxT("Tonkin House") }, + { wxT("C8"), wxT("Koei") }, + { wxT("CA"), wxT("Konami/Palcom/Ultra") }, + { wxT("CB"), wxT("Vapinc/NTVIC") }, + { wxT("CC"), wxT("Use Co.,Ltd.") }, + { wxT("CD"), wxT("Meldac") }, + { wxT("CE"), wxT("FCI/Pony Canyon") }, + { wxT("CF"), wxT("Angel") }, + { wxT("CM"), wxT("Konami Computer Entertainment Osaka") }, + { wxT("CP"), wxT("Enterbrain") }, + { wxT("D1"), wxT("Sofel") }, + { wxT("D2"), wxT("Quest") }, + { wxT("D3"), wxT("Sigma Enterprises") }, + { wxT("D4"), wxT("Ask Kodansa") }, + { wxT("D6"), wxT("Naxat") }, + { wxT("D7"), wxT("Copya System") }, + { wxT("D9"), wxT("Banpresto") }, + { wxT("DA"), wxT("TOMY") }, + { wxT("DB"), wxT("LJN Japan") }, + { wxT("DD"), wxT("NCS") }, + { wxT("DF"), wxT("Altron Corporation") }, + { wxT("DH"), wxT("Gaps Inc.") }, + { wxT("DN"), wxT("ELF") }, + { wxT("E2"), wxT("Yutaka") }, + { wxT("E3"), wxT("Varie") }, + { wxT("E5"), wxT("Epoch") }, + { wxT("E7"), wxT("Athena") }, + { wxT("E8"), wxT("Asmik Ace Entertainment Inc.") }, + { wxT("E9"), wxT("Natsume") }, + { wxT("EA"), wxT("King Records") }, + { wxT("EB"), wxT("Atlus") }, + { wxT("EC"), wxT("Epic/Sony Records") }, + { wxT("EE"), wxT("IGS") }, + { wxT("EL"), wxT("Spike") }, + { wxT("EM"), wxT("Konami Computer Entertainment Tokyo") }, + { wxT("EN"), wxT("Alphadream Corporation") }, + { wxT("F0"), wxT("A Wave") }, + { wxT("G1"), wxT("PCCW") }, + { wxT("G4"), wxT("KiKi Co Ltd") }, + { wxT("G5"), wxT("Open Sesame Inc.") }, + { wxT("G6"), wxT("Sims") }, + { wxT("G7"), wxT("Broccoli") }, + { wxT("G8"), wxT("Avex") }, + { wxT("G9"), wxT("D3 Publisher") }, + { wxT("GB"), wxT("Konami Computer Entertainment Japan") }, + { wxT("GD"), wxT("Square-Enix") }, + { wxT("HY"), wxT("Sachen") } +}; +#define num_makers (sizeof(makers)/sizeof(makers[0])) +static bool maker_lt(const rom_maker &r1, const rom_maker &r2) +{ + return wxStrcmp(r1.code, r2.code) < 0; +} + +EVT_HANDLER_MASK(RomInformation, "ROM information...", CMDEN_GB|CMDEN_GBA) +{ + wxString s; +#define setlab(id) do { \ + /* SetLabelText is not in 2.8 */ \ + s.Replace(wxT("&"), wxT("&&"), true); \ + XRCCTRL(*dlg, id, wxControl)->SetLabel(s); \ +} while(0) +#define setblab(id, b) do { \ + s.Printf(wxT("%02x"), (unsigned int)b); \ + setlab(id); \ +} while(0) +#define setblabs(id, b, ts) do { \ + s.Printf(wxT("%02x (%s)"), (unsigned int)b, ts); \ + setlab(id); \ +} while(0) +#define setlabs(id, ts, l) do { \ + s = wxString((const char *)&(ts), wxConvLibc, l); \ + setlab(id); \ +} while(0) + switch(panel->game_type()) { + case IMAGE_GB: + { + wxDialog *dlg = GetXRCDialog("GBROMInfo"); + setlabs("Title", gbRom[0x134], 15); + setblab("Color", gbRom[0x143]); + if(gbRom[0x14b] == 0x33) + s = wxString((const char *)&gbRom[0x144], wxConvUTF8, 2); + else + s.Printf(wxT("%02x"), gbRom[0x14b]); + setlab("MakerCode"); + const rom_maker m = { s.c_str() }, *rm; + rm = std::lower_bound(&makers[0], &makers[num_makers], m, maker_lt); + if(rm < &makers[num_makers] && !wxStrcmp(m.code, rm->code)) + s = rm->name; + else + s = _("Unknown"); + setlab("MakerName"); + setblab("UnitCode", gbRom[0x146]); + const wxChar *type; + switch(gbRom[0x147]) { + case 0x00: + type = _("ROM"); + break; + case 0x01: + type = _("ROM+MBC1"); + break; + case 0x02: + type = _("ROM+MBC1+RAM"); + break; + case 0x03: + type = _("ROM+MBC1+RAM+BATT"); + break; + case 0x05: + type = _("ROM+MBC2"); + break; + case 0x06: + type = _("ROM+MBC2+BATT"); + break; + case 0x0b: + type = _("ROM+MMM01"); + break; + case 0x0c: + type = _("ROM+MMM01+RAM"); + break; + case 0x0d: + type = _("ROM+MMM01+RAM+BATT"); + break; + case 0x0f: + type = _("ROM+MBC3+TIMER+BATT"); + break; + case 0x10: + type = _("ROM+MBC3+TIMER+RAM+BATT"); + break; + case 0x11: + type = _("ROM+MBC3"); + break; + case 0x12: + type = _("ROM+MBC3+RAM"); + break; + case 0x13: + type = _("ROM+MBC3+RAM+BATT"); + break; + case 0x19: + type = _("ROM+MBC5"); + break; + case 0x1a: + type = _("ROM+MBC5+RAM"); + break; + case 0x1b: + type = _("ROM+MBC5+RAM+BATT"); + break; + case 0x1c: + type = _("ROM+MBC5+RUMBLE"); + break; + case 0x1d: + type = _("ROM+MBC5+RUMBLE+RAM"); + break; + case 0x1e: + type = _("ROM+MBC5+RUMBLE+RAM+BATT"); + break; + case 0x22: + type = _("ROM+MBC7+BATT"); + break; + case 0x55: + type = _("GameGenie"); + break; + case 0x56: + type = _("GameShark V3.0"); + break; + case 0xfc: + type = _("ROM+POCKET CAMERA"); + break; + case 0xfd: + type = _("ROM+BANDAI TAMA5"); + break; + case 0xfe: + type = _("ROM+HuC-3"); + break; + case 0xff: + type = _("ROM+HuC-1"); + break; + default: + type = _("Unknown"); + } + setblabs("DeviceType", gbRom[0x147], type); + switch(gbRom[0x148]) { + case 0: + type = wxT("32K"); + break; + case 1: + type = wxT("64K"); + break; + case 2: + type = wxT("128K"); + break; + case 3: + type = wxT("256K"); + break; + case 4: + type = wxT("512K"); + break; + case 5: + type = wxT("1M"); + break; + case 6: + type = wxT("2M"); + break; + case 7: + type = wxT("4M"); + break; + default: + type = _("Unknown"); + } + setblabs("ROMSize", gbRom[0x148], type); + switch(gbRom[0x149]) { + case 0: + type = _("None"); + break; + case 1: + type = wxT("2K"); + break; + case 2: + type = wxT("8K"); + break; + case 3: + type = wxT("32K"); + break; + case 4: + type = wxT("128K"); + break; + case 5: + type = wxT("64K"); + break; + } + setblabs("RAMSize", gbRom[0x149], type); + setblab("DestCode", gbRom[0x14a]); + setblab("LicCode", gbRom[0x14b]); + setblab("Version", gbRom[0x14c]); + u8 crc = 25; + for(int i = 0x134; i < 0x14d; i++) + crc += gbRom[i]; + crc = 256 - crc; + s.Printf(wxT("%02x (%02x)"), crc, gbRom[0x14d]); + setlab("CRC"); + u16 crc16 = 0; + for(int i = 0; i < gbRomSize; i++) + crc16 += gbRom[i]; + crc16 -= gbRom[0x14e] + gbRom[0x14f]; + s.Printf(wxT("%04x (%04x)"), crc16, gbRom[0x14e]*256+gbRom[0x14f]); + setlab("Checksum"); + dlg->Fit(); + ShowModal(dlg); + } + break; + case IMAGE_GBA: + { + wxDialog *dlg = GetXRCDialog("GBAROMInfo"); + setlabs("Title", rom[0xa0], 12); + setlabs("GameCode", rom[0xac], 4); + setlabs("MakerCode", rom[0xb0], 2); + const rom_maker m = { s.c_str() }, *rm; + rm = std::lower_bound(&makers[0], &makers[num_makers], m, maker_lt); + if(rm < &makers[num_makers] && !wxStrcmp(m.code, rm->code)) + s = rm->name; + else + s = _("Unknown"); + setlab("MakerName"); + setblab("UnitCode", rom[0xb3]); + s.Printf(wxT("%02x"), (unsigned int)rom[0xb4]); + if(rom[0xb4] & 0x80) + s.append(wxT(" (DACS)")); + setlab("DeviceType"); + setblab("Version", rom[0xbc]); + u8 crc = 0x19; + for(int i = 0xa0; i < 0xbd; i++) + crc += rom[i]; + crc = -crc; + s.Printf(wxT("%02x (%02x)"), crc, rom[0xbd]); + setlab("CRC"); + dlg->Fit(); + ShowModal(dlg); + } + break; + } +} + +static wxString batimp_path; + +EVT_HANDLER_MASK(ImportBatteryFile, "Import battery file...", CMDEN_GB|CMDEN_GBA) +{ + if(!batimp_path.size()) + batimp_path = panel->bat_dir(); + wxFileDialog dlg(this, _("Select battery file"), batimp_path, wxEmptyString, + _("Battery file (*.sav)|*.sav|Flash save (*.dat)|*.dat"), wxFD_OPEN|wxFD_FILE_MUST_EXIST); + int ret = ShowModal(&dlg); + batimp_path = dlg.GetDirectory(); + if(ret != wxID_OK) + return; + wxString fn = dlg.GetPath(); + ret = wxMessageBox(_("Importing a battery file will erase any saved games (permanently after the next write). Do you want to continue?"), + _("Confirm import"), wxYES_NO|wxICON_EXCLAMATION); + if(ret == wxYES) { + wxString msg; + if(panel->emusys->emuReadBattery(fn.mb_fn_str())) + msg.Printf(_("Loaded battery %s"), fn.c_str()); + else + msg.Printf(_("Error loading battery %s"), fn.c_str()); + systemScreenMessage(msg); + } +} + +EVT_HANDLER_MASK(ImportGamesharkCodeFile, "Import GameShark code file...", CMDEN_GB|CMDEN_GBA) +{ + static wxString path; + wxFileDialog dlg(this, _("Select code file"), path, wxEmptyString, + panel->game_type() == IMAGE_GBA ? + _("Gameshark Code File (*.spc;*.xpc)|*.spc;*.xpc"): + _("Gameshark Code File (*.gcf)|*.gcf"), + wxFD_OPEN|wxFD_FILE_MUST_EXIST); + int ret = ShowModal(&dlg); + path = dlg.GetDirectory(); + if(ret != wxID_OK) + return; + wxString fn = dlg.GetPath(); + ret = wxMessageBox(_("Importing a code file will replace any loaded cheats. Do you want to continue?"), + _("Confirm import"), wxYES_NO|wxICON_EXCLAMATION); + if(ret == wxYES) { + wxString msg; + bool res; + if(panel->game_type() == IMAGE_GB) + // FIXME: this routine will not work on big-endian systems + // if the underlying file format is little-endian + // (fix in gb/gbCheats.cpp) + res = gbCheatReadGSCodeFile(fn.mb_fn_str()); + else { + // need to select game first + wxFFile f(fn, wxT("rb")); + if(!f.IsOpened()) { + wxLogError(_("Cannot open file %s"), fn.c_str()); + return; + } + // FIXME: in my code, I assume file format is little-endian + // however, in core code, it is assumed to be native-endian + u32 len; + char buf[14]; + if(f.Read(&len, sizeof(len)) != sizeof(len) || + wxUINT32_SWAP_ON_BE(len) != 14 || + f.Read(buf, 14) != 14 || memcmp(buf, "SharkPortCODES", 14)) { + wxLogError(_("Unsupported code file %s"), fn.c_str()); + return; + } + f.Seek(0x1e); + if(f.Read(&len, sizeof(len)) != sizeof(len)) + len = 0; + u32 game = 0; + if(len > 1) { + wxDialog *seldlg = GetXRCDialog("CodeSelect"); + wxControlWithItems *lst = XRCCTRL(*seldlg, "CodeList", wxControlWithItems); + lst->Clear(); + while(len-- > 0) { + u32 slen; + if(f.Read(&slen, sizeof(slen)) != sizeof(slen) || + slen > 1024) // arbitrary upper bound + break; + char buf[slen]; + if(f.Read(buf, slen) != slen) + break; + lst->Append(wxString(buf, wxConvLibc, slen)); + u32 ncodes; + if(f.Read(&ncodes, sizeof(ncodes)) != sizeof(ncodes)) + break; + for(; ncodes > 0; ncodes--) { + if(f.Read(&slen, sizeof(slen)) != sizeof(slen)) + break; + f.Seek(slen, wxFromCurrent); + if(f.Read(&slen, sizeof(slen)) != sizeof(slen)) + break; + f.Seek(slen + 4, wxFromCurrent); + if(f.Read(&slen, sizeof(slen)) != sizeof(slen)) + break; + f.Seek(slen * 12, wxFromCurrent); + } + } + int sel = ShowModal(seldlg); + if(sel != wxID_OK) + return; + game = lst->GetSelection(); + if(game == wxNOT_FOUND) + game = 0; + } + bool v3 = fn.size() >= 4 && + wxString(fn.substr(fn.size() - 4)).IsSameAs(wxT(".xpc"), false); + + // FIXME: this routine will not work on big-endian systems + // if the underlying file format is little-endian + // (fix in gba/Cheats.cpp) + res = cheatsImportGSACodeFile(fn.mb_fn_str(), game, v3); + } + if(res) + msg.Printf(_("Loaded code file %s"), fn.c_str()); + else + msg.Printf(_("Error loading code file %s"), fn.c_str()); + systemScreenMessage(msg); + } +} + +static wxString gss_path; + +EVT_HANDLER_MASK(ImportGamesharkActionReplaySnapshot, + "Import GameShark Action Replay snapshot...", CMDEN_GB|CMDEN_GBA) +{ + wxFileDialog dlg(this, _("Select snapshot file"), gss_path, wxEmptyString, + panel->game_type() == IMAGE_GBA ? + _("GS & PAC Snapshots (*.sps;*.xps)|*.sps;*.xps|GameShark SP Snapshots (*.gsv)|*.gsv"): + _("Gameboy Snapshot (*.gbs)|*.gbs"), + wxFD_OPEN|wxFD_FILE_MUST_EXIST); + int ret = ShowModal(&dlg); + gss_path = dlg.GetDirectory(); + if(ret != wxID_OK) + return; + wxString fn = dlg.GetPath(); + ret = wxMessageBox(_("Importing a snapshot file will erase any saved games (permanently after the next write). Do you want to continue?"), + _("Confirm import"), wxYES_NO|wxICON_EXCLAMATION); + if(ret == wxYES) { + wxString msg; + bool res; + if(panel->game_type() == IMAGE_GB) + res = gbReadGSASnapshot(fn.mb_fn_str()); + else { + bool gsv = fn.size() >= 4 && + wxString(fn.substr(fn.size() - 4)).IsSameAs(wxT(".gsv"), false); + if(gsv) + // FIXME: this will fail on big-endian machines if + // file format is little-endian + // fix in GBA.cpp + res = CPUReadGSASPSnapshot(fn.mb_fn_str()); + else + // FIXME: this will fail on big-endian machines if + // file format is little-endian + // fix in GBA.cpp + res = CPUReadGSASnapshot(fn.mb_fn_str()); + } + if(res) + msg.Printf(_("Loaded snapshot file %s"), fn.c_str()); + else + msg.Printf(_("Error loading snapshot file %s"), fn.c_str()); + systemScreenMessage(msg); + } +} + +EVT_HANDLER_MASK(ExportBatteryFile, "Export battery file...", CMDEN_GB|CMDEN_GBA) +{ + if(!batimp_path.size()) + batimp_path = panel->bat_dir(); + wxFileDialog dlg(this, _("Select battery file"), batimp_path, wxEmptyString, + _("Battery file (*.sav)|*.sav|Flash save (*.dat)|*.dat"), wxFD_SAVE|wxFD_OVERWRITE_PROMPT); + int ret = ShowModal(&dlg); + batimp_path = dlg.GetDirectory(); + if(ret != wxID_OK) + return; + wxString fn = dlg.GetPath(); + wxString msg; + if(panel->emusys->emuWriteBattery(fn.mb_fn_str())) + msg.Printf(_("Wrote battery %s"), fn.c_str()); + else + msg.Printf(_("Error writing battery %s"), fn.c_str()); + systemScreenMessage(msg); +} + +EVT_HANDLER_MASK(ExportGamesharkSnapshot, "Export GameShark snapshot...", CMDEN_GBA) +{ + if(eepromInUse) { + wxLogError(_("EEPROM saves cannot be exported")); + return; + } + wxString def_name = panel->game_name(); + def_name.append(wxT(".sps")); + wxFileDialog dlg(this, _("Select snapshot file"), gss_path, def_name, + _("Gameshark Snapshot (*.sps)|*.sps"), wxFD_SAVE|wxFD_OVERWRITE_PROMPT); + int ret = ShowModal(&dlg); + gss_path = dlg.GetDirectory(); + if(ret != wxID_OK) + return; + wxString fn = dlg.GetPath(); + wxDialog *infodlg = GetXRCDialog("ExportSPS"); + wxTextCtrl *tit = XRCCTRL(*infodlg, "Title", wxTextCtrl), + *dsc = XRCCTRL(*infodlg, "Description", wxTextCtrl), + *n = XRCCTRL(*infodlg, "Notes", wxTextCtrl); + tit->SetValue(wxString((const char *)&rom[0xa0], wxConvLibc, 12)); + dsc->SetValue(wxDateTime::Now().Format(wxT("%c"))); + n->SetValue(_("Exported from VisualBoyAdvance-M")); + if(ShowModal(infodlg) != wxID_OK) + return; + wxString msg; + + // FIXME: this will fail on big-endian machines if file format is + // little-endian + // fix in GBA.cpp + if(CPUWriteGSASnapshot(fn.mb_fn_str(), tit->GetValue().utf8_str(), + dsc->GetValue().utf8_str(), n->GetValue().utf8_str())) + msg.Printf(_("Saved snapshot file %s"), fn.c_str()); + else + msg.Printf(_("Error saving snapshot file %s"), fn.c_str()); + systemScreenMessage(msg); +} + +EVT_HANDLER_MASK(ScreenCapture, "Screen capture...", CMDEN_GB|CMDEN_GBA) +{ + static wxString scap_path; + if(!scap_path.size()) { + scap_path = gopts.scrshot_dir; + if(scap_path.size()) { + wxFileName sp(scap_path, wxEmptyString); + if(!sp.IsAbsolute()) + scap_path = panel->game_dir() + wxT('/') + gopts.scrshot_dir; + wxFileName::Mkdir(scap_path, 0777, wxPATH_MKDIR_FULL); + } + } + wxString def_name = panel->game_name(); + if(gopts.cap_format == 0) + def_name.append(wxT(".png")); + else + def_name.append(wxT(".bmp")); + wxFileDialog dlg(this, _("Select output file"), scap_path, def_name, + _("PNG images|*.png|BMP images|*.bmp"), wxFD_SAVE|wxFD_OVERWRITE_PROMPT); + dlg.SetFilterIndex(gopts.cap_format); + int ret = ShowModal(&dlg); + scap_path = dlg.GetDirectory(); + if(ret != wxID_OK) + return; + wxString fn = dlg.GetPath(); + int fmt = dlg.GetFilterIndex(); + if(fn.size() >= 4) { + if(wxString(fn.substr(fn.size() - 4)).IsSameAs(wxT(".bmp"), false)) + fmt = 1; + else if(wxString(fn.substr(fn.size() - 4)).IsSameAs(wxT(".png"), false)) + fmt = 0; + } + if(fmt == 0) + panel->emusys->emuWritePNG(fn.mb_fn_str()); + else + panel->emusys->emuWriteBMP(fn.mb_fn_str()); + wxString msg; + msg.Printf(_("Wrote snapshot %s"), fn.c_str()); + systemScreenMessage(msg); +} + +EVT_HANDLER_MASK(RecordSoundStartRecording, "Start sound recording...", CMDEN_NSREC) +{ +#ifndef NO_FFMPEG + static wxString sound_exts; + static int sound_extno; + static wxString sound_path; + + if(!sound_exts.size()) { + sound_extno = -1; + int extno; + AVOutputFormat *fmt; + for(fmt = NULL, extno = 0; (fmt = av_oformat_next(fmt)); ) { + if(!fmt->extensions) + continue; + if(fmt->audio_codec == CODEC_ID_NONE) + continue; + sound_exts.append(wxString(fmt->long_name ? fmt->long_name : fmt->name, wxConvLibc)); + sound_exts.append(_(" files (")); + wxString ext(fmt->extensions, wxConvLibc); + ext.Replace(wxT(","), wxT(";*.")); + ext.insert(0, wxT("*.")); + if(sound_extno < 0 && ext.find(wxT("*.wav")) != wxString::npos) + sound_extno = extno; + sound_exts.append(ext); + sound_exts.append(wxT(")|")); + sound_exts.append(ext); + sound_exts.append(wxT('|')); + extno++; + } + sound_exts.append(wxALL_FILES); + if(sound_extno < 0) + sound_extno = extno; + } + if(!sound_path.size()) { + if(!gopts.recording_dir.size()) + sound_path = panel->game_dir(); + else { + wxFileName sp(gopts.recording_dir, wxEmptyString); + if(sp.IsAbsolute()) + sound_path = gopts.recording_dir; + else + sound_path = panel->game_dir() + wxT('/') + gopts.recording_dir; + } + wxFileName::Mkdir(sound_path, 0777, wxPATH_MKDIR_FULL); + } + wxString def_name = panel->game_name(); + const wxChar *extoff = sound_exts.c_str(); + for(int i = 0; i < sound_extno; i++) { + extoff = wxStrchr(extoff, wxT('|')) + 1; + extoff = wxStrchr(extoff, wxT('|')) + 1; + } + extoff = wxStrchr(extoff, wxT('|')) + 2; // skip * + def_name += wxString(extoff, wxStrcspn(extoff, wxT(";|"))); + wxFileDialog dlg(this, _("Select output file"), sound_path, def_name, + sound_exts, wxFD_SAVE|wxFD_OVERWRITE_PROMPT); + dlg.SetFilterIndex(sound_extno); + int ret = ShowModal(&dlg); + sound_extno = dlg.GetFilterIndex(); + sound_path = dlg.GetDirectory(); + if(ret != wxID_OK) + return; + panel->StartSoundRecording(dlg.GetPath()); +#endif +} + +EVT_HANDLER_MASK(RecordSoundStopRecording, "Stop sound recording", CMDEN_SREC) +{ +#ifndef NO_FFMPEG + panel->StopSoundRecording(); +#endif +} + +EVT_HANDLER_MASK(RecordAVIStartRecording, "Start video recording...", CMDEN_NVREC) +{ +#ifndef NO_FFMPEG + static wxString vid_exts; + static int vid_extno; + static wxString vid_path; + + if(!vid_exts.size()) { + vid_extno = -1; + int extno; + AVOutputFormat *fmt; + for(fmt = NULL, extno = 0; (fmt = av_oformat_next(fmt)); ) { + if(!fmt->extensions) + continue; + if(fmt->video_codec == CODEC_ID_NONE) + continue; + vid_exts.append(wxString(fmt->long_name ? fmt->long_name : fmt->name, wxConvLibc)); + vid_exts.append(_(" files (")); + wxString ext(fmt->extensions, wxConvLibc); + ext.Replace(wxT(","), wxT(";*.")); + ext.insert(0, wxT("*.")); + if(vid_extno < 0 && ext.find(wxT("*.avi")) != wxString::npos) + vid_extno = extno; + vid_exts.append(ext); + vid_exts.append(wxT(")|")); + vid_exts.append(ext); + vid_exts.append(wxT('|')); + extno++; + } + vid_exts.append(wxALL_FILES); + if(vid_extno < 0) + vid_extno = extno; + } + if(!vid_path.size()) { + if(!gopts.recording_dir.size()) + vid_path = panel->game_dir(); + else { + wxFileName sp(gopts.recording_dir, wxEmptyString); + if(sp.IsAbsolute()) + vid_path = gopts.recording_dir; + else + vid_path = panel->game_dir() + wxT('/') + gopts.recording_dir; + } + wxFileName::Mkdir(vid_path, 0777, wxPATH_MKDIR_FULL); + } + wxString def_name = panel->game_name(); + const wxChar *extoff = vid_exts.c_str(); + for(int i = 0; i < vid_extno; i++) { + extoff = wxStrchr(extoff, wxT('|')) + 1; + extoff = wxStrchr(extoff, wxT('|')) + 1; + } + extoff = wxStrchr(extoff, wxT('|')) + 2; // skip * + def_name += wxString(extoff, wxStrcspn(extoff, wxT(";|"))); + wxFileDialog dlg(this, _("Select output file"), vid_path, def_name, + vid_exts, wxFD_SAVE|wxFD_OVERWRITE_PROMPT); + dlg.SetFilterIndex(vid_extno); + int ret = ShowModal(&dlg); + vid_extno = dlg.GetFilterIndex(); + vid_path = dlg.GetDirectory(); + if(ret != wxID_OK) + return; + panel->StartVidRecording(dlg.GetPath()); +#endif +} + +EVT_HANDLER_MASK(RecordAVIStopRecording, "Stop video recording", CMDEN_VREC) +{ +#ifndef NO_FFMPEG + panel->StopVidRecording(); +#endif +} + +static wxString mov_path; + +EVT_HANDLER_MASK(RecordMovieStartRecording, "Start game recording...", CMDEN_NGREC) +{ + if(!mov_path.size()) { + if(!gopts.recording_dir.size()) + mov_path = panel->game_dir(); + else { + wxFileName sp(gopts.recording_dir, wxEmptyString); + if(sp.IsAbsolute()) + mov_path = gopts.recording_dir; + else + mov_path = panel->game_dir() + wxT('/') + gopts.recording_dir; + } + wxFileName::Mkdir(mov_path, 0777, wxPATH_MKDIR_FULL); + } + wxString def_name = panel->game_name() + wxT(".vmv"); + wxFileDialog dlg(this, _("Select output file"), mov_path, def_name, + _("VBA Movie files|*.vmv"), wxFD_SAVE|wxFD_OVERWRITE_PROMPT); + int ret = ShowModal(&dlg); + mov_path = dlg.GetDirectory(); + if(ret != wxID_OK) + return; + systemStartGameRecording(dlg.GetPath()); +} + +EVT_HANDLER_MASK(RecordMovieStopRecording, "Stop game recording", CMDEN_GREC) +{ + systemStopGameRecording(); +} + +EVT_HANDLER_MASK(PlayMovieStartPlaying, "Start playing movie...", CMDEN_NGREC|CMDEN_NGPLAY) +{ + if(!mov_path.size()) { + if(!gopts.recording_dir.size()) + mov_path = panel->game_dir(); + else { + wxFileName sp(gopts.recording_dir, wxEmptyString); + if(sp.IsAbsolute()) + mov_path = gopts.recording_dir; + else + mov_path = panel->game_dir() + wxT('/') + gopts.recording_dir; + } + if(!wxFileName::DirExists(mov_path)) + mov_path = wxFileName::GetCwd(); + } + systemStopGamePlayback(); + wxString def_name = panel->game_name() + wxT(".vmv"); + wxFileDialog dlg(this, _("Select file"), mov_path, def_name, + _("VBA Movie files|*.vmv"), wxFD_OPEN|wxFD_FILE_MUST_EXIST); + int ret = ShowModal(&dlg); + mov_path = dlg.GetDirectory(); + if(ret != wxID_OK) + return; + systemStartGamePlayback(dlg.GetPath()); +} + +EVT_HANDLER_MASK(PlayMovieStopPlaying, "Stop playing movie", CMDEN_GPLAY) +{ + systemStopGamePlayback(); +} + +// formerly Close +EVT_HANDLER_MASK(wxID_CLOSE, "Close", CMDEN_GB|CMDEN_GBA) +{ + panel->UnloadGame(); +} + +// formerly Exit +EVT_HANDLER(wxID_EXIT, "Exit") +{ + Close(false); +} + + +// Emulation menu +EVT_HANDLER(Pause, "Pause (toggle)") +{ + update_bcheck("Pause", paused); + if(paused) + panel->Pause(); + else if(!IsPaused()) + panel->Resume(); + // undo next-frame's zeroing of frameskip + int fs = panel->game_type() == IMAGE_GB ? gopts.gb_frameskip : gopts.gba_frameskip; + if(fs > 0) + systemFrameSkip = fs; +} + +// new +EVT_HANDLER_MASK(EmulatorSpeedupToggle, "Turbo mode (toggle)", CMDEN_GB|CMDEN_GBA) +{ + update_bcheck("EmulatorSpeedupToggle", turbo); +} + +EVT_HANDLER_MASK(Reset, "Reset", CMDEN_GB|CMDEN_GBA) +{ + panel->emusys->emuReset(); + // systemScreenMessage("Reset"); +} + +EVT_HANDLER(ToggleFullscreen, "Full screen (toggle)") +{ + panel->ShowFullScreen(!IsFullScreen()); +} + +EVT_HANDLER(JoypadAutofireA, "Autofire A (toggle)") +{ + update_icheck1("JoypadAutofireA", autofire, KEYM_A); +} + +EVT_HANDLER(JoypadAutofireB, "Autofire B (toggle)") +{ + update_icheck1("JoypadAutofireB", autofire, KEYM_B); +} + +EVT_HANDLER(JoypadAutofireL, "Autofire L (toggle)") +{ + update_icheck1("JoypadAutofireL", autofire, KEYM_LEFT); +} + +EVT_HANDLER(JoypadAutofireR, "Autofire R (toggle)") +{ + update_icheck1("JoypadAutofireR", autofire, KEYM_RIGHT); +} + +// new +EVT_HANDLER_MASK(LanLink, "Start LAN link", CMDEN_LINK_ANY) +{ +#ifndef NO_LINK + if(lanlink.connected) { + // while we could deactivate the command when connected, it is more + // user-friendly to display a message indidcating why + wxLogError(_("LAN link is already active. Disable link mode to disconnect.")); + return; + } + if(rfu_enabled) { + // see above comment + wxLogError(_("RFU is currently only supported in local mode.")); + return; + } + wxDialog *dlg = GetXRCDialog("NetLink"); + ShowModal(dlg); + panel->SetFrameTitle(); +#endif +} + +EVT_HANDLER_MASK(LoadGameRecent, "Load most recent save", CMDEN_SAVST) +{ + panel->LoadState(); +} + +EVT_HANDLER(LoadGameAutoLoad, "Auto load most recent save (toggle)") +{ + update_bcheck("LoadGameAutoLoad", gopts.autoload_state); + update_opts(); +} + +EVT_HANDLER_MASK(LoadGame01, "Load saved state 1", CMDEN_SAVST) +{ + panel->LoadState(1); +} + +EVT_HANDLER_MASK(LoadGame02, "Load saved state 2", CMDEN_SAVST) +{ + panel->LoadState(2); +} + +EVT_HANDLER_MASK(LoadGame03, "Load saved state 3", CMDEN_SAVST) +{ + panel->LoadState(3); +} + +EVT_HANDLER_MASK(LoadGame04, "Load saved state 4", CMDEN_SAVST) +{ + panel->LoadState(4); +} + +EVT_HANDLER_MASK(LoadGame05, "Load saved state 5", CMDEN_SAVST) +{ + panel->LoadState(5); +} + +EVT_HANDLER_MASK(LoadGame06, "Load saved state 6", CMDEN_SAVST) +{ + panel->LoadState(6); +} + +EVT_HANDLER_MASK(LoadGame07, "Load saved state 7", CMDEN_SAVST) +{ + panel->LoadState(7); +} + +EVT_HANDLER_MASK(LoadGame08, "Load saved state 8", CMDEN_SAVST) +{ + panel->LoadState(8); +} + +EVT_HANDLER_MASK(LoadGame09, "Load saved state 9", CMDEN_SAVST) +{ + panel->LoadState(9); +} + +EVT_HANDLER_MASK(LoadGame10, "Load saved state 10", CMDEN_SAVST) +{ + panel->LoadState(10); +} + +static wxString st_dir; + +EVT_HANDLER_MASK(Load, "Load state...", CMDEN_GB|CMDEN_GBA) +{ + if(st_dir.empty()) + st_dir = panel->state_dir(); + wxFileDialog dlg(this, _("Select state file"), st_dir, wxEmptyString, + _("VisualBoyAdvance saved game files|*.sgm"), wxFD_OPEN|wxFD_FILE_MUST_EXIST); + int ret = ShowModal(&dlg); + st_dir = dlg.GetDirectory(); + if(ret != wxID_OK) + return; + panel->LoadState(dlg.GetPath()); +} + +// new +EVT_HANDLER(KeepSaves, "Do not load battery saves (toggle)") +{ + update_bcheck("KeepSaves", skipSaveGameBattery); + update_opts(); +} + +// new +EVT_HANDLER(KeepCheats, "Do not change cheat list (toggle)") +{ + update_bcheck("KeepCheats", skipSaveGameCheats); + update_opts(); +} + +EVT_HANDLER_MASK(SaveGameOldest, "Save state to oldest slot", CMDEN_GB|CMDEN_GBA) +{ + panel->SaveState(); +} + +EVT_HANDLER_MASK(SaveGame01, "Save state 1", CMDEN_GB|CMDEN_GBA) +{ + panel->SaveState(1); +} + +EVT_HANDLER_MASK(SaveGame02, "Save state 2", CMDEN_GB|CMDEN_GBA) +{ + panel->SaveState(2); +} + +EVT_HANDLER_MASK(SaveGame03, "Save state 3", CMDEN_GB|CMDEN_GBA) +{ + panel->SaveState(3); +} + +EVT_HANDLER_MASK(SaveGame04, "Save state 4", CMDEN_GB|CMDEN_GBA) +{ + panel->SaveState(4); +} + +EVT_HANDLER_MASK(SaveGame05, "Save state 5", CMDEN_GB|CMDEN_GBA) +{ + panel->SaveState(5); +} + +EVT_HANDLER_MASK(SaveGame06, "Save state 6", CMDEN_GB|CMDEN_GBA) +{ + panel->SaveState(6); +} + +EVT_HANDLER_MASK(SaveGame07, "Save state 7", CMDEN_GB|CMDEN_GBA) +{ + panel->SaveState(7); +} + +EVT_HANDLER_MASK(SaveGame08, "Save state 8", CMDEN_GB|CMDEN_GBA) +{ + panel->SaveState(8); +} + +EVT_HANDLER_MASK(SaveGame09, "Save state 9", CMDEN_GB|CMDEN_GBA) +{ + panel->SaveState(9); +} + +EVT_HANDLER_MASK(SaveGame10, "Save state 10", CMDEN_GB|CMDEN_GBA) +{ + panel->SaveState(10); +} + +EVT_HANDLER_MASK(Save, "Save state as...", CMDEN_GB|CMDEN_GBA) +{ + if(st_dir.empty()) + st_dir = panel->state_dir(); + wxFileDialog dlg(this, _("Select state file"), st_dir, wxEmptyString, + _("VisualBoyAdvance saved game files|*.sgm"), wxFD_SAVE|wxFD_OVERWRITE_PROMPT); + int ret = ShowModal(&dlg); + st_dir = dlg.GetDirectory(); + if(ret != wxID_OK) + return; + panel->SaveState(dlg.GetPath()); +} + +static int state_slot = 0; + +// new +EVT_HANDLER_MASK(LoadGameSlot, "Load current state slot", CMDEN_GB|CMDEN_GBA) +{ + panel->LoadState(state_slot + 1); +} + +// new +EVT_HANDLER_MASK(SaveGameSlot, "Save current state slot", CMDEN_GB|CMDEN_GBA) +{ + panel->SaveState(state_slot + 1); +} + +// new +EVT_HANDLER_MASK(IncrGameSlot, "Increase state slot number", CMDEN_GB|CMDEN_GBA) +{ + state_slot = (state_slot + 1) % 10; +} + +// new +EVT_HANDLER_MASK(DecrGameSlot, "Decrease state slot number", CMDEN_GB|CMDEN_GBA) +{ + state_slot = (state_slot + 9) % 10; +} + +// new +EVT_HANDLER_MASK(IncrGameSlotSave, "Increase state slot number and save", CMDEN_GB|CMDEN_GBA) +{ + state_slot = (state_slot + 1) % 10; + panel->SaveState(state_slot + 1); +} + +EVT_HANDLER_MASK(Rewind, "Rewind", CMDEN_REWIND) +{ + MainFrame *mf = wxGetApp().frame; + GameArea *panel = mf->GetPanel(); + int rew_st = (panel->next_rewind_state + NUM_REWINDS - 1) % NUM_REWINDS; + // if within 5 seconds of last one, and > 1 state, delete last state & move back + // FIXME: 5 should actually be user-configurable + // maybe instead of 5, 10% of rewind_interval + if(panel->num_rewind_states > 1 && + (gopts.rewind_interval <= 5 || + panel->rewind_time / 6 > gopts.rewind_interval - 5)) { + --panel->num_rewind_states; + panel->next_rewind_state = rew_st; + if(gopts.rewind_interval > 5) + rew_st = (rew_st + NUM_REWINDS - 1) % NUM_REWINDS; + } + panel->emusys->emuReadMemState(&panel->rewind_mem[rew_st * REWIND_SIZE], + REWIND_SIZE); + InterframeCleanup(); + // FIXME: if(paused) blank screen + panel->do_rewind = false; + panel->rewind_time = gopts.rewind_interval * 6; +// systemScreenMessage(_("Rewinded")); +} + +EVT_HANDLER_MASK(CheatsList, "List cheats...", CMDEN_GB|CMDEN_GBA) +{ + wxDialog *dlg = GetXRCDialog("CheatList"); + ShowModal(dlg); +} + +EVT_HANDLER_MASK(CheatsSearch, "Create cheat...", CMDEN_GB|CMDEN_GBA) +{ + wxDialog *dlg = GetXRCDialog("CheatCreate"); + ShowModal(dlg); +} + +// new +EVT_HANDLER(CheatsAutoSaveLoad, "Auto save/load cheats (toggle)") +{ + update_bcheck("CheatsAutoSaveLoad", gopts.autoload_cheats); + update_opts(); +} + +// was CheatsDisable +// changed for convenience to match internal variable functionality +EVT_HANDLER(CheatsEnable, "Enable cheats (toggle)") +{ + update_bcheck("CheatsEnable", cheatsEnabled); + update_opts(); +} + + +// Debug menu +EVT_HANDLER_MASK(VideoLayersBG0, "Video layer BG0 (toggle)", CMDEN_GB|CMDEN_GBA) +{ + update_icheck1("VideoLayersBG0", layerSettings, (1<<8)); + layerEnable = DISPCNT & layerSettings; + CPUUpdateRenderBuffers(false); +} + +EVT_HANDLER_MASK(VideoLayersBG1, "Video layer BG1 (toggle)", CMDEN_GB|CMDEN_GBA) +{ + update_icheck1("VideoLayersBG1", layerSettings, (1<<9)); + layerEnable = DISPCNT & layerSettings; + CPUUpdateRenderBuffers(false); +} + +EVT_HANDLER_MASK(VideoLayersBG2, "Video layer BG2 (toggle)", CMDEN_GB|CMDEN_GBA) +{ + update_icheck1("VideoLayersBG2", layerSettings, (1<<10)); + layerEnable = DISPCNT & layerSettings; + CPUUpdateRenderBuffers(false); +} + +EVT_HANDLER_MASK(VideoLayersBG3, "Video layer BG3 (toggle)", CMDEN_GB|CMDEN_GBA) +{ + update_icheck1("VideoLayersBG3", layerSettings, (1<<11)); + layerEnable = DISPCNT & layerSettings; + CPUUpdateRenderBuffers(false); +} + +EVT_HANDLER_MASK(VideoLayersOBJ, "Video layer OBJ (toggle)", CMDEN_GB|CMDEN_GBA) +{ + update_icheck1("VideoLayersOBJ", layerSettings, (1<<12)); + layerEnable = DISPCNT & layerSettings; + CPUUpdateRenderBuffers(false); +} + +EVT_HANDLER_MASK(VideoLayersWIN0, "Video layer WIN0 (toggle)", CMDEN_GB|CMDEN_GBA) +{ + update_icheck1("VideoLayersWIN0", layerSettings, (1<<13)); + layerEnable = DISPCNT & layerSettings; + CPUUpdateRenderBuffers(false); +} + +EVT_HANDLER_MASK(VideoLayersWIN1, "Video layer WIN1 (toggle)", CMDEN_GB|CMDEN_GBA) +{ + update_icheck1("VideoLayersWIN1", layerSettings, (1<<14)); + layerEnable = DISPCNT & layerSettings; + CPUUpdateRenderBuffers(false); +} + +EVT_HANDLER_MASK(VideoLayersOBJWIN, "Video layer OBJWIN (toggle)", CMDEN_GB|CMDEN_GBA) +{ + update_icheck1("VideoLayersOBJWIN", layerSettings, (1<<15)); + layerEnable = DISPCNT & layerSettings; + CPUUpdateRenderBuffers(false); +} + +// not in menu +EVT_HANDLER_MASK(VideoLayersReset, "Show all video layers", CMDEN_GB|CMDEN_GBA) +{ +#define set_vl(s) do { \ + int id = XRCID(s); \ + for(int i = 0; i < checkable_mi.size(); i++) \ + if(checkable_mi[i].cmd == id) { \ + checkable_mi[i].mi->Check(true); \ + break; \ + } \ +} while(0) + layerSettings = 0x7f00; + layerEnable = DISPCNT & layerSettings; + set_vl("VideoLayersBG0"); + set_vl("VideoLayersBG1"); + set_vl("VideoLayersBG2"); + set_vl("VideoLayersBG3"); + set_vl("VideoLayersOBJ"); + set_vl("VideoLayersWIN0"); + set_vl("VideoLayersWIN1"); + set_vl("VideoLayersOBJWIN"); + CPUUpdateRenderBuffers(false); +} + +EVT_HANDLER_MASK(SoundChannel1, "Sound Channel 1 (toggle)", CMDEN_GB|CMDEN_GBA) +{ + update_icheck1("SoundChannel1", gopts.sound_en, (1<<0)); + soundSetEnable(gopts.sound_en); + update_opts(); +} + +EVT_HANDLER_MASK(SoundChannel2, "Sound Channel 2 (toggle)", CMDEN_GB|CMDEN_GBA) +{ + update_icheck1("SoundChannel2", gopts.sound_en, (1<<1)); + soundSetEnable(gopts.sound_en); + update_opts(); +} + +EVT_HANDLER_MASK(SoundChannel3, "Sound Channel 3 (toggle)", CMDEN_GB|CMDEN_GBA) +{ + update_icheck1("SoundChannel3", gopts.sound_en, (1<<2)); + soundSetEnable(gopts.sound_en); + update_opts(); +} + +EVT_HANDLER_MASK(SoundChannel4, "Sound Channel 4 (toggle)", CMDEN_GB|CMDEN_GBA) +{ + update_icheck1("SoundChannel4", gopts.sound_en, (1<<3)); + soundSetEnable(gopts.sound_en); + update_opts(); +} + +EVT_HANDLER_MASK(DirectSoundA, "Direct Sound A (toggle)", CMDEN_GBA) +{ + update_icheck1("DirectSoundA", gopts.sound_en, (1<<8)); + soundSetEnable(gopts.sound_en); + update_opts(); +} + +EVT_HANDLER_MASK(DirectSoundB, "Direct Sound B (toggle)", CMDEN_GBA) +{ + update_icheck1("DirectSoundB", gopts.sound_en, (1<<9)); + soundSetEnable(gopts.sound_en); + update_opts(); +} + +EVT_HANDLER(ToggleSound, "Enable/disable all sound channels") +{ + bool en = gopts.sound_en == 0; + gopts.sound_en = en ? 0x30f : 0; + update_check("SoundChannel1", en); + update_check("SoundChannel2", en); + update_check("SoundChannel3", en); + update_check("SoundChannel4", en); + update_check("DirectSoundA", en); + update_check("DirectSoundB", en); + soundSetEnable(gopts.sound_en); + update_opts(); + systemScreenMessage(en ? _("Sound enabled") : _("Sound disabled")); +} + +EVT_HANDLER(IncreaseVolume, "Increase volume") +{ + gopts.sound_vol += 5; + if(gopts.sound_vol > 200) + gopts.sound_vol = 200; + update_opts(); + soundSetVolume((float)gopts.sound_vol / 100.0); + wxString msg; + msg.Printf(_("Volume: %d%%"), gopts.sound_vol); + systemScreenMessage(msg); +} + +EVT_HANDLER(DecreaseVolume, "Decrease volume") +{ + gopts.sound_vol -= 5; + if(gopts.sound_vol < 0) + gopts.sound_vol = 0; + update_opts(); + soundSetVolume((float)gopts.sound_vol / 100.0); + wxString msg; + msg.Printf(_("Volume: %d%%"), gopts.sound_vol); + systemScreenMessage(msg); +} + +EVT_HANDLER_MASK(NextFrame, "Next Frame", CMDEN_GB|CMDEN_GBA) +{ + update_check("Pause", true); + paused = true; + pause_next = true; + if(!IsPaused()) + panel->Resume(); + systemFrameSkip = 0; +} + +EVT_HANDLER_MASK(Disassemble, "Disassemble...", CMDEN_GB|CMDEN_GBA) +{ + Disassemble(); +} + +// only GBA generates the log messages this handles +// you could view them even w/o a gba cart, but why? +EVT_HANDLER_MASK(Logging, "Logging...", CMDEN_GBA) +{ + wxDialog *dlg = wxGetApp().frame->logdlg; + dlg->Show(); + dlg->Raise(); +} + +EVT_HANDLER_MASK(IOViewer, "I/O Viewer...", CMDEN_GBA) +{ + IOViewer(); +} + +EVT_HANDLER_MASK(MapViewer, "Map Viewer...", CMDEN_GB|CMDEN_GBA) +{ + MapViewer(); +} + +EVT_HANDLER_MASK(MemoryViewer, "Memory Viewer...", CMDEN_GB|CMDEN_GBA) +{ + MemViewer(); +} + +EVT_HANDLER_MASK(OAMViewer, "OAM Viewer...", CMDEN_GB|CMDEN_GBA) +{ + OAMViewer(); +} + +EVT_HANDLER_MASK(PaletteViewer, "Palette Viewer...", CMDEN_GB|CMDEN_GBA) +{ + PaletteViewer(); +} + +EVT_HANDLER_MASK(TileViewer, "Tile Viewer...", CMDEN_GB|CMDEN_GBA) +{ + TileViewer(); +} + +extern int remotePort; + +EVT_HANDLER_MASK(DebugGDB, "Wait for GDB connection...", CMDEN_NGDB_GBA) +{ + ModalPause mp; + int port = wxGetNumberFromUser( +#ifdef __WXMSW__ + wxEmptyString, +#else + _("Set to 0 for pseudo tty"), +#endif + _("Port to wait for connection:"), + _("GDB Connection"), remotePort, +#ifdef __WXMSW__ + 1025, +#else + 0, +#endif + 65535, this); + if(port < 0) + return; + remotePort = port; + wxString msg; +#ifndef __WXMSW__ + if(!port) { + if(!debugOpenPty()) + return; + msg.Printf(_("Waiting for connection at %s"), debugGetSlavePty().c_str()); + } else +#endif + { + if(!debugStartListen(port)) + return; + msg.Printf(_("Waiting for connection on port %d"), port); + } + wxProgressDialog dlg(_("Waiting for GDB..."), msg, 100, this, + wxPD_APP_MODAL|wxPD_CAN_ABORT|wxPD_ELAPSED_TIME); + bool connected = false; + while(dlg.Pulse()) { +#ifndef __WXMSW__ + if(!port) + connected = debugWaitPty(); + else +#endif + connected = debugWaitSocket(); + if(connected) + break; + // sleep a bit more in case of infinite loop + wxMilliSleep(10); + } + if(!connected) + remoteCleanUp(); + else { + debugger = true; + dbgMain = remoteStubMain; + dbgSignal = remoteStubSignal; + dbgOutput = remoteOutput; + cmd_enable &= ~(CMDEN_NGDB_ANY|CMDEN_NGDB_GBA); + cmd_enable |= CMDEN_GDB; + enable_menus(); + } +} + +EVT_HANDLER_MASK(DebugGDBLoad, "Load and wait for GDB...", CMDEN_NGDB_ANY) +{ + wxCommandEvent ev; + ModalPause mp; + OnwxID_OPEN(ev); + if(wxGetApp().pending_load.empty()) + return; + panel->UnloadGame(); + DoDebugGDB(); +} + +EVT_HANDLER_MASK(DebugGDBBreak, "Break into GDB", CMDEN_GDB) +{ + if(armState) { + armNextPC -= 4; + reg[15].I -= 4; + } else { + armNextPC -= 2; + reg[15].I -= 2; + } + debugger = true; +} + +EVT_HANDLER_MASK(DebugGDBDisconnect, "Disconnect GDB", CMDEN_GDB) +{ + debugger = false; + dbgMain = NULL; + dbgSignal = NULL; + dbgOutput = NULL; + remoteCleanUp(); + cmd_enable &= ~CMDEN_GDB; + cmd_enable |= CMDEN_NGDB_GBA|CMDEN_NGDB_ANY; + enable_menus(); +} + + +// Options menu +EVT_HANDLER(GeneralConfigure, "General options...") +{ + int rew = gopts.rewind_interval; + wxDialog *dlg = GetXRCDialog("GeneralConfig"); + if(ShowModal(dlg) == wxID_OK) + update_opts(); + if(panel->game_type() != IMAGE_UNKNOWN) + soundSetThrottle(gopts.throttle); + if(rew != gopts.rewind_interval) { + if(!gopts.rewind_interval) { + if(panel->num_rewind_states) { + cmd_enable &= ~CMDEN_REWIND; + enable_menus(); + } + panel->num_rewind_states = 0; + panel->do_rewind = false; + } else { + if(!panel->num_rewind_states) + panel->do_rewind = true; + panel->rewind_time = gopts.rewind_interval * 6; + } + } +} + +EVT_HANDLER(GameBoyConfigure, "Game Boy options...") +{ + wxDialog *dlg = GetXRCDialog("GameBoyConfig"); + wxChoice *c = XRCCTRL(*dlg, "Borders", wxChoice); + bool borderon = gbBorderOn; + bool printeron = gopts.gbprint; + if(!gbBorderOn && !gbBorderAutomatic) + c->SetSelection(0); + else if(gbBorderOn) + c->SetSelection(1); + else + c->SetSelection(2); + if(ShowModal(dlg) != wxID_OK) + return; + switch(c->GetSelection()) { + case 0: + gbBorderOn = gbBorderAutomatic = false; + break; + case 1: + gbBorderOn = true; + break; + case 2: + gbBorderOn = false; + gbBorderAutomatic = true; + break; + } + // this value might have been overwritten by FrameSkip + if(XRCCTRL(*dlg, "FrameSkipAuto", wxCheckBox)->GetValue()) + gopts.gb_frameskip = -1; + update_opts(); + if(panel->game_type() == IMAGE_GB) { + if(borderon != gbBorderOn) { + if(gbBorderOn) { + panel->AddBorder(); + gbSgbRenderBorder(); + } else + panel->DelBorder(); + } + // autoskip will self-adjust + if(gopts.gb_frameskip >= 0) + systemFrameSkip = gopts.gb_frameskip; + // don't want to have to reset to change colors + memcpy(gbPalette, &systemGbPalette[gbPaletteOption * 8], 8 * sizeof(systemGbPalette[0])); + } + if(printeron != gopts.gbprint) { + if(gopts.gbprint) + gbSerialFunction = gbPrinterSend; + else + gbSerialFunction = NULL; + } +} + +EVT_HANDLER(GameBoyAdvanceConfigure, "Game Boy Advance options...") +{ + wxDialog *dlg = GetXRCDialog("GameBoyAdvanceConfig"); + wxTextCtrl *ovcmt = XRCCTRL(*dlg, "Comment", wxTextCtrl); + wxString cmt; + wxChoice *ovrtc = XRCCTRL(*dlg, "OvRTC", wxChoice), + *ovst = XRCCTRL(*dlg, "OvSaveType", wxChoice), + *ovfs = XRCCTRL(*dlg, "OvFlashSize", wxChoice), + *ovmir = XRCCTRL(*dlg, "OvMirroring", wxChoice); + if(panel->game_type() == IMAGE_GBA) { + wxString s = wxString((const char *)&rom[0xac], wxConvLibc, 4); + XRCCTRL(*dlg, "GameCode", wxControl)->SetLabel(s); + cmt = wxString((const char *)&rom[0xa0], wxConvLibc, 12); + wxFileConfig *cfg = wxGetApp().overrides; + if(cfg->HasGroup(s)) { + cfg->SetPath(s); + cmt = cfg->Read(wxT("comment"), cmt); + ovcmt->SetValue(cmt); + ovrtc->SetSelection(cfg->Read(wxT("rtcEnabled"), -1) + 1); + ovst->SetSelection(cfg->Read(wxT("saveType"), -1) + 1); + ovfs->SetSelection((cfg->Read(wxT("flashSize"), -1) >> 17) + 1); + ovmir->SetSelection(cfg->Read(wxT("mirroringEnabled"), -1) + 1); + cfg->SetPath(wxT("/")); + } else { + ovcmt->SetValue(cmt); + ovrtc->SetSelection(0); + ovst->SetSelection(0); + ovfs->SetSelection(0); + ovmir->SetSelection(0); + } + } else { + XRCCTRL(*dlg, "GameCode", wxControl)->SetLabel(wxEmptyString); + ovcmt->SetValue(wxEmptyString); + ovrtc->SetSelection(0); + ovst->SetSelection(0); + ovfs->SetSelection(0); + ovmir->SetSelection(0); + } + if(ShowModal(dlg) != wxID_OK) + return; + // this value might have been overwritten by FrameSkip + if(XRCCTRL(*dlg, "FrameSkipAuto", wxCheckBox)->GetValue()) + gopts.gba_frameskip = -1; + update_opts(); + if(panel->game_type() == IMAGE_GBA) { + // autoskip will self-adjust + if(gopts.gba_frameskip >= 0) + systemFrameSkip = gopts.gba_frameskip; + agbPrintEnable(gopts.agbprint); +#if 0 // disabled in win32 version for undocumented "problems" + if(gopts.skip_intro) + *((u32 *)rom) = 0xea00002e; + else + *((u32 *)rom) = /* original value */; +#endif + wxString s = wxString((const char *)&rom[0xac], wxConvLibc, 4); + wxFileConfig *cfg = wxGetApp().overrides; + bool chg; + if(cfg->HasGroup(s)) { + cfg->SetPath(s); + chg = + ovcmt->GetValue() != cmt || + ovrtc->GetSelection() != cfg->Read(wxT("rtcEnabled"), -1) + 1 || + ovst->GetSelection() != cfg->Read(wxT("saveType"), -1) + 1 || + ovfs->GetSelection() != (cfg->Read(wxT("flashSize"), -1) >> 17) + 1 || + ovmir->GetSelection() != cfg->Read(wxT("mirroringEnabled"), -1) + 1; + cfg->SetPath(wxT("/")); + } else + chg = ovrtc->GetSelection() != 0 || ovst->GetSelection() != 0 || + ovfs->GetSelection() != 0 || ovmir->GetSelection() != 0; + if(chg) { + wxString vba_over; + wxFileName fn(wxStandardPaths::Get().GetUserDataDir(), wxT("vba-over.ini")); + if(fn.FileExists()) { + wxFileInputStream fis(fn.GetFullPath()); + wxStringOutputStream sos(&vba_over); + fis.Read(sos); + } + if(cfg->HasGroup(s)) { + cfg->SetPath(s); + if(cfg->Read(wxT("path"), wxEmptyString) == fn.GetPath()) { + // EOL can be either \n (unix), \r\n (dos), or \r (old mac) + wxString res(wxT("(^|[\n\r])" // a new line + "(" // capture group as \2 + "(#[^\n\r]*(\r?\n|\r))?" // an optional comment line + "\\[")); // the group header + res += s; + res += wxT( "\\]" + "([^[#]" // non-comment non-group-start chars + "|[^\r\n \t][ \t]*[[#]" // or comment/grp start chars in middle of line + "|#[^\n\r]*(\r?\n|\r)[^[]" // or comments not followed by grp start + ")*" + ")" // end of group + // no need to try to describe what's next + // as the regex should maximize match size + ); + wxRegEx re(res); + // there may be more than one group if it was hand-edited + // so remove them all + // could use re.Replace(), but this is more reliable + while(re.Matches(vba_over)) { + size_t beg, end; + re.GetMatch(&beg, &end, 2); + vba_over.erase(beg, end - beg); + } + } + cfg->SetPath(wxT("/")); + cfg->DeleteGroup(s); + } + cfg->SetPath(s); + cfg->Write(wxT("path"), fn.GetPath()); + cfg->Write(wxT("comment"), ovcmt->GetValue()); + vba_over.append(wxT("# ")); + vba_over.append(ovcmt->GetValue()); + vba_over.append(wxTextFile::GetEOL()); + vba_over.append(wxT('[')); + vba_over.append(s); + vba_over.append(wxT(']')); + vba_over.append(wxTextFile::GetEOL()); + int sel; +#define appendval(n) do { \ + vba_over.append(wxT(n)); \ + vba_over.append(wxT('=')); \ + vba_over.append((wxChar)(wxT('0') + sel - 1)); \ + vba_over.append(wxTextFile::GetEOL()); \ + cfg->Write(wxT(n), sel - 1); \ +} while(0) + if((sel = ovrtc->GetSelection()) > 0) + appendval("rtcEnabled"); + if((sel = ovst->GetSelection()) > 0) + appendval("saveType"); + if((sel = ovfs->GetSelection()) > 0) { + vba_over.append(wxT("flashSize=")); + vba_over.append(sel == 1 ? wxT("65536") : wxT("131072")); + vba_over.append(wxTextFile::GetEOL()); + cfg->Write(wxT("flashSize"), 0x10000 << (sel - 1)); + } + if((sel = ovmir->GetSelection()) > 0) + appendval("mirroringEnabled"); + cfg->SetPath(wxT("/")); + vba_over.append(wxTextFile::GetEOL()); + fn.Mkdir(0777, wxPATH_MKDIR_FULL); + wxTempFileOutputStream fos(fn.GetFullPath()); + fos.Write(vba_over.mb_str(), vba_over.size()); + fos.Commit(); + } + } +} + +EVT_HANDLER_MASK(DisplayConfigure, "Display options...", CMDEN_NREC_ANY) +{ + bool fs = gopts.fullscreen; + int max_scale = gopts.max_scale; + int renderer = gopts.render_method; + bool bilinear = gopts.bilinear; + int filt = gopts.filter; + wxString fp = gopts.filter_plugin; + wxVideoMode dm = gopts.fs_mode; + bool vsync = gopts.vsync; + + if(gopts.max_threads == 1) + gopts.max_threads = 0; + else { + gopts.max_threads = wxThread::GetCPUCount(); + if(gopts.max_threads > 8) + gopts.max_threads = 8; + } + wxDialog *dlg = GetXRCDialog("DisplayConfig"); + if(ShowModal(dlg) != wxID_OK) + return; + if(!gopts.max_threads) + gopts.max_threads = 1; + update_opts(); + if(max_scale != gopts.max_scale && panel->panel) { + if(gopts.max_scale) { + panel->panel->GetWindow()->SetMaxSize(wxDefaultSize); + if(fs == gopts.fullscreen && (!panel->IsFullScreen() || + dm == gopts.fs_mode)) + panel->Layout(); + // else the window will be resized anyway + else if(fs == gopts.fullscreen && (!panel->IsFullScreen() || + dm == gopts.fs_mode)) { + // can't compute max size here, so just destroy & rebuild + // drawing panel + panel->panel->Delete(); + panel->panel = NULL; + } + } + } + if(fs != gopts.fullscreen) + panel->ShowFullScreen(gopts.fullscreen); + else if(panel->IsFullScreen() && dm != gopts.fs_mode) { + // maybe not the best way to do this.. + panel->ShowFullScreen(false); + panel->ShowFullScreen(true); + } + if(panel->panel && + // change in renderer obviously requires restart + (renderer != gopts.render_method || + // change in bilinear filter requires restart only for 3d renderers + // but restart for all anyway + bilinear != gopts.bilinear || + // change in scale requires buffer resizing + builtin_ff_scale(filt) != builtin_ff_scale(gopts.filter) || + // plugin is only loaded on init + (filt == FF_PLUGIN && fp != gopts.filter_plugin) || + // ifb doesn't support 24-bit + (gopts.ifb != IFB_NONE && systemColorDepth == 24) || + // standard prefers 24-bit + (gopts.ifb == IFB_NONE && gopts.filter == FF_NONE && + systemColorDepth == 32 && gopts.render_method == 0) || + // vsync is only set in init + vsync != gopts.vsync)) { + panel->panel->Delete(); + panel->panel = NULL; + } +} + +EVT_HANDLER_MASK(ChangeFilter, "Change Pixel Filter", CMDEN_NREC_ANY) +{ + int filt = gopts.filter; + if(filt == FF_PLUGIN || + ++gopts.filter == FF_PLUGIN && gopts.filter_plugin.empty()) + gopts.filter = 0; + update_opts(); + if(panel->panel && + builtin_ff_scale(filt) != builtin_ff_scale(gopts.filter)) { + panel->panel->Delete(); + panel->panel = NULL; + } +} + +EVT_HANDLER_MASK(ChangeIFB, "Change Interframe Blending", CMDEN_NREC_ANY) +{ + gopts.ifb = (gopts.ifb + 1) % 3; + update_opts(); + if(panel->panel && + // ifb doesn't support 24-bit + (gopts.ifb != IFB_NONE && systemColorDepth == 24) || + // standard prefers 24-bit + (gopts.ifb == IFB_NONE && gopts.filter == FF_NONE && + systemColorDepth == 32 && gopts.render_method == 0)) { + panel->panel->Delete(); + panel->panel = NULL; + } +} + +EVT_HANDLER_MASK(SoundConfigure, "Sound options...", CMDEN_NREC_ANY) +{ + int oqual = gopts.sound_qual, oapi = gopts.audio_api; + bool oupmix = gopts.upmix, ohw = gopts.dsound_hw_accel; + wxString odev = gopts.audio_dev; + wxDialog *dlg = GetXRCDialog("SoundConfig"); + if(ShowModal(dlg) != wxID_OK) + return; + update_opts(); + switch(panel->game_type()) { + case IMAGE_UNKNOWN: + return; + case IMAGE_GB: + gb_effects_config.echo = (float)gopts.gb_echo / 100.0; + gb_effects_config.stereo = (float)gopts.gb_stereo / 100.0; + // note that setting declick may reset gb sound engine + gbSoundSetDeclicking(gopts.gb_declick); + gbSoundSetSampleRate(!gopts.sound_qual ? 48000 : + 44100 / (1 << (gopts.sound_qual - 1))); + break; + case IMAGE_GBA: + soundSetSampleRate(!gopts.sound_qual ? 48000 : + 44100 / (1 << (gopts.sound_qual - 1))); + break; + } + // changing sample rate causes driver reload, so no explicit reload needed + if(oqual == gopts.sound_qual && + // otherwise reload if API changes + (oapi != gopts.audio_api || odev != gopts.audio_dev || + // or init-only options + (oapi == AUD_XAUDIO2 && oupmix != gopts.upmix) || + (oapi == AUD_DIRECTSOUND && ohw != gopts.dsound_hw_accel))) { + soundShutdown(); + soundInit(); + } + soundSetVolume((float)gopts.sound_vol / 100.0); +} + +EVT_HANDLER(EmulatorDirectories, "Directories...") +{ + wxDialog *dlg = GetXRCDialog("DirectoriesConfig"); + if(ShowModal(dlg) == wxID_OK) + update_opts(); +} + +EVT_HANDLER(JoypadConfigure, "Joypad options...") +{ + wxDialog *dlg = GetXRCDialog("JoypadConfig"); + joy.Attach(NULL); + joy.Add(); + if(ShowModal(dlg) == wxID_OK) + update_opts(); + SetJoystick(); +} + +// new +EVT_HANDLER(LinkConfigure, "Link options...") +{ +#ifndef NO_LINK + bool jb = gba_joybus_enabled; + wxString jh = gopts.joybus_host; + wxDialog *dlg = GetXRCDialog("LinkConfig"); + if(ShowModal(dlg) != wxID_OK) + return; + update_opts(); + if(jb != gba_joybus_enabled) { + if(gba_joybus_enabled) + JoyBusConnect(); + else + JoyBusShutdown(); + } else if(jh != gopts.joybus_host) { + joybusHostAddr = std::string(gopts.joybus_host.mb_str()); + JoyBusConnect(); + } + if(gba_link_enabled != did_link_init) { + if(gba_link_enabled) { + if((did_link_init = InitLink())) + cmd_enable |= CMDEN_LINK_ANY; + } else { + did_link_init = false; + CloseLink(); + lanlink.active = false; + cmd_enable &= ~CMDEN_LINK_ANY; + } + enable_menus(); + } +#endif +} + +EVT_HANDLER(Customize, "Customize UI...") +{ + wxDialog *dlg = GetXRCDialog("AccelConfig"); + if(ShowModal(dlg) == wxID_OK) + update_opts(); +} + +EVT_HANDLER(BugReport, "Report bugs...") +{ + wxLaunchDefaultBrowser(wxT("http://sourceforge.net/tracker/?group_id=212795&atid=1023154")); +} + +EVT_HANDLER(FAQ, "VBA-M support forum") +{ + wxLaunchDefaultBrowser(wxT("http://vba-m.com/forum/")); +} + +// was About +EVT_HANDLER(wxID_ABOUT, "About...") +{ + wxAboutDialogInfo ai; + ai.SetName(wxT("VisualBoyAdvance-M")); + ai.SetVersion(wxT(VERSION)); + // setting website, icon, license uses custom aboutbox on win32 & macosx + // but at least win32 standard about is nothing special + ai.SetWebSite(wxT("http://www.vba-m.com/")); + ai.SetIcon(GetIcon()); + ai.SetDescription(_("Nintendo GameBoy (+Color+Advance) emulator.")); + ai.SetCopyright(_("Copyright (C) 1999-2003 Forgotten\nCopyright (C) 2004-2006 VBA development team\nCopyright (C) 2007-2011 VBA-M development team")); + ai.SetLicense(_("This program is free software: you can redistribute it and/or modify\n" + "it under the terms of the GNU General Public License as published by\n" + "the Free Software Foundation, either version 2 of the License, or\n" + "(at your option) any later version.\n\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program. If not, see http://www.gnu.org/licenses .")); + // from gtk + ai.AddDeveloper(wxT("Forgotten")); + ai.AddDeveloper(wxT("kxu")); + ai.AddDeveloper(wxT("Pokemonhacker")); + ai.AddDeveloper(wxT("Spacy51")); + ai.AddDeveloper(wxT("mudlord")); + ai.AddDeveloper(wxT("Nach")); + ai.AddDeveloper(wxT("jbo_85")); + ai.AddDeveloper(wxT("bgK")); + ai.AddArtist(wxT("Matteo Drera")); + ai.AddArtist(wxT("Jakub Steiner")); + ai.AddArtist(wxT("Jones Lee")); + // from win32 + ai.AddDeveloper(wxT("Jonas Quinn")); + ai.AddDeveloper(wxT("DJRobX")); + ai.AddDeveloper(wxT("Spacy")); + ai.AddDeveloper(wxT("Squall Leonhart")); + // wx + ai.AddDeveloper(wxT("Thomas J. Moore")); + // from win32 "thanks" + ai.AddDeveloper(wxT("blargg")); + ai.AddDeveloper(wxT("Costis")); + ai.AddDeveloper(wxT("chrono")); + ai.AddDeveloper(wxT("xKiv")); + ai.AddDeveloper(wxT("Orig. VBA team")); + + wxAboutBox(ai); +} + +// Dummy for disabling system key bindings +EVT_HANDLER_MASK(NOOP, "Do nothing", CMDEN_NEVER) +{ +} + +// The following have been moved to dialogs +// I will not implement as command unless there is great demand +// CheatsList +//EVT_HANDLER(CheatsLoad, "Load Cheats...") +//EVT_HANDLER(CheatsSave, "Save Cheats...") +//GeneralConfigure +//EVT_HANDLER(EmulatorRewindInterval, "EmulatorRewindInterval") +//EVT_HANDLER(EmulatorAutoApplyPatchFiles, "EmulatorAutoApplyPatchFiles") +//EVT_HANDLER(ThrottleNone, "ThrottleNone") +//EVT_HANDLER(Throttle025%, "Throttle025%") +//EVT_HANDLER(Throttle050%, "Throttle050%") +//EVT_HANDLER(Throttle100%, "Throttle100%") +//EVT_HANDLER(Throttle150%, "Throttle150%") +//EVT_HANDLER(Throttle200%, "Throttle200%") +//EVT_HANDLER(ThrottleOther, "ThrottleOther") +//GameBoyConfigure/GameBoyAdvanceConfigure +//EVT_HANDLER(FrameSkip0, "FrameSkip0") +//EVT_HANDLER(FrameSkip1, "FrameSkip1") +//EVT_HANDLER(FrameSkip2, "FrameSkip2") +//EVT_HANDLER(FrameSkip3, "FrameSkip3") +//EVT_HANDLER(FrameSkip4, "FrameSkip4") +//EVT_HANDLER(FrameSkip5, "FrameSkip5") +//EVT_HANDLER(FrameSkip6, "FrameSkip6") +//EVT_HANDLER(FrameSkip7, "FrameSkip7") +//EVT_HANDLER(FrameSkip8, "FrameSkip8") +//EVT_HANDLER(FrameSkip9, "FrameSkip9") +// GameBoyConfigure +//EVT_HANDLER(GameboyBorder, "GameboyBorder") +//EVT_HANDLER(GameboyBorderAutomatic, "GameboyBorderAutomatic") +//EVT_HANDLER(GameboyColors, "GameboyColors") +//GameBoyAdvanceConfigure +//EVT_HANDLER(EmulatorAGBPrint, "EmulatorAGBPrint") +//EVT_HANDLER(EmulatorSaveAuto, "EmulatorSaveAuto") +//EVT_HANDLER(EmulatorSaveEEPROM, "EmulatorSaveEEPROM") +//EVT_HANDLER(EmulatorSaveSRAM, "EmulatorSaveSRAM") +//EVT_HANDLER(EmulatorSaveFLASH, "EmulatorSaveFLASH") +//EVT_HANDLER(EmulatorSaveEEPROMSensor, "EmulatorSaveEEPROMSensor") +//EVT_HANDLER(EmulatorSaveFlash64K, "EmulatorSaveFlash64K") +//EVT_HANDLER(EmulatorSaveFlash128K, "EmulatorSaveFlash128K") +//EVT_HANDLER(EmulatorSaveDetectNow, "EmulatorSaveDetectNow") +//EVT_HANDLER(EmulatorRTC, "EmulatorRTC") +//DisplayConfigure +//EVT_HANDLER(EmulatorShowSpeedNone, "EmulatorShowSpeedNone") +//EVT_HANDLER(EmulatorShowSpeedPercentage, "EmulatorShowSpeedPercentage") +//EVT_HANDLER(EmulatorShowSpeedDetailed, "EmulatorShowSpeedDetailed") +//EVT_HANDLER(EmulatorShowSpeedTransparent, "EmulatorShowSpeedTransparent") +//EVT_HANDLER(VideoX1, "VideoX1") +//EVT_HANDLER(VideoX2, "VideoX2") +//EVT_HANDLER(VideoX3, "VideoX3") +//EVT_HANDLER(VideoX4, "VideoX4") +//EVT_HANDLER(VideoX5, "VideoX5") +//EVT_HANDLER(VideoX6, "VideoX6") +//EVT_HANDLER(Video320x240, "Video320x240") +//EVT_HANDLER(Video640x480, "Video640x480") +//EVT_HANDLER(Video800x600, "Video800x600") +//EVT_HANDLER(VideoFullscreen, "VideoFullscreen") +//EVT_HANDLER(VideoFullscreenMaxScale, "VideoFullscreenMaxScale") +//EVT_HANDLER(VideoRenderDDRAW, "VideoRenderDDRAW") +//EVT_HANDLER(VideoRenderD3D, "VideoRenderD3D") +//EVT_HANDLER(VideoRenderOGL, "VideoRenderOGL") +//EVT_HANDLER(VideoVsync, "VideoVsync") +//EVT_HANDLER(FilterNormal, "FilterNormal") +//EVT_HANDLER(FilterTVMode, "FilterTVMode") +//EVT_HANDLER(Filter2xSaI, "Filter2xSaI") +//EVT_HANDLER(FilterSuper2xSaI, "FilterSuper2xSaI") +//EVT_HANDLER(FilterSuperEagle, "FilterSuperEagle") +//EVT_HANDLER(FilterPixelate, "FilterPixelate") +//EVT_HANDLER(FilterMotionBlur, "FilterMotionBlur") +//EVT_HANDLER(FilterAdMameScale2x, "FilterAdMameScale2x") +//EVT_HANDLER(FilterSimple2x, "FilterSimple2x") +//EVT_HANDLER(FilterBilinear, "FilterBilinear") +//EVT_HANDLER(FilterBilinearPlus, "FilterBilinearPlus") +//EVT_HANDLER(FilterScanlines, "FilterScanlines") +//EVT_HANDLER(FilterHq2x, "FilterHq2x") +//EVT_HANDLER(FilterLq2x, "FilterLq2x") +//EVT_HANDLER(FilterIFBNone, "FilterIFBNone") +//EVT_HANDLER(FilterIFBMotionBlur, "FilterIFBMotionBlur") +//EVT_HANDLER(FilterIFBSmart, "FilterIFBSmart") +//EVT_HANDLER(FilterDisableMMX, "FilterDisableMMX") +//JoypadConfigure +//EVT_HANDLER(JoypadConfigure1, "JoypadConfigure1") +//EVT_HANDLER(JoypadConfigure2, "JoypadConfigure2") +//EVT_HANDLER(JoypadConfigure3, "JoypadConfigure3") +//EVT_HANDLER(JoypadConfigure4, "JoypadConfigure4") +//EVT_HANDLER(JoypadMotionConfigure, "JoypadMotionConfigure") + +// The following functionality has been removed +// It should be done in OS, rather than in vbam +//EVT_HANDLER(EmulatorAssociate, "EmulatorAssociate") + +// The following functionality has been removed +// It should be done at OS level (e.g. window manager) +//EVT_HANDLER(SystemMinimize, "SystemMinimize") diff --git a/src/wx/copy-events.cmake b/src/wx/copy-events.cmake new file mode 100644 index 0000000..06b30de --- /dev/null +++ b/src/wx/copy-events.cmake @@ -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) diff --git a/src/wx/drawing.h b/src/wx/drawing.h new file mode 100644 index 0000000..e65264f --- /dev/null +++ b/src/wx/drawing.h @@ -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 + +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 + +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 */ diff --git a/src/wx/dsound.cpp b/src/wx/dsound.cpp new file mode 100644 index 0000000..abf275c --- /dev/null +++ b/src/wx/dsound.cpp @@ -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 + +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(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; +} diff --git a/src/wx/filters.h b/src/wx/filters.h new file mode 100644 index 0000000..fe8bd5d --- /dev/null +++ b/src/wx/filters.h @@ -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 */ diff --git a/src/wx/gfxviewers.cpp b/src/wx/gfxviewers.cpp new file mode 100644 index 0000000..34ce55a --- /dev/null +++ b/src/wx/gfxviewers.cpp @@ -0,0 +1,1565 @@ + +// these are all the viewer dialogs with graphical panel areas +// they can be instantiated multiple times + +#include "wxvbam.h" +#include "viewsupt.h" +#include +#include + +// FIXME: many of these read e.g. palette data directly without regard to +// byte order. Need to determine where things are stored in emulated machine +// order and where in native order, and swap the latter on big-endian + +// most of these have label fields that need to be sized and later filled +// mv is a string to initialize with for sizing +#define getlab(v, n, mv) do { \ + v = XRCCTRL(*this, n, wxControl); \ + if(!v) \ + baddialog(); \ + v->SetLabel(wxT(mv)); \ +} while(0) + +// FIXME: this should be in a header +extern u8 gbInvertTab[256]; + +// avoid exporting classes +namespace Viewers { + class MapViewer : public GfxViewer + { + public: + MapViewer() : GfxViewer(wxT("MapViewer"), 1024, 1024) + { + frame = bg = 0; + getradio(fr0 = , "Frame0", frame, 0); + getradio(fr1 = , "Frame1", frame, 0xa000); + getradio(bg0 = , "BG0", bg, 0); + getradio(bg1 = , "BG1", bg, 1); + getradio(bg2 = , "BG2", bg, 2); + getradio(bg3 = , "BG3", bg, 3); + getlab(modelab, "Mode", "8"); + getlab(mapbase, "MapBase", "0xWWWWWWWW"); + getlab(charbase, "CharBase", "0xWWWWWWWW"); + getlab(size, "Size", "1024x1024"); + getlab(colors, "Colors", "2WW"); + getlab(prio, "Priority", "3"); + getlab(mosaic, "Mosaic", "0"); + getlab(overflow, "Overflow", "0"); + getlab(coords, "Coords", "(1023,1023)"); + getlab(addr, "Address", "0xWWWWWWWW"); + getlab(tile, "Tile", "1023"); + getlab(flip, "Flip", "HV"); + getlab(palette, "Palette", "---"); + Fit(); + selx = sely = -1; + Update(); + } + void Update() + { + mode = DISPCNT & 7; + switch(bg) { + case 0: + control = BG0CNT; + break; + case 1: + control = BG1CNT; + break; + case 2: + control = BG2CNT; + break; + case 3: + control = BG3CNT; + break; + } + bool fr0en = true, fr1en = true, bg0en = true, bg1en = true, + bg2en = true, bg3en = true; + switch(mode) { + case 0: + fr0en = fr1en = false; + renderTextScreen(); + break; + case 1: + fr0en = fr1en = false; + bg3en = false; + if(bg == 3) { + bg = 0; + control = BG0CNT; + bg0->SetValue(true); + } + if(bg < 2) + renderTextScreen(); + else + renderRotScreen(); + break; + case 2: + fr0en = fr1en = false; + bg0en = bg1en = false; + if(bg < 2) { + bg = 2; + control = BG2CNT; + bg2->SetValue(true); + } + renderRotScreen(); + break; + case 3: + fr0en = fr1en = false; + bg0en = bg1en = bg2en = bg3en = false; + bg = 2; + bg2->SetValue(true); + renderMode3(); + break; + case 4: + bg0en = bg1en = bg2en = bg3en = false; + bg = 2; + bg2->SetValue(true); + renderMode4(); + break; + case 5: + case 6: + case 7: + bg = 2; + bg2->SetValue(true); + renderMode5(); + break; + } + ChangeBMP(); + fr0->Enable(fr0en); + fr1->Enable(fr1en); + bg0->Enable(bg0en); + bg1->Enable(bg1en); + bg2->Enable(bg2en); + bg3->Enable(bg3en); + + wxString s; + s.Printf(wxT("%d"), (int)mode); + modelab->SetLabel(s); + if(mode >= 3) { + mapbase->SetLabel(wxEmptyString); + charbase->SetLabel(wxEmptyString); + } else { + s.Printf(wxT("0x%08X"), ((control >> 8) & 0x1f) * 0x800 + 0x6000000); + mapbase->SetLabel(s); + s.Printf(wxT("0x%08X"), ((control >> 2) & 0x03) * 0x4000 + 0x6000000); + charbase->SetLabel(s); + } + s.Printf(wxT("%dx%d"), gv->bmw, gv->bmh); + size->SetLabel(s); + colors->SetLabel(control & 0x80 ? wxT("256") : wxT("16")); + s.Printf(wxT("%d"), control & 3); + prio->SetLabel(s); + mosaic->SetLabel(control & 0x40 ? wxT("1") : wxT("0")); + overflow->SetLabel(bg <= 1 ? wxEmptyString : + control & 0x2000 ? wxT("1") : wxT("0")); + UpdateMouseInfo(); + } + + void UpdateMouseInfoEv(wxMouseEvent &ev) + { + selx = ev.GetX(); + sely = ev.GetY(); + UpdateMouseInfo(); // note that this will be inaccurate if game + // not paused since last refresh + } + + u32 AddressFromSel() + { + u32 base = ((control >> 8) & 0x1f) * 0x800 + 0x6000000; + // all text bgs (16 bits) + if(mode == 0 || (mode < 3 && bg < 2) || mode == 6 || mode == 7) { + if(sely > 255) { + base += 0x800; + if(gv->bmw > 256) + base += 0x800; + } + if(selx >= 256) + base += 0x800; + return base + ((selx & 0xff) >> 3) * 2 + 64 * ((sely & 0xff) >> 3); + } + // rot bgs (8 bits) + if(mode < 3) + return base + (selx>>3) + (gv->bmw>>3) * (sely >> 3); + // mode 3/5 (16 bits) + if(mode != 4) + return 0x6000000 + 0xa000*frame + (selx + gv->bmw * sely) * 2; + // mode 4 (8 bits) + return 0x6000000 + 0xa000*frame + selx + gv->bmw * sely; + } + + void UpdateMouseInfo() + { + if(selx > gv->bmw || sely > gv->bmh) + selx = sely = -1; + if(selx < 0) { + coords->SetLabel(wxEmptyString); + addr->SetLabel(wxEmptyString); + tile->SetLabel(wxEmptyString); + flip->SetLabel(wxEmptyString); + palette->SetLabel(wxEmptyString); + } else { + wxString s; + s.Printf(wxT("(%d,%d)"), selx, sely); + coords->SetLabel(s); + u32 address = AddressFromSel(); + s.Printf(wxT("0x%08X"), address); + addr->SetLabel(s); + + if(!mode || (mode < 3 || mode > 5) && bg < 2) { + u16 value = *((u16 *)&vram[address - 0x6000000]); + s.Printf(wxT("%d"), value & 1023); + tile->SetLabel(s); + s = value & 1024 ? wxT('H') : wxT('-'); + s += value & 2048 ? wxT('V') : wxT('-'); + flip->SetLabel(s); + if(control & 0x80) + palette->SetLabel(wxT("---")); + else { + s.Printf(wxT("%d"), (value >> 12) & 15); + palette->SetLabel(s); + } + } else { + tile->SetLabel(wxT("---")); + flip->SetLabel(wxT("--")); + palette->SetLabel(wxT("---")); + } + } + } + protected: + u16 control, mode; + int frame, bg; + wxRadioButton *fr0, *fr1, *bg0, *bg1, *bg2, *bg3; + wxControl *modelab, *mapbase, *charbase, *size, *colors, *prio, *mosaic, + *overflow; + wxControl *coords, *addr, *tile, *flip, *palette; + int selx, sely; + + // following routines were copied from win32/MapView.cpp with little + // attempt to read & validate, except: + // stride = 1024, rgb instead of bgr + // FIXME: probably needs changing for big-endian + + void renderTextScreen() + { + u16 *palette = (u16 *)paletteRAM; + u8 *charBase = &vram[((control >> 2) & 0x03) * 0x4000]; + u16 *screenBase = (u16 *)&vram[((control >> 8) & 0x1f) * 0x800]; + u8 *bmp = image.GetData(); + + int sizeX = 256; + int sizeY = 256; + switch((control >> 14) & 3) { + case 0: + break; + case 1: + sizeX = 512; + break; + case 2: + sizeY = 512; + break; + case 3: + sizeX = 512; + sizeY = 512; + break; + } + + BMPSize(sizeX, sizeY); + + if(control & 0x80) { + for(int y = 0; y < sizeY; y++) { + int yy = y & 255; + + if(y == 256 && sizeY > 256) { + screenBase += 0x400; + if(sizeX > 256) + screenBase += 0x400; + } + u16 *screenSource = screenBase + ((yy>>3)*32); + + for(int x = 0; x < sizeX; x++) { + u16 data = *screenSource; + + int tile = data & 0x3FF; + int tileX = (x & 7); + int tileY = y & 7; + + if(data & 0x0400) + tileX = 7 - tileX; + if(data & 0x0800) + tileY = 7 - tileY; + + u8 c = charBase[tile * 64 + tileY * 8 + tileX]; + + u16 color = palette[c]; + + *bmp++ = (color & 0x1f) << 3; + *bmp++ = ((color >> 5) & 0x1f) << 3; + *bmp++ = ((color >> 10) & 0x1f) << 3; + + if(data & 0x0400) { + if(tileX == 0) + screenSource++; + } else if(tileX == 7) + screenSource++; + if(x == 255 && sizeX > 256) { + screenSource = screenBase + 0x400 + ((yy>>3)*32); + } + } + bmp += 3 * (1024 - sizeX); + } + } else { + for(int y = 0; y < sizeY; y++) { + int yy = y & 255; + + if(y == 256 && sizeY > 256) { + screenBase += 0x400; + if(sizeX > 256) + screenBase += 0x400; + } + u16 *screenSource = screenBase + ((yy>>3)*32); + + for(int x = 0; x < sizeX; x++) { + u16 data = *screenSource; + + int tile = data & 0x3FF; + int tileX = (x & 7); + int tileY = y & 7; + + if(data & 0x0400) + tileX = 7 - tileX; + if(data & 0x0800) + tileY = 7 - tileY; + + u8 color = charBase[tile * 32 + tileY * 4 + (tileX>>1)]; + + if(tileX & 1) { + color = (color >> 4); + } else { + color &= 0x0F; + } + + int pal = (*screenSource>>8) & 0xF0; + u16 color2 = palette[pal + color]; + + *bmp++ = (color2 & 0x1f) << 3; + *bmp++ = ((color2 >> 5) & 0x1f) << 3; + *bmp++ = ((color2 >> 10) & 0x1f) << 3; + + if(data & 0x0400) { + if(tileX == 0) + screenSource++; + } else if(tileX == 7) + screenSource++; + + if(x == 255 && sizeX > 256) { + screenSource = screenBase + 0x400 + ((yy>>3)*32); + } + } + bmp += 3 * (1024 - sizeX); + } + } +#if 0 + switch(bg) { + case 0: + renderView(BG0HOFS<<8, BG0VOFS<<8, + 0x100, 0x000, + 0x000, 0x100, + (sizeX -1) <<8, + (sizeY -1) << 8, + true); + break; + case 1: + renderView(BG1HOFS<<8, BG1VOFS<<8, + 0x100, 0x000, + 0x000, 0x100, + (sizeX -1) <<8, + (sizeY -1) << 8, + true); + break; + case 2: + renderView(BG2HOFS<<8, BG2VOFS<<8, + 0x100, 0x000, + 0x000, 0x100, + (sizeX -1) <<8, + (sizeY -1) << 8, + true); + break; + case 3: + renderView(BG3HOFS<<8, BG3VOFS<<8, + 0x100, 0x000, + 0x000, 0x100, + (sizeX -1) <<8, + (sizeY -1) << 8, + true); + break; + } +#endif + } + + void renderRotScreen() + { + u16 *palette = (u16 *)paletteRAM; + u8 *charBase = &vram[((control >> 2) & 0x03) * 0x4000]; + u8 *screenBase = (u8 *)&vram[((control >> 8) & 0x1f) * 0x800]; + u8 *bmp = image.GetData(); + + int sizeX = 128; + int sizeY = 128; + switch((control >> 14) & 3) { + case 0: + break; + case 1: + sizeX = sizeY = 256; + break; + case 2: + sizeX = sizeY = 512; + break; + case 3: + sizeX = sizeY = 1024; + break; + } + + BMPSize(sizeX, sizeY); + + if(control & 0x80) { + for(int y = 0; y < sizeY; y++) { + for(int x = 0; x < sizeX; x++) { + int tile = screenBase[(x>>3) + (y>>3)*(sizeX>>3)]; + + int tileX = (x & 7); + int tileY = y & 7; + + u8 color = charBase[tile * 64 + tileY * 8 + tileX]; + u16 color2 = palette[color]; + + *bmp++ = (color2 & 0x1f) << 3; + *bmp++ = ((color2 >> 5) & 0x1f) << 3; + *bmp++ = ((color2 >> 10) & 0x1f) << 3; + } + } + bmp += 3 * (1024 - sizeX); + } else { + for(int y = 0; y < sizeY; y++) { + for(int x = 0; x < sizeX; x++) { + int tile = screenBase[(x>>3) + (y>>3)*(sizeX>>3)]; + + int tileX = (x & 7); + int tileY = y & 7; + + u8 color = charBase[tile * 64 + tileY * 8 + tileX]; + u16 color2 = palette[color]; + + *bmp++ = (color2 & 0x1f) << 3; + *bmp++ = ((color2 >> 5) & 0x1f) << 3; + *bmp++ = ((color2 >> 10) & 0x1f) << 3; + } + } + bmp += 3 * (1024 - sizeX); + } + + u32 xx; + u32 yy; + + switch(bg) { + case 2: + xx = BG2X_L | BG2X_H << 16; + yy = BG2Y_L | BG2Y_H << 16; + + +#if 0 + renderView(xx, yy, + BG2PA, BG2PC, + BG2PB, BG2PD, + (sizeX -1) <<8, + (sizeY -1) << 8, + (control & 0x2000) != 0); +#endif + break; + case 3: + xx = BG3X_L | BG3X_H << 16; + yy = BG3Y_L | BG3Y_H << 16; +#if 0 + renderView(xx, yy, + BG3PA, BG3PC, + BG3PB, BG3PD, + (sizeX -1) <<8, + (sizeY -1) << 8, + (control & 0x2000) != 0); +#endif + break; + } + } + + void renderMode3() + { + u8 *bmp = image.GetData(); + u16 *src = (u16 *)&vram[0]; + + BMPSize(240, 160); + + for(int y = 0; y < 160; y++) { + for(int x = 0; x < 240; x++) { + u16 data = *src++; + *bmp++ = (data & 0x1f) << 3; + *bmp++ = ((data >> 5) & 0x1f) << 3; + *bmp++ = ((data >> 10) & 0x1f) << 3; + } + bmp += 3 * (1024 - 240); + } + } + + + void renderMode4() + { + u8 *bmp = image.GetData(); + u8 *src = frame ? &vram[0xa000] : &vram[0]; + u16 *pal = (u16 *)&paletteRAM[0]; + + BMPSize(240, 160); + + for(int y = 0; y < 160; y++) { + for(int x = 0; x < 240; x++) { + u8 c = *src++; + u16 data = pal[c]; + *bmp++ = (data & 0x1f) << 3; + *bmp++ = ((data >> 5) & 0x1f) << 3; + *bmp++ = ((data >> 10) & 0x1f) << 3; + } + bmp += 3 * (1024 - 240); + } + } + + + void renderMode5() + { + u8 *bmp = image.GetData(); + u16 *src = (u16 *)(frame ? &vram[0xa000] : &vram[0]); + + BMPSize(160, 128); + + for(int y = 0; y < 128; y++) { + for(int x = 0; x < 160; x++) { + u16 data = *src++; + *bmp++ = (data & 0x1f) << 3; + *bmp++ = ((data >> 5) & 0x1f) << 3; + *bmp++ = ((data >> 10) & 0x1f) << 3; + } + bmp += 3 * (1024 - 160); + } + } + + DECLARE_EVENT_TABLE() + }; + + BEGIN_EVENT_TABLE(MapViewer, GfxViewer) + EVT_GFX_CLICK(wxID_ANY, MapViewer::UpdateMouseInfoEv) + END_EVENT_TABLE() + + class GBMapViewer : public GfxViewer + { + public: + GBMapViewer() : GfxViewer(wxT("GBMapViewer"), 256, 256) + { + getradio(,"CharBase0", charbase, 0x0000); + getradio(,"CharBase1", charbase, 0x0800); + getradio(,"MapBase0", mapbase, 0x1800); + getradio(,"MapBase1", mapbase, 0x1c00); + getlab(coords, "Coords", "(2WW,2WW)"); + getlab(addr, "Address", "0xWWWW"); + getlab(tile, "Tile", "2WW"); + getlab(flip, "Flip", "HV"); + getlab(palette, "Palette", "---"); + getlab(prio, "Priority", "P"); + Fit(); + selx = sely = -1; + Update(); + } + void Update() + { + u8 *bank0, *bank1; + if(gbCgbMode) { + bank0 = &gbVram[0x0000]; + bank1 = &gbVram[0x2000]; + } else { + bank0 = &gbMemory[0x8000]; + bank1 = NULL; + } + int tile_map_address = mapbase; + + // following copied almost verbatim from win32/GBMapView.cpp + int tile = 0; + for(int y = 0; y < 32; y++) { + for(int x = 0; x < 32; x++) { + u8 *bmp = &image.GetData()[y * 8 * 32 * 24 + x*24]; + u8 attrs = 0; + if(bank1 != NULL) + attrs = bank1[tile_map_address]; + u8 tile = bank0[tile_map_address]; + tile_map_address++; + + if(charbase) { + if(tile < 128) tile += 128; + else tile -= 128; + } + for(int j = 0; j < 8; j++) { + int charbase_address = attrs & 0x40 ? + charbase + tile*16 + (7-j)*2: + charbase + tile*16+j*2; + + u8 tile_a = 0; + u8 tile_b = 0; + + if(attrs & 0x08) { + tile_a = bank1[charbase_address++]; + tile_b = bank1[charbase_address]; + } else { + tile_a = bank0[charbase_address++]; + tile_b = bank0[charbase_address]; + } + + if(attrs & 0x20) { + tile_a = gbInvertTab[tile_a]; + tile_b = gbInvertTab[tile_b]; + } + + u8 mask = 0x80; + + while(mask > 0) { + u8 c = (tile_a & mask) ? 1 : 0; + c += (tile_b & mask) ? 2 : 0; + + if(gbCgbMode) + c = c + (attrs & 7)*4; + + u16 color = gbPalette[c]; + + *bmp++ = (color & 0x1f) << 3; + *bmp++ = ((color >> 5) & 0x1f) << 3; + *bmp++ = ((color >> 10) & 0x1f) << 3; + + mask >>= 1; + } + bmp += 31*24; + } + } + } + ChangeBMP(); + UpdateMouseInfo(); + } + + void UpdateMouseInfoEv(wxMouseEvent &ev) + { + selx = ev.GetX(); + sely = ev.GetY(); + UpdateMouseInfo(); // note that this will be inaccurate if game + // not paused since last refresh + } + + void UpdateMouseInfo() + { + if(selx > gv->bmw || sely > gv->bmh) + selx = sely = -1; + if(selx < 0) { + coords->SetLabel(wxEmptyString); + addr->SetLabel(wxEmptyString); + tile->SetLabel(wxEmptyString); + flip->SetLabel(wxEmptyString); + palette->SetLabel(wxEmptyString); + prio->SetLabel(wxEmptyString); + } else { + wxString s; + s.Printf(wxT("(%d,%d)"), selx, sely); + coords->SetLabel(s); + u16 address = mapbase + 0x8000 + (sely >> 3) * 32 + (selx >> 3); + s.Printf(wxT("0x%04X"), address); + addr->SetLabel(s); + u8 attrs = 0; + u8 tilev = gbMemoryMap[9][address & 0xfff]; + if(gbCgbMode) { + attrs = gbVram[0x2000 + address - 0x8000]; + tilev = gbVram[address & 0x1fff]; + } + if(charbase) { + if(tilev >= 128) + tilev -= 128; + else + tilev += 128; + } + s.Printf(wxT("%d"), (int)tilev); + tile->SetLabel(s); + s = attrs & 0x20 ? wxT('H') : wxT('-'); + s += attrs & 0x40 ? wxT('V') : wxT('-'); + flip->SetLabel(s); + if(gbCgbMode) { + s.Printf(wxT("%d"), attrs & 7); + palette->SetLabel(s); + } else + palette->SetLabel(wxT("---")); + prio->SetLabel(wxString(attrs & 0x80 ? wxT('P') : wxT('-'))); + } + } + protected: + int charbase, mapbase; + wxControl *coords, *addr, *tile, *flip, *palette, *prio; + int selx, sely; + + DECLARE_EVENT_TABLE() + }; + + BEGIN_EVENT_TABLE(GBMapViewer, GfxViewer) + EVT_GFX_CLICK(wxID_ANY, GBMapViewer::UpdateMouseInfoEv) + END_EVENT_TABLE() +} + +void MainFrame::MapViewer() +{ + switch(panel->game_type()) { + case IMAGE_GBA: + LoadXRCViewer(Map); + break; + case IMAGE_GB: + LoadXRCViewer(GBMap); + break; + } +} + +namespace Viewers { + class OAMViewer : public GfxViewer + { + public: + OAMViewer() : GfxViewer(wxT("OAMViewer"), 64, 64) + { + sprite = 0; + getspin(,"Sprite", sprite); + getlab(pos, "Pos", "5WW,2WW"); + getlab(mode, "Mode", "3"); + getlab(colors, "Colors", "256"); + getlab(pallab, "Palette", "1W"); + getlab(tile, "Tile", "1WWW"); + getlab(prio, "Priority", "3"); + getlab(size, "Size", "64x64"); + getlab(rot, "Rotation", "3W"); + getlab(flg, "Flags", "RHVMD"); + Fit(); + Update(); + } + void Update() + { + u16 *sparms = &((u16 *)oam)[4 * sprite]; + u16 a0 = sparms[0], a1 = sparms[1], a2 = sparms[2]; + u16 *pal = &((u16 *)paletteRAM)[0x100]; + u8 *bmp = image.GetData(); + + int sizeX = 8, sizeY = 8; + + // following is almost verbatim from OamView.cpp + // shape = (a0 >> 14) & 3; + // size = (a1 >> 14) & 3; + switch(((a0 >> 12) & 0xc) | (a1 >> 14)) { + case 0: + break; + case 1: + sizeX = sizeY = 16; + break; + case 2: + sizeX = sizeY = 32; + break; + case 3: + sizeX = sizeY = 64; + break; + case 4: + sizeX = 16; + break; + case 5: + sizeX = 32; + break; + case 6: + sizeX = 32; + sizeY = 16; + break; + case 7: + sizeX = 64; + sizeY = 32; + break; + case 8: + sizeY = 16; + break; + case 9: + sizeY = 32; + break; + case 10: + sizeX = 16; + sizeY = 32; + break; + case 11: + sizeX = 32; + sizeY = 64; + break; + default: + pos->SetLabel(wxEmptyString); + mode->SetLabel(wxEmptyString); + colors->SetLabel(wxEmptyString); + pallab->SetLabel(wxEmptyString); + tile->SetLabel(wxEmptyString); + prio->SetLabel(wxEmptyString); + size->SetLabel(wxEmptyString); + rot->SetLabel(wxEmptyString); + flg->SetLabel(wxEmptyString); + BMPSize(sizeX, sizeY); + memset(bmp, 0, 8 * 8 * 3); + ChangeBMP(); + return; + } + BMPSize(sizeX, sizeY); + + int sy = (a0 & 255); + + if(a0 & 0x2000) { + int c = (a2 & 0x3FF); + //if((DISPCNT & 7) > 2 && (c < 512)) + // return; + int inc = 32; + if(DISPCNT & 0x40) + inc = sizeX >> 2; + else + c &= 0x3FE; + + for(int y = 0; y < sizeY; y++) { + for(int x = 0; x < sizeX; x++) { + u32 color = vram[0x10000 + (((c + (y>>3) * inc)* + 32 + (y & 7) * 8 + (x >> 3) * 64 + + (x & 7))&0x7FFF)]; + color = pal[color]; + *bmp++ = (color & 0x1f) << 3; + *bmp++ = ((color >> 5) & 0x1f) << 3; + *bmp++ = ((color >> 10) & 0x1f) << 3; + } + bmp += (64 - sizeX) * 3; + } + } else { + int c = (a2 & 0x3FF); + //if((DISPCNT & 7) > 2 && (c < 512)) + // return; + int inc = 32; + if(DISPCNT & 0x40) + inc = sizeX >> 3; + int palette = (a2 >> 8) & 0xF0; + for(int y = 0; y < sizeY; y++) { + for(int x = 0; x < sizeX; x++) { + u32 color = vram[0x10000 + (((c + (y>>3) * inc)* + 32 + (y & 7) * 4 + (x >> 3) * 32 + + ((x & 7)>>1))&0x7FFF)]; + if(x & 1) + color >>= 4; + else + color &= 0x0F; + + color = pal[palette+color]; + *bmp++ = (color & 0x1f) << 3; + *bmp++ = ((color >> 5) & 0x1f) << 3; + *bmp++ = ((color >> 10) & 0x1f) << 3; + } + bmp += (64 - sizeX) * 3; + } + } + ChangeBMP(); + + wxString s; + s.Printf(wxT("%d,%d"), a1 & 511, a0 & 255); + pos->SetLabel(s); + s.Printf(wxT("%d"), (a0 >> 10) & 3); + mode->SetLabel(s); + colors->SetLabel(a0 & 8192 ? wxT("256") : wxT("16")); + s.Printf(wxT("%d"), (a2 >> 12) & 15); + pallab->SetLabel(s); + s.Printf(wxT("%d"), a2 & 1023); + tile->SetLabel(s); + s.Printf(wxT("%d"), (a2 >> 10) & 3); + prio->SetLabel(s); + s.Printf(wxT("%dx%d"), sizeX, sizeY); + size->SetLabel(s); + if(a0 & 512) { + s.Printf(wxT("%d"), (a1 >> 9) & 31); + rot->SetLabel(s); + } else + rot->SetLabel(wxEmptyString); + s = wxEmptyString; + if(a0 & 512) + s.append(wxT("R--")); + else { + s.append(wxT('-')); + s.append(a1 & 4096 ? wxT('H') : wxT('-')); + s.append(a1 & 8192 ? wxT('V') : wxT('-')); + } + s.append(a0 & 4096 ? wxT('M') : wxT('-')); + s.append(a0 & 1024 ? wxT('D') : wxT('-')); + flg->SetLabel(s); + } + protected: + int sprite; + wxControl *pos, *mode, *colors, *pallab, *tile, *prio, *size, *rot, *flg; + }; + + class GBOAMViewer : public GfxViewer + { + public: + GBOAMViewer() : GfxViewer(wxT("GBOAMViewer"), 8, 16) + { + sprite = 0; + getspin(,"Sprite", sprite); + getlab(pos, "Pos", "2WW,2WW"); + getlab(tilelab, "Tile", "2WW"); + getlab(prio, "Priority", "W"); + getlab(oap, "OAP", "W"); + getlab(pallab, "Palette", "W"); + getlab(flg, "Flags", "HV"); + getlab(banklab, "Bank", "W"); + Fit(); + Update(); + } + void Update() + { + u8 *bmp = image.GetData(); + + // following is almost verbatim from GBOamView.cpp + u16 addr = sprite * 4 + 0xfe00; + + int size = register_LCDC & 4; + + u8 y = gbMemory[addr++]; + u8 x = gbMemory[addr++]; + u8 tile = gbMemory[addr++]; + if(size) + tile &= 254; + u8 flags = gbMemory[addr++]; + + int w = 8; + int h = size ? 16 : 8; + BMPSize(w, h); + + u8 * bank0; + u8 * bank1; + if(gbCgbMode) { + if(register_VBK & 1) { + bank0 = &gbVram[0x0000]; + bank1 = &gbVram[0x2000]; + } else { + bank0 = &gbVram[0x0000]; + bank1 = &gbVram[0x2000]; + } + } else { + bank0 = &gbMemory[0x8000]; + bank1 = NULL; + } + + int init = 0x0000; + + u8 *pal = gbObp0; + + if((flags & 0x10)) + pal = gbObp1; + + for(int yy = 0; yy < h; yy++) { + int address = init + tile * 16 + 2*yy; + int a = 0; + int b = 0; + + if(gbCgbMode && flags & 0x08) { + a = bank1[address++]; + b = bank1[address++]; + } else { + a = bank0[address++]; + b = bank0[address++]; + } + + for(int xx = 0; xx < 8; xx++) { + u8 mask = 1 << (7-xx); + u8 c = 0; + if( (a & mask)) + c++; + if( (b & mask)) + c+=2; + + // make sure that sprites will work even in CGB mode + if(gbCgbMode) { + c = c + (flags & 0x07)*4 + 32; + } else { + c = pal[c]; + } + + u16 color = gbPalette[c]; + *bmp++ = (color & 0x1f) << 3; + *bmp++ = ((color >> 5) & 0x1f) << 3; + *bmp++ = ((color >> 10) & 0x1f) << 3; + } + } + ChangeBMP(); + + wxString s; + s.Printf(wxT("%d,%d"), x, y); + pos->SetLabel(s); + s.Printf(wxT("%d"), tile); + tilelab->SetLabel(s); + prio->SetLabel(flags & 0x80 ? wxT("1") : wxT("0")); + oap->SetLabel(flags & 0x08 ? wxT("1") : wxT("0")); + s.Printf(wxT("%d"), flags & 7); + pallab->SetLabel(s); + s = flags & 0x20 ? wxT('H') : wxT('-'); + s.append(flags & 0x40 ? wxT('V') : wxT('-')); + flg->SetLabel(s); + banklab->SetLabel(flags & 0x10 ? wxT("1") : wxT("0")); + } + protected: + int sprite; + wxControl *pos, *tilelab, *prio, *oap, *pallab, *flg, *banklab; + }; +} + +void MainFrame::OAMViewer() +{ + switch(panel->game_type()) { + case IMAGE_GBA: + LoadXRCViewer(OAM); + break; + case IMAGE_GB: + LoadXRCViewer(GBOAM); + break; + } +} + +namespace Viewers { + static int ptype = 0; + static wxString pdir; + void savepal(wxWindow *parent, const u8 *data, int ncols, const wxChar *type) + { + // no attempt is made here to translate the palette type name + // it's just a suggested name, anyway + wxString def_name = wxGetApp().frame->GetPanel()->game_name() + + wxT('-') + type; + if(ptype == 2) + def_name += wxT(".act"); + else + def_name += wxT(".pal"); + wxFileDialog dlg(parent, _("Select output file and type"), pdir, def_name, + _("Windows Palette (*.pal)|*.pal|PaintShop Palette (*.pal)|*.pal|Adobe Color Table (*.act)|*.act"), + wxFD_SAVE|wxFD_OVERWRITE_PROMPT); + dlg.SetFilterIndex(ptype); + int ret = dlg.ShowModal(); + ptype = dlg.GetFilterIndex(); + pdir = dlg.GetDirectory(); + if(ret != wxID_OK) + return; + wxFFile f(dlg.GetPath(), wxT("wb")); + // FIXME: check for errors + switch(ptype) { + case 0: // Windows palette + { + f.Write("RIFF", 4); + u32 d = wxUINT32_SWAP_ON_BE(256 * 4 + 16); + f.Write(&d, 4); + f.Write("PAL data", 8); + d = wxUINT32_SWAP_ON_BE(256 * 4 + 4); + f.Write(&d, 4); + u16 w = wxUINT16_SWAP_ON_BE(0x0300); + f.Write(&w, 2); + w = wxUINT16_SWAP_ON_BE(256); // cuases problems if not 16 or 256 + f.Write(&w, 2); + for(int i = 0; i < ncols; i++, data += 3) { + f.Write(data, 3); + u8 z = 0; + f.Write(&z, 1); + } + for(int i = ncols; i < 256; i++) { + d = 0; + f.Write(&d, 4); + } + } + break; + case 1: // PaintShop palette + { +#define jasc_head "JASC-PAL\r\n0100\r\n256\r\n" + f.Write(jasc_head, sizeof(jasc_head) - 1); + for(int i = 0; i < ncols; i++, data += 3) { + char buf[14]; + int l = sprintf(buf, "%d %d %d\r\n", data[0], data[1], data[2]); + f.Write(buf, l); + } + for(int i = ncols; i < 256; i++) + f.Write("0 0 0\r\n", 7); + break; + } + case 2: // Adobe color table + { + f.Write(data, ncols * 3); + u32 d = 0; + for(int i = ncols; i < 256; i++) + f.Write(&d, 3); + } + break; + } + f.Close(); // FIXME: check for errors + } + + class PaletteViewer : public Viewer + { + public: + PaletteViewer() : Viewer(wxT("PaletteViewer")) + { + colorctrl(cv, "Color"); + pixview(bpv, "Background", 16, 16, cv); + pixview(spv, "Sprite", 16, 16, cv); + getlab(addr, "Address", "0x5000WWW"); + getlab(val, "Value", "0xWWWW"); + Fit(); + Update(); + } + void Update() + { + if(paletteRAM) { + u16 *pp = (u16 *)paletteRAM; + u8 *bmp = colbmp; + for(int i = 0; i < 512; i++, pp++) { + *bmp++ = (*pp & 0x1f) << 3; + *bmp++ = (*pp & 0x3e0) >> 2; + *bmp++ = (*pp & 0x7c00) >> 7; + } + } else + memset(colbmp, 0, sizeof(colbmp)); + bpv->SetData(colbmp, 16, 0, 0); + spv->SetData(colbmp + 16*16*3, 16, 0, 0); + ShowSel(); + } + void SelBG(wxMouseEvent &ev) + { + spv->SetSel(-1, -1, false); + ShowSel(); + } + void SelSprite(wxMouseEvent &ev) + { + bpv->SetSel(-1, -1, false); + ShowSel(); + } + void ShowSel() + { + int x, y; + bool isbg = true; + bpv->GetSel(x, y); + if(x < 0) { + isbg = false; + spv->GetSel(x, y); + if(x < 0) { + addr->SetLabel(wxEmptyString); + val->SetLabel(wxEmptyString); + return; + } + } + int off = x + y * 16; + if(!isbg) + off += 16 * 16; + u8 *pix = &colbmp[off * 3]; + u16 v = (pix[0] >> 3) + ((pix[1] >> 3) << 5) + ((pix[2] >> 3) << 10); + wxString s; + s.Printf(wxT("0x%04X"), (int)v); + val->SetLabel(s); + s.Printf(wxT("0x%08X"), 0x5000000 + 2 * off); + addr->SetLabel(s); + } + void SaveBG(wxCommandEvent &ev) + { + savepal(this, colbmp, 16 * 16, wxT("bg")); + } + void SaveOBJ(wxCommandEvent &ev) + { + savepal(this, colbmp + 16 * 16 * 3, 16 * 16, wxT("obj")); + } + void ChangeBackdrop(wxCommandEvent &ev) + { + // FIXME: this should really be a preference + // should also have some way of indicating selection + // perhaps replace w/ checkbox + colorpickerctrl + static wxColourData *cd = NULL; + wxColourDialog dlg(this, cd); + if(dlg.ShowModal() == wxID_OK) { + if(!cd) + cd = new wxColourData(); + *cd = dlg.GetColourData(); + wxColour c = cd->GetColour(); + customBackdropColor = + (c.Red() >> 3) + + (c.Green() >> 3) << 5 + + (c.Blue() >> 3) << 10; + } else + // kind of an unintuitive way to turn it off... + customBackdropColor = -1; + } + protected: + ColorView *cv; + PixView *bpv, *spv; + u8 colbmp[16*16*3*2]; + wxControl *addr, *val; + + DECLARE_EVENT_TABLE() + }; + + BEGIN_EVENT_TABLE(PaletteViewer, Viewer) + EVT_BUTTON(XRCID("SaveBG"), PaletteViewer::SaveBG) + EVT_BUTTON(XRCID("SaveOBJ"), PaletteViewer::SaveOBJ) + EVT_BUTTON(XRCID("ChangeBackdrop"), PaletteViewer::ChangeBackdrop) + EVT_GFX_CLICK(XRCID("Background"), PaletteViewer::SelBG) + EVT_GFX_CLICK(XRCID("Sprite"), PaletteViewer::SelSprite) + END_EVENT_TABLE() + + class GBPaletteViewer : public Viewer + { + public: + GBPaletteViewer() : Viewer(wxT("GBPaletteViewer")) + { + colorctrl(cv, "Color"); + pixview(bpv, "Background", 4, 8, cv); + pixview(spv, "Sprite", 4, 8, cv); + getlab(idx, "Index", "3W"); + getlab(val, "Value", "0xWWWW"); + Fit(); + Update(); + } + void Update() + { + u16 *pp = gbPalette; + u8 *bmp = colbmp; + for(int i = 0; i < 64; i++, pp++) { + *bmp++ = (*pp & 0x1f) << 3; + *bmp++ = (*pp & 0x3e0) >> 2; + *bmp++ = (*pp & 0x7c00) >> 7; + } + bpv->SetData(colbmp, 4, 0, 0); + spv->SetData(colbmp + 4*8*3, 4, 0, 0); + ShowSel(); + } + void SelBG(wxMouseEvent &ev) + { + spv->SetSel(-1, -1, false); + ShowSel(); + } + void SelSprite(wxMouseEvent &ev) + { + bpv->SetSel(-1, -1, false); + ShowSel(); + } + void ShowSel() + { + int x, y; + bool isbg = true; + bpv->GetSel(x, y); + if(x < 0) { + isbg = false; + spv->GetSel(x, y); + if(x < 0) { + idx->SetLabel(wxEmptyString); + val->SetLabel(wxEmptyString); + return; + } + } + u8 *pix = &colbmp[(x + y * 4) * 3]; + if(isbg) + pix += 4 * 8 * 3; + u16 v = (pix[0] >> 3) + ((pix[1] >> 3) << 5) + ((pix[2] >> 3) << 10); + wxString s; + s.Printf(wxT("0x%04X"), (int)v); + val->SetLabel(s); + s.Printf(wxT("%d"), x + y * 4); + idx->SetLabel(s); + } + void SaveBG(wxCommandEvent &ev) + { + savepal(this, colbmp, 4 * 8, wxT("bg")); + } + void SaveOBJ(wxCommandEvent &ev) + { + savepal(this, colbmp + 4 * 8 * 3, 4 * 8, wxT("obj")); + } + protected: + ColorView *cv; + PixView *bpv, *spv; + u8 colbmp[4*8*3*2]; + wxControl *idx, *val; + DECLARE_EVENT_TABLE() + }; + + BEGIN_EVENT_TABLE(GBPaletteViewer, Viewer) + EVT_BUTTON(XRCID("SaveBG"), GBPaletteViewer::SaveBG) + EVT_BUTTON(XRCID("SaveOBJ"), GBPaletteViewer::SaveOBJ) + EVT_GFX_CLICK(XRCID("Background"), GBPaletteViewer::SelBG) + EVT_GFX_CLICK(XRCID("Sprite"), GBPaletteViewer::SelSprite) + END_EVENT_TABLE() +} + +void MainFrame::PaletteViewer() +{ + switch(panel->game_type()) { + case IMAGE_GBA: + LoadXRCViewer(Palette); + break; + case IMAGE_GB: + LoadXRCViewer(GBPalette); + break; + } +} + +namespace Viewers { + class TileViewer : public GfxViewer + { + public: + TileViewer() : GfxViewer(wxT("TileViewer"), 32*8, 32*8) + { + is256 = charbase = 0; + getradio(,"Color16", is256, 0); + getradio(,"Color256", is256, 1); + getradio(,"CharBase0", charbase, 0); + getradio(,"CharBase1", charbase, 0x4000); + getradio(,"CharBase2", charbase, 0x8000); + getradio(,"CharBase3", charbase, 0xc000); + getradio(,"CharBase4", charbase, 0x10000); + getslider(,"Palette", palette); + getlab(tileno, "Tile", "1WWW"); + getlab(addr, "Address", "06WWWWWW"); + selx = sely = -1; + Fit(); + Update(); + } + void Update() + { + // Following copied almost verbatim from TileView.cpp + u16 *palette = (u16 *)paletteRAM; + u8 *charBase = &vram[charbase]; + + int maxY; + + if(is256) { + int tile = 0; + maxY = 16; + for(int y = 0; y < maxY; y++) { + for(int x = 0; x < 32; x++) { + if(charbase == 4 * 0x4000) + render256(tile, x, y, charBase, &palette[256]); + else + render256(tile, x, y, charBase, palette); + tile++; + } + } + BMPSize(32*8, maxY*8); + } else { + int tile = 0; + maxY = 32; + if(charbase == 3 * 0x4000) + maxY = 16; + for(int y = 0; y < maxY; y++) { + for(int x = 0; x < 32; x++) { + render16(tile, x, y, charBase, palette); + tile++; + } + } + BMPSize(32*8, maxY*8); + } + ChangeBMP(); + UpdateMouseInfo(); + } + void UpdateMouseInfoEv(wxMouseEvent &ev) + { + selx = ev.GetX(); + sely = ev.GetY(); + UpdateMouseInfo(); + } + + void UpdateMouseInfo() + { + if(selx > gv->bmw || sely > gv->bmh) + selx = sely = -1; + if(selx < 0) { + addr->SetLabel(wxEmptyString); + tileno->SetLabel(wxEmptyString); + } else { + int x = selx / 8; + int y = sely / 8; + int t = 32 * y + x; + if(is256) + t *= 2; + wxString s; + s.Printf(wxT("%d"), t); + tileno->SetLabel(s); + s.Printf(wxT("%08X"), 0x6000000 + charbase + 32 * t); + addr->SetLabel(s); + } + } + // following 2 functions copied almost verbatim from TileView.cpp + void render256(int tile, int x, int y, u8 *charBase, u16 *palette) + { + u8 *bmp = &image.GetData()[24*x + 8*32*24*y]; + + for(int j = 0; j < 8; j++) { + for(int i = 0; i < 8; i++) { + u8 c = charBase[tile*64 + j * 8 + i]; + + u16 color = palette[c]; + + *bmp++ = (color & 0x1f) << 3; + *bmp++ = ((color >> 5) & 0x1f) << 3; + *bmp++ = ((color >> 10) & 0x1f) << 3; + + } + bmp += 31*24; // advance line + } + } + + void render16(int tile, int x, int y, u8 *charBase, u16 *palette) + { + u8 *bmp = &image.GetData()[24*x + 8*32*24*y]; + + int pal = this->palette; + + if(this->charbase == 4 * 0x4000) + pal += 16; + + for(int j = 0; j < 8; j++) { + for(int i = 0; i < 8; i++) { + u8 c = charBase[tile*32 + j * 4 + (i>>1)]; + + if(i & 1) + c = c>>4; + else + c = c & 15; + + u16 color = palette[pal*16+c]; + + *bmp++ = (color & 0x1f) << 3; + *bmp++ = ((color >> 5) & 0x1f) << 3; + *bmp++ = ((color >> 10) & 0x1f) << 3; + } + bmp += 31*24; // advance line + } + } + + protected: + int charbase, is256, palette; + wxControl *tileno, *addr; + int selx, sely; + + DECLARE_EVENT_TABLE() + }; + + BEGIN_EVENT_TABLE(TileViewer, GfxViewer) + EVT_GFX_CLICK(wxID_ANY, TileViewer::UpdateMouseInfoEv) + END_EVENT_TABLE() + + class GBTileViewer : public GfxViewer + { + public: + GBTileViewer() : GfxViewer(wxT("GBTileViewer"), 16*8, 16*8) + { + bank = charbase = 0; + getradio(,"Bank0", bank, 0); + getradio(,"Bank1", bank, 0x2000); + getradio(,"CharBase0", charbase, 0); + getradio(,"CharBase1", charbase, 0x800); + getslider(,"Palette", palette); + getlab(tileno, "Tile", "2WW"); + getlab(addr, "Address", "WWWW"); + selx = sely = -1; + Fit(); + Update(); + } + void Update() + { + // following copied almost verbatim from GBTileView.cpp + u8 *charBase = (gbVram != NULL) ? + &gbVram[bank+charbase] : + &gbMemory[0x8000+charbase]; + + int tile = 0; + for(int y = 0; y < 16; y++) { + for(int x = 0; x < 16; x++) { + render(tile, x, y, charBase); + tile++; + } + } + ChangeBMP(); + UpdateMouseInfo(); + } + void UpdateMouseInfoEv(wxMouseEvent &ev) + { + selx = ev.GetX(); + sely = ev.GetY(); + UpdateMouseInfo(); + } + + void UpdateMouseInfo() + { + if(selx > gv->bmw || sely > gv->bmh) + selx = sely = -1; + if(selx < 0) { + addr->SetLabel(wxEmptyString); + tileno->SetLabel(wxEmptyString); + } else { + int x = selx / 8; + int y = sely / 8; + int t = 16 * y + x; + wxString s; + s.Printf(wxT("%d"), t); + tileno->SetLabel(s); + s.Printf(wxT("%04X"), 0x8000 + charbase + 16 * t); + addr->SetLabel(s); + } + } + + // following function copied almost verbatim from GBTileView.cpp + void render(int tile, int x, int y, u8 *charBase) + { + u8 *bmp = &image.GetData()[24*x + 8*16*24*y]; + + for(int j = 0; j < 8; j++) { + u8 mask = 0x80; + u8 tile_a = charBase[tile*16+j*2]; + u8 tile_b = charBase[tile*16+j*2+1]; + + for(int i = 0; i < 8; i++) { + u8 c = (tile_a & mask) ? 1 : 0; + c += ((tile_b & mask) ? 2 : 0); + + if(gbCgbMode) { + c = c + palette*4; + } else { + c = gbBgp[c]; + } + + u16 color = gbPalette[c]; + + *bmp++ = (color & 0x1f) << 3; + *bmp++ = ((color >> 5) & 0x1f) << 3; + *bmp++ = ((color >> 10) & 0x1f) << 3; + + mask >>= 1; + } + bmp += 15*24; // advance line + } + } + protected: + int bank, charbase, palette; + wxControl *addr, *tileno; + int selx, sely; + + DECLARE_EVENT_TABLE() + }; + + BEGIN_EVENT_TABLE(GBTileViewer, GfxViewer) + EVT_GFX_CLICK(wxID_ANY, GBTileViewer::UpdateMouseInfoEv) + END_EVENT_TABLE() +} + +void MainFrame::TileViewer() +{ + switch(panel->game_type()) { + case IMAGE_GBA: + LoadXRCViewer(Tile); + break; + case IMAGE_GB: + LoadXRCViewer(GBTile); + break; + } +} diff --git a/src/wx/guiinit.cpp b/src/wx/guiinit.cpp new file mode 100644 index 0000000..b61594f --- /dev/null +++ b/src/wx/guiinit.cpp @@ -0,0 +1,3319 @@ + +// initialize menus & dialogs, etc. +// for most of the prefs dialogs, all code resides here in the form of +// event handlers & validators +// other non-viewer dialogs are at least validated enough that they won't crash +// viewer dialogs are not commonly used, so they are initialized on demand + +#include "wxvbam.h" + +#include +#include +#include +#include +#include +#include +#include "wx/checkedlistctrl.h" +#include +#include +#include "../gba/CheatSearch.h" + +// The program icon, in case it's missing from .xrc (MSW gets it from .rc file) +#if !defined(__WXMSW__) && !defined(__WXPM__) +// ImageMagick makes the name wxvbam, but wx expects wxvbam_xpm +#define wxvbam wxvbam_xpm +const +#include "wxvbam.xpm" +#undef wxvbam +#endif + +// this is supposed to happen automatically if a parent is marked recursive +// but some dialogs don't do it (propertydialog?) +// so go ahead and mark all dialogs for fully recursive validation +static void mark_recursive(wxWindowBase *w) +{ + w->SetExtraStyle(w->GetExtraStyle() | wxWS_EX_VALIDATE_RECURSIVELY); + wxWindowList l = w->GetChildren(); + for(wxWindowList::iterator ch = l.begin(); ch != l.end(); ch++) + mark_recursive(*ch); +} + +#define GetXRCDialog(n) \ + wxStaticCast(wxGetApp().frame->FindWindow(XRCID(n)), wxDialog) + +// Event handlers must be methods of wxEvtHandler-derived objects + +// manage the network link dialog +#ifndef NO_LINK +static class NetLink_t : public wxEvtHandler +{ +public: + wxDialog *dlg; + int n_players; + NetLink_t() : n_players(2) {} + wxButton *okb; + void ServerOKButton(wxCommandEvent &ev) + { + okb->SetLabel(_("Start!")); + } + void ClientOKButton(wxCommandEvent &ev) + { + okb->SetLabel(_("Connect")); + } + // attached to OK, so skip when OK + void NetConnect(wxCommandEvent &ev) + { + if(!dlg->Validate() || !dlg->TransferDataFromWindow()) + return; + update_opts(); // save fast flag and client host + + wxString connmsg, pmsg; + + wxMutex lock; + wxCondition sig(lock); + lock.Lock(); + + bool done = false; + + if(lanlink.server) { + lanlink.numslaves = n_players - 1; + class sid_t : public ServerInfoDisplay + { + wxMutex *lock; + wxCondition *sig; + wxString *connmsg, *pmsg; + bool *done; + bool conn[3]; + public: + sid_t(wxMutex *m, wxCondition *c, wxString *cm, wxString *pm, + bool *d) : + lock(m), sig(c), connmsg(cm), pmsg(pm), done(d) {} + void ShowServerIP(const sf::IPAddress &addr) { + wxString addr_s(addr.ToString().c_str(), wxConvLibc); + wxString msg; + msg.Printf(_("Server IP address is: %s\n"), addr_s.c_str()); + connmsg->append(msg); + conn[0] = conn[1] = conn[2] = false; + } + void ShowConnect(int player) { + wxString msg; + conn[player - 1] = true; + lock->Lock(); + pmsg->clear(); + for(int i = 0; i < 3; i++) + if(conn[i]) { + msg.Printf(_("Player %d connected\n"), i + 2); + pmsg->append(msg); + } + sig->Signal(); + lock->Unlock(); + } + void Ping() { + lock->Lock(); + sig->Signal(); + if(*done) + lanlink.terminate = true; + lock->Unlock(); + } + void Connected() { + lock->Lock(); + *done = true; + sig->Signal(); + lock->Unlock(); + } + }; + + sid_t* sid = new sid_t(&lock, &sig, &connmsg, &pmsg, &done); + + if (!ls.Init(sid)) { + wxLogError(_("Error occurred.\nPlease try again.")); + lock.Unlock(); + delete sid; + return; + } + + wxProgressDialog + pdlg(_("Waiting for clients..."), connmsg, + 100, dlg, wxPD_APP_MODAL|wxPD_CAN_ABORT|wxPD_ELAPSED_TIME); + + while(!done) { + if(!pdlg.Pulse(connmsg + pmsg)) + done = true; + sig.Wait(); + } + } else { + class cid_t : public ClientInfoDisplay + { + wxMutex *lock; + wxCondition *sig; + wxString *connmsg, *pmsg; + bool *done; + public: + cid_t(wxMutex *m, wxCondition *c, wxString *cm, wxString *pm, + bool *d) : + lock(m), sig(c), connmsg(cm), pmsg(pm), done(d) {} + void ConnectStart(const sf::IPAddress &addr) { + wxString addr_s(addr.ToString().c_str(), wxConvLibc); + connmsg->Printf(_("Connecting to %s\n"), addr_s.c_str()); + } + void ShowConnect(int player, int togo) { + wxString msg; + lock->Lock(); + pmsg->Printf(_("Connected as #%d\n"), player); + if(togo) + msg.Printf(_("Waiting for %d players to join"), togo); + else + msg = _("All players joined."); + pmsg->append(msg); + sig->Signal(); + lock->Unlock(); + } + void Ping() { + lock->Lock(); + sig->Signal(); + if(*done) + lanlink.terminate = true; + lock->Unlock(); + } + void Connected() { + lock->Lock(); + *done = true; + sig->Signal(); + lock->Unlock(); + } + }; + + cid_t* cid = new cid_t(&lock, &sig, &connmsg, &pmsg, &done); + + if (!lc.Init(sf::IPAddress(std::string(gopts.link_host.mb_str())), cid)) { + wxLogError(_("Error occurred.\nPlease try again.")); + lock.Unlock(); + delete cid; + return; + } + + wxProgressDialog + pdlg(_("Waiting for connection..."), connmsg, + 100, dlg, wxPD_APP_MODAL|wxPD_CAN_ABORT|wxPD_ELAPSED_TIME); + + while(!done) { + if(!pdlg.Pulse(connmsg + pmsg)) + done = true; + sig.Wait(); + } + } + lock.Unlock(); + if(lanlink.connected) { + pmsg.Replace(wxT("\n"), wxT(" ")); + systemScreenMessage(pmsg); + lanlink.active = true; + ev.Skip(); // all OK + } + } +} net_link_handler; +#endif + +// manage the cheat list dialog +static class CheatList_t : public wxEvtHandler +{ +public: + wxDialog *dlg; + wxCheckedListCtrl *list; + wxListItem item0, item1; + int col1minw; + wxString cheatdir, cheatfn, deffn; + bool isgb; + bool *dirty; + + // add/edit dialog + wxString ce_desc; + wxString ce_codes; + wxChoice *ce_type_ch; + wxControl *ce_codes_tc; + int ce_type; + + void Reload() + { + list->DeleteAllItems(); + Reload(0); + } + + void Reload(int start) + { + if(isgb) { + for(int i = start; i < gbCheatNumber; i++) { + item0.SetId(i); + item0.SetText(wxString(gbCheatList[i].cheatCode, wxConvLibc)); + list->InsertItem(item0); + item1.SetId(i); + item1.SetText(wxString(gbCheatList[i].cheatDesc, wxConvUTF8)); + list->SetItem(item1); + list->Check(i, gbCheatList[i].enabled); + } + } else { + for(int i = start; i < cheatsNumber; i++) { + item0.SetId(i); + item0.SetText(wxString(cheatsList[i].codestring, wxConvLibc)); + list->InsertItem(item0); + item1.SetId(i); + item1.SetText(wxString(cheatsList[i].desc, wxConvUTF8)); + list->SetItem(item1); + list->Check(i, cheatsList[i].enabled); + } + } + AdjustDescWidth(); + } + + void Tool(wxCommandEvent &ev) + { + switch(ev.GetId()) { + case wxID_OPEN: + { + wxFileDialog subdlg(dlg, _("Select cheat file"), cheatdir, + cheatfn, _("VBA cheat lists (*.clt)|*.clt"), + wxFD_OPEN|wxFD_FILE_MUST_EXIST); + int ret = subdlg.ShowModal(); + cheatdir = subdlg.GetDirectory(); + cheatfn = subdlg.GetPath(); + if(ret != wxID_OK) + break; + bool cld; + if(isgb) + cld = gbCheatsLoadCheatList(cheatfn.mb_fn_str()); + else + cld = cheatsLoadCheatList(cheatfn.mb_fn_str()); + if(cld) { + *dirty = cheatfn != deffn; + systemScreenMessage(_("Loaded cheats")); + } else + *dirty = true; // attempted load always clears + Reload(); + } + break; + case wxID_SAVE: + { + wxFileDialog subdlg(dlg, _("Select cheat file"), cheatdir, + cheatfn, _("VBA cheat lists (*.clt)|*.clt"), + wxFD_SAVE|wxFD_OVERWRITE_PROMPT); + int ret = subdlg.ShowModal(); + cheatdir = subdlg.GetDirectory(); + cheatfn = subdlg.GetPath(); + if(ret != wxID_OK) + break; + // note that there is no way to test for succes of save + if(isgb) + gbCheatsSaveCheatList(cheatfn.mb_fn_str()); + else + cheatsSaveCheatList(cheatfn.mb_fn_str()); + if(cheatfn == deffn) + *dirty = false; + systemScreenMessage(_("Saved cheats")); + } + break; + case wxID_ADD: + { + int ncheats = isgb ? gbCheatNumber : cheatsNumber; + ce_codes = wxEmptyString; + wxDialog *subdlg = GetXRCDialog("CheatEdit"); + subdlg->ShowModal(); + AddCheat(); + Reload(ncheats); + } + break; + case wxID_REMOVE: + { + bool asked = false, restore; + for(int i = list->GetItemCount() - 1; i >= 0; i--) + if(list->GetItemState(i, wxLIST_STATE_SELECTED)) { + list->DeleteItem(i); + if(isgb) + gbCheatRemove(i); + else { + if(!asked) { + asked = true; + restore = wxMessageBox(_("Restore old values?"), + _("Removing cheats"), + wxYES_NO|wxICON_QUESTION) == wxYES; + } + cheatsDelete(i, restore); + } + } + } + break; + case wxID_CLEAR: + if(isgb) { + if(gbCheatNumber) { + *dirty = true; + gbCheatRemoveAll(); + } + } else { + if(cheatsNumber) { + bool restore = wxMessageBox(_("Restore old values?"), + _("Removing cheats"), + wxYES_NO|wxICON_QUESTION) == wxYES; + *dirty = true; + cheatsDeleteAll(restore); + } + } + Reload(); + break; + case wxID_SELECTALL: + // FIXME: probably ought to limit to selected items if any items + // are selected + *dirty = true; + if(isgb) { + int i; + for(i = 0; i < gbCheatNumber; i++) + if(!gbCheatList[i].enabled) + break; + if(i < gbCheatNumber) + for(; i < gbCheatNumber; i++) { + gbCheatEnable(i); + list->Check(i, true); + } + else + for(i = 0; i < gbCheatNumber; i++) { + gbCheatDisable(i); + list->Check(i, false); + } + } else { + int i; + for(i = 0; i < cheatsNumber; i++) + if(!cheatsList[i].enabled) + break; + if(i < cheatsNumber) + for(; i < cheatsNumber; i++) { + cheatsEnable(i); + list->Check(i, true); + } + else + for(i = 0; i < cheatsNumber; i++) { + cheatsDisable(i); + list->Check(i, false); + } + } + break; + } + } + + void Check(wxListEvent &ev) + { + int ch = ev.GetIndex(); + if(isgb) { + if(!gbCheatList[ch].enabled) { + gbCheatEnable(ev.GetIndex()); + *dirty = true; + } + } else { + if(!cheatsList[ch].enabled) { + cheatsEnable(ev.GetIndex()); + *dirty = true; + } + } + } + + void UnCheck(wxListEvent &ev) + { + int ch = ev.GetIndex(); + if(isgb) { + if(gbCheatList[ch].enabled) { + gbCheatDisable(ev.GetIndex()); + *dirty = true; + } + } else { + if(cheatsList[ch].enabled) { + cheatsDisable(ev.GetIndex()); + *dirty = true; + } + } + } + + void AddCheat() + { + wxStringTokenizer tk(ce_codes.MakeUpper()); + while(tk.HasMoreTokens()) { + wxString tok = tk.GetNextToken(); + if(isgb) { + if(!ce_type) + gbAddGsCheat(tok.mb_str(), ce_desc.mb_str()); + else + gbAddGgCheat(tok.mb_str(), ce_desc.mb_str()); + } else { + if(!ce_type) + cheatsAddCheatCode(tok.mb_str(), ce_desc.mb_str()); + // following determination of type by lengths is + // same used by win32 and gtk code + // and like win32/gtk code, user-chosen fmt is ignored + else if(tok.size() == 12) { + tok = tok.substr(0, 8) + wxT(' ') + tok.substr(8); + cheatsAddCBACode(tok.mb_str(), ce_desc.mb_str()); + } else if(tok.size() == 16) + // not sure why 1-tok is !v3 and 2-tok is v3.. + cheatsAddGSACode(tok.mb_str(), ce_desc.mb_str(), false); + // CBA codes are assumed to be N+4, and anything else + // is assumed to be GSA v3 (although I assume the + // actual formats should be 8+4 and 8+8) + else { + if(!tk.HasMoreTokens()) { + // throw an error appropriate to chosen type + if(ce_type == 1) // GSA + cheatsAddGSACode(tok.mb_str(), ce_desc.mb_str(), false); + else + cheatsAddCBACode(tok.mb_str(), ce_desc.mb_str()); + } else { + wxString tok2 = tk.GetNextToken(); + if(tok2.size() == 4) { + tok += wxT(' ') + tok2; + cheatsAddCBACode(tok.mb_str(), ce_desc.mb_str()); + } else { + tok += tok2; + cheatsAddGSACode(tok.mb_str(), ce_desc.mb_str(), true); + } + } + } + } + } + } + + void Edit(wxListEvent &ev) + { + int id = ev.GetIndex(); + + // GetItem() followed by GetText doesn't work, so retrieve from + // source + wxString odesc, ocode; + bool ochecked; + int otype; + if(isgb) { + ochecked = gbCheatList[id].enabled; + ce_codes = ocode = wxString(gbCheatList[id].cheatCode, wxConvLibc); + ce_desc = odesc = wxString(gbCheatList[id].cheatDesc, wxConvUTF8); + if(ce_codes.find(wxT('-')) == wxString::npos) + otype = ce_type = 0; + else + otype = ce_type = 1; + } else { + ochecked = cheatsList[id].enabled; + ce_codes = ocode = wxString(cheatsList[id].codestring, wxConvLibc); + ce_desc = odesc = wxString(cheatsList[id].desc, wxConvUTF8); + if(ce_codes.find(wxT(':')) != wxString::npos) + otype = ce_type = 0; + else if(ce_codes.find(wxT(' ')) == wxString::npos) + otype = ce_type = 1; + else + otype = ce_type = 2; + } + wxDialog *subdlg = GetXRCDialog("CheatEdit"); + if(subdlg->ShowModal() != wxID_OK) + return; + if(otype != ce_type || ocode != ce_codes) { + // vba core certainly doesn't make this easy + // there is no "change" function, so the only way to retain + // the old order is to delete this and all subsequent items, and + // then re-add them + // The MFC code got around this by not even supporting edits on + // gba codes (which have order dependencies) and just forcing + // edited codes to the rear on gb codes. + // It might be safest to only support desc edits, and force the + // user to re-enter codes to change them + int ncodes = isgb ? gbCheatNumber : cheatsNumber; + if(ncodes > id + 1) { + wxString codes[ncodes - id - 1]; + wxString descs[ncodes - id - 1]; + bool checked[ncodes - id - 1]; + bool v3[ncodes - id - 1]; + for(int i = id + 1; i < ncodes; i++) { + codes[i - id - 1] = wxString(isgb ? + gbCheatList[i].cheatCode : + cheatsList[i].codestring, + wxConvLibc); + descs[i - id - 1] = wxString(isgb ? + gbCheatList[i].cheatDesc : + cheatsList[i].desc, + wxConvUTF8); + checked[i - id - 1] = isgb ? gbCheatList[i].enabled : + cheatsList[i].enabled; + v3[i - id - 1] = isgb ? false : cheatsList[i].code == 257; + } + for(int i = ncodes - 1; i >= id; i--) { + list->DeleteItem(i); + if(isgb) + gbCheatRemove(i); + else + cheatsDelete(i, cheatsList[i].enabled); + } + AddCheat(); + if(!ochecked) { + if(isgb) + gbCheatDisable(id); + else + cheatsDisable(id); + } + for(int i = id + 1; i < ncodes; i++) { + ce_codes = codes[i - id - 1]; + ce_desc = descs[i - id - 1]; + if(isgb) { + if(ce_codes.find(wxT('-')) == wxString::npos) + ce_type = 0; + else + ce_type = 1; + } else { + if(ce_codes.find(wxT(':')) != wxString::npos) + ce_type = 0; + else if(ce_codes.find(wxT(' ')) == wxString::npos) { + ce_type = 1; + if(v3[i - id - 1]) + ce_codes.insert(8, 1, wxT(' ')); + } else + ce_type = 2; + } + AddCheat(); + if(!checked[i - id - 1]) { + if(isgb) + gbCheatDisable(i); + else + cheatsDisable(i); + } + } + } else { + list->DeleteItem(id); + if(isgb) + gbCheatRemove(id); + else + cheatsDelete(id, cheatsList[id].enabled); + AddCheat(); + if(!ochecked) { + if(isgb) + gbCheatDisable(id); + else + cheatsDisable(id); + } + } + Reload(id); + } else if(ce_desc != odesc) { + *dirty = true; + char *p = isgb ? gbCheatList[id].cheatDesc : cheatsList[id].desc; + strncpy(p, ce_desc.mb_str(), sizeof(cheatsList[0].desc)); + p[sizeof(cheatsList[0].desc) - 1] = 0; + item1.SetId(id); + item1.SetText(wxString(p, wxConvUTF8)); + list->SetItem(item1); + } + } + + void AdjustDescWidth() + { + // why is it so hard to get an accurate measurement out of wx? + // on msw, wxLIST_AUTOSIZE might actually be accurate. On wxGTK, + // and probably wxMAC (both of which use generic impl) wrong + // font is used both for rendering (col 0's font) and for + // wxLIST_AUTOSIZE calculation (the widget's font). + // The only way to defeat this is to calculate size manually + // Instead, this just allows user to set max size, and retains + // it. + int ow = list->GetColumnWidth(1); + list->SetColumnWidth(1, wxLIST_AUTOSIZE); + int cw = list->GetColumnWidth(1); + // subtracted in renderer from width avail for text + // but not added in wxLIST_AUTOSIZE + cw += 8; + if(cw < col1minw) + cw = col1minw; + if(cw < ow) + cw = ow; + list->SetColumnWidth(1, cw); + } +} cheat_list_handler; + +// onshow handler for above, in the form of an overzealous validator +class CheatListFill : public wxValidator +{ +public: + CheatListFill() : wxValidator() {} + CheatListFill(const CheatListFill &e) : wxValidator() {} + wxObject *Clone() const { return new CheatListFill(*this); } + bool TransferFromWindow() { return true; } + bool Validate(wxWindow *p) { return true; } + bool TransferToWindow() { + CheatList_t &clh = cheat_list_handler; + GameArea *panel = wxGetApp().frame->GetPanel(); + clh.isgb = panel->game_type() == IMAGE_GB; + clh.dirty = &panel->cheats_dirty; + clh.cheatfn = panel->game_name() + wxT(".clt"); + clh.cheatdir = panel->game_dir(); + clh.deffn = wxFileName(clh.cheatdir, clh.cheatfn).GetFullPath(); + clh.Reload(); + + clh.ce_desc = wxEmptyString; + wxChoice *ch = clh.ce_type_ch; + ch->Clear(); + if(clh.isgb) { + ch->Append(_("GameShark")); + ch->Append(_("GameGenie")); + } else { + ch->Append(_("Generic Code")); + ch->Append(_("GameShark Advance")); + ch->Append(_("CodeBreaker Advance")); + } + ch->SetSelection(0); + + return true; + } +}; + +// manage the cheat search dialog +enum cf_vfmt { + CFVFMT_SD, CFVFMT_UD, CFVFMT_UH +}; + +// virtual ListCtrl for cheat search results +class CheatListCtrl : public wxListCtrl +{ + public: + wxArrayInt addrs; + int cap_size; // size in effect when addrs were generated + int count8, count16, count32; // number of aligned addresses in addrs + wxString OnGetItemText(long item, long column) const; + + DECLARE_DYNAMIC_CLASS() +}; + +IMPLEMENT_DYNAMIC_CLASS(CheatListCtrl, wxListCtrl); + +static class CheatFind_t : public wxEvtHandler +{ +public: + wxDialog *dlg; + int valsrc, size, op, fmt; + int ofmt, osize; + wxString val_s; + wxTextCtrl *val_tc; + CheatListCtrl *list; + + // for enable/disable + wxRadioButton *old_rb, *val_rb; + wxControl *update_b, *clear_b, *add_b; + + bool isgb; + + // add dialog + wxString ca_desc, ca_val; + wxTextCtrl *ca_val_tc; + wxControl *ca_fmt, *ca_addr; + + CheatFind_t() : wxEvtHandler(), valsrc(0), size(0), op(0), fmt(0), val_s() {} + ~CheatFind_t() + { + // not that it matters to anyone but mem leak detectors.. + cheatSearchCleanup(&cheatSearchData); + } + + void Search(wxCommandEvent &ev) + { + dlg->TransferDataFromWindow(); + if(!valsrc && val_s.empty()) { + wxLogError(_("Number cannot be empty")); + return; + } + if(!cheatSearchData.count) + ResetSearch(ev); + if(valsrc) + cheatSearch(&cheatSearchData, op, size, fmt == CFVFMT_SD); + else + cheatSearchValue(&cheatSearchData, op, size, fmt == CFVFMT_SD, + SignedValue()); + Deselect(); + list->addrs.clear(); + list->count8 = list->count16 = list->count32 = 0; + list->cap_size = size; + for(int i = 0; i < cheatSearchData.count; i++) { + CheatSearchBlock *block = &cheatSearchData.blocks[i]; + for(int j = 0; j < block->size; j += (1 << size)) { + if(IS_BIT_SET(block->bits, j)) { + list->addrs.push_back((i << 28) + j); + if(!(j & 1)) + list->count16++; + if(!(j & 3)) + list->count32++; + // since listctrl is virtual, it should be able to handle + // at least 256k results, which is about the most you + // will ever get +#if 0 + if(list->addrs.size() > 1000) { + wxLogError(_("Search produced %d results. Please refine better"), + list->addrs.size()); + list->addrs.clear(); + return; + } +#endif + } + } + } + if(list->addrs.empty()) { + wxLogError(_("Search produced no results")); + // no point in keeping empty search results around + ResetSearch(ev); + if(old_rb->GetValue()) { + val_rb->SetValue(true); + // SetValue doesn't generate an event + val_tc->Enable(); + } + old_rb->Disable(); + update_b->Disable(); + clear_b->Disable(); + } else { + switch(size) { + case BITS_32: + list->count16 = list->count32 * 2; + // fall through + case BITS_16: + list->count8 = list->count16 * 2; + break; + case BITS_8: + list->count8 = list->addrs.size(); + } + old_rb->Enable(); + update_b->Enable(); + clear_b->Enable(); + } + list->SetItemCount(list->addrs.size()); + list->Refresh(); + } + + void UpdateVals(wxCommandEvent &ev) + { + if(cheatSearchData.count) { + cheatSearchUpdateValues(&cheatSearchData); + if(list->count8) + list->Refresh(); + update_b->Disable(); + } + } + + void ResetSearch(wxCommandEvent &ev) + { + if(!cheatSearchData.count) { + CheatSearchBlock *block = cheatSearchData.blocks; + + if(isgb) { + block->offset = 0xa000; + if(gbRam) + block->data = gbRam; + else + block->data = &gbMemory[0xa000]; + block->saved = (u8 *)malloc(gbRamSize); + block->size = gbRamSize; + block->bits = (u8 *)malloc(gbRamSize >> 3); + if(gbCgbMode) { + block++; + block->offset = 0xc000; + block->data = &gbMemory[0xc000]; + block->saved = (u8 *)malloc(0x1000); + block->size = 0x1000; + block->bits = (u8 *)malloc(0x1000 >> 3); + block++; + block->offset = 0xd000; + block->data = gbWram; + block->saved = (u8 *)malloc(0x8000); + block->size = 0x8000; + block->bits = (u8 *)malloc(0x8000 >> 3); + } else { + block++; + block->offset = 0xc000; + block->data = &gbMemory[0xc000]; + block->saved = (u8 *)malloc(0x2000); + block->size = 0x2000; + block->bits = (u8 *)malloc(0x2000 >> 3); + } + } else { + block->size = 0x40000; + block->offset = 0x2000000; + block->bits = (u8 *)malloc(0x40000 >> 3); + block->data = workRAM; + block->saved = (u8 *)malloc(0x40000); + block++; + block->size = 0x8000; + block->offset = 0x3000000; + block->bits = (u8 *)malloc(0x8000 >> 3); + block->data = internalRAM; + block->saved = (u8 *)malloc(0x8000); + } + cheatSearchData.count = (int)((block + 1) - cheatSearchData.blocks); + } + cheatSearchStart(&cheatSearchData); + if(list->count8) { + Deselect(); + list->count8 = list->count16 = list->count32 = 0; + list->addrs.clear(); + list->SetItemCount(0); + list->Refresh(); + if(old_rb->GetValue()) { + val_rb->SetValue(true); + // SetValue doesn't generate an event + val_tc->Enable(); + } + old_rb->Disable(); + update_b->Disable(); + clear_b->Disable(); + } + } + + void Deselect() + { + int idx = list->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + if(idx >= 0) + list->SetItemState(idx, 0, wxLIST_STATE_SELECTED); + add_b->Disable(); + } + + void Select(wxListEvent &ev) + { + add_b->Enable(list->GetItemState(ev.GetIndex(), wxLIST_STATE_SELECTED) != 0); + } + + void AddCheatB(wxCommandEvent &ev) + { + int idx = list->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + if(idx >= 0) + AddCheat(idx); + } + + void AddCheatL(wxListEvent &ev) + { + AddCheat(ev.GetIndex()); + } + + void AddCheat(int idx) + { + wxString addr_s = list->OnGetItemText(idx, 0); + ca_addr->SetLabel(addr_s); + wxString s; + switch(size) { + case BITS_8: + s = _("8-bit "); + break; + case BITS_16: + s = _("16-bit "); + break; + case BITS_32: + s = _("32-bit "); + break; + } + switch(fmt) { + case CFVFMT_SD: + s += _("signed decimal"); + break; + case CFVFMT_UD: + s += _("unsigned decimal"); + break; + case CFVFMT_UH: + s += _("unsigned hexadecimal"); + break; + } + ca_fmt->SetLabel(s); + // probably pointless (but inoffensive) to suggest a value + ca_val = list->OnGetItemText(idx, 2); // sugest "New" value + SetValVal(ca_val_tc); + wxDialog *subdlg = GetXRCDialog("CheatAdd"); + if(subdlg->ShowModal() != wxID_OK) + return; + if(ca_val.empty()) { + wxLogError(_("Number cannot be empty")); + return; + } + u32 val = GetValue(ca_val, fmt); + if(isgb) { + long bank, addr; + addr_s.ToLong(&bank, 16); + addr_s.erase(0, 3); + addr_s.ToLong(&addr, 16); + if(addr >= 0xd000) + bank += 0x90; + else + bank = 1; + for(int i = 0; i < (1 << size); i++) { + addr_s.Printf(wxT("%02X%02X%02X%02X"), bank, val & 0xff, + addr & 0xff, addr >> 8); + gbAddGsCheat(addr_s.mb_str(), ca_desc.mb_str()); + val >>= 8; + addr++; + } + } else { + wxString s; + switch(size) { + case BITS_8: + s.Printf(wxT(":%02X"), val); + break; + case BITS_16: + s.Printf(wxT(":%04X"), val); + break; + case BITS_32: + s.Printf(wxT(":%08X"), val); + break; + } + addr_s.append(s); + cheatsAddCheatCode(addr_s.mb_str(), ca_desc.mb_str()); + } + } + + void SetValVal(wxTextCtrl *tc) + { + wxTextValidator *v = wxStaticCast(tc->GetValidator(), wxTextValidator); + switch(fmt) { + case CFVFMT_SD: + v->SetIncludes(val_sigdigits); + break; + case CFVFMT_UD: + v->SetIncludes(val_unsdigits); + break; + case CFVFMT_UH: + v->SetIncludes(val_hexdigits); + break; + } + } + + u32 GetValue(wxString &s, int fmt) + { + long val; + // FIXME: probably ought to throw an error if ToLong + // returns false or val is out of range + s.ToLong(&val, fmt == CFVFMT_UH ? 16 : 10); + if(size != BITS_32) + val &= size == BITS_8 ? 0xff : 0xffff; + return val; + } + + u32 GetValue(int fmt) + { + return GetValue(val_s, fmt); + } + + u32 GetValue() + { + return GetValue(fmt); + } + + s32 SignedValue(wxString &s, int fmt) + { + s32 val = GetValue(s, fmt); + if(fmt == CFVFMT_SD) { + if(size == BITS_8) + val = (s32)(s8)val; + else if(size == BITS_16) + val = (s32)(s16)val; + } + return val; + } + + s32 SignedValue(int fmt) + { + return SignedValue(val_s, fmt); + } + + s32 SignedValue() + { + return SignedValue(fmt); + } + + void FormatValue(s32 val, wxString &s) + { + if(fmt != CFVFMT_SD && size != BITS_32) + val &= size == BITS_8 ? 0xff : 0xffff; + switch(fmt) { + case CFVFMT_SD: + s.Printf(wxT("%d"), val); + break; + case CFVFMT_UD: + s.Printf(wxT("%u"), val); + break; + case CFVFMT_UH: + switch(size) { + case BITS_8: + s.Printf(wxT("%02X"), val); + break; + case BITS_16: + s.Printf(wxT("%04X"), val); + break; + case BITS_32: + s.Printf(wxT("%08X"), val); + break; + } + } + } + + void UpdateView(wxCommandEvent &ev) + { + dlg->TransferDataFromWindow(); + if(ofmt != fmt && !val_s.empty()) { + s32 val = GetValue(ofmt); + switch(fmt) { + case CFVFMT_SD: + switch(size) { + case BITS_8: + val = (s32)(s8)val; + break; + case BITS_16: + val = (s32)(s16)val; + } + val_s.Printf(wxT("%d"), val); + break; + case CFVFMT_UD: + val_s.Printf(wxT("%u"), val); + break; + case CFVFMT_UH: + val_s.Printf(wxT("%x"), val); + break; + } + val_tc->SetValue(val_s); + } + if(ofmt != fmt) + SetValVal(val_tc); + if(list->count8 && osize != size) { + switch(size) { + case BITS_32: + list->SetItemCount(list->count32); + break; + case BITS_16: + list->SetItemCount(list->count16); + break; + case BITS_8: + list->SetItemCount(list->count8); + break; + } + } + if(ofmt != fmt || osize != size) + list->Refresh(); + ofmt = fmt; + osize = size; + } + + void EnableVal(wxCommandEvent &ev) + { + val_tc->Enable(ev.GetId() == XRCID("SpecificValue")); + } + +} cheat_find_handler; + +// clear cheat find dialog between games +void MainFrame::ResetCheatSearch() +{ + CheatFind_t &cfh = cheat_find_handler; + cfh.fmt = cfh.size = cfh.op = cfh.valsrc = 0; + cfh.val_s = wxEmptyString; + cfh.Deselect(); + cfh.list->SetItemCount(0); + cfh.list->count8 = cfh.list->count16 = cfh.list->count32 = 0; + cfh.list->addrs.clear(); + cfh.ca_desc = wxEmptyString; + cheatSearchCleanup(&cheatSearchData); +} + +// onshow handler for above, in the form of an overzealous validator +class CheatFindFill : public wxValidator +{ +public: + CheatFindFill() : wxValidator() {} + CheatFindFill(const CheatFindFill &e) : wxValidator() {} + wxObject *Clone() const { return new CheatFindFill(*this); } + bool TransferFromWindow() { return true; } + bool Validate(wxWindow *p) { return true; } + bool TransferToWindow() { + CheatFind_t &cfh = cheat_find_handler; + GameArea *panel = wxGetApp().frame->GetPanel(); + cfh.isgb = panel->game_type() == IMAGE_GB; + cfh.val_tc->Enable(!cfh.valsrc); + cfh.ofmt = cfh.fmt; + cfh.SetValVal(cfh.val_tc); + return true; + } +}; + +// the implementation of the virtual list ctrl for search results +// requires CheatFind_t to be implemented +wxString CheatListCtrl::OnGetItemText(long item, long column) const +{ + wxString s; + CheatFind_t &cfh = cheat_find_handler; + // allowing GUI to change format after search makes this a little + // more complicated than necessary... + int off = 0; + int size = cfh.size; + if(cap_size > size) { + off = (item & ((1 << (cap_size - size)) - 1)) << size; + item >>= cap_size - size; + } else if(cap_size < size) { + for(int i = 0; i < addrs.size(); i++) { + if(!(addrs[i] & ((1 << size) - 1)) && !item--) { + item = i; + break; + } + } + } + CheatSearchBlock *block = &cheatSearchData.blocks[addrs[item] >> 28]; + off += addrs[item] & 0xfffffff; + + switch(column) { + case 0: // address + if(cfh.isgb) { + int bank = 0; + int addr = block->offset; + if(block->offset == 0xa000) { + bank = off / 0x2000; + addr += off % 0x2000; + } else if(block->offset == 0xd000) { + bank = off / 0x1000; + addr += off % 0x1000; + } else + addr += off; + s.Printf(wxT("%02X:%04X"), bank, addr); + } else + s.Printf(wxT("%08X"), block->offset + off); + break; + case 1: // old + cfh.FormatValue(cheatSearchSignedRead(block->saved, off, size), s); + break; + case 2: // new + cfh.FormatValue(cheatSearchSignedRead(block->data, off, size), s); + break; + } + return s; +} + +// these are the choices for canned colors; their order must match the +// names in the choice control +static const u16 defaultPalettes[][8] = { + { // Standard + 0x7FFF, 0x56B5, 0x318C, 0x0000, 0x7FFF, 0x56B5, 0x318C, 0x0000, + }, + { // Blue Sea + 0x6200, 0x7E10, 0x7C10, 0x5000, 0x6200, 0x7E10, 0x7C10, 0x5000, + }, + { // Dark Night + 0x4008, 0x4000, 0x2000, 0x2008, 0x4008, 0x4000, 0x2000, 0x2008, + }, + { // Green Forest + 0x43F0, 0x03E0, 0x4200, 0x2200, 0x43F0, 0x03E0, 0x4200, 0x2200, + }, + { // Hot Desert + 0x43FF, 0x03FF, 0x221F, 0x021F, 0x43FF, 0x03FF, 0x221F, 0x021F, + }, + { // Pink Dreams + 0x621F, 0x7E1F, 0x7C1F, 0x2010, 0x621F, 0x7E1F, 0x7C1F, 0x2010, + }, + { // Weird Colors + 0x621F, 0x401F, 0x001F, 0x2010, 0x621F, 0x401F, 0x001F, 0x2010, + }, + { // Real GB Colors + 0x1B8E, 0x02C0, 0x0DA0, 0x1140, 0x1B8E, 0x02C0, 0x0DA0, 0x1140, + }, + { // Real 'GB on GBASP' Colors + 0x7BDE, /*0x23F0*/ 0x5778, /*0x5DC0*/ 0x5640, 0x0000, 0x7BDE, /*0x3678*/ 0x529C, /*0x0980*/ 0x2990, 0x0000, + } +}; + +// manage the GB color prefs' canned color selecter +static class GBColorConfig_t : public wxEvtHandler +{ +public: + wxWindow *p; + wxChoice *c; + wxColourPickerCtrl *cp[8]; + int pno; + void ColorSel(wxCommandEvent &ev) + { + if(ev.GetSelection() > 0) { + const u16 *color = defaultPalettes[ev.GetSelection() - 1]; + for(int i = 0; i < 8; i++, color++) + cp[i]->SetColour(wxColor(((*color << 3) & 0xf8), + ((*color >> 2) & 0xf8), + ((*color >> 7) & 0xf8))); + } + } + void ColorReset(wxCommandEvent &ev) + { + const u16 *color = &systemGbPalette[pno * 8]; + for(int i = 0; i < 8; i++, color++) + cp[i]->SetColour(wxColor(((*color << 3) & 0xf8), + ((*color >> 2) & 0xf8), + ((*color >> 7) & 0xf8))); + } + + void ColorButton(wxCommandEvent &ev) + { + c->SetSelection(0); + } +} GBColorConfigHandler[3]; + +// disable controls if a GBA game is not loaded +class GBACtrlEnabler : public wxValidator +{ +public: + GBACtrlEnabler() : wxValidator() {} + GBACtrlEnabler(const GBACtrlEnabler &e) : wxValidator() {} + wxObject *Clone() const { return new GBACtrlEnabler(*this); } + bool TransferFromWindow() { return true; } + bool Validate(wxWindow *p) { return true; } + bool TransferToWindow() { + GetWindow()->Enable(wxGetApp().frame->GetPanel()->game_type() == IMAGE_GBA); + return true; + } +}; + +// manage save game area settings for GBA prefs +static class BatConfig_t : public wxEvtHandler +{ +public: + wxChoice *type, *size; + void ChangeType(wxCommandEvent &ev) + { + int i = ev.GetSelection(); + size->Enable(!i || i == 3); // automatic/flash + } + void Detect(wxCommandEvent &ev) + { + // note: win32 version just pops up a dialog stating what it found + // which is appropriate becauase it was in a menu + // this code sets the controls instead, since there right there + u32 sz = wxGetApp().frame->GetPanel()->game_size(); +#define ch4(a, b, c, d) \ + wxUINT32_SWAP_ON_BE(a + (b << 8) + (c << 16) + (d << 24)) + for(u32 addr = 0; addr < sz - 10; addr += 4) { + switch(*(u32 *)&rom[addr]) { + case ch4('E', 'E', 'P', 'R'): + if(memcmp(&rom[addr + 4], "OM_V", 4)) + break; + // apparently no sensor autodetection + type->SetSelection(1); + size->Disable(); + return; + case ch4('S', 'R', 'A', 'M'): + if(memcmp(&rom[addr + 4], "_V", 2)) + break; + type->SetSelection(2); + size->Disable(); + return; + case ch4('F', 'L', 'A', 'S'): + if(!memcmp(&rom[addr + 4], "H_V", 3)) { + type->SetSelection(3); + size->SetSelection(0); + size->Enable(); + return; + } else if(!memcmp(&rom[addr + 4], "H1M_V", 5)) { + type->SetSelection(3); + size->SetSelection(1); + size->Enable(); + return; + } + break; + } + } + type->SetSelection(5); + size->Disable(); + } +} BatConfigHandler; + +// manage the sound prefs dialog +static class SoundConfig_t : public wxEvtHandler +{ +public: + wxSlider *vol, *bufs; + wxControl *bufinfo; + int lastapi; + wxChoice *dev; + wxControl *umix, *hwacc; + wxArrayString dev_ids; + + void FullVol(wxCommandEvent &ev) + { + vol->SetValue(100); + } + void AdjustFrames(int count) + { + wxString s; + s.Printf(_("%d frames = %.2f ms"), count, (double)count / 60.0 * 1000.0); + bufinfo->SetLabel(s); + } + void AdjustFramesEv(wxCommandEvent &ev) + { + AdjustFrames(bufs->GetValue()); + } + + bool FillDev(int api) + { + dev->Clear(); + dev->Append(_("Default device")); + dev_ids.clear(); + wxArrayString names; + switch(api) { + case AUD_SDL: + break; +#ifndef NO_OAL + case AUD_OPENAL: + if(!GetOALDevices(names, dev_ids)) + return false; + break; +#endif +#ifdef __WXMSW__ + case AUD_DIRECTSOUND: + if(!(GetDSDevices(names, dev_ids))) + return false; + break; +#ifndef NO_XAUDIO2 + case AUD_XAUDIO2: + if(!GetXA2Devices(names, dev_ids)) + return false; + break; +#endif +#endif + } + dev->SetSelection(0); + for(int i = 0; i < names.size(); i++) { + dev->Append(names[i]); + if(api == gopts.audio_api && gopts.audio_dev == dev_ids[i]) + dev->SetSelection(i + 1); + } + umix->Enable(api == AUD_XAUDIO2); + hwacc->Enable(api == AUD_DIRECTSOUND); + lastapi = api; + } + void SetAPI(wxCommandEvent &ev) + { + int api = gopts.audio_api; + wxValidator *v = wxStaticCast(ev.GetEventObject(), wxWindow)->GetValidator(); + v->TransferFromWindow(); + int newapi = gopts.audio_api; + gopts.audio_api = api; + if(newapi == lastapi) + return; + FillDev(newapi); + } +} sound_config_handler; + +// Validator/widget filler for sound device selector & time indicator +class SoundConfigLoad : public wxValidator +{ +public: + SoundConfigLoad() : wxValidator() {} + SoundConfigLoad(const SoundConfigLoad &e) : wxValidator() {} + wxObject *Clone() const { return new SoundConfigLoad(*this); } + bool Validate(wxWindow *p) { return true; } + bool TransferToWindow() { + SoundConfig_t &sch = sound_config_handler; + sch.FillDev(gopts.audio_api); + sch.AdjustFrames(gopts.audio_buffers); + return true; + } + bool TransferFromWindow() { + SoundConfig_t &sch = sound_config_handler; + int devs = sch.dev->GetSelection(); + if(!devs) + gopts.audio_dev = wxEmptyString; + else + gopts.audio_dev = sch.dev_ids[devs - 1]; + return true; + } +}; + +// manage the joypad prefs' per-panel default/clear buttons +static class JoyPadConfig_t : public wxEvtHandler +{ +public: + wxWindow *p; + void JoypadConfigButtons(wxCommandEvent &ev) { + bool clear = ev.GetId() == XRCID("Clear"); + for(int i = 0; i < NUM_KEYS; i++) { + wxJoyKeyTextCtrl *tc = XRCCTRL_D(*p, joynames[i], wxJoyKeyTextCtrl); + if(clear) + tc->SetValue(wxEmptyString); + else { + wxJoyKeyBinding_v a; + if(defkeys[i*2].key) + a.push_back(defkeys[i*2]); + if(defkeys[i*2+1].joy) + a.push_back(defkeys[i*2+1]); + tc->SetValue(wxJoyKeyTextCtrl::ToString(a)); + } + } + + } +} JoyPadConfigHandler[4]; + +#ifndef NO_LINK +// tc validator for IP addresses using SFML for validation instead of wx +class IPHostValidator : public wxValidator +{ + wxString *valp; +public: + IPHostValidator(wxString *v) : wxValidator(), valp(v) {} + IPHostValidator(const IPHostValidator &e) : wxValidator(), valp(e.valp) {} + wxObject *Clone() const { return new IPHostValidator(*this); } + bool Validate(wxWindow *p) { + wxTextCtrl *tc = wxStaticCast(GetWindow(), wxTextCtrl); + if(!tc->IsEnabled()) + return true; + wxString val = tc->GetValue(); + bool isv = true; + if(val.empty()) + isv = false; + else { + sf::IPAddress srv = std::string(val.mb_str()); + isv = srv.IsValid(); + } + if(!isv) + wxMessageBox(_("You must enter a valid host name"), + _("Host name invalid"), wxICON_ERROR|wxOK); + return isv; + } + bool TransferToWindow() { + wxTextCtrl *tc = wxStaticCast(GetWindow(), wxTextCtrl); + tc->SetValue(*valp); + return true; + } + bool TransferFromWindow() { + wxTextCtrl *tc = wxStaticCast(GetWindow(), wxTextCtrl); + *valp = tc->GetValue(); + return true; + } +}; +#endif + +// manage fullscreen mode widget +// technically, it's more than a validator: it modifies the widget as well +class ScreenModeList : public wxValidator +{ +public: + ScreenModeList() : wxValidator() {} + ScreenModeList(const ScreenModeList &e) : wxValidator() {} + wxObject *Clone() const { return new ScreenModeList(*this); } + bool Validate(wxWindow *p) { return true; } + bool TransferToWindow() + { + wxChoice *c = wxStaticCast(GetWindow(), wxChoice); + wxDisplay d(wxDisplay::GetFromWindow(c->GetParent())); + c->Clear(); + int modeno = 0, bestmode = 0; + int bm_bpp = 0; + c->Append(_("Desktop mode")); + // probably ought to just disable this whole control on UNIX/X11 since + // wxDisplay is so broken. + vm = d.GetModes(); + wxString s; + for(int i = 0; i < vm.size(); i++) { + s.Printf(_("%d x %d - %dbpp @ %dHz"), vm[i].w, vm[i].h, vm[i].bpp, vm[i].refresh); + c->Append(s); + if(!modeno && gopts.fs_mode.w == vm[i].w && gopts.fs_mode.h == vm[i].h) { + if(gopts.fs_mode.bpp == vm[i].bpp && gopts.fs_mode.refresh == vm[i].refresh) + modeno = i + 1; + else if(vm[i].bpp == gopts.fs_mode.bpp && + bm_bpp != gopts.fs_mode.bpp) { + bestmode = i + 1; + bm_bpp = vm[i].bpp; + } else if(bm_bpp != gopts.fs_mode.bpp && bm_bpp != 32 && + vm[i].bpp == 32) { + bm_bpp = vm[i].bpp; + bestmode = i + 1; + } else if(bm_bpp != gopts.fs_mode.bpp && bm_bpp < 24 && + vm[i].bpp == 24) { + bm_bpp = vm[i].bpp; + bestmode = i + 1; + } else if(bm_bpp != gopts.fs_mode.bpp && bm_bpp < 24 && + bm_bpp != 16 && vm[i].bpp == 16) { + bm_bpp = vm[i].bpp; + bestmode = i + 1; + } else if(!bm_bpp) { + bm_bpp = vm[i].bpp; + bestmode = i + 1; + } + } + } + if(!modeno && bestmode) + modeno = bestmode; + c->SetSelection(modeno); + return true; + } + bool TransferFromWindow() + { + int bestmode = wxStaticCast(GetWindow(), wxChoice)->GetSelection(); + if(!bestmode) + gopts.fs_mode.h = gopts.fs_mode.w = gopts.fs_mode.bpp = gopts.fs_mode.refresh = 0; + else + gopts.fs_mode = vm[bestmode - 1]; + return true; + } +private: + wxArrayVideoModes vm; +}; + +// enable plugin-related iff filter choice is plugin +class PluginEnabler : public wxValidator +{ +public: + PluginEnabler() : wxValidator() {} + PluginEnabler(const PluginEnabler &e) : wxValidator() {} + wxObject *Clone() const { return new PluginEnabler(*this); } + bool TransferFromWindow() { return true; } + bool Validate(wxWindow *p) { return true; } + bool TransferToWindow() + { + GetWindow()->Enable(gopts.filter == FF_PLUGIN); + return true; + } +}; + +// The same, but as an event handler +static class PluginEnable_t : public wxEvtHandler +{ +public: + wxWindow *lab, *ch; + void ToggleChoice(wxCommandEvent &ev) + { + bool en = ev.GetSelection() == FF_PLUGIN; + lab->Enable(en); + ch->Enable(en); + } +} PluginEnableHandler; + +// fill in plugin list +class PluginListFiller : public PluginEnabler +{ +public: + PluginListFiller(wxDialog *parent, wxControl *lab, wxChoice *ch) : + PluginEnabler(), txt(lab), dlg(parent), plugins(), filtch(ch) {} + PluginListFiller(const PluginListFiller &e) : + PluginEnabler(), txt(e.txt), dlg(e.dlg), plugins(e.plugins), + filtch(e.filtch) {} + wxObject *Clone() const { return new PluginListFiller(*this); } + bool Validate(wxWindow *p) { return true; } + bool TransferToWindow() + { + PluginEnabler::TransferToWindow(); + wxChoice *ch = wxStaticCast(GetWindow(), wxChoice); + ch->Clear(); + ch->Append(_("None")); + plugins.clear(); + const wxString &plpath = wxStandardPaths::Get().GetPluginsDir(); + wxDir::GetAllFiles(plpath, &plugins, wxT("*.rpi")); + for(int i = 0; i < plugins.size(); i++) { + wxDynamicLibrary dl(plugins[i], wxDL_VERBATIM|wxDL_NOW); + RENDPLUG_GetInfo GetInfo; + const RENDER_PLUGIN_INFO *rpi; + if(dl.IsLoaded() && + (GetInfo = (RENDPLUG_GetInfo)dl.GetSymbol(wxT("RenderPluginGetInfo"))) && + // note that in actual kega fusion plugins, rpi->Output is + // unused (as is rpi->Handle) + dl.GetSymbol(wxT("RenderPluginOutput")) && + (rpi = GetInfo()) && + // FIXME: maybe this should be >= RPI_VERISON + (rpi->Flags & 0xff) == RPI_VERSION && + // RPI_565_SUPP is not supported + // although it would be possible + // and it would make Cairo more efficient + (rpi->Flags & (RPI_555_SUPP|RPI_888_SUPP))) { + wxFileName fn(plugins[i]); + wxString s = fn.GetName(); + s += wxT(": "); + s += wxString(rpi->Name, wxConvUTF8, sizeof(rpi->Name)); + fn.MakeRelativeTo(plpath); + plugins[i] = fn.GetFullName(); + ch->Append(s); + if(plugins[i] == gopts.filter_plugin) + ch->SetSelection(i + 1); + } else + plugins.RemoveAt(i--); + } + if(ch->GetCount() == 1) { + // this is probably the only place the user can find out where + // to put the plugins... it depends on where program was + // installed, and of course OS + wxString msg; + msg.Printf(_("No usable rpi plugins found in %s"), plpath.c_str()); + systemScreenMessage(msg); + ch->Hide(); + txt->Hide(); + int cursel = filtch->GetSelection(); + if(cursel == FF_PLUGIN) + cursel = 0; + if(filtch->GetCount() == FF_PLUGIN + 1) { + filtch->Delete(FF_PLUGIN); + // apparently wxgtk loses selection after this, even + // if selection was not FF_PLUGIN + filtch->SetSelection(cursel); + } + } else { + ch->Show(); + txt->Show(); + if(filtch->GetCount() < FF_PLUGIN + 1) + filtch->Append(_("Plugin")); + } + // FIXME: this isn't enough. It only resizes 2nd time around + dlg->Fit(); + return true; + } + bool TransferFromWindow() + { + wxChoice *ch = wxStaticCast(GetWindow(), wxChoice); + if(ch->GetCount() == 1) { + gopts.filter_plugin = wxEmptyString; + // this happens if "Plugin" was selected and the entry was + // subsequently removed + if(ch->GetSelection() < 0) + ch->SetSelection(0); + if(gopts.filter < 0) + gopts.filter = 0; + } else { + int n = ch->GetSelection(); + if(n > 0) + gopts.filter_plugin = plugins[n - 1]; + else { + if(filtch->GetSelection() == FF_PLUGIN) { + wxMessageBox(_("Please select a plugin or a different filter"), + _("Plugin selection error"), wxOK|wxICON_ERROR); + return false; + } + gopts.filter_plugin = wxEmptyString; + } + } + return true; + } +private: + wxDialog *dlg; + wxControl *txt; + wxChoice *filtch; + wxArrayString plugins; +}; + +// this is the cmd table index for the accel tree ctrl +// one of the "benefits" of using TreeItemData is that we have to +// malloc them all, because treectrl destructor will free them all +// that means we can't use e.g. a single static table of len ncmds +class TreeInt : public wxTreeItemData +{ +public: + TreeInt(int i) : wxTreeItemData() { val = i; } + int val; +}; + +// Convert a tree selection ID to a name +// root +// parent +// item +static bool treeid_to_name(int id, wxString &name, wxTreeCtrl *tc, + const wxTreeItemId &parent, int lev = 0) +{ + wxTreeItemIdValue cookie; + for(wxTreeItemId tid = tc->GetFirstChild(parent, cookie); tid.IsOk(); + tid = tc->GetNextChild(parent, cookie)) { + const TreeInt *ti = static_cast(tc->GetItemData(tid)); + if(ti && ti->val == id) { + name = wxString(wxT(' '), 2 * lev) + tc->GetItemText(tid); + return true; + } + if(treeid_to_name(id, name, tc, tid, lev + 1)) { + name = wxString(wxT(' '), 2 * lev) + tc->GetItemText(tid) + wxT('\n') + name; + return true; + } + } + return false; +} + +// for sorting accels by command ID +static bool cmdid_lt(const wxAcceleratorEntry &a, const wxAcceleratorEntry &b) +{ + return a.GetCommand() < b.GetCommand(); +} + +// manage the accel editor dialog +static class AccelConfig_t : public wxEvtHandler +{ +public: + wxTreeCtrl *tc; + wxControlWithItems *lb; + wxAcceleratorEntry_v user_accels, accels; + wxWindow *asb, *remb; + wxKeyTextCtrl *key; + wxControl *curas; + + // since this is not the actual dialog, derived from wxDialog, which is + // the normal way of doing things, do init on the show event instead of + // constructor + void Init(wxShowEvent &ev) + { +#if wxCHECK_VERSION(2,9,0) +#define GetShow IsShown +#endif + ev.Skip(); + if(!ev.GetShow()) + return; + lb->Clear(); + tc->Unselect(); + tc->ExpandAll(); + user_accels = gopts.accels; + key->SetValue(wxT("")); + asb->Enable(false); + remb->Enable(false); + curas->SetLabel(wxT("")); + accels = wxGetApp().frame->get_accels(user_accels); + } + + // on OK, save the accels in gopts + void Set(wxCommandEvent &ev) + { + // opts.cpp assumes that gopts.accels entries with same command ID + // are contiguous, so sort first + std::sort(gopts.accels.begin(), gopts.accels.end(), cmdid_lt); + gopts.accels = user_accels; + wxGetApp().frame->set_global_accels(); + ev.Skip(); + } + + // After selecting item in command list, fill in key list + // and maybe enable asb + void CommandSel(wxTreeEvent &ev) + { + // wxTreeCtrl *tc = wxStaticCast(evt.GetEventObject(), wxTreeCtrl); + // can't use wxStaticCast; wxTreeItemData does not derive from wxObject + const TreeInt *id = static_cast(tc->GetItemData(ev.GetItem())); + if(!id) { + ev.Veto(); + return; + } + if(ev.GetEventType() == wxEVT_COMMAND_TREE_SEL_CHANGING) { + ev.Skip(); + return; + } + lb->Clear(); + remb->Enable(false); + asb->Enable(!key->GetValue().empty()); + int cmd = id->val; + + for(int i = 0; i < accels.size(); i++) + if(accels[i].GetCommand() == cmdtab[cmd].cmd_id) + lb->Append(wxKeyTextCtrl::ToString(accels[i].GetFlags(), + accels[i].GetKeyCode())); + } + + // after selecting a key in key list, enable Remove button + void KeySel(wxCommandEvent &ev) + { + remb->Enable(lb->GetSelection() != wxNOT_FOUND); + } + + // remove selected binding + void Remove(wxCommandEvent &ev) + { + int lsel = lb->GetSelection(); + if(lsel == wxNOT_FOUND) + return; + wxString selstr = lb->GetString(lsel); + int selmod, selkey; + if(!wxKeyTextCtrl::FromString(selstr, selmod, selkey)) + return; // this should never happen + remb->Enable(false); + // if this key is currently in the shortcut field, clear out curas + if(selstr == key->GetValue()) + curas->SetLabel(wxT("")); + lb->Delete(lsel); + // first drop from user accels, if applicable + for(wxAcceleratorEntry_v::iterator i = user_accels.begin(); + i < user_accels.end(); i++) + if(i->GetFlags() == selmod && i->GetKeyCode() == selkey) { + user_accels.erase(i); + break; + } + // if it's a system accel, disable by assigning to NOOP + wxAcceleratorEntry_v &sys_accels = wxGetApp().frame->sys_accels; + for(int i = 0; i < sys_accels.size(); i++) + if(sys_accels[i].GetFlags() == selmod && + sys_accels[i].GetKeyCode() == selkey) { + wxAcceleratorEntry ne(selmod, selkey, XRCID("NOOP")); + user_accels.push_back(ne); + } + // finally, remove from accels instead of recomputing + for(wxAcceleratorEntry_v::iterator i = accels.begin(); + i < accels.end(); i++) + if(i->GetFlags() == selmod && i->GetKeyCode() == selkey) { + accels.erase(i); + break; + } + } + + // wipe out all user bindings + void ResetAll(wxCommandEvent &ev) + { + if(user_accels.empty() || + wxMessageBox(_("This will clear all user-defined accelerators. Are you sure?"), + _("Confirm"), wxYES_NO) != wxYES) + return; + user_accels.clear(); + accels = wxGetApp().frame->sys_accels; + tc->Unselect(); + lb->Clear(); + // rather than recomputing curas, just clear it + key->SetValue(wxT("")); + curas->SetLabel(wxT("")); + } + + // remove old key binding, add new key binding, and update GUI + void Assign(wxCommandEvent &ev) + { + wxTreeItemId csel = tc->GetSelection(); + wxString accel = key->GetValue(); + if(!csel.IsOk() || accel.empty()) + return; + int acmod, ackey; + if(!wxKeyTextCtrl::FromString(accel, acmod, ackey)) + return; // this should never happen + for(int i = 0; i < lb->GetCount(); i++) + if(lb->GetString(i) == accel) + return; // ignore attempts to add twice + lb->Append(accel); + + // first drop from user accels, if applicable + for(wxAcceleratorEntry_v::iterator i = user_accels.begin(); + i < user_accels.end(); i++) + if(i->GetFlags() == acmod && i->GetKeyCode() == ackey) { + user_accels.erase(i); + break; + } + // then assign to this command + const TreeInt *id = static_cast(tc->GetItemData(csel)); + wxAcceleratorEntry ne(acmod, ackey, cmdtab[id->val].cmd_id); + user_accels.push_back(ne); + + // now assigned to this cmd... + wxString lab; + treeid_to_name(id->val, lab, tc, tc->GetRootItem()); + curas->SetLabel(lab); + + // finally, instead of recomputing accels, just append new accel + accels.push_back(ne); + } + + // update curas and maybe enable asb + void CheckKey(wxCommandEvent &ev) + { + wxString nkey = key->GetValue(); + if(nkey.empty()) { + curas->SetLabel(wxT("")); + asb->Enable(false); + return; + } + int acmod, ackey; + if(!wxKeyTextCtrl::FromString(nkey, acmod, ackey)) { + // this should never happen + key->SetValue(wxT("")); + asb->Enable(false); + return; + } + asb->Enable(tc->GetSelection().IsOk()); + int cmd = -1; + for(int i = 0; i < accels.size(); i++) + if(accels[i].GetFlags() == acmod && accels[i].GetKeyCode() == ackey) { + int cmdid = accels[i].GetCommand(); + for(cmd = 0; cmd < ncmds; cmd++) + if(cmdid == cmdtab[cmd].cmd_id) + break; + break; + } + if(cmd < 0 || cmdtab[cmd].cmd_id == XRCID("NOOP")) { + curas->SetLabel(wxT("")); + return; + } + wxString lab; + treeid_to_name(cmd, lab, tc, tc->GetRootItem()); + curas->SetLabel(lab); + } +} accel_config_handler; + +// build initial accel tree control from menu +void MainFrame::add_menu_accels(wxTreeCtrl *tc, wxTreeItemId &parent, wxMenu *menu) +{ + wxMenuItemList mil = menu->GetMenuItems(); + for(wxMenuItemList::iterator mi = mil.begin(); mi != mil.end(); mi++) { + if((*mi)->IsSeparator()) { + tc->AppendItem(parent, wxT("-----")); + } else if((*mi)->IsSubMenu()) { + wxTreeItemId id = tc->AppendItem(parent, (*mi)->GetItemLabelText()); + add_menu_accels(tc, id, (*mi)->GetSubMenu()); + if((*mi)->GetSubMenu() == recent) { + for(int i = wxID_FILE1; i <= wxID_FILE10; i++) { + int cmdid; + for(cmdid = 0; cmdid < ncmds; cmdid++) + if(cmdtab[cmdid].cmd_id == i) + break; + TreeInt *val = new TreeInt(cmdid); + tc->AppendItem(id, cmdtab[cmdid].name, -1, -1, val); + } + } + } else { + int mid = (*mi)->GetId(); + if(mid >= wxID_FILE1 && mid <= wxID_FILE10) + continue; + int cmdid; + for(cmdid = 0; cmdid < ncmds; cmdid++) + if(cmdtab[cmdid].cmd_id == mid) + break; + if(cmdid == ncmds) + continue; // bad menu item; should inform user really + TreeInt *val = new TreeInt(cmdid); + // ugh. There has to be a better way... + // perhaps make XRCID ranges a requirement for load/save st? + // but then if the user overides main menu, that req. is broken.. + wxString txt = (*mi)->GetItemLabelText(); + for(int i = 0; i < 10; i++) + if(*mi == loadst_mi[i] || *mi == savest_mi[i]) { + txt = cmdtab[i].name; + break; + } + tc->AppendItem(parent, txt, -1, -1, val); + } + } +} + +// manage throttle spinctrl/canned setting choice interaction +static class ThrottleCtrl_t : public wxEvtHandler +{ +public: + wxSpinCtrl *thr; + wxChoice *thrsel; + + // set thrsel from thr + void SetThrottleSel(wxSpinEvent &evt) + { + DoSetThrottleSel(thr->GetValue()); + } + + void DoSetThrottleSel(int val) + { + switch(val) { + case 0: + thrsel->SetSelection(1); + break; + case 25: + thrsel->SetSelection(2); + break; + case 50: + thrsel->SetSelection(3); + break; + case 100: + thrsel->SetSelection(4); + break; + case 150: + thrsel->SetSelection(5); + break; + case 200: + thrsel->SetSelection(6); + break; + default: + thrsel->SetSelection(0); + break; + } + } + + // set thr from thrsel + void SetThrottle(wxCommandEvent &evt) + { + switch(thrsel->GetSelection()) { + case 0: // blank; leave it alone + break; + case 1: + thr->SetValue(0); + break; + case 2: + thr->SetValue(25); + break; + case 3: + thr->SetValue(50); + break; + case 4: + thr->SetValue(100); + break; + case 5: + thr->SetValue(150); + break; + case 6: + thr->SetValue(200); + break; + } + } + + // since this is not the actual dialog, derived from wxDialog, which is + // the normal way of doing things, do init on the show event instead of + // constructor + // could've also used a validator, I guess... + void Init(wxShowEvent &ev) + { + ev.Skip(); + DoSetThrottleSel(gopts.throttle); + } +} throttle_ctrl; + +///////////////////////////// + +bool MainFrame::InitMore(void) +{ + // Make sure display panel present and correct type + panel = XRCCTRL(*this, "DisplayArea", GameArea); + if(!panel) { + wxLogError(_("Main display panel not found")); + return false; + } + panel->AdjustSize(false); + + // only the panel does idle events (the emulator loop) + // however, do not enable until end of init, since errors will start + // the idle loop on wxGTK + wxIdleEvent::SetMode(wxIDLE_PROCESS_SPECIFIED); + + // could/should probably take this from xrc as well + // but I don't think xrc supports icon from Windows resource + wxIcon icon = wxXmlResource::Get()->LoadIcon(wxT("MainIcon")); + if(!icon.IsOk()) { + wxLogInfo(_("Main icon not found")); + icon = wxICON(wxvbam); + } + SetIcon(icon); + + // NOOP if no status area + SetStatusText(_("Welcome to wxVBAM!")); + + // Prepare system accel table + for(int i = 0; i < num_def_accels; i++) + sys_accels.push_back(default_accels[i]); + + // If there is a menubar, store all special menuitems +#define XRCITEM_I(id) menubar->FindItem(id, NULL) +#define XRCITEM_D(s) XRCITEM_I(XRCID_D(s)) +#define XRCITEM(s) XRCITEM_D(wxT(s)) + wxMenuBar *menubar = GetMenuBar(); + ctx_menu = NULL; + if(menubar) { +#if 0 // doesn't work in 2.9 at all (causes main menu to malfunction) + // to fix, recursively copy entire menu insted of just copying + // menubar. This means that every saved menu item must also be + // saved twice... A lot of work for a mostly worthless feature. + // If you want the menu, just exit full-screen mode. + // Either that, or add an option to retain the regular + // menubar in full-screen mode + + // create a context menu for fullscreen mode + // FIXME: on gtk port, this gives Gtk-WARNING **: + // gtk_menu_attach_to_widget(): menu already attached to GtkMenuItem + // but it works anyway + // Note: menu default accelerators (e.g. alt-f for file menu) don't + // work with context menu (and can never work, since there is no + // way to pop up a submenu) + // It would probably be better, in the end, to use a collapsed menu + // bar (either Amiga-style press RMB to make appear, or Windows + // collapsed toolbar-style move mouse to within a pixel of top to + // make appear). Not supported in wx without a lot of work, though. + // Maybe this feature should just be dropped; the user would simply + // have to exit fullscreen mode to use the menu. + ctx_menu = new wxMenu(); + for(int i = 0; i < menubar->GetMenuCount(); i++) + ctx_menu->AppendSubMenu(menubar->GetMenu(i), menubar->GetMenuLabel(i)); +#endif + + // save all menu items in the command table + for(int i = 0; i < ncmds; i++) { + wxMenuItem *mi = cmdtab[i].mi = XRCITEM_I(cmdtab[i].cmd_id); + // remove unsupported commands first +#ifdef NO_FFMPEG + if(cmdtab[i].mask_flags & (CMDEN_SREC|CMDEN_NSREC|CMDEN_VREC|CMDEN_NVREC)) { + if(mi) + mi->GetMenu()->Remove(mi); + cmdtab[i].cmd_id = XRCID("NOOP"); + cmdtab[i].mi = NULL; + continue; + } +#endif +#ifndef GBA_LOGGING + if(cmdtab[i].cmd_id == XRCID("Logging")) { + if(mi) + mi->GetMenu()->Remove(mi); + cmdtab[i].cmd_id = XRCID("NOOP"); + cmdtab[i].mi = NULL; + continue; + } +#endif +#ifdef NO_LINK + if(cmdtab[i].cmd_id == XRCID("LinkConfigure") || + cmdtab[i].cmd_id == XRCID("LanLink")) { + if(mi) + mi->GetMenu()->Remove(mi); + cmdtab[i].cmd_id = XRCID("NOOP"); + cmdtab[i].mi = NULL; + continue; + } +#endif + if(mi) { + // wxgtk provides no way to retrieve stock label/accel + // and does not override wxGetStockLabel() + // as of 2.8.12/2.9.1 + // so override with wx's stock label + // at least you still get gtk's stock icon + if(mi->GetItemLabel().empty()) + mi->SetItemLabel(wxGetStockLabel(mi->GetId(), + wxSTOCK_WITH_MNEMONIC|wxSTOCK_WITH_ACCELERATOR)); + + // add accelerator to global accel table + wxAcceleratorEntry *a = mi->GetAccel(); + if(a) { + a->Set(a->GetFlags(), a->GetKeyCode(), cmdtab[i].cmd_id, mi); + // only add it if not already there + for(wxAcceleratorEntry_v::iterator e = sys_accels.begin(); + e < sys_accels.end(); e++) + if(a->GetFlags() == e->GetFlags() && + a->GetKeyCode() == e->GetKeyCode()) { + if(e->GetMenuItem()) { + wxLogInfo(_("Duplicate menu accelerator: %s for %s and %s; keeping first"), + wxKeyTextCtrl::ToString(a->GetFlags(), a->GetKeyCode()).c_str(), + e->GetMenuItem()->GetItemLabelText().c_str(), + mi->GetItemLabelText().c_str()); + delete a; + a = 0; + } else { + if(e->GetCommand() != a->GetCommand()) { + int cmd; + for(cmd = 0; cmd < ncmds; cmd++) + if(cmdtab[cmd].cmd_id == e->GetCommand()) + break; + wxLogInfo(_("Menu accelerator %s for %s overrides default for %s ; keeping menu"), + wxKeyTextCtrl::ToString(a->GetFlags(), a->GetKeyCode()).c_str(), + mi->GetItemLabelText().c_str(), + cmdtab[cmd].cmd); + } + sys_accels.erase(e); + } + break; + } + if(a) + sys_accels.push_back(*a); + else + // strip from label so user isn't confused + DoSetAccel(mi, NULL); + } + + // store checkable items + if(mi->IsCheckable()) { + checkable_mi_t cmi = { cmdtab[i].cmd_id, mi }; + checkable_mi.push_back(cmi); + } + } + } + + // if a recent menu is present, save its location + wxMenuItem *recentmi = XRCITEM("RecentMenu"); + if(recentmi && recentmi->IsSubMenu()) { + recent = recentmi->GetSubMenu(); + gopts.recent->UseMenu(recent); + gopts.recent->AddFilesToMenu(); + } else + recent = NULL; + // if save/load state menu items present, save their locations + for(int i = 0; i < 10; i++) { + wxString n; + n.Printf(wxT("LoadGame%02d"), i + 1); + loadst_mi[i] = XRCITEM_D(n); + n.Printf(wxT("SaveGame%02d"), i + 1); + savest_mi[i] = XRCITEM_D(n); + } + } else { + recent = NULL; + for(int i = 0; i < 10; i++) + loadst_mi[i] = savest_mi[i] = NULL; + } + // just setting to UNLOAD_CMDEN_KEEP is invalid + // so just set individual flags here + cmd_enable = CMDEN_NGDB_ANY | CMDEN_NREC_ANY; + update_state_ts(true); + enable_menus(); + // set pointers for checkable menu items + // and set initial checked status + if(checkable_mi.size()) { +#define add_bcheck(s, f) do { \ + int id = XRCID(s); \ + for(int i = 0; i < checkable_mi.size(); i++) { \ + if(checkable_mi[i].cmd != id) \ + continue; \ + checkable_mi[i].boolopt = &f; \ + checkable_mi[i].mi->Check(f); \ + break; \ + } \ +} while(0) + +#define add_icheck(s, f, m, v) do { \ + int id = XRCID(s); \ + for(int i = 0; i < checkable_mi.size(); i++) { \ + if(checkable_mi[i].cmd != id) \ + continue; \ + checkable_mi[i].intopt = &f; \ + checkable_mi[i].mask = m; \ + checkable_mi[i].val = v; \ + checkable_mi[i].mi->Check((f & m) == v); \ + break; \ + } \ +} while(0) +#define add_icheck1(s, f, m) add_icheck(s, f, m, m) + add_bcheck("RecentFreeze", gopts.recent_freeze); + add_bcheck("Pause", paused); + add_icheck1("SoundChannel1", gopts.sound_en, (1<<0)); + add_icheck1("SoundChannel2", gopts.sound_en, (1<<1)); + add_icheck1("SoundChannel3", gopts.sound_en, (1<<2)); + add_icheck1("SoundChannel4", gopts.sound_en, (1<<3)); + add_icheck1("DirectSoundA", gopts.sound_en, (1<<8)); + add_icheck1("DirectSoundB", gopts.sound_en, (1<<9)); + add_icheck1("VideoLayersBG0", layerSettings, (1<<8)); + add_icheck1("VideoLayersBG1", layerSettings, (1<<9)); + add_icheck1("VideoLayersBG2", layerSettings, (1<<10)); + add_icheck1("VideoLayersBG3", layerSettings, (1<<11)); + add_icheck1("VideoLayersOBJ", layerSettings, (1<<12)); + add_icheck1("VideoLayersWIN0", layerSettings, (1<<13)); + add_icheck1("VideoLayersWIN1", layerSettings, (1<<14)); + add_icheck1("VideoLayersOBJWIN", layerSettings, (1<<15)); + add_bcheck("CheatsAutoSaveLoad", gopts.autoload_cheats); + add_bcheck("CheatsEnable", cheatsEnabled); + add_bcheck("KeepSaves", skipSaveGameBattery); + add_bcheck("KeepCheats", skipSaveGameCheats); + add_bcheck("LoadGameAutoLoad", gopts.autoload_state); + add_icheck1("JoypadAutofireA", autofire, KEYM_A); + add_icheck1("JoypadAutofireB", autofire, KEYM_B); + add_icheck1("JoypadAutofireL", autofire, KEYM_LEFT); + add_icheck1("JoypadAutofireR", autofire, KEYM_RIGHT); + add_bcheck("EmulatorSpeedupToggle", turbo); + } + for(int i = 0; i < checkable_mi.size(); i++) + if(!checkable_mi[i].boolopt && !checkable_mi[i].intopt) { + wxLogError(_("Invalid menu item %s; removing"), + checkable_mi[i].mi->GetItemLabelText().c_str()); + checkable_mi[i].mi->GetMenu()->Remove(checkable_mi[i].mi); + checkable_mi[i].mi = NULL; + } + for(checkable_mi_array_t::iterator it = checkable_mi.end(); + it != checkable_mi.begin(); it--) + if(!it[-1].mi) + checkable_mi.erase(it-1); + + set_global_accels(); + + // preload and verify all resource dialogs + // this will take init time and memory, but catches errors in xrc sooner + // note that the only verification done is to ensure no crashes. It's the + // user's responsibility to ensure that the GUI works as intended after + // modifications + + wxDialog *d = 0; + const wxChar *dname; +#define baddialog() do { \ + wxLogError(_("Unable to load dialog %s from resources"), dname); \ + return false; \ +} while(0) +#define baddialogcv(n) do { \ + wxLogError(_("Unable to load dialog %s (control %s) from resources"), dname, n); \ + return false; \ +} while(0) +#define baddialogc(n) baddialogcv(wxT(n)) +#define LoadXRCDialog(n) do { \ + /* why do I have to manually Fit()? */ \ + /* since I do, always do it for last item so other init happens first */ \ + /* don't forget to Fit() the last dialog! */ \ + if(d != 0) \ + d->Fit(); \ + dname = wxT(n); \ + /* using this instead of LoadDialog() allows non-wxDialog classes that */ \ + /* are derived from wxDialog (like wxPropertyDialog) to work */ \ + d = wxDynamicCast(wxXmlResource::Get()->LoadObject(this, dname, wxEmptyString), \ + wxDialog); \ + if(!d) \ + baddialog(); \ + /* wx-2.9.1 doesn't set parent for propertysheetdialogs for some reason */ \ + /* this will generate a gtk warning but it is necessary for later */ \ + /* retrieval using FindWindow() */ \ + if(!d->GetParent()) \ + d->Reparent(this); \ + \ + mark_recursive(d); \ +} while(0) + +#define vfld(f, t) do { \ + if(!XRCCTRL(*d, f, t)) \ + baddialogc(f); \ +} while(0) +#define getfld(v, f, t) do { \ + if(!(v = XRCCTRL(*d, f, t))) \ + baddialogc(f); \ +} while(0) +#define getfldv(v, f, t) do { \ + if(!(v = XRCCTRL_D(*d, f, t))) \ + baddialogcv(f.c_str()); \ +} while(0) + + //// displayed during run + LoadXRCDialog("GBPrinter"); + // just verify preview window & mag sel present + { + wxPanel *prev; + getfld(prev, "Preview", wxPanel); + if(!wxDynamicCast(prev->GetParent(), wxScrolledWindow)) + baddialogc("Preview"); + vfld("Magnification", wxControlWithItems); + } + + //// File menu + LoadXRCDialog("GBAROMInfo"); + // just verify fields present + wxControl *lab; +#define getlab(n) getfld(lab, n, wxControl) + getlab("Title"); + getlab("GameCode"); + getlab("MakerCode"); + getlab("MakerName"); + getlab("UnitCode"); + getlab("DeviceType"); + getlab("Version"); + getlab("CRC"); + + LoadXRCDialog("GBROMInfo"); + // just verify fields present + getlab("Title"); + getlab("MakerCode"); + getlab("MakerName"); + getlab("UnitCode"); + getlab("DeviceType"); + getlab("Version"); + getlab("CRC"); + getlab("Color"); + getlab("ROMSize"); + getlab("RAMSize"); + getlab("DestCode"); + getlab("LicCode"); + getlab("Checksum"); + + LoadXRCDialog("CodeSelect"); + // just verify list present + vfld("CodeList", wxControlWithItems); + + LoadXRCDialog("ExportSPS"); + // just verify text fields present + vfld("Title", wxTextCtrl); + vfld("Description", wxTextCtrl); + vfld("Notes", wxTextCtrl); + + //// Emulation menu +#ifndef NO_LINK + LoadXRCDialog("NetLink"); +#endif + wxRadioButton *rb; +#define getrbi(n, o, v) do { \ + getfld(rb, n, wxRadioButton); \ + rb->SetValidator(wxBoolIntValidator(&o, v)); \ +} while(0) +#define getrbb(n, o) do { \ + getfld(rb, n, wxRadioButton); \ + rb->SetValidator(wxGenericValidator(&o)); \ +} while(0) +#define getrbbr(n, o) do { \ + getfld(rb, n, wxRadioButton); \ + rb->SetValidator(wxBoolRevValidator(&o)); \ +} while(0) + wxBoolEnValidator *benval; + wxBoolEnHandler *ben; +#define getbe(n, o, cv, t, wt) do { \ + getfld(cv, n, t); \ + cv->SetValidator(wxBoolEnValidator(&o)); \ + benval = wxStaticCast(cv->GetValidator(), wxBoolEnValidator); \ + static wxBoolEnHandler _ben; \ + ben = &_ben; \ + wx##wt##BoolEnHandlerConnect(cv, wxID_ANY, _ben); \ +} while(0) + // brenval & friends are here just to allow yes/no radioboxes in place + // of checkboxes. A lot of work for little benefit. + wxBoolRevEnValidator *brenval; +#define getbre(n, o, cv, t, wt) do { \ + getfld(cv, n, t); \ + cv->SetValidator(wxBoolRevEnValidator(&o)); \ + brenval = wxStaticCast(cv->GetValidator(), wxBoolRevEnValidator); \ + wx##wt##BoolEnHandlerConnect(rb, wxID_ANY, *ben); \ +} while(0) +#define addbe(n) do { \ + ben->controls.push_back(n); \ + benval->controls.push_back(n); \ +} while(0) +#define addrbe(n) do { \ + addbe(n); \ + brenval->controls.push_back(n); \ +} while(0) +#define addber(n, r) do { \ + ben->controls.push_back(n); \ + ben->reverse.push_back(r); \ + benval->controls.push_back(n); \ + benval->reverse.push_back(r); \ +} while(0) +#define addrber(n, r) do { \ + addber(n, r); \ + brenval->controls.push_back(n); \ + brenval->reverse.push_back(r); \ +} while(0) +#define getrbbe(n, o) getbe(n, o, rb, wxRadioButton, RBE) +#define getrbbd(n, o) getbre(n, o, rb, wxRadioButton, RBD) + wxTextCtrl *tc; +#define gettc(n, o) do { \ + getfld(tc, n, wxTextCtrl); \ + tc->SetValidator(wxTextValidator(wxFILTER_NONE, &o)); \ +} while(0) +#ifndef NO_LINK + { + net_link_handler.dlg = d; + getrbbe("Server", lanlink.server); + getrbbd("Client", lanlink.server); + getlab("PlayersLab"); + addrber(lab, false); + getrbi("Link2P", net_link_handler.n_players, 2); + addrber(rb, false); + getrbi("Link3P", net_link_handler.n_players, 3); + addrber(rb, false); + getrbi("Link4P", net_link_handler.n_players, 4); + addrber(rb, false); + getlab("ServerIPLab"); + addrber(lab, true); + gettc("ServerIP", gopts.link_host); + addrber(tc, true); + tc->SetValidator(IPHostValidator(&gopts.link_host)); + getrbbr("SpeedOff", lanlink.speed); + getrbb("SpeedOn", lanlink.speed); + wxWindow *okb = d->FindWindow(wxID_OK); + if(okb) { // may be gone if style guidlines removed it + net_link_handler.okb = wxStaticCast(okb, wxButton); + d->Connect(XRCID("Server"), wxEVT_COMMAND_RADIOBUTTON_SELECTED, + wxCommandEventHandler(NetLink_t::ServerOKButton), + NULL, &net_link_handler); + d->Connect(XRCID("Client"), wxEVT_COMMAND_RADIOBUTTON_SELECTED, + wxCommandEventHandler(NetLink_t::ClientOKButton), + NULL, &net_link_handler); + } + // this should intercept wxID_OK before the dialog handler gets it + d->Connect(wxID_OK, wxEVT_COMMAND_BUTTON_CLICKED, + wxCommandEventHandler(NetLink_t::NetConnect), + NULL, &net_link_handler); + } +#endif + + LoadXRCDialog("CheatList"); + { + cheat_list_handler.dlg = d; + d->SetEscapeId(wxID_OK); + wxCheckedListCtrl *cl; + getfld(cl, "Cheats", wxCheckedListCtrl); + if(!cl->Init()) + baddialogc("Cheats"); + cheat_list_handler.list = cl; + cl->SetValidator(CheatListFill()); + cl->InsertColumn(0, _("Code")); + // can't just set font for whole column; must set in each + // individual item + wxFont of = cl->GetFont(); + // of.SetFamily(wxFONTFAMILY_MODERN); // doesn't work (no font change) + wxFont f(of.GetPointSize(), wxFONTFAMILY_MODERN, of.GetStyle(), + of.GetWeight()); + cheat_list_handler.item0.SetFont(f); + cheat_list_handler.item0.SetColumn(0); + cl->InsertColumn(1, _("Description")); + // too bad I can't just set the size to windowwidth - other cols + // default width is header width, but using following will probably + // make it 80 pixels wide regardless + // cl->SetColumnWidth(1, wxLIST_AUTOSIZE_USEHEADER); + cheat_list_handler.col1minw = cl->GetColumnWidth(1); + // on wxGTK, column 1 seems to inherit column 0's font regardless + // of requested font + cheat_list_handler.item1.SetFont(cl->GetFont()); + cheat_list_handler.item1.SetColumn(1); +#if 0 + // the ideal way to set col 0's width would be to use + // wxLIST_AUTOSIZE after setting value to a sample: + cheat_list_handler.item0.SetText(wxT("00000000 00000000")); + cl->InsertItem(cheat_list_handler.item0); + cl->SetColumnWidth(0, wxLIST_AUTOSIZE); + cl->RemoveItem(0); +#else + // however, the generic listctrl implementation uses the wrong + // font to determine width (window vs. item), and does not + // calculate the margins the same way in calculation vs. actual + // drawing. so calculate manually, using knowledge of underlying + // code. This is highly version-unportable, but better than using + // buggy wx code.. + int w, h; + cl->GetImageList(wxIMAGE_LIST_SMALL)->GetSize(0, w, h); + w += 5; // IMAGE_MARGIN_IN_REPORT_MODE + // following is missing from wxLIST_AUTOSIZE + w += 8; // ??? subtracted from width avail for text + { + int charwidth, charheight; + wxClientDC dc(cl); + // following is item font instead of window font, + // and so is missing from wxLIST_AUTOSIZE + dc.SetFont(f); + dc.GetTextExtent(wxT('M'), &charwidth, &charheight); + w += (8 + 1 + 8) * charwidth; + } + cl->SetColumnWidth(0, w); +#endif + + d->Connect(wxEVT_COMMAND_TOOL_CLICKED, + wxCommandEventHandler(CheatList_t::Tool), + NULL, &cheat_list_handler); + d->Connect(wxEVT_COMMAND_LIST_ITEM_CHECKED, + wxListEventHandler(CheatList_t::Check), + NULL, &cheat_list_handler); + d->Connect(wxEVT_COMMAND_LIST_ITEM_UNCHECKED, + wxListEventHandler(CheatList_t::UnCheck), + NULL, &cheat_list_handler); + d->Connect(wxEVT_COMMAND_LIST_ITEM_ACTIVATED, + wxListEventHandler(CheatList_t::Edit), + NULL, &cheat_list_handler); + } + + LoadXRCDialog("CheatEdit"); + wxChoice *ch; +#define getch(n, o) do { \ + getfld(ch, n, wxChoice); \ + ch->SetValidator(wxGenericValidator(&o)); \ +} while(0) + { + // d->Reparent(cheat_list_handler.dlg); // broken + getch("Type", cheat_list_handler.ce_type); + cheat_list_handler.ce_type_ch = ch; + gettc("Desc", cheat_list_handler.ce_desc); + tc->SetMaxLength(sizeof(cheatsList[0].desc) - 1); + gettc("Codes", cheat_list_handler.ce_codes); + cheat_list_handler.ce_codes_tc = tc; + } + + LoadXRCDialog("CheatCreate"); + { + cheat_find_handler.dlg = d; + d->SetEscapeId(wxID_OK); + CheatListCtrl *list; + getfld(list, "CheatList", CheatListCtrl); + cheat_find_handler.list = list; + list->SetValidator(CheatFindFill()); + list->InsertColumn(0, _("Address")); + list->InsertColumn(1, _("Old Value")); + list->InsertColumn(2, _("New Value")); + getrbi("EQ", cheat_find_handler.op, SEARCH_EQ); + getrbi("NE", cheat_find_handler.op, SEARCH_NE); + getrbi("LT", cheat_find_handler.op, SEARCH_LT); + getrbi("LE", cheat_find_handler.op, SEARCH_LE); + getrbi("GT", cheat_find_handler.op, SEARCH_GT); + getrbi("GE", cheat_find_handler.op, SEARCH_GE); +#define cf_make_update() \ + rb->Connect(wxEVT_COMMAND_RADIOBUTTON_SELECTED, \ + wxCommandEventHandler(CheatFind_t::UpdateView), \ + NULL, &cheat_find_handler) + getrbi("Size8", cheat_find_handler.size, BITS_8); + cf_make_update(); + getrbi("Size16", cheat_find_handler.size, BITS_16); + cf_make_update(); + getrbi("Size32", cheat_find_handler.size, BITS_32); + cf_make_update(); + getrbi("Signed", cheat_find_handler.fmt, CFVFMT_SD); + cf_make_update(); + getrbi("Unsigned", cheat_find_handler.fmt, CFVFMT_UD); + cf_make_update(); + getrbi("Hexadecimal", cheat_find_handler.fmt, CFVFMT_UH); + cf_make_update(); +#define cf_make_valen() \ + rb->Connect(wxEVT_COMMAND_RADIOBUTTON_SELECTED, \ + wxCommandEventHandler(CheatFind_t::EnableVal), \ + NULL, &cheat_find_handler) + getrbi("OldValue", cheat_find_handler.valsrc, 1); + cf_make_valen(); + cheat_find_handler.old_rb = rb; + rb->Disable(); + getrbi("SpecificValue", cheat_find_handler.valsrc, 0); + cf_make_valen(); + cheat_find_handler.val_rb = rb; + gettc("Value", cheat_find_handler.val_s); + cheat_find_handler.val_tc = tc; + wxStaticCast(tc->GetValidator(), wxTextValidator)->SetStyle(wxFILTER_INCLUDE_CHAR_LIST); +#define cf_button(n, f) \ + d->Connect(XRCID(n), wxEVT_COMMAND_BUTTON_CLICKED, \ + wxCommandEventHandler(CheatFind_t::f), \ + NULL, &cheat_find_handler); +#define cf_enbutton(n, v) do { \ + getfld(cheat_find_handler.v, n, wxButton); \ + cheat_find_handler.v->Disable(); \ +} while(0) + cf_button("Search", Search); + cf_button("Update", UpdateVals); + cf_enbutton("Update", update_b); + cf_button("Clear", ResetSearch); + cf_enbutton("Clear", clear_b); + cf_button("AddCheat", AddCheatB); + cf_enbutton("AddCheat", add_b); + d->Connect(wxEVT_COMMAND_LIST_ITEM_ACTIVATED, + wxListEventHandler(CheatFind_t::AddCheatL), + NULL, &cheat_find_handler); + d->Connect(wxEVT_COMMAND_LIST_ITEM_SELECTED, + wxListEventHandler(CheatFind_t::Select), + NULL, &cheat_find_handler); + } + + LoadXRCDialog("CheatAdd"); + { + // d->Reparent(cheat_find_handler.dlg); // broken + gettc("Desc", cheat_find_handler.ca_desc); + tc->SetMaxLength(sizeof(cheatsList[0].desc) - 1); + gettc("Value", cheat_find_handler.ca_val); + cheat_find_handler.ca_val_tc = tc; + // MFC interface used this for cheat list's generic code adder as well, + // and made format selectable in interface. I think the plain + // interface is good enough, even though the format for GB cheats + // is non-obvious. Therefore, the format is now just a read-only + // field. + getlab("Format"); + cheat_find_handler.ca_fmt = lab; + getlab("Address"); + cheat_find_handler.ca_addr = lab; + } + + //// config menu + LoadXRCDialog("GeneralConfig"); + wxCheckBox *cb; +#define getcbb(n, o) do { \ + getfld(cb, n, wxCheckBox); \ + cb->SetValidator(wxGenericValidator(&o)); \ +} while(0) + wxSpinCtrl *sc; +#define getsc(n, o) do { \ + getfld(sc, n, wxSpinCtrl); \ + sc->SetValidator(wxGenericValidator(&o)); \ +} while(0) + { + getcbb("PauseWhenInactive", gopts.defocus_pause); + getcbb("ApplyPatches", gopts.apply_patches); + getrbi("PNG", gopts.cap_format, 0); + getrbi("BMP", gopts.cap_format, 1); + getsc("RewindInterval", gopts.rewind_interval); + getsc("Throttle", gopts.throttle); + throttle_ctrl.thr = sc; + getfld(throttle_ctrl.thrsel, "ThrottleSel", wxChoice); + throttle_ctrl.thr-> + Connect(wxEVT_COMMAND_SPINCTRL_UPDATED, + wxSpinEventHandler(ThrottleCtrl_t::SetThrottleSel), + NULL, &throttle_ctrl); + throttle_ctrl.thrsel-> + Connect(wxEVT_COMMAND_CHOICE_SELECTED, + wxCommandEventHandler(ThrottleCtrl_t::SetThrottle), + NULL, &throttle_ctrl); + d->Connect(wxEVT_SHOW, wxShowEventHandler(ThrottleCtrl_t::Init), + NULL, &throttle_ctrl); + } + +#define getcbbe(n, o) getbe(n, o, cb, wxCheckBox, CB) + wxBoolIntEnValidator *bienval; +#define getbie(n, o, v, cv, t, wt) do { \ + getfld(cv, n, t); \ + cv->SetValidator(wxBoolIntEnValidator(&o, v, v)); \ + bienval = wxStaticCast(cv->GetValidator(), wxBoolIntEnValidator); \ + static wxBoolEnHandler _ben; \ + ben = &_ben; \ + wx##wt##BoolEnHandlerConnect(cv, wxID_ANY, _ben); \ +} while(0) +#define addbie(n) do { \ + ben->controls.push_back(n); \ + bienval->controls.push_back(n); \ +} while(0) +#define addbier(n, r) do { \ + ben->controls.push_back(n); \ + ben->reverse.push_back(r); \ + bienval->controls.push_back(n); \ + bienval->reverse.push_back(r); \ +} while(0) +#define getcbie(n, o, v) getbie(n, o, v, cb, wxCheckBox, CB) + wxFilePickerCtrl *fp; +#define getfp(n, o) do { \ + getfld(fp, n, wxFilePickerCtrl); \ + fp->SetValidator(wxFileDirPickerValidator(&o)); \ +} while(0) + LoadXRCDialog("GameBoyConfig"); + { + /// System and Peripherals + getch("System", gbEmulatorType); + // "Display borders" corresponds to 2 variables, so it is handled + // in command handler. Plus making changes might require resizing + // game area. Validation only here. + vfld("Borders", wxChoice); + getcbbe("Printer", gopts.gbprint); + getcbb("PrintGather", gopts.print_auto_page); + addbe(cb); + getcbb("PrintSnap", gopts.print_screen_cap); + addbe(cb); + /// Speed + // AutoSkip/FrameSkip are 2 controls for 1 value. Needs post-process + // to ensure checkbox not ignored + getcbie("FrameSkipAuto", gopts.gb_frameskip, -1); + getsc("FrameSkip", gopts.gb_frameskip); + addbier(sc, true); + getlab("FrameSkipLab"); + addbier(lab, true); + /// Boot ROM + getcbbe("BootRomEn", gopts.gb_use_bios); + getfp("BootRom", gopts.gb_bios); + addbe(fp); + getlab("BootRomLab"); + addbe(lab); + getcbbe("CBootRomEn", gopts.gbc_use_bios); + getfp("CBootRom", gopts.gbc_bios); + addbe(fp); + getlab("CBootRomLab"); + addbe(lab); + /// Custom Colors + getcbb("Color", gbColorOption); + wxFarRadio *r = 0; + for(int i = 0; i < 3; i++) { + wxString pn; + // NOTE: wx2.9.1 behaves differently for referenced nodes + // than 2.8! Unless there is an actual child node, the ID field + // will not be overwritten. This means that there should be a + // dummy child node (e.g. position=(0,0)). If you get + // "Unable to load dialog GameBoyConfig from resources", this is + // probably the reason. + pn.Printf(wxT("cp%d"), i + 1); + wxWindow *w; + getfldv(w, pn, wxWindow); + GBColorConfigHandler[i].p = w; + GBColorConfigHandler[i].pno = i; + wxFarRadio *cb; +#define d w + getfld(cb, "UsePalette", wxFarRadio); + if(r) + cb->SetGroup(r); + else + r = cb; + cb->SetValidator(wxBoolIntValidator(&gbPaletteOption, i)); + getfld(ch, "ColorSet", wxChoice); + GBColorConfigHandler[i].c = ch; + for(int j = 0; j < 8; j++) { + wxString s; + s.Printf(wxT("Color%d"), j); + wxColourPickerCtrl *cp; + getfldv(cp, s, wxColourPickerCtrl); + GBColorConfigHandler[i].cp[j] = cp; + cp->SetValidator(wxColorValidator(&systemGbPalette[i * 8 + j])); + } + w->Connect(wxEVT_COMMAND_CHOICE_SELECTED, + wxCommandEventHandler(GBColorConfig_t::ColorSel), + NULL, &GBColorConfigHandler[i]); + w->Connect(XRCID("Reset"), wxEVT_COMMAND_BUTTON_CLICKED, + wxCommandEventHandler(GBColorConfig_t::ColorReset), + NULL, &GBColorConfigHandler[i]); + w->Connect(wxID_ANY, wxEVT_COMMAND_COLOURPICKER_CHANGED, + wxCommandEventHandler(GBColorConfig_t::ColorButton), + NULL, &GBColorConfigHandler[i]); +#undef d + } + } + + LoadXRCDialog("GameBoyAdvanceConfig"); + { + /// System and peripherals + getch("SaveType", gopts.save_type); + BatConfigHandler.type = ch; + getch("FlashSize", gopts.flash_size); + BatConfigHandler.size = ch; + d->Connect(XRCID("SaveType"), wxEVT_COMMAND_CHOICE_SELECTED, + wxCommandEventHandler(BatConfig_t::ChangeType), + NULL, &BatConfigHandler); +#define getgbaw(n) do { \ + wxWindow *w = d->FindWindow(XRCID(n)); \ + if(!w) \ + baddialogc(n); \ + w->SetValidator(GBACtrlEnabler()); \ +} while(0) + getgbaw("Detect"); + d->Connect(XRCID("Detect"), wxEVT_COMMAND_BUTTON_CLICKED, + wxCommandEventHandler(BatConfig_t::Detect), + NULL, &BatConfigHandler); + getcbb("RTC", gopts.rtc); + getcbb("AGBPrinter", gopts.agbprint); + + /// Speed + // AutoSkip/FrameSkip are 2 controls for 1 value. Needs post-process + // to ensure checkbox not ignored + getcbie("FrameSkipAuto", gopts.gba_frameskip, -1); + getsc("FrameSkip", gopts.gba_frameskip); + addbier(sc, true); + getlab("FrameSkipLab"); + addbier(lab, true); + + /// Boot ROM + getcbbe("BootRomEn", gopts.gb_use_bios); + getfp("BootRom", gopts.gb_bios); + addbe(fp); + getlab("BootRomLab"); + addbe(lab); + getcbb("SkipIntro", gopts.skip_intro); + addbe(cb); + // doesn't work right now + cb->Hide(); + + /// Game Overrides + getgbaw("GameSettings"); + // the rest must be filled in by command handler; just validate + vfld("Comment", wxTextCtrl); + vfld("OvRTC", wxChoice); + vfld("OvSaveType", wxChoice); + vfld("OvFlashSize", wxChoice); + vfld("OvMirroring", wxChoice); + } + + LoadXRCDialog("DisplayConfig"); + { + /// On-Screen Display + getch("SpeedIndicator", gopts.osd_speed); + getcbb("NoStatusMsg", gopts.no_osd_status); + getcbb("Transparent", gopts.osd_transparent); + + /// Zoom + // this was a choice, but I'd rather not have to make an off-by-one + // validator just for this, and spinctrl is good enough. + getsc("DefaultScale", gopts.video_scale); + getcbb("RetainAspect", gopts.retain_aspect); + getsc("MaxScale", gopts.max_scale); + // fs modes should be filled in at popup time + // since they may change based on what screen is current + vfld("FullscreenMode", wxChoice); + getcbb("Fullscreen", gopts.fullscreen); + + /// Advanced + getrbi("OutputSimple", gopts.render_method, RND_SIMPLE); + getrbi("OutputOpenGL", gopts.render_method, RND_OPENGL); +#ifdef NO_OGL + rb->Hide(); +#endif + getrbi("OutputCairo", gopts.render_method, RND_CAIRO); +#ifdef NO_CAIRO + rb->Hide(); +#endif + getrbi("OutputDirect3D", gopts.render_method, RND_DIRECT3D); +#if !defined(__WXMSW__) || defined(NO_D3D) || 1 // not implemented + rb->Hide(); +#endif + getcbb("Bilinear", gopts.bilinear); + getcbb("VSync", gopts.vsync); + // FIXME: make cb disabled when not GL or d3d +#define getcbi(n, o, v) do { \ + getfld(cb, n, wxCheckBox); \ + cb->SetValidator(wxBoolIntValidator(&o, v)); \ +} while(0) + int mthr = wxThread::GetCPUCount(); + if(mthr > 8) + mthr = 8; + if(mthr < 0) + mthr = 2; + getcbi("Multithread", gopts.max_threads, mthr); + if(mthr <= 1) + cb->Hide(); +#ifdef MMX + getcbb("MMX", cpu_mmx); +#else + getfld(cb, "MMX", wxCheckBox); + cb->Hide(); +#endif + getch("Filter", gopts.filter); + // these two are filled and/or hidden at dialog load time + wxControl *pll; + wxChoice *pl; + getfld(pll, "PluginLab", wxControl); + getfld(pl, "Plugin", wxChoice); + pll->SetValidator(PluginEnabler()); + pl->SetValidator(PluginListFiller(d, pll, ch)); + PluginEnableHandler.lab = pll; + PluginEnableHandler.ch = pl; + ch->Connect(wxEVT_COMMAND_CHOICE_SELECTED, + wxCommandEventHandler(PluginEnable_t::ToggleChoice), + NULL, &PluginEnableHandler); + getch("IFB", gopts.ifb); + } + + LoadXRCDialog("SoundConfig"); + wxSlider *sl; +#define getsl(n, o) do { \ + getfld(sl, n, wxSlider); \ + sl->SetValidator(wxGenericValidator(&o)); \ +} while(0) + { + /// Basic + getsl("Volume", gopts.sound_vol); + sound_config_handler.vol = sl; + d->Connect(XRCID("Volume100"), wxEVT_COMMAND_BUTTON_CLICKED, + wxCommandEventHandler(SoundConfig_t::FullVol), + NULL, &sound_config_handler); + getch("Rate", gopts.sound_qual); + + /// Advanced +#define audapi_rb(n, v) do {\ + getrbi(n, gopts.audio_api, v); \ + rb->Connect(wxEVT_COMMAND_RADIOBUTTON_SELECTED, \ + wxCommandEventHandler(SoundConfig_t::SetAPI), \ + NULL, &sound_config_handler); \ +} while(0) + audapi_rb("SDL", AUD_SDL); + audapi_rb("OpenAL", AUD_OPENAL); +#ifdef NO_OAL + rb->Hide(); +#endif + audapi_rb("DirectSound", AUD_DIRECTSOUND); +#ifndef __WXMSW__ + rb->Hide(); +#endif + audapi_rb("XAudio2", AUD_XAUDIO2); +#if !defined(__WXMSW__) || defined(NO_XAUDIO2) + rb->Hide(); +#endif + getfld(sound_config_handler.dev, "Device", wxChoice); + sound_config_handler.dev->SetValidator(SoundConfigLoad()); + getcbb("Upmix", gopts.upmix); + sound_config_handler.umix = cb; +#if !defined(__WXMSW__) || defined(NO_XAUDIO2) + cb->Hide(); +#endif + getcbb("HWAccel", gopts.dsound_hw_accel); + sound_config_handler.hwacc = cb; +#ifndef __WXMSW__ + cb->Hide(); +#endif + getcbb("SyncGameAudio", synchronize); + getsl("Buffers", gopts.audio_buffers); + sound_config_handler.bufs = sl; + getlab("BuffersInfo"); + sound_config_handler.bufinfo = lab; + sl->Connect(wxEVT_SCROLL_CHANGED, + wxCommandEventHandler(SoundConfig_t::AdjustFramesEv), + NULL, &sound_config_handler); + sl->Connect(wxEVT_SCROLL_THUMBTRACK, + wxCommandEventHandler(SoundConfig_t::AdjustFramesEv), + NULL, &sound_config_handler); + sound_config_handler.AdjustFrames(10); + + /// Game Boy + getcbb("GBDeclicking", gopts.gb_declick); + getcbbe("GBEnhanceSound", gb_effects_config.enabled); + wxPanel *p; + getfld(p, "GBEnhanceSoundDep", wxPanel); + addbe(p); + getcbb("GBSurround", gb_effects_config.surround); + getsl("GBEcho", gopts.gb_echo); + getsl("GBStereo", gopts.gb_stereo); + + /// Game Boy Advance + getcbb("GBASoundInterpolation", soundInterpolation); + getsl("GBASoundFiltering", gopts.gba_sound_filter); + } + + wxDirPickerCtrl *dp; +#define getdp(n, o) do { \ + getfld(dp, n, wxDirPickerCtrl); \ + dp->SetValidator(wxFileDirPickerValidator(&o)); \ +} while(0) + LoadXRCDialog("DirectoriesConfig"); + { + getdp("GBARoms", gopts.gba_rom_dir); + getdp("GBRoms", gopts.gb_rom_dir); + getdp("BatSaves", gopts.battery_dir); + getdp("StateSaves", gopts.state_dir); + getdp("Screenshots", gopts.scrshot_dir); + getdp("Recordings", gopts.recording_dir); + } + + LoadXRCDialog("JoypadConfig"); + wxFarRadio *r = 0; + for(int i = 0; i < 4; i++) { + wxString pn; + // NOTE: wx2.9.1 behaves differently for referenced nodes + // than 2.8! Unless there is an actual child node, the ID field + // will not be overwritten. This means that there should be a + // dummy child node (e.g. position=(0,0)). If you get + // "Unable to load dialog JoypadConfig from resources", this is + // probably the reason. + pn.Printf(wxT("joy%d"), i + 1); + wxWindow *w; + getfldv(w, pn, wxWindow); +#define d w + wxFarRadio *cb; + getfld(cb, "DefaultConfig", wxFarRadio); + if(r) + cb->SetGroup(r); + else + r = cb; + cb->SetValidator(wxBoolIntValidator(&gopts.default_stick, i + 1)); + wxWindow *prev = NULL, *prevp = NULL; + for(int j = 0; j < NUM_KEYS; j++) { + wxJoyKeyTextCtrl *tc = XRCCTRL_D(*w, joynames[j], wxJoyKeyTextCtrl); + if(!tc) + baddialogcv(joynames[j]); + wxWindow *p = tc->GetParent(); + if(p == prevp) + tc->MoveAfterInTabOrder(prev); + prev = tc; + prevp = p; + tc->SetValidator(wxJoyKeyValidator(&gopts.joykey_bindings[i][j])); + } + JoyPadConfigHandler[i].p = w; + w->Connect(XRCID("Defaults"), wxEVT_COMMAND_BUTTON_CLICKED, + wxCommandEventHandler(JoyPadConfig_t::JoypadConfigButtons), + NULL, &JoyPadConfigHandler[i]); + w->Connect(XRCID("Clear"), wxEVT_COMMAND_BUTTON_CLICKED, + wxCommandEventHandler(JoyPadConfig_t::JoypadConfigButtons), + NULL, &JoyPadConfigHandler[i]); +#undef d + } + +#ifndef NO_LINK + LoadXRCDialog("LinkConfig"); + { + getcbbe("Joybus", gba_joybus_enabled); + getlab("JoybusHostLab"); + addbe(lab); + gettc("JoybusHost", gopts.joybus_host); + tc->SetValidator(IPHostValidator(&gopts.joybus_host)); + addbe(tc); + getcbbe("Link", gba_link_enabled); + getcbb("RFU", rfu_enabled); + addbe(cb); + getlab("LinkTimeoutLab"); + addbe(lab); + getsc("LinkTimeout", linktimeout); + addbe(sc); + } +#endif + + LoadXRCDialog("AccelConfig"); + { + wxTreeCtrl *tc; + getfld(tc, "Commands", wxTreeCtrl); + accel_config_handler.tc = tc; + wxControlWithItems *lb; + getfld(lb, "Current", wxControlWithItems); + accel_config_handler.lb = lb; + getfld(accel_config_handler.asb, "Assign", wxButton); + getfld(accel_config_handler.remb, "Remove", wxButton); + getfld(accel_config_handler.key, "Shortcut", wxKeyTextCtrl); + getfld(accel_config_handler.curas, "AlreadyThere", wxControl); + accel_config_handler.key->MoveBeforeInTabOrder(accel_config_handler.asb); + accel_config_handler.key->SetMultikey(0); + accel_config_handler.key->SetClearable(false); + wxTreeItemId rid = tc->AddRoot(wxT("root")); + if(menubar) { + wxTreeItemId mid = tc->AppendItem(rid, _("Menu commands")); + for(int i = 0; i < menubar->GetMenuCount(); i++) { +#if wxCHECK_VERSION(2,8,8) + wxTreeItemId id = tc->AppendItem(mid, menubar->GetMenuLabelText(i)); +#else + // 2.8.4 has no equivalent for GetMenuLabelText() + wxString txt = menubar->GetMenuLabel(i); + txt.Replace(wxT("&"), wxT("")); + wxTreeItemId id = tc->AppendItem(mid, txt); +#endif + add_menu_accels(tc, id, menubar->GetMenu(i)); + } + } + wxTreeItemId oid; + int noop_id = XRCID("NOOP"); + for(int i = 0; i < ncmds; i++) { + if(cmdtab[i].mi || (recent && cmdtab[i].cmd_id >= wxID_FILE1 && + cmdtab[i].cmd_id <= wxID_FILE10) || + cmdtab[i].cmd_id == noop_id) + continue; + if(!oid.IsOk()) + oid = tc->AppendItem(rid, _("Other commands")); + TreeInt *val = new TreeInt(i); + tc->AppendItem(oid, cmdtab[i].name, -1, -1, val); + } + tc->ExpandAll(); + // FIXME: make this actually show the entire line w/o scrolling + // BestSize cuts off on rhs; MaxSize is completely invalid + wxSize sz = tc->GetBestSize(); + if(sz.GetHeight() > 200) + sz.SetHeight(200); + tc->SetSize(sz); + sz.SetWidth(-1); // maybe allow it to become bigger + tc->SetSizeHints(sz, sz); + int w, h; + lb->GetTextExtent(wxT("CTRL-ALT-SHIFT-ENTER"), &w, &h); + sz.Set(w, h); + lb->SetMinSize(sz); + sz.Set(0, 0); + wxControl *curas = accel_config_handler.curas; + for(int i = 0; i < ncmds; i++) { + wxString labs; + treeid_to_name(i, labs, tc, tc->GetRootItem()); + curas->GetTextExtent(labs, &w, &h); + if(w > sz.GetWidth()) + sz.SetWidth(w); + if(h > sz.GetHeight()) + sz.SetHeight(h); + } + curas->SetSize(sz); + curas->SetSizeHints(sz); + tc->Connect(wxEVT_COMMAND_TREE_SEL_CHANGING, + wxTreeEventHandler(AccelConfig_t::CommandSel), + NULL, &accel_config_handler); + tc->Connect(wxEVT_COMMAND_TREE_SEL_CHANGED, + wxTreeEventHandler(AccelConfig_t::CommandSel), + NULL, &accel_config_handler); + d->Connect(wxEVT_SHOW, wxShowEventHandler(AccelConfig_t::Init), + NULL, &accel_config_handler); + d->Connect(wxID_OK, wxEVT_COMMAND_BUTTON_CLICKED, + wxCommandEventHandler(AccelConfig_t::Set), + NULL, &accel_config_handler); + d->Connect(XRCID("Assign"), wxEVT_COMMAND_BUTTON_CLICKED, + wxCommandEventHandler(AccelConfig_t::Assign), + NULL, &accel_config_handler); + d->Connect(XRCID("Remove"), wxEVT_COMMAND_BUTTON_CLICKED, + wxCommandEventHandler(AccelConfig_t::Remove), + NULL, &accel_config_handler); + d->Connect(XRCID("ResetAll"), wxEVT_COMMAND_BUTTON_CLICKED, + wxCommandEventHandler(AccelConfig_t::ResetAll), + NULL, &accel_config_handler); + lb->Connect(wxEVT_COMMAND_LISTBOX_SELECTED, + wxCommandEventHandler(AccelConfig_t::KeySel), + NULL, &accel_config_handler); + d->Connect(XRCID("Shortcut"), wxEVT_COMMAND_TEXT_UPDATED, + wxCommandEventHandler(AccelConfig_t::CheckKey), + NULL, &accel_config_handler); + } + + d->Fit(); + + //// Debug menu + // actually, the viewers can be instantiated multiple times. + // since they're for debugging, it's probably OK to just detect errors + // at popup time. + // The only one that can only be popped up once is logging, so allocate + // and check it already. + logdlg = new LogDialog; + + // activate OnDropFile event handler +#if !defined(__WXGTK__) || wxCHECK_VERSION(2,8,10) + // may not actually do anything, but verfied to work w/ Linux/Nautilus + DragAcceptFiles(true); +#endif + + // delayed fullscreen + if(wxGetApp().pending_fullscreen || gopts.fullscreen) + panel->ShowFullScreen(true); + +#ifndef NO_LINK + if(gba_joybus_enabled) { + bool isv = !gopts.joybus_host.empty(); + if(isv) { + joybusHostAddr = std::string(gopts.joybus_host.mb_str()); + isv = joybusHostAddr.IsValid(); + } + if(!isv) { + wxLogError(_("JoyBus host invalid; disabling")); + gba_joybus_enabled = false; + } else + JoyBusConnect(); + } + if(gba_link_enabled) + if((did_link_init = InitLink())) + cmd_enable |= CMDEN_LINK_ANY; + +#endif + panel->SetFrameTitle(); + + // All OK; activate idle loop + panel->SetExtraStyle(panel->GetExtraStyle() | wxWS_EX_PROCESS_IDLE); + + return true; +} + + +wxAcceleratorEntry_v MainFrame::get_accels(wxAcceleratorEntry_v user_accels) +{ + // set global accelerators + // first system + wxAcceleratorEntry_v accels = sys_accels; + // then user overrides + // silently keep only last defined binding + // same horribly inefficent O(n*m) search for duplicates as above.. + for(int i = 0; i < user_accels.size(); i++) { + const wxAcceleratorEntry &ae = user_accels[i]; + for(wxAcceleratorEntry_v::iterator e = accels.begin(); e < accels.end(); e++) + if(ae.GetFlags() == e->GetFlags() && + ae.GetKeyCode() == e->GetKeyCode()) { + accels.erase(e); + break; + } + accels.push_back(ae); + } + return accels; +} + +void MainFrame::set_global_accels() +{ + wxAcceleratorEntry_v accels = get_accels(gopts.accels); + + // this is needed on Wine/win32 to support accels for close & quit + wxGetApp().accels = accels; + + // Update menus; this probably takes the longest + // as a side effect, any system-defined accels that weren't already in + // the menus will be added now + + // first, zero out menu item on all accels + for(int i = 0; i < accels.size(); i++) + accels[i].Set(accels[i].GetFlags(), accels[i].GetKeyCode(), + accels[i].GetCommand()); + + // yet another O(n*m) loop. I really ought to sort the accel arrays + for(int i = 0; i < ncmds; i++) { + wxMenuItem *mi = cmdtab[i].mi; + if(!mi) + continue; + // only *last* accelerator is made visible in menu + // and is flagged as such by setting menu item in accel + // the last is chosen so menu overrides non-menu and user overrides + // system + int cmd = cmdtab[i].cmd_id; + int last_accel = -1; + for(int j = 0; j < accels.size(); j++) + if(cmd == accels[j].GetCommand()) + last_accel = j; + if(last_accel >= 0) { + DoSetAccel(mi, &accels[last_accel]); + accels[last_accel].Set(accels[last_accel].GetFlags(), + accels[last_accel].GetKeyCode(), + accels[last_accel].GetCommand(), mi); + } else + // clear out user-cleared menu items + DoSetAccel(mi, NULL); + } + // Finally, install a global accelerator table for any non-menu accels + int len = 0; + for(int i = 0; i < accels.size(); i++) + if(!accels[i].GetMenuItem()) + len++; + if(len) { + wxAcceleratorEntry tab[len]; + for(int i = 0, j = 0; i < accels.size(); i++) + if(!accels[i].GetMenuItem()) + tab[j++] = accels[i]; + wxAcceleratorTable atab(len, tab); + // set the table on the panel, where focus usually is + // otherwise accelerators are lost sometimes + panel->SetAcceleratorTable(atab); + } else + panel->SetAcceleratorTable(wxNullAcceleratorTable); + + // save recent accels + for(int i = 0; i < 10; i++) + recent_accel[i] = wxAcceleratorEntry(); + for(int i = 0; i < accels.size(); i++) + if(accels[i].GetCommand() >= wxID_FILE1 && + accels[i].GetCommand() <= wxID_FILE10) + recent_accel[accels[i].GetCommand() - wxID_FILE1] = accels[i]; + SetRecentAccels(); +} diff --git a/src/wx/ioregs.h b/src/wx/ioregs.h new file mode 100644 index 0000000..1ea3097 --- /dev/null +++ b/src/wx/ioregs.h @@ -0,0 +1,2072 @@ +// this is essentially a copy of ../win32/IOViewRegisters.h using translatable +// strings and more consts +struct IOData { + u16 *address; + u16 offset; + const wxChar *name; + u16 write; + const wxChar *bits[16]; +}; + +/* const */ IOData ioregs[] = { // not const so tranlation can be done once + { + &DISPCNT, 0, wxTRANSLATE("0x4000000-DISPCNT"), 0xFFF7, + { + wxT(""), + wxT(""), + wxTRANSLATE("BG Mode (3 bits)"), + wxTRANSLATE("CGB Mode"), + wxTRANSLATE("Display Frame"), + wxTRANSLATE("H-Blank Interval OBJ processing"), + wxTRANSLATE("OBJ Character mapping"), + wxTRANSLATE("Forced blank"), + wxTRANSLATE("BG0"), + wxTRANSLATE("BG1"), + wxTRANSLATE("BG2"), + wxTRANSLATE("BG3"), + wxTRANSLATE("OBJ"), + wxTRANSLATE("WIN0"), + wxTRANSLATE("WIN1"), + wxTRANSLATE("OBJWIN") + } + }, + { + &DISPSTAT, 4, wxTRANSLATE("0x4000004-DISPSTAT"), 0xFF38, + { + wxTRANSLATE("V-Blank Status"), + wxTRANSLATE("H-Blank Status"), + wxTRANSLATE("VCOUNT Evaluation"), + wxTRANSLATE("V-Blank Interrupt Enable"), + wxTRANSLATE("H-Blank Interrupt Enable"), + wxTRANSLATE("VCOUNT Match Interrupt Enable"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("VCOUNT setting (8 bits)") + } + }, + { + &VCOUNT, 6, wxTRANSLATE("0x4000006-VCOUNT"), 0x0000, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("VCOUNT (8 bits)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT("") + } + }, + { + &BG0CNT, 8, wxTRANSLATE("0x4000008-BG0CNT"), 0xDFCF, + { + wxT(""), + wxTRANSLATE("Priority (2 bits)"), + wxT(""), + wxTRANSLATE("Char base (2 bits)"), + wxT(""), + wxT(""), + wxTRANSLATE("Mosaic"), + wxTRANSLATE("16/256 colors"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Screen Base Block (5 bits)"), + wxT(""), + wxT(""), + wxTRANSLATE("Size (2 bits)") + } + }, + { + &BG1CNT, 0xA, wxTRANSLATE("0x400000A-BG1CNT"), 0xDFCF, + { + wxT(""), + wxTRANSLATE("Priority (2 bits)"), + wxT(""), + wxTRANSLATE("Char base (2 bits)"), + wxT(""), + wxT(""), + wxTRANSLATE("Mosaic"), + wxTRANSLATE("16/256 colors"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Screen Base Block (5 bits)"), + wxT(""), + wxT(""), + wxTRANSLATE("Size (2 bits)") + } + }, + { + &BG2CNT, 0xC, wxTRANSLATE("0x400000C-BG2CNT"), 0xFFCF, + { + wxT(""), + wxTRANSLATE("Priority (2 bits)"), + wxT(""), + wxTRANSLATE("Char base (2 bits)"), + wxT(""), + wxT(""), + wxTRANSLATE("Mosaic"), + wxTRANSLATE("16/256 colors"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Screen Base Block (5 bits)"), + wxTRANSLATE("Area Overflow"), + wxT(""), + wxTRANSLATE("Size (2 bits)") + } + }, + { + &BG3CNT, 0xE, wxTRANSLATE("0x400000E-BG3CNT"), 0xFFCF, + { + wxT(""), + wxTRANSLATE("Priority (2 bits)"), + wxT(""), + wxTRANSLATE("Char base (2 bits)"), + wxT(""), + wxT(""), + wxTRANSLATE("Mosaic"), + wxTRANSLATE("16/256 colors"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Screen Base Block (5 bits)"), + wxTRANSLATE("Area Overflow"), + wxT(""), + wxTRANSLATE("Size (2 bits)") + } + }, + { + &BG0HOFS, 0x10, wxTRANSLATE("0x4000010-BG0HOFS"), 0x01FF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Horizontal Offset (9 bits, W)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT("") + } + }, + { + &BG0VOFS, 0x12, wxTRANSLATE("0x4000012-BG0VOFS"), 0x01FF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Vertical Offset (9 bits, W)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT("") + } + }, + { + &BG1HOFS, 0x14, wxTRANSLATE("0x4000014-BG1HOFS"), 0x01FF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Horizontal Offset (9 bits, W)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT("") + } + }, + { + &BG1VOFS, 0x16, wxTRANSLATE("0x4000016-BG1VOFS"), 0x01FF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Vertical Offset (9 bits, W)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT("") + } + }, + { + &BG2HOFS, 0x18, wxTRANSLATE("0x4000018-BG2HOFS"), 0x01FF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Horizontal Offset (9 bits, W)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT("") + } + }, + { + &BG2VOFS, 0x1A, wxTRANSLATE("0x400001A-BG2VOFS"), 0x01FF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Vertical Offset (9 bits, W)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT("") + } + }, + { + &BG3HOFS, 0x1C, wxTRANSLATE("0x400001C-BG3HOFS"), 0x01FF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Horizontal Offset (9 bits,W)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT("") + } + }, + { + &BG3VOFS, 0x1E, wxTRANSLATE("0x400001E-BG3VOFS"), 0x01FF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Vertical Offset (9 bits,W)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT("") + } + }, + { + &BG2PA, 0x20, wxTRANSLATE("0x4000020-BG2PA"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("dx (16 bits,W)") + } + }, + { + &BG2PB, 0x22, wxTRANSLATE("0x4000022-BG2PB"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("dmx (16 bits,W)") + } + }, + { + &BG2PC, 0x24, wxTRANSLATE("0x4000024-BG2PC"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("dy (16 bits,W)") + } + }, + { + &BG2PD, 0x26, wxTRANSLATE("0x4000026-BG2PD"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("dmy (16 bits,W)") + } + }, + { + &BG2X_L, 0x28, wxTRANSLATE("0x4000028-BG2X_L"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("X low bits (16 bits,W)") + } + }, + { + &BG2X_H, 0x2A, wxTRANSLATE("0x400002A-BG2X_H"), 0x0FFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("X high bits (12 bits,W)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + } + }, + { + &BG2Y_L, 0x2C, wxTRANSLATE("0x400002C-BG2Y_L"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Y low bits (16 bits,W)") + } + }, + { + &BG2Y_H, 0x2E, wxTRANSLATE("0x400002E-BG2Y_H"), 0x0FFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Y hight bits (12 bits,W)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + } + }, + { + &BG3PA, 0x30, wxTRANSLATE("0x4000030-BG3PA"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("dx (16 bits,W)") + } + }, + { + &BG3PB, 0x32, wxTRANSLATE("0x4000032-BG3PB"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("dmx (16 bits,W)") + } + }, + { + &BG3PC, 0x34, wxTRANSLATE("0x4000034-BG3PC"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("dy (16 bits,W)") + } + }, + { + &BG3PD, 0x36, wxTRANSLATE("0x4000036-BG3PD"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("dmy (16 bits,W)") + } + }, + { + &BG3X_L, 0x38, wxTRANSLATE("0x4000038-BG3X_L"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("X low bits (16 bits,W)") + } + }, + { + &BG3X_H, 0x3A, wxTRANSLATE("0x400003A-BG3X_H"), 0x0FFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("X hight bits (12 bits,W)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + } + }, + { + &BG3Y_L, 0x3C, wxTRANSLATE("0x400003C-BG3Y_L"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Y low bits (16 bits,W)") + } + }, + { + &BG3Y_H, 0x3E, wxTRANSLATE("0x400003E-BG3Y_H"), 0x0FFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Y hight bits (12 bits,W)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + } + }, + { + &WIN0H, 0x40, wxTRANSLATE("0x4000040-WIN0H"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Win 0 lower-right X (8 bits,W)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Win 0 upper-left X (8 bits,W)"), + } + }, + { + &WIN1H, 0x42, wxTRANSLATE("0x4000042-WIN1H"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Win 1 lower-right X (8 bits,W)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Win 1 upper-left (8 bits,W)"), + } + }, + { + &WIN0V, 0x44, wxTRANSLATE("0x4000044-WIN0V"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Win 0 lower-right Y (8 bits,W)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Win 0 upper-left Y (8 bits,W)"), + } + }, + { + &WIN1V, 0x46, wxTRANSLATE("0x4000046-WIN1V"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Win 1 lower-right Y (8 bits,W)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Win 1 upper-left Y (8 bits,W)"), + } + }, + { + &WININ, 0x48, wxTRANSLATE("0x4000048-WININ"), 0x3F3F, + { + wxTRANSLATE("WIN0 BG0"), + wxTRANSLATE("WIN0 BG1"), + wxTRANSLATE("WIN0 BG2"), + wxTRANSLATE("WIN0 BG3"), + wxTRANSLATE("WIN0 OBJ"), + wxTRANSLATE("WIN0 Special FX"), + wxT(""), + wxT(""), + wxTRANSLATE("WIN1 BG0"), + wxTRANSLATE("WIN1 BG1"), + wxTRANSLATE("WIN1 BG2"), + wxTRANSLATE("WIN1 BG3"), + wxTRANSLATE("WIN1 OBJ"), + wxTRANSLATE("WIN1 Special FX"), + wxT(""), + wxT(""), + } + }, + { + &WINOUT, 0x4A, wxTRANSLATE("0x400004A-WINOUT"), 0x3F3F, + { + wxTRANSLATE("WIN0/1 BG0"), + wxTRANSLATE("WIN0/1 BG1"), + wxTRANSLATE("WIN0/1 BG2"), + wxTRANSLATE("WIN0/1 BG3"), + wxTRANSLATE("WIN0/1 OBJ"), + wxTRANSLATE("WIN0/1 Special FX"), + wxT(""), + wxT(""), + wxTRANSLATE("OBJWIN BG0"), + wxTRANSLATE("OBJWIN BG1"), + wxTRANSLATE("OBJWIN BG2"), + wxTRANSLATE("OBJWIN BG3"), + wxTRANSLATE("OBJWIN OBJ"), + wxTRANSLATE("OBJWIN Special FX"), + wxT(""), + wxT(""), + } + }, + { + &MOSAIC, 0x4C, wxTRANSLATE("0x400004C-MOSAIC"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("BG H Size (4 bits,W)"), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("BG V Size (4 bits,W)"), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("OBJ H Size (4 bits,W)"), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("OBJ V Size (4 bits,W)"), + } + }, + { + &BLDMOD, 0x50, wxTRANSLATE("0x4000050-BLDMOD"), 0x3FFF, + { + wxTRANSLATE("1st BG0"), + wxTRANSLATE("1st BG1"), + wxTRANSLATE("1st BG2"), + wxTRANSLATE("1st BG3"), + wxTRANSLATE("1st OBJ"), + wxTRANSLATE("1st BD"), + wxT(""), + wxTRANSLATE("FX Type (2 bits)"), + wxTRANSLATE("2nd BG0"), + wxTRANSLATE("2nd BG1"), + wxTRANSLATE("2nd BG2"), + wxTRANSLATE("2nd BG3"), + wxTRANSLATE("2nd OBJ"), + wxTRANSLATE("2nd BD"), + wxT(""), + wxT(""), + } + }, + { + &COLEV, 0x52, wxTRANSLATE("0x4000052-COLEV"), 0x1F1F, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Coefficient EVA (5 bits,W)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Coefficient EVB (5 bits,W)"), + wxT(""), + wxT(""), + wxT(""), + } + }, + { + &COLY, 0x54, wxTRANSLATE("0x4000054-COLEY"), 0x001F, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Coefficient EVY (5 bits,W)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + } + }, + { + NULL, 0x60, wxTRANSLATE("0x4000060-SG10_L"), 0x007F, + { + wxT(""), + wxT(""), + wxTRANSLATE("Sweep Shifts (3 bits)"), + wxTRANSLATE("Sweep addition/decrease"), + wxT(""), + wxT(""), + wxTRANSLATE("Sweep Time (3 bits)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + } + }, + { + NULL, 0x62, wxTRANSLATE("0x4000062-SG10_H"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Sound Length (6 bits,W)"), + wxT(""), + wxTRANSLATE("Waveform Type (2 bits)"), + wxT(""), + wxT(""), + wxTRANSLATE("Envelope Steps (3 bits)"), + wxTRANSLATE("Envelope Attenuate/Amplify"), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Envelope Initial Value"), + } + }, + { + NULL, 0x64, wxTRANSLATE("0x4000064-SG11"), 0xC7FF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Frequency (11 bits,W)"), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Sound Continuous/Counter"), + wxTRANSLATE("Initialization (W)"), + } + }, + { + NULL, 0x68, wxTRANSLATE("0x4000068-SG20"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Sound Length (6 bits,W)"), + wxT(""), + wxTRANSLATE("Waveform Type (2 bits)"), + wxT(""), + wxT(""), + wxTRANSLATE("Envelope Steps (3 bits)"), + wxTRANSLATE("Envelope Attenuate/Amplify"), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Envelope Initial Value"), + } + }, + { + NULL, 0x6C, wxTRANSLATE("0x400006C-SG21"), 0xC7FF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Frequency (11 bits,W)"), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Sound Continuous/Counter"), + wxTRANSLATE("Initialization (W)"), + } + }, + { + NULL, 0x70, wxTRANSLATE("0x4000070-SG30_L"), 0x00E0, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Waveform 32/64 Steps"), + wxTRANSLATE("Waveform Bank 0/1"), + wxTRANSLATE("Sound Output"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + } + }, + { + NULL, 0x72, wxTRANSLATE("0x4000072-SG30_H"), 0xE0FF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Sound Length (8 bits,W)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Output Level (2 bits)"), + wxTRANSLATE("Forced 3/4 Output Level"), + } + }, + { + NULL, 0x74, wxTRANSLATE("0x4000074-SG31"), 0xC7FF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Frequency (11 bits,W)"), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Sound Continuous/Counter"), + wxTRANSLATE("Initialization (W)"), + } + }, + { + NULL, 0x78, wxTRANSLATE("0x4000078-SG40"), 0xFF3F, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Sound Length (6 bits,W)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Envelope Steps (3 bits)"), + wxTRANSLATE("Envelope Attenuate/Amplify"), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Envelope Initial Value"), + } + }, + { + NULL, 0x7C, wxTRANSLATE("0x400007C-SG41"), 0xC0FF, + { + wxT(""), + wxT(""), + wxTRANSLATE("Dividing Ratio Freq. (3 bits)"), + wxTRANSLATE("Counter 15/7 Steps"), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Counter Shift Clock (4 bits)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Sound Continuous/Counter"), + wxTRANSLATE("Initialization (W)"), + } + }, + { + NULL, 0x80, wxTRANSLATE("0x4000080-SGCNT0_L"), 0xFF77, + { + wxT(""), + wxT(""), + wxTRANSLATE("Right Volume (3 bits)"), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Left Volume (3 bits)"), + wxT(""), + wxTRANSLATE("Channel 1->Right"), + wxTRANSLATE("Channel 2->Right"), + wxTRANSLATE("Channel 3->Right"), + wxTRANSLATE("Channel 4->Right"), + wxTRANSLATE("Channel 1->Left"), + wxTRANSLATE("Channel 2->Left"), + wxTRANSLATE("Channel 3->Left"), + wxTRANSLATE("Channel 4->Left"), + } + }, + { + NULL, 0x82, wxTRANSLATE("0x4000082-SGCNT0_H"), 0xFF0F, + { + wxT(""), + wxTRANSLATE("Sound 1-4 Volume (2 bits)"), + wxTRANSLATE("DMA Sound A Volume"), + wxTRANSLATE("DMA Sound B Volume"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("DMA Sound A->Right"), + wxTRANSLATE("DMA Sound A->Left"), + wxTRANSLATE("DMA Sound A Timer"), + wxTRANSLATE("DMA Sound A Reset FIFO"), + wxTRANSLATE("DMA Sound B->Right"), + wxTRANSLATE("DMA Sound B->Left"), + wxTRANSLATE("DMA Sound B Timer"), + wxTRANSLATE("DMA Sound B Reset FIFO"), + } + }, + { + NULL, 0x84, wxTRANSLATE("0x4000084-SGCNT1"), 0x0080, + { + wxTRANSLATE("Sound 1 On"), + wxTRANSLATE("Sound 2 On"), + wxTRANSLATE("Sound 3 On"), + wxTRANSLATE("Sound 4 On"), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Master Sound Enable"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + } + }, + { + NULL, 0x88, wxTRANSLATE("0x4000088-SGBIAS"), 0xC3FF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Bias Level (10 bits)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Sampling Rate (2 bits)"), + } + }, + { + NULL, 0xA0, wxTRANSLATE("0x40000A0-SIGFIFOA_L"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Data 0 (8 bits)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Data 1 (8 bits)"), + } + }, + { + NULL, 0xA2, wxTRANSLATE("0x40000A2-SIGFIFOA_H"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Data 2 (8 bits)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Data 3 (8 bits)"), + } + }, + { + NULL, 0xA4, wxTRANSLATE("0x40000A4-SIGFIFOB_L"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Data 0 (8 bits)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Data 1 (8 bits)"), + } + }, + { + NULL, 0xA6, wxTRANSLATE("0x40000A6-SIGFIFOB_H"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Data 2 (8 bits)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Data 3 (8 bits)"), + } + }, + { + &DM0SAD_L, 0xB0, wxTRANSLATE("0x40000B0-DM0SAD_L"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Source Address (lower 16 bits)"), + } + }, + { + &DM0SAD_H, 0xB2, wxTRANSLATE("0x40000B2-DM0SAD_H"), 0x07FF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Source Address (upper 11 bits)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + } + }, + { + &DM0DAD_L, 0xB4, wxTRANSLATE("0x40000B4-DM0DAD_L"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Destination Address (lower 16 bits)"), + } + }, + { + &DM0DAD_H, 0xB6, wxTRANSLATE("0x40000B6-DM0DAD_H"), 0x07FF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Destination Address (upper 11 bits)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + } + }, + { + &DM0CNT_L, 0xB8, wxTRANSLATE("0x40000B8-DM0CNT_L"), 0x3FFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Count (14 bits)"), + wxT(""), + wxT(""), + } + }, + { + &DM0CNT_H, 0xBA, wxTRANSLATE("0x40000BA-DM0CNT_H"), 0xF7E0, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Destination Address Control (2 bits)"), + wxT(""), + wxTRANSLATE("Source Address Control (2 bits)"), + wxTRANSLATE("Repeat"), + wxTRANSLATE("Transfer Type"), + wxT(""), + wxT(""), + wxTRANSLATE("Start Timing (2 bits)"), + wxTRANSLATE("Interrupt Request"), + wxTRANSLATE("Enable"), + } + }, + { + &DM1SAD_L, 0xBC, wxTRANSLATE("0x40000BC-DM1SAD_L"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Source Address (lower 16 bits)"), + } + }, + { + &DM1SAD_H, 0xBE, wxTRANSLATE("0x40000BE-DM1SAD_H"), 0x0FFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Source Address (upper 12 bits)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + } + }, + { + &DM1DAD_L, 0xC0, wxTRANSLATE("0x40000C0-DM1DAD_L"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Destination Address (lower 16 bits)"), + } + }, + { + &DM1DAD_H, 0xC2, wxTRANSLATE("0x40000C2-DM1DAD_H"), 0x07FF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Destination Address (upper 11 bits)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + } + }, + { + &DM1CNT_L, 0xC4, wxTRANSLATE("0x40000C4-DM1CNT_L"), 0x3FFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Count (14 bits)"), + wxT(""), + wxT(""), + } + }, + { + &DM1CNT_H, 0xC6, wxTRANSLATE("0x40000C6-DM1CNT_H"), 0xF7E0, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Destination Address Control (2 bits)"), + wxT(""), + wxTRANSLATE("Source Address Control (2 bits)"), + wxTRANSLATE("Repeat"), + wxTRANSLATE("Transfer Type"), + wxT(""), + wxT(""), + wxTRANSLATE("Start Timing (2 bits)"), + wxTRANSLATE("Interrupt Request"), + wxTRANSLATE("Enable"), + } + }, + { + &DM2SAD_L, 0xC8, wxTRANSLATE("0x40000C8-DM2SAD_L"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Source Address (lower 16 bits)"), + } + }, + { + &DM2SAD_H, 0xCA, wxTRANSLATE("0x40000CA-DM2SAD_H"), 0x0FFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Source Address (upper 12 bits)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + } + }, + { + &DM2DAD_L, 0xCC, wxTRANSLATE("0x40000CC-DM2DAD_L"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Destination Address (lower 16 bits)"), + } + }, + { + &DM2DAD_H, 0xCE, wxTRANSLATE("0x40000CE-DM2DAD_H"), 0x07FF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Destination Address (upper 11 bits)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + } + }, + { + &DM2CNT_L, 0xD0, wxTRANSLATE("0x40000D0-DM2CNT_L"), 0x3FFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Count (14 bits)"), + wxT(""), + wxT(""), + } + }, + { + &DM2CNT_H, 0xD2, wxTRANSLATE("0x40000D2-DM2CNT_H"), 0xF7E0, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Destination Address Control (2 bits)"), + wxT(""), + wxTRANSLATE("Source Address Control (2 bits)"), + wxTRANSLATE("Repeat"), + wxTRANSLATE("Transfer Type"), + wxT(""), + wxT(""), + wxTRANSLATE("Start Timing (2 bits)"), + wxTRANSLATE("Interrupt Request"), + wxTRANSLATE("Enable"), + } + }, + { + &DM3SAD_L, 0xD4, wxTRANSLATE("0x40000D4-DM3SAD_L"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Source Address (lower 16 bits)"), + } + }, + { + &DM3SAD_H, 0xD6, wxTRANSLATE("0x40000D6-DM3SAD_H"), 0x0FFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Source Address (upper 12 bits)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + } + }, + { + &DM3DAD_L, 0xD8, wxTRANSLATE("0x40000D8-DM3DAD_L"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Destination Address (lower 16 bits)"), + } + }, + { + &DM3DAD_H, 0xDA, wxTRANSLATE("0x40000DA-DM3DAD_H"), 0x0FFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Destination Address (upper 12 bits)"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + } + }, + { + &DM3CNT_L, 0xDC, wxTRANSLATE("0x40000DC-DM3CNT_L"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Count (16 bits)"), + } + }, + { + &DM3CNT_H, 0xDE, wxTRANSLATE("0x40000DE-DM3CNT_H"), 0xFFE0, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Destination Address Control (2 bits)"), + wxT(""), + wxTRANSLATE("Source Address Control (2 bits)"), + wxTRANSLATE("Repeat"), + wxTRANSLATE("Transfer Type"), + wxTRANSLATE("Game Pak Data Request"), + wxT(""), + wxTRANSLATE("Start Timing (2 bits)"), + wxTRANSLATE("Interrupt Request"), + wxTRANSLATE("Enable"), + } + }, + { + &TM0D, 0x100, wxTRANSLATE("0x4000100-TM0D"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Timer Counter (16 bits)"), + } + }, + { + &TM0CNT, 0x102, wxTRANSLATE("0x4000102-TM0CNT"), 0x00C7, + { + wxT(""), + wxTRANSLATE("Scalar Selection (2 bits)"), + wxTRANSLATE("Count Up"), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Interrupt Request"), + wxTRANSLATE("Enable"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + } + }, + { + &TM1D, 0x104, wxTRANSLATE("0x4000104-TM1D"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Timer Counter (16 bits)"), + } + }, + { + &TM1CNT, 0x106, wxTRANSLATE("0x4000106-TM1CNT"), 0x00C7, + { + wxT(""), + wxTRANSLATE("Scalar Selection (2 bits)"), + wxTRANSLATE("Count Up"), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Interrupt Request"), + wxTRANSLATE("Enable"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + } + }, + { + &TM2D, 0x108, wxTRANSLATE("0x4000108-TM2D"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Timer Counter (16 bits)"), + } + }, + { + &TM2CNT, 0x10A, wxTRANSLATE("0x400010A-TM2CNT"), 0x00C7, + { + wxT(""), + wxTRANSLATE("Scalar Selection (2 bits)"), + wxTRANSLATE("Count Up"), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Interrupt Request"), + wxTRANSLATE("Enable"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + } + }, + { + &TM3D, 0x10C, wxTRANSLATE("0x400010C-TM3D"), 0xFFFF, + { + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Timer Counter (16 bits)"), + } + }, + { + &TM3CNT, 0x10E, wxTRANSLATE("0x400010E-TM3CNT"), 0x00C7, + { + wxT(""), + wxTRANSLATE("Scalar Selection (2 bits)"), + wxTRANSLATE("Count Up"), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Interrupt Request"), + wxTRANSLATE("Enable"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + } + }, + { + &P1, 0x130, wxTRANSLATE("0x4000130-P1"), 0x03FF, + { + wxTRANSLATE("A"), + wxTRANSLATE("B"), + wxTRANSLATE("Select"), + wxTRANSLATE("Start"), + wxTRANSLATE("Right"), + wxTRANSLATE("Left"), + wxTRANSLATE("Up"), + wxTRANSLATE("Down"), + wxTRANSLATE("Shoulder Right"), + wxTRANSLATE("Shoulder Left"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + } + }, + { + NULL, 0x132, wxTRANSLATE("0x4000132-P1CNT"), 0xC3FF, + { + wxTRANSLATE("A"), + wxTRANSLATE("B"), + wxTRANSLATE("Select"), + wxTRANSLATE("Start"), + wxTRANSLATE("Right"), + wxTRANSLATE("Left"), + wxTRANSLATE("Up"), + wxTRANSLATE("Down"), + wxTRANSLATE("Shoulder Right"), + wxTRANSLATE("Shoulder Left"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Interrupt Request"), + wxTRANSLATE("Interrupt Condition"), + } + }, + { + &IE, 0x200, wxTRANSLATE("0x4000200-IE"), 0x3FFF, + { + wxTRANSLATE("VBlank"), + wxTRANSLATE("HBlank"), + wxTRANSLATE("VCount"), + wxTRANSLATE("Timer 0"), + wxTRANSLATE("Timer 1"), + wxTRANSLATE("Timer 2"), + wxTRANSLATE("Timer 3"), + wxTRANSLATE("Serial"), + wxTRANSLATE("DMA 0"), + wxTRANSLATE("DMA 1"), + wxTRANSLATE("DMA 2"), + wxTRANSLATE("DMA 3"), + wxTRANSLATE("Keypad"), + wxTRANSLATE("Game Pak"), + wxT(""), + wxT(""), + } + }, + { + &IF, 0x202, wxTRANSLATE("0x4000202-IF"), 0x0000, + { + wxTRANSLATE("VBlank"), + wxTRANSLATE("HBlank"), + wxTRANSLATE("VCount"), + wxTRANSLATE("Timer 0"), + wxTRANSLATE("Timer 1"), + wxTRANSLATE("Timer 2"), + wxTRANSLATE("Timer 3"), + wxTRANSLATE("Serial"), + wxTRANSLATE("DMA 0"), + wxTRANSLATE("DMA 1"), + wxTRANSLATE("DMA 2"), + wxTRANSLATE("DMA 3"), + wxTRANSLATE("Keypad"), + wxTRANSLATE("Game Pak"), + wxT(""), + wxT(""), + } + }, + { + NULL, 0x204, wxTRANSLATE("0x4000204-WAITCNT"), 0x5FFF, + { + wxT(""), + wxTRANSLATE("SRAM Wait Control (2 bits)"), + wxT(""), + wxTRANSLATE("Wait State 0 First Access (2 bits)"), + wxTRANSLATE("Wait State 0 Second Access"), + wxT(""), + wxTRANSLATE("Wait State 1 First Access (2 bits)"), + wxTRANSLATE("Wait State 1 Second Access"), + wxT(""), + wxTRANSLATE("Wait State 2 First Access (2 bits)"), + wxTRANSLATE("Wait State 2 Second Access"), + wxT(""), + wxTRANSLATE("PHI Terminal Output (2 bits)"), + wxT(""), + wxTRANSLATE("Game Pak Prefetch Buffer"), + wxTRANSLATE("Game Pak Type Flag"), + } + }, + { + &IME, 0x208, wxTRANSLATE("0x4000208-IME"), 0x0001, + { + wxTRANSLATE("Master Interrupt Enable"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + } + }, + { + NULL, 0x300, wxTRANSLATE("0x4000300-HALTCNT"), 0x8001, + { + wxTRANSLATE("First Boot"), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxT(""), + wxTRANSLATE("Power Down"), + } + }, +}; + +#define NUM_IOREGS (sizeof(ioregs)/sizeof(ioregs[0])) diff --git a/src/wx/openal.cpp b/src/wx/openal.cpp new file mode 100644 index 0000000..092c703 --- /dev/null +++ b/src/wx/openal.cpp @@ -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 +#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 diff --git a/src/wx/openal.h b/src/wx/openal.h new file mode 100644 index 0000000..a0a2aba --- /dev/null +++ b/src/wx/openal.h @@ -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 +#include + +// 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; +}; diff --git a/src/wx/opts.cpp b/src/wx/opts.cpp new file mode 100644 index 0000000..fe7d443 --- /dev/null +++ b/src/wx/opts.cpp @@ -0,0 +1,789 @@ +#include "wxvbam.h" +#include +#include +/* + * 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[] = { + + /// Display + 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"), gopts.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), + + /// GB + BOOLOPT("GB/AutomaticBorder", wxTRANSLATE("Automatically enable border for Super GameBoy games"), gopts.gbBorderAutomatic), + STROPT ("GB/BiosFile", wxTRANSLATE("BIOS file to use for GB, if enabled"), gopts.gb_bios), + BOOLOPT("GB/Border", wxTRANSLATE("Always enable border"), gopts.gbBorderOn), + ENUMOPT("GB/EmulatorType", wxTRANSLATE("Type of system to emulate"), gopts.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"), gopts.gbcColorOption), + ENUMOPT("GB/Palette", wxTRANSLATE("The palette to use"), gopts.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), + + /// GBA + 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"), gopts.gba_joybus_enabled), + STROPT ("GBA/JoybusHost", wxTRANSLATE("Joybus host address"), gopts.joybus_host), + BOOLOPT("GBA/Link", wxTRANSLATE("Enable link cable"), gopts.gba_link_enabled), + BOOLOPT("GBA/LinkFast", wxTRANSLATE("Enable faster network protocol by default"), gopts.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"), gopts.rfu_enabled), + INTOPT ("GBA/LinkTimeout", wxTRANSLATE("Link timeout (ms)"), gopts.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), + + /// General + 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"), gopts.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"), gopts.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"), gopts.skipSaveGameBattery), + BOOLOPT("General/StateLoadNoCheat", wxTRANSLATE("Do not overwrite cheat list when loading state"), gopts.skipSaveGameCheats), + INTOPT ("General/Throttle", wxTRANSLATE("Throttle game speed, even when accelerated (0-1000%, 0 = disabled)"), gopts.throttle, 0, 1000), + + /// Joypad + { wxT("Joypad/*/*"), wxTRANSLATE("The parameter Joypad//