diff --git a/.gitignore b/.gitignore index 4c3ecc507..53bfc9878 100644 --- a/.gitignore +++ b/.gitignore @@ -48,6 +48,9 @@ reicast-ios.xccheckout shell/linux/.map shell/linux/nosym-reicast.elf shell/linux/reicast.elf +shell/linux/reicast_naomi.elf +shell/linux/reicast_awave.elf +shell/linux/dispframe.elf # Visual Studio generated diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..b4ccfb012 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,313 @@ +cmake_minimum_required(VERSION 3.5.0 FATAL_ERROR) + +set(TNAME reicast) + +project(${TNAME}) + +enable_language(ASM) +enable_language(ASM_MASM) + +set(DEBUG_CMAKE ON) + +set(CMAKE_VERBOSE_MAKEFILE ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) + + +set(reicast_root_path "${CMAKE_CURRENT_SOURCE_DIR}") +set(reicast_core_path "${reicast_root_path}/core") +set(reicast_shell_path "${reicast_root_path}/shell") + +list(APPEND CMAKE_MODULE_PATH "${reicast_shell_path}/cmake") + + +include(GetGitRevisionDescription) +git_describe(GIT_VERSION --tags) +configure_file(${reicast_core_path}/version.h.in ${reicast_core_path}/version.h @ONLY) + + + + +## reicast build modules # +# + +set(reicast_SRCS "") + +include(config) # configure build settings, must be first + + + +### libdreamcast.cmake ######################################################################### + +set(d_core ${reicast_core_path}) + +file(GLOB_RECURSE hw_SRCS ${d_core}/hw/*.cpp ${d_core}/hw/*.h) + +file(GLOB cfg_SRCS ${d_core}/cfg/*.cpp ${d_core}/cfg/*.h) +file(GLOB rend_SRCS ${d_core}/rend/*.cpp ${d_core}/rend/*.h) +file(GLOB input_SRCS ${d_core}/input/*.cpp ${d_core}/input/*.h) +file(GLOB reios_SRCS ${d_core}/reios/*.cpp ${d_core}/reios/*.h) +file(GLOB imgread_SRCS ${d_core}/imgread/*.cpp ${d_core}/imgread/*.h) +file(GLOB profiler_SRCS ${d_core}/profiler/*.cpp ${d_core}/profiler/*.h) +file(GLOB archive_SRCS ${d_core}/archive/*.cpp ${d_core}/archive/*.h) + +#### option(rend) +file(GLOB gl4_SRCS ${d_core}/rend/gl4/*.cpp ${d_core}/rend/gl4/*.h) +file(GLOB gles_SRCS ${d_core}/rend/gles/*.cpp ${d_core}/rend/gles/*.h) + +set(core_SRCS + ${hw_SRCS} + ${cfg_SRCS} + ${rend_SRCS} + ${gl4_SRCS} + ${gles_SRCS} + ${input_SRCS} + ${reios_SRCS} + ${imgread_SRCS} + ${profiler_SRCS} + ${d_core}/archive/archive.cpp ${d_core}/archive/archive.h + ${d_core}/nullDC.cpp + ${d_core}/stdclass.cpp + ${d_core}/dispframe.cpp + ${d_core}/serialize.cpp +) + + +if(${BUILD_COMPILER} EQUAL ${COMPILER_GCC}) # Add Clang if NOT WIN32 *FIXME* + list(APPEND core_SRCS ${archive_SRCS}) +endif() + +if(${FEAT_SHREC} EQUAL ${DYNAREC_JIT}) +# + if(${HOST_CPU} EQUAL ${CPU_X86}) + list(APPEND core_SRCS + ${d_core}/rec-x86/rec_x86_driver.cpp + ${d_core}/rec-x86/rec_x86_il.cpp + ${d_core}/rec-x86/rec_x86_asm.cpp # change for linux , rec_lin86_asm.S + ${d_core}/rec-x86/rec_x86_ngen.h + ) + elseif(${HOST_CPU} EQUAL ${CPU_ARM}) + list(APPEND core_SRCS + ${d_core}/rec-ARM/ngen_arm.S + ${d_core}/rec-ARM/rec_arm.cpp + ) + elseif(${HOST_CPU} EQUAL ${CPU_X64}) + + ### FIXME: asm with cmake ninja+VC + if(${BUILD_COMPILER} EQUAL ${COMPILER_VC}) + list(APPEND core_SRCS ${d_core}/rec-x64/msvc.asm) + endif() + + list(APPEND core_SRCS ${d_core}/rec-x64/rec_x64.cpp ${d_core}/rec-x64/x64_regalloc.h) + + elseif(${HOST_CPU} EQUAL ${CPU_A64}) + list(APPEND core_SRCS ${d_core}/rec-ARM64/rec_arm64.cpp ${d_core}/rec-ARM64/arm64_regalloc.h) + + else() + message(" FEAT_SHREC==DYNAREC_JIT && HOST_CPU Unknown Default add arch or disable rec if not avail.") + error() + endif() +# +elseif(${FEAT_SHREC} EQUAL ${DYNAREC_CPP}) + list(APPEND core_SRCS ${d_core}/rec-cpp/rec_cpp.cpp) +endif() + +add_definitions(/DFEAT_HAS_SOFTREND=0) + + +### deps.cmake ################################################################################# + +set(d_deps ${reicast_core_path}/deps) +include_directories ("${d_deps}") +include_directories ("${d_deps}/picotcp/include") +include_directories ("${d_deps}/picotcp/modules") + +file(GLOB xbyak_H ${d_deps}/xbyak/*.h) # include headers into cmake target/project view + +file(GLOB chdr_SRCS ${d_deps}/chdr/*.c) +file(GLOB lzma_SRCS ${d_deps}/lzma/*.c) +file(GLOB lz_SRCS ${d_deps}/zlib/*.c) +file(GLOB lzip_SRCS ${d_deps}/libzip/*.c) +file(GLOB lpng_SRCS ${d_deps}/libpng/*.c) +file(GLOB lelf_SRCS ${d_deps}/libelf/el*.cpp) +file(GLOB crypt_SRCS ${d_deps}/crypto/*.cpp) +file(GLOB imgui_SRCS ${d_deps}/imgui/*.cpp) +file(GLOB lws_SRCS ${d_deps}/libwebsocket/*.c) + +file(GLOB picoModS ${d_deps}/picotcp/modules/*.c) +file(GLOB picoStkS ${d_deps}/picotcp/stack/*.c) +set(pico_SRCS ${picoModS} ${picoStkS}) + +set(deps_SRCS + ${lz_SRCS} + ${lpng_SRCS} + ${lelf_SRCS} + ${chdr_SRCS} + ${crypt_SRCS} + ${imgui_SRCS} + ${d_deps}/xbrz/xbrz.cpp + ${d_deps}/dirent/dirent.c + ${d_deps}/xxhash/xxhash.c + ${d_deps}/chdpsr/cdipsr.cpp # sigh, this dir is named chdpsr for some reason ... + ${d_deps}/coreio/coreio.cpp +# ${d_deps}/ifaddrs/ifaddrs.c + ${xbyak_H} +) + +if(${BUILD_COMPILER} EQUAL ${COMPILER_GCC}) # Add Clang if NOT WIN32 *FIXME* + list(APPEND deps_SRCS + ${lzip_SRCS} + ${lzma_SRCS} + ${pico_SRCS} + ) + add_definitions(-D_7ZIP_ST -DCHD5_LZMA) +endif() + +### libosd.cmake ################################################################################ + + +set(d_aout ${reicast_core_path}/oslib) + +include_directories ("${d_core}/khronos") + +## I really should just glob all of the dirs and ;shrug; if guards don't do it all ## + +set(osd_SRCS "") + +list(APPEND osd_SRCS ${d_aout}/audiostream.cpp) + +if (${HOST_OS} EQUAL ${OS_WINDOWS}) + + list(APPEND osd_SRCS ${d_core}/windows/winmain.cpp) + list(APPEND osd_SRCS ${d_core}/windows/win_vmem.cpp) + list(APPEND osd_SRCS ${d_aout}/audiobackend_directsound.cpp) + + + link_libraries(dsound.lib winmm.lib xinput.lib wsock32.lib opengl32.lib) + + +elseif (${HOST_OS} EQUAL ${OS_LINUX} OR ${HOST_OS} EQUAL ${OS_ANDROID}) + + list(APPEND osd_SRCS + ${d_core}/linux/common.cpp + ${d_core}/linux/context.cpp + ${d_core}/linux/nixprof/nixprof.cpp + + ${d_aout}/audiobackend_oss.cpp # add option + ) # todo: configure linux audio lib options + + if(NOT ANDROID) + list(APPEND osd_SRCS + ${d_core}/linux-dist/x11.cpp + ${d_core}/linux-dist/main.cpp + ${d_core}/linux-dist/evdev.cpp) + + add_definitions(-DSUPPORT_X11) ## don't use GLES ? + link_libraries(X11) + else() + list(APPEND osd_SRCS + .//android-studio/reicast/src/main/jni/src/Android.cpp + .//android-studio/reicast/src/main/jni/src/utils.cpp + # .//android-studio/reicast/src/main/jni/src/XperiaPlay.c + ) + endif() # ANDROID + + add_definitions(-DGLES -DUSE_EVDEV) + + link_libraries(pthread dl rt asound Xext GLESv2 EGL) + +elseif(${HOST_OS} EQUAL ${OS_DARWIN}) +# + list(APPEND objc_SRCS + ./shell/apple/emulator-osx/emulator-osx/osx-main.mm + ./shell/apple/emulator-osx/emulator-osx/AppDelegate.swift + ./shell/apple/emulator-osx/emulator-osx/EmuGLView.swift + ) + + set_source_files_properties(${objc_SRCS} PROPERTIES COMPILE_FLAGS "-x objective-c++") + + list(APPEND osd_SRCS ${objc_SRCS} + ${d_osd}/linux/common.cpp + ${d_osd}/linux/context.cpp + ${d_osd}/audiobackend/audiobackend_coreaudio.cpp + # if NOT USE_SWIFT / ObjC + #${d_osd}/apple/osx_osd.cpp + ) + +else() +# + message("OS Unhandled") + error() +# +endif() + + + +## + +include_directories ("${reicast_core_path}") + + + + +set(reicast_SRCS ${core_SRCS} ${deps_SRCS} ${osd_SRCS}) + +add_executable(${TNAME}${binSuffix} ${reicast_SRCS} ${deps_SRCS}) + + + + + +if(APPLE) + enable_language(Swift) + set_property(TARGET ${TNAME} PROPERTY XCODE_ATTRIBUTE_SWIFT_OBJC_BRIDGING_HEADER "./shell/apple/emulator-osx/emulator-osx/emulator-osx-Bridging-Header.h") + + target_link_libraries(${TNAME} +# "-framework Cocoa" +# "-framework AppKit" + "-framework CoreData" + "-framework CoreAudio" + "-framework AudioUnit" + "-framework AudioToolbox" + "-framework Foundation" +) + +#### OSX Notes, when not using xcode you have to make app bundle, edit plist and copy, convert MainMenu.xib to nib and copy, +#null@devpc:~$ /Users/null/Documents/projects/reicast-emulator/bin/RelWithDebInfo/Reicast.app/Contents/MacOS/reicast ; exit; +#2019-03-18 14:28:44.842 reicast[11468:131797] Unknown class _TtC12emulator_osx9EmuGLView in Interface Builder file at path /Users/null/Documents/projects/reicast-emulator/bin/RelWithDebInfo/Reicast.app/Contents/Resources/MainMenu.nib. +#2019-03-18 14:28:44.842 reicast[11468:131797] Unknown class _TtC12emulator_osx11AppDelegate in Interface Builder file at path /Users/null/Documents/projects/reicast-emulator/bin/RelWithDebInfo/Reicast.app/Contents/Resources/MainMenu.nib. +#2019-03-18 14:28:44.860 reicast[11468:131797] Failed to connect (window) outlet from (NSObject) to (NSWindow): missing setter or instance variable +# + +endif() #APPLE + + +if(DEBUG_CMAKE) + message(" ------------------------------------------------") + message(" - HOST_OS: ${HOST_OS} - HOST_CPU: ${HOST_CPU} ") + message(" - host_os: ${host_os} - host_arch: ${host_arch} ") + message(" ------------------------------------------------") + message(" C Flags: ${CMAKE_C_FLAGS} ") + message(" CXX Flags: ${CMAKE_CXX_FLAGS} ") + message(" LINK_DIRS: ${LINK_DIRECTORIES}") + message("LINK_FLAGS: ${CMAKE_EXE_LINKER_FLAGS}") + message(" ------------------------------------------------\n") +endif() + + + + + + + + + + + + + + + + + + + diff --git a/CMakeSettings.json b/CMakeSettings.json new file mode 100644 index 000000000..7fbe9b101 --- /dev/null +++ b/CMakeSettings.json @@ -0,0 +1,220 @@ +{ + "environments": [ + { + "environment": "toolchain.generic", + "TOOLCHAIN_FILE": "ps4sdk.cmake" + } + ], + "configurations": [ + { + "name": "win-x86-Debug", + "generator": "Ninja", + "description": "TemplateDescription_Localize_x86Debug", + "configurationType": "Debug", + "inheritEnvironments": [ + "msvc_x86" + ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\build\\${name}\\install", + "cmakeCommandArgs": "", + "buildCommandArgs": "-v", + "ctestCommandArgs": "" + }, + { + "name": "win-x86-Release", + "generator": "Ninja", + "description": "TemplateDescription_Localize_x86Release", + "configurationType": "RelWithDebInfo", + "inheritEnvironments": [ + "msvc_x86" + ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\build\\${name}\\install", + "cmakeCommandArgs": "", + "buildCommandArgs": "-v", + "ctestCommandArgs": "" + }, + { + "name": "win-x64-Debug", + "generator": "Ninja", + "description": "TemplateDescription_Localize_x64Debug", + "configurationType": "Debug", + "inheritEnvironments": [ + "msvc_x64_x64" + ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\build\\${name}\\install", + "cmakeCommandArgs": "-DNINJA=1", + "buildCommandArgs": "-v", + "ctestCommandArgs": "" + }, + { + "name": "win-x64-Release", + "generator": "Ninja", + "description": "TemplateDescription_Localize_x64Release", + "configurationType": "RelWithDebInfo", + "inheritEnvironments": [ + "msvc_x64_x64" + ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\build\\${name}\\install", + "cmakeCommandArgs": "-DNINJA=1", + "buildCommandArgs": "-v", + "ctestCommandArgs": "" + }, + { + "name": "win-x64-Clang-RelWithDebInfo", + "generator": "Ninja", + "description": "TemplateDescription_Localize_x64Release", + "configurationType": "RelWithDebInfo", + "inheritEnvironments": [ + "msvc_x64_x64" + ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\build\\${name}\\install", + "cmakeCommandArgs": "", + "buildCommandArgs": "-v", + "ctestCommandArgs": "", + "variables": [ + { + "name": "CMAKE_C_COMPILER", + "value": "clang-cl.exe" + }, + { + "name": "CMAKE_CXX_COMPILER", + "value": "clang-cl.exe" + } + ] + }, + + // Console SDK's + { + "name": "PS4 SDK", + "generator": "Ninja", + "description": "TemplateDescription_Localize_PS4SDK", + "configurationType": "RelWithDebInfo", + "inheritEnvironments": [ + "toolchain.generic" + ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\build\\${name}\\install", + "cmakeCommandArgs": "-DCMAKE_TOOLCHAIN_FILE=${projectDir}\\cmake\\ps4sdk.cmake", + "buildCommandArgs": "-v", + "ctestCommandArgs": "" + }, + { + "name": "NSW SDK", + "generator": "Ninja", + "description": "TemplateDescription_Localize_PS4SDK", + "configurationType": "RelWithDebInfo", + "inheritEnvironments": [ + "toolchain.generic" + ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\build\\${name}\\install", + "cmakeCommandArgs": "-DCMAKE_TOOLCHAIN_FILE=${projectDir}\\cmake\\devkitA64.cmake", + "buildCommandArgs": "-v", + "ctestCommandArgs": "" + }, + + // UWP and VS Gen (temp?) + { + "name": "uwp-x64-Release", + "generator": "Visual Studio 15 2017 Win64", + "description": "TemplateDescription_Localize_x64Release", + "configurationType": "RelWithDebInfo", + "inheritEnvironments": [ + "msvc_x64_x64" + ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\build\\${name}\\install", + "cmakeCommandArgs": "-DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10", + "buildCommandArgs": "", + "ctestCommandArgs": "" + }, + { + "name": "win-x64-MSBuild-Release", + "generator": "Visual Studio 15 2017 Win64", + "description": "TemplateDescription_Localize_x64Release", + "configurationType": "RelWithDebInfo", + "inheritEnvironments": [ + "msvc_x64_x64" + ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\build\\${name}\\install", + "cmakeCommandArgs": "", + "buildCommandArgs": "", + "ctestCommandArgs": "" + }, + { + "environments": [ + { + //"MINGW64_ROOT": "C:\\msys64\\mingw64", + "BIN_ROOT": "${env.MINGW64_ROOT}\\bin", + "FLAVOR": "x86_64-w64-mingw32", + "TOOLSET_VERSION": "7.3.0", + "PATH": "${env.MINGW64_ROOT}\\bin;${env.MINGW64_ROOT}\\..\\usr\\local\\bin;${env.MINGW64_ROOT}\\..\\usr\\bin;${env.MINGW64_ROOT}\\..\\bin;${env.PATH}", + "INCLUDE": "${env.INCLUDE};${env.MINGW64_ROOT}\\include\\c++\\${env.TOOLSET_VERSION};${env.MINGW64_ROOT}\\include\\c++\\${env.TOOLSET_VERSION}\\tr1;${env.MINGW64_ROOT}\\include\\c++\\${env.TOOLSET_VERSION}\\${env.FLAVOR}", + "environment": "mingw_64" + } + ], + "name": "Mingw64-Release", + "generator": "Ninja", + "configurationType": "RelWithDebInfo", + "inheritEnvironments": [ + "mingw_64" + ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\build\\${name}\\install", + "cmakeCommandArgs": "", + "buildCommandArgs": "-v", + "ctestCommandArgs": "", + "intelliSenseMode": "linux-gcc-x64", + "variables": [ + { + "name": "CMAKE_C_COMPILER", + "value": "${env.BIN_ROOT}\\gcc.exe" + }, + { + "name": "CMAKE_CXX_COMPILER", + "value": "${env.BIN_ROOT}\\g++.exe" + } + ] + }, + { + "environments": [ + { + //"MINGW64_ROOT": "C:\\msys64\\mingw64", + "BIN_ROOT": "${env.MINGW64_ROOT}\\bin", + "FLAVOR": "x86_64-w64-mingw32", + "TOOLSET_VERSION": "7.3.0", + "PATH": "${env.MINGW64_ROOT}\\bin;${env.MINGW64_ROOT}\\..\\usr\\local\\bin;${env.MINGW64_ROOT}\\..\\usr\\bin;${env.MINGW64_ROOT}\\..\\bin;${env.PATH}", + "INCLUDE": "${env.INCLUDE};${env.MINGW64_ROOT}\\include\\c++\\${env.TOOLSET_VERSION};${env.MINGW64_ROOT}\\include\\c++\\${env.TOOLSET_VERSION}\\tr1;${env.MINGW64_ROOT}\\include\\c++\\${env.TOOLSET_VERSION}\\${env.FLAVOR}", + "environment": "mingw_64" + } + ], + "name": "Mingw64-Debug", + "generator": "Ninja", + "configurationType": "Debug", + "inheritEnvironments": [ + "mingw_64" + ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\build\\${name}\\install", + "cmakeCommandArgs": "", + "buildCommandArgs": "-v", + "ctestCommandArgs": "", + "intelliSenseMode": "linux-gcc-x64", + "variables": [ + { + "name": "CMAKE_C_COMPILER", + "value": "${env.BIN_ROOT}\\gcc.exe" + }, + { + "name": "CMAKE_CXX_COMPILER", + "value": "${env.BIN_ROOT}\\g++.exe" + } + ] + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index 3151a96bb..07bb44e7b 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,18 @@ +[![Snap Status](https://build.snapcraft.io/badge/reicast/reicast-emulator.svg)](https://build.snapcraft.io/user/reicast/reicast-emulator) + reicast =========== -reicast is a multi-platform Sega Dreamcast emulator. +**reicast** is a multi-platform Sega Dreamcast emulator. This is a developer-oriented resource, if you just want bins head over to http://reicast.com/ For development discussion, join [#reicast in freenode](https://webchat.freenode.net/?channels=reicast) or stop by the [reicast Discord server](http://discord.gg/Hc6CF72) -Caution -------- -The source is a mess, and dragons might eat your cat when you clone this project. We're working on cleaning things up, but don't hold your breath. Why don't you lend a hand? - Rebranding/(hard)forks ---------------- -If you are interested into further porting/adapting/whatever, *please* don't fork off. I hate that. Really. +If you are interested into further porting/adapting/whatever, *please* do not fork off. +We hate that. **Really**. Let's try to keep everything under a single project :) @@ -28,7 +27,8 @@ Bugs that do not include a form may be closed until it is filled out. Contributing ------------ -For small/one-off fixes a PR from a github fork is alright. For longer term collaboration we prefer to use namespaced branches in the form of `/` in the main repo. +- For small/one-off fixes, a PR from a GitHub fork is alright. +- For longer term collaboration, we prefer to use namespaced branches in the form of `/` in the main repo. Before you work on something major, make sure to check the issue tracker to coordinate with other contributors, and open an issue to get feedback before doing big changes/PRs. It is always polite to check the history of the code you're working on and collaborate with the people that have worked on it. You can introduce yourself in [Meet the team](https://github.com/reicast/reicast-emulator/issues/1113). @@ -109,23 +109,26 @@ Or open the .xcodeproj in Xcode and hit "Build". Building for Linux ------------------ -Requirements: -* build-essential -* libasound2 -* libegl1-mesa-dev -* libgles2-mesa-dev -* libasound2-dev -* mesa-common-dev -* libgl1-mesa-dev - -From project root directory: +### Using traditional make +- Requirements: + * build-essential + * libasound2 + * libegl1-mesa-dev + * libgles2-mesa-dev + * libasound2-dev + * mesa-common-dev + * libgl1-mesa-dev +- From project root directory: ``` cd shell/linux make ``` +### Using snap +- Refer to our [snap README](https://github.com/reicast/reicast-emulator/tree/master/snap/README.md) + Translations ------------ New and updated translations are always appreciated! @@ -178,7 +181,6 @@ Our IRC channel is [#reicast @ chat.freenode.net](irc://chat.freenode.net/reicas The original reicast team consisted of drk||Raziel (mostly just writing code), PsyMan (debugging/testing and everything else) and a little bit of gb_away - Special thanks -------------- In previous iterations a lot of people have worked on this, notably David @@ -186,3 +188,4 @@ Miller (aka, ZeZu), the nullDC team, friends from #pcsx2 and all over the world [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/reicast/reicast-emulator/trend.png)](https://bitdeli.com/free "Bitdeli Badge") + diff --git a/bitrise.yml b/bitrise.yml index e29fae404..ccbafee94 100644 --- a/bitrise.yml +++ b/bitrise.yml @@ -54,8 +54,6 @@ workflows: - export_method: "$BITRISE_EXPORT_METHOD" - deploy-to-bitrise-io@1.3.19: {} - cache-push@2.1.1: {} - after_run: - - deploy-naomi app: envs: - opts: diff --git a/core/.gitignore b/core/.gitignore new file mode 100644 index 000000000..a948ed453 --- /dev/null +++ b/core/.gitignore @@ -0,0 +1 @@ +/version.h diff --git a/core/README.md b/core/README.md index 06303e4cf..269845e43 100644 --- a/core/README.md +++ b/core/README.md @@ -1,4 +1,15 @@ -core +# core/libdreamcast =========== -All of the interesting bits are here \ No newline at end of file +Here lies the core of our codebase. Everything that's OS inspecific rests here. +** Please check per directory README for more info ** + +### Some rudimentary categories are: +- hw -- DC Hardware Components Implementation +- nullDC.cpp -- NullDC, thy mighty child (also referenced as "debugger") +- emitter -- Cookie machine +- khronos -- Vulkan stuff +- oslib -- Codebase abstraction effort +- cfg -- Configuration backend structure +- reios -- (Our)Implementation of the DreamCast BIOS (Not functional) +- deps -- External C libraries (hackish, hand-written versions) diff --git a/core/archive/archive.cpp b/core/archive/archive.cpp index 904839a5f..1bef0f2a4 100644 --- a/core/archive/archive.cpp +++ b/core/archive/archive.cpp @@ -21,12 +21,15 @@ #include "archive.h" #include "7zArchive.h" +#ifndef _MSC_VER #include "ZipArchive.h" +#endif Archive *OpenArchive(const char *path) { std::string base_path(path); +#ifndef _MSC_VER Archive *sz_archive = new SzArchive(); if (sz_archive->Open(base_path.c_str()) || sz_archive->Open((base_path + ".7z").c_str()) || sz_archive->Open((base_path + ".7Z").c_str())) return sz_archive; @@ -36,6 +39,7 @@ Archive *OpenArchive(const char *path) if (zip_archive->Open(base_path.c_str()) || zip_archive->Open((base_path + ".zip").c_str()) || zip_archive->Open((base_path + ".ZIP").c_str())) return zip_archive; delete zip_archive; +#endif return NULL; } diff --git a/core/build.h b/core/build.h index de8a72034..1a8eb5626 100755 --- a/core/build.h +++ b/core/build.h @@ -129,10 +129,18 @@ #define DC_PLATFORM_AURORA 6 /* Needs to be done, Uses newer 300 mhz sh4 + 150 mhz pvr mbx SoC */ + //HOST_OS #define OS_WINDOWS 0x10000001 #define OS_LINUX 0x10000002 #define OS_DARWIN 0x10000003 +#define OS_IOS 0x10000004 +#define OS_ANDROID 0x10000005 + +#define OS_UWP 0x10000011 +#define OS_NSW_HOS 0x80000001 +#define OS_PS4_BSD 0x80000002 + //HOST_CPU #define CPU_X86 0x20000001 @@ -140,11 +148,16 @@ #define CPU_MIPS 0x20000003 #define CPU_X64 0x20000004 #define CPU_GENERIC 0x20000005 //used for pnacl, emscripten, etc -#define CPU_ARM64 0x20000006 +#define CPU_PPC 0x20000006 +#define CPU_PPC64 0x20000007 +#define CPU_ARM64 0x20000008 +#define CPU_MIPS64 0x20000009 //BUILD_COMPILER -#define COMPILER_VC 0x30000001 -#define COMPILER_GCC 0x30000002 +#define COMPILER_VC 0x30000001 +#define COMPILER_GCC 0x30000002 +#define COMPILER_CLANG 0x30000003 +#define COMPILER_INTEL 0x30000004 //FEAT_SHREC, FEAT_AREC, FEAT_DSPREC #define DYNAREC_NONE 0x40000001 @@ -154,6 +167,8 @@ //automatic +#ifndef CMAKE_BUILD + #if defined(_WIN32) && !defined(TARGET_WIN86) && !defined(TARGET_WIN64) #if !defined(_M_AMD64) && !defined(__x86_64__) #define TARGET_WIN86 @@ -235,6 +250,8 @@ #define FEAT_DSPREC DYNAREC_NONE #endif +#endif // !CMAKE_BUILD + #if defined(TARGET_NO_NIXPROF) #define FEAT_HAS_NIXPROF 0 @@ -304,3 +321,134 @@ #ifdef HOST_NO_AREC #error Dont use HOST_NO_AREC #endif + + +// Compiler Related + +#define COMPILER_VC_OR_CLANG_WIN32 ((BUILD_COMPILER == COMPILER_VC) || (BUILD_COMPILER == COMPILER_CLANG) && HOST_OS == OS_WINDOWS) + +#if BUILD_COMPILER!=COMPILER_VC +#define ATTR_USED __attribute__((used)) +#define ATTR_UNUSED __attribute__((used)) +#else +#define ATTR_USED +#define ATTR_UNUSED +#endif + + +// Some restrictions on FEAT_NO_RWX_PAGES +#if defined(FEAT_NO_RWX_PAGES) && FEAT_SHREC == DYNAREC_JIT +#if HOST_CPU != CPU_X64 && HOST_CPU != CPU_ARM64 +#error "FEAT_NO_RWX_PAGES Only implemented for X64 and ARMv8" +#endif +#endif + + +// TARGET PLATFORM + +#define RAM_SIZE_MAX (32*1024*1024) +#define VRAM_SIZE_MAX (16*1024*1024) +#define ARAM_SIZE_MAX (8*1024*1024) + +#if (DC_PLATFORM==DC_PLATFORM_DREAMCAST) + + #define BUILD_DREAMCAST 1 + + //DC : 16 mb ram, 8 mb vram, 2 mb aram, 2 mb bios, 128k flash + #define RAM_SIZE (16*1024*1024) + #define VRAM_SIZE (8*1024*1024) + #define ARAM_SIZE (2*1024*1024) + #define BIOS_SIZE (2*1024*1024) + #define FLASH_SIZE (128*1024) + + #define ROM_PREFIX "dc_" + #define ROM_NAMES + #define NVR_OPTIONAL 0 + +#elif (DC_PLATFORM==DC_PLATFORM_DEV_UNIT) + + #define BUILD_DEV_UNIT 1 + + //Devkit : 32 mb ram, 8? mb vram, 2? mb aram, 2? mb bios, ? flash + #define RAM_SIZE (32*1024*1024) + #define VRAM_SIZE (8*1024*1024) + #define ARAM_SIZE (2*1024*1024) + #define BIOS_SIZE (2*1024*1024) + #define FLASH_SIZE (128*1024) + + #define ROM_PREFIX "hkt_" + #define ROM_NAMES + #define NVR_OPTIONAL 0 + +#elif (DC_PLATFORM==DC_PLATFORM_NAOMI) + + //Naomi : 32 mb ram, 16 mb vram, 8 mb aram, 2 mb bios, ? flash + #define RAM_SIZE (32*1024*1024) + #define VRAM_SIZE (16*1024*1024) + #define ARAM_SIZE (8*1024*1024) + #define BIOS_SIZE (2*1024*1024) + #define BBSRAM_SIZE (32*1024) + + #define ROM_PREFIX "naomi_" + #define ROM_NAMES ";epr-21576d.bin" + #define NVR_OPTIONAL 1 + +#elif (DC_PLATFORM==DC_PLATFORM_NAOMI2) + + //Naomi2 : 32 mb ram, 16 mb vram, 8 mb aram, 2 mb bios, ? flash + #define RAM_SIZE (32*1024*1024) + #define VRAM_SIZE (16*1024*1024) + #define ARAM_SIZE (8*1024*1024) + #define BIOS_SIZE (2*1024*1024) + #define BBSRAM_SIZE (32*1024) + + #define ROM_PREFIX "n2_" + #define ROM_NAMES + #define NVR_OPTIONAL 1 + +#elif (DC_PLATFORM==DC_PLATFORM_ATOMISWAVE) + + #define BUILD_ATOMISWAVE 1 + + //Atomiswave : 16 mb ram, 8 mb vram, 8 mb aram, 128kb bios on flash, 128kb battery-backed ram + #define RAM_SIZE (16*1024*1024) + #define VRAM_SIZE (8*1024*1024) + #define ARAM_SIZE (8*1024*1024) + #define BIOS_SIZE (128*1024) + #define BBSRAM_SIZE (128*1024) + + #define ROM_PREFIX "aw_" + #define ROM_NAMES ";bios.ic23_l" + #define NVR_OPTIONAL 1 + +#else + #error invalid build config +#endif + +#define RAM_MASK (RAM_SIZE-1) +#define VRAM_MASK (VRAM_SIZE-1) +#define ARAM_MASK (ARAM_SIZE-1) +#define BIOS_MASK (BIOS_SIZE-1) + +#ifdef FLASH_SIZE +#define FLASH_MASK (FLASH_SIZE-1) +#endif + +#ifdef BBSRAM_SIZE +#define BBSRAM_MASK (BBSRAM_SIZE-1) +#endif + +#define GD_CLOCK 33868800 //GDROM XTAL -- 768fs + +#define AICA_CORE_CLOCK (GD_CLOCK*4/3) //[45158400] GD->PLL 3:4 -> AICA CORE -- 1024fs +#define ADAC_CLOCK (AICA_CORE_CLOCK/2) //[11289600] 44100*256, AICA CORE -> PLL 4:1 -> ADAC -- 256fs +#define AICA_ARM_CLOCK (AICA_CORE_CLOCK/2) //[22579200] AICA CORE -> PLL 2:1 -> ARM +#define AICA_SDRAM_CLOCK (GD_CLOCK*2) //[67737600] GD-> PLL 2 -> SDRAM +#define SH4_MAIN_CLOCK (200*1000*1000) //[200000000] XTal(13.5) -> PLL (33.3) -> PLL 1:6 (200) +#define SH4_RAM_CLOCK (100*1000*1000) //[100000000] XTal(13.5) -> PLL (33.3) -> PLL 1:3 (100) , also suplied to HOLLY chip +#define G2_BUS_CLOCK (25*1000*1000) //[25000000] from Holly, from SH4_RAM_CLOCK w/ 2 2:1 plls + +#if defined(GLES) && !defined(GLES3) +// Only use GL ES 2.0 API functions +#define GLES2 +#endif diff --git a/core/cfg/cfg.cpp b/core/cfg/cfg.cpp index 1d221ea8d..fda8959b2 100644 --- a/core/cfg/cfg.cpp +++ b/core/cfg/cfg.cpp @@ -12,7 +12,7 @@ static string cfgPath; static bool save_config = true; -static ConfigFile cfgdb; +static emucfg::ConfigFile cfgdb; static string game_id; static bool has_game_specific_config = false; diff --git a/core/cfg/cfg.h b/core/cfg/cfg.h index 75ffbbf4d..398cad5e3 100644 --- a/core/cfg/cfg.h +++ b/core/cfg/cfg.h @@ -1,7 +1,6 @@ #pragma once #include "types.h" - /* ** cfg* prototypes, if you pass NULL to a cfgSave* it will wipe out the section ** } if you pass it to lpKey it will wipe out that particular entry diff --git a/core/cfg/cl.cpp b/core/cfg/cl.cpp index cd2b67200..06bf74e31 100644 --- a/core/cfg/cl.cpp +++ b/core/cfg/cl.cpp @@ -6,8 +6,8 @@ */ #include -#include #include +#include #include "cfg/cfg.h" @@ -101,12 +101,14 @@ int setconfig(wchar** arg,int cl) int showhelp(wchar** arg,int cl) { - printf("Available commands :\n"); + printf("\nAvailable commands :\n"); - printf("-config section:key=value [, ..]: add a virtual config value\n Virtual config values won't be saved to the .cfg file\n unless a different value is written to em\nNote :\n You can specify many settings in the xx:yy=zz , gg:hh=jj , ...\n format.The spaces between the values and ',' are needed."); + printf("-config section:key=value [, ..]: add a virtual config value\n Virtual config values won't be saved to the .cfg file\n unless a different value is written to em\nNote :\n You can specify many settings in the xx:yy=zz , gg:hh=jj , ...\n format.The spaces between the values and ',' are needed.\n"); + printf("\n-help: show help info\n"); return 0; } + bool ParseCommandLine(int argc,wchar* argv[]) { cfgSetVirtual("config", "image", ""); @@ -114,14 +116,12 @@ bool ParseCommandLine(int argc,wchar* argv[]) wchar** arg=argv+1; while(cl>=0) { - if (stricmp(*arg,"-help")==0) + if (stricmp(*arg,"-help")==0 || stricmp(*arg,"--help")==0) { - int as=showhelp(arg,cl); - cl-=as; - arg+=as; + showhelp(arg,cl); return true; } - else if (stricmp(*arg,"-config")==0) + else if (stricmp(*arg,"-config")==0 || stricmp(*arg,"--config")==0) { int as=setconfig(arg,cl); cl-=as; diff --git a/core/cfg/ini.cpp b/core/cfg/ini.cpp index c0aa1945b..4cd9d05e5 100644 --- a/core/cfg/ini.cpp +++ b/core/cfg/ini.cpp @@ -3,6 +3,8 @@ wchar* trim_ws(wchar* str); +namespace emucfg { + /* ConfigEntry */ string ConfigEntry::get_string() @@ -299,3 +301,5 @@ void ConfigFile::delete_entry(const std::string& section_name, const std::string section->delete_entry(entry_name); } +} // namespace emucfg + diff --git a/core/cfg/ini.h b/core/cfg/ini.h index 81236399d..8c694dce6 100644 --- a/core/cfg/ini.h +++ b/core/cfg/ini.h @@ -2,6 +2,8 @@ #include "types.h" #include +namespace emucfg { + struct ConfigEntry { std::string value; std::string get_string(); @@ -45,3 +47,6 @@ struct ConfigFile { void delete_section(const std::string& section_name); void delete_entry(const std::string& section_name, const std::string& entry_name); }; + +} // namespace emucfg + diff --git a/core/core.mk b/core/core.mk index f91ecc7a7..4573ba5e7 100755 --- a/core/core.mk +++ b/core/core.mk @@ -1,26 +1,17 @@ -#LOCAL_PATH:= +#LOCAL_PATH:= #MFLAGS := -marm -march=armv7-a -mtune=cortex-a8 -mfpu=vfpv3-d16 -mfloat-abi=softfp #ASFLAGS := -march=armv7-a -mfpu=vfp-d16 -mfloat-abi=softfp -#LDFLAGS := -Wl,-Map,$(notdir $@).map,--gc-sections -Wl,-O3 -Wl,--sort-common +#LDFLAGS := -Wl,-Map,$(notdir $@).map,--gc-sections -Wl,-O3 -Wl,--sort-common RZDCY_SRC_DIR ?= $(call my-dir) -VERSION_SRC := $(RZDCY_SRC_DIR)/version/version.cpp +VERSION_HEADER := $(RZDCY_SRC_DIR)/version.h -RZDCY_MODULES := cfg/ hw/arm7/ hw/aica/ hw/holly/ hw/ hw/gdrom/ hw/maple/ hw/modem/ \ +RZDCY_MODULES := cfg/ hw/arm7/ hw/aica/ hw/holly/ hw/ hw/gdrom/ hw/maple/ \ hw/mem/ hw/pvr/ hw/sh4/ hw/sh4/interpr/ hw/sh4/modules/ plugins/ profiler/ oslib/ \ hw/extdev/ hw/arm/ hw/naomi/ imgread/ ./ deps/coreio/ deps/zlib/ deps/chdr/ deps/crypto/ \ deps/libelf/ deps/chdpsr/ arm_emitter/ rend/ reios/ deps/libpng/ deps/xbrz/ \ - deps/picotcp/modules/ deps/picotcp/stack/ deps/xxhash/ deps/libzip/ deps/imgui/ \ - archive/ input/ - -ifdef CHD5_LZMA - RZDCY_MODULES += deps/lzma/ -endif - -ifdef CHD5_FLAC - RZDCY_MODULES += deps/flac/src/libFLAC/ -endif + deps/xxhash/ deps/libzip/ deps/imgui/ archive/ input/ ifdef WEBUI RZDCY_MODULES += webui/ @@ -31,10 +22,6 @@ ifdef WEBUI endif endif -ifndef NO_REC - RZDCY_MODULES += hw/sh4/dyna/ -endif - ifndef NOT_ARM RZDCY_MODULES += rec-ARM/ endif @@ -66,10 +53,6 @@ else RZDCY_MODULES += rend/norend/ endif -ifdef HAS_SOFTREND - RZDCY_MODULES += rend/soft/ -endif - ifndef NO_NIXPROF RZDCY_MODULES += linux/nixprof/ endif @@ -90,12 +73,6 @@ ifdef FOR_WINDOWS RZDCY_MODULES += windows/ endif -RZDCY_FILES := $(foreach dir,$(addprefix $(RZDCY_SRC_DIR)/,$(RZDCY_MODULES)),$(wildcard $(dir)*.cpp)) -RZDCY_FILES += $(foreach dir,$(addprefix $(RZDCY_SRC_DIR)/,$(RZDCY_MODULES)),$(wildcard $(dir)*.cc)) -RZDCY_FILES += $(foreach dir,$(addprefix $(RZDCY_SRC_DIR)/,$(RZDCY_MODULES)),$(wildcard $(dir)*.c)) -RZDCY_FILES += $(foreach dir,$(addprefix $(RZDCY_SRC_DIR)/,$(RZDCY_MODULES)),$(wildcard $(dir)*.S)) -RZDCY_FILES += $(VERSION_SRC) - ifdef FOR_PANDORA RZDCY_CFLAGS := \ $(CFLAGS) -c -O3 \ @@ -134,11 +111,17 @@ RZDCY_CFLAGS := endif RZDCY_CFLAGS += -I$(RZDCY_SRC_DIR) -I$(RZDCY_SRC_DIR)/rend/gles -I$(RZDCY_SRC_DIR)/deps \ - -I$(RZDCY_SRC_DIR)/deps/picotcp/include -I$(RZDCY_SRC_DIR)/deps/picotcp/modules \ -I$(RZDCY_SRC_DIR)/deps/vixl -I$(RZDCY_SRC_DIR)/khronos +ifdef USE_MODEM + RZDCY_CFLAGS += -DENABLE_MODEM -I$(RZDCY_SRC_DIR)/deps/picotcp/include -I$(RZDCY_SRC_DIR)/deps/picotcp/modules + RZDCY_MODULES += hw/modem/ deps/picotcp/modules/ deps/picotcp/stack/ +endif + ifdef NO_REC - RZDCY_CFLAGS += -DTARGET_NO_REC + RZDCY_CFLAGS += -DTARGET_NO_REC +else + RZDCY_MODULES += hw/sh4/dyna/ endif ifdef USE_GLES @@ -147,17 +130,30 @@ endif ifdef HAS_SOFTREND RZDCY_CFLAGS += -DTARGET_SOFTREND + RZDCY_MODULES += rend/soft/ endif ifdef CHD5_FLAC - RZDCY_CFLAGS += -I$(RZDCY_SRC_DIR)/deps/flac/src/libFLAC/include/ -I$(RZDCY_SRC_DIR)/deps/flac/include + RZDCY_CFLAGS += -DCHD5_FLAC -I$(RZDCY_SRC_DIR)/deps/flac/src/libFLAC/include/ -I$(RZDCY_SRC_DIR)/deps/flac/include RZDCY_CFLAGS += -DPACKAGE_VERSION=\"1.3.2\" -DFLAC__HAS_OGG=0 -DFLAC__NO_DLL -DHAVE_LROUND -DHAVE_STDINT_H -DHAVE_STDLIB_H -DHAVE_SYS_PARAM_H + RZDCY_MODULES += deps/flac/src/libFLAC/ +endif + +# 7-Zip/LZMA settings (CHDv5) +ifdef CHD5_LZMA + RZDCY_MODULES += deps/lzma/ + RZDCY_CFLAGS += -D_7ZIP_ST -DCHD5_LZMA endif RZDCY_CXXFLAGS := $(RZDCY_CFLAGS) -fno-exceptions -fno-rtti -std=gnu++11 -$(VERSION_SRC): - echo "const char *version = \"`git describe --tags --always`\";" > $(VERSION_SRC) - echo "const char *git_hash = \"`git rev-parse --short HEAD`\";" >> $(VERSION_SRC) - echo "const char *build_date = \"`date '+%Y-%m-%d %H:%M:%S %Z'`\";" >> $(VERSION_SRC) +RZDCY_FILES := $(foreach dir,$(addprefix $(RZDCY_SRC_DIR)/,$(RZDCY_MODULES)),$(wildcard $(dir)*.cpp)) +RZDCY_FILES += $(foreach dir,$(addprefix $(RZDCY_SRC_DIR)/,$(RZDCY_MODULES)),$(wildcard $(dir)*.cc)) +RZDCY_FILES += $(foreach dir,$(addprefix $(RZDCY_SRC_DIR)/,$(RZDCY_MODULES)),$(wildcard $(dir)*.c)) +RZDCY_FILES += $(foreach dir,$(addprefix $(RZDCY_SRC_DIR)/,$(RZDCY_MODULES)),$(wildcard $(dir)*.S)) + +$(VERSION_HEADER): + echo "#define REICAST_VERSION \"`git describe --tags --always`\"" > $(VERSION_HEADER) + echo "#define GIT_HASH \"`git rev-parse --short HEAD`\"" >> $(VERSION_HEADER) + echo "#define BUILD_DATE \"`date '+%Y-%m-%d %H:%M:%S %Z'`\"" >> $(VERSION_HEADER) diff --git a/core/deps/dirent/dirent.c b/core/deps/dirent/dirent.c new file mode 100644 index 000000000..8d496474c --- /dev/null +++ b/core/deps/dirent/dirent.c @@ -0,0 +1,148 @@ +/* + + Implementation of POSIX directory browsing functions and types for Win32. + + Author: Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com) + History: Created March 1997. Updated June 2003 and July 2012. + Rights: See end of file. + +*/ + +#include "dirent.h" +#include +#include /* _findfirst and _findnext set errno iff they return -1 */ +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef ptrdiff_t handle_type; /* C99's intptr_t not sufficiently portable */ + + struct DIR + { + handle_type handle; /* -1 for failed rewind */ + struct _finddata_t info; + struct dirent result; /* d_name null iff first time */ + char *name; /* null-terminated char string */ + }; + + DIR *opendir(const char *name) + { + DIR *dir = 0; + + if (name && name[0]) + { + size_t base_length = strlen(name); + const char *all = /* search pattern must end with suitable wildcard */ + strchr("/\\", name[base_length - 1]) ? "*" : "/*"; + + if ((dir = (DIR *)malloc(sizeof *dir)) != 0 && + (dir->name = (char *)malloc(base_length + strlen(all) + 1)) != 0) + { + strcat(strcpy(dir->name, name), all); + + if ((dir->handle = + (handle_type)_findfirst(dir->name, &dir->info)) != -1) + { + dir->result.d_name = 0; + } + else /* rollback */ + { + free(dir->name); + free(dir); + dir = 0; + } + } + else /* rollback */ + { + free(dir); + dir = 0; + errno = ENOMEM; + } + } + else + { + errno = EINVAL; + } + + return dir; + } + + int closedir(DIR *dir) + { + int result = -1; + + if (dir) + { + if (dir->handle != -1) + { + result = _findclose(dir->handle); + } + + free(dir->name); + free(dir); + } + + if (result == -1) /* map all errors to EBADF */ + { + errno = EBADF; + } + + return result; + } + + struct dirent *readdir(DIR *dir) + { + struct dirent *result = 0; + + if (dir && dir->handle != -1) + { + if (!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) + { + result = &dir->result; + result->d_name = dir->info.name; + } + } + else + { + errno = EBADF; + } + + return result; + } + + void rewinddir(DIR *dir) + { + if (dir && dir->handle != -1) + { + _findclose(dir->handle); + dir->handle = (handle_type)_findfirst(dir->name, &dir->info); + dir->result.d_name = 0; + } + else + { + errno = EBADF; + } + } + +#ifdef __cplusplus +} +#endif + +/* + + Copyright Kevlin Henney, 1997, 2003, 2012. All rights reserved. + + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose is hereby granted without fee, provided + that this copyright and permissions notice appear in all copies and + derivatives. + + This software is supplied "as is" without express or implied warranty. + + But that said, if there are any problems please get in touch. + +*/ diff --git a/core/deps/dirent/dirent.h b/core/deps/dirent/dirent.h new file mode 100644 index 000000000..192be112f --- /dev/null +++ b/core/deps/dirent/dirent.h @@ -0,0 +1,50 @@ +#ifndef DIRENT_INCLUDED +#define DIRENT_INCLUDED + +/* + + Declaration of POSIX directory browsing functions and types for Win32. + + Author: Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com) + History: Created March 1997. Updated June 2003. + Rights: See end of file. + +*/ + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct DIR DIR; + + struct dirent + { + char *d_name; + }; + + DIR *opendir(const char *); + int closedir(DIR *); + struct dirent *readdir(DIR *); + void rewinddir(DIR *); + + /* + + Copyright Kevlin Henney, 1997, 2003. All rights reserved. + + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose is hereby granted without fee, provided + that this copyright and permissions notice appear in all copies and + derivatives. + + This software is supplied "as is" without express or implied warranty. + + But that said, if there are any problems please get in touch. + + */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/core/emitter/README.md b/core/emitter/README.md new file mode 100644 index 000000000..e3b9d8560 --- /dev/null +++ b/core/emitter/README.md @@ -0,0 +1,6 @@ +# Our Emitter +### Oh god , x86 is a sooo badly designed opcode arch -_- +--- +Emitters are the place where you have functions that you give symbolic + instructions and you get binaries out. + diff --git a/core/hw/aica/aica.cpp b/core/hw/aica/aica.cpp index df2f2b6c7..4ef983a31 100644 --- a/core/hw/aica/aica.cpp +++ b/core/hw/aica/aica.cpp @@ -1,4 +1,5 @@ #include "aica.h" +#include "aica_if.h" #include "sgc_if.h" #include "aica_mem.h" #include @@ -180,6 +181,7 @@ template void WriteAicaReg<2>(u32 reg,u32 data); s32 libAICA_Init() { init_mem(); + aica_Init(); verify(sizeof(*CommonData)==0x508); verify(sizeof(*DSPData)==0x15C8); @@ -203,9 +205,12 @@ s32 libAICA_Init() return rv_ok; } -void libAICA_Reset(bool m) +void libAICA_Reset(bool manual) { + if (!manual) + init_mem(); sgc_Init(); + aica_Reset(manual); } void libAICA_Term() diff --git a/core/hw/aica/aica_if.cpp b/core/hw/aica/aica_if.cpp index 49c09f844..c1799793b 100644 --- a/core/hw/aica/aica_if.cpp +++ b/core/hw/aica/aica_if.cpp @@ -13,11 +13,12 @@ #include -VArray2 aica_ram; +VLockedMemory aica_ram; u32 VREG;//video reg =P u32 ARMRST;//arm reset reg u32 rtc_EN=0; int dma_sched_id; +u32 RealTimeClock; u32 GetRTC_now() { @@ -39,9 +40,9 @@ u32 ReadMem_aica_rtc(u32 addr,u32 sz) switch( addr & 0xFF ) { case 0: - return settings.dreamcast.RTC>>16; + return RealTimeClock>>16; case 4: - return settings.dreamcast.RTC &0xFFFF; + return RealTimeClock &0xFFFF; case 8: return 0; } @@ -57,16 +58,16 @@ void WriteMem_aica_rtc(u32 addr,u32 data,u32 sz) case 0: if (rtc_EN) { - settings.dreamcast.RTC&=0xFFFF; - settings.dreamcast.RTC|=(data&0xFFFF)<<16; + RealTimeClock&=0xFFFF; + RealTimeClock|=(data&0xFFFF)<<16; rtc_EN=0; } return; case 4: if (rtc_EN) { - settings.dreamcast.RTC&=0xFFFF0000; - settings.dreamcast.RTC|= data&0xFFFF; + RealTimeClock&=0xFFFF0000; + RealTimeClock|= data&0xFFFF; //TODO: Clean the internal timer ? } return; @@ -153,15 +154,14 @@ void WriteMem_aica_reg(u32 addr,u32 data,u32 sz) //Init/res/term void aica_Init() { - //mmnnn ? gotta fill it w/ something + RealTimeClock = GetRTC_now(); } void aica_Reset(bool Manual) { - if (!Manual) - { - aica_ram.Zero(); - } + aica_Init(); + VREG = 0; + ARMRST = 0; } void aica_Term() @@ -183,6 +183,7 @@ int dma_end_sched(int tag, int cycl, int jitt) SB_ADST = 0x00000000;//dma done SB_ADLEN = 0x00000000; + // indicate that dma is not happening, or has been paused SB_ADSUSP |= 0x10; asic_RaiseInterrupt(holly_SPU_DMA); @@ -190,7 +191,6 @@ int dma_end_sched(int tag, int cycl, int jitt) return 0; } - void Write_SB_ADST(u32 addr, u32 data) { //0x005F7800 SB_ADSTAG RW AICA:G2-DMA G2 start address @@ -227,14 +227,24 @@ void Write_SB_ADST(u32 addr, u32 data) WriteMem32_nommu(dst+i,data); } */ + + // idicate that dma is in progress SB_ADSUSP &= ~0x10; - // Schedule the end of DMA transfer interrupt - int cycles = len * (SH4_MAIN_CLOCK / 2 / 25000000); // 16 bits @ 25 MHz - if (cycles < 4096) - dma_end_sched(0, 0, 0); + if (!settings.aica.OldSyncronousDma) + { + + // Schedule the end of DMA transfer interrupt + int cycles = len * (SH4_MAIN_CLOCK / 2 / 25000000); // 16 bits @ 25 MHz + if (cycles < 4096) + dma_end_sched(0, 0, 0); + else + sh4_sched_request(dma_sched_id, cycles); + } else - sh4_sched_request(dma_sched_id, cycles); + { + dma_end_sched(0, 0, 0); + } } } } diff --git a/core/hw/aica/aica_if.h b/core/hw/aica/aica_if.h index 8c6009a66..13ee6fd97 100644 --- a/core/hw/aica/aica_if.h +++ b/core/hw/aica/aica_if.h @@ -2,7 +2,8 @@ #include "types.h" extern u32 VREG; -extern VArray2 aica_ram; +extern VLockedMemory aica_ram; +extern u32 RealTimeClock; u32 ReadMem_aica_rtc(u32 addr,u32 sz); void WriteMem_aica_rtc(u32 addr,u32 data,u32 sz); u32 ReadMem_aica_reg(u32 addr,u32 sz); @@ -17,4 +18,4 @@ void aica_Term(); void aica_sb_Init(); void aica_sb_Reset(bool Manual); -void aica_sb_Term(); \ No newline at end of file +void aica_sb_Term(); diff --git a/core/hw/aica/dsp.cpp b/core/hw/aica/dsp.cpp index cf3899aee..676c03978 100644 --- a/core/hw/aica/dsp.cpp +++ b/core/hw/aica/dsp.cpp @@ -186,13 +186,7 @@ void dsp_init() dsp.RBP=0; dsp.regs.MDEC_CT=1; - - //os_MakeExecutable(dsp.DynCode,sizeof(dsp.DynCode)); -#if HOST_OS == OS_WINDOWS - DWORD old; - VirtualProtect(dsp.DynCode, sizeof(dsp.DynCode), PAGE_EXECUTE_READWRITE, &old); -#endif - + mem_region_set_exec(dsp.DynCode, sizeof(dsp.DynCode)); } void dsp_recompile(); diff --git a/core/hw/aica/dsp_arm64.cpp b/core/hw/aica/dsp_arm64.cpp index ba5515ed4..77b5ef39b 100644 --- a/core/hw/aica/dsp_arm64.cpp +++ b/core/hw/aica/dsp_arm64.cpp @@ -21,13 +21,12 @@ #if HOST_CPU == CPU_ARM64 && FEAT_DSPREC != DYNAREC_NONE -#include #include "dsp.h" #include "hw/aica/aica_if.h" #include "deps/vixl/aarch64/macro-assembler-aarch64.h" using namespace vixl::aarch64; -extern void Arm64CacheFlush(void* start, void* end); +extern void vmem_platform_flush_cache(void *icache_start, void *icache_end, void *dcache_start, void *dcache_end); class DSPAssembler : public MacroAssembler { @@ -54,9 +53,9 @@ public: Stp(xzr, xzr, MemOperand(x0, 48)); Ret(); FinalizeCode(); -#ifdef _ANDROID - Arm64CacheFlush(GetBuffer()->GetStartAddress(), GetBuffer()->GetEndAddress()); -#endif + vmem_platform_flush_cache( + GetBuffer()->GetStartAddress(), GetBuffer()->GetEndAddress(), + GetBuffer()->GetStartAddress(), GetBuffer()->GetEndAddress()); return; } @@ -387,9 +386,9 @@ public: #endif FinalizeCode(); -#ifdef _ANDROID - Arm64CacheFlush(GetBuffer()->GetStartAddress(), GetBuffer()->GetEndAddress()); -#endif + vmem_platform_flush_cache( + GetBuffer()->GetStartAddress(), GetBuffer()->GetEndAddress(), + GetBuffer()->GetStartAddress(), GetBuffer()->GetEndAddress()); } private: @@ -522,7 +521,7 @@ void dsp_init() dsp.regs.MDEC_CT = 1; dsp.dyndirty = true; - if (mprotect(dsp.DynCode, sizeof(dsp.DynCode), PROT_EXEC | PROT_READ | PROT_WRITE)) + if (!mem_region_set_exec(dsp.DynCode, sizeof(dsp.DynCode))) { perror("Couldn’t mprotect DSP code"); die("mprotect failed in arm64 dsp"); diff --git a/core/hw/aica/sgc_if.cpp b/core/hw/aica/sgc_if.cpp index 1e9f90b0a..4b970ee32 100755 --- a/core/hw/aica/sgc_if.cpp +++ b/core/hw/aica/sgc_if.cpp @@ -751,7 +751,10 @@ __forceinline SampleType DecodeADPCM(u32 sample,s32 prev,s32& quant) u32 data=sample&7; /*(1 - 2 * L4) * (L3 + L2/2 +L1/4 + 1/8) * quantized width (ƒΆn) + decode value (Xn - 1) */ - SampleType rv = prev + sign*((quant*adpcm_scale[data])>>3); + SampleType rv = (quant * adpcm_scale[data]) >> 3; + if (rv > 0x7FFF) + rv = 0x7FFF; + rv = sign * rv + prev; quant = (quant * adpcm_qs[data])>>8; diff --git a/core/hw/arm7/arm-new.h b/core/hw/arm7/arm-new.h index c670b0278..982c099fb 100644 --- a/core/hw/arm7/arm-new.h +++ b/core/hw/arm7/arm-new.h @@ -17,6 +17,7 @@ // along with this program; if not, write to the Free Software Foundation, // Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + #ifdef BKPT_SUPPORT #define CONSOLE_OUTPUT(a,b) \ extern void (*dbgOutput)(char *, u32);\ diff --git a/core/hw/arm7/arm64.cpp b/core/hw/arm7/arm64.cpp index bcaf7d70c..e712855bd 100644 --- a/core/hw/arm7/arm64.cpp +++ b/core/hw/arm7/arm64.cpp @@ -28,7 +28,7 @@ using namespace vixl::aarch64; //#include "deps/vixl/aarch32/disasm-aarch32.h" -extern void Arm64CacheFlush(void* start, void* end); +extern void vmem_platform_flush_cache(void *icache_start, void *icache_end, void *dcache_start, void *dcache_end); extern u32 arm_single_op(u32 opcode); extern "C" void arm_dispatch(); extern "C" void arm_exit(); @@ -41,7 +41,7 @@ extern reg_pair arm_Reg[RN_ARM_REG_COUNT]; MacroAssembler *assembler; extern "C" void armFlushICache(void *bgn, void *end) { - Arm64CacheFlush(bgn, end); + vmem_platform_flush_cache(bgn, end, bgn, end); } static MemOperand arm_reg_operand(u32 regn) @@ -143,7 +143,9 @@ void armv_end(void* codestart, u32 cycl) assembler->FinalizeCode(); verify(assembler->GetBuffer()->GetCursorOffset() <= assembler->GetBuffer()->GetCapacity()); - Arm64CacheFlush(codestart, assembler->GetBuffer()->GetEndAddress()); + vmem_platform_flush_cache( + codestart, assembler->GetBuffer()->GetEndAddress(), + codestart, assembler->GetBuffer()->GetEndAddress()); icPtr += assembler->GetBuffer()->GetSizeInBytes(); #if 0 @@ -499,8 +501,13 @@ __asm__ ( ".hidden arm_dispatch \n" "arm_dispatch: \n\t" "ldp w0, w1, [x28, #184] \n\t" // load Next PC, interrupt - - "ubfx w2, w0, #2, #21 \n\t" // w2 = pc >> 2. Note: assuming address space <= 8 MB (23 bits) +#if ARAM_SIZE == 2*1024*1024 + "ubfx w2, w0, #2, #19 \n\t" // w2 = pc >> 2. Note: assuming address space == 2 MB (21 bits) +#elif ARAM_SIZE == 8*1024*1024 + "ubfx w2, w0, #2, #21 \n\t" // w2 = pc >> 2. Note: assuming address space == 8 MB (23 bits) +#else +#error Unsupported AICA RAM size +#endif "cbnz w1, arm_dofiq \n\t" // if interrupt pending, handle it "add x2, x26, x2, lsl #3 \n\t" // x2 = EntryPoints + pc << 1 diff --git a/core/hw/arm7/arm7.cpp b/core/hw/arm7/arm7.cpp index 9906f96de..4d5f0e117 100644 --- a/core/hw/arm7/arm7.cpp +++ b/core/hw/arm7/arm7.cpp @@ -69,12 +69,6 @@ bool Arm7Enabled=false; u8 cpuBitsSet[256]; -bool intState = false; -bool stopState = false; -bool holdState = false; - - - void CPUSwitchMode(int mode, bool saveState, bool breakLoop=true); extern "C" void CPUFiq(); void CPUUpdateCPSR(); @@ -428,7 +422,7 @@ void arm_Run(u32 CycleCount) { } #else // FEAT_AREC != DYNAREC_NONE -#if HOST_OS == OS_LINUX || HOST_OS == OS_DARWIN +#if HOST_OS == OS_DARWIN #include #endif @@ -1563,10 +1557,6 @@ naked void arm_exit() * */ -//mprotect and stuff .. - -#include - void armEmit32(u32 emit32) { if (icPtr >= (ICache+ICacheSize-1024)) @@ -2189,25 +2179,12 @@ void armt_init() ICache = (u8*)mmap(ICache, ICacheSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_ANON, 0, 0); #endif -#if HOST_OS == OS_WINDOWS - DWORD old; - VirtualProtect(ICache,ICacheSize,PAGE_EXECUTE_READWRITE,&old); -#elif HOST_OS == OS_LINUX || HOST_OS == OS_DARWIN - - printf("\n\t ARM7_TCB addr: %p | from: %p | addr here: %p\n", ICache, ARM7_TCB, armt_init); - - if (mprotect(ICache, ICacheSize, PROT_EXEC|PROT_READ|PROT_WRITE)) - { - perror("\n\tError - Couldn’t mprotect ARM7_TCB!"); - verify(false); - } + mem_region_set_exec(ICache, ICacheSize); #if TARGET_IPHONE memset((u8*)mmap(ICache, ICacheSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_ANON, 0, 0),0xFF,ICacheSize); #else memset(ICache,0xFF,ICacheSize); -#endif - #endif icPtr=ICache; diff --git a/core/hw/flashrom/flashrom.h b/core/hw/flashrom/flashrom.h index 4b915385d..8e0e3e665 100644 --- a/core/hw/flashrom/flashrom.h +++ b/core/hw/flashrom/flashrom.h @@ -21,7 +21,7 @@ struct MemChip this->mask=size-1;//must be power of 2 this->write_protect_size = write_protect_size; } - ~MemChip() { delete[] data; } + virtual ~MemChip() { delete[] data; } virtual u8 Read8(u32 addr) { @@ -118,14 +118,11 @@ struct MemChip printf("Saved %s as %s\n\n",path,title.c_str()); } + virtual void Reset() {} }; struct RomChip : MemChip { RomChip(u32 sz, u32 write_protect_size = 0) : MemChip(sz, write_protect_size) {} - void Reset() - { - //nothing, its permanent read only ;p - } void Write(u32 addr,u32 data,u32 sz) { die("Write to RomChip is not possible, address=%x, data=%x, size=%d"); @@ -135,10 +132,6 @@ struct SRamChip : MemChip { SRamChip(u32 sz, u32 write_protect_size = 0) : MemChip(sz, write_protect_size) {} - void Reset() - { - //nothing, its battery backed up storage - } void Write(u32 addr,u32 val,u32 sz) { addr&=mask; @@ -234,7 +227,7 @@ struct DCFlashChip : MemChip }; FlashState state; - void Reset() + virtual void Reset() override { //reset the flash chip state state = FS_Normal; @@ -378,19 +371,19 @@ struct DCFlashChip : MemChip else if ((val & 0xff) == 0x30) { // sector erase - addr = max(addr, write_protect_size); -#if DC_PLATFORM != DC_PLATFORM_ATOMISWAVE - printf("Erase Sector %08X! (%08X)\n",addr,addr&(~0x3FFF)); - memset(&data[addr&(~0x3FFF)],0xFF,0x4000); -#else - // AtomisWave's Macronix 29L001mc has 64k blocks - printf("Erase Sector %08X! (%08X)\n",addr,addr&(~0xFFFF)); - u8 save[0x2000]; - // this area is write-protected on AW - memcpy(save, data + 0x1a000, 0x2000); - memset(&data[addr&(~0xFFFF)], 0xFF, 0x10000); - memcpy(data + 0x1a000, save, 0x2000); + if (addr >= write_protect_size) + { +#if DC_PLATFORM == DC_PLATFORM_ATOMISWAVE + u8 save[0x2000]; + // this area is write-protected on AW + memcpy(save, data + 0x1a000, 0x2000); #endif + printf("Erase Sector %08X! (%08X)\n",addr,addr&(~0x3FFF)); + memset(&data[addr&(~0x3FFF)],0xFF,0x4000); +#if DC_PLATFORM == DC_PLATFORM_ATOMISWAVE + memcpy(data + 0x1a000, save, 0x2000); +#endif + } state = FS_Normal; } else diff --git a/core/hw/gdrom/README.md b/core/hw/gdrom/README.md new file mode 100644 index 000000000..4e7d04164 --- /dev/null +++ b/core/hw/gdrom/README.md @@ -0,0 +1,8 @@ +# GD-ROM: Gigabyte Disc Read-Only Memory + +- This is the GD-ROM emulation part v3. v1 was unusable and v2 was our initial + release. + +### Notes: + - Technical approach is wrong + - Some secondary stuff are not (really) implemented diff --git a/core/hw/gdrom/gdromv3.cpp b/core/hw/gdrom/gdromv3.cpp index fac898596..e714f9c22 100644 --- a/core/hw/gdrom/gdromv3.cpp +++ b/core/hw/gdrom/gdromv3.cpp @@ -256,6 +256,7 @@ void gd_set_state(gd_states state) void gd_setdisc() { + cdda.playing = false; DiscType newd = (DiscType)libGDR_GetDiscType(); switch(newd) @@ -321,6 +322,13 @@ u32 GetFAD(u8* data, bool msf) void libCore_gdrom_disc_change() { gd_setdisc(); + read_params = { 0 }; + set_mode_offset = 0; + packet_cmd = { 0 }; + memset(&read_buff, 0, sizeof(read_buff)); + pio_buff = { gds_waitcmd, 0 }; + ata_cmd = { 0 }; + cdda = { 0 }; } //This handles the work of setting up the pio regs/state :) diff --git a/core/hw/holly/holly_intc.cpp b/core/hw/holly/holly_intc.cpp index ec4e48e80..fc73b3183 100644 --- a/core/hw/holly/holly_intc.cpp +++ b/core/hw/holly/holly_intc.cpp @@ -74,19 +74,19 @@ void RaiseAsicErr(HollyInterruptID inter) void asic_RaiseInterrupt(HollyInterruptID inter) { - u8 m=inter>>8; - switch(m) - { - case 0: - RaiseAsicNormal(inter); - break; - case 1: - RaiseAsicExt(inter); - break; - case 2: - RaiseAsicErr(inter); - break; - } + u8 m=inter>>8; + switch(m) + { + case 0: + RaiseAsicNormal(inter); + break; + case 1: + RaiseAsicExt(inter); + break; + case 2: + RaiseAsicErr(inter); + break; + } } u32 Read_SB_ISTNRM(u32 addr) diff --git a/core/hw/holly/sb.cpp b/core/hw/holly/sb.cpp index 53064c4d6..4c962b6f6 100644 --- a/core/hw/holly/sb.cpp +++ b/core/hw/holly/sb.cpp @@ -14,6 +14,8 @@ #include "hw/naomi/naomi.h" +extern void dc_request_reset(); + Array sb_regs(0x540); //(addr>= 0x005F6800) && (addr<=0x005F7CFF) -> 0x1500 bytes -> 0x540 possible registers , 125 actually exist only @@ -194,7 +196,8 @@ void SB_SFRES_write32(u32 addr, u32 data) { if ((u16)data==0x7611) { - printf("SB/HOLLY: System reset requested -- but cannot SOFT_RESET\n"); + printf("SB/HOLLY: System reset requested\n"); + dc_request_reset(); } } void sb_Init() @@ -780,7 +783,7 @@ void sb_Init() maple_Init(); aica_sb_Init(); -#if DC_PLATFORM == DC_PLATFORM_DREAMCAST +#if DC_PLATFORM == DC_PLATFORM_DREAMCAST && defined(ENABLE_MODEM) ModemInit(); #endif } diff --git a/core/hw/holly/sb_mem.cpp b/core/hw/holly/sb_mem.cpp index 1e7fec64b..21c593c43 100644 --- a/core/hw/holly/sb_mem.cpp +++ b/core/hw/holly/sb_mem.cpp @@ -41,7 +41,7 @@ bool LoadRomFiles(const string& root) { #if DC_PLATFORM == DC_PLATFORM_DREAMCAST // Dreamcast absolutely needs a BIOS - msgboxf("Unable to find bios in \n%s\nExiting...", MBX_ICONERROR, root.c_str()); + msgboxf("Unable to find bios in %s. Exiting...", MBX_ICONERROR, root.c_str()); return false; #endif } @@ -85,7 +85,9 @@ bool LoadRomFiles(const string& root) if (settings.dreamcast.language <= 5) syscfg.lang = settings.dreamcast.language; - sys_nvmem.WriteBlock(FLASH_PT_USER, FLASH_USER_SYSCFG, &syscfg); + if (sys_nvmem.WriteBlock(FLASH_PT_USER, FLASH_USER_SYSCFG, &syscfg) != 1) + printf("Failed to save time and language to flash RAM\n"); + #endif #if DC_PLATFORM == DC_PLATFORM_ATOMISWAVE @@ -206,7 +208,9 @@ T DYNACALL ReadMem_area0(u32 addr) } else if (likely((addr>= 0x005F8000) && (addr<=0x005F9FFF))) // :TA / PVR Core Reg. { - if (sz != 4) return 0; // House of the Dead 2 + if (sz != 4) + // House of the Dead 2 + return 0; return (T)pvr_ReadReg(addr); } } @@ -215,8 +219,10 @@ T DYNACALL ReadMem_area0(u32 addr) { #if DC_PLATFORM == DC_PLATFORM_NAOMI || DC_PLATFORM == DC_PLATFORM_ATOMISWAVE return (T)libExtDevice_ReadMem_A0_006(addr, sz); -#else +#elif defined(ENABLE_MODEM) return (T)ModemReadMem_A0_006(addr, sz); +#else + return (T)0; #endif } //map 0x0060 to 0x006F @@ -300,7 +306,7 @@ void DYNACALL WriteMem_area0(u32 addr,T data) { #if DC_PLATFORM == DC_PLATFORM_NAOMI || DC_PLATFORM == DC_PLATFORM_ATOMISWAVE libExtDevice_WriteMem_A0_006(addr, data, sz); -#else +#elif defined(ENABLE_MODEM) ModemWriteMem_A0_006(addr, data, sz); #endif } @@ -344,6 +350,10 @@ void sh4_area0_Init() void sh4_area0_Reset(bool Manual) { sb_Reset(Manual); + sys_rom.Reset(); +#if defined(FLASH_SIZE) || defined(BBSRAM_SIZE) + sys_nvmem.Reset(); +#endif } void sh4_area0_Term() diff --git a/core/hw/maple/maple_cfg.cpp b/core/hw/maple/maple_cfg.cpp index 5a02ef11b..4f8c9d2d6 100644 --- a/core/hw/maple/maple_cfg.cpp +++ b/core/hw/maple/maple_cfg.cpp @@ -4,6 +4,7 @@ #include "maple_devs.h" #include "maple_cfg.h" #include "cfg/cfg.h" +#include "hw/naomi/naomi_cart.h" #define HAS_VMU /* @@ -28,13 +29,31 @@ extern u16 kcode[4]; extern u32 vks[4]; extern s8 joyx[4],joyy[4]; extern u8 rt[4],lt[4]; -extern bool naomi_test_button; u8 GetBtFromSgn(s8 val) { return val+128; } +u32 awave_button_mapping[] = { + AWAVE_SERVICE_KEY, // DC_BTN_C + AWAVE_BTN1_KEY, // DC_BTN_B + AWAVE_BTN0_KEY, // DC_BTN_A + AWAVE_START_KEY, // DC_BTN_START + AWAVE_UP_KEY, // DC_DPAD_UP + AWAVE_DOWN_KEY, // DC_DPAD_DOWN + AWAVE_LEFT_KEY, // DC_DPAD_LEFT + AWAVE_RIGHT_KEY, // DC_DPAD_RIGHT + AWAVE_TEST_KEY, // DC_BTN_Z + AWAVE_BTN3_KEY, // DC_BTN_Y + AWAVE_BTN2_KEY, // DC_BTN_X + AWAVE_COIN_KEY, // DC_BTN_D + // DC_DPAD2_UP + // DC_DPAD2_DOWN + // DC_DPAD2_LEFT + // DC_DPAD2_RIGHT +}; + struct MapleConfigMap : IMapleConfigMap { maple_device* dev; @@ -59,17 +78,32 @@ struct MapleConfigMap : IMapleConfigMap pjs->kcode=kcode[player_num]; #if DC_PLATFORM == DC_PLATFORM_DREAMCAST - pjs->kcode |= 0xF901; -#elif DC_PLATFORM == DC_PLATFORM_ATOMISWAVE - if (naomi_test_button) - pjs->kcode &= ~(1 << 14); -// if (!(pjs->kcode & (1 << 9))) // Hack (Y -> service btn) -// pjs->kcode &= ~(1 << 13); -#endif + pjs->kcode |= 0xF901; // mask off DPad2, C, D and Z pjs->joy[PJAI_X1]=GetBtFromSgn(joyx[player_num]); pjs->joy[PJAI_Y1]=GetBtFromSgn(joyy[player_num]); pjs->trigger[PJTI_R]=rt[player_num]; pjs->trigger[PJTI_L]=lt[player_num]; +#elif DC_PLATFORM == DC_PLATFORM_ATOMISWAVE + pjs->kcode = 0xFFFF; + for (int i = 0; i < 16; i++) + { + if ((kcode[player_num] & (1 << i)) == 0) + pjs->kcode &= ~awave_button_mapping[i]; + } + pjs->joy[PJAI_X1] = GetBtFromSgn(joyx[player_num]); + if (NaomiGameInputs != NULL && NaomiGameInputs->axes[1].name != NULL && NaomiGameInputs->axes[1].type == Half) + { + // Driving games: put axis 2 on RT (accel) and axis 3 on LT (brake) + pjs->joy[PJAI_Y1] = rt[player_num]; + pjs->joy[PJAI_X2] = lt[player_num]; + } + else + { + pjs->joy[PJAI_Y1] = GetBtFromSgn(joyy[player_num]); + pjs->joy[PJAI_X2] = rt[player_num]; + pjs->joy[PJAI_Y2] = lt[player_num]; + } +#endif } void SetImage(void* img) { @@ -77,6 +111,16 @@ struct MapleConfigMap : IMapleConfigMap } }; +bool maple_atomiswave_coin_chute(int slot) +{ + for (int i = 0; i < 16; i++) + { + if ((kcode[slot] & (1 << i)) == 0 && awave_button_mapping[i] == AWAVE_COIN_KEY) + return true; + } + return false; +} + void mcfg_Create(MapleDeviceType type, u32 bus, u32 port, s32 player_num = -1) { if (MapleDevices[bus][port] != NULL) @@ -100,17 +144,32 @@ void mcfg_CreateAtomisWaveControllers() // Then other devices on port 2 and 3 for analog axes, light guns, ... mcfg_Create(MDT_SegaController, 0, 5); mcfg_Create(MDT_SegaController, 1, 5); -// mcfg_Create(MDT_SegaController, 2, 5, 0); -// mcfg_Create(MDT_SegaController, 3, 5, 1); -// mcfg_Create(MDT_LightGun, 2, 5, 0); -// mcfg_Create(MDT_LightGun, 3, 5, 1); -// mcfg_Create(MDT_Mouse, 2, 5, 0); - // Guilty Gear Isuka (4P) needs 4 std controllers - // Faster Than Speed needs 1 std controller on port 0 (digital inputs) and one on port 2 (analog axes) - // Maximum Speed same - // Clay Challenge needs 2 std controllers on port 0 & 1 (digital in) and light guns on port 2 & 3 - // Sports Shooting same - // Sega Bass Fishing Challenge needs a mouse (track-ball) on port 3 + if (NaomiGameInputs != NULL && NaomiGameInputs->axes[0].name != NULL) + { + // Game needs analog axes + mcfg_Create(MDT_SegaController, 2, 5, 0); + mcfg_Create(MDT_SegaController, 3, 5, 1); + // Faster Than Speed needs 1 std controller on port 0 (digital inputs) and one on port 2 (analog axes) + // Maximum Speed same + } + else if (settings.input.JammaSetup == 1) + { + // 4 players + mcfg_Create(MDT_SegaController, 2, 5); + mcfg_Create(MDT_SegaController, 3, 5); + } + else if (settings.input.JammaSetup == 5) + { + // Clay Challenge needs 2 std controllers on port 0 & 1 (digital in) and light guns on port 2 & 3 + // Sports Shooting same + mcfg_Create(MDT_LightGun, 2, 5, 0); + mcfg_Create(MDT_LightGun, 3, 5, 1); + } + else if (settings.input.JammaSetup == 3) + { + // Sega Bass Fishing Challenge needs a mouse (track-ball) on port 2 + mcfg_Create(MDT_Mouse, 2, 5, 0); + } } void mcfg_CreateDevices() diff --git a/core/hw/maple/maple_cfg.h b/core/hw/maple/maple_cfg.h index 4afcc99ce..75d4d116e 100644 --- a/core/hw/maple/maple_cfg.h +++ b/core/hw/maple/maple_cfg.h @@ -68,3 +68,5 @@ void mcfg_CreateAtomisWaveControllers(); void mcfg_DestroyDevices(); void mcfg_SerializeDevices(void **data, unsigned int *total_size); void mcfg_UnserializeDevices(void **data, unsigned int *total_size); + +bool maple_atomiswave_coin_chute(int slot); diff --git a/core/hw/maple/maple_devs.cpp b/core/hw/maple/maple_devs.cpp index e1b8ae0a4..76c2153c6 100755 --- a/core/hw/maple/maple_devs.cpp +++ b/core/hw/maple/maple_devs.cpp @@ -7,6 +7,7 @@ #include "hw/naomi/naomi.h" #include "hw/naomi/naomi_cart.h" #include "hw/pvr/spg.h" +#include "input/gamepad.h" #include #include "deps/zlib/zlib.h" @@ -198,6 +199,35 @@ struct maple_base: maple_device */ struct maple_sega_controller: maple_base { + virtual u32 get_capabilities() { + // byte 0: 0 0 0 0 0 0 0 0 + // byte 1: 0 0 a5 a4 a3 a2 a1 a0 + // byte 2: R2 L2 D2 U2 D X Y Z + // byte 3: R L D U St A B C + + return 0xfe060f00; // 4 analog axes (0-3) X Y A B Start U D L R + } + + virtual u32 transform_kcode(u32 kcode) { + return kcode; + } + + virtual u32 get_analog_axis(int index, const PlainJoystickState &pjs) { + switch (index) + { + case 0: + return pjs.trigger[PJTI_R]; // Right trigger + case 1: + return pjs.trigger[PJTI_L]; // Left trigger + case 2: + return pjs.joy[PJAI_X1]; // Stick X + case 3: + return pjs.joy[PJAI_Y1]; // Stick Y + default: + return 0x80; // unused + } + } + virtual MapleDeviceType get_device_type() { return MDT_SegaController; @@ -215,9 +245,9 @@ struct maple_sega_controller: maple_base //struct data //3*4 - w32( 0xfe060f00); - w32( 0); - w32( 0); + w32(get_capabilities()); + w32(0); + w32(0); //1 area code w8(0xFF); @@ -250,26 +280,26 @@ struct maple_sega_controller: maple_base //state data //2 key code - w16(pjs.kcode); + w16(transform_kcode(pjs.kcode)); //triggers //1 R - w8(pjs.trigger[PJTI_R]); + w8(get_analog_axis(0, pjs)); //1 L - w8(pjs.trigger[PJTI_L]); + w8(get_analog_axis(1, pjs)); //joyx //1 - w8(pjs.joy[PJAI_X1]); + w8(get_analog_axis(2, pjs)); //joyy //1 - w8(pjs.joy[PJAI_Y1]); + w8(get_analog_axis(3, pjs)); //not used //1 - w8(0x80); + w8(get_analog_axis(4, pjs)); //1 - w8(0x80); + w8(get_analog_axis(5, pjs)); } return MDRS_DataTransfer; @@ -281,6 +311,38 @@ struct maple_sega_controller: maple_base } }; +struct maple_atomiswave_controller: maple_sega_controller +{ + virtual u32 get_capabilities() override { + // byte 0: 0 0 0 0 0 0 0 0 + // byte 1: 0 0 a5 a4 a3 a2 a1 a0 + // byte 2: R2 L2 D2 U2 D X Y Z + // byte 3: R L D U St A B C + + return 0xff663f00; // 6 analog axes, X Y L2/D2(?) A B C Start U D L R + } + + virtual u32 transform_kcode(u32 kcode) override { + return kcode | AWAVE_TRIGGER_KEY; + } + + virtual u32 get_analog_axis(int index, const PlainJoystickState &pjs) override { + switch (index) + { + case 2: + return pjs.joy[PJAI_X1]; + case 3: + return pjs.joy[PJAI_Y1]; + case 4: + return pjs.joy[PJAI_X2]; + case 5: + return pjs.joy[PJAI_Y2]; + default: + return 0x80; + } + } +}; + /* Sega Dreamcast Visual Memory Unit This is pretty much done (?) @@ -622,9 +684,7 @@ struct maple_sega_vmu: maple_base } } config->SetImage(lcd_data_decoded); -#if !defined(TARGET_PANDORA) && HOST_OS != OS_DARWIN - push_vmu_screen(lcd_data_decoded); -#endif + push_vmu_screen(bus_id, bus_port, lcd_data_decoded); #if 0 // Update LCD window if (!dev->lcd.visible) @@ -1247,6 +1307,10 @@ struct maple_mouse : maple_base struct maple_lightgun : maple_base { + virtual u32 transform_kcode(u32 kcode) { + return kcode | 0xFF01; + } + virtual MapleDeviceType get_device_type() { return MDT_LightGun; @@ -1295,21 +1359,13 @@ struct maple_lightgun : maple_base PlainJoystickState pjs; config->GetInput(&pjs); - // Also use the mouse buttons - if (!(mo_buttons & 4)) // Left button - pjs.kcode &= ~4; // A - if (!(mo_buttons & 2)) // Right button - pjs.kcode &= ~2; // B - if (!(mo_buttons & 8)) // Wheel button - pjs.kcode &= ~8; // Start - //caps //4 w32(MFID_0_Input); //state data //2 key code - w16(pjs.kcode | 0xFF01); + w16(transform_kcode(pjs.kcode)); //not used //2 @@ -1335,6 +1391,13 @@ struct maple_lightgun : maple_base } }; +struct atomiswave_lightgun : maple_lightgun +{ + virtual u32 transform_kcode(u32 kcode) override { + return (kcode & AWAVE_TRIGGER_KEY) == 0 ? ~AWAVE_BTN0_KEY : ~0; + } +}; + extern u16 kcode[4]; extern s8 joyx[4],joyy[4]; extern u8 rt[4], lt[4]; @@ -1361,20 +1424,29 @@ static u16 getRightTriggerAxis() return rt[0] << 8; } -NaomiInputMapping Naomi_Mapping = { - { getJoystickXAxis, getJoystickYAxis, getRightTriggerAxis, getLeftTriggerAxis }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0 }, - { 0x40, 0x01, 0x02, 0x80, 0x20, 0x10, 0x08, 0x04, 0, 0x80, 0x40, 0, 0 }, -// SERVICE BTN1 BTN0 START UP DOWN LEFT RIGHT BTN2 BTN3 +u32 naomi_button_mapping[] = { + NAOMI_SERVICE_KEY, // DC_BTN_C + NAOMI_BTN1_KEY, // DC_BTN_B + NAOMI_BTN0_KEY, // DC_BTN_A + NAOMI_START_KEY, // DC_BTN_START + NAOMI_UP_KEY, // DC_DPAD_UP + NAOMI_DOWN_KEY, // DC_DPAD_DOWN + NAOMI_LEFT_KEY, // DC_DPAD_LEFT + NAOMI_RIGHT_KEY, // DC_DPAD_RIGHT + NAOMI_TEST_KEY, // DC_BTN_Z + NAOMI_BTN3_KEY, // DC_BTN_Y + NAOMI_BTN2_KEY, // DC_BTN_X + NAOMI_COIN_KEY, // DC_BTN_D + // DC_DPAD2_UP + // DC_DPAD2_DOWN + // DC_DPAD2_LEFT + // DC_DPAD2_RIGHT }; - /* * Sega JVS I/O board */ -bool coin_chute; -static bool old_coin_chute; -static int coin_count; -bool naomi_test_button = false; +static bool old_coin_chute[4]; +static int coin_count[4]; struct maple_naomi_jamma; @@ -2293,63 +2365,56 @@ u32 jvs_io_board::handle_jvs_message(u8 *buffer_in, u32 length_in, u8 *buffer_ou { JVS_STATUS1(); // report byte - LOGJVS("btns "); - JVS_OUT(naomi_test_button ? 0x80 : 0x00); // test, tilt1, tilt2, tilt3, unused, unused, unused, unused - // FIXME in-lst mapping - u8 buttons[8] = { 0 }; - u32 keycode = ~kcode[0]; - for (int i = 0; i < 16; i++) - if ((keycode & (1 << i)) != 0) - { - buttons[Naomi_Mapping.button_mapping_byte[i]] |= Naomi_Mapping.button_mapping_mask[i]; - } - for (int player = 0; player < buffer_in[cmdi + 1]; player++) + u16 buttons[4] = { 0 }; + for (int player = 0; player < buffer_in[cmdi + 1] && first_player + player < ARRAY_SIZE(kcode); player++) { - u8 *cur_btns = &buttons[(first_player + player) * 2]; - LOGJVS("P%d %02x ", player + 1 + first_player, cur_btns[0]); - JVS_OUT(cur_btns[0]); - if (buffer_in[cmdi + 2] == 2) + u32 keycode = ~kcode[first_player + player]; + for (int i = 0; i < 16; i++) { - LOGJVS("%02x ", cur_btns[1]); - JVS_OUT(cur_btns[1]); + if ((keycode & (1 << i)) != 0) + buttons[player] |= naomi_button_mapping[i]; + } + } + + LOGJVS("btns "); + JVS_OUT((buttons[0] & NAOMI_TEST_KEY) ? 0x80 : 0x00); // test, tilt1, tilt2, tilt3, unused, unused, unused, unused + for (int player = 0; player < buffer_in[cmdi + 1]; player++) + { + u16 cur_btns = first_player + player < ARRAY_SIZE(buttons) ? buttons[first_player + player] : 0; + LOGJVS("P%d %02x ", player + 1 + first_player, cur_btns >> 8); + JVS_OUT(cur_btns >> 8); + if (buffer_in[cmdi + 2] == 2) + { + LOGJVS("%02x ", cur_btns & 0xFF); + JVS_OUT(cur_btns); } } -// for (int player = 0; player < jvs_request[channel][cmdi + 1]; player++) -// { -// u32 keycode = ~kcode[player]; -// if (keycode & DC_BTN_C) -// keycode |= 0xFFff; -// -// if (jvs_request[channel][cmdi + 2] == 1) -// JVS_OUT(keycode); -// else -// w16(keycode); -// } cmdi += 3; } break; case 0x21: // Read coins { - if (coin_chute && !old_coin_chute) - coin_count++; - old_coin_chute = coin_chute; JVS_STATUS1(); // report byte LOGJVS("coins "); for (int slot = 0; slot < buffer_in[cmdi + 1]; slot++) { - if (slot == 0) + bool coin_chute = false; + u32 keycode = ~kcode[first_player + slot]; + for (int i = 0; i < 16 && !coin_chute; i++) { - LOGJVS("0:%d ", coin_count); - JVS_OUT((coin_count >> 8) & 0x3F); // status (2 highest bits, 0: normal), coin count MSB - JVS_OUT(coin_count); // coin count LSB - } - else - { - LOGJVS("%d:0 ", slot); - JVS_OUT(0); - JVS_OUT(0); + if (naomi_button_mapping[i] == NAOMI_COIN_KEY && (keycode & (1 << i)) != 0) + coin_chute = true; } + if (coin_chute && !old_coin_chute[first_player + slot]) + coin_count[first_player + slot] += 1; + old_coin_chute[first_player + slot] = coin_chute; + + LOGJVS("%d:%d ", slot + 1 + first_player, coin_count[first_player + slot]); + // status (2 highest bits, 0: normal), coin count MSB + JVS_OUT((coin_count[first_player + slot] >> 8) & 0x3F); + // coin count LSB + JVS_OUT(coin_count[first_player + slot]); } cmdi += 2; } @@ -2373,34 +2438,50 @@ u32 jvs_io_board::handle_jvs_message(u8 *buffer_in, u32 length_in, u8 *buffer_ou } LOGJVS("x,y:%4x,%4x ", x, y); JVS_OUT(x >> 8); // X, MSB - JVS_OUT(x); // X, LSB + JVS_OUT(x); // X, LSB JVS_OUT(y >> 8); // Y, MSB - JVS_OUT(y); // Y, LSB + JVS_OUT(y); // Y, LSB axis = 2; } + int full_axis_count = 0; + int half_axis_count = 0; for (; axis < buffer_in[cmdi + 1]; axis++) { - // FIXME Need to know how many axes per player for proper mapping u16 axis_value; - if (axis + first_player * 4 < 8 && Naomi_Mapping.axis[axis + first_player * 4] != NULL) - axis_value = Naomi_Mapping.axis[axis + first_player * 4](); + if (NaomiGameInputs != NULL + && axis < ARRAY_SIZE(NaomiGameInputs->axes) + && NaomiGameInputs->axes[axis].name != NULL + && NaomiGameInputs->axes[axis].type == Half) + { + if (half_axis_count == 0) + axis_value = rt[first_player] << 8; + else if (half_axis_count == 1) + axis_value = lt[first_player] << 8; + else + axis_value = 0; + half_axis_count++; + } else { - switch (axis) { + switch (full_axis_count) { case 0: - axis_value = (joyx[first_player + axis / 4] + 128) << 8; + axis_value = (joyx[first_player] + 128) << 8; break; case 1: - axis_value = (joyy[first_player + axis / 4] + 128) << 8; - break; - case 2: - axis_value = rt[first_player + axis / 4] << 8; - break; - case 3: - axis_value = lt[first_player + axis / 4] << 8; + axis_value = (joyy[first_player] + 128) << 8; break; + // TODO right analog stick +// case 2: +// axis_value = (joyrx[first_player] + 128) << 8; +// break; +// case 3: +// axis_value = (joyry[first_player] + 128) << 8; +// break; + default: + axis_value = 128; } + full_axis_count++; } LOGJVS("%d:%4x ", axis, axis_value); JVS_OUT(axis_value >> 8); @@ -2473,8 +2554,8 @@ u32 jvs_io_board::handle_jvs_message(u8 *buffer_in, u32 length_in, u8 *buffer_ou break; case 0x30: // substract coin - if (buffer_in[cmdi + 1] == 1) - coin_count -= (buffer_in[cmdi + 2] << 8) + buffer_in[cmdi + 3]; + if (buffer_in[cmdi + 1] > 0 && first_player + buffer_in[cmdi + 1] - 1 < ARRAY_SIZE(coin_count)) + coin_count[first_player + buffer_in[cmdi + 1] - 1] -= (buffer_in[cmdi + 2] << 8) + buffer_in[cmdi + 3]; JVS_STATUS1(); // report byte cmdi += 4; break; @@ -2522,7 +2603,11 @@ maple_device* maple_Create(MapleDeviceType type) switch(type) { case MDT_SegaController: - rv=new maple_sega_controller(); +#if DC_PLATFORM != DC_PLATFORM_ATOMISWAVE + rv = new maple_sega_controller(); +#else + rv = new maple_atomiswave_controller(); +#endif break; case MDT_Microphone: @@ -2546,7 +2631,11 @@ maple_device* maple_Create(MapleDeviceType type) break; case MDT_LightGun: +#if DC_PLATFORM != DC_PLATFORM_ATOMISWAVE rv = new maple_lightgun(); +#else + rv = new atomiswave_lightgun(); +#endif break; case MDT_NaomiJamma: diff --git a/core/hw/maple/maple_devs.h b/core/hw/maple/maple_devs.h index 8ad5db972..ec45b4986 100755 --- a/core/hw/maple/maple_devs.h +++ b/core/hw/maple/maple_devs.h @@ -101,6 +101,6 @@ maple_device* maple_Create(MapleDeviceType type); #define SIZE_OF_MIC_DATA 480 //ALSO DEFINED IN SipEmulator.java #ifndef TARGET_PANDORA int get_mic_data(u8* buffer); //implemented in Android.cpp -int push_vmu_screen(u8* buffer); //implemented in Android.cpp #endif +void push_vmu_screen(int bus_id, int bus_port, u8* buffer); #define MAPLE_PORTS 4 diff --git a/core/hw/maple/maple_if.cpp b/core/hw/maple/maple_if.cpp index 6da941e15..f66d56259 100644 --- a/core/hw/maple/maple_if.cpp +++ b/core/hw/maple/maple_if.cpp @@ -2,6 +2,7 @@ #include #include "maple_if.h" +#include "maple_cfg.h" #include "cfg/cfg.h" @@ -36,6 +37,7 @@ int maple_schid; */ void maple_DoDma(); +static void maple_handle_reconnect(); //really hackish //misses delay , and stop/start implementation @@ -69,6 +71,9 @@ void maple_vblank() maple_ddt_pending_reset=false; } } +#if DC_PLATFORM == DC_PLATFORM_DREAMCAST + maple_handle_reconnect(); +#endif } void maple_SB_MSHTCL_Write(u32 addr, u32 data) { @@ -273,3 +278,22 @@ void maple_Term() { } + +#if DC_PLATFORM == DC_PLATFORM_DREAMCAST +static u64 reconnect_time; + +void maple_ReconnectDevices() +{ + mcfg_DestroyDevices(); + reconnect_time = sh4_sched_now64() + SH4_MAIN_CLOCK / 10; +} + +static void maple_handle_reconnect() +{ + if (reconnect_time != 0 && reconnect_time <= sh4_sched_now64()) + { + reconnect_time = 0; + mcfg_CreateDevices(); + } +} +#endif diff --git a/core/hw/maple/maple_if.h b/core/hw/maple/maple_if.h index 374f6a981..1ae3081a9 100644 --- a/core/hw/maple/maple_if.h +++ b/core/hw/maple/maple_if.h @@ -8,5 +8,6 @@ extern maple_device* MapleDevices[4][6]; void maple_Init(); void maple_Reset(bool Manual); void maple_Term(); +void maple_ReconnectDevices(); -void maple_vblank(); \ No newline at end of file +void maple_vblank(); diff --git a/core/hw/mem/_vmem.cpp b/core/hw/mem/_vmem.cpp index cde6d9a18..3f7cb5947 100644 --- a/core/hw/mem/_vmem.cpp +++ b/core/hw/mem/_vmem.cpp @@ -21,7 +21,6 @@ _vmem_WriteMem32FP* _vmem_WF32[HANDLER_COUNT]; //upper 8b of the address void* _vmem_MemInfo_ptr[0x100]; - void _vmem_get_ptrs(u32 sz,bool write,void*** vmap,void*** func) { *vmap=_vmem_MemInfo_ptr; @@ -394,10 +393,7 @@ void _vmem_reset() verify(_vmem_register_handler(0,0,0,0,0,0)==0); } -void _vmem_term() -{ - -} +void _vmem_term() {} #include "hw/pvr/pvr_mem.h" #include "hw/sh4/sh4_mem.h" @@ -418,414 +414,120 @@ void* malloc_pages(size_t size) { #endif } -bool _vmem_reserve_nonvmem() -{ - virt_ram_base = 0; - - p_sh4rcb=(Sh4RCB*)malloc_pages(sizeof(Sh4RCB)); - - mem_b.size=RAM_SIZE; - mem_b.data=(u8*)malloc_pages(RAM_SIZE); - - vram.size=VRAM_SIZE; - vram.data=(u8*)malloc_pages(VRAM_SIZE); - - aica_ram.size=ARAM_SIZE; - aica_ram.data=(u8*)malloc_pages(ARAM_SIZE); - - return true; -} - -void _vmem_bm_reset_nvmem(); - +// Resets the FPCB table (by either clearing it to the default val +// or by flushing it and making it fault on access again. void _vmem_bm_reset() { - if (virt_ram_base) { - #if !defined(TARGET_NO_NVMEM) - _vmem_bm_reset_nvmem(); - #endif - } - -#ifndef TARGET_IPHONE - if (!virt_ram_base) -#endif - { - bm_vmem_pagefill((void**)p_sh4rcb->fpcb, FPCB_SIZE); - } + // If we allocated it via vmem: + if (virt_ram_base) + vmem_platform_reset_mem(p_sh4rcb->fpcb, sizeof(p_sh4rcb->fpcb)); + else + // We allocated it via a regular malloc/new/whatever on the heap + bm_vmem_pagefill((void**)p_sh4rcb->fpcb, sizeof(p_sh4rcb->fpcb)); } -static void _vmem_release_nonvmem() -{ - free(p_sh4rcb); - free(vram.data); - free(aica_ram.data); - free(mem_b.data); -} +// This gets called whenever there is a pagefault, it is possible that it lands +// on the fpcb memory range, which is allocated on miss. Returning true tells the +// fault handler this was us, and that the page is resolved and can continue the execution. +bool BM_LockedWrite(u8* address) { + if (!virt_ram_base) + return false; // No vmem, therefore not us who caused this. -#if !defined(TARGET_NO_NVMEM) + uintptr_t ptrint = (uintptr_t)address; + uintptr_t start = (uintptr_t)p_sh4rcb->fpcb; + uintptr_t end = start + sizeof(p_sh4rcb->fpcb); -#define MAP_RAM_START_OFFSET 0 -#define MAP_VRAM_START_OFFSET (MAP_RAM_START_OFFSET+RAM_SIZE) -#define MAP_ARAM_START_OFFSET (MAP_VRAM_START_OFFSET+VRAM_SIZE) - -#if HOST_OS==OS_WINDOWS -#include -HANDLE mem_handle; - -void* _nvmem_map_buffer(u32 dst,u32 addrsz,u32 offset,u32 size, bool w) -{ - void* ptr; - void* rv; - - u32 map_times=addrsz/size; - verify((addrsz%size)==0); - verify(map_times>=1); - - rv= MapViewOfFileEx(mem_handle,FILE_MAP_READ | (w?FILE_MAP_WRITE:0),0,offset,size,&virt_ram_base[dst]); - if (!rv) - return 0; - - for (u32 i=1;i - #include - #include - #include - #include - #include - -#ifndef MAP_NOSYNC -#define MAP_NOSYNC 0 //missing from linux :/ -- could be the cause of android slowness ? -#endif - -#ifdef _ANDROID -#include - -#ifndef ASHMEM_DEVICE -#define ASHMEM_DEVICE "/dev/ashmem" -#endif -int ashmem_create_region(const char *name, size_t size) -{ - int fd, ret; - - fd = open(ASHMEM_DEVICE, O_RDWR); - if (fd < 0) - return fd; - - if (name) { - char buf[ASHMEM_NAME_LEN]; - - strlcpy(buf, name, sizeof(buf)); - ret = ioctl(fd, ASHMEM_SET_NAME, buf); - if (ret < 0) - goto error; - } - - ret = ioctl(fd, ASHMEM_SET_SIZE, size); - if (ret < 0) - goto error; - - return fd; - -error: - close(fd); - return ret; -} -#endif - - int vmem_fd = -1; - void* _nvmem_unused_buffer(u32 start,u32 end) - { - void* ptr=mmap(&virt_ram_base[start], end-start, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0); - if (MAP_FAILED==ptr) - return 0; - return ptr; - } - - - void* _nvmem_map_buffer(u32 dst,u32 addrsz,u32 offset,u32 size, bool w) - { - void* ptr; - void* rv; - - printf("MAP %08X w/ %d\n",dst,offset); - u32 map_times=addrsz/size; - verify((addrsz%size)==0); - verify(map_times>=1); - u32 prot=PROT_READ|(w?PROT_WRITE:0); - rv= mmap(&virt_ram_base[dst], size, prot, MAP_SHARED | MAP_NOSYNC | MAP_FIXED, vmem_fd, offset); - if (MAP_FAILED==rv || rv!=(void*)&virt_ram_base[dst] || (mprotect(rv,size,prot)!=0)) - { - printf("MAP1 failed %d\n",errno); - return 0; - } - - for (u32 i=1;i slow and stuttery - { - vmem_fd = open("/data/data/com.reicast.emulator/files/dcnzorz_mem",O_CREAT|O_RDWR|O_TRUNC,S_IRWXU|S_IRWXG|S_IRWXO); - unlink("/data/data/com.reicast.emulator/files/dcnzorz_mem"); - } -#endif - - - - u32 sz = 512*1024*1024 + sizeof(Sh4RCB) + ARAM_SIZE_MAX + 0x10000; - void* rv=mmap(0, sz, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); - verify(rv != NULL); - munmap(rv,sz); - return (u8*)rv + 0x10000 - unat(rv)%0x10000;//align to 64 KB (Needed for linaro mmap not to extend to next region) - } -#endif - -#define map_buffer(dsts,dste,offset,sz,w) {ptr=_nvmem_map_buffer(dsts,dste-dsts,offset,sz,w);if (!ptr) return false;} -#define unused_buffer(start,end) {ptr=_nvmem_unused_buffer(start,end);if (!ptr) return false;} - -u32 pagecnt; -void _vmem_bm_reset_nvmem() -{ - #if defined(TARGET_NO_NVMEM) - return; - #endif - - #ifdef TARGET_IPHONE - //On iOS & nacl we allways allocate all of the mapping table - mprotect(p_sh4rcb, sizeof(p_sh4rcb->fpcb), PROT_READ | PROT_WRITE); - return; - #endif - pagecnt=0; - -#if HOST_OS==OS_WINDOWS - VirtualFree(p_sh4rcb,sizeof(p_sh4rcb->fpcb),MEM_DECOMMIT); -#else - mprotect(p_sh4rcb, sizeof(p_sh4rcb->fpcb), PROT_NONE); - madvise(p_sh4rcb,sizeof(p_sh4rcb->fpcb),MADV_DONTNEED); - #ifdef MADV_REMOVE - madvise(p_sh4rcb,sizeof(p_sh4rcb->fpcb),MADV_REMOVE); - #else - //OSX, IOS - madvise(p_sh4rcb,sizeof(p_sh4rcb->fpcb),MADV_FREE); - #endif -#endif - - printf("Freeing fpcb\n"); -} - -bool BM_LockedWrite(u8* address) -{ - if (!_nvmem_enabled()) - return false; - -#if FEAT_SHREC != DYNAREC_NONE - size_t addr=address-(u8*)p_sh4rcb->fpcb; - - address=(u8*)p_sh4rcb->fpcb+ (addr&~PAGE_MASK); - - if (addrfpcb)) - { - //printf("Allocated %d PAGES [%08X]\n",++pagecnt,addr); - -#if HOST_OS==OS_WINDOWS - verify(VirtualAlloc(address,PAGE_SIZE,MEM_COMMIT,PAGE_READWRITE)); -#else - mprotect (address, PAGE_SIZE, PROT_READ | PROT_WRITE); -#endif - - bm_vmem_pagefill((void**)address,PAGE_SIZE); - + if (ptrint >= start && ptrint < end) { + // Alloc the page then and initialize it to default values + void *aligned_addr = (void*)(ptrint & (~PAGE_MASK)); + vmem_platform_ondemand_page(aligned_addr, PAGE_SIZE); + bm_vmem_pagefill((void**)aligned_addr, PAGE_SIZE); return true; } -#else -die("BM_LockedWrite and NO REC"); -#endif return false; } -bool _vmem_reserve() -{ - void* ptr=0; - +bool _vmem_reserve() { + // TODO: Static assert? verify((sizeof(Sh4RCB)%PAGE_SIZE)==0); - if (settings.dynarec.disable_nvmem) - return _vmem_reserve_nonvmem(); + VMemType vmemstatus = MemTypeError; - virt_ram_base=(u8*)_nvmem_alloc_mem(); + // Use vmem only if settings mandate so, and if we have proper exception handlers. + #ifndef TARGET_NO_EXCEPTIONS + if (!settings.dynarec.disable_nvmem) + vmemstatus = vmem_platform_init((void**)&virt_ram_base, (void**)&p_sh4rcb); + #endif - if (virt_ram_base==0) - return _vmem_reserve_nonvmem(); - - p_sh4rcb=(Sh4RCB*)virt_ram_base; + // Fallback to statically allocated buffers, this results in slow-ops being generated. + if (vmemstatus == MemTypeError) { + printf("Warning! nvmem is DISABLED (due to failure or not being built-in\n"); + virt_ram_base = 0; - // Map the sh4 context but protect access to Sh4RCB.fpcb[] -#if HOST_OS==OS_WINDOWS - //verify(p_sh4rcb==VirtualAlloc(p_sh4rcb,sizeof(Sh4RCB),MEM_RESERVE|MEM_COMMIT,PAGE_READWRITE)); - verify(p_sh4rcb==VirtualAlloc(p_sh4rcb,sizeof(Sh4RCB),MEM_RESERVE,PAGE_NOACCESS)); + // Allocate it all and initialize it. + p_sh4rcb = (Sh4RCB*)malloc_pages(sizeof(Sh4RCB)); + bm_vmem_pagefill((void**)p_sh4rcb->fpcb, sizeof(p_sh4rcb->fpcb)); - verify(VirtualAlloc((u8*)p_sh4rcb + sizeof(p_sh4rcb->fpcb),sizeof(Sh4RCB)-sizeof(p_sh4rcb->fpcb),MEM_COMMIT,PAGE_READWRITE)); -#else - verify(p_sh4rcb==mmap(p_sh4rcb,sizeof(Sh4RCB),PROT_NONE,MAP_PRIVATE | MAP_ANON, -1, 0)); - mprotect((u8*)p_sh4rcb + sizeof(p_sh4rcb->fpcb),sizeof(Sh4RCB)-sizeof(p_sh4rcb->fpcb),PROT_READ|PROT_WRITE); -#endif - virt_ram_base+=sizeof(Sh4RCB); + mem_b.size = RAM_SIZE; + mem_b.data = (u8*)malloc_pages(RAM_SIZE); - //Area 0 - //[0x00000000 ,0x00800000) -> unused - unused_buffer(0x00000000,0x00800000); + vram.size = VRAM_SIZE; + vram.data = (u8*)malloc_pages(VRAM_SIZE); - //I wonder, aica ram warps here ?.? - //I really should check teh docs before codin ;p - //[0x00800000,0x00A00000); - map_buffer(0x00800000,0x01000000,MAP_ARAM_START_OFFSET,ARAM_SIZE,false); - map_buffer(0x20000000,0x20000000+ARAM_SIZE,MAP_ARAM_START_OFFSET,ARAM_SIZE,true); + aica_ram.size = ARAM_SIZE; + aica_ram.data = (u8*)malloc_pages(ARAM_SIZE); + } + else { + printf("Info: nvmem is enabled, with addr space of size %s\n", vmemstatus == MemType4GB ? "4GB" : "512MB"); + printf("Info: p_sh4rcb: %p virt_ram_base: %p\n", p_sh4rcb, virt_ram_base); + // Map the different parts of the memory file into the new memory range we got. + #define MAP_RAM_START_OFFSET 0 + #define MAP_VRAM_START_OFFSET (MAP_RAM_START_OFFSET+RAM_SIZE) + #define MAP_ARAM_START_OFFSET (MAP_VRAM_START_OFFSET+VRAM_SIZE) + const vmem_mapping mem_mappings[] = { + {0x00000000, 0x00800000, 0, 0, false}, // Area 0 -> unused + {0x00800000, 0x01000000, MAP_ARAM_START_OFFSET, ARAM_SIZE, false}, // Aica, wraps too + {0x20000000, 0x20000000+ARAM_SIZE, MAP_ARAM_START_OFFSET, ARAM_SIZE, true}, + {0x01000000, 0x04000000, 0, 0, false}, // More unused + {0x04000000, 0x05000000, MAP_VRAM_START_OFFSET, VRAM_SIZE, true}, // Area 1 (vram, 16MB, wrapped on DC as 2x8MB) + {0x05000000, 0x06000000, 0, 0, false}, // 32 bit path (unused) + {0x06000000, 0x07000000, MAP_VRAM_START_OFFSET, VRAM_SIZE, true}, // VRAM mirror + {0x07000000, 0x08000000, 0, 0, false}, // 32 bit path (unused) mirror + {0x08000000, 0x0C000000, 0, 0, false}, // Area 2 + {0x0C000000, 0x10000000, MAP_RAM_START_OFFSET, RAM_SIZE, true}, // Area 3 (main RAM + 3 mirrors) + {0x10000000, 0x20000000, 0, 0, false}, // Area 4-7 (unused) + }; + vmem_platform_create_mappings(&mem_mappings[0], sizeof(mem_mappings) / sizeof(mem_mappings[0])); - aica_ram.size=ARAM_SIZE; - aica_ram.data=(u8*)ptr; - //[0x01000000 ,0x04000000) -> unused - unused_buffer(0x01000000,0x04000000); - + // Point buffers to actual data pointers + aica_ram.size = ARAM_SIZE; + aica_ram.data = &virt_ram_base[0x20000000]; // Points to the writtable AICA addrspace - //Area 1 - //[0x04000000,0x05000000) -> vram (16mb, warped on dc) - map_buffer(0x04000000,0x05000000,MAP_VRAM_START_OFFSET,VRAM_SIZE,true); - - vram.size=VRAM_SIZE; - vram.data=(u8*)ptr; + vram.size = VRAM_SIZE; + vram.data = &virt_ram_base[0x04000000]; // Points to first vram mirror (writtable and lockable) - //[0x05000000,0x06000000) -> unused (32b path) - unused_buffer(0x05000000,0x06000000); - - //[0x06000000,0x07000000) -> vram mirror - map_buffer(0x06000000,0x07000000,MAP_VRAM_START_OFFSET,VRAM_SIZE,true); - - - //[0x07000000,0x08000000) -> unused (32b path) mirror - unused_buffer(0x07000000,0x08000000); - - //Area 2 - //[0x08000000,0x0C000000) -> unused - unused_buffer(0x08000000,0x0C000000); - - //Area 3 - //[0x0C000000,0x0D000000) -> main ram - //[0x0D000000,0x0E000000) -> main ram mirror - //[0x0E000000,0x0F000000) -> main ram mirror - //[0x0F000000,0x10000000) -> main ram mirror - map_buffer(0x0C000000,0x10000000,MAP_RAM_START_OFFSET,RAM_SIZE,true); - - mem_b.size=RAM_SIZE; - mem_b.data=(u8*)ptr; - - //Area 4 - //Area 5 - //Area 6 - //Area 7 - //all -> Unused - //[0x10000000,0x20000000) -> unused - unused_buffer(0x10000000,0x20000000); - - printf("vmem reserve: base: %p, aram: %p, vram: %p, ram: %p\n",virt_ram_base,aica_ram.data,vram.data,mem_b.data); + mem_b.size = RAM_SIZE; + mem_b.data = &virt_ram_base[0x0C000000]; // Main memory, first mirror + } + // Clear out memory aica_ram.Zero(); vram.Zero(); mem_b.Zero(); - printf("Mem alloc successful!\n"); - - return virt_ram_base!=0; + return true; } -void _vmem_release() -{ - if (!_nvmem_enabled()) - _vmem_release_nonvmem(); - else - { - if (virt_ram_base != NULL) - { -#if HOST_OS == OS_WINDOWS - VirtualFree(virt_ram_base, 0, MEM_RELEASE); -#else - munmap(virt_ram_base, 0x20000000); -#endif - virt_ram_base = NULL; - } -#if HOST_OS != OS_WINDOWS - close(vmem_fd); -#endif +#define freedefptr(x) \ + if (x) { free(x); x = NULL; } + +void _vmem_release() { + if (virt_ram_base) + vmem_platform_destroy(); + else { + freedefptr(p_sh4rcb); + freedefptr(vram.data); + freedefptr(aica_ram.data); + freedefptr(mem_b.data); } } -#else - -bool _vmem_reserve() -{ - return _vmem_reserve_nonvmem(); -} -void _vmem_release() -{ - _vmem_release_nonvmem(); -} -#endif diff --git a/core/hw/mem/_vmem.h b/core/hw/mem/_vmem.h index 528cf2a90..e609b1b09 100644 --- a/core/hw/mem/_vmem.h +++ b/core/hw/mem/_vmem.h @@ -1,6 +1,41 @@ #pragma once #include "types.h" +enum VMemType { + MemType4GB, + MemType512MB, + MemTypeError +}; + +struct vmem_mapping { + u32 start_address, end_address; + unsigned memoffset, memsize; + bool allow_writes; +}; + +// Platform specific vmemory API +// To initialize (maybe) the vmem subsystem +VMemType vmem_platform_init(void **vmem_base_addr, void **sh4rcb_addr); +// To reset the on-demand allocated pages. +void vmem_platform_reset_mem(void *ptr, unsigned size_bytes); +// To handle a fault&allocate an ondemand page. +void vmem_platform_ondemand_page(void *address, unsigned size_bytes); +// To create the mappings in the address space. +void vmem_platform_create_mappings(const vmem_mapping *vmem_maps, unsigned nummaps); +// Just tries to wipe as much as possible in the relevant area. +void vmem_platform_destroy(); +// Given a block of data in the .text section, prepares it for JIT action. +// both code_area and size are page aligned. Returns success. +bool vmem_platform_prepare_jit_block(void *code_area, unsigned size, void **code_area_rwx); +// Same as above but uses two address spaces one with RX and RW protections. +// Note: this function doesnt have to be implemented, it's a fallback for the above one. +bool vmem_platform_prepare_jit_block(void *code_area, unsigned size, void **code_area_rw, uintptr_t *rx_offset); +// This might not need an implementation (ie x86/64 cpus). +void vmem_platform_flush_cache(void *icache_start, void *icache_end, void *dcache_start, void *dcache_end); + +// Note: if you want to disable vmem magic in any given platform, implement the +// above functions as empty functions and make vmem_platform_init return MemTypeError. + //Typedef's //ReadMem typedef u8 DYNACALL _vmem_ReadMem8FP(u32 Address); diff --git a/core/hw/modem/dns.cpp b/core/hw/modem/dns.cpp index 1dc7f46c8..95c8657c4 100644 --- a/core/hw/modem/dns.cpp +++ b/core/hw/modem/dns.cpp @@ -18,6 +18,9 @@ You should have received a copy of the GNU General Public License along with reicast. If not, see . */ +#include "types.h" + +#if BUILD_COMPILER!=COMPILER_VC && (BUILD_COMPILER!=COMPILER_CLANG || !defined(WIN32)) #include #include @@ -145,3 +148,5 @@ char *read_name(char *reader, char *buffer, int *count) return name; } + +#endif // !COMPILER_VC_OR_CLANG_WIN32 \ No newline at end of file diff --git a/core/hw/naomi/awcartridge.cpp b/core/hw/naomi/awcartridge.cpp index 02b773b1a..f013ce8f7 100644 --- a/core/hw/naomi/awcartridge.cpp +++ b/core/hw/naomi/awcartridge.cpp @@ -162,6 +162,11 @@ ROM board internal layouts: */ #include "awcartridge.h" #include "awave_regs.h" +#ifdef _MSC_VER +#undef min +#undef max +#include +#endif u32 AWCartridge::ReadMem(u32 address, u32 size) { verify(size != 1); diff --git a/core/hw/naomi/naomi.cpp b/core/hw/naomi/naomi.cpp index dcb0ed2aa..1b75b1a03 100644 --- a/core/hw/naomi/naomi.cpp +++ b/core/hw/naomi/naomi.cpp @@ -6,6 +6,7 @@ #include "hw/holly/sb.h" #include "hw/sh4/sh4_mem.h" #include "hw/holly/holly_intc.h" +#include "hw/maple/maple_cfg.h" #include "naomi.h" #include "naomi_cart.h" @@ -15,7 +16,7 @@ u32 naomi_updates; //#define NAOMI_COMM -u32 BoardID=0x980055AA; +static const u32 BoardID=0x980055AA; u32 GSerialBuffer=0,BSerialBuffer=0; int GBufPos=0,BBufPos=0; int GState=0,BState=0; @@ -388,6 +389,7 @@ u32 reg_dimm_48; //parameters u32 reg_dimm_4c=0x11; //status/control reg ? bool NaomiDataRead = false; +static bool aw_ram_test_skipped = false; void naomi_process(u32 r3c,u32 r40,u32 r44, u32 r48) { @@ -552,6 +554,8 @@ void naomi_reg_Term() void naomi_reg_Reset(bool Manual) { NaomiDataRead = false; + aw_ram_test_skipped = false; + BLastCmd = 0; } void Update_naomi() @@ -631,8 +635,6 @@ void Update_naomi() } static u8 aw_maple_devs; -extern bool coin_chute; -static bool once = false; u32 libExtDevice_ReadMem_A0_006(u32 addr,u32 size) { addr &= 0x7ff; @@ -645,18 +647,19 @@ u32 libExtDevice_ReadMem_A0_006(u32 addr,u32 size) { // c/d - 3P/4P coin inputs (EX. IO board), active low // // (ab == 0) -> BIOS skip RAM test - if (!once) + if (!aw_ram_test_skipped) { // Skip RAM test at startup - once = true; + aw_ram_test_skipped = true; return 0; } - if (coin_chute) { - // FIXME Coin Error if coin_chute is set for too long - return 0xE; + u8 coin_input = 0xF; + for (int slot = 0; slot < 4; slot++) + if (maple_atomiswave_coin_chute(slot)) + coin_input &= ~(1 << slot); + return coin_input; } - return 0xF; case 0x284: // Atomiswave maple devices // ddcc0000 where cc/dd are the types of devices on maple bus 2 and 3: diff --git a/core/hw/naomi/naomi_cart.cpp b/core/hw/naomi/naomi_cart.cpp index cc33fd89d..55bfdd5c1 100644 --- a/core/hw/naomi/naomi_cart.cpp +++ b/core/hw/naomi/naomi_cart.cpp @@ -24,15 +24,14 @@ bool bios_loaded = false; #include #include - #include #include #endif -fd_t* RomCacheMap; +fd_t* RomCacheMap = NULL; u32 RomCacheMapCount; char naomi_game_id[33]; -InputDescriptors *naomi_game_inputs; +InputDescriptors *NaomiGameInputs; u8 *naomi_default_eeprom; extern RomChip sys_rom; @@ -246,7 +245,7 @@ static bool naomi_cart_LoadZip(char *filename) break; } CurrentCartridge->SetKey(game->key); - naomi_game_inputs = game->inputs; + NaomiGameInputs = game->inputs; for (int romid = 0; game->blobs[romid].filename != NULL; romid++) { @@ -485,15 +484,9 @@ bool naomi_cart_LoadRom(char* file) RomCacheMapCount = (u32)files.size(); RomCacheMap = new fd_t[files.size()](); - //Allocate space for the ram, so we are sure we have a segment of continius ram -#if HOST_OS == OS_WINDOWS - RomPtr = (u8*)VirtualAlloc(0, RomSize, MEM_RESERVE, PAGE_NOACCESS); -#else - RomPtr = (u8*)mmap(0, RomSize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); -#endif - - verify(RomPtr != 0); - verify(RomPtr != (void*)-1); + //Allocate space for the ram, so we are sure we have a segment of continuous ram + RomPtr = (u8*)mem_region_reserve(NULL, RomSize); + verify(RomPtr != NULL); bool load_error = false; @@ -550,11 +543,7 @@ bool naomi_cart_LoadRom(char* file) } //Release the segment we reserved so we can map the files there -#if HOST_OS == OS_WINDOWS - verify(VirtualFree(RomPtr, 0, MEM_RELEASE)); -#else - munmap(RomPtr, RomSize); -#endif + mem_region_release(RomPtr, RomSize); if (load_error) { @@ -574,23 +563,13 @@ bool naomi_cart_LoadRom(char* file) if (RomCacheMap[i] == INVALID_FD) { //printf("-Reserving ram at 0x%08X, size 0x%08X\n", fstart[i], fsize[i]); - -#if HOST_OS == OS_WINDOWS - bool mapped = RomDest == VirtualAlloc(RomDest, fsize[i], MEM_RESERVE, PAGE_NOACCESS); -#else - bool mapped = RomDest == (u8*)mmap(RomDest, RomSize, PROT_NONE, MAP_PRIVATE, 0, 0); -#endif - + bool mapped = RomDest == (u8 *)mem_region_reserve(RomDest, fsize[i]); verify(mapped); } else { //printf("-Mapping \"%s\" at 0x%08X, size 0x%08X\n", files[i].c_str(), fstart[i], fsize[i]); -#if HOST_OS == OS_WINDOWS - bool mapped = RomDest == MapViewOfFileEx(RomCacheMap[i], FILE_MAP_READ, 0, 0, fsize[i], RomDest); -#else - bool mapped = RomDest == mmap(RomDest, fsize[i], PROT_READ, MAP_PRIVATE, RomCacheMap[i], 0 ); -#endif + bool mapped = RomDest == (u8 *)mem_region_map_file((void *)(uintptr_t)RomCacheMap[i], RomDest, fsize[i], 0, false); if (!mapped) { printf("-Mapping ROM FAILED: %s @ %08x size %x\n", files[i].c_str(), fstart[i], fsize[i]); @@ -636,11 +615,11 @@ bool naomi_cart_SelectFile() if (!naomi_cart_LoadRom(SelectedFile)) { printf("Cannot load %s: error %d\n", SelectedFile, errno); + cfgSetVirtual("config", "image", ""); + return false; } - cfgSaveStr("emu", "gamefile", SelectedFile); - return true; } @@ -653,7 +632,8 @@ Cartridge::Cartridge(u32 size) Cartridge::~Cartridge() { - free(RomPtr); + if (RomPtr != NULL) + free(RomPtr); } bool Cartridge::Read(u32 offset, u32 size, void* dst) @@ -1036,3 +1016,11 @@ void M2Cartridge::Unserialize(void** data, unsigned int* total_size) { REICAST_US(naomi_cart_ram); NaomiCartridge::Unserialize(data, total_size); } + +DecryptedCartridge::~DecryptedCartridge() +{ + // TODO this won't work on windows -> need to unmap each file first + mem_region_release(RomPtr, RomSize); + // Avoid crash when freeing vmem + RomPtr = NULL; +} diff --git a/core/hw/naomi/naomi_cart.h b/core/hw/naomi/naomi_cart.h index b5c9745dc..85758fb28 100644 --- a/core/hw/naomi/naomi_cart.h +++ b/core/hw/naomi/naomi_cart.h @@ -58,7 +58,7 @@ class DecryptedCartridge : public NaomiCartridge { public: DecryptedCartridge(u8 *rom_ptr, u32 size) : NaomiCartridge(size) { RomPtr = rom_ptr; } - // FIXME Must do a munmap and close for each segment + virtual ~DecryptedCartridge() override; }; class M2Cartridge : public NaomiCartridge @@ -110,6 +110,6 @@ struct InputDescriptors AxisDescriptor axes[8]; }; -extern InputDescriptors *naomi_game_inputs; +extern InputDescriptors *NaomiGameInputs; #endif //NAOMI_CART_H diff --git a/core/hw/pvr/Renderer_if.cpp b/core/hw/pvr/Renderer_if.cpp index d7ad66589..d7ce6720b 100644 --- a/core/hw/pvr/Renderer_if.cpp +++ b/core/hw/pvr/Renderer_if.cpp @@ -82,8 +82,7 @@ bool renderer_enabled = true; // Signals the renderer thread to exit bool renderer_changed = false; // Signals the renderer thread to switch renderer #if !defined(TARGET_NO_THREADS) -cResetEvent rs(false,true); -cResetEvent re(false,true); +cResetEvent rs, re; #endif static bool swap_pending; static bool do_swap; @@ -91,16 +90,15 @@ static bool do_swap; int max_idx,max_mvo,max_op,max_pt,max_tr,max_vtx,max_modt, ovrn; static bool render_called = false; -u32 fb1_watch_addr_start; -u32 fb1_watch_addr_end; -u32 fb2_watch_addr_start; -u32 fb2_watch_addr_end; +u32 fb_watch_addr_start; +u32 fb_watch_addr_end; bool fb_dirty; TA_context* _pvrrc; void SetREP(TA_context* cntx); void killtex(); bool render_output_framebuffer(); +static void rend_create_renderer(); void dump_frame(const char* file, TA_context* ctx, u8* vram, u8* vram_ref = NULL) { FILE* fw = fopen(file, "wb"); @@ -269,6 +267,13 @@ bool rend_frame(TA_context* ctx, bool draw_osd) { bool rend_single_frame() { + if (renderer_changed) + { + renderer_changed = false; + rend_term_renderer(); + rend_create_renderer(); + rend_init_renderer(); + } //wait render start only if no frame pending do { @@ -389,7 +394,6 @@ void rend_term_renderer() delete fallback_renderer; fallback_renderer = NULL; } - tactx_Term(); } void* rend_thread(void* p) @@ -556,6 +560,7 @@ void rend_end_render() void rend_stop_renderer() { renderer_enabled = false; + tactx_Term(); } void rend_vblank() @@ -574,10 +579,8 @@ void rend_vblank() void check_framebuffer_write() { u32 fb_size = (FB_R_SIZE.fb_y_size + 1) * (FB_R_SIZE.fb_x_size + FB_R_SIZE.fb_modulus) * 4; - fb1_watch_addr_start = FB_R_SOF1 & VRAM_MASK; - fb1_watch_addr_end = fb1_watch_addr_start + fb_size; - fb2_watch_addr_start = FB_R_SOF2 & VRAM_MASK; - fb2_watch_addr_end = fb2_watch_addr_start + fb_size; + fb_watch_addr_start = FB_R_SOF2 & VRAM_MASK; + fb_watch_addr_end = fb_watch_addr_start + fb_size; } void rend_cancel_emu_wait() diff --git a/core/hw/pvr/Renderer_if.h b/core/hw/pvr/Renderer_if.h index 6619d7f33..2cdec4db4 100644 --- a/core/hw/pvr/Renderer_if.h +++ b/core/hw/pvr/Renderer_if.h @@ -62,10 +62,8 @@ Renderer* rend_GL4(); Renderer* rend_norend(); Renderer* rend_softrend(); -extern u32 fb1_watch_addr_start; -extern u32 fb1_watch_addr_end; -extern u32 fb2_watch_addr_start; -extern u32 fb2_watch_addr_end; +extern u32 fb_watch_addr_start; +extern u32 fb_watch_addr_end; extern bool fb_dirty; void check_framebuffer_write(); diff --git a/core/hw/pvr/pvr_mem.cpp b/core/hw/pvr/pvr_mem.cpp index c9af98a90..45fb1a721 100644 --- a/core/hw/pvr/pvr_mem.cpp +++ b/core/hw/pvr/pvr_mem.cpp @@ -233,9 +233,7 @@ void DYNACALL pvr_write_area1_8(u32 addr,u8 data) void DYNACALL pvr_write_area1_16(u32 addr,u16 data) { u32 vaddr = addr & VRAM_MASK; - if (!fb_dirty - && ((vaddr >= fb1_watch_addr_start && vaddr < fb1_watch_addr_end) - || (vaddr >= fb2_watch_addr_start && vaddr < fb2_watch_addr_end))) + if (vaddr >= fb_watch_addr_start && vaddr < fb_watch_addr_end) { fb_dirty = true; } @@ -244,9 +242,7 @@ void DYNACALL pvr_write_area1_16(u32 addr,u16 data) void DYNACALL pvr_write_area1_32(u32 addr,u32 data) { u32 vaddr = addr & VRAM_MASK; - if (!fb_dirty - && ((vaddr >= fb1_watch_addr_start && vaddr < fb1_watch_addr_end) - || (vaddr >= fb2_watch_addr_start && vaddr < fb2_watch_addr_end))) + if (vaddr >= fb_watch_addr_start && vaddr < fb_watch_addr_end) { fb_dirty = true; } diff --git a/core/hw/pvr/pvr_mem.h b/core/hw/pvr/pvr_mem.h index 722d37bf6..f2584937d 100644 --- a/core/hw/pvr/pvr_mem.h +++ b/core/hw/pvr/pvr_mem.h @@ -9,7 +9,7 @@ f32 vrf(u32 addr); u32 vri(u32 addr); //vram 32-64b -extern VArray2 vram; +extern VLockedMemory vram; //read u8 DYNACALL pvr_read_area1_8(u32 addr); u16 DYNACALL pvr_read_area1_16(u32 addr); @@ -36,4 +36,4 @@ extern "C" void DYNACALL TAWriteSQ(u32 address,u8* sqb); void YUV_init(); //registers -#define PVR_BASE 0x005F8000 \ No newline at end of file +#define PVR_BASE 0x005F8000 diff --git a/core/hw/pvr/spg.cpp b/core/hw/pvr/spg.cpp index 0cf4c4d0b..a83a482af 100755 --- a/core/hw/pvr/spg.cpp +++ b/core/hw/pvr/spg.cpp @@ -88,6 +88,8 @@ double full_rps; static u32 lightgun_line = 0xffff; static u32 lightgun_hpos; +double mspdf; + u32 fskip=0; //called from sh4 context , should update pvr/ta state and everything else int spg_line_sched(int tag, int cycl, int jit) @@ -117,6 +119,20 @@ int spg_line_sched(int tag, int cycl, int jit) SPG_STATUS.vsync=in_vblank; SPG_STATUS.scanline=prv_cur_scanline; + switch (SPG_HBLANK_INT.hblank_int_mode) + { + case 0x0: + if (prv_cur_scanline == SPG_HBLANK_INT.line_comp_val) + asic_RaiseInterrupt(holly_HBLank); + break; + case 0x2: + asic_RaiseInterrupt(holly_HBLank); + break; + default: + die("Unimplemented HBLANK INT mode"); + break; + } + //Vblank start -- really need to test the scanline values if (prv_cur_scanline==0) { @@ -127,7 +143,6 @@ int spg_line_sched(int tag, int cycl, int jit) //Vblank counter vblk_cnt++; - asic_RaiseInterrupt(holly_HBLank);// -> This turned out to be HBlank btw , needs to be emulated ;( //TODO : rend_if_VBlank(); rend_vblank();//notify for vblank :) @@ -174,7 +189,7 @@ int spg_line_sched(int tag, int cycl, int jit) } double frames_done=spd_cpu/2; - double mspdf=1/frames_done*1000; + mspdf=1/frames_done*1000; full_rps=(spd_fps+fskip/ts); diff --git a/core/hw/pvr/ta.h b/core/hw/pvr/ta.h index eb8505bf6..e4e649090 100644 --- a/core/hw/pvr/ta.h +++ b/core/hw/pvr/ta.h @@ -20,5 +20,3 @@ void DYNACALL ta_vtx_data32(void* data); void ta_vtx_data(u32* data, u32 size); bool ta_parse_vdrc(TA_context* ctx); - -#define TRIG_SORT 1 diff --git a/core/hw/pvr/ta_ctx.cpp b/core/hw/pvr/ta_ctx.cpp index 781d82aa4..31e80b07e 100644 --- a/core/hw/pvr/ta_ctx.cpp +++ b/core/hw/pvr/ta_ctx.cpp @@ -124,7 +124,7 @@ void VDecEnd() cMutex mtx_rqueue; TA_context* rqueue; -cResetEvent frame_finished(false, true); +cResetEvent frame_finished; double last_frame = 0; u64 last_cyces = 0; diff --git a/core/hw/pvr/ta_vtx.cpp b/core/hw/pvr/ta_vtx.cpp index a03e86e85..f25ea0f4d 100644 --- a/core/hw/pvr/ta_vtx.cpp +++ b/core/hw/pvr/ta_vtx.cpp @@ -1311,10 +1311,10 @@ public: idx[1]=vbase+1; idx[2]=vbase+2; idx[3]=vbase+3; - idx[4]=vbase+3; - idx[5]=vbase+4; + idx[4]=vbase+3; + idx[5]=vbase+4; - CurrentPP->count=vdrc.idx.used()-CurrentPP->first-2; + CurrentPP->count=vdrc.idx.used()-CurrentPP->first-2; Vertex* cv = vdrc.verts.Append(4); @@ -1519,7 +1519,6 @@ int ta_parse_cnt = 0; bool ta_parse_vdrc(TA_context* ctx) { bool rv=false; - verify( vd_ctx == 0); vd_ctx = ctx; vd_rc = vd_ctx->rend; diff --git a/core/hw/sh4/dyna/blockmanager.cpp b/core/hw/sh4/dyna/blockmanager.cpp index 2d21f3971..5c75ea41f 100644 --- a/core/hw/sh4/dyna/blockmanager.cpp +++ b/core/hw/sh4/dyna/blockmanager.cpp @@ -89,15 +89,16 @@ u32 bm_gc_luc,bm_gcf_luc; #define FPCA(x) ((DynarecCodeEntryPtr&)sh4rcb.fpcb[(x>>1)&FPCB_MASK]) // addr must be a physical address +// This returns an executable address DynarecCodeEntryPtr DYNACALL bm_GetCode(u32 addr) { - //rdv_FailedToFindBlock_pc=addr; - DynarecCodeEntryPtr rv=(DynarecCodeEntryPtr)FPCA(addr); + DynarecCodeEntryPtr rv = (DynarecCodeEntryPtr)FPCA(addr); return rv; } // addr must be a virtual address +// This returns an executable address DynarecCodeEntryPtr DYNACALL bm_GetCodeByVAddr(u32 addr) { #ifndef NO_MMU @@ -157,36 +158,41 @@ DynarecCodeEntryPtr DYNACALL bm_GetCodeByVAddr(u32 addr) } // addr must be a physical address +// This returns an executable address RuntimeBlockInfo* DYNACALL bm_GetBlock(u32 addr) { - DynarecCodeEntryPtr cde=bm_GetCode(addr); + DynarecCodeEntryPtr cde = bm_GetCode(addr); // Returns RX ptr - if (cde==ngen_FailedToFindBlock) + if (cde == ngen_FailedToFindBlock) return 0; else - return bm_GetBlock((void*)cde); + return bm_GetBlock((void*)cde); // Returns RX pointer } +// This takes a RX address and returns the info block ptr (RW space) RuntimeBlockInfo* bm_GetBlock(void* dynarec_code) { - blkmap_t::iterator iter=blkmap.find((RuntimeBlockInfo*)dynarec_code); - if (iter!=blkmap.end()) + void *dynarecrw = CC_RX2RW(dynarec_code); + blkmap_t::iterator iter = blkmap.find((RuntimeBlockInfo*)dynarecrw); + if (iter != blkmap.end()) { - verify((*iter)->contains_code((u8*)dynarec_code)); + verify((*iter)->contains_code((u8*)dynarecrw)); return *iter; } else { - printf("bm_GetBlock(%p) failed ..\n",dynarec_code); + printf("bm_GetBlock(%p) failed ..\n", dynarec_code); return 0; } } +// Takes RX pointer and returns a RW pointer RuntimeBlockInfo* bm_GetStaleBlock(void* dynarec_code) { + void *dynarecrw = CC_RX2RW(dynarec_code); for(u32 i=0;icontains_code((u8*)dynarec_code)) + if (del_blocks[i]->contains_code((u8*)dynarecrw)) return del_blocks[i]; } @@ -206,9 +212,8 @@ void bm_AddBlock(RuntimeBlockInfo* blk) } blkmap.insert(blk); - verify((void*)bm_GetCode(blk->addr)==(void*)ngen_FailedToFindBlock); - FPCA(blk->addr)=blk->code; + FPCA(blk->addr) = (DynarecCodeEntryPtr)CC_RW2RX(blk->code); #ifdef DYNA_OPROF if (oprofHandle) @@ -394,6 +399,8 @@ void bm_Rebuild() { return; + die("this is broken in multiple levels, including compile options"); + void RASDASD(); RASDASD(); @@ -411,7 +418,7 @@ void bm_Rebuild() //constprop(all_blocks[i]); //#endif } - ngen_Compile(all_blocks[i],false,false,all_blocks[i]->staging_runs>0,do_opts); + ngen_Compile(all_blocks[i],NoCheck,false,all_blocks[i]->staging_runs>0,do_opts); blkmap.insert(all_blocks[i]); verify(bm_GetBlock((RuntimeBlockInfo*)all_blocks[i]->code)==all_blocks[i]); @@ -427,9 +434,9 @@ void bm_Rebuild() rebuild_counter=30; } -void bm_vmem_pagefill(void** ptr,u32 PAGE_SZ) +void bm_vmem_pagefill(void** ptr, u32 size_bytes) { - for (size_t i=0; i -#if HOST_OS==OS_WINDOWS -#include -#elif HOST_OS==OS_LINUX -#include -#include -#endif - #include "../sh4_interpreter.h" #include "../sh4_opcode_list.h" #include "../sh4_core.h" #include "../sh4_if.h" #include "hw/sh4/sh4_interrupts.h" +#include "hw/mem/_vmem.h" #include "hw/sh4/sh4_mem.h" #include "hw/sh4/modules/mmu.h" #include "hw/pvr/pvr_mem.h" @@ -28,9 +22,7 @@ #include "decoder.h" #if FEAT_SHREC != DYNAREC_NONE -//uh uh -#if !defined(_WIN64) u8 SH4_TCB[CODE_SIZE + TEMP_CODE_SIZE + 4096] #if HOST_OS == OS_WINDOWS || FEAT_SHREC != DYNAREC_JIT ; @@ -41,10 +33,10 @@ u8 SH4_TCB[CODE_SIZE + TEMP_CODE_SIZE + 4096] #else #error SH4_TCB ALLOC #endif -#endif u8* CodeCache; u8* TempCodeCache; +uintptr_t cc_rx_offset; u32 LastAddr; u32 LastAddr_min; @@ -101,6 +93,15 @@ void recSh4_Run() sh4_dyna_rcb=(u8*)&Sh4cntx + sizeof(Sh4cntx); printf("cntx // fpcb offset: %td // pc offset: %td // pc %08X\n",(u8*)&sh4rcb.fpcb - sh4_dyna_rcb, (u8*)&sh4rcb.cntx.pc - sh4_dyna_rcb,sh4rcb.cntx.pc); + + if (!settings.dynarec.safemode) + printf("Warning: Dynarec safe mode is off\n"); + + if (settings.dynarec.unstable_opt) + printf("Warning: Unstable optimizations is on\n"); + + if (settings.dynarec.SmcCheckLevel != FullCheck) + printf("Warning: SMC check mode is %d\n", settings.dynarec.SmcCheckLevel); verify(rcb_noffs(&next_pc)==-184); ngen_mainloop(sh4_dyna_rcb); @@ -141,34 +142,51 @@ u32 emit_FreeSpace() } // pc must be a physical address -bool DoCheck(u32 pc) +SmcCheckEnum DoCheck(u32 pc) { - if (IsOnRam(pc)) - { - if (!settings.dynarec.unstable_opt) - return true; - pc&=0xFFFFFF; - switch(pc) - { - //DOA2LE - case 0x3DAFC6: - case 0x3C83F8: + switch (settings.dynarec.SmcCheckLevel) { - //Shenmue 2 - case 0x348000: - - //Shenmue - case 0x41860e: - + // Heuristic-elimintaed FastChecks + case NoCheck: { + if (IsOnRam(pc)) + { + pc&=0xFFFFFF; + switch(pc) + { + //DOA2LE + case 0x3DAFC6: + case 0x3C83F8: - return true; + //Shenmue 2 + case 0x348000: + + //Shenmue + case 0x41860e: + - default: - return false; + return FastCheck; + + default: + return NoCheck; + } + } + return NoCheck; } + break; + + // Fast Check everything + case FastCheck: + return FastCheck; + + // Full Check everything + case FullCheck: + return FullCheck; + + default: + die("Unhandled settings.dynarec.SmcCheckLevel"); + return FullCheck; } - return false; } void AnalyseBlock(RuntimeBlockInfo* blk); @@ -307,6 +325,8 @@ DynarecCodeEntryPtr DYNACALL rdv_FailedToFindBlock(u32 pc) DynarecCodeEntryPtr code = rdv_CompilePC(0); if (code == NULL) code = bm_GetCodeByVAddr(next_pc); + else + code = (DynarecCodeEntryPtr)CC_RW2RX(code); return code; } @@ -361,7 +381,7 @@ DynarecCodeEntryPtr DYNACALL rdv_BlockCheckFail(u32 addr) next_pc = addr; recSh4_ClearCache(); } - return rdv_CompilePC(blockcheck_failures); + return (DynarecCodeEntryPtr)CC_RW2RX(rdv_CompilePC(blockcheck_failures)); } //DynarecCodeEntryPtr rdv_FindCode() @@ -375,21 +395,22 @@ DynarecCodeEntryPtr DYNACALL rdv_BlockCheckFail(u32 addr) DynarecCodeEntryPtr rdv_FindOrCompile() { - DynarecCodeEntryPtr rv=bm_GetCodeByVAddr(next_pc); - if (rv==ngen_FailedToFindBlock) - rv=rdv_CompilePC(0); + DynarecCodeEntryPtr rv = bm_GetCodeByVAddr(next_pc); // Returns exec addr + if (rv == ngen_FailedToFindBlock) + rv = (DynarecCodeEntryPtr)CC_RW2RX(rdv_CompilePC(0)); // Returns rw addr return rv; } void* DYNACALL rdv_LinkBlock(u8* code,u32 dpc) { - RuntimeBlockInfo* rbi=bm_GetBlock(code); + // code is the RX addr to return after, however bm_GetBlock returns RW + RuntimeBlockInfo* rbi = bm_GetBlock(code); if (!rbi) { printf("Stale block .."); - rbi=bm_GetStaleBlock(code); + rbi = bm_GetStaleBlock(code); } verify(rbi != NULL); @@ -412,7 +433,7 @@ void* DYNACALL rdv_LinkBlock(u8* code,u32 dpc) next_pc=rbi->NextBlock; } - DynarecCodeEntryPtr rv=rdv_FindOrCompile(); + DynarecCodeEntryPtr rv = rdv_FindOrCompile(); // Returns rx ptr bool do_link = !mmu_enabled() && bm_GetBlock(code) == rbi; @@ -481,10 +502,6 @@ void recSh4_Reset(bool Manual) Sh4_int_Reset(Manual); } -#if HOST_OS == OS_DARWIN -#include -#endif - void recSh4_Init() { printf("recSh4 Init\n"); @@ -502,57 +519,24 @@ void recSh4_Init() if (_nvmem_enabled()) { verify(mem_b.data==((u8*)p_sh4rcb->sq_buffer+512+0x0C000000)); } - -#if defined(_WIN64) -#ifdef _MSC_VER - for (int i = 10; i < 1300; i++) { + // Prepare some pointer to the pre-allocated code cache: + void *candidate_ptr = (void*)(((unat)SH4_TCB + 4095) & ~4095); - //align to next page .. - u8* ptr = (u8*)recSh4_Init - i * 1024 * 1024; - - CodeCache = (u8*)VirtualAlloc(ptr, CODE_SIZE + TEMP_CODE_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);//; (u8*)(((unat)SH4_TCB+4095)& ~4095); - - if (CodeCache) - break; - } -#else - CodeCache = (u8*)VirtualAlloc(NULL, CODE_SIZE + TEMP_CODE_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); -#endif - verify(CodeCache != NULL); -#else - CodeCache = (u8*)(((unat)SH4_TCB+4095)& ~4095); -#endif - -#if HOST_OS == OS_DARWIN - munmap(CodeCache, CODE_SIZE + TEMP_CODE_SIZE); - CodeCache = (u8*)mmap(CodeCache, CODE_SIZE + TEMP_CODE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_ANON, 0, 0); -#endif - -#if HOST_OS == OS_WINDOWS - DWORD old; - VirtualProtect(CodeCache,CODE_SIZE + TEMP_CODE_SIZE,PAGE_EXECUTE_READWRITE,&old); -#elif HOST_OS == OS_LINUX || HOST_OS == OS_DARWIN - - printf("\n\t CodeCache addr: %p | from: %p | addr here: %p\n", CodeCache, CodeCache, recSh4_Init); - - #if FEAT_SHREC == DYNAREC_JIT - if (mprotect(CodeCache, CODE_SIZE + TEMP_CODE_SIZE, PROT_READ|PROT_WRITE|PROT_EXEC)) - { - perror("\n\tError,Couldn’t mprotect CodeCache!"); - die("Couldn’t mprotect CodeCache"); - } + // Call the platform-specific magic to make the pages RWX + CodeCache = NULL; + #ifdef FEAT_NO_RWX_PAGES + verify(vmem_platform_prepare_jit_block(candidate_ptr, CODE_SIZE + TEMP_CODE_SIZE, (void**)&CodeCache, &cc_rx_offset)); + #else + verify(vmem_platform_prepare_jit_block(candidate_ptr, CODE_SIZE + TEMP_CODE_SIZE, (void**)&CodeCache)); #endif + // Ensure the pointer returned is non-null + verify(CodeCache != NULL); -#if TARGET_IPHONE - memset((u8*)mmap(CodeCache, CODE_SIZE + TEMP_CODE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_ANON, 0, 0),0xFF,CODE_SIZE + TEMP_CODE_SIZE); -#else - memset(CodeCache,0xFF,CODE_SIZE + TEMP_CODE_SIZE); -#endif - -#endif + memset(CodeCache, 0xFF, CODE_SIZE + TEMP_CODE_SIZE); TempCodeCache = CodeCache + CODE_SIZE; ngen_init(); + bm_Reset(); } void recSh4_Term() @@ -580,4 +564,5 @@ void Get_Sh4Recompiler(sh4_if* rv) rv->IsCpuRunning = recSh4_IsCpuRunning; rv->ResetCache = recSh4_ClearCache; } -#endif + +#endif // FEAT_SHREC != DYNAREC_NONE diff --git a/core/hw/sh4/dyna/ngen.h b/core/hw/sh4/dyna/ngen.h index 76abe6210..6507421c5 100644 --- a/core/hw/sh4/dyna/ngen.h +++ b/core/hw/sh4/dyna/ngen.h @@ -52,6 +52,20 @@ #define TEMP_CODE_SIZE (1024*1024) #endif +// When NO_RWX is enabled there's two address-spaces, one executable and +// one writtable. The emitter and most of the code in rec-* will work with +// the RW pointer. However the fpcb table and other pointers during execution +// (ie. exceptions) are RX pointers. These two macros convert between them by +// sub/add the pointer offset. CodeCache will point to the RW pointer for simplicity. +#ifdef FEAT_NO_RWX_PAGES + extern uintptr_t cc_rx_offset; + #define CC_RW2RX(ptr) (void*)(((uintptr_t)(ptr)) + cc_rx_offset) + #define CC_RX2RW(ptr) (void*)(((uintptr_t)(ptr)) - cc_rx_offset) +#else + #define CC_RW2RX(ptr) (ptr) + #define CC_RX2RW(ptr) (ptr) +#endif + //alternative emit ptr, set to 0 to use the main buffer extern u32* emit_ptr; extern u8* CodeCache; @@ -89,7 +103,7 @@ u32 DYNACALL rdv_DoInterrupts_pc(u32 pc); void ngen_init(); //Called to compile a block -void ngen_Compile(RuntimeBlockInfo* block,bool force_checks, bool reset, bool staging,bool optimise); +void ngen_Compile(RuntimeBlockInfo* block, SmcCheckEnum smc_checks, bool reset, bool staging,bool optimise); //Called when blocks are reseted void ngen_ResetBlocks(); diff --git a/core/hw/sh4/dyna/shil_canonical.h b/core/hw/sh4/dyna/shil_canonical.h index 13d142546..b8624e4e0 100644 --- a/core/hw/sh4/dyna/shil_canonical.h +++ b/core/hw/sh4/dyna/shil_canonical.h @@ -668,25 +668,10 @@ shil_opc(cvt_f2i_t) shil_canonical ( u32,f1,(f32 f1), - if (f1 > 2147483520.0f) // IEEE 754: 0x4effffff + if (f1 > 2147483520.0f) // IEEE 754: 0x4effffff return 0x7fffffff; else return (s32)f1; - - // No fast-math -// if (f1 != f1) // NaN -// return 0x80000000; -// else if (f1 > 2147483520.0f) // IEEE 754: 0x4effffff -// return 0x7fffffff; -// else -// { -// u32 res = (s32)f1; -// // Fix result sign for Intel CPUs -// if (res == 0x80000000 && *(s32 *)&f1 > 0) -// res = 0x7fffffff; -// -// return res; -// } ) shil_compile diff --git a/core/hw/sh4/interpr/sh4_fpu.cpp b/core/hw/sh4/interpr/sh4_fpu.cpp index 45877f7b1..71d6abd2c 100644 --- a/core/hw/sh4/interpr/sh4_fpu.cpp +++ b/core/hw/sh4/interpr/sh4_fpu.cpp @@ -628,12 +628,12 @@ sh4op(i1111_nnnn_0011_1101) if (fpscr.PR == 0) { u32 n = GetN(op); - fpul = (u32)(s32)min(fr[n], 2147483520.0f); // IEEE 754: 0x4effffff + fpul = (u32)(s32)min(fr[n], 2147483520.0f); // IEEE 754: 0x4effffff // Intel CPUs convert out of range float numbers to 0x80000000. Manually set the correct sign if (fpul == 0x80000000) { - if (*(int *)&fr[n] > 0) // Using integer math to avoid issues with Inf and NaN + if (*(int *)&fr[n] > 0) // Using integer math to avoid issues with Inf and NaN fpul--; } } @@ -646,7 +646,7 @@ sh4op(i1111_nnnn_0011_1101) // Intel CPUs convert out of range float numbers to 0x80000000. Manually set the correct sign if (fpul == 0x80000000) { - if (*(s64 *)&f > 0) // Using integer math to avoid issues with Inf and NaN + if (*(s64 *)&f > 0) // Using integer math to avoid issues with Inf and NaN fpul--; } } diff --git a/core/hw/sh4/interpr/sh4_interpreter.cpp b/core/hw/sh4/interpr/sh4_interpreter.cpp index b9dc3a765..40e7519b5 100644 --- a/core/hw/sh4/interpr/sh4_interpreter.cpp +++ b/core/hw/sh4/interpr/sh4_interpreter.cpp @@ -232,7 +232,7 @@ int AicaUpdate(int tag, int c, int j) int DreamcastSecond(int tag, int c, int j) { - settings.dreamcast.RTC++; + RealTimeClock++; #if 1 //HOST_OS==OS_WINDOWS prof_periodical(); diff --git a/core/hw/sh4/modules/ccn.cpp b/core/hw/sh4/modules/ccn.cpp index efb1375a9..7ed472e3f 100644 --- a/core/hw/sh4/modules/ccn.cpp +++ b/core/hw/sh4/modules/ccn.cpp @@ -13,6 +13,8 @@ //Types +#define printf_smc(...) // printf + u32 CCN_QACR_TR[2]; @@ -83,13 +85,18 @@ void CCN_CCR_write(u32 addr, u32 value) temp.reg_data=value; - //what is 0xAC13DBF8 from ? - if (temp.ICI && curr_pc!=0xAC13DBF8) - { - //printf("Sh4: i-cache invalidation %08X\n",curr_pc); - // Shikigami No Shiro II sets ICI frequently - // Any reason to flush the dynarec cache for this? - //sh4_cpu.ResetCache(); + if (temp.ICI) { + printf_smc("Sh4: i-cache invalidation %08X\n",curr_pc); + + if (settings.dynarec.SmcCheckLevel != FullCheck) { + //TODO: Add skip/check vectors for Shikigami No Shiro II (uses ICI frequently) + //which game is 0xAC13DBF8 from ? + if (curr_pc != 0xAC13DBF8) + { + printf("Sh4: code cache clear (ICI) pc: %08X\n",curr_pc); + sh4_cpu.ResetCache(); + } + } } temp.ICI=0; diff --git a/core/hw/sh4/sh4_if.h b/core/hw/sh4/sh4_if.h index c6999df96..a74ba3a8c 100644 --- a/core/hw/sh4/sh4_if.h +++ b/core/hw/sh4/sh4_if.h @@ -317,7 +317,7 @@ struct Sh4RCB Sh4Context cntx; }; -extern Sh4RCB* p_sh4rcb; +extern "C" Sh4RCB* p_sh4rcb; extern u8* sh4_dyna_rcb; INLINE u32 sh4_sr_GetFull() diff --git a/core/hw/sh4/sh4_interpreter.h b/core/hw/sh4/sh4_interpreter.h index 2bf603e76..8a22caa83 100644 --- a/core/hw/sh4/sh4_interpreter.h +++ b/core/hw/sh4/sh4_interpreter.h @@ -61,6 +61,7 @@ void ExecuteDelayslot_RTE(); extern "C" { int UpdateSystem(); -__attribute__((used)) int UpdateSystem_INTC(); + +ATTR_USED int UpdateSystem_INTC(); } diff --git a/core/hw/sh4/sh4_mem.cpp b/core/hw/sh4/sh4_mem.cpp index b05c2c419..e3a76f08f 100644 --- a/core/hw/sh4/sh4_mem.cpp +++ b/core/hw/sh4/sh4_mem.cpp @@ -17,7 +17,7 @@ //main system mem -VArray2 mem_b; +VLockedMemory mem_b; void _vmem_init(); void _vmem_reset(); diff --git a/core/hw/sh4/sh4_mem.h b/core/hw/sh4/sh4_mem.h index 153d3a527..9fe5886bb 100644 --- a/core/hw/sh4/sh4_mem.h +++ b/core/hw/sh4/sh4_mem.h @@ -2,7 +2,7 @@ #include "types.h" //main system mem -extern VArray2 mem_b; +extern VLockedMemory mem_b; #include "hw/mem/_vmem.h" #include "modules/mmu.h" diff --git a/core/hw/sh4/sh4_sched.cpp b/core/hw/sh4/sh4_sched.cpp index baeef15a8..65b8af0c7 100755 --- a/core/hw/sh4/sh4_sched.cpp +++ b/core/hw/sh4/sh4_sched.cpp @@ -25,15 +25,15 @@ u64 sh4_sched_ffb; u32 sh4_sched_intr; -vector list; +vector sch_list; // using list as external inside a macro confuses clang and msc int sh4_sched_next_id=-1; u32 sh4_sched_remaining(int id, u32 reference) { - if (list[id].end != -1) + if (sch_list[id].end != -1) { - return list[id].end - reference; + return sch_list[id].end - reference; } else { @@ -51,7 +51,7 @@ void sh4_sched_ffts() u32 diff=-1; int slot=-1; - for (size_t i=0;i= 0 && cycles <= SH4_MAIN_CLOCK)); - list[id].start=sh4_sched_now(); + sch_list[id].start=sh4_sched_now(); if (cycles == -1) { - list[id].end = -1; + sch_list[id].end = -1; } else { - list[id].end = list[id].start + cycles; - if (list[id].end == -1) - list[id].end++; + sch_list[id].end = sch_list[id].start + cycles; + if (sch_list[id].end == -1) + sch_list[id].end++; } sh4_sched_ffts(); @@ -120,10 +120,10 @@ void sh4_sched_request(int id, int cycles) int sh4_sched_elapsed(int id) { - if (list[id].end!=-1) + if (sch_list[id].end!=-1) { - int rv=sh4_sched_now()-list[id].start; - list[id].start=sh4_sched_now(); + int rv=sh4_sched_now()-sch_list[id].start; + sch_list[id].start=sh4_sched_now(); return rv; } else @@ -132,12 +132,12 @@ int sh4_sched_elapsed(int id) void handle_cb(int id) { - int remain=list[id].end-list[id].start; + int remain=sch_list[id].end-sch_list[id].start; int elapsd=sh4_sched_elapsed(id); int jitter=elapsd-remain; - list[id].end=-1; - int re_sch=list[id].cb(list[id].tag,remain,jitter); + sch_list[id].end=-1; + int re_sch=sch_list[id].cb(sch_list[id].tag,remain,jitter); if (re_sch > 0) sh4_sched_request(id, max(0, re_sch - jitter)); @@ -156,7 +156,7 @@ void sh4_sched_tick(int cycles) sh4_sched_intr++; if (sh4_sched_next_id!=-1) { - for (int i=0;i= 0 || remaining == -1); diff --git a/core/imgread/chd.cpp b/core/imgread/chd.cpp index 59b7e9d35..57e6081fb 100644 --- a/core/imgread/chd.cpp +++ b/core/imgread/chd.cpp @@ -13,7 +13,7 @@ struct CHDDisc : Disc u32 hunkbytes; u32 sph; - + CHDDisc() { chd=0; @@ -22,8 +22,8 @@ struct CHDDisc : Disc bool TryOpen(const wchar* file); - ~CHDDisc() - { + ~CHDDisc() + { if (hunk_mem) delete [] hunk_mem; if (chd) @@ -48,7 +48,7 @@ struct CHDTrack : TrackFile this->swap_bytes = swap_bytes; } - virtual void Read(u32 FAD,u8* dst,SectorFormat* sector_type,u8* subcode,SubcodeFormat* subcode_type) + virtual void Read(u32 FAD, u8* dst, SectorFormat* sector_type, u8* subcode, SubcodeFormat* subcode_type) { u32 fad_offs = FAD + Offset; u32 hunk=(fad_offs)/disc->sph; @@ -58,9 +58,9 @@ struct CHDTrack : TrackFile disc->old_hunk = hunk; } - u32 hunk_ofs=fad_offs%disc->sph; + u32 hunk_ofs = fad_offs%disc->sph; - memcpy(dst,disc->hunk_mem+hunk_ofs*(2352+96),fmt); + memcpy(dst, disc->hunk_mem + hunk_ofs * (2352+96), fmt); if (swap_bytes) { @@ -71,7 +71,6 @@ struct CHDTrack : TrackFile dst[i + 1] = b; } } - *sector_type=fmt==2352?SECFMT_2352:SECFMT_2048_MODE1; //While space is reserved for it, the images contain no actual subcodes @@ -100,23 +99,24 @@ bool CHDDisc::TryOpen(const wchar* file) sph = hunkbytes/(2352+96); - if (hunkbytes%(2352+96)!=0) + if (hunkbytes%(2352+96)!=0) { printf("chd: hunkbytes is invalid, %d\n",hunkbytes); return false; } - + u32 tag; u8 flags; char temp[512]; u32 temp_len; - u32 total_frames=150; - s32 Offset = 0; + u32 total_frames = 150; + + u32 Offset = 0; for(;;) { char type[16], subtype[16], pgtype[16], pgsub[16]; - int tkid=-1,frames=0,pregap=0,postgap=0, padframes=0; + int tkid=-1, frames=0, pregap=0, postgap=0, padframes=0; err = chd_get_metadata(chd, CDROM_TRACK_METADATA2_TAG, tracks.size(), temp, sizeof(temp), &temp_len, &tag, &flags); if (err == CHDERR_NONE) @@ -124,7 +124,7 @@ bool CHDDisc::TryOpen(const wchar* file) //"TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d PREGAP:%d PGTYPE:%s PGSUB:%s POSTGAP:%d" sscanf(temp, CDROM_TRACK_METADATA2_FORMAT, &tkid, type, subtype, &frames, &pregap, pgtype, pgsub, &postgap); } - else if (CHDERR_NONE == (err = chd_get_metadata(chd, CDROM_TRACK_METADATA_TAG, tracks.size(), temp, sizeof(temp), &temp_len, &tag, &flags)) ) + else if (CHDERR_NONE== (err = chd_get_metadata(chd, CDROM_TRACK_METADATA_TAG, tracks.size(), temp, sizeof(temp), &temp_len, &tag, &flags)) ) { //CDROM_TRACK_METADATA_FORMAT "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d" sscanf(temp, CDROM_TRACK_METADATA_FORMAT, &tkid, type, subtype, &frames); @@ -136,7 +136,6 @@ bool CHDDisc::TryOpen(const wchar* file) { err = chd_get_metadata(chd, GDROM_TRACK_METADATA_TAG, tracks.size(), temp, sizeof(temp), &temp_len, &tag, &flags); } - if (err == CHDERR_NONE) { //GDROM_TRACK_METADATA_FORMAT "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d PAD:%d PREGAP:%d PGTYPE:%s PGSUB:%s POSTGAP:%d" @@ -160,8 +159,12 @@ bool CHDDisc::TryOpen(const wchar* file) t.EndFAD = total_frames - 1; t.ADDR = 0; t.CTRL = strcmp(type,"AUDIO") == 0 ? 0 : 4; - t.file = new CHDTrack(this, t.StartFAD, Offset - t.StartFAD, strcmp(type,"MODE1") ? 2352 : 2048, t.CTRL == 0 && head->version >= 5); + t.file = new CHDTrack(this, t.StartFAD, Offset - t.StartFAD, strcmp(type, "MODE1") ? 2352 : 2048, + // audio tracks are byteswapped in CHDv5+ + t.CTRL == 0 && head->version >= 5); + + // CHD files are padded, so we have to respect the offset int padded = (frames + CD_TRACK_PADDING - 1) / CD_TRACK_PADDING; Offset += padded * CD_TRACK_PADDING; @@ -186,7 +189,7 @@ bool CHDDisc::TryOpen(const wchar* file) Disc* chd_parse(const wchar* file) { CHDDisc* rv = new CHDDisc(); - + if (rv->TryOpen(file)) return rv; else diff --git a/core/imgread/common.cpp b/core/imgread/common.cpp index 363ae3a90..0697517fd 100644 --- a/core/imgread/common.cpp +++ b/core/imgread/common.cpp @@ -133,7 +133,7 @@ bool ConvertSector(u8* in_buff , u8* out_buff , int from , int to,int sector) Disc* OpenDisc(const wchar* fn) { - Disc* rv = nullptr; + Disc* rv = NULL; for (unat i=0; drivers[i] && !rv; i++) { // ;drivers[i] && !(rv=drivers[i](fn)); rv = drivers[i](fn); diff --git a/core/imgread/gdi.cpp b/core/imgread/gdi.cpp index 359f03a8b..92c896519 100644 --- a/core/imgread/gdi.cpp +++ b/core/imgread/gdi.cpp @@ -7,9 +7,9 @@ string normalize_path_separator(string path) { - #if HOST_OS == OS_WINDOWS - std::replace( path.begin(), path.end(), '/', '\\'); - #endif +#if HOST_OS == OS_WINDOWS + std::replace(path.begin(), path.end(), '/', '\\'); +#endif return path; } diff --git a/core/imgread/ioctl.cpp b/core/imgread/ioctl.cpp index 4486dc99a..9dfd66b43 100644 --- a/core/imgread/ioctl.cpp +++ b/core/imgread/ioctl.cpp @@ -4,7 +4,7 @@ #include "common.h" #include -#include +#include #include #include "SCSIDEFS.H" @@ -388,4 +388,4 @@ Disc* ioctl_parse(const wchar* file) } } -#endif \ No newline at end of file +#endif diff --git a/core/input/gamepad.h b/core/input/gamepad.h index 46996f6c8..5c37c0038 100644 --- a/core/input/gamepad.h +++ b/core/input/gamepad.h @@ -53,8 +53,8 @@ enum DreamcastKey // System axes EMU_AXIS_NONE = 0x00000, - EMU_AXIS_DPAD1_X = 0x00001, - EMU_AXIS_DPAD1_Y = 0x00002, - EMU_AXIS_DPAD2_X = 0x00003, - EMU_AXIS_DPAD2_Y = 0x00004, + EMU_AXIS_DPAD1_X = DC_DPAD_LEFT, + EMU_AXIS_DPAD1_Y = DC_DPAD_UP, + EMU_AXIS_DPAD2_X = DC_DPAD2_LEFT, + EMU_AXIS_DPAD2_Y = DC_DPAD2_RIGHT, }; diff --git a/core/input/gamepad_device.cpp b/core/input/gamepad_device.cpp index 29cb1436a..82ccefdf8 100644 --- a/core/input/gamepad_device.cpp +++ b/core/input/gamepad_device.cpp @@ -20,6 +20,10 @@ #include #include "gamepad_device.h" #include "rend/gui.h" +#include "oslib/oslib.h" +#include "cfg/cfg.h" + +#define MAPLE_PORT_CFG_PREFIX "maple_" extern void dc_exit(); @@ -32,7 +36,8 @@ std::mutex GamepadDevice::_gamepads_mutex; bool GamepadDevice::gamepad_btn_input(u32 code, bool pressed) { - if (_input_detected != NULL && _detecting_button && pressed) + if (_input_detected != NULL && _detecting_button + && os_GetSeconds() >= _detection_start_time && pressed) { _input_detected(code); _input_detected = NULL; @@ -46,7 +51,39 @@ bool GamepadDevice::gamepad_btn_input(u32 code, bool pressed) if (key < 0x10000) { if (pressed) + { kcode[_maple_port] &= ~(u16)key; + // Avoid two opposite dpad keys being pressed simultaneously + switch (key) + { + case DC_DPAD_UP: + kcode[_maple_port] |= (u16)DC_DPAD_DOWN; + break; + case DC_DPAD_DOWN: + kcode[_maple_port] |= (u16)DC_DPAD_UP; + break; + case DC_DPAD_LEFT: + kcode[_maple_port] |= (u16)DC_DPAD_RIGHT; + break; + case DC_DPAD_RIGHT: + kcode[_maple_port] |= (u16)DC_DPAD_LEFT; + break; + case DC_DPAD2_UP: + kcode[_maple_port] |= (u16)DC_DPAD2_DOWN; + break; + case DC_DPAD2_DOWN: + kcode[_maple_port] |= (u16)DC_DPAD2_UP; + break; + case DC_DPAD2_LEFT: + kcode[_maple_port] |= (u16)DC_DPAD2_RIGHT; + break; + case DC_DPAD2_RIGHT: + kcode[_maple_port] |= (u16)DC_DPAD2_LEFT; + break; + default: + break; + } + } else kcode[_maple_port] |= (u16)key; } @@ -84,7 +121,8 @@ bool GamepadDevice::gamepad_axis_input(u32 code, int value) v = (get_axis_min_value(code) + get_axis_range(code) - value) * 255 / get_axis_range(code) - 128; else v = (value - get_axis_min_value(code)) * 255 / get_axis_range(code) - 128; //-128 ... + 127 range - if (_input_detected != NULL && !_detecting_button && (v >= 64 || v <= -64)) + if (_input_detected != NULL && !_detecting_button + && os_GetSeconds() >= _detection_start_time && (v >= 64 || v <= -64)) { _input_detected(code); _input_detected = NULL; @@ -235,3 +273,51 @@ void UpdateVibration(u32 port, float power, float inclination, u32 duration_ms) gamepad->rumble(power, inclination, duration_ms); } } + +void GamepadDevice::detect_btn_input(input_detected_cb button_pressed) +{ + _input_detected = button_pressed; + _detecting_button = true; + _detection_start_time = os_GetSeconds() + 0.2; +} + +void GamepadDevice::detect_axis_input(input_detected_cb axis_moved) +{ + _input_detected = axis_moved; + _detecting_button = false; + _detection_start_time = os_GetSeconds() + 0.2; +} + +void GamepadDevice::Register(std::shared_ptr gamepad) +{ + int maple_port = cfgLoadInt("input", + (MAPLE_PORT_CFG_PREFIX + gamepad->unique_id()).c_str(), 12345); + if (maple_port != 12345) + gamepad->set_maple_port(maple_port); + + _gamepads_mutex.lock(); + _gamepads.push_back(gamepad); + _gamepads_mutex.unlock(); +} + +void GamepadDevice::Unregister(std::shared_ptr gamepad) +{ + gamepad->save_mapping(); + _gamepads_mutex.lock(); + for (auto it = _gamepads.begin(); it != _gamepads.end(); it++) + if (*it == gamepad) { + _gamepads.erase(it); + break; + } + _gamepads_mutex.unlock(); +} + +void GamepadDevice::SaveMaplePorts() +{ + for (int i = 0; i < GamepadDevice::GetGamepadCount(); i++) + { + std::shared_ptr gamepad = GamepadDevice::GetGamepad(i); + if (gamepad != NULL && !gamepad->unique_id().empty()) + cfgSaveInt("input", (MAPLE_PORT_CFG_PREFIX + gamepad->unique_id()).c_str(), gamepad->maple_port()); + } +} diff --git a/core/input/gamepad_device.h b/core/input/gamepad_device.h index e11c5e90d..92a75692d 100644 --- a/core/input/gamepad_device.h +++ b/core/input/gamepad_device.h @@ -31,20 +31,13 @@ public: const std::string& name() { return _name; } int maple_port() { return _maple_port; } void set_maple_port(int port) { _maple_port = port; } + const std::string& unique_id() { return _unique_id; } virtual bool gamepad_btn_input(u32 code, bool pressed); bool gamepad_axis_input(u32 code, int value); virtual ~GamepadDevice() {} - void detect_btn_input(input_detected_cb button_pressed) - { - _input_detected = button_pressed; - _detecting_button = true; - } - void detect_axis_input(input_detected_cb axis_moved) - { - _input_detected = axis_moved; - _detecting_button = false; - } + void detect_btn_input(input_detected_cb button_pressed); + void detect_axis_input(input_detected_cb axis_moved); void cancel_detect_input() { _input_detected = NULL; @@ -58,28 +51,13 @@ public: virtual void update_rumble() {} bool is_rumble_enabled() { return _rumble_enabled; } - static void Register(std::shared_ptr gamepad) - { - _gamepads_mutex.lock(); - _gamepads.push_back(gamepad); - _gamepads_mutex.unlock(); - } + static void Register(std::shared_ptr gamepad); - static void Unregister(std::shared_ptr gamepad) - { - gamepad->save_mapping(); - _gamepads_mutex.lock(); - for (auto it = _gamepads.begin(); it != _gamepads.end(); it++) - if (*it == gamepad) - { - _gamepads.erase(it); - break; - } - _gamepads_mutex.unlock(); - } + static void Unregister(std::shared_ptr gamepad); static int GetGamepadCount(); static std::shared_ptr GetGamepad(int index); + static void SaveMaplePorts(); protected: GamepadDevice(int maple_port, const char *api_name, bool remappable = true) @@ -91,6 +69,7 @@ protected: virtual void load_axis_min_max(u32 axis) {} std::string _name; + std::string _unique_id = ""; InputMapping *input_mapper; std::map axis_min_values; std::map axis_ranges; @@ -104,6 +83,7 @@ private: std::string _api_name; int _maple_port; bool _detecting_button = false; + double _detection_start_time; input_detected_cb _input_detected; bool _remappable; float _dead_zone = 0.1f; diff --git a/core/input/mapping.cpp b/core/input/mapping.cpp index bfbb30d76..0157fb922 100644 --- a/core/input/mapping.cpp +++ b/core/input/mapping.cpp @@ -104,6 +104,8 @@ void InputMapping::set_axis(DreamcastKey id, u32 code, bool is_inverted) } } +using namespace emucfg; + void InputMapping::load(FILE* fp) { ConfigFile mf; diff --git a/core/linux-dist/evdev_gamepad.h b/core/linux-dist/evdev_gamepad.h index f3c5c4bd4..2d06ce4fe 100644 --- a/core/linux-dist/evdev_gamepad.h +++ b/core/linux-dist/evdev_gamepad.h @@ -9,12 +9,18 @@ public: : GamepadDevice(maple_port, "evdev"), _fd(fd), _rumble_effect_id(-1), _devnode(devnode) { fcntl(fd, F_SETFL, O_NONBLOCK); - char name[256] = "Unknown"; - if (ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0) + char buf[256] = "Unknown"; + if (ioctl(fd, EVIOCGNAME(sizeof(buf) - 1), buf) < 0) perror("evdev: ioctl(EVIOCGNAME)"); else - printf("evdev: Opened device '%s' ", name); - _name = name; + printf("evdev: Opened device '%s' ", buf); + _name = buf; + buf[0] = 0; + if (ioctl(fd, EVIOCGUNIQ(sizeof(buf) - 1), buf) == 0) + _unique_id = buf; + if (_unique_id.empty()) + _unique_id = devnode; + if (!find_mapping(mapping_file)) { #if defined(TARGET_PANDORA) @@ -22,18 +28,18 @@ public: #elif defined(TARGET_GCW0) mapping_file = "controller_gcwz.cfg"; #else - if (!strcmp(name, "Microsoft X-Box 360 pad") - || !strcmp(name, "Xbox 360 Wireless Receiver") - || !strcmp(name, "Xbox 360 Wireless Receiver (XBOX)")) + if (_name == "Microsoft X-Box 360 pad" + || _name == "Xbox 360 Wireless Receiver" + || _name == "Xbox 360 Wireless Receiver (XBOX)") { mapping_file = "controller_xpad.cfg"; } - else if (strstr(name, "Xbox Gamepad (userspace driver)") != NULL) + else if (_name.find("Xbox Gamepad (userspace driver)") != std::string::npos) { mapping_file = "controller_xboxdrv.cfg"; } - else if (strstr(name, "keyboard") != NULL || - strstr(name, "Keyboard") != NULL) + else if (_name.find("keyboard") != std::string::npos + || _name.find("Keyboard") != std::string::npos) { mapping_file = "keyboard.cfg"; } diff --git a/core/linux-dist/main.cpp b/core/linux-dist/main.cpp index cee37671d..b6931a125 100644 --- a/core/linux-dist/main.cpp +++ b/core/linux-dist/main.cpp @@ -4,13 +4,11 @@ #if HOST_OS==OS_LINUX #include #include -//#include #include #include #include #include #include -#include #include #include "hw/sh4/dyna/blockmanager.h" #include "hw/maple/maple_cfg.h" @@ -408,7 +406,6 @@ int main(int argc, wchar* argv[]) #endif int get_mic_data(u8* buffer) { return 0; } -int push_vmu_screen(u8* buffer) { return 0; } void os_DebugBreak() { diff --git a/core/linux-dist/x11.cpp b/core/linux-dist/x11.cpp index da977e6b3..e053097be 100644 --- a/core/linux-dist/x11.cpp +++ b/core/linux-dist/x11.cpp @@ -52,6 +52,7 @@ public: X11MouseGamepadDevice(int maple_port) : GamepadDevice(maple_port, "X11") { _name = "Mouse"; + _unique_id = "x11_mouse"; if (!find_mapping()) input_mapper = new MouseInputMapping(); } @@ -83,8 +84,6 @@ Atom wmDeleteMessage; void* x11_vis; extern bool dump_frame_switch; -extern bool naomi_test_button; -extern bool coin_chute; void dc_exit(void); @@ -274,16 +273,6 @@ void input_x11_handle() x11_fullscreen = !x11_fullscreen; x11_window_set_fullscreen(x11_fullscreen); } -#if DC_PLATFORM == DC_PLATFORM_NAOMI || DC_PLATFORM == DC_PLATFORM_ATOMISWAVE - else if (e.xkey.keycode == KEY_F8) - { - coin_chute = e.type == KeyPress; - } - else if (e.xkey.keycode == KEY_F7) - { - naomi_test_button = e.type == KeyPress; - } -#endif } } break; @@ -372,6 +361,11 @@ void input_x11_init() printf("X11 Keyboard input disabled by config.\n"); } +static int x11_error_handler(Display *, XErrorEvent *) +{ + return 0; +} + void x11_window_create() { if (cfgLoadInt("pvr", "nox11", 0) == 0) @@ -530,20 +524,22 @@ void x11_window_create() GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, None }; + int (*old_handler)(Display *, XErrorEvent *) = XSetErrorHandler(&x11_error_handler); x11_glc = glXCreateContextAttribsARB(x11Display, bestFbc, 0, True, context_attribs); if (!x11_glc) { printf("Open GL 4.3 not supported\n"); - // Try GL 3.1 + // Try GL 3.0 context_attribs[1] = 3; - context_attribs[3] = 1; + context_attribs[3] = 0; x11_glc = glXCreateContextAttribsARB(x11Display, bestFbc, 0, True, context_attribs); if (!x11_glc) { - die("Open GL 3.1 not supported\n"); + die("Open GL 3.0 not supported\n"); } } + XSetErrorHandler(old_handler); XSync(x11Display, False); #endif diff --git a/core/linux-dist/x11_keyboard.h b/core/linux-dist/x11_keyboard.h index e78717e32..70e3edd69 100644 --- a/core/linux-dist/x11_keyboard.h +++ b/core/linux-dist/x11_keyboard.h @@ -181,6 +181,7 @@ public: X11KbGamepadDevice(int maple_port) : GamepadDevice(maple_port, "X11") { _name = "Keyboard"; + _unique_id = "x11_keyboard"; if (!find_mapping()) input_mapper = new KbInputMapping(); } diff --git a/core/linux/common.cpp b/core/linux/common.cpp index b4a1cc0b2..358244aee 100644 --- a/core/linux/common.cpp +++ b/core/linux/common.cpp @@ -11,22 +11,16 @@ #include #include #endif -//#include #include #include #include #include #include -#include #include #if !defined(TARGET_BSD) && !defined(_ANDROID) && !defined(TARGET_IPHONE) && !defined(TARGET_NACL32) && !defined(TARGET_EMSCRIPTEN) && !defined(TARGET_OSX) && !defined(TARGET_OSX_X64) #include #include #endif -#if HOST_OS == OS_DARWIN -#include -#include -#endif #include #include "hw/sh4/dyna/blockmanager.h" #include "hw/mem/vmem32.h" @@ -58,14 +52,13 @@ void sigill_handler(int sn, siginfo_t * si, void *segfault_ctx) { } #endif -#if !defined(TARGET_NO_EXCEPTIONS) void fault_handler (int sn, siginfo_t * si, void *segfault_ctx) { rei_host_context_t ctx; context_from_segfault(&ctx, segfault_ctx); - bool dyna_cde = ((unat)ctx.pc>(unat)CodeCache) && ((unat)ctx.pc<(unat)(CodeCache + CODE_SIZE + TEMP_CODE_SIZE)); + bool dyna_cde = ((unat)CC_RX2RW(ctx.pc) > (unat)CodeCache) && ((unat)CC_RX2RW(ctx.pc) < (unat)(CodeCache + CODE_SIZE + TEMP_CODE_SIZE)); //ucontext_t* ctx=(ucontext_t*)ctxr; //printf("mprot hit @ ptr 0x%08X @@ code: %08X, %d\n",si->si_addr,ctx->uc_mcontext.arm_pc,dyna_cde); @@ -121,12 +114,9 @@ void fault_handler (int sn, siginfo_t * si, void *segfault_ctx) signal(SIGSEGV, SIG_DFL); } } -#endif -#endif -void install_fault_handler (void) +void install_fault_handler(void) { -#if !defined(TARGET_NO_EXCEPTIONS) struct sigaction act, segv_oact; memset(&act, 0, sizeof(act)); act.sa_sigaction = fault_handler; @@ -140,172 +130,14 @@ void install_fault_handler (void) act.sa_sigaction = sigill_handler; sigaction(SIGILL, &act, &segv_oact); #endif -#endif } - -#if !defined(TARGET_NO_THREADS) - -//Thread class -cThread::cThread(ThreadEntryFP* function,void* prm) -{ - Entry=function; - param=prm; -} - -void cThread::Start() -{ - pthread_create( (pthread_t*)&hThread, NULL, Entry, param); -} - -void cThread::WaitToEnd() -{ - pthread_join((pthread_t)hThread,0); -} - -//End thread class -#endif - -//cResetEvent Calss -cResetEvent::cResetEvent(bool State,bool Auto) -{ - //sem_init((sem_t*)hEvent, 0, State?1:0); - verify(State==false&&Auto==true); - pthread_mutex_init(&mutx, NULL); - pthread_cond_init(&cond, NULL); -} -cResetEvent::~cResetEvent() -{ - //Destroy the event object ? - -} -void cResetEvent::Set()//Signal -{ - pthread_mutex_lock( &mutx ); - state=true; - pthread_cond_signal( &cond); - pthread_mutex_unlock( &mutx ); -} -void cResetEvent::Reset()//reset -{ - pthread_mutex_lock( &mutx ); - state=false; - pthread_mutex_unlock( &mutx ); -} -bool cResetEvent::Wait(u32 msec)//Wait for signal , then reset -{ - pthread_mutex_lock( &mutx ); - if (!state) - { - struct timespec ts; -#if HOST_OS == OS_DARWIN - // OSX doesn't have clock_gettime. - clock_serv_t cclock; - mach_timespec_t mts; - - host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); - clock_get_time(cclock, &mts); - mach_port_deallocate(mach_task_self(), cclock); - ts.tv_sec = mts.tv_sec; - ts.tv_nsec = mts.tv_nsec; -#else - clock_gettime(CLOCK_REALTIME, &ts); -#endif - ts.tv_sec += msec / 1000; - ts.tv_nsec += (msec % 1000) * 1000000; - while (ts.tv_nsec > 1000000000) - { - ts.tv_nsec -= 1000000000; - ts.tv_sec++; - } - pthread_cond_timedwait( &cond, &mutx, &ts ); - } - bool rc = state; - state=false; - pthread_mutex_unlock( &mutx ); - - return rc; -} -void cResetEvent::Wait()//Wait for signal , then reset -{ - pthread_mutex_lock( &mutx ); - if (!state) - { - pthread_cond_wait( &cond, &mutx ); - } - state=false; - pthread_mutex_unlock( &mutx ); -} - -//End AutoResetEvent +#else // !defined(TARGET_NO_EXCEPTIONS) +// No exceptions/nvmem dummy handlers. +void install_fault_handler(void) {} +#endif // !defined(TARGET_NO_EXCEPTIONS) #include -void VArray2::LockRegion(u32 offset,u32 size) -{ - #if !defined(TARGET_NO_EXCEPTIONS) - u32 inpage=offset & PAGE_MASK; - u32 rv=mprotect (data+offset-inpage, size+inpage, PROT_READ ); - if (rv!=0) - { - printf("mprotect(%8s,%08X,R) failed: %d | %d\n",data+offset-inpage,size+inpage,rv,errno); - die("mprotect failed ..\n"); - } - - #else - //printf("VA2: LockRegion\n"); - #endif -} - -void print_mem_addr() -{ - FILE *ifp, *ofp; - - char outputFilename[] = "/data/data/com.reicast.emulator/files/mem_alloc.txt"; - - ifp = fopen("/proc/self/maps", "r"); - - if (ifp == NULL) { - fprintf(stderr, "Can't open input file /proc/self/maps!\n"); - exit(1); - } - - ofp = fopen(outputFilename, "w"); - - if (ofp == NULL) { - fprintf(stderr, "Can't open output file %s!\n", - outputFilename); -#if HOST_OS == OS_LINUX - ofp = stderr; -#else - exit(1); -#endif - } - - char line [ 512 ]; - while (fgets(line, sizeof line, ifp) != NULL) { - fprintf(ofp, "%s", line); - } - - fclose(ifp); - if (ofp != stderr) - fclose(ofp); -} - -void VArray2::UnLockRegion(u32 offset,u32 size) -{ - #if !defined(TARGET_NO_EXCEPTIONS) - u32 inpage=offset & PAGE_MASK; - u32 rv=mprotect (data+offset-inpage, size+inpage, PROT_READ | PROT_WRITE); - if (rv!=0) - { - print_mem_addr(); - printf("mprotect(%8p,%08X,RW) failed: %d | %d\n",data+offset-inpage,size+inpage,rv,errno); - die("mprotect failed ..\n"); - } - #else - //printf("VA2: UnLockRegion\n"); - #endif -} double os_GetSeconds() { timeval a; diff --git a/core/linux/context.h b/core/linux/context.h index 667be6a7d..c97de30e9 100644 --- a/core/linux/context.h +++ b/core/linux/context.h @@ -1,3 +1,5 @@ +#pragma once + #include "types.h" diff --git a/core/linux/posix_vmem.cpp b/core/linux/posix_vmem.cpp new file mode 100644 index 000000000..dacb9ce61 --- /dev/null +++ b/core/linux/posix_vmem.cpp @@ -0,0 +1,322 @@ + +// Implementation of the vmem related function for POSIX-like platforms. +// There's some minimal amount of platform specific hacks to support +// Android and OSX since they are slightly different in some areas. + +// This implements the VLockedMemory interface, as defined in _vmem.h +// The implementation allows it to be empty (that is, to not lock memory). + +#include +#include +#include +#include +#include +#include + +#include "hw/mem/_vmem.h" +#include "stdclass.h" + +#ifndef MAP_NOSYNC +#define MAP_NOSYNC 0 //missing from linux :/ -- could be the cause of android slowness ? +#endif + +#ifdef _ANDROID + #include + #ifndef ASHMEM_DEVICE + #define ASHMEM_DEVICE "/dev/ashmem" + #undef PAGE_MASK + #define PAGE_MASK (PAGE_SIZE-1) +#else + #define PAGE_SIZE 4096 + #define PAGE_MASK (PAGE_SIZE-1) +#endif + +// Android specific ashmem-device stuff for creating shared memory regions +int ashmem_create_region(const char *name, size_t size) { + int fd = open(ASHMEM_DEVICE, O_RDWR); + if (fd < 0) + return -1; + + if (ioctl(fd, ASHMEM_SET_SIZE, size) < 0) { + close(fd); + return -1; + } + + return fd; +} +#endif // #ifdef _ANDROID + +bool mem_region_lock(void *start, size_t len) +{ + size_t inpage = (uintptr_t)start & PAGE_MASK; + if (mprotect((u8*)start - inpage, len + inpage, PROT_READ)) + die("mprotect failed..."); + return true; +} + +bool mem_region_unlock(void *start, size_t len) +{ + size_t inpage = (uintptr_t)start & PAGE_MASK; + if (mprotect((u8*)start - inpage, len + inpage, PROT_READ | PROT_WRITE)) + // Add some way to see why it failed? gdb> info proc mappings + die("mprotect failed..."); + return true; +} + +bool mem_region_set_exec(void *start, size_t len) +{ + size_t inpage = (uintptr_t)start & PAGE_MASK; + if (mprotect((u8*)start - inpage, len + inpage, PROT_READ | PROT_WRITE | PROT_EXEC)) + die("mprotect failed..."); + return true; +} + +void *mem_region_reserve(void *start, size_t len) +{ + void *p = mmap(start, len, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); + if (p == MAP_FAILED) + { + perror("mmap"); + return NULL; + } + else + return p; +} + +bool mem_region_release(void *start, size_t len) +{ + return munmap(start, len) == 0; +} + +void *mem_region_map_file(void *file_handle, void *dest, size_t len, size_t offset, bool readwrite) +{ + int flags = MAP_SHARED | MAP_NOSYNC | (dest != NULL ? MAP_FIXED : 0); + void *p = mmap(dest, len, PROT_READ | (readwrite ? PROT_WRITE : 0), flags, (int)(uintptr_t)file_handle, offset); + if (p == MAP_FAILED) + { + perror("mmap"); + return NULL; + } + else + return p; +} + +bool mem_region_unmap_file(void *start, size_t len) +{ + return mem_region_release(start, len); +} + +// Allocates memory via a fd on shmem/ahmem or even a file on disk +static int allocate_shared_filemem(unsigned size) { + int fd = -1; + #if defined(_ANDROID) + // Use Android's specific shmem stuff. + fd = ashmem_create_region(0, size); + #else + #if HOST_OS != OS_DARWIN + fd = shm_open("/dcnzorz_mem", O_CREAT | O_EXCL | O_RDWR, S_IREAD | S_IWRITE); + shm_unlink("/dcnzorz_mem"); + #endif + + // if shmem does not work (or using OSX) fallback to a regular file on disk + if (fd < 0) { + string path = get_writable_data_path("/dcnzorz_mem"); + fd = open(path.c_str(), O_CREAT|O_RDWR|O_TRUNC, S_IRWXU|S_IRWXG|S_IRWXO); + unlink(path.c_str()); + } + // If we can't open the file, fallback to slow mem. + if (fd < 0) + return -1; + + // Finally make the file as big as we need! + if (ftruncate(fd, size)) { + // Can't get as much memory as needed, fallback. + close(fd); + return -1; + } + #endif + + return fd; +} + +// Implement vmem initialization for RAM, ARAM, VRAM and SH4 context, fpcb etc. +// The function supports allocating 512MB or 4GB addr spaces. + +int vmem_fd = -1; +static int shmem_fd2 = -1; + +// vmem_base_addr points to an address space of 512MB (or 4GB) that can be used for fast memory ops. +// In negative offsets of the pointer (up to FPCB size, usually 65/129MB) the context and jump table +// can be found. If the platform init returns error, the user is responsible for initializing the +// memory using a fallback (that is, regular mallocs and falling back to slow memory JIT). +VMemType vmem_platform_init(void **vmem_base_addr, void **sh4rcb_addr) { + // Firt let's try to allocate the shm-backed memory + vmem_fd = allocate_shared_filemem(RAM_SIZE_MAX + VRAM_SIZE_MAX + ARAM_SIZE_MAX); + if (vmem_fd < 0) + return MemTypeError; + + // Now try to allocate a contiguous piece of memory. + unsigned memsize = 512*1024*1024 + sizeof(Sh4RCB) + ARAM_SIZE_MAX + 0x10000; + void *first_ptr = mem_region_reserve(NULL, memsize); + if (!first_ptr) { + close(vmem_fd); + return MemTypeError; + } + + // Align pointer to 64KB too, some Linaro bug (no idea but let's just be safe I guess). + uintptr_t ptrint = (uintptr_t)first_ptr; + ptrint = (ptrint + 0x10000 - 1) & (~0xffff); + *sh4rcb_addr = (void*)ptrint; + *vmem_base_addr = (void*)(ptrint + sizeof(Sh4RCB)); + void *sh4rcb_base_ptr = (void*)(ptrint + FPCB_SIZE); + + // Now map the memory for the SH4 context, do not include FPCB on purpose (paged on demand). + mem_region_unlock(sh4rcb_base_ptr, sizeof(Sh4RCB) - FPCB_SIZE); + + return MemType512MB; +} + +// Just tries to wipe as much as possible in the relevant area. +void vmem_platform_destroy() { + mem_region_release(virt_ram_base, 0x20000000); +} + +// Resets a chunk of memory by deleting its data and setting its protection back. +void vmem_platform_reset_mem(void *ptr, unsigned size_bytes) { + // Mark them as non accessible. + mprotect(ptr, size_bytes, PROT_NONE); + // Tell the kernel to flush'em all (FIXME: perhaps unmap+mmap 'd be better?) + madvise(ptr, size_bytes, MADV_DONTNEED); + #if defined(MADV_REMOVE) + madvise(ptr, size_bytes, MADV_REMOVE); + #elif defined(MADV_FREE) + madvise(ptr, size_bytes, MADV_FREE); + #endif +} + +// Allocates a bunch of memory (page aligned and page-sized) +void vmem_platform_ondemand_page(void *address, unsigned size_bytes) { + verify(mem_region_unlock(address, size_bytes)); +} + +// Creates mappings to the underlying file including mirroring sections +void vmem_platform_create_mappings(const vmem_mapping *vmem_maps, unsigned nummaps) { + for (unsigned i = 0; i < nummaps; i++) { + // Ignore unmapped stuff, it is already reserved as PROT_NONE + if (!vmem_maps[i].memsize) + continue; + + // Calculate the number of mirrors + unsigned address_range_size = vmem_maps[i].end_address - vmem_maps[i].start_address; + unsigned num_mirrors = (address_range_size) / vmem_maps[i].memsize; + verify((address_range_size % vmem_maps[i].memsize) == 0 && num_mirrors >= 1); + + for (unsigned j = 0; j < num_mirrors; j++) { + unsigned offset = vmem_maps[i].start_address + j * vmem_maps[i].memsize; + verify(mem_region_unmap_file(&virt_ram_base[offset], vmem_maps[i].memsize)); + verify(mem_region_map_file((void*)(uintptr_t)vmem_fd, &virt_ram_base[offset], + vmem_maps[i].memsize, vmem_maps[i].memoffset, vmem_maps[i].allow_writes) != NULL); + } + } +} + +// Prepares the code region for JIT operations, thus marking it as RWX +bool vmem_platform_prepare_jit_block(void *code_area, unsigned size, void **code_area_rwx) { + // Try to map is as RWX, this fails apparently on OSX (and perhaps other systems?) + if (!mem_region_set_exec(code_area, size)) + { + // Well it failed, use another approach, unmap the memory area and remap it back. + // Seems it works well on Darwin according to reicast code :P + munmap(code_area, size); + void *ret_ptr = mmap(code_area, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_ANON, 0, 0); + // Ensure it's the area we requested + if (ret_ptr != code_area) + return false; // Couldn't remap it? Perhaps RWX is disabled? This should never happen in any supported Unix platform. + } + + // Pointer location should be same: + *code_area_rwx = code_area; + return true; +} + +// Use two addr spaces: need to remap something twice, therefore use allocate_shared_filemem() +bool vmem_platform_prepare_jit_block(void *code_area, unsigned size, void **code_area_rw, uintptr_t *rx_offset) { + shmem_fd2 = allocate_shared_filemem(size); + if (shmem_fd2 < 0) + return false; + + // Need to unmap the section we are about to use (it might be already unmapped but nevertheless...) + munmap(code_area, size); + + // Map the RX bits on the code_area, for proximity, as usual. + void *ptr_rx = mmap(code_area, size, PROT_READ | PROT_EXEC, + MAP_SHARED | MAP_NOSYNC | MAP_FIXED, shmem_fd2, 0); + if (ptr_rx != code_area) + return false; + + // Now remap the same memory as RW in some location we don't really care at all. + void *ptr_rw = mmap(NULL, size, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_NOSYNC, shmem_fd2, 0); + + *code_area_rw = ptr_rw; + *rx_offset = (char*)ptr_rx - (char*)ptr_rw; + printf("Info: Using NO_RWX mode, rx ptr: %p, rw ptr: %p, offset: %lu\n", ptr_rx, ptr_rw, (unsigned long)*rx_offset); + + return (ptr_rw != MAP_FAILED); +} + +// Some OSes restrict cache flushing, cause why not right? :D + +#if HOST_CPU == CPU_ARM64 + +// Code borrowed from Dolphin https://github.com/dolphin-emu/dolphin +static void Arm64_CacheFlush(void* start, void* end) { + if (start == end) + return; + +#if HOST_OS == OS_DARWIN + // Header file says this is equivalent to: sys_icache_invalidate(start, end - start); + sys_cache_control(kCacheFunctionPrepareForExecution, start, end - start); +#else + // Don't rely on GCC's __clear_cache implementation, as it caches + // icache/dcache cache line sizes, that can vary between cores on + // big.LITTLE architectures. + u64 addr, ctr_el0; + static size_t icache_line_size = 0xffff, dcache_line_size = 0xffff; + size_t isize, dsize; + + __asm__ volatile("mrs %0, ctr_el0" : "=r"(ctr_el0)); + isize = 4 << ((ctr_el0 >> 0) & 0xf); + dsize = 4 << ((ctr_el0 >> 16) & 0xf); + + // use the global minimum cache line size + icache_line_size = isize = icache_line_size < isize ? icache_line_size : isize; + dcache_line_size = dsize = dcache_line_size < dsize ? dcache_line_size : dsize; + + addr = (u64)start & ~(u64)(dsize - 1); + for (; addr < (u64)end; addr += dsize) + // use "civac" instead of "cvau", as this is the suggested workaround for + // Cortex-A53 errata 819472, 826319, 827319 and 824069. + __asm__ volatile("dc civac, %0" : : "r"(addr) : "memory"); + __asm__ volatile("dsb ish" : : : "memory"); + + addr = (u64)start & ~(u64)(isize - 1); + for (; addr < (u64)end; addr += isize) + __asm__ volatile("ic ivau, %0" : : "r"(addr) : "memory"); + + __asm__ volatile("dsb ish" : : : "memory"); + __asm__ volatile("isb" : : : "memory"); +#endif +} + + +void vmem_platform_flush_cache(void *icache_start, void *icache_end, void *dcache_start, void *dcache_end) { + Arm64_CacheFlush(dcache_start, dcache_end); + + // Dont risk it and flush and invalidate icache&dcache for both ranges just in case. + if (icache_start != dcache_start) + Arm64_CacheFlush(icache_start, icache_end); +} + +#endif // #if HOST_CPU == CPU_ARM64 + diff --git a/core/nullDC.cpp b/core/nullDC.cpp index 5c5e357a4..be9042423 100755 --- a/core/nullDC.cpp +++ b/core/nullDC.cpp @@ -23,6 +23,7 @@ #include "imgread/common.h" #include "rend/gui.h" #include "profiler/profiler.h" +#include "input/gamepad_device.h" void FlushCache(); void LoadCustom(); @@ -99,10 +100,6 @@ s32 plugins_Init() if (s32 rv = libGDR_Init()) return rv; #endif -#if DC_PLATFORM == DC_PLATFORM_NAOMI || DC_PLATFORM == DC_PLATFORM_ATOMISWAVE - if (!naomi_cart_SelectFile()) - return rv_serror; -#endif if (s32 rv = libAICA_Init()) return rv; @@ -142,7 +139,7 @@ void LoadSpecialSettings() extra_depth_game = false; full_mmu_game = false; disable_vmem32_game = false; - + if (reios_windows_ce) { printf("Enabling Full MMU and Extra depth scaling for Windows CE game\n"); @@ -218,7 +215,7 @@ void LoadSpecialSettings() } #elif DC_PLATFORM == DC_PLATFORM_NAOMI || DC_PLATFORM == DC_PLATFORM_ATOMISWAVE printf("Game ID is [%s]\n", naomi_game_id); - + if (!strcmp("METAL SLUG 6", naomi_game_id) || !strcmp("WAVE RUNNER GP", naomi_game_id)) { printf("Enabling Dynarec safe mode for game %s\n", naomi_game_id); @@ -241,12 +238,14 @@ void LoadSpecialSettings() printf("Enabling JVS rotary encoders for game %s\n", naomi_game_id); settings.input.JammaSetup = 2; } - else if (!strcmp("POWER STONE 2 JAPAN", naomi_game_id)) + else if (!strcmp("POWER STONE 2 JAPAN", naomi_game_id) // Naomi + || !strcmp("GUILTY GEAR isuka", naomi_game_id)) // AW { printf("Enabling 4-player setup for game %s\n", naomi_game_id); settings.input.JammaSetup = 1; } - else if (!strcmp("SEGA MARINE FISHING JAPAN", naomi_game_id)) + else if (!strcmp("SEGA MARINE FISHING JAPAN", naomi_game_id) + || !strcmp(naomi_game_id, "BASS FISHING SIMULATOR VER.A")) // AW { printf("Enabling specific JVS setup for game %s\n", naomi_game_id); settings.input.JammaSetup = 3; @@ -256,9 +255,11 @@ void LoadSpecialSettings() printf("Enabling specific JVS setup for game %s\n", naomi_game_id); settings.input.JammaSetup = 4; } - else if (!strcmp("NINJA ASSAULT", naomi_game_id)) + else if (!strcmp("NINJA ASSAULT", naomi_game_id) + || !strcmp(naomi_game_id, "Sports Shooting USA") // AW + || !strcmp(naomi_game_id, "SEGA CLAY CHALLENGE")) // AW { - printf("Enabling specific JVS setup for game %s\n", naomi_game_id); + printf("Enabling lightgun setup for game %s\n", naomi_game_id); settings.input.JammaSetup = 5; } else if (!strcmp(" BIOHAZARD GUN SURVIVOR2", naomi_game_id)) @@ -284,6 +285,7 @@ void dc_reset() } static bool init_done; +static bool reset_requested; int reicast_init(int argc, char* argv[]) { @@ -344,10 +346,25 @@ int dc_start_game(const char *path) InitSettings(); LoadSettings(false); #if DC_PLATFORM == DC_PLATFORM_DREAMCAST - if (DiscSwap()) - LoadCustom(); + if (!settings.bios.UseReios) +#endif + if (!LoadRomFiles(get_readonly_data_path(DATA_PATH))) + return -5; + +#if DC_PLATFORM == DC_PLATFORM_DREAMCAST + if (path == NULL) + { + // Boot BIOS + settings.imgread.LastImage[0] = 0; + TermDrive(); + InitDrive(); + } + else + { + if (DiscSwap()) + LoadCustom(); + } #elif DC_PLATFORM == DC_PLATFORM_NAOMI || DC_PLATFORM == DC_PLATFORM_ATOMISWAVE - LoadRomFiles(get_readonly_data_path(DATA_PATH)); if (!naomi_cart_SelectFile()) return -6; LoadCustom(); @@ -365,7 +382,6 @@ int dc_start_game(const char *path) return 0; } - settings.dreamcast.RTC = GetRTC_now(); // FIXME This shouldn't be in settings anymore if (settings.bios.UseReios || !LoadRomFiles(get_readonly_data_path(DATA_PATH))) { #ifdef USE_REIOS @@ -386,6 +402,11 @@ int dc_start_game(const char *path) if (plugins_Init()) return -3; +#if DC_PLATFORM == DC_PLATFORM_NAOMI || DC_PLATFORM == DC_PLATFORM_ATOMISWAVE + if (!naomi_cart_SelectFile()) + return -6; +#endif + LoadCustom(); #if FEAT_SHREC != DYNAREC_NONE @@ -446,9 +467,18 @@ void* dc_run(void*) Get_Sh4Interpreter(&sh4_cpu); printf("Using Interpreter\n"); } - sh4_cpu.Run(); + do { + reset_requested = false; + + sh4_cpu.Run(); + + SaveRomFiles(get_writable_data_path("/data/")); + if (reset_requested) + { + dc_reset(); + } + } while (reset_requested); - SaveRomFiles(get_writable_data_path("/data/")); TermAudio(); return NULL; @@ -476,6 +506,13 @@ void dc_stop() emu_thread.WaitToEnd(); } +// Called on the emulator thread for soft reset +void dc_request_reset() +{ + reset_requested = true; + sh4_cpu.Stop(); +} + void dc_exit() { dc_stop(); @@ -484,7 +521,6 @@ void dc_exit() void InitSettings() { - settings.dreamcast.RTC = GetRTC_now(); settings.dynarec.Enable = true; settings.dynarec.idleskip = true; settings.dynarec.unstable_opt = false; @@ -495,10 +531,12 @@ void InitSettings() settings.dreamcast.broadcast = 4; // default settings.dreamcast.language = 6; // default settings.dreamcast.FullMMU = false; + settings.dynarec.SmcCheckLevel = FullCheck; settings.aica.DSPEnabled = false; - settings.aica.LimitFPS = true; + settings.aica.LimitFPS = LimitFPSEnabled; settings.aica.NoBatch = false; settings.aica.NoSound = false; + settings.audio.backend = "auto"; settings.rend.UseMipmaps = true; settings.rend.WideScreen = false; settings.rend.ShowFPS = false; @@ -513,6 +551,11 @@ void InitSettings() settings.rend.CustomTextures = false; settings.rend.DumpTextures = false; settings.rend.ScreenScaling = 100; + settings.rend.ScreenStretching = 100; + settings.rend.Fog = true; + settings.rend.FloatVMUs = false; + settings.rend.Rotate90 = false; + settings.rend.PerStripSorting = false; settings.pvr.ta_skip = 0; settings.pvr.rend = 0; @@ -559,21 +602,24 @@ void LoadSettings(bool game_specific) { const char *config_section = game_specific ? cfgGetGameId() : "config"; const char *input_section = game_specific ? cfgGetGameId() : "input"; + const char *audio_section = game_specific ? cfgGetGameId() : "audio"; settings.dynarec.Enable = cfgLoadBool(config_section, "Dynarec.Enabled", settings.dynarec.Enable); settings.dynarec.idleskip = cfgLoadBool(config_section, "Dynarec.idleskip", settings.dynarec.idleskip); settings.dynarec.unstable_opt = cfgLoadBool(config_section, "Dynarec.unstable-opt", settings.dynarec.unstable_opt); settings.dynarec.safemode = cfgLoadBool(config_section, "Dynarec.safe-mode", settings.dynarec.safemode); settings.dynarec.disable_vmem32 = cfgLoadBool(config_section, "Dynarec.DisableVmem32", settings.dynarec.disable_vmem32); + settings.dynarec.SmcCheckLevel = (SmcCheckEnum)cfgLoadInt(config_section, "Dynarec.SmcCheckLevel", settings.dynarec.SmcCheckLevel); //disable_nvmem can't be loaded, because nvmem init is before cfg load settings.dreamcast.cable = cfgLoadInt(config_section, "Dreamcast.Cable", settings.dreamcast.cable); settings.dreamcast.region = cfgLoadInt(config_section, "Dreamcast.Region", settings.dreamcast.region); settings.dreamcast.broadcast = cfgLoadInt(config_section, "Dreamcast.Broadcast", settings.dreamcast.broadcast); settings.dreamcast.language = cfgLoadInt(config_section, "Dreamcast.Language", settings.dreamcast.language); settings.dreamcast.FullMMU = cfgLoadBool(config_section, "Dreamcast.FullMMU", settings.dreamcast.FullMMU); - settings.aica.LimitFPS = cfgLoadBool(config_section, "aica.LimitFPS", settings.aica.LimitFPS); + settings.aica.LimitFPS = (LimitFPSEnum)cfgLoadInt(config_section, "aica.LimitFPS", (int)settings.aica.LimitFPS); settings.aica.DSPEnabled = cfgLoadBool(config_section, "aica.DSPEnabled", settings.aica.DSPEnabled); settings.aica.NoSound = cfgLoadBool(config_section, "aica.NoSound", settings.aica.NoSound); + settings.audio.backend = cfgLoadStr(audio_section, "backend", settings.audio.backend.c_str()); settings.rend.UseMipmaps = cfgLoadBool(config_section, "rend.UseMipmaps", settings.rend.UseMipmaps); settings.rend.WideScreen = cfgLoadBool(config_section, "rend.WideScreen", settings.rend.WideScreen); settings.rend.ShowFPS = cfgLoadBool(config_section, "rend.ShowFPS", settings.rend.ShowFPS); @@ -595,6 +641,11 @@ void LoadSettings(bool game_specific) settings.rend.DumpTextures = cfgLoadBool(config_section, "rend.DumpTextures", settings.rend.DumpTextures); settings.rend.ScreenScaling = cfgLoadInt(config_section, "rend.ScreenScaling", settings.rend.ScreenScaling); settings.rend.ScreenScaling = min(max(1, settings.rend.ScreenScaling), 100); + settings.rend.ScreenStretching = cfgLoadInt(config_section, "rend.ScreenStretching", settings.rend.ScreenStretching); + settings.rend.Fog = cfgLoadBool(config_section, "rend.Fog", settings.rend.Fog); + settings.rend.FloatVMUs = cfgLoadBool(config_section, "rend.FloatVMUs", settings.rend.FloatVMUs); + settings.rend.Rotate90 = cfgLoadBool(config_section, "rend.Rotate90", settings.rend.Rotate90); + settings.rend.PerStripSorting = cfgLoadBool(config_section, "rend.PerStripSorting", settings.rend.PerStripSorting); settings.pvr.ta_skip = cfgLoadInt(config_section, "ta.skip", settings.pvr.ta_skip); settings.pvr.rend = cfgLoadInt(config_section, "pvr.rend", settings.pvr.rend); @@ -659,9 +710,9 @@ void LoadSettings(bool game_specific) } /* //make sure values are valid - settings.dreamcast.cable = min(max(settings.dreamcast.cable, 0),3); - settings.dreamcast.region = min(max(settings.dreamcast.region, 0),3); - settings.dreamcast.broadcast= min(max(settings.dreamcast.broadcast,0),4); + settings.dreamcast.cable = min(max(settings.dreamcast.cable, 0),3); + settings.dreamcast.region = min(max(settings.dreamcast.region, 0),3); + settings.dreamcast.broadcast = min(max(settings.dreamcast.broadcast,0),4); */ } @@ -701,12 +752,35 @@ void SaveSettings() cfgSaveBool("config", "Dynarec.unstable-opt", settings.dynarec.unstable_opt); if (!safemode_game || !settings.dynarec.safemode) cfgSaveBool("config", "Dynarec.safe-mode", settings.dynarec.safemode); + cfgSaveInt("config", "Dynarec.SmcCheckLevel", (int)settings.dynarec.SmcCheckLevel); + if (!disable_vmem32_game || !settings.dynarec.disable_vmem32) cfgSaveBool("config", "Dynarec.DisableVmem32", settings.dynarec.disable_vmem32); cfgSaveInt("config", "Dreamcast.Language", settings.dreamcast.language); - cfgSaveBool("config", "aica.LimitFPS", settings.aica.LimitFPS); + cfgSaveInt("config", "aica.LimitFPS", (int)settings.aica.LimitFPS); cfgSaveBool("config", "aica.DSPEnabled", settings.aica.DSPEnabled); cfgSaveBool("config", "aica.NoSound", settings.aica.NoSound); + cfgSaveStr("audio", "backend", settings.audio.backend.c_str()); + + // Write backend specific settings + // std::map> + for (std::map>::iterator it = settings.audio.options.begin(); it != settings.audio.options.end(); ++it) + { + + std::pair> p = (std::pair>)*it; + std::string section = p.first; + std::map options = p.second; + + for (std::map::iterator it2 = options.begin(); it2 != options.end(); ++it2) + { + std::pair p2 = (std::pair)*it2; + std::string key = p2.first; + std::string val = p2.second; + + cfgSaveStr(section.c_str(), key.c_str(), val.c_str()); + } + } + cfgSaveBool("config", "rend.WideScreen", settings.rend.WideScreen); cfgSaveBool("config", "rend.ShowFPS", settings.rend.ShowFPS); if (!rtt_to_buffer_game || !settings.rend.RenderToTextureBuffer) @@ -719,8 +793,13 @@ void SaveSettings() cfgSaveBool("config", "rend.CustomTextures", settings.rend.CustomTextures); cfgSaveBool("config", "rend.DumpTextures", settings.rend.DumpTextures); cfgSaveInt("config", "rend.ScreenScaling", settings.rend.ScreenScaling); + cfgSaveInt("config", "rend.ScreenStretching", settings.rend.ScreenStretching); + cfgSaveBool("config", "rend.Fog", settings.rend.Fog); + cfgSaveBool("config", "rend.FloatVMUs", settings.rend.FloatVMUs); + cfgSaveBool("config", "rend.Rotate90", settings.rend.Rotate90); cfgSaveInt("config", "ta.skip", settings.pvr.ta_skip); cfgSaveInt("config", "pvr.rend", settings.pvr.rend); + cfgSaveBool("config", "rend.PerStripSorting", settings.rend.PerStripSorting); cfgSaveInt("config", "pvr.MaxThreads", settings.pvr.MaxThreads); cfgSaveBool("config", "pvr.SynchronousRendering", settings.pvr.SynchronousRender); @@ -747,6 +826,9 @@ void SaveSettings() paths += path; } cfgSaveStr("config", "Dreamcast.ContentPath", paths.c_str()); + + GamepadDevice::SaveMaplePorts(); + #ifdef _ANDROID void SaveAndroidSettings(); SaveAndroidSettings(); diff --git a/core/oslib/audiobackend_alsa.cpp b/core/oslib/audiobackend_alsa.cpp index 0f2a5599c..2ac08fe46 100644 --- a/core/oslib/audiobackend_alsa.cpp +++ b/core/oslib/audiobackend_alsa.cpp @@ -1,6 +1,7 @@ -#include "oslib/audiobackend_alsa.h" +#include "oslib/audiostream.h" #if USE_ALSA #include +#include "cfg/cfg.h" static snd_pcm_t *handle; static bool pcm_blocking = true; @@ -10,29 +11,63 @@ static snd_pcm_uframes_t period_size; // We're making these functions static - there's no need to pollute the global namespace static void alsa_init() { - - long loops; - int size; - snd_pcm_hw_params_t *params; unsigned int val; int dir=-1; - /* Open PCM device for playback. */ - int rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0); + string device = cfgLoadStr("alsa", "device", ""); - if (rc<0) - rc = snd_pcm_open(&handle, "plughw:0,0,0", SND_PCM_STREAM_PLAYBACK, 0); + int rc = -1; + if (device == "" || device == "auto") + { + printf("ALSA: trying to determine audio device\n"); - if (rc<0) - rc = snd_pcm_open(&handle, "plughw:0,0", SND_PCM_STREAM_PLAYBACK, 0); + // trying default device + device = "default"; + rc = snd_pcm_open(&handle, device.c_str(), SND_PCM_STREAM_PLAYBACK, 0); + + // "default" didn't work, try first device + if (rc < 0) + { + device = "plughw:0,0,0"; + rc = snd_pcm_open(&handle, device.c_str(), SND_PCM_STREAM_PLAYBACK, 0); + + if (rc < 0) + { + device = "plughw:0,0"; + rc = snd_pcm_open(&handle, device.c_str(), SND_PCM_STREAM_PLAYBACK, 0); + } + } + + // first didn't work, try second + if (rc < 0) + { + device = "plughw:1,0"; + rc = snd_pcm_open(&handle, device.c_str(), SND_PCM_STREAM_PLAYBACK, 0); + } + + // try pulse audio backend + if (rc < 0) + { + device = "pulse"; + rc = snd_pcm_open(&handle, device.c_str(), SND_PCM_STREAM_PLAYBACK, 0); + } + + if (rc < 0) + printf("ALSA: unable to automatically determine audio device.\n"); + } + else { + rc = snd_pcm_open(&handle, device.c_str(), SND_PCM_STREAM_PLAYBACK, 0); + } if (rc < 0) { - fprintf(stderr, "unable to open PCM device: %s\n", snd_strerror(rc)); + fprintf(stderr, "ALSA: unable to open PCM device %s: %s\n", device.c_str(), snd_strerror(rc)); return; } + printf("ALSA: Successfully initialized \"%s\"\n", device.c_str()); + /* Allocate a hardware parameters object. */ snd_pcm_hw_params_alloca(¶ms); @@ -40,7 +75,7 @@ static void alsa_init() rc=snd_pcm_hw_params_any(handle, params); if (rc < 0) { - fprintf(stderr, "Error:snd_pcm_hw_params_any %s\n", snd_strerror(rc)); + fprintf(stderr, "ALSA: Error:snd_pcm_hw_params_any %s\n", snd_strerror(rc)); return; } @@ -50,7 +85,7 @@ static void alsa_init() rc=snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); if (rc < 0) { - fprintf(stderr, "Error:snd_pcm_hw_params_set_access %s\n", snd_strerror(rc)); + fprintf(stderr, "ALSA: Error:snd_pcm_hw_params_set_access %s\n", snd_strerror(rc)); return; } @@ -58,7 +93,7 @@ static void alsa_init() rc=snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE); if (rc < 0) { - fprintf(stderr, "Error:snd_pcm_hw_params_set_format %s\n", snd_strerror(rc)); + fprintf(stderr, "ALSA: Error:snd_pcm_hw_params_set_format %s\n", snd_strerror(rc)); return; } @@ -66,7 +101,7 @@ static void alsa_init() rc=snd_pcm_hw_params_set_channels(handle, params, 2); if (rc < 0) { - fprintf(stderr, "Error:snd_pcm_hw_params_set_channels %s\n", snd_strerror(rc)); + fprintf(stderr, "ALSA: Error:snd_pcm_hw_params_set_channels %s\n", snd_strerror(rc)); return; } @@ -75,7 +110,7 @@ static void alsa_init() rc=snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir); if (rc < 0) { - fprintf(stderr, "Error:snd_pcm_hw_params_set_rate_near %s\n", snd_strerror(rc)); + fprintf(stderr, "ALSA: Error:snd_pcm_hw_params_set_rate_near %s\n", snd_strerror(rc)); return; } @@ -84,26 +119,31 @@ static void alsa_init() rc=snd_pcm_hw_params_set_period_size_near(handle, params, &period_size, &dir); if (rc < 0) { - fprintf(stderr, "Error:snd_pcm_hw_params_set_buffer_size_near %s\n", snd_strerror(rc)); + fprintf(stderr, "ALSA: Error:snd_pcm_hw_params_set_buffer_size_near %s\n", snd_strerror(rc)); return; } else + { printf("ALSA: period size set to %ld\n", period_size); + } + buffer_size = (44100 * 100 /* settings.omx.Audio_Latency */ / 1000 / period_size + 1) * period_size; rc=snd_pcm_hw_params_set_buffer_size_near(handle, params, &buffer_size); if (rc < 0) { - fprintf(stderr, "Error:snd_pcm_hw_params_set_buffer_size_near %s\n", snd_strerror(rc)); + fprintf(stderr, "ALSA: Error:snd_pcm_hw_params_set_buffer_size_near %s\n", snd_strerror(rc)); return; } else + { printf("ALSA: buffer size set to %ld\n", buffer_size); + } /* Write the parameters to the driver */ rc = snd_pcm_hw_params(handle, params); if (rc < 0) { - fprintf(stderr, "Unable to set hw parameters: %s\n", snd_strerror(rc)); + fprintf(stderr, "ALSA: Unable to set hw parameters: %s\n", snd_strerror(rc)); return; } } @@ -119,30 +159,13 @@ static u32 alsa_push(void* frame, u32 samples, bool wait) if (rc == -EPIPE) { /* EPIPE means underrun */ - fprintf(stderr, "ALSA: underrun occurred\n"); snd_pcm_prepare(handle); // Write some silence then our samples const size_t silence_size = period_size * 4; void *silence = alloca(silence_size * 4); memset(silence, 0, silence_size * 4); - rc = snd_pcm_writei(handle, silence, silence_size); - if (rc < 0) - fprintf(stderr, "ALSA: error from writei(silence): %s\n", snd_strerror(rc)); - else if (rc < silence_size) - fprintf(stderr, "ALSA: short write from writei(silence): %d/%ld frames\n", rc, silence_size); - rc = snd_pcm_writei(handle, frame, samples); - if (rc < 0) - fprintf(stderr, "ALSA: error from writei(again): %s\n", snd_strerror(rc)); - else if (rc < samples) - fprintf(stderr, "ALSA: short write from writei(again): %d/%d frames\n", rc, samples); - } - else if (rc < 0) - { - fprintf(stderr, "ALSA: error from writei: %s\n", snd_strerror(rc)); - } - else if (rc != samples) - { - fprintf(stderr, "ALSA: short write, wrote %d frames of %d\n", rc, samples); + snd_pcm_writei(handle, silence, silence_size); + snd_pcm_writei(handle, frame, samples); } return 1; } @@ -153,11 +176,84 @@ static void alsa_term() snd_pcm_close(handle); } -audiobackend_t audiobackend_alsa = { +std::vector alsa_get_devicelist() +{ + std::vector result; + + char **hints; + int err = snd_device_name_hint(-1, "pcm", (void***)&hints); + + // Error initializing ALSA + if (err != 0) + return result; + + // special value to automatically detect on initialization + result.push_back("auto"); + + char** n = hints; + while (*n != NULL) + { + // Get the type (NULL/Input/Output) + char *type = snd_device_name_get_hint(*n, "IOID"); + char *name = snd_device_name_get_hint(*n, "NAME"); + + if (name != NULL) + { + // We only want output or special devices (like "default" or "pulse") + // TODO Only those with type == NULL? + if (type == NULL || strcmp(type, "Output") == 0) + { + // TODO Check if device works (however we need to hash the resulting list then) + /*snd_pcm_t *handle; + int rc = snd_pcm_open(&handle, name, SND_PCM_STREAM_PLAYBACK, 0); + + if (rc == 0) + { + result.push_back(name); + snd_pcm_close(handle); + } + */ + + result.push_back(name); + } + + } + + if (type != NULL) + free(type); + + if (name != NULL) + free(name); + + n++; + } + + snd_device_name_free_hint((void**)hints); + + return result; +} + +static audio_option_t* alsa_audio_options(int* option_count) +{ + *option_count = 1; + static audio_option_t result[1]; + + result[0].cfg_name = "device"; + result[0].caption = "Device"; + result[0].type = list; + result[0].list_callback = alsa_get_devicelist; + + return result; +} + +static audiobackend_t audiobackend_alsa = { "alsa", // Slug "Advanced Linux Sound Architecture", // Name &alsa_init, &alsa_push, - &alsa_term + &alsa_term, + &alsa_audio_options }; + +static bool alsa = RegisterAudioBackend(&audiobackend_alsa); #endif diff --git a/core/oslib/audiobackend_alsa.h b/core/oslib/audiobackend_alsa.h deleted file mode 100644 index 3995cb1ac..000000000 --- a/core/oslib/audiobackend_alsa.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include "oslib/audiostream.h" - -extern audiobackend_t audiobackend_alsa; diff --git a/core/oslib/audiobackend_android.h b/core/oslib/audiobackend_android.h deleted file mode 100644 index c2765c14b..000000000 --- a/core/oslib/audiobackend_android.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include "oslib/audiostream.h" - -extern audiobackend_t audiobackend_android; \ No newline at end of file diff --git a/core/oslib/audiobackend_coreaudio.cpp b/core/oslib/audiobackend_coreaudio.cpp index 9ce685cd9..4f7cb44a8 100644 --- a/core/oslib/audiobackend_coreaudio.cpp +++ b/core/oslib/audiobackend_coreaudio.cpp @@ -1,18 +1,18 @@ /* Simple Core Audio backend for osx (and maybe ios?) Based off various audio core samples and dolphin's code - + This is part of the Reicast project, please consult the LICENSE file for licensing & related information - + This could do with some locking logic to avoid race conditions, and some variable length buffer logic to support chunk sizes other than 512 bytes - + It does work on my macmini though */ -#include "oslib/audiobackend_coreaudio.h" +#include "oslib/audiostream.h" #if HOST_OS == OS_DARWIN #include @@ -28,7 +28,7 @@ static u8 samples_temp[BUFSIZE]; static std::atomic samples_wptr; static std::atomic samples_rptr; -static cResetEvent bufferEmpty(false, true); +static cResetEvent bufferEmpty; static OSStatus coreaudio_callback(void* ctx, AudioUnitRenderActionFlags* flags, const AudioTimeStamp* ts, UInt32 bus, UInt32 frames, AudioBufferList* abl) @@ -49,9 +49,9 @@ static OSStatus coreaudio_callback(void* ctx, AudioUnitRenderActionFlags* flags, samples_rptr = (samples_rptr + buf_size) % BUFSIZE; } } - + bufferEmpty.Set(); - + return noErr; } @@ -63,7 +63,7 @@ static void coreaudio_init() AudioStreamBasicDescription format; AudioComponentDescription desc; AudioComponent component; - + desc.componentType = kAudioUnitType_Output; #if !defined(TARGET_IPHONE) desc.componentSubType = kAudioUnitSubType_DefaultOutput; @@ -75,12 +75,12 @@ static void coreaudio_init() desc.componentFlagsMask = 0; desc.componentManufacturer = kAudioUnitManufacturer_Apple; component = AudioComponentFindNext(nullptr, &desc); - + verify(component != nullptr); - + err = AudioComponentInstanceNew(component, &audioUnit); verify(err == noErr); - + FillOutASBDForLPCM(format, 44100, 2, 16, 16, false, false, false); err = AudioUnitSetProperty(audioUnit, @@ -88,7 +88,7 @@ static void coreaudio_init() kAudioUnitScope_Input, 0, &format, sizeof(AudioStreamBasicDescription)); verify(err == noErr); - + callback_struct.inputProc = coreaudio_callback; callback_struct.inputProcRefCon = 0; err = AudioUnitSetProperty(audioUnit, @@ -96,24 +96,24 @@ static void coreaudio_init() kAudioUnitScope_Input, 0, &callback_struct, sizeof callback_struct); verify(err == noErr); - + /* err = AudioUnitSetParameter(audioUnit, kHALOutputParam_Volume, kAudioUnitParameterFlag_Output, 0, 1, 0); verify(err == noErr); - + */ - + err = AudioUnitInitialize(audioUnit); - + verify(err == noErr); - + err = AudioOutputUnitStart(audioUnit); verify(err == noErr); - + bufferEmpty.Set(); } @@ -134,23 +134,23 @@ static u32 coreaudio_push(void* frame, u32 samples, bool wait) samples_wptr = (samples_wptr + byte_size) % BUFSIZE; break; } - + return 1; } static void coreaudio_term() { OSStatus err; - + err = AudioOutputUnitStop(audioUnit); verify(err == noErr); - + err = AudioUnitUninitialize(audioUnit); verify(err == noErr); - + err = AudioComponentInstanceDispose(audioUnit); verify(err == noErr); - + bufferEmpty.Set(); } @@ -159,6 +159,10 @@ audiobackend_t audiobackend_coreaudio = { "Core Audio", // Name &coreaudio_init, &coreaudio_push, - &coreaudio_term + &coreaudio_term, + NULL }; + +static bool core = RegisterAudioBackend(&audiobackend_coreaudio); + #endif diff --git a/core/oslib/audiobackend_coreaudio.h b/core/oslib/audiobackend_coreaudio.h deleted file mode 100644 index 8f3172317..000000000 --- a/core/oslib/audiobackend_coreaudio.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once -#include "oslib/audiostream.h" - -extern audiobackend_t audiobackend_coreaudio; - diff --git a/core/oslib/audiobackend_directsound.cpp b/core/oslib/audiobackend_directsound.cpp index 36a4d692c..f004bdfd2 100644 --- a/core/oslib/audiobackend_directsound.cpp +++ b/core/oslib/audiobackend_directsound.cpp @@ -1,4 +1,4 @@ -#include "oslib/audiobackend_directsound.h" +#include "oslib/audiostream.h" #if HOST_OS==OS_WINDOWS #include "oslib.h" #include @@ -19,31 +19,31 @@ static void directsound_init() verifyc(dsound->SetCooperativeLevel((HWND)libPvr_GetRenderTarget(),DSSCL_PRIORITY)); IDirectSoundBuffer* buffer_; - WAVEFORMATEX wfx; - DSBUFFERDESC desc; + WAVEFORMATEX wfx; + DSBUFFERDESC desc; - // Set up WAV format structure. + // Set up WAV format structure. - memset(&wfx, 0, sizeof(WAVEFORMATEX)); - wfx.wFormatTag = WAVE_FORMAT_PCM; - wfx.nChannels = 2; - wfx.nSamplesPerSec = 44100; - wfx.nBlockAlign = 4; - wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; - wfx.wBitsPerSample = 16; + memset(&wfx, 0, sizeof(WAVEFORMATEX)); + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nChannels = 2; + wfx.nSamplesPerSec = 44100; + wfx.nBlockAlign = 4; + wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; + wfx.wBitsPerSample = 16; - // Set up DSBUFFERDESC structure. + // Set up DSBUFFERDESC structure. ds_ring_size=8192*wfx.nBlockAlign; - memset(&desc, 0, sizeof(DSBUFFERDESC)); - desc.dwSize = sizeof(DSBUFFERDESC); - desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY;// _CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY; - - desc.dwBufferBytes = ds_ring_size; - desc.lpwfxFormat = &wfx; + memset(&desc, 0, sizeof(DSBUFFERDESC)); + desc.dwSize = sizeof(DSBUFFERDESC); + desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY;// _CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY; + + desc.dwBufferBytes = ds_ring_size; + desc.lpwfxFormat = &wfx; + - if (settings.aica.HW_mixing==0) { @@ -71,7 +71,7 @@ static void directsound_init() //Play the buffer ! verifyc(buffer->Play(0,0,DSBPLAY_LOOPING)); - + } @@ -159,7 +159,7 @@ static u32 directsound_push(void* frame, u32 samples, bool wait) wait &= w; */ int ffs=1; - + /* while (directsound_IsAudioBufferedLots() && wait) if (ffs == 0) @@ -175,7 +175,7 @@ static u32 directsound_push(void* frame, u32 samples, bool wait) static void directsound_term() { buffer->Stop(); - + buffer->Release(); dsound->Release(); } @@ -185,6 +185,9 @@ audiobackend_t audiobackend_directsound = { "Microsoft DirectSound", // Name &directsound_init, &directsound_push, - &directsound_term + &directsound_term, + NULL }; + +static bool ds = RegisterAudioBackend(&audiobackend_directsound); #endif diff --git a/core/oslib/audiobackend_directsound.h b/core/oslib/audiobackend_directsound.h deleted file mode 100644 index 544ba629d..000000000 --- a/core/oslib/audiobackend_directsound.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include "oslib/audiostream.h" - -extern audiobackend_t audiobackend_directsound; diff --git a/core/oslib/audiobackend_libao.cpp b/core/oslib/audiobackend_libao.cpp index 1445c1492..0a6a72e7d 100644 --- a/core/oslib/audiobackend_libao.cpp +++ b/core/oslib/audiobackend_libao.cpp @@ -1,4 +1,4 @@ -#include "oslib/audiobackend_libao.h" +#include "oslib/audiostream.h" #ifdef USE_LIBAO #include @@ -10,12 +10,12 @@ static void libao_init() { ao_initialize(); memset(&aoformat, 0, sizeof(aoformat)); - + aoformat.bits = 16; aoformat.channels = 2; aoformat.rate = 44100; aoformat.byte_format = AO_FMT_LITTLE; - + aodevice = ao_open_live(ao_default_driver_id(), &aoformat, NULL); // Live output if (!aodevice) aodevice = ao_open_live(ao_driver_id("null"), &aoformat, NULL); @@ -23,13 +23,13 @@ static void libao_init() static u32 libao_push(void* frame, u32 samples, bool wait) { - if (aodevice) + if (aodevice) ao_play(aodevice, (char*)frame, samples * 4); - + return 1; } -static void libao_term() +static void libao_term() { if (aodevice) { @@ -43,7 +43,9 @@ audiobackend_t audiobackend_libao = { "libao", // Name &libao_init, &libao_push, - &libao_term + &libao_term, + NULL }; +static bool ao = RegisterAudioBackend(&audiobackend_libao); #endif diff --git a/core/oslib/audiobackend_libao.h b/core/oslib/audiobackend_libao.h deleted file mode 100644 index 5bf957286..000000000 --- a/core/oslib/audiobackend_libao.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include "oslib/audiostream.h" - -extern audiobackend_t audiobackend_libao; diff --git a/core/oslib/audiobackend_omx.cpp b/core/oslib/audiobackend_omx.cpp index ffb0779a9..c8c8a6536 100644 --- a/core/oslib/audiobackend_omx.cpp +++ b/core/oslib/audiobackend_omx.cpp @@ -1,4 +1,4 @@ -#include "oslib/audiobackend_omx.h" +#include "oslib/audiostream.h" #if USE_OMX #include @@ -313,7 +313,9 @@ audiobackend_t audiobackend_omx = { "OpenMAX IL", // Name &omx_init, &omx_push, - &omx_term + &omx_term, + NULL }; +static bool omx = RegisterAudioBackend(&audiobackend_omx); #endif diff --git a/core/oslib/audiobackend_omx.h b/core/oslib/audiobackend_omx.h deleted file mode 100644 index 63fdcb5fd..000000000 --- a/core/oslib/audiobackend_omx.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include "oslib/audiostream.h" - -extern audiobackend_t audiobackend_omx; diff --git a/core/oslib/audiobackend_oss.cpp b/core/oslib/audiobackend_oss.cpp index 67015db6c..b3a48cbd6 100644 --- a/core/oslib/audiobackend_oss.cpp +++ b/core/oslib/audiobackend_oss.cpp @@ -1,4 +1,4 @@ -#include "oslib/audiobackend_oss.h" +#include "oslib/audiostream.h" #ifdef USE_OSS #include #include @@ -48,7 +48,9 @@ audiobackend_t audiobackend_oss = { "Open Sound System", // Name &oss_init, &oss_push, - &oss_term + &oss_term, + NULL }; +static bool oss = RegisterAudioBackend(&audiobackend_oss); #endif diff --git a/core/oslib/audiobackend_oss.h b/core/oslib/audiobackend_oss.h deleted file mode 100644 index cfeb53955..000000000 --- a/core/oslib/audiobackend_oss.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include "oslib/audiostream.h" - -extern audiobackend_t audiobackend_oss; diff --git a/core/oslib/audiobackend_pulseaudio.cpp b/core/oslib/audiobackend_pulseaudio.cpp index dea4256cc..b651b4003 100644 --- a/core/oslib/audiobackend_pulseaudio.cpp +++ b/core/oslib/audiobackend_pulseaudio.cpp @@ -1,4 +1,4 @@ -#include "oslib/audiobackend_pulseaudio.h" +#include "oslib/audiostream.h" #ifdef USE_PULSEAUDIO #include #include @@ -43,6 +43,9 @@ audiobackend_t audiobackend_pulseaudio = { "PulseAudio", // Name &pulseaudio_init, &pulseaudio_push, - &pulseaudio_term + &pulseaudio_term, + NULL }; + +static bool pulse = RegisterAudioBackend(&audiobackend_pulseaudio); #endif diff --git a/core/oslib/audiobackend_pulseaudio.h b/core/oslib/audiobackend_pulseaudio.h deleted file mode 100644 index 7948d5b16..000000000 --- a/core/oslib/audiobackend_pulseaudio.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include "oslib/audiostream.h" - -extern audiobackend_t audiobackend_pulseaudio; diff --git a/core/oslib/audiobackend_sdl2.cpp b/core/oslib/audiobackend_sdl2.cpp new file mode 100644 index 000000000..39d2e8464 --- /dev/null +++ b/core/oslib/audiobackend_sdl2.cpp @@ -0,0 +1,136 @@ + +#if defined(USE_SDL_AUDIO) + +#include +#include "oslib/audiostream.h" +#include "stdclass.h" + +static SDL_AudioDeviceID audiodev; +static bool needs_resampling; +static cResetEvent read_wait; +static cMutex stream_mutex; +static struct { + uint32_t prevs; + uint32_t sample_buffer[2048]; +} audiobuf; +static unsigned sample_count = 0; + +// To easily access samples. +union Sample { int16_t s[2]; uint32_t l; }; + +static float InterpolateCatmull4pt3oX(float x0, float x1, float x2, float x3, float t) { + return 0.45 * ((2 * x1) + t * ((-x0 + x2) + t * ((2 * x0 - 5 * x1 + 4 * x2 - x3) + t * (-x0 + 3 * x1 - 3 * x2 + x3)))); +} + +static void sdl2_audiocb(void* userdata, Uint8* stream, int len) { + stream_mutex.Lock(); + // Wait until there's enough samples to feed the kraken + unsigned oslen = len / sizeof(uint32_t); + unsigned islen = needs_resampling ? oslen * 16 / 17 : oslen; + unsigned minlen = needs_resampling ? islen + 2 : islen; // Resampler looks ahead by 2 samples. + + if (sample_count < minlen) { + // No data, just output a bit of silence for the underrun + memset(stream, 0, len); + stream_mutex.Unlock(); + read_wait.Set(); + return; + } + + if (!needs_resampling) { + // Just copy bytes for this case. + memcpy(stream, &audiobuf.sample_buffer[0], len); + } + else { + // 44.1KHz to 48KHz (actually 46.86KHz) resampling + uint32_t *outbuf = (uint32_t*)stream; + const float ra = 1.0f / 17; + Sample *sbuf = (Sample*)&audiobuf.sample_buffer[0]; // [-1] stores the previous iteration last sample output + for (int i = 0; i < islen/16; i++) { + *outbuf++ = sbuf[i*16+ 0].l; // First sample stays at the same location. + for (int k = 1; k < 17; k++) { + Sample r; + // Note we access offset -1 on first iteration, as to access prevs + r.s[0] = InterpolateCatmull4pt3oX(sbuf[i*16+k-2].s[0], sbuf[i*16+k-1].s[0], sbuf[i*16+k].s[0], sbuf[i*16+k+1].s[0], 1 - ra*k); + r.s[1] = InterpolateCatmull4pt3oX(sbuf[i*16+k-2].s[1], sbuf[i*16+k-1].s[1], sbuf[i*16+k].s[1], sbuf[i*16+k+1].s[1], 1 - ra*k); + *outbuf++ = r.l; + } + } + audiobuf.prevs = audiobuf.sample_buffer[islen-1]; + } + + // Move samples in the buffer and consume them + memmove(&audiobuf.sample_buffer[0], &audiobuf.sample_buffer[islen], (sample_count-islen)*sizeof(uint32_t)); + sample_count -= islen; + + stream_mutex.Unlock(); + read_wait.Set(); +} + +static void sdl2_audio_init() { + if (!SDL_WasInit(SDL_INIT_AUDIO)) + SDL_InitSubSystem(SDL_INIT_AUDIO); + + // Support 44.1KHz (native) but also upsampling to 48KHz + SDL_AudioSpec wav_spec, out_spec; + memset(&wav_spec, 0, sizeof(wav_spec)); + wav_spec.freq = 44100; + wav_spec.format = AUDIO_S16; + wav_spec.channels = 2; + wav_spec.samples = 1024; // Must be power of two + wav_spec.callback = sdl2_audiocb; + + // Try 44.1KHz which should be faster since it's native. + audiodev = SDL_OpenAudioDevice(NULL, 0, &wav_spec, &out_spec, 0); + if (!audiodev) { + needs_resampling = true; + wav_spec.freq = 48000; + audiodev = SDL_OpenAudioDevice(NULL, 0, &wav_spec, &out_spec, 0); + verify(audiodev); + } +} + +static u32 sdl2_audio_push(void* frame, u32 samples, bool wait) { + // Unpause the device shall it be paused. + if (SDL_GetAudioDeviceStatus(audiodev) != SDL_AUDIO_PLAYING) + SDL_PauseAudioDevice(audiodev, 0); + + // If wait, then wait for the buffer to be smaller than a certain size. + stream_mutex.Lock(); + if (wait) { + while (sample_count + samples > sizeof(audiobuf.sample_buffer)/sizeof(audiobuf.sample_buffer[0])) { + stream_mutex.Unlock(); + read_wait.Wait(); + read_wait.Reset(); + stream_mutex.Lock(); + } + } + + // Copy as many samples as possible, drop any remaining (this should not happen usually) + unsigned free_samples = sizeof(audiobuf.sample_buffer) / sizeof(audiobuf.sample_buffer[0]) - sample_count; + unsigned tocopy = samples < free_samples ? samples : free_samples; + memcpy(&audiobuf.sample_buffer[sample_count], frame, tocopy * sizeof(uint32_t)); + sample_count += tocopy; + stream_mutex.Unlock(); + + return 1; +} + +static void sdl2_audio_term() { + // Stop audio playback. + SDL_PauseAudioDevice(audiodev, 1); + read_wait.Set(); +} + +audiobackend_t audiobackend_sdl2audio = { + "sdl2", // Slug + "Simple DirectMedia Layer 2 Audio", // Name + &sdl2_audio_init, + &sdl2_audio_push, + &sdl2_audio_term +}; + +static bool sdl2audiobe = RegisterAudioBackend(&audiobackend_sdl2audio); + +#endif + diff --git a/core/oslib/audiostream.cpp b/core/oslib/audiostream.cpp index f89e783fb..d0f1ebeb4 100644 --- a/core/oslib/audiostream.cpp +++ b/core/oslib/audiostream.cpp @@ -2,14 +2,6 @@ #include "cfg/cfg.h" #include "oslib/oslib.h" #include "audiostream.h" -#include "oslib/audiobackend_directsound.h" -#include "oslib/audiobackend_android.h" -#include "oslib/audiobackend_alsa.h" -#include "oslib/audiobackend_oss.h" -#include "oslib/audiobackend_pulseaudio.h" -#include "oslib/audiobackend_coreaudio.h" -#include "oslib/audiobackend_omx.h" -#include "oslib/audiobackend_libao.h" struct SoundFrame { s16 l;s16 r; }; #define SAMPLE_COUNT 512 @@ -25,16 +17,27 @@ u32 gen_samples=0; double time_diff = 128/44100.0; double time_last; + #ifdef LOG_SOUND +// TODO Only works on Windows! WaveWriter rawout("d:\\aica_out.wav"); #endif -static bool audiobackends_registered = false; static unsigned int audiobackends_num_max = 1; static unsigned int audiobackends_num_registered = 0; -static audiobackend_t **audiobackends = static_cast(calloc(audiobackends_num_max, sizeof(audiobackend_t*))); +static audiobackend_t **audiobackends = NULL; static audiobackend_t *audiobackend_current = NULL; +u32 GetAudioBackendCount() +{ + return audiobackends_num_registered; +} + +audiobackend_t* GetAudioBackend(int num) +{ + return audiobackends[num]; +} + bool RegisterAudioBackend(audiobackend_t *backend) { /* This function announces the availability of an audio backend to reicast. */ @@ -44,10 +47,16 @@ bool RegisterAudioBackend(audiobackend_t *backend) printf("ERROR: Tried to register invalid audio backend (NULL pointer).\n"); return false; } + if (backend->slug == "auto" || backend->slug == "none") { printf("ERROR: Tried to register invalid audio backend (slug \"%s\" is a reserved keyword).\n", backend->slug.c_str()); return false; } + + // First call to RegisterAudioBackend(), create the backend structure; + if (audiobackends == NULL) + audiobackends = static_cast(calloc(audiobackends_num_max, sizeof(audiobackend_t*))); + // Check if we need to allocate addition memory for storing the pointers and allocate if neccessary if (audiobackends_num_registered == audiobackends_num_max) { @@ -67,46 +76,19 @@ bool RegisterAudioBackend(audiobackend_t *backend) } audiobackends = new_ptr; } + audiobackends[audiobackends_num_registered] = backend; audiobackends_num_registered++; return true; } -void RegisterAllAudioBackends() { - #if HOST_OS==OS_WINDOWS - RegisterAudioBackend(&audiobackend_directsound); - #endif - #if ANDROID - RegisterAudioBackend(&audiobackend_android); - #endif - #if USE_OMX - RegisterAudioBackend(&audiobackend_omx); - #endif - #if USE_ALSA - RegisterAudioBackend(&audiobackend_alsa); - #endif - #if USE_OSS - RegisterAudioBackend(&audiobackend_oss); - #endif - #if USE_PULSEAUDIO - RegisterAudioBackend(&audiobackend_pulseaudio); - #endif - #if USE_LIBAO - RegisterAudioBackend(&audiobackend_libao); - #endif - #if HOST_OS == OS_DARWIN - RegisterAudioBackend(&audiobackend_coreaudio); - #endif - audiobackends_registered = true; -} - -static audiobackend_t* GetAudioBackend(std::string slug) +audiobackend_t* GetAudioBackend(std::string slug) { if (slug == "none") { printf("WARNING: Audio backend set to \"none\"!\n"); } - else if(audiobackends_num_registered > 0) + else if (audiobackends_num_registered > 0) { if (slug == "auto") { @@ -135,7 +117,8 @@ static audiobackend_t* GetAudioBackend(std::string slug) return NULL; } -u32 PushAudio(void* frame, u32 amt, bool wait) { +u32 PushAudio(void* frame, u32 amt, bool wait) +{ if (audiobackend_current != NULL) { return audiobackend_current->push(frame, amt, wait); } @@ -151,11 +134,13 @@ u32 asRingUsedCount() //s32 sz=(WritePtr+1)%RingBufferSampleCount-ReadPtr; //return sz<0?sz+RingBufferSampleCount:sz; } + u32 asRingFreeCount() { return RingBufferSampleCount-asRingUsedCount(); } +extern double mspdf; void WriteSample(s16 r, s16 l) { const u32 ptr=(WritePtr+1)%RingBufferSampleCount; @@ -165,7 +150,31 @@ void WriteSample(s16 r, s16 l) if (WritePtr==(SAMPLE_COUNT-1)) { - PushAudio(RingBuffer,SAMPLE_COUNT,settings.aica.LimitFPS); + bool do_wait = settings.aica.LimitFPS == LimitFPSEnabled + || (settings.aica.LimitFPS == LimitFPSAuto && mspdf <= 11); + + PushAudio(RingBuffer,SAMPLE_COUNT, do_wait); + } +} + +static bool backends_sorted = false; +void SortAudioBackends() +{ + if (backends_sorted) + return; + + // Sort backends by slug + for (int n = audiobackends_num_registered; n > 0; n--) + { + for (int i = 0; i < n-1; i++) + { + if (audiobackends[i]->slug > audiobackends[i+1]->slug) + { + audiobackend_t* swap = audiobackends[i]; + audiobackends[i] = audiobackends[i+1]; + audiobackends[i+1] = swap; + } + } } } @@ -176,26 +185,24 @@ void InitAudio() return; } - cfgSaveInt("audio","disable",0); - - if (!audiobackends_registered) { - //FIXME: There might some nicer way to do this. - RegisterAllAudioBackends(); - } + cfgSaveInt("audio", "disable", 0); if (audiobackend_current != NULL) { printf("ERROR: The audio backend \"%s\" (%s) has already been initialized, you need to terminate it before you can call audio_init() again!\n", audiobackend_current->slug.c_str(), audiobackend_current->name.c_str()); return; } - string audiobackend_slug = cfgLoadStr("audio", "backend", "auto"); // FIXME: This could be made a parameter + SortAudioBackends(); + + string audiobackend_slug = settings.audio.backend; audiobackend_current = GetAudioBackend(audiobackend_slug); if (audiobackend_current == NULL) { printf("WARNING: Running without audio!\n"); return; } + printf("Initializing audio backend \"%s\" (%s)...\n", audiobackend_current->slug.c_str(), audiobackend_current->name.c_str()); - audiobackend_current->init(); + audiobackend_current->init(); } void TermAudio() @@ -204,5 +211,5 @@ void TermAudio() audiobackend_current->term(); printf("Terminating audio backend \"%s\" (%s)...\n", audiobackend_current->slug.c_str(), audiobackend_current->name.c_str()); audiobackend_current = NULL; - } + } } diff --git a/core/oslib/audiostream.h b/core/oslib/audiostream.h index b700eb066..408592b55 100644 --- a/core/oslib/audiostream.h +++ b/core/oslib/audiostream.h @@ -1,5 +1,6 @@ #pragma once #include "types.h" +#include //Get used size in the ring buffer u32 asRingUsedCount(); @@ -10,6 +11,29 @@ u32 asRingFreeCount(); bool asRingRead(u8* dst,u32 count=0); void UpdateBuff(u8* pos); +typedef std::vector (*audio_option_callback_t)(); +enum audio_option_type +{ + integer = 0 +, checkbox = 1 +, list = 2 +}; + +typedef struct { + std::string cfg_name; + std::string caption; + audio_option_type type; + + // type int_value (spin edit) + int min_value; + int max_value; + + // type list edit (string/char*) + audio_option_callback_t list_callback; +} audio_option_t; + +typedef audio_option_t* (*audio_options_func_t)(int* option_count); + typedef void (*audio_backend_init_func_t)(); typedef u32 (*audio_backend_push_func_t)(void*, u32, bool); typedef void (*audio_backend_term_func_t)(); @@ -19,8 +43,14 @@ typedef struct { audio_backend_init_func_t init; audio_backend_push_func_t push; audio_backend_term_func_t term; + audio_options_func_t get_options; } audiobackend_t; extern bool RegisterAudioBackend(audiobackend_t* backend); extern void InitAudio(); extern u32 PushAudio(void* frame, u32 amt, bool wait); extern void TermAudio(); + +u32 GetAudioBackendCount(); +void SortAudioBackends(); +audiobackend_t* GetAudioBackend(int num); +audiobackend_t* GetAudioBackend(std::string slug); diff --git a/core/oslib/oslib.h b/core/oslib/oslib.h index da7d41745..8e5e9e433 100644 --- a/core/oslib/oslib.h +++ b/core/oslib/oslib.h @@ -2,7 +2,6 @@ #include "types.h" void os_SetWindowText(const char* text); -void os_MakeExecutable(void* ptr, u32 sz); double os_GetSeconds(); void os_DoEvents(); diff --git a/core/rec-ARM/ngen_arm.S b/core/rec-ARM/ngen_arm.S index 61ab59e82..55fbc8fad 100644 --- a/core/rec-ARM/ngen_arm.S +++ b/core/rec-ARM/ngen_arm.S @@ -58,6 +58,7 @@ bkpt #0 bkpt #endif ubfx r0,r3,#5,#19 @ get vram offset + @ should be only 18 bits for 8MB VRAM but it wraps around on dc add r3,r1,#0x04000000 @ get vram ptr from r1, part 1 add r3,#512 @ get ram ptr from r1, part 2 add r3,r0,lsl #5 @ ram + offset @@ -180,6 +181,7 @@ CSYM(no_update): @ next_pc _MUST_ be on r4 *R4 NOT R0 anymore* #if RAM_SIZE_MAX == 33554432 sub r2,r8,#0x4100000 ubfx r1,r4,#1,#24 @ 24+1 bits: 32 MB + @ RAM wraps around so if actual RAM size is 16MB, we won't overflow #elif RAM_SIZE_MAX == 16777216 sub r2,r8,#0x2100000 ubfx r1,r4,#1,#23 @ 23+1 bits: 16 MB @@ -241,7 +243,13 @@ HIDDEN(arm_dispatch) CSYM(arm_dispatch): ldrd r0,r1,[r8,#184] @load: Next PC, interrupt +#if ARAM_SIZE == 2*1024*1024 + ubfx r2,r0,#2,#19 @ assuming 2 MB address space max (21 bits) +#elif ARAM_SIZE == 8*1024*1024 ubfx r2,r0,#2,#21 @ assuming 8 MB address space max (23 bits) +#else +#error Unsupported AICA RAM size +#endif cmp r1,#0 bne arm_dofiq diff --git a/core/rec-ARM/rec_arm.cpp b/core/rec-ARM/rec_arm.cpp index 9e61af8ac..33109d11d 100644 --- a/core/rec-ARM/rec_arm.cpp +++ b/core/rec-ARM/rec_arm.cpp @@ -1,5 +1,4 @@ #include -#include #include "types.h" #if FEAT_SHREC == DYNAREC_JIT @@ -2082,7 +2081,7 @@ __default: } -void ngen_Compile(RuntimeBlockInfo* block,bool force_checks, bool reset, bool staging,bool optimise) +void ngen_Compile(RuntimeBlockInfo* block, SmcCheckEnum smc_checks, bool reset, bool staging,bool optimise) { //printf("Compile: %08X, %d, %d\n",block->addr,staging,optimise); block->code=(DynarecCodeEntryPtr)EMIT_GET_PTR(); @@ -2114,39 +2113,68 @@ void ngen_Compile(RuntimeBlockInfo* block,bool force_checks, bool reset, bool st reg.OpBegin(&block->oplist[0],0); //scheduler - if (force_checks) - { - s32 sz = block->sh4_code_size; - u32 addr = block->addr; - MOV32(r0,addr); + switch (smc_checks) { + case NoCheck: + break; - while (sz > 0) - { - if (sz > 2) + case FastCheck: { + MOV32(r0,block->addr); + u32* ptr=(u32*)GetMemPtr(block->addr,4); + if (ptr != NULL) { - u32* ptr=(u32*)GetMemPtr(addr,4); MOV32(r2,(u32)ptr); LDR(r2,r2,0); MOV32(r1,*ptr); CMP(r1,r2); - JUMP((u32)ngen_blockcheckfail, CC_NE); - addr += 4; - sz -= 4; } - else + } + break; + + case FullCheck: { + s32 sz = block->sh4_code_size; + u32 addr = block->addr; + MOV32(r0,addr); + + while (sz > 0) { - u16* ptr = (u16 *)GetMemPtr(addr, 2); - MOV32(r2, (u32)ptr); - LDRH(r2, r2, 0, AL); - MOVW(r1, *ptr, AL); - CMP(r1, r2); + if (sz > 2) + { + u32* ptr=(u32*)GetMemPtr(addr,4); + if (ptr != NULL) + { + MOV32(r2,(u32)ptr); + LDR(r2,r2,0); + MOV32(r1,*ptr); + CMP(r1,r2); - JUMP((u32)ngen_blockcheckfail, CC_NE); - addr += 2; - sz -= 2; + JUMP((u32)ngen_blockcheckfail, CC_NE); + } + addr += 4; + sz -= 4; + } + else + { + u16* ptr = (u16 *)GetMemPtr(addr, 2); + if (ptr != NULL) + { + MOV32(r2, (u32)ptr); + LDRH(r2, r2, 0, AL); + MOVW(r1, *ptr, AL); + CMP(r1, r2); + + JUMP((u32)ngen_blockcheckfail, CC_NE); + } + addr += 2; + sz -= 2; + } } } + break; + + default: { + die("unhandled smc_checks"); + } } u32 cyc=block->guest_cycles; diff --git a/core/rec-ARM64/rec_arm64.cpp b/core/rec-ARM64/rec_arm64.cpp index a76f796b6..5cf45ef76 100644 --- a/core/rec-ARM64/rec_arm64.cpp +++ b/core/rec-ARM64/rec_arm64.cpp @@ -22,7 +22,6 @@ #if FEAT_SHREC == DYNAREC_JIT #include -#include #include #include @@ -47,12 +46,11 @@ using namespace vixl::aarch64; extern "C" void no_update(); extern "C" void intc_sched(); extern "C" void ngen_blockcheckfail(u32 pc); - extern "C" void ngen_LinkBlock_Generic_stub(); extern "C" void ngen_LinkBlock_cond_Branch_stub(); extern "C" void ngen_LinkBlock_cond_Next_stub(); - extern "C" void ngen_FailedToFindBlock_(); +extern void vmem_platform_flush_cache(void *icache_start, void *icache_end, void *dcache_start, void *dcache_end); struct DynaRBI : RuntimeBlockInfo { @@ -63,47 +61,6 @@ struct DynaRBI : RuntimeBlockInfo } }; -// Code borrowed from Dolphin https://github.com/dolphin-emu/dolphin -void Arm64CacheFlush(void* start, void* end) -{ - if (start == end) - return; - -#if HOST_OS == OS_DARWIN - // Header file says this is equivalent to: sys_icache_invalidate(start, end - start); - sys_cache_control(kCacheFunctionPrepareForExecution, start, end - start); -#else - // Don't rely on GCC's __clear_cache implementation, as it caches - // icache/dcache cache line sizes, that can vary between cores on - // big.LITTLE architectures. - u64 addr, ctr_el0; - static size_t icache_line_size = 0xffff, dcache_line_size = 0xffff; - size_t isize, dsize; - - __asm__ volatile("mrs %0, ctr_el0" : "=r"(ctr_el0)); - isize = 4 << ((ctr_el0 >> 0) & 0xf); - dsize = 4 << ((ctr_el0 >> 16) & 0xf); - - // use the global minimum cache line size - icache_line_size = isize = icache_line_size < isize ? icache_line_size : isize; - dcache_line_size = dsize = dcache_line_size < dsize ? dcache_line_size : dsize; - - addr = (u64)start & ~(u64)(dsize - 1); - for (; addr < (u64)end; addr += dsize) - // use "civac" instead of "cvau", as this is the suggested workaround for - // Cortex-A53 errata 819472, 826319, 827319 and 824069. - __asm__ volatile("dc civac, %0" : : "r"(addr) : "memory"); - __asm__ volatile("dsb ish" : : : "memory"); - - addr = (u64)start & ~(u64)(isize - 1); - for (; addr < (u64)end; addr += isize) - __asm__ volatile("ic ivau, %0" : : "r"(addr) : "memory"); - - __asm__ volatile("dsb ish" : : : "memory"); - __asm__ volatile("isb" : : : "memory"); -#endif -} - double host_cpu_time; u64 guest_cpu_cycles; static jmp_buf jmp_env; @@ -151,7 +108,7 @@ __asm__ "ngen_LinkBlock_Shared_stub: \n\t" "mov x0, lr \n\t" "sub x0, x0, #4 \n\t" // go before the call - "bl rdv_LinkBlock \n\t" + "bl rdv_LinkBlock \n\t" // returns an RX addr "br x0 \n" ".hidden ngen_FailedToFindBlock_ \n\t" @@ -466,15 +423,15 @@ public: return *ret_reg; } - void ngen_Compile(RuntimeBlockInfo* block, bool force_checks, bool reset, bool staging, bool optimise) + void ngen_Compile(RuntimeBlockInfo* block, SmcCheckEnum smc_checks, bool reset, bool staging, bool optimise) { //printf("REC-ARM64 compiling %08x\n", block->addr); #ifdef PROFILING SaveFramePointer(); #endif this->block = block; - if (force_checks) - CheckBlock(block); + + CheckBlock(smc_checks, block); // run register allocator regalloc.DoAlloc(block); @@ -1243,7 +1200,7 @@ public: Ldr(w29, sh4_context_mem_operand(&next_pc)); - GenBranch(no_update); + GenBranchRuntime(no_update); break; default: @@ -1268,7 +1225,12 @@ public: emit_Skip(block->host_code_size); } - Arm64CacheFlush(GetBuffer()->GetStartAddress(), GetBuffer()->GetEndAddress()); + + // Flush and invalidate caches + vmem_platform_flush_cache( + CC_RW2RX(GetBuffer()->GetStartAddress()), CC_RW2RX(GetBuffer()->GetEndAddress()), + GetBuffer()->GetStartAddress(), GetBuffer()->GetEndAddress()); + #if 0 // if (rewrite) { @@ -1290,10 +1252,13 @@ public: } private: + // Runtime branches/calls need to be adjusted if rx space is different to rw space. + // Therefore can't mix GenBranch with GenBranchRuntime! + template void GenCallRuntime(R (*function)(P...)) { - ptrdiff_t offset = reinterpret_cast(function) - GetBuffer()->GetStartAddress(); + ptrdiff_t offset = reinterpret_cast(function) - reinterpret_cast(CC_RW2RX(GetBuffer()->GetStartAddress())); verify(offset >= -128 * 1024 * 1024 && offset <= 128 * 1024 * 1024); verify((offset & 3) == 0); Label function_label; @@ -1301,6 +1266,17 @@ private: Bl(&function_label); } + template + void GenBranchRuntime(R (*target)(P...)) + { + ptrdiff_t offset = reinterpret_cast(target) - reinterpret_cast(CC_RW2RX(GetBuffer()->GetStartAddress())); + verify(offset >= -128 * 1024 * 1024 && offset <= 128 * 1024 * 1024); + verify((offset & 3) == 0); + Label target_label; + BindToOffset(&target_label, offset); + B(&target_label); + } + template void GenBranch(R (*code)(P...), Condition cond = al) { @@ -1623,17 +1599,12 @@ private: verify (GetCursorAddress() - start_instruction == code_size * kInstructionSize); } - void CheckBlock(RuntimeBlockInfo* block) + void CheckBlock(SmcCheckEnum smc_checks, RuntimeBlockInfo* block) { - s32 sz = block->sh4_code_size; Label blockcheck_fail; Label blockcheck_success; - u8* ptr = GetMemPtr(block->addr, sz); - if (ptr == NULL) - return; - if (mmu_enabled()) { Ldr(w10, sh4_context_mem_operand(&next_pc)); @@ -1642,37 +1613,66 @@ private: B(ne, &blockcheck_fail); } - Ldr(x9, reinterpret_cast(ptr)); + switch (smc_checks) { + case NoCheck: + return; - while (sz > 0) - { - if (sz >= 8) - { - Ldr(x10, MemOperand(x9, 8, PostIndex)); - Ldr(x11, *(u64*)ptr); - Cmp(x10, x11); - sz -= 8; - ptr += 8; - } - else if (sz >= 4) - { - Ldr(w10, MemOperand(x9, 4, PostIndex)); + case FastCheck: { + u8* ptr = GetMemPtr(block->addr, 4); + if (ptr == NULL) + return; + Ldr(x9, reinterpret_cast(ptr)); + Ldr(w10, MemOperand(x9)); Ldr(w11, *(u32*)ptr); Cmp(w10, w11); - sz -= 4; - ptr += 4; + B(eq, &blockcheck_success); } - else - { - Ldrh(w10, MemOperand(x9, 2, PostIndex)); - Mov(w11, *(u16*)ptr); - Cmp(w10, w11); - sz -= 2; - ptr += 2; + break; + + case FullCheck: { + s32 sz = block->sh4_code_size; + + u8* ptr = GetMemPtr(block->addr, sz); + if (ptr == NULL) + return; + + Ldr(x9, reinterpret_cast(ptr)); + + while (sz > 0) + { + if (sz >= 8) + { + Ldr(x10, MemOperand(x9, 8, PostIndex)); + Ldr(x11, *(u64*)ptr); + Cmp(x10, x11); + sz -= 8; + ptr += 8; + } + else if (sz >= 4) + { + Ldr(w10, MemOperand(x9, 4, PostIndex)); + Ldr(w11, *(u32*)ptr); + Cmp(w10, w11); + sz -= 4; + ptr += 4; + } + else + { + Ldrh(w10, MemOperand(x9, 2, PostIndex)); + Mov(w11, *(u16*)ptr); + Cmp(w10, w11); + sz -= 2; + ptr += 2; + } + B(ne, &blockcheck_fail); + } + B(&blockcheck_success); } - B(ne, &blockcheck_fail); + break; + + default: + die("unhandled smc_checks"); } - B(&blockcheck_success); Bind(&blockcheck_fail); Ldr(w0, block->addr); @@ -1767,18 +1767,18 @@ private: RuntimeBlockInfo* block = NULL; const int read_memory_rewrite_size = 6; // worst case for u64: add, bfc, ldr, fmov, lsr, fmov // FIXME rewrite size per read/write size? - const int write_memory_rewrite_size = 4; + const int write_memory_rewrite_size = 4; // TODO only 3 if !mmu }; static Arm64Assembler* compiler; -void ngen_Compile(RuntimeBlockInfo* block, bool force_checks, bool reset, bool staging, bool optimise) +void ngen_Compile(RuntimeBlockInfo* block, SmcCheckEnum smc_checks, bool reset, bool staging, bool optimise) { verify(emit_FreeSpace() >= 16 * 1024); compiler = new Arm64Assembler(); - compiler->ngen_Compile(block, force_checks, reset, staging, optimise); + compiler->ngen_Compile(block, smc_checks, reset, staging, optimise); delete compiler; compiler = NULL; @@ -1807,13 +1807,14 @@ void ngen_CC_Finish(shil_opcode* op) bool ngen_Rewrite(unat& host_pc, unat, unat) { //printf("ngen_Rewrite pc %p\n", host_pc); - RuntimeBlockInfo *block = bm_GetBlock((void *)host_pc); + void *host_pc_rw = (void*)CC_RX2RW(host_pc); + RuntimeBlockInfo *block = bm_GetBlock((void*)host_pc); if (block == NULL) { printf("ngen_Rewrite: Block at %p not found\n", (void *)host_pc); return false; } - u32 *code_ptr = (u32*)host_pc; + u32 *code_ptr = (u32*)host_pc_rw; auto it = block->memory_accesses.find(code_ptr); if (it == block->memory_accesses.end()) { @@ -1831,7 +1832,7 @@ bool ngen_Rewrite(unat& host_pc, unat, unat) assembler->GenWriteMemorySlow(op); assembler->Finalize(true); delete assembler; - host_pc = (unat)(code_ptr - 2 - (mmu_enabled() ? 1 : 0)); + host_pc = (unat)CC_RW2RX(code_ptr - 2 - (mmu_enabled() ? 1 : 0)); return true; } diff --git a/core/rec-cpp/rec_cpp.cpp b/core/rec-cpp/rec_cpp.cpp index 6dbf5ac0f..a9f15fd5a 100644 --- a/core/rec-cpp/rec_cpp.cpp +++ b/core/rec-cpp/rec_cpp.cpp @@ -13,7 +13,6 @@ #include "hw/sh4/dyna/ngen.h" #include "hw/sh4/sh4_mem.h" #include "hw/sh4/dyna/regalloc.h" -#include "emitter/x86_emitter.h" #include "profiler/profiler.h" #include "oslib/oslib.h" @@ -812,13 +811,19 @@ struct opcode_check_block : public opcodeExec { opcodeExec* setup(RuntimeBlockInfo* block) { this->block = block; ptr = GetMemPtr(block->addr, 4); - code.resize(sz == -1 ? block->sh4_code_size : sz); - memcpy(&code[0], ptr, sz == -1 ? block->sh4_code_size : sz); + if (ptr != NULL) + { + code.resize(sz == -1 ? block->sh4_code_size : sz); + memcpy(&code[0], ptr, sz == -1 ? block->sh4_code_size : sz); + } return this; } void execute() { + if (code.empty()) + return; + switch (sz) { case 4: @@ -1191,10 +1196,10 @@ public: size_t opcode_index; opcodeExec** ptrsg; - void compile(RuntimeBlockInfo* block, bool force_checks, bool reset, bool staging, bool optimise) { + void compile(RuntimeBlockInfo* block, SmcCheckEnum smc_checks, bool reset, bool staging, bool optimise) { //we need an extra one for the end opcode and optionally one more for block check - auto ptrs = fnnCtor_forreal(block->oplist.size() + 1 + (force_checks ? 1 : 0))(block->guest_cycles); + auto ptrs = fnnCtor_forreal(block->oplist.size() + 1 + (smc_checks != NoCheck ? 1 : 0))(block->guest_cycles); ptrsg = ptrs.ptrs; @@ -1208,9 +1213,16 @@ public: } size_t i = 0; - if (force_checks) + if (smc_checks != NoCheck) { + verify (smc_checks == FastCheck || smc_checks == FullCheck) opcodeExec* op; + int check_size = block->sh4_code_size; + + if (smc_checks == FastCheck) { + check_size = 4; + } + switch (block->sh4_code_size) { case 4: @@ -1228,6 +1240,7 @@ public: } ptrs.ptrs[i++] = op; } + for (size_t opnum = 0; opnum < block->oplist.size(); opnum++, i++) { opcode_index = i; shil_opcode& op = block->oplist[opnum]; @@ -1552,14 +1565,14 @@ public: BlockCompiler* compiler; -void ngen_Compile(RuntimeBlockInfo* block, bool force_checks, bool reset, bool staging, bool optimise) +void ngen_Compile(RuntimeBlockInfo* block, SmcCheckEnum smc_checks, bool reset, bool staging, bool optimise) { verify(emit_FreeSpace() >= 16 * 1024); compiler = new BlockCompiler(); - compiler->compile(block, force_checks, reset, staging, optimise); + compiler->compile(block, smc_checks, reset, staging, optimise); delete compiler; } diff --git a/core/rec-x64/msvc.asm b/core/rec-x64/msvc.asm new file mode 100644 index 000000000..4562cbdd2 --- /dev/null +++ b/core/rec-x64/msvc.asm @@ -0,0 +1,61 @@ +_TEXT SEGMENT + +SH4_TIMESLICE = 448 +CPU_RUNNING = 135266148 +PC = 135266120 + +EXTERN bm_GetCode2: PROC +EXTERN UpdateSystem_INTC: PROC +EXTERN cycle_counter: dword +EXTERN p_sh4rcb: qword + +PUBLIC ngen_mainloop +ngen_mainloop PROC + + push rbx + push rbp + push rdi + push rsi + push r12 + push r13 + push r14 + push r15 + sub rsp, 40 ; 32-byte shadow space + 8 for stack 16-byte alignment + + mov dword ptr [cycle_counter], SH4_TIMESLICE + +run_loop: + mov rax, qword ptr [p_sh4rcb] + mov edx, dword ptr[CPU_RUNNING + rax] + test edx, edx + je end_run_loop + +slice_loop: + mov rax, qword ptr [p_sh4rcb] + mov ecx, dword ptr[PC + rax] + call bm_GetCode2 + call rax + mov ecx, dword ptr [cycle_counter] + test ecx, ecx + jg slice_loop + + add ecx, SH4_TIMESLICE + mov dword ptr [cycle_counter], ecx + call UpdateSystem_INTC + jmp run_loop + +end_run_loop: + + add rsp, 40 + pop r15 + pop r14 + pop r13 + pop r12 + pop rsi + pop rdi + pop rbp + pop rbx + ret +ngen_mainloop ENDP +_TEXT ENDS +END diff --git a/core/rec-x64/rec_x64.cpp b/core/rec-x64/rec_x64.cpp index 9abdd14d2..e932f7f48 100644 --- a/core/rec-x64/rec_x64.cpp +++ b/core/rec-x64/rec_x64.cpp @@ -36,7 +36,9 @@ struct DynaRBI : RuntimeBlockInfo } }; -int cycle_counter; +extern "C" { + int cycle_counter; +} double host_cpu_time; u64 guest_cpu_cycles; @@ -82,6 +84,8 @@ static __attribute((used)) void end_slice() jmp_buf jmp_env; +#ifndef _MSC_VER + #ifdef _WIN32 // Fully naked function in win32 for proper SEH prologue __asm__ ( @@ -191,6 +195,7 @@ WIN32_ONLY( ".seh_pushreg %r14 \n\t") } #endif +#endif // !_MSC_VER #undef _U #undef _S @@ -331,20 +336,18 @@ public: call_regsxmm.push_back(xmm3); } - void compile(RuntimeBlockInfo* block, bool force_checks, bool reset, bool staging, bool optimise) + void compile(RuntimeBlockInfo* block, SmcCheckEnum smc_checks, bool reset, bool staging, bool optimise) { //printf("X86_64 compiling %08x to %p\n", block->addr, emit_GetCCPtr()); current_opid = -1; - if (force_checks) { - CheckBlock(block); - } + + CheckBlock(smc_checks, block); #ifdef _WIN32 sub(rsp, 0x28); // 32-byte shadow space + 8 byte alignment #else sub(rsp, 0x8); // align stack #endif - if (mmu_enabled() && block->has_fpu_op) { Xbyak::Label fpu_enabled; @@ -358,7 +361,14 @@ public: jmp(exit_block, T_NEAR); L(fpu_enabled); } +#ifdef FEAT_NO_RWX_PAGES + // Use absolute addressing for this one + // TODO(davidgfnet) remove the ifsef using CC_RX2RW/CC_RW2RX + mov(rax, (uintptr_t)&cycle_counter); + sub(dword[rax], block->guest_cycles); +#else sub(dword[rip + &cycle_counter], block->guest_cycles); +#endif #ifdef PROFILING mov(rax, (uintptr_t)&guest_cpu_cycles); mov(ecx, block->guest_cycles); @@ -1479,7 +1489,7 @@ private: return true; } - void CheckBlock(RuntimeBlockInfo* block) { + void CheckBlock(SmcCheckEnum smc_checks, RuntimeBlockInfo* block) { mov(call_regs[0], block->addr); // FIXME This test shouldn't be necessary @@ -1493,38 +1503,64 @@ private: jne(reinterpret_cast(&ngen_blockcheckfail)); } - s32 sz=block->sh4_code_size; - u32 sa=block->addr; + switch (smc_checks) { + case NoCheck: + return; - while (sz > 0) - { - void* ptr = (void*)GetMemPtr(sa, sz > 8 ? 8 : sz); - if (ptr) - { - mov(rax, reinterpret_cast(ptr)); - - if (sz >= 8) { - mov(rdx, *(u64*)ptr); - cmp(qword[rax], rdx); - sz -= 8; - sa += 8; - } - else if (sz >= 4) { + case FastCheck: { + void* ptr = (void*)GetMemPtr(block->addr, 4); + if (ptr) + { + mov(call_regs[0], block->addr); + mov(rax, reinterpret_cast(ptr)); mov(edx, *(u32*)ptr); cmp(dword[rax], edx); - sz -= 4; - sa += 4; + jne(reinterpret_cast(CC_RX2RW(&ngen_blockcheckfail))); } - else { - mov(edx, *(u16*)ptr); - cmp(word[rax],dx); - sz -= 2; - sa += 2; - } - jne(reinterpret_cast(&ngen_blockcheckfail)); - } - } + } + break; + case FullCheck: { + s32 sz=block->sh4_code_size; + u32 sa=block->addr; + + void* ptr = (void*)GetMemPtr(sa, sz > 8 ? 8 : sz); + if (ptr) + { + mov(call_regs[0], block->addr); + + while (sz > 0) + { + mov(rax, reinterpret_cast(ptr)); + + if (sz >= 8) { + mov(rdx, *(u64*)ptr); + cmp(qword[rax], rdx); + sz -= 8; + sa += 8; + } + else if (sz >= 4) { + mov(edx, *(u32*)ptr); + cmp(dword[rax], edx); + sz -= 4; + sa += 4; + } + else { + mov(edx, *(u16*)ptr); + cmp(word[rax],dx); + sz -= 2; + sa += 2; + } + jne(reinterpret_cast(CC_RX2RW(&ngen_blockcheckfail))); + ptr = (void*)GetMemPtr(sa, sz > 8 ? 8 : sz); + } + } + } + break; + + default: + die("unhandled smc_checks"); + } } void GenBinaryOp(const shil_opcode &op, X64BinaryOp natop) @@ -1584,7 +1620,7 @@ private: } #endif - call(function); + call(CC_RX2RW(function)); #ifndef _WIN32 if (xmm8_mapped || xmm9_mapped || xmm10_mapped || xmm11_mapped) @@ -1747,13 +1783,13 @@ void X64RegAlloc::Writeback_FPU(u32 reg, s8 nreg) static BlockCompiler* compiler; -void ngen_Compile(RuntimeBlockInfo* block, bool force_checks, bool reset, bool staging, bool optimise) +void ngen_Compile(RuntimeBlockInfo* block, SmcCheckEnum smc_checks, bool reset, bool staging, bool optimise) { verify(emit_FreeSpace() >= 16 * 1024); compiler = new BlockCompiler(); - compiler->compile(block, force_checks, reset, staging, optimise); + compiler->compile(block, smc_checks, reset, staging, optimise); delete compiler; } diff --git a/core/rec-x86/rec_x86_driver.cpp b/core/rec-x86/rec_x86_driver.cpp index 9b9911f73..727cf4eee 100644 --- a/core/rec-x86/rec_x86_driver.cpp +++ b/core/rec-x86/rec_x86_driver.cpp @@ -31,60 +31,27 @@ bool sse_3=true; bool ssse_3=true; bool mmx=true; -void DetectCpuFeatures() -{ +void DetectCpuFeatures() { static bool detected=false; if (detected) return; detected=true; -#ifdef _MSC_VER - __try - { - __asm addps xmm0,xmm0 - } - __except(1) - { - sse_1=false; - } - - __try - { - __asm addpd xmm0,xmm0 - } - __except(1) - { - sse_2=false; - } - - __try - { - __asm addsubpd xmm0,xmm0 - } - __except(1) - { - sse_3=false; - } - - __try - { - __asm phaddw xmm0,xmm0 - } - __except(1) - { - ssse_3=false; - } - - - __try - { - __asm paddd mm0,mm1 - __asm emms; - } - __except(1) - { - mmx=false; - } -#endif + #if BUILD_COMPILER == COMPILER_VC + #include + int info[4]; + __cpuid(info, 1); + mmx = info[3] & (1 << 23); + sse_1 = info[3] & (1 << 25); + sse_2 = info[3] & (1 << 26); + sse_3 = info[2] & (1 << 0); + ssse_3 = info[2] & (1 << 9); + #else + mmx = __builtin_cpu_supports("mmx"); + sse_1 = __builtin_cpu_supports("sse"); + sse_2 = __builtin_cpu_supports("sse2"); + sse_3 = __builtin_cpu_supports("sse3"); + ssse_3 = __builtin_cpu_supports("ssse3"); + #endif } @@ -262,36 +229,56 @@ u32 rdmt[6]; extern u32 memops_t,memops_l; extern int mips_counter; -void CheckBlock(RuntimeBlockInfo* block,x86_ptr_imm place) +//TODO: Get back validating mode for this +void CheckBlock(SmcCheckEnum smc_checks, RuntimeBlockInfo* block) { - s32 sz=block->sh4_code_size; - u32 sa=block->addr; - while(sz>0) - { - void* ptr=(void*)GetMemPtr(sa,4); - if (ptr) - { - if (sz==2) - x86e->Emit(op_cmp16,ptr,*(u16*)ptr); - else - x86e->Emit(op_cmp32,ptr,*(u32*)ptr); - x86e->Emit(op_jne,place); + switch (smc_checks) { + case NoCheck: + break; + + case FastCheck: { + void* ptr = (void*)GetMemPtr(block->addr, 4); + if (ptr) + { + x86e->Emit(op_cmp32, ptr, *(u32*)ptr); + x86e->Emit(op_jne, x86_ptr_imm(ngen_blockcheckfail)); + } } - sz-=4; - sa+=4; + break; + + case FullCheck: { + s32 sz=block->sh4_code_size; + u32 sa=block->addr; + while(sz>0) + { + void* ptr=(void*)GetMemPtr(sa,4); + if (ptr) + { + if (sz==2) + x86e->Emit(op_cmp16,ptr,*(u16*)ptr); + else + x86e->Emit(op_cmp32,ptr,*(u32*)ptr); + x86e->Emit(op_jne,x86_ptr_imm(ngen_blockcheckfail)); + } + sz-=4; + sa+=4; + } + } + break; + + default: + die("unhandled smc_checks"); } - } -void ngen_Compile(RuntimeBlockInfo* block,bool force_checks, bool reset, bool staging,bool optimise) +void ngen_Compile(RuntimeBlockInfo* block, SmcCheckEnum smc_checks, bool reset, bool staging,bool optimise) { //initialise stuff DetectCpuFeatures(); ((DynaRBI*)block)->reloc_info=0; - //Setup emitter x86e = new x86_block(); x86e->Init(0,0); @@ -316,7 +303,7 @@ void ngen_Compile(RuntimeBlockInfo* block,bool force_checks, bool reset, bool st //block invl. checks x86e->Emit(op_mov32,ECX,block->addr); - CheckBlock(block,force_checks?x86_ptr_imm(ngen_blockcheckfail):x86_ptr_imm(ngen_blockcheckfail2)); + CheckBlock(smc_checks, block); //Scheduler x86_Label* no_up=x86e->CreateLabel(false,8); @@ -339,8 +326,8 @@ void ngen_Compile(RuntimeBlockInfo* block,bool force_checks, bool reset, bool st if (prof.enable) { - if (force_checks) - x86e->Emit(op_add32,&prof.counters.blkrun.force_check,1); + //if (force_checks) + // x86e->Emit(op_add32,&prof.counters.blkrun.force_check,1); x86e->Emit(op_add32,&prof.counters.blkrun.cycles[block->guest_cycles],1); } diff --git a/core/rec-x86/rec_x86_il.cpp b/core/rec-x86/rec_x86_il.cpp index 8d3b710cd..ca2d6f9e4 100644 --- a/core/rec-x86/rec_x86_il.cpp +++ b/core/rec-x86/rec_x86_il.cpp @@ -1453,7 +1453,7 @@ void ngen_opcode(RuntimeBlockInfo* block, shil_opcode* op,x86_block* x86e, bool verify(op->rs1.is_r32f()); verify(reg.IsAllocg(op->rd)); verify(reg.IsAllocf(op->rs1)); - static f32 sse_ftrc_saturate = 2147483520.0f; // IEEE 754: 0x4effffff + static f32 sse_ftrc_saturate = 2147483520.0f; // IEEE 754: 0x4effffff x86e->Emit(op_movaps, XMM0, reg.mapf(op->rs1)); x86e->Emit(op_minss, XMM0, &sse_ftrc_saturate); x86e->Emit(op_cvttss2si, reg.mapg(op->rd), XMM0); diff --git a/core/reios/reios.cpp b/core/reios/reios.cpp index de7e8a0dd..d24852d98 100644 --- a/core/reios/reios.cpp +++ b/core/reios/reios.cpp @@ -657,7 +657,7 @@ u32 hook_addr(hook_fp* fn) { if (hooks_rev.count(fn)) return hooks_rev[fn]; else { - printf("hook_addr: Failed to reverse lookup %08X\n", (unat)fn); + printf("hook_addr: Failed to reverse lookup %p\n", fn); verify(false); return 0; } diff --git a/core/rend/TexCache.cpp b/core/rend/TexCache.cpp index dba4825fd..ad993b6d8 100644 --- a/core/rend/TexCache.cpp +++ b/core/rend/TexCache.cpp @@ -126,8 +126,7 @@ void palette_update() using namespace std; vector VramLocks[VRAM_SIZE/PAGE_SIZE]; -//vram 32-64b -VArray2 vram; +VLockedMemory vram; // vram 32-64b //List functions // @@ -209,7 +208,7 @@ vram_block* libCore_vramlock_Lock(u32 start_offset64,u32 end_offset64,void* user { vramlist_lock.Lock(); - vram.LockRegion(block->start,block->len); + vram.LockRegion(block->start, block->len); //TODO: Fix this for 32M wrap as well if (_nvmem_enabled() && VRAM_SIZE == 0x800000) { diff --git a/core/rend/d3d11/d3d11.cpp b/core/rend/d3d11/d3d11.cpp index bf059af21..974ac94e8 100644 --- a/core/rend/d3d11/d3d11.cpp +++ b/core/rend/d3d11/d3d11.cpp @@ -1,6 +1,6 @@ #include -#include "hw\pvr\Renderer_if.h" -#include "oslib\oslib.h" +#include "hw/pvr/Renderer_if.h" +#include "oslib/oslib.h" #pragma comment(lib,"d3d11.lib") @@ -102,4 +102,4 @@ struct d3d11 : Renderer Renderer* rend_D3D11() { return new d3d11(); -} \ No newline at end of file +} diff --git a/core/rend/gl4/abuffer.cpp b/core/rend/gl4/abuffer.cpp index b09cc962d..7e9b67c64 100644 --- a/core/rend/gl4/abuffer.cpp +++ b/core/rend/gl4/abuffer.cpp @@ -331,23 +331,23 @@ void initABuffer() { char source[16384]; sprintf(source, final_shader_source, 1); - gl4CompilePipelineShader(&g_abuffer_final_shader, source); + gl4CompilePipelineShader(&g_abuffer_final_shader, false, source); } if (g_abuffer_final_nosort_shader.program == 0) { char source[16384]; sprintf(source, final_shader_source, 0); - gl4CompilePipelineShader(&g_abuffer_final_nosort_shader, source); + gl4CompilePipelineShader(&g_abuffer_final_nosort_shader, false, source); } if (g_abuffer_clear_shader.program == 0) - gl4CompilePipelineShader(&g_abuffer_clear_shader, clear_shader_source); + gl4CompilePipelineShader(&g_abuffer_clear_shader, false, clear_shader_source); if (g_abuffer_tr_modvol_shaders[0].program == 0) { char source[16384]; for (int mode = 0; mode < ModeCount; mode++) { sprintf(source, tr_modvol_shader_source, mode); - gl4CompilePipelineShader(&g_abuffer_tr_modvol_shaders[mode], source); + gl4CompilePipelineShader(&g_abuffer_tr_modvol_shaders[mode], false, source); } } @@ -417,6 +417,17 @@ void termABuffer() glDeleteBuffers(1, &g_quadBuffer); g_quadBuffer = 0; } + glcache.DeleteProgram(g_abuffer_final_shader.program); + g_abuffer_final_shader.program = 0; + glcache.DeleteProgram(g_abuffer_final_nosort_shader.program); + g_abuffer_final_nosort_shader.program = 0; + glcache.DeleteProgram(g_abuffer_clear_shader.program); + g_abuffer_clear_shader.program = 0; + for (int mode = 0; mode < ModeCount; mode++) + { + glcache.DeleteProgram(g_abuffer_tr_modvol_shaders[mode].program); + g_abuffer_tr_modvol_shaders[mode].program = 0; + } } void reshapeABuffer(int w, int h) diff --git a/core/rend/gl4/gl4.h b/core/rend/gl4/gl4.h index 7abc1db20..4c3ca82ac 100755 --- a/core/rend/gl4/gl4.h +++ b/core/rend/gl4/gl4.h @@ -1,6 +1,6 @@ #pragma once #include "rend/gles/gles.h" -#include +#include void gl4DrawStrips(GLuint output_fbo); @@ -44,7 +44,8 @@ struct gl4_ctx GLuint extra_depth_scale; } modvol_shader; - std::map shaders; + std::unordered_map shaders; + bool rotate90; struct { @@ -53,16 +54,6 @@ struct gl4_ctx GLuint modvol_vao; GLuint tr_poly_params; } vbo; - - gl4PipelineShader *getShader(int programId) { - gl4PipelineShader *shader = shaders[programId]; - if (shader == NULL) { - shader = new gl4PipelineShader(); - shaders[programId] = shader; - shader->program = -1; - } - return shader; - } }; extern gl4_ctx gl4; @@ -76,7 +67,8 @@ bool gl4_render_output_framebuffer(); void abufferDrawQuad(bool upsideDown = false, float x = 0.f, float y = 0.f, float w = 0.f, float h = 0.f); extern const char *gl4PixelPipelineShader; -bool gl4CompilePipelineShader(gl4PipelineShader* s, const char *source = gl4PixelPipelineShader); +bool gl4CompilePipelineShader(gl4PipelineShader* s, bool rotate_90, const char *source = gl4PixelPipelineShader); +void gl4_delete_shaders(); extern GLuint stencilTexId; extern GLuint depthTexId; diff --git a/core/rend/gl4/gldraw.cpp b/core/rend/gl4/gldraw.cpp index 3342325d7..dfa403a19 100644 --- a/core/rend/gl4/gldraw.cpp +++ b/core/rend/gl4/gldraw.cpp @@ -45,10 +45,15 @@ static GLuint texSamplers[2]; static GLuint depth_fbo; GLuint depthSaveTexId; -static int gl4GetProgramID(u32 cp_AlphaTest, u32 pp_ClipTestMode, +static gl4PipelineShader *gl4GetProgram(u32 cp_AlphaTest, u32 pp_ClipTestMode, u32 pp_Texture, u32 pp_UseAlpha, u32 pp_IgnoreTexA, u32 pp_ShadInstr, u32 pp_Offset, u32 pp_FogCtrl, bool pp_TwoVolumes, u32 pp_DepthFunc, bool pp_Gouraud, bool pp_BumpMap, bool fog_clamping, int pass) { + if (settings.rend.Rotate90 != gl4.rotate90) + { + gl4_delete_shaders(); + gl4.rotate90 = settings.rend.Rotate90; + } u32 rv=0; rv|=pp_ClipTestMode; @@ -66,45 +71,27 @@ static int gl4GetProgramID(u32 cp_AlphaTest, u32 pp_ClipTestMode, rv <<= 1; rv |= fog_clamping; rv <<= 2; rv |= pass; - return rv; -} - -static void setCurrentShader(u32 cp_AlphaTest, u32 pp_ClipTestMode, - u32 pp_Texture, u32 pp_UseAlpha, u32 pp_IgnoreTexA, u32 pp_ShadInstr, u32 pp_Offset, - u32 pp_FogCtrl, bool pp_TwoVolumes, u32 pp_DepthFunc, bool pp_Gouraud, bool pp_BumpMap, bool fog_clamping, int pass) -{ - int shaderId = gl4GetProgramID(cp_AlphaTest, - pp_ClipTestMode + 1, - pp_Texture, - pp_UseAlpha, - pp_IgnoreTexA, - pp_ShadInstr, - pp_Offset, - pp_FogCtrl, - pp_TwoVolumes, - pp_DepthFunc, - pp_Gouraud, - pp_BumpMap, - fog_clamping, - pass); - CurrentShader = gl4.getShader(shaderId); - if (CurrentShader->program == -1) { - CurrentShader->cp_AlphaTest = cp_AlphaTest; - CurrentShader->pp_ClipTestMode = pp_ClipTestMode; - CurrentShader->pp_Texture = pp_Texture; - CurrentShader->pp_UseAlpha = pp_UseAlpha; - CurrentShader->pp_IgnoreTexA = pp_IgnoreTexA; - CurrentShader->pp_ShadInstr = pp_ShadInstr; - CurrentShader->pp_Offset = pp_Offset; - CurrentShader->pp_FogCtrl = pp_FogCtrl; - CurrentShader->pp_TwoVolumes = pp_TwoVolumes; - CurrentShader->pp_DepthFunc = pp_DepthFunc; - CurrentShader->pp_Gouraud = pp_Gouraud; - CurrentShader->pp_BumpMap = pp_BumpMap; - CurrentShader->fog_clamping = fog_clamping; - CurrentShader->pass = pass; - gl4CompilePipelineShader(CurrentShader); + gl4PipelineShader *shader = &gl4.shaders[rv]; + if (shader->program == 0) + { + shader->cp_AlphaTest = cp_AlphaTest; + shader->pp_ClipTestMode = pp_ClipTestMode; + shader->pp_Texture = pp_Texture; + shader->pp_UseAlpha = pp_UseAlpha; + shader->pp_IgnoreTexA = pp_IgnoreTexA; + shader->pp_ShadInstr = pp_ShadInstr; + shader->pp_Offset = pp_Offset; + shader->pp_FogCtrl = pp_FogCtrl; + shader->pp_TwoVolumes = pp_TwoVolumes; + shader->pp_DepthFunc = pp_DepthFunc; + shader->pp_Gouraud = pp_Gouraud; + shader->pp_BumpMap = pp_BumpMap; + shader->fog_clamping = fog_clamping; + shader->pass = pass; + gl4CompilePipelineShader(shader, settings.rend.Rotate90); } + + return shader; } static void SetTextureRepeatMode(int index, GLuint dir, u32 clamp, u32 mirror) @@ -132,7 +119,7 @@ template if (pass == 0) { - setCurrentShader(Type == ListType_Punch_Through ? 1 : 0, + CurrentShader = gl4GetProgram(Type == ListType_Punch_Through ? 1 : 0, clipping, Type == ListType_Punch_Through ? gp->pcw.Texture : 0, 1, @@ -153,6 +140,8 @@ template bool two_volumes_mode = (gp->tsp1.full != -1) && Type != ListType_Translucent; bool color_clamp = gp->tsp.ColorClamp && (pvrrc.fog_clamp_min != 0 || pvrrc.fog_clamp_max != 0xffffffff); + int fog_ctrl = settings.rend.Fog ? gp->tsp.FogCtrl : 2; + int depth_func = 0; if (Type == ListType_Translucent) { @@ -162,14 +151,14 @@ template depth_func = gp->isp.DepthMode; } - setCurrentShader(Type == ListType_Punch_Through ? 1 : 0, + CurrentShader = gl4GetProgram(Type == ListType_Punch_Through ? 1 : 0, clipping, gp->pcw.Texture, gp->tsp.UseAlpha, gp->tsp.IgnoreTexA, gp->tsp.ShadInstr, gp->pcw.Offset, - gp->tsp.FogCtrl, + fog_ctrl, two_volumes_mode, depth_func, gp->pcw.Gouraud, @@ -681,7 +670,7 @@ static void gl4_draw_quad_texture(GLuint texture, bool upsideDown, float x = 0.f ShaderUniforms.trilinear_alpha = 1.0; - setCurrentShader(0, + CurrentShader = gl4GetProgram(0, 0, 1, 0, @@ -713,11 +702,15 @@ void gl4DrawFramebuffer(float w, float h) bool gl4_render_output_framebuffer() { - glBindFramebuffer(GL_FRAMEBUFFER, 0); glViewport(0, 0, screen_width, screen_height); - if (gl.ofbo.tex == 0) + glcache.Disable(GL_SCISSOR_TEST); + if (gl.ofbo.fbo == 0) return false; - - gl4_draw_quad_texture(gl.ofbo.tex, true); + glBindFramebuffer(GL_READ_FRAMEBUFFER, gl.ofbo.fbo); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glBlitFramebuffer(0, 0, gl.ofbo.width, gl.ofbo.height, + 0, 0, screen_width, screen_height, + GL_COLOR_BUFFER_BIT, GL_LINEAR); + glBindFramebuffer(GL_FRAMEBUFFER, 0); return true; } diff --git a/core/rend/gl4/gles.cpp b/core/rend/gl4/gles.cpp index 505b1d23f..4d3003569 100644 --- a/core/rend/gl4/gles.cpp +++ b/core/rend/gl4/gles.cpp @@ -14,6 +14,7 @@ static const char* VertexShaderSource = "\ #version 140 \n\ #define pp_Gouraud %d \n\ +#define ROTATE_90 %d \n\ \n\ #if pp_Gouraud == 0 \n\ #define INTERPOLATION flat \n\ @@ -56,6 +57,9 @@ void main() \n\ \n\ vpos.w = extra_depth_scale / vpos.z; \n\ vpos.z = vpos.w; \n\ +#if ROTATE_90 == 1 \n\ + vpos.xy = vec2(vpos.y, -vpos.x); \n\ +#endif \n\ vpos.xy=vpos.xy*scale.xy-scale.zw; \n\ vpos.xy*=vpos.w; \n\ gl_Position = vpos; \n\ @@ -393,11 +397,11 @@ gl4_ctx gl4; struct gl4ShaderUniforms_t gl4ShaderUniforms; -bool gl4CompilePipelineShader( gl4PipelineShader* s, const char *source /* = PixelPipelineShader */) +bool gl4CompilePipelineShader( gl4PipelineShader* s, bool rotate_90, const char *source /* = PixelPipelineShader */) { char vshader[16384]; - sprintf(vshader, VertexShaderSource, s->pp_Gouraud); + sprintf(vshader, VertexShaderSource, s->pp_Gouraud, rotate_90); char pshader[16384]; @@ -478,28 +482,45 @@ bool gl4CompilePipelineShader( gl4PipelineShader* s, const char *source /* = Pix void gl_term(); +void gl4_delete_shaders() +{ + for (auto it : gl4.shaders) + { + if (it.second.program != 0) + glcache.DeleteProgram(it.second.program); + } + gl4.shaders.clear(); + glcache.DeleteProgram(gl4.modvol_shader.program); + gl4.modvol_shader.program = 0; +} + static void gles_term(void) { - glDeleteProgram(gl4.modvol_shader.program); glDeleteBuffers(1, &gl4.vbo.geometry); gl4.vbo.geometry = 0; glDeleteBuffers(1, &gl4.vbo.modvols); glDeleteBuffers(1, &gl4.vbo.idxs); glDeleteBuffers(1, &gl4.vbo.idxs2); glDeleteBuffers(1, &gl4.vbo.tr_poly_params); - for (auto it = gl4.shaders.begin(); it != gl4.shaders.end(); it++) - { - if (it->second->program != -1) - glDeleteProgram(it->second->program); - delete it->second; - } - gl4.shaders.clear(); + gl4_delete_shaders(); glDeleteVertexArrays(1, &gl4.vbo.main_vao); glDeleteVertexArrays(1, &gl4.vbo.modvol_vao); gl_term(); } +static void create_modvol_shader() +{ + if (gl4.modvol_shader.program != 0) + return; + char vshader[16384]; + sprintf(vshader, VertexShaderSource, 1, settings.rend.Rotate90); + + gl4.modvol_shader.program=gl_CompileAndLink(vshader, ModifierVolumeShader); + gl4.modvol_shader.scale = glGetUniformLocation(gl4.modvol_shader.program, "scale"); + gl4.modvol_shader.extra_depth_scale = glGetUniformLocation(gl4.modvol_shader.program, "extra_depth_scale"); +} + static bool gl_create_resources() { if (gl4.vbo.geometry != 0) @@ -521,12 +542,7 @@ static bool gl_create_resources() gl4SetupMainVBO(); gl4SetupModvolVBO(); - char vshader[16384]; - sprintf(vshader, VertexShaderSource, 1); - - gl4.modvol_shader.program=gl_CompileAndLink(vshader, ModifierVolumeShader); - gl4.modvol_shader.scale = glGetUniformLocation(gl4.modvol_shader.program, "scale"); - gl4.modvol_shader.extra_depth_scale = glGetUniformLocation(gl4.modvol_shader.program, "extra_depth_scale"); + create_modvol_shader(); gl_load_osd_resources(); @@ -604,6 +620,7 @@ static bool RenderFrame() old_screen_scaling = settings.rend.ScreenScaling; } DoCleanup(); + create_modvol_shader(); bool is_rtt=pvrrc.isRTT; @@ -639,6 +656,8 @@ static bool RenderFrame() { scale_x=fb_scale_x; scale_y=fb_scale_y; + if (SCALER_CTL.interlace == 0 && SCALER_CTL.vscalefactor >= 0x400) + scale_y *= SCALER_CTL.vscalefactor / 0x400; //work out scaling parameters ! //Pixel doubling is on VO, so it does not affect any pixel operations @@ -662,16 +681,41 @@ static bool RenderFrame() /* Handle Dc to screen scaling */ - float screen_scaling = is_rtt ? 1.f : settings.rend.ScreenScaling / 100.f; - float dc2s_scale_h = is_rtt ? (screen_width / dc_width) : (screen_height / 480.0); - dc2s_scale_h *= screen_scaling; - float ds2s_offs_x = is_rtt ? 0 : (((screen_width * screen_scaling) - dc2s_scale_h * 640.0) / 2); + float screen_scaling = settings.rend.ScreenScaling / 100.f; + float screen_stretching = settings.rend.ScreenStretching / 100.f; - //-1 -> too much to left - gl4ShaderUniforms.scale_coefs[0] = 2.0f / (screen_width * screen_scaling / dc2s_scale_h * scale_x); - gl4ShaderUniforms.scale_coefs[1] = (is_rtt ? 2 : -2) / dc_height; // FIXME CT2 needs 480 here instead of dc_height=512 - gl4ShaderUniforms.scale_coefs[2] = 1 - 2 * ds2s_offs_x / (screen_width * screen_scaling); - gl4ShaderUniforms.scale_coefs[3] = (is_rtt ? 1 : -1); + float dc2s_scale_h; + float ds2s_offs_x; + + if (is_rtt) + { + gl4ShaderUniforms.scale_coefs[0] = 2.0f / dc_width; + gl4ShaderUniforms.scale_coefs[1] = 2.0f / dc_height; // FIXME CT2 needs 480 here instead of dc_height=512 + gl4ShaderUniforms.scale_coefs[2] = 1; + gl4ShaderUniforms.scale_coefs[3] = 1; + } + else + { + if (settings.rend.Rotate90) + { + dc2s_scale_h = screen_height / 640.0; + ds2s_offs_x = (screen_width - dc2s_scale_h * 480.0 * screen_stretching) / 2; + gl4ShaderUniforms.scale_coefs[0] = 2.0f / (screen_width / dc2s_scale_h * scale_x) * screen_stretching; + gl4ShaderUniforms.scale_coefs[1] = -2.0f / dc_width; + gl4ShaderUniforms.scale_coefs[2] = 1 - 2 * ds2s_offs_x / screen_width; + gl4ShaderUniforms.scale_coefs[3] = 1; + } + else + { + dc2s_scale_h = screen_height / 480.0; + ds2s_offs_x = (screen_width - dc2s_scale_h * 640.0 * screen_stretching) / 2; + //-1 -> too much to left + gl4ShaderUniforms.scale_coefs[0] = 2.0f / (screen_width / dc2s_scale_h * scale_x) * screen_stretching; + gl4ShaderUniforms.scale_coefs[1] = -2.0f / dc_height; + gl4ShaderUniforms.scale_coefs[2] = 1 - 2 * ds2s_offs_x / screen_width; + gl4ShaderUniforms.scale_coefs[3] = -1; + } + } gl4ShaderUniforms.extra_depth_scale = settings.rend.ExtraDepthScale; @@ -704,7 +748,7 @@ static bool RenderFrame() gl4ShaderUniforms.fog_clamp_max[2] = ((pvrrc.fog_clamp_max >> 0) & 0xFF) / 255.0f; gl4ShaderUniforms.fog_clamp_max[3] = ((pvrrc.fog_clamp_max >> 24) & 0xFF) / 255.0f; - if (fog_needs_update) + if (fog_needs_update && settings.rend.Fog) { fog_needs_update = false; UpdateFogTexture((u8 *)FOG_TABLE, GL_TEXTURE5, GL_RED); @@ -764,7 +808,7 @@ static bool RenderFrame() { if (settings.rend.ScreenScaling != 100 || gl.swap_buffer_not_preserved) { - output_fbo = init_output_framebuffer(screen_width * screen_scaling, screen_height * screen_scaling); + output_fbo = init_output_framebuffer(screen_width * screen_scaling + 0.5f, screen_height * screen_scaling + 0.5f); } else { @@ -826,22 +870,37 @@ static bool RenderFrame() float min_y = pvrrc.fb_Y_CLIP.min / scale_y; if (!is_rtt) { + if (SCALER_CTL.interlace && SCALER_CTL.vscalefactor >= 0x400) + { + // Clipping is done after scaling/filtering so account for that if enabled + height *= SCALER_CTL.vscalefactor / 0x400; + min_y *= SCALER_CTL.vscalefactor / 0x400; + } + if (settings.rend.Rotate90) + { + float t = width; + width = height; + height = t; + t = min_x; + min_x = min_y; + min_y = 640 - t - height; + } // Add x offset for aspect ratio > 4/3 - min_x = min_x * dc2s_scale_h + ds2s_offs_x; + min_x = (min_x * dc2s_scale_h * screen_stretching + ds2s_offs_x) * screen_scaling; // Invert y coordinates when rendering to screen - min_y = screen_height * screen_scaling - (min_y + height) * dc2s_scale_h; - width *= dc2s_scale_h; - height *= dc2s_scale_h; + min_y = (screen_height - (min_y + height) * dc2s_scale_h) * screen_scaling; + width *= dc2s_scale_h * screen_stretching * screen_scaling; + height *= dc2s_scale_h * screen_scaling; if (ds2s_offs_x > 0) { - float rounded_offs_x = ds2s_offs_x + 0.5f; + float scaled_offs_x = ds2s_offs_x * screen_scaling; glcache.ClearColor(0.f, 0.f, 0.f, 0.f); glcache.Enable(GL_SCISSOR_TEST); - glScissor(0, 0, rounded_offs_x, screen_height); + glScissor(0, 0, scaled_offs_x + 0.5f, screen_height * screen_scaling + 0.5f); glClear(GL_COLOR_BUFFER_BIT); - glScissor(screen_width - rounded_offs_x, 0, rounded_offs_x, screen_height); + glScissor(screen_width * screen_scaling - scaled_offs_x + 0.5f, 0, scaled_offs_x + 1.f, screen_height * screen_scaling + 0.5f); glClear(GL_COLOR_BUFFER_BIT); } } diff --git a/core/rend/gl4/gltex.cpp b/core/rend/gl4/gltex.cpp index 85a0b9430..52aba888e 100644 --- a/core/rend/gl4/gltex.cpp +++ b/core/rend/gl4/gltex.cpp @@ -1,5 +1,5 @@ #include "gl4.h" -#include "glcache.h" +#include "../gles/glcache.h" GLuint gl4BindRTT(u32 addy, u32 fbw, u32 fbh, u32 channels, u32 fmt) { diff --git a/core/rend/gles/CustomTexture.cpp b/core/rend/gles/CustomTexture.cpp index 6b3e032ad..8f062d1b2 100644 --- a/core/rend/gles/CustomTexture.cpp +++ b/core/rend/gles/CustomTexture.cpp @@ -22,7 +22,11 @@ #include #include #include +#ifdef _MSC_VER +#include "dirent/dirent.h" +#else #include +#endif #include "deps/libpng/png.h" #include "reios/reios.h" @@ -46,19 +50,28 @@ void CustomTexture::LoaderThread() if (texture != NULL) { - // FIXME texture may have been deleted. Need to detect this. texture->ComputeHash(); - int width, height; - u8 *image_data = LoadCustomTexture(texture->texture_hash, width, height); - if (image_data != NULL) + if (texture->custom_image_data != NULL) { - if (texture->custom_image_data != NULL) - delete [] texture->custom_image_data; - texture->custom_width = width; - texture->custom_height = height; - texture->custom_image_data = image_data; + delete [] texture->custom_image_data; + texture->custom_image_data = NULL; } - texture->custom_load_in_progress = false; + if (!texture->dirty) + { + int width, height; + u8 *image_data = LoadCustomTexture(texture->texture_hash, width, height); + if (image_data == NULL) + { + image_data = LoadCustomTexture(texture->old_texture_hash, width, height); + } + if (image_data != NULL) + { + texture->custom_width = width; + texture->custom_height = height; + texture->custom_image_data = image_data; + } + } + texture->custom_load_in_progress--; } } while (texture != NULL); @@ -126,7 +139,7 @@ u8* CustomTexture::LoadCustomTexture(u32 hash, int& width, int& height) std::stringstream path; path << textures_path << std::hex << hash << ".png"; - u8 *image_data = loadPNGData(path.str(), width, height, false); + u8 *image_data = loadPNGData(path.str(), width, height); if (image_data == NULL) unknown_hashes.insert(hash); @@ -136,10 +149,9 @@ u8* CustomTexture::LoadCustomTexture(u32 hash, int& width, int& height) void CustomTexture::LoadCustomTextureAsync(TextureCacheData *texture_data) { if (!Init()) - { - texture_data->custom_load_in_progress = false; return; - } + + texture_data->custom_load_in_progress++; work_queue_mutex.Lock(); work_queue.insert(work_queue.begin(), texture_data); work_queue_mutex.Unlock(); @@ -173,8 +185,8 @@ void CustomTexture::DumpTexture(u32 hash, int w, int h, GLuint textype, void *te png_bytepp rows = (png_bytepp)malloc(h * sizeof(png_bytep)); for (int y = 0; y < h; y++) { - rows[y] = (png_bytep)malloc(w * 4); // 32-bit per pixel - u8 *dst = (u8 *)rows[y]; + rows[h - y - 1] = (png_bytep)malloc(w * 4); // 32-bit per pixel + u8 *dst = (u8 *)rows[h - y - 1]; switch (textype) { case GL_UNSIGNED_SHORT_4_4_4_4: diff --git a/core/rend/gles/CustomTexture.h b/core/rend/gles/CustomTexture.h index d0847717b..fd8a00db1 100644 --- a/core/rend/gles/CustomTexture.h +++ b/core/rend/gles/CustomTexture.h @@ -29,9 +29,9 @@ public: CustomTexture() : #ifndef TARGET_NO_THREADS - loader_thread(loader_thread_func, this), + loader_thread(loader_thread_func, this) #endif - wakeup_thread(false, true) {} + {} ~CustomTexture() { Terminate(); } u8* LoadCustomTexture(u32 hash, int& width, int& height); void LoadCustomTextureAsync(TextureCacheData *texture_data); @@ -53,7 +53,7 @@ private: cThread loader_thread; #endif cResetEvent wakeup_thread; - std::vector work_queue; + std::vector work_queue; cMutex work_queue_mutex; }; diff --git a/core/rend/gles/gl32funcs.c b/core/rend/gles/gl32funcs.c index de06ae4ad..05e54c41e 100644 --- a/core/rend/gles/gl32funcs.c +++ b/core/rend/gles/gl32funcs.c @@ -2,10 +2,11 @@ #include #include #include "gl32funcs.h" +#include "build.h" void load_gles_symbols() { -#ifdef _ANDROID +#ifdef GLES3 for (int i = 0; rglgen_symbol_map[i].sym != NULL; i++) *(void **)rglgen_symbol_map[i].ptr = eglGetProcAddress(rglgen_symbol_map[i].sym); #endif diff --git a/core/rend/gles/glcache.h b/core/rend/gles/glcache.h index d7f292af9..32c73c644 100644 --- a/core/rend/gles/glcache.h +++ b/core/rend/gles/glcache.h @@ -149,6 +149,19 @@ public: return _texture_ids[--_texture_cache_size]; } + void DeleteProgram(GLuint program) + { + GLsizei shader_count; + GLuint shaders[2]; + glGetAttachedShaders(program, ARRAY_SIZE(shaders), &shader_count, shaders); + for (int i = 0; i < shader_count; i++) + glDeleteShader(shaders[i]); + + glDeleteProgram(program); + if (_program == program) + _program = 0; + } + void Reset() { _texture = 0xFFFFFFFFu; _src_blend_factor = 0xFFFFFFFFu; @@ -179,7 +192,7 @@ public: void DisableCache() { _disable_cache = true; } void EnableCache() { - _disable_cache = true; + _disable_cache = false; Reset(); } diff --git a/core/rend/gles/gldraw.cpp b/core/rend/gles/gldraw.cpp index 5ed09a66e..66c208886 100644 --- a/core/rend/gles/gldraw.cpp +++ b/core/rend/gles/gldraw.cpp @@ -114,13 +114,30 @@ s32 SetTileClip(u32 val, GLint uniform) csy /= scale_y; cex /= scale_x; cey /= scale_y; - float t = cey; - cey = 480 - csy; - csy = 480 - t; - float dc2s_scale_h = screen_height / 480.0f; - float ds2s_offs_x = (screen_width - dc2s_scale_h * 640) / 2; - csx = csx * dc2s_scale_h + ds2s_offs_x; - cex = cex * dc2s_scale_h + ds2s_offs_x; + float dc2s_scale_h; + float ds2s_offs_x; + float screen_stretching = settings.rend.ScreenStretching / 100.f; + + if (settings.rend.Rotate90) + { + float t = cex; + cex = cey; + cey = 640 - csx; + csx = csy; + csy = 640 - t; + dc2s_scale_h = screen_height / 640.0f; + ds2s_offs_x = (screen_width - dc2s_scale_h * 480.0 * screen_stretching) / 2; + } + else + { + float t = cey; + cey = 480 - csy; + csy = 480 - t; + dc2s_scale_h = screen_height / 480.0f; + ds2s_offs_x = (screen_width - dc2s_scale_h * 640.0 * screen_stretching) / 2; + } + csx = csx * dc2s_scale_h * screen_stretching + ds2s_offs_x; + cex = cex * dc2s_scale_h * screen_stretching + ds2s_offs_x; csy = csy * dc2s_scale_h; cey = cey * dc2s_scale_h; } @@ -173,28 +190,25 @@ __forceinline ShaderUniforms.trilinear_alpha = 1.f; bool color_clamp = gp->tsp.ColorClamp && (pvrrc.fog_clamp_min != 0 || pvrrc.fog_clamp_max != 0xffffffff); + int fog_ctrl = settings.rend.Fog ? gp->tsp.FogCtrl : 2; - CurrentShader = &gl.pogram_table[ - GetProgramID(Type == ListType_Punch_Through ? 1 : 0, - SetTileClip(gp->tileclip, -1) + 1, - gp->pcw.Texture, - gp->tsp.UseAlpha, - gp->tsp.IgnoreTexA, - gp->tsp.ShadInstr, - gp->pcw.Offset, - gp->tsp.FogCtrl, - gp->pcw.Gouraud, - gp->tcw.PixelFmt == PixelBumpMap, - color_clamp, - ShaderUniforms.trilinear_alpha != 1.f)]; + CurrentShader = GetProgram(Type == ListType_Punch_Through ? 1 : 0, + SetTileClip(gp->tileclip, -1) + 1, + gp->pcw.Texture, + gp->tsp.UseAlpha, + gp->tsp.IgnoreTexA, + gp->tsp.ShadInstr, + gp->pcw.Offset, + fog_ctrl, + gp->pcw.Gouraud, + gp->tcw.PixelFmt == PixelBumpMap, + color_clamp, + ShaderUniforms.trilinear_alpha != 1.f); - if (CurrentShader->program == -1) - CompilePipelineShader(CurrentShader); - else - { - glcache.UseProgram(CurrentShader->program); - ShaderUniforms.Set(CurrentShader); - } + glcache.UseProgram(CurrentShader->program); + if (CurrentShader->trilinear_alpha != -1) + glUniform1f(CurrentShader->trilinear_alpha, ShaderUniforms.trilinear_alpha); + SetTileClip(gp->tileclip, CurrentShader->pp_ClipTest); //This bit control which pixels are affected @@ -246,11 +260,9 @@ __forceinline glcache.DepthFunc(Zfunction[gp->isp.DepthMode]); } -#if TRIG_SORT - if (SortingEnabled) + if (SortingEnabled && !settings.rend.PerStripSorting) glcache.DepthMask(GL_FALSE); else -#endif glcache.DepthMask(!gp->isp.ZWriteDis); } @@ -934,9 +946,10 @@ void SetMVS_Mode(ModifierVolumeMode mv_mode, ISP_Modvol ispc) static void SetupMainVBO() { +#ifndef GLES2 if (gl.gl_major >= 3) glBindVertexArray(gl.vbo.vao); - +#endif glBindBuffer(GL_ARRAY_BUFFER, gl.vbo.geometry); glCheck(); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl.vbo.idxs); glCheck(); @@ -956,9 +969,10 @@ static void SetupMainVBO() void SetupModvolVBO() { +#ifndef GLES2 if (gl.gl_major >= 3) glBindVertexArray(gl.vbo.vao); - +#endif glBindBuffer(GL_ARRAY_BUFFER, gl.vbo.modvols); glCheck(); //setup vertex buffers attrib pointers @@ -1089,13 +1103,16 @@ void DrawStrips() { if (current_pass.autosort) { -#if TRIG_SORT - GenSorted(previous_pass.tr_count, current_pass.tr_count - previous_pass.tr_count); - DrawSorted(render_pass < pvrrc.render_passes.used() - 1); -#else - SortPParams(previous_pass.tr_count, current_pass.tr_count - previous_pass.tr_count); - DrawList(pvrrc.global_param_tr, previous_pass.tr_count, current_pass.tr_count - previous_pass.tr_count); -#endif + if (!settings.rend.PerStripSorting) + { + GenSorted(previous_pass.tr_count, current_pass.tr_count - previous_pass.tr_count); + DrawSorted(render_pass < pvrrc.render_passes.used() - 1); + } + else + { + SortPParams(previous_pass.tr_count, current_pass.tr_count - previous_pass.tr_count); + DrawList(pvrrc.global_param_tr, previous_pass.tr_count, current_pass.tr_count - previous_pass.tr_count); + } } else DrawList(pvrrc.global_param_tr, previous_pass.tr_count, current_pass.tr_count - previous_pass.tr_count); @@ -1122,14 +1139,8 @@ static void DrawQuad(GLuint texId, float x, float y, float w, float h, float u0, ShaderUniforms.trilinear_alpha = 1.0; - PipelineShader *shader = &gl.pogram_table[GetProgramID(0, 1, 1, 0, 1, 0, 0, 2, false, false, false, false)]; - if (shader->program == -1) - CompilePipelineShader(shader); - else - { - glcache.UseProgram(shader->program); - ShaderUniforms.Set(shader); - } + PipelineShader *shader = GetProgram(0, 1, 1, 0, 1, 0, 0, 2, false, false, false, false); + glcache.UseProgram(shader->program); glActiveTexture(GL_TEXTURE0); glcache.BindTexture(GL_TEXTURE_2D, texId); @@ -1150,17 +1161,29 @@ void DrawFramebuffer(float w, float h) bool render_output_framebuffer() { -#if HOST_OS != OS_DARWIN - //Fix this in a proper way - glBindFramebuffer(GL_FRAMEBUFFER, 0); + glcache.Disable(GL_SCISSOR_TEST); + if (gl.gl_major < 3) + { + glViewport(0, 0, screen_width, screen_height); + if (gl.ofbo.tex == 0) + return false; + glBindFramebuffer(GL_FRAMEBUFFER, 0); + float scl = 480.f / screen_height; + float tx = (screen_width * scl - 640.f) / 2; + DrawQuad(gl.ofbo.tex, -tx, 0, 640.f + tx * 2, 480.f, 0, 1, 1, 0); + } + else + { +#ifndef GLES2 + if (gl.ofbo.fbo == 0) + return false; + glBindFramebuffer(GL_READ_FRAMEBUFFER, gl.ofbo.fbo); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glBlitFramebuffer(0, 0, gl.ofbo.width, gl.ofbo.height, + 0, 0, screen_width, screen_height, + GL_COLOR_BUFFER_BIT, GL_LINEAR); + glBindFramebuffer(GL_FRAMEBUFFER, 0); #endif - glViewport(0, 0, screen_width, screen_height); - if (gl.ofbo.tex == 0) - return false; - - float scl = 480.f / screen_height; - float tx = (screen_width * scl - 640.f) / 2; - DrawQuad(gl.ofbo.tex, -tx, 0, 640.f + tx * 2, 480.f, 0, 1, 1, 0); - + } return true; } diff --git a/core/rend/gles/gles.cpp b/core/rend/gles/gles.cpp index 26a7210a8..0f65d02b4 100644 --- a/core/rend/gles/gles.cpp +++ b/core/rend/gles/gles.cpp @@ -79,6 +79,7 @@ const char* VertexShaderSource = %s \n\ #define TARGET_GL %s \n\ #define pp_Gouraud %d \n\ +#define ROTATE_90 %d \n\ \n\ #define GLES2 0 \n\ #define GLES3 1 \n\ @@ -136,6 +137,9 @@ void main() \n\ vpos.z = vpos.w; \n\ #else \n\ vpos.z=depth_scale.x+depth_scale.y*vpos.w; \n\ +#endif \n\ +#if ROTATE_90 == 1 \n\ + vpos.xy = vec2(vpos.y, -vpos.x); \n\ #endif \n\ vpos.xy=vpos.xy*scale.xy-scale.zw; \n\ vpos.xy*=vpos.w; \n\ @@ -560,6 +564,7 @@ GLuint fogTextureId; return false; } #ifdef GLES +// EGL only supports runtime loading with android? TDB load_gles_symbols(); #else egl_makecurrent(); @@ -779,7 +784,7 @@ GLuint fogTextureId; return rv; } - #include + #include void gl_swap() { wglSwapLayerBuffers(ourWindowHandleToDeviceContext,WGL_SWAP_MAIN_PLANE); @@ -850,9 +855,20 @@ GLuint fogTextureId; extern void gl_term(); #endif +static void gl_delete_shaders() +{ + for (auto it : gl.shaders) + { + if (it.second.program != 0) + glcache.DeleteProgram(it.second.program); + } + gl.shaders.clear(); + glcache.DeleteProgram(gl.modvol_shader.program); + gl.modvol_shader.program = 0; +} + static void gles_term() { - glDeleteProgram(gl.modvol_shader.program); glDeleteBuffers(1, &gl.vbo.geometry); gl.vbo.geometry = 0; glDeleteBuffers(1, &gl.vbo.modvols); @@ -865,7 +881,7 @@ static void gles_term() gl_free_osd_resources(); free_output_framebuffer(); - memset(gl.pogram_table, 0, sizeof(gl.pogram_table)); + gl_delete_shaders(); gl_term(); } @@ -1014,10 +1030,15 @@ GLuint gl_CompileAndLink(const char* VertexShader, const char* FragmentShader) return program; } -int GetProgramID(u32 cp_AlphaTest, u32 pp_ClipTestMode, +PipelineShader *GetProgram(u32 cp_AlphaTest, u32 pp_ClipTestMode, u32 pp_Texture, u32 pp_UseAlpha, u32 pp_IgnoreTexA, u32 pp_ShadInstr, u32 pp_Offset, u32 pp_FogCtrl, bool pp_Gouraud, bool pp_BumpMap, bool fog_clamping, bool trilinear) { + if (settings.rend.Rotate90 != gl.rotate90) + { + gl_delete_shaders(); + gl.rotate90 = settings.rend.Rotate90; + } u32 rv=0; rv|=pp_ClipTestMode; @@ -1033,14 +1054,32 @@ int GetProgramID(u32 cp_AlphaTest, u32 pp_ClipTestMode, rv<<=1; rv|=fog_clamping; rv<<=1; rv|=trilinear; - return rv; + PipelineShader *shader = &gl.shaders[rv]; + if (shader->program == 0) + { + shader->cp_AlphaTest = cp_AlphaTest; + shader->pp_ClipTestMode = pp_ClipTestMode-1; + shader->pp_Texture = pp_Texture; + shader->pp_UseAlpha = pp_UseAlpha; + shader->pp_IgnoreTexA = pp_IgnoreTexA; + shader->pp_ShadInstr = pp_ShadInstr; + shader->pp_Offset = pp_Offset; + shader->pp_FogCtrl = pp_FogCtrl; + shader->pp_Gouraud = pp_Gouraud; + shader->pp_BumpMap = pp_BumpMap; + shader->fog_clamping = fog_clamping; + shader->trilinear = trilinear; + CompilePipelineShader(shader); + } + + return shader; } bool CompilePipelineShader( PipelineShader* s) { char vshader[8192]; - sprintf(vshader, VertexShaderSource, gl.glsl_version_header, gl.gl_version, s->pp_Gouraud); + sprintf(vshader, VertexShaderSource, gl.glsl_version_header, gl.gl_version, s->pp_Gouraud, settings.rend.Rotate90); char pshader[8192]; @@ -1117,20 +1156,39 @@ void gl_load_osd_resources() gl.OSD_SHADER.scale = glGetUniformLocation(gl.OSD_SHADER.program, "scale"); glUniform1i(glGetUniformLocation(gl.OSD_SHADER.program, "tex"), 0); //bind osd texture to slot 0 +#ifdef _ANDROID int w, h; if (osd_tex == 0) osd_tex = loadPNG(get_readonly_data_path("/data/buttons.png"), w, h); +#endif } void gl_free_osd_resources() { - glDeleteProgram(gl.OSD_SHADER.program); + glcache.DeleteProgram(gl.OSD_SHADER.program); if (osd_tex != 0) { glcache.DeleteTextures(1, &osd_tex); osd_tex = 0; } } + +static void create_modvol_shader() +{ + if (gl.modvol_shader.program != 0) + return; + char vshader[8192]; + sprintf(vshader, VertexShaderSource, gl.glsl_version_header, gl.gl_version, 1, settings.rend.Rotate90); + char fshader[8192]; + sprintf(fshader, ModifierVolumeShader, gl.glsl_version_header, gl.gl_version); + + gl.modvol_shader.program=gl_CompileAndLink(vshader, fshader); + gl.modvol_shader.scale = glGetUniformLocation(gl.modvol_shader.program, "scale"); + gl.modvol_shader.sp_ShaderColor = glGetUniformLocation(gl.modvol_shader.program, "sp_ShaderColor"); + gl.modvol_shader.depth_scale = glGetUniformLocation(gl.modvol_shader.program, "depth_scale"); + gl.modvol_shader.extra_depth_scale = glGetUniformLocation(gl.modvol_shader.program, "extra_depth_scale"); +} + bool gl_create_resources() { if (gl.vbo.geometry != 0) @@ -1145,7 +1203,9 @@ bool gl_create_resources() //create vao //This is really not "proper", vaos are supposed to be defined once //i keep updating the same one to make the es2 code work in 3.1 context +#ifndef GLES2 glGenVertexArrays(1, &gl.vbo.vao); +#endif } //create vbos @@ -1154,84 +1214,7 @@ bool gl_create_resources() glGenBuffers(1, &gl.vbo.idxs); glGenBuffers(1, &gl.vbo.idxs2); - memset(gl.pogram_table,0,sizeof(gl.pogram_table)); - - PipelineShader* dshader=0; - u32 compile=0; -#define forl(name,max) for(u32 name=0;name<=max;name++) - forl(cp_AlphaTest,1) - { - forl(pp_ClipTestMode,2) - { - forl(pp_UseAlpha,1) - { - forl(pp_Texture,1) - { - forl(pp_FogCtrl,3) - { - forl(pp_IgnoreTexA,1) - { - forl(pp_ShadInstr,3) - { - forl(pp_Offset,1) - { - forl(pp_Gouraud,1) - { - forl(pp_BumpMap,1) - { - forl(fog_clamping,1) - { - forl(trilinear,1) - { - dshader=&gl.pogram_table[GetProgramID(cp_AlphaTest,pp_ClipTestMode,pp_Texture,pp_UseAlpha,pp_IgnoreTexA, - pp_ShadInstr,pp_Offset,pp_FogCtrl, (bool)pp_Gouraud, (bool)pp_BumpMap, (bool)fog_clamping, - (bool)trilinear)]; - - dshader->cp_AlphaTest = cp_AlphaTest; - dshader->pp_ClipTestMode = pp_ClipTestMode-1; - dshader->pp_Texture = pp_Texture; - dshader->pp_UseAlpha = pp_UseAlpha; - dshader->pp_IgnoreTexA = pp_IgnoreTexA; - dshader->pp_ShadInstr = pp_ShadInstr; - dshader->pp_Offset = pp_Offset; - dshader->pp_FogCtrl = pp_FogCtrl; - dshader->pp_Gouraud = pp_Gouraud; - dshader->pp_BumpMap = pp_BumpMap; - dshader->fog_clamping = fog_clamping; - dshader->trilinear = trilinear; - dshader->program = -1; - } - } - } - } - } - } - } - } - } - } - } - } - - char vshader[8192]; - sprintf(vshader, VertexShaderSource, gl.glsl_version_header, gl.gl_version, 1); - char fshader[8192]; - sprintf(fshader, ModifierVolumeShader, gl.glsl_version_header, gl.gl_version); - - gl.modvol_shader.program=gl_CompileAndLink(vshader, fshader); - gl.modvol_shader.scale = glGetUniformLocation(gl.modvol_shader.program, "scale"); - gl.modvol_shader.sp_ShaderColor = glGetUniformLocation(gl.modvol_shader.program, "sp_ShaderColor"); - gl.modvol_shader.depth_scale = glGetUniformLocation(gl.modvol_shader.program, "depth_scale"); - gl.modvol_shader.extra_depth_scale = glGetUniformLocation(gl.modvol_shader.program, "extra_depth_scale"); - - //#define PRECOMPILE_SHADERS - #ifdef PRECOMPILE_SHADERS - for (u32 i=0;i= 0x400) + scale_y *= SCALER_CTL.vscalefactor / 0x400; //work out scaling parameters ! //Pixel doubling is on VO, so it does not affect any pixel operations @@ -1717,17 +1703,41 @@ bool RenderFrame() /* Handle Dc to screen scaling */ - float screen_scaling = is_rtt ? 1.f : settings.rend.ScreenScaling / 100.f; - float dc2s_scale_h = is_rtt ? (screen_width / dc_width) : (screen_height / 480.0); - dc2s_scale_h *= screen_scaling; - float ds2s_offs_x = is_rtt ? 0 : (((screen_width * screen_scaling) - dc2s_scale_h * 640.0) / 2); + float screen_stretching = settings.rend.ScreenStretching / 100.f; + float screen_scaling = settings.rend.ScreenScaling / 100.f; - //-1 -> too much to left - ShaderUniforms.scale_coefs[0] = 2.0f / (screen_width * screen_scaling / dc2s_scale_h * scale_x); - ShaderUniforms.scale_coefs[1]= (is_rtt ? 2 : -2) / dc_height; // FIXME CT2 needs 480 here instead of dc_height=512 - ShaderUniforms.scale_coefs[2]= 1 - 2 * ds2s_offs_x / (screen_width * screen_scaling); - ShaderUniforms.scale_coefs[3]= (is_rtt ? 1 : -1); + float dc2s_scale_h; + float ds2s_offs_x; + if (is_rtt) + { + ShaderUniforms.scale_coefs[0] = 2.0f / dc_width; + ShaderUniforms.scale_coefs[1] = 2.0f / dc_height; // FIXME CT2 needs 480 here instead of dc_height=512 + ShaderUniforms.scale_coefs[2] = 1; + ShaderUniforms.scale_coefs[3] = 1; + } + else + { + if (settings.rend.Rotate90) + { + dc2s_scale_h = screen_height / 640.0f; + ds2s_offs_x = (screen_width - dc2s_scale_h * 480.0f * screen_stretching) / 2; + ShaderUniforms.scale_coefs[0] = 2.0f / (screen_width / dc2s_scale_h * scale_x) * screen_stretching; + ShaderUniforms.scale_coefs[1] = -2.0f / dc_width; + ShaderUniforms.scale_coefs[2] = 1 - 2 * ds2s_offs_x / screen_width; + ShaderUniforms.scale_coefs[3] = 1; + } + else + { + dc2s_scale_h = screen_height / 480.0f; + ds2s_offs_x = (screen_width - dc2s_scale_h * 640.0f * screen_stretching) / 2; + ShaderUniforms.scale_coefs[0] = 2.0f / (screen_width / dc2s_scale_h * scale_x) * screen_stretching; + ShaderUniforms.scale_coefs[1] = -2.0f / dc_height; + ShaderUniforms.scale_coefs[2] = 1 - 2 * ds2s_offs_x / screen_width; + ShaderUniforms.scale_coefs[3] = -1; + } + //-1 -> too much to left + } ShaderUniforms.depth_coefs[0]=2/(vtx_max_fZ-vtx_min_fZ); ShaderUniforms.depth_coefs[1]=-vtx_min_fZ-1; @@ -1766,7 +1776,7 @@ bool RenderFrame() ShaderUniforms.fog_clamp_max[2] = ((pvrrc.fog_clamp_max >> 0) & 0xFF) / 255.0f; ShaderUniforms.fog_clamp_max[3] = ((pvrrc.fog_clamp_max >> 24) & 0xFF) / 255.0f; - if (fog_needs_update) + if (fog_needs_update && settings.rend.Fog) { fog_needs_update = false; UpdateFogTexture((u8 *)FOG_TABLE, GL_TEXTURE1, gl.fog_image_format); @@ -1780,16 +1790,12 @@ bool RenderFrame() ShaderUniforms.PT_ALPHA=(PT_ALPHA_REF&0xFF)/255.0f; -// for (u32 i=0;iprogram == -1) -// continue; -// -// glcache.UseProgram(s->program); -// -// ShaderUniforms.Set(s); -// } + for (auto it : gl.shaders) + { + glcache.UseProgram(it.second.program); + ShaderUniforms.Set(&it.second); + } + //setup render target first if (is_rtt) { @@ -1834,7 +1840,7 @@ bool RenderFrame() { if (settings.rend.ScreenScaling != 100 || gl.swap_buffer_not_preserved) { - init_output_framebuffer(screen_width * screen_scaling, screen_height * screen_scaling); + init_output_framebuffer(screen_width * screen_scaling + 0.5f, screen_height * screen_scaling + 0.5f); } else { @@ -1881,9 +1887,6 @@ bool RenderFrame() glBufferData(GL_ARRAY_BUFFER,pvrrc.modtrig.bytes(),pvrrc.modtrig.head(),GL_STREAM_DRAW); glCheck(); } - int offs_x=ds2s_offs_x+0.5f; - //this needs to be scaled - //not all scaling affects pixel operations, scale to adjust for that scale_x *= scissoring_scale_x; @@ -1902,22 +1905,37 @@ bool RenderFrame() float min_y = pvrrc.fb_Y_CLIP.min / scale_y; if (!is_rtt) { + if (SCALER_CTL.interlace && SCALER_CTL.vscalefactor >= 0x400) + { + // Clipping is done after scaling/filtering so account for that if enabled + height *= SCALER_CTL.vscalefactor / 0x400; + min_y *= SCALER_CTL.vscalefactor / 0x400; + } + if (settings.rend.Rotate90) + { + float t = width; + width = height; + height = t; + t = min_x; + min_x = min_y; + min_y = 640 - t - height; + } // Add x offset for aspect ratio > 4/3 - min_x = min_x * dc2s_scale_h + ds2s_offs_x; + min_x = (min_x * dc2s_scale_h * screen_stretching + ds2s_offs_x) * screen_scaling; // Invert y coordinates when rendering to screen - min_y = screen_height * screen_scaling - (min_y + height) * dc2s_scale_h; - width *= dc2s_scale_h; - height *= dc2s_scale_h; + min_y = (screen_height - (min_y + height) * dc2s_scale_h) * screen_scaling; + width *= dc2s_scale_h * screen_stretching * screen_scaling; + height *= dc2s_scale_h * screen_scaling; if (ds2s_offs_x > 0) { - float rounded_offs_x = ds2s_offs_x + 0.5f; + float scaled_offs_x = ds2s_offs_x * screen_scaling; glcache.ClearColor(0.f, 0.f, 0.f, 0.f); glcache.Enable(GL_SCISSOR_TEST); - glScissor(0, 0, rounded_offs_x, screen_height); + glScissor(0, 0, scaled_offs_x + 0.5f, screen_height * screen_scaling + 0.5f); glClear(GL_COLOR_BUFFER_BIT); - glScissor(screen_width - rounded_offs_x, 0, rounded_offs_x, screen_height); + glScissor(screen_width * screen_scaling - scaled_offs_x + 0.5f, 0, scaled_offs_x + 1.f, screen_height * screen_scaling + 0.5f); glClear(GL_COLOR_BUFFER_BIT); } } @@ -1986,8 +2004,10 @@ struct glesrend : Renderer void DrawOSD(bool clear_screen) { +#ifndef GLES2 if (gl.gl_major >= 3) glBindVertexArray(gl.vbo.vao); +#endif glBindBuffer(GL_ARRAY_BUFFER, gl.vbo.geometry); glCheck(); glEnableVertexAttribArray(VERTEX_POS_ARRAY); glVertexAttribPointer(VERTEX_POS_ARRAY, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex,x)); @@ -2016,7 +2036,7 @@ void png_cstd_read(png_structp png_ptr, png_bytep data, png_size_t length) fread(data,1, length,pngfile); } -u8* loadPNGData(const string& fname, int &width, int &height, bool bottom_to_top) +u8* loadPNGData(const string& fname, int &width, int &height) { const char* filename=fname.c_str(); FILE* file = fopen(filename, "rb"); @@ -2134,16 +2154,8 @@ u8* loadPNGData(const string& fname, int &width, int &height, bool bottom_to_top } // set the individual row_pointers to point at the correct offsets of image_data - if (bottom_to_top) - { - for (int i = 0; i < height; ++i) - row_pointers[height - 1 - i] = image_data + i * rowbytes; - } - else - { - for (int i = 0; i < height; ++i) - row_pointers[i] = image_data + i * rowbytes; - } + for (int i = 0; i < height; ++i) + row_pointers[height - 1 - i] = image_data + i * rowbytes; //read the png into image_data through row_pointers png_read_image(png_ptr, row_pointers); @@ -2155,7 +2167,7 @@ u8* loadPNGData(const string& fname, int &width, int &height, bool bottom_to_top return image_data; } -GLuint loadPNG(const string& fname, int &width, int &height, bool bottom_to_top) +GLuint loadPNG(const string& fname, int &width, int &height) { png_byte *image_data = loadPNGData(fname, width, height); if (image_data == NULL) diff --git a/core/rend/gles/gles.h b/core/rend/gles/gles.h index 7a899a8b4..dec2f287d 100755 --- a/core/rend/gles/gles.h +++ b/core/rend/gles/gles.h @@ -1,4 +1,6 @@ #pragma once +#include +#include #include "rend/rend.h" #if (defined(GLES) && !defined(TARGET_NACL32) && HOST_OS != OS_DARWIN && !defined(USE_SDL)) || defined(_ANDROID) @@ -15,7 +17,9 @@ #endif #include #include +#ifndef GLES2 #include "gl32funcs.h" +#endif #ifndef GL_NV_draw_path //IMGTEC GLES emulation @@ -93,7 +97,9 @@ struct gl_ctx } modvol_shader; - PipelineShader pogram_table[24576]; + std::unordered_map shaders; + bool rotate90; + struct { GLuint program; @@ -117,6 +123,7 @@ struct gl_ctx struct { GLuint depthb; + GLuint colorb; GLuint tex; GLuint fbo; int width; @@ -176,7 +183,7 @@ void free_output_framebuffer(); void HideOSD(); void OSD_DRAW(bool clear_screen); -int GetProgramID(u32 cp_AlphaTest, u32 pp_ClipTestMode, +PipelineShader *GetProgram(u32 cp_AlphaTest, u32 pp_ClipTestMode, u32 pp_Texture, u32 pp_UseAlpha, u32 pp_IgnoreTexA, u32 pp_ShadInstr, u32 pp_Offset, u32 pp_FogCtrl, bool pp_Gouraud, bool pp_BumpMap, bool fog_clamping, bool trilinear); @@ -184,8 +191,8 @@ GLuint gl_CompileShader(const char* shader, GLuint type); GLuint gl_CompileAndLink(const char* VertexShader, const char* FragmentShader); bool CompilePipelineShader(PipelineShader* s); #define TEXTURE_LOAD_ERROR 0 -u8* loadPNGData(const string& subpath, int &width, int &height, bool bottom_to_top = true); -GLuint loadPNG(const string& subpath, int &width, int &height, bool bottom_to_top = true); +u8* loadPNGData(const string& subpath, int &width, int &height); +GLuint loadPNG(const string& subpath, int &width, int &height); extern struct ShaderUniforms_t { @@ -223,9 +230,6 @@ extern struct ShaderUniforms_t if (s->sp_FOG_COL_VERT!=-1) glUniform3fv( s->sp_FOG_COL_VERT, 1, ps_FOG_COL_VERT); - if (s->trilinear_alpha != -1) - glUniform1f(s->trilinear_alpha, trilinear_alpha); - if (s->fog_clamp_min != -1) glUniform4fv(s->fog_clamp_min, 1, fog_clamp_min); if (s->fog_clamp_max != -1) @@ -271,10 +275,11 @@ struct TextureCacheData //VQ quantizers table for VQ tex //a texture can't be both VQ and PAL at the same time u32 texture_hash; // xxhash of texture data, used for custom textures - u8* custom_image_data; // loaded custom image data - u32 custom_width; - u32 custom_height; - bool custom_load_in_progress; + u32 old_texture_hash; // legacy hash + u8* volatile custom_image_data; // loaded custom image data + volatile u32 custom_width; + volatile u32 custom_height; + std::atomic_int custom_load_in_progress; void PrintTextureName(); diff --git a/core/rend/gles/gltex.cpp b/core/rend/gles/gltex.cpp index dc68a0312..1a3802fb7 100644 --- a/core/rend/gles/gltex.cpp +++ b/core/rend/gles/gltex.cpp @@ -243,6 +243,8 @@ void TextureCacheData::ComputeHash() texture_hash = XXH32(&vram[sa], size, 7); if (IsPaletted()) texture_hash ^= palette_hash; + old_texture_hash = texture_hash; + texture_hash ^= tcw.full; } void TextureCacheData::Update() @@ -294,10 +296,7 @@ void TextureCacheData::Update() } } if (settings.rend.CustomTextures) - { - custom_load_in_progress = true; custom_texture.LoadCustomTextureAsync(this); - } void *temp_tex_buffer = NULL; u32 upscaled_w = w; @@ -426,7 +425,7 @@ void TextureCacheData::UploadToGPU(GLuint textype, int width, int height, u8 *te void TextureCacheData::CheckCustomTexture() { - if (custom_image_data != NULL) + if (custom_load_in_progress == 0 && custom_image_data != NULL) { UploadToGPU(GL_UNSIGNED_BYTE, custom_width, custom_height, custom_image_data); delete [] custom_image_data; @@ -444,7 +443,7 @@ bool TextureCacheData::NeedsUpdate() { bool TextureCacheData::Delete() { - if (custom_load_in_progress) + if (custom_load_in_progress > 0) return false; if (pData) { @@ -734,11 +733,7 @@ TextureCacheData *getTextureCacheData(TSP tsp, TCW tcw) { } else //create if not existing { - TextureCacheData tfc={0}; - TexCache[key] = tfc; - - tx=TexCache.find(key); - tf=&tx->second; + tf=&TexCache[key]; tf->tsp = tsp; tf->tcw = tcw; @@ -798,11 +793,7 @@ text_info raw_GetTexture(TSP tsp, TCW tcw) } else //create if not existing { - TextureCacheData tfc = { 0 }; - TexCache[key] = tfc; - - tx = TexCache.find(key); - tf = &tx->second; + tf = &TexCache[key]; tf->tsp = tsp; tf->tcw = tcw; @@ -1005,13 +996,7 @@ GLuint init_output_framebuffer(int width, int height) { if (width != gl.ofbo.width || height != gl.ofbo.height) { - if (gl.ofbo.fbo != 0) - { - glDeleteFramebuffers(1, &gl.ofbo.fbo); - gl.ofbo.fbo = 0; - glDeleteRenderbuffers(1, &gl.ofbo.depthb); - glcache.DeleteTextures(1, &gl.ofbo.tex); - } + free_output_framebuffer(); gl.ofbo.width = width; gl.ofbo.height = height; } @@ -1035,15 +1020,25 @@ GLuint init_output_framebuffer(int width, int height) else glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); - // Create a texture for rendering to - gl.ofbo.tex = glcache.GenTexture(); - glcache.BindTexture(GL_TEXTURE_2D, gl.ofbo.tex); + if (gl.gl_major < 3) + { + // Create a texture for rendering to + gl.ofbo.tex = glcache.GenTexture(); + glcache.BindTexture(GL_TEXTURE_2D, gl.ofbo.tex); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + else + { + // Use a renderbuffer and glBlitFramebuffer + glGenRenderbuffers(1, &gl.ofbo.colorb); + glBindRenderbuffer(GL_RENDERBUFFER, gl.ofbo.colorb); + glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, height); + } // Create the framebuffer glGenFramebuffers(1, &gl.ofbo.fbo); @@ -1055,13 +1050,20 @@ GLuint init_output_framebuffer(int width, int height) if (!gl.is_gles || gl.GL_OES_packed_depth_stencil_supported) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, gl.ofbo.depthb); - // Attach the texture to the FBO - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gl.ofbo.tex, 0); + // Attach the texture/renderbuffer to the FBO + if (gl.gl_major < 3) + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gl.ofbo.tex, 0); + else + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, gl.ofbo.colorb); // Check that our FBO creation was successful GLuint uStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER); verify(uStatus == GL_FRAMEBUFFER_COMPLETE); + + glcache.Disable(GL_SCISSOR_TEST); + glcache.ClearColor(0.f, 0.f, 0.f, 0.f); + glClear(GL_COLOR_BUFFER_BIT); } else glBindFramebuffer(GL_FRAMEBUFFER, gl.ofbo.fbo); @@ -1080,7 +1082,15 @@ void free_output_framebuffer() gl.ofbo.fbo = 0; glDeleteRenderbuffers(1, &gl.ofbo.depthb); gl.ofbo.depthb = 0; - glcache.DeleteTextures(1, &gl.ofbo.tex); - gl.ofbo.tex = 0; + if (gl.ofbo.tex != 0) + { + glcache.DeleteTextures(1, &gl.ofbo.tex); + gl.ofbo.tex = 0; + } + if (gl.ofbo.colorb != 0) + { + glDeleteRenderbuffers(1, &gl.ofbo.colorb); + gl.ofbo.colorb = 0; + } } } diff --git a/core/rend/gles/imgui_impl_opengl3.cpp b/core/rend/gles/imgui_impl_opengl3.cpp index 3fe069942..4d2936b10 100644 --- a/core/rend/gles/imgui_impl_opengl3.cpp +++ b/core/rend/gles/imgui_impl_opengl3.cpp @@ -67,6 +67,7 @@ #endif #include "gles.h" +#include "glcache.h" // OpenGL Data static char g_GlslVersionString[32] = ""; @@ -127,28 +128,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data, bool save_backgr draw_data->ScaleClipRects(io.DisplayFramebufferScale); // Backup GL state - GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture); glActiveTexture(GL_TEXTURE0); - GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program); - GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - GLint last_sampler; glGetIntegerv(GL_SAMPLER_BINDING, &last_sampler); - GLint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); - GLint last_vertex_array; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); -#ifdef GL_POLYGON_MODE - GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); -#endif - GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport); - GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box); - GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, (GLint*)&last_blend_src_rgb); - GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, (GLint*)&last_blend_dst_rgb); - GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint*)&last_blend_src_alpha); - GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint*)&last_blend_dst_alpha); - GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, (GLint*)&last_blend_equation_rgb); - GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, (GLint*)&last_blend_equation_alpha); - GLboolean last_enable_blend = glIsEnabled(GL_BLEND); - GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE); - GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST); - GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST); bool clip_origin_lower_left = true; #ifdef GL_CLIP_ORIGIN if (gl.gl_major >= 4 && glClipControl != NULL) @@ -161,31 +141,33 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data, bool save_backgr if (save_background) { +#ifndef GLES2 if (!gl.is_gles && glReadBuffer != NULL) glReadBuffer(GL_FRONT); // (Re-)create the background texture and reserve space for it if (g_BackgroundTexture != 0) - glDeleteTextures(1, &g_BackgroundTexture); - glGenTextures(1, &g_BackgroundTexture); - glBindTexture(GL_TEXTURE_2D, g_BackgroundTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glcache.DeleteTextures(1, &g_BackgroundTexture); + g_BackgroundTexture = glcache.GenTexture(); + glcache.BindTexture(GL_TEXTURE_2D, g_BackgroundTexture); + glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, fb_width, fb_height, 0, GL_RGB, GL_UNSIGNED_BYTE, (GLvoid*)NULL); // Copy the current framebuffer into it glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, fb_width, fb_height); +#endif } // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill - glEnable(GL_BLEND); + glcache.Enable(GL_BLEND); glBlendEquation(GL_FUNC_ADD); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDisable(GL_CULL_FACE); - glDisable(GL_DEPTH_TEST); - glEnable(GL_SCISSOR_TEST); + glcache.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glcache.Disable(GL_CULL_FACE); + glcache.Disable(GL_DEPTH_TEST); + glcache.Enable(GL_SCISSOR_TEST); #ifdef GL_POLYGON_MODE if (glPolygonMode != NULL) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); @@ -205,13 +187,15 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data, bool save_backgr { 0.0f, 0.0f, -1.0f, 0.0f }, { (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f }, }; - glUseProgram(g_ShaderHandle); + glcache.UseProgram(g_ShaderHandle); glUniform1i(g_AttribLocationTex, 0); glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]); +#ifndef GLES2 if (gl.gl_major >= 3 && glBindSampler != NULL) glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise. - +#endif GLuint vao_handle = 0; +#ifndef GLES2 if (gl.gl_major >= 3) { // Recreate the VAO every time @@ -219,6 +203,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data, bool save_backgr glGenVertexArrays(1, &vao_handle); glBindVertexArray(vao_handle); } +#endif glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle); glEnableVertexAttribArray(g_AttribLocationPosition); glEnableVertexAttribArray(g_AttribLocationUV); @@ -260,37 +245,17 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data, bool save_backgr glScissor((int)clip_rect.x, (int)clip_rect.y, (int)clip_rect.z, (int)clip_rect.w); // Support for GL 4.5's glClipControl(GL_UPPER_LEFT) // Bind texture, Draw - glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId); + glcache.BindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId); glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset); } } idx_buffer_offset += pcmd->ElemCount; } } +#ifndef GLES2 if (vao_handle != 0) glDeleteVertexArrays(1, &vao_handle); - - // Restore modified GL state - glUseProgram(last_program); - glBindTexture(GL_TEXTURE_2D, last_texture); - if (gl.gl_major >= 3 && glBindSampler != NULL) - glBindSampler(0, last_sampler); - glActiveTexture(last_active_texture); - glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); - if (gl.gl_major >= 3) - glBindVertexArray(last_vertex_array); - glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha); - glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha); - if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND); - if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); - if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST); - if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST); -#ifdef GL_POLYGON_MODE - if (glPolygonMode != NULL) - glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]); #endif - glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]); - glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]); } bool ImGui_ImplOpenGL3_CreateFontsTexture() @@ -302,21 +267,16 @@ bool ImGui_ImplOpenGL3_CreateFontsTexture() io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. // Upload texture to graphics system - GLint last_texture; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - glGenTextures(1, &g_FontTexture); - glBindTexture(GL_TEXTURE_2D, g_FontTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + g_FontTexture = glcache.GenTexture(); + glcache.BindTexture(GL_TEXTURE_2D, g_FontTexture); + glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); // Store our identifier io.Fonts->TexID = (ImTextureID)(intptr_t)g_FontTexture; - // Restore state - glBindTexture(GL_TEXTURE_2D, last_texture); - return true; } @@ -325,7 +285,7 @@ void ImGui_ImplOpenGL3_DestroyFontsTexture() if (g_FontTexture) { ImGuiIO& io = ImGui::GetIO(); - glDeleteTextures(1, &g_FontTexture); + glcache.DeleteTextures(1, &g_FontTexture); io.Fonts->TexID = 0; g_FontTexture = 0; } @@ -367,14 +327,8 @@ static bool CheckProgram(GLuint handle, const char* desc) return (GLboolean)status == GL_TRUE; } -bool ImGui_ImplOpenGL3_CreateDeviceObjects() +bool ImGui_ImplOpenGL3_CreateDeviceObjects() { - // Backup GL state - GLint last_texture, last_array_buffer, last_vertex_array; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); - glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); - // Parse GLSL version string int glsl_version = 130; sscanf(g_GlslVersionString, "#version %d", &glsl_version); @@ -534,12 +488,6 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects() ImGui_ImplOpenGL3_CreateFontsTexture(); - // Restore modified GL state - glBindTexture(GL_TEXTURE_2D, last_texture); - glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); - if (gl.gl_major >= 3) - glBindVertexArray(last_vertex_array); - return true; } @@ -549,26 +497,23 @@ void ImGui_ImplOpenGL3_DestroyDeviceObjects() if (g_ElementsHandle) glDeleteBuffers(1, &g_ElementsHandle); g_VboHandle = g_ElementsHandle = 0; - if (g_ShaderHandle && g_VertHandle) glDetachShader(g_ShaderHandle, g_VertHandle); - if (g_VertHandle) glDeleteShader(g_VertHandle); + glcache.DeleteProgram(g_ShaderHandle); g_VertHandle = 0; - - if (g_ShaderHandle && g_FragHandle) glDetachShader(g_ShaderHandle, g_FragHandle); - if (g_FragHandle) glDeleteShader(g_FragHandle); g_FragHandle = 0; - - if (g_ShaderHandle) glDeleteProgram(g_ShaderHandle); g_ShaderHandle = 0; ImGui_ImplOpenGL3_DestroyFontsTexture(); if (g_BackgroundTexture != 0) - glDeleteTextures(1, &g_BackgroundTexture); + glcache.DeleteTextures(1, &g_BackgroundTexture); g_BackgroundTexture = 0; } void ImGui_ImplOpenGL3_DrawBackground() { + glcache.Disable(GL_SCISSOR_TEST); + glcache.ClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); if (g_BackgroundTexture != 0) { ImGuiIO& io = ImGui::GetIO(); @@ -579,9 +524,20 @@ void ImGui_ImplOpenGL3_DrawBackground() ImGui::GetWindowDrawList()->AddImage((ImTextureID)(uintptr_t)g_BackgroundTexture, ImVec2(0, 0), io.DisplaySize, ImVec2(0, 1), ImVec2(1, 0), 0xffffffff); ImGui::End(); } - else - { - glClearColor(0, 0, 0, 0); - glClear(GL_COLOR_BUFFER_BIT); - } +} + +ImTextureID ImGui_ImplOpenGL3_CreateVmuTexture(const unsigned int *data) +{ + GLuint tex_id = glcache.GenTexture(); + glcache.BindTexture(GL_TEXTURE_2D, tex_id); + glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 48, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + + return reinterpret_cast(tex_id); +} + +void ImGui_ImplOpenGL3_DeleteVmuTexture(ImTextureID tex_id) +{ + glcache.DeleteTextures(1, &(GLuint &)tex_id); } diff --git a/core/rend/gles/imgui_impl_opengl3.h b/core/rend/gles/imgui_impl_opengl3.h index f736563bb..a5146a6e2 100644 --- a/core/rend/gles/imgui_impl_opengl3.h +++ b/core/rend/gles/imgui_impl_opengl3.h @@ -34,6 +34,8 @@ IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown(); IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame(); IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data, bool save_background = false); IMGUI_IMPL_API void ImGui_ImplOpenGL3_DrawBackground(); +IMGUI_IMPL_API ImTextureID ImGui_ImplOpenGL3_CreateVmuTexture(const unsigned int *); +IMGUI_IMPL_API void ImGui_ImplOpenGL3_DeleteVmuTexture(ImTextureID); // Called by Init/NewFrame/Shutdown IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture(); diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index 831b2e2b7..69327efcf 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -18,13 +18,18 @@ */ #include #include +#ifdef _MSC_VER +#include "dirent/dirent.h" +#define S_ISDIR(mode) (((mode) & _S_IFMT) == _S_IFDIR) +#else #include +#endif #include #include "gui.h" #include "oslib/oslib.h" #include "cfg/cfg.h" -#include "hw/maple/maple_cfg.h" +#include "hw/maple/maple_if.h" #include "imgui/imgui.h" #include "gles/imgui_impl_opengl3.h" #include "imgui/roboto_medium.h" @@ -34,7 +39,10 @@ #include "linux-dist/main.h" // FIXME for kcode[] #include "gui_util.h" #include "gui_android.h" -#include "version/version.h" + +#include "version.h" +#include "oslib/audiostream.h" + extern void dc_loadstate(); extern void dc_savestate(); @@ -64,6 +72,10 @@ GuiState gui_state = Main; static bool settings_opening; static bool touch_up; +static void display_vmus(); +static void reset_vmus(); +static void term_vmus(); + void gui_init() { if (inited) @@ -290,6 +302,10 @@ static void gui_display_commands() if (!settings_opening) ImGui_ImplOpenGL3_DrawBackground(); + if (!settings.rend.FloatVMUs) + // If floating VMUs, they are already visible on the background + display_vmus(); + ImGui::SetNextWindowPos(ImVec2(screen_width / 2.f, screen_height / 2.f), ImGuiCond_Always, ImVec2(0.5f, 0.5f)); ImGui::SetNextWindowSize(ImVec2(330 * scaling, 0)); @@ -611,6 +627,8 @@ void directory_selected_callback(bool cancelled, std::string selection) static void gui_display_settings() { + static bool maple_devices_changed; + ImGui_Impl_NewFrame(); ImGui::NewFrame(); @@ -633,11 +651,14 @@ static void gui_display_settings() gui_state = Commands; else gui_state = Main; + if (maple_devices_changed) + { + maple_devices_changed = false; #if DC_PLATFORM == DC_PLATFORM_DREAMCAST - // TODO only if changed? sleep time on emu thread? - mcfg_DestroyDevices(); - mcfg_CreateDevices(); + maple_ReconnectDevices(); + reset_vmus(); #endif + } SaveSettings(); } if (game_started) @@ -791,13 +812,14 @@ static void gui_display_settings() if (ImGui::BeginTabItem("Controls")) { ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, normal_padding); -#if DC_PLATFORM == DC_PLATFORM_DREAMCAST +#if DC_PLATFORM == DC_PLATFORM_DREAMCAST || DC_PLATFORM == DC_PLATFORM_ATOMISWAVE if (ImGui::CollapsingHeader("Dreamcast Devices", ImGuiTreeNodeFlags_DefaultOpen)) { for (int bus = 0; bus < MAPLE_PORTS; bus++) { ImGui::Text("Device %c", bus + 'A'); ImGui::SameLine(); +#if DC_PLATFORM == DC_PLATFORM_DREAMCAST char device_name[32]; sprintf(device_name, "##device%d", bus); float w = ImGui::CalcItemWidth() / 3; @@ -808,7 +830,10 @@ static void gui_display_settings() { bool is_selected = settings.input.maple_devices[bus] == maple_device_type_from_index(i); if (ImGui::Selectable(maple_device_types[i], &is_selected)) + { settings.input.maple_devices[bus] = maple_device_type_from_index(i); + maple_devices_changed = true; + } if (is_selected) ImGui::SetItemDefaultFocus(); } @@ -826,7 +851,10 @@ static void gui_display_settings() { bool is_selected = settings.input.maple_expansion_devices[bus][port] == maple_expansion_device_type_from_index(i); if (ImGui::Selectable(maple_expansion_device_types[i], &is_selected)) + { settings.input.maple_expansion_devices[bus][port] = maple_expansion_device_type_from_index(i); + maple_devices_changed = true; + } if (is_selected) ImGui::SetItemDefaultFocus(); } @@ -835,6 +863,10 @@ static void gui_display_settings() ImGui::PopID(); } ImGui::PopItemWidth(); +#elif DC_PLATFORM == DC_PLATFORM_ATOMISWAVE + if (MapleDevices[bus][5] != NULL) + ImGui::Text("%s", maple_device_name(MapleDevices[bus][5]->get_device_type())); +#endif } ImGui::Spacing(); } @@ -910,20 +942,45 @@ static void gui_display_settings() if (ImGui::BeginTabItem("Video")) { ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, normal_padding); -#if !defined(GLES) && HOST_OS != OS_DARWIN - if (!gl.is_gles && gl.gl_major >= 4 && ImGui::CollapsingHeader("Transparent Sorting", ImGuiTreeNodeFlags_DefaultOpen)) + int renderer = settings.pvr.rend == 3 ? 2 : settings.rend.PerStripSorting ? 1 : 0; +#if HOST_OS != OS_DARWIN + bool has_per_pixel = !gl.is_gles && gl.gl_major >= 4; +#else + bool has_per_pixel = false; +#endif + if (ImGui::CollapsingHeader("Transparent Sorting", ImGuiTreeNodeFlags_DefaultOpen)) { - ImGui::Columns(2, "renderers", false); - ImGui::RadioButton("Per Triangle", (int *)&settings.pvr.rend, 0); + ImGui::Columns(has_per_pixel ? 3 : 2, "renderers", false); + ImGui::RadioButton("Per Triangle", &renderer, 0); ImGui::SameLine(); ShowHelpMarker("Sort transparent polygons per triangle. Fast but may produce graphical glitches"); - ImGui::NextColumn(); - ImGui::RadioButton("Per Pixel", (int *)&settings.pvr.rend, 3); + ImGui::NextColumn(); + ImGui::RadioButton("Per Strip", &renderer, 1); ImGui::SameLine(); - ShowHelpMarker("Sort transparent polygons per pixel. Slower but accurate"); + ShowHelpMarker("Sort transparent polygons per strip. Faster but may produce graphical glitches"); + if (has_per_pixel) + { + ImGui::NextColumn(); + ImGui::RadioButton("Per Pixel", &renderer, 2); + ImGui::SameLine(); + ShowHelpMarker("Sort transparent polygons per pixel. Slower but accurate"); + } ImGui::Columns(1, NULL, false); + switch (renderer) + { + case 0: + settings.pvr.rend = 0; + settings.rend.PerStripSorting = false; + break; + case 1: + settings.pvr.rend = 0; + settings.rend.PerStripSorting = true; + break; + case 2: + settings.pvr.rend = 3; + break; + } } -#endif if (ImGui::CollapsingHeader("Rendering Options", ImGuiTreeNodeFlags_DefaultOpen)) { ImGui::Checkbox("Synchronous Rendering", &settings.pvr.SynchronousRender); @@ -935,15 +992,27 @@ static void gui_display_settings() ImGui::Checkbox("Shadows", &settings.rend.ModifierVolumes); ImGui::SameLine(); ShowHelpMarker("Enable modifier volumes, usually used for shadows"); + ImGui::Checkbox("Fog", &settings.rend.Fog); + ImGui::SameLine(); + ShowHelpMarker("Enable fog effects"); ImGui::Checkbox("Widescreen", &settings.rend.WideScreen); ImGui::SameLine(); ShowHelpMarker("Draw geometry outside of the normal 4:3 aspect ratio. May produce graphical glitches in the revealed areas"); ImGui::Checkbox("Show FPS Counter", &settings.rend.ShowFPS); ImGui::SameLine(); ShowHelpMarker("Show on-screen frame/sec counter"); + ImGui::Checkbox("Show VMU in game", &settings.rend.FloatVMUs); + ImGui::SameLine(); + ShowHelpMarker("Show the VMU LCD screens while in game"); + ImGui::Checkbox("Rotate screen 90°", &settings.rend.Rotate90); + ImGui::SameLine(); + ShowHelpMarker("Rotate the screen 90° counterclockwise"); ImGui::SliderInt("Scaling", (int *)&settings.rend.ScreenScaling, 1, 100); ImGui::SameLine(); ShowHelpMarker("Downscaling factor relative to native screen resolution. Higher is better"); + ImGui::SliderInt("Horizontal Stretching", (int *)&settings.rend.ScreenStretching, 100, 150); + ImGui::SameLine(); + ShowHelpMarker("Stretch the screen horizontally"); ImGui::SliderInt("Frame Skipping", (int *)&settings.pvr.ta_skip, 0, 6); ImGui::SameLine(); ShowHelpMarker("Number of frames to skip between two actually rendered frames"); @@ -984,9 +1053,131 @@ static void gui_display_settings() ImGui::Checkbox("Enable DSP", &settings.aica.DSPEnabled); ImGui::SameLine(); ShowHelpMarker("Enable the Dreamcast Digital Sound Processor. Only recommended on fast and arm64 platforms"); - ImGui::Checkbox("Limit FPS", &settings.aica.LimitFPS); - ImGui::SameLine(); - ShowHelpMarker("Use the sound output to limit the speed of the emulator. Recommended in most cases"); + const char *preview = settings.aica.LimitFPS == LimitFPSDisabled ? "Disabled" : settings.aica.LimitFPS == LimitFPSAuto ? "Automatic" : "Enabled"; + if (ImGui::BeginCombo("Limit Emulator Speed", preview, ImGuiComboFlags_None)) + { + bool is_selected = settings.aica.LimitFPS == LimitFPSDisabled; + if (ImGui::Selectable("Disabled", &is_selected)) + settings.aica.LimitFPS = LimitFPSDisabled; + if (is_selected) + ImGui::SetItemDefaultFocus(); + is_selected = settings.aica.LimitFPS == LimitFPSAuto; + if (ImGui::Selectable("Automatic", &is_selected)) + settings.aica.LimitFPS = LimitFPSAuto; + if (is_selected) + ImGui::SetItemDefaultFocus(); + is_selected = settings.aica.LimitFPS == LimitFPSEnabled; + if (ImGui::Selectable("Enabled", &is_selected)) + settings.aica.LimitFPS = LimitFPSEnabled; + if (is_selected) + ImGui::SetItemDefaultFocus(); + ImGui::EndCombo(); + } + ImGui::SameLine(); + ShowHelpMarker("Whether to limit the emulator speed using the audio output. Enabled recommended"); + + audiobackend_t* backend = NULL;; + std::string backend_name = settings.audio.backend; + if (backend_name != "auto" && backend_name != "none") + { + backend = GetAudioBackend(settings.audio.backend); + if (backend != NULL) + backend_name = backend->slug; + } + + SortAudioBackends(); + + audiobackend_t* current_backend = backend; + if (ImGui::BeginCombo("Audio Driver", backend_name.c_str(), ImGuiComboFlags_None)) + { + bool is_selected = (settings.audio.backend == "auto"); + if (ImGui::Selectable("auto - Automatic driver selection", &is_selected)) + settings.audio.backend = "auto"; + + is_selected = (settings.audio.backend == "none"); + if (ImGui::Selectable("none - No audio driver", &is_selected)) + settings.audio.backend = "none"; + + for (int i = 0; i < GetAudioBackendCount(); i++) + { + backend = GetAudioBackend(i); + is_selected = (settings.audio.backend == backend->slug); + + if (is_selected) + current_backend = backend; + + if (ImGui::Selectable((backend->slug + " - " + backend->name).c_str(), &is_selected)) + settings.audio.backend = backend->slug; + if (is_selected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } + ImGui::SameLine(); + ShowHelpMarker("The audio driver to use"); + + if (current_backend != NULL && current_backend->get_options != NULL) + { + // get backend specific options + int option_count; + audio_option_t* options = current_backend->get_options(&option_count); + + // initialize options if not already done + std::map* cfg_entries = &settings.audio.options[current_backend->slug]; + bool populate_entries = (cfg_entries->size() == 0); + + for (int o = 0; o < option_count; o++) + { + std::string value; + if (populate_entries) + { + value = cfgLoadStr(current_backend->slug.c_str(), options->cfg_name.c_str(), ""); + (*cfg_entries)[options->cfg_name] = value; + } + value = (*cfg_entries)[options->cfg_name]; + + if (options->type == integer) + { + int val = stoi(value); + ImGui::SliderInt(options->caption.c_str(), &val, options->min_value, options->max_value); + (*cfg_entries)[options->cfg_name] = to_string(val); + } + else if (options->type == checkbox) + { + bool check = (value == "1"); + ImGui::Checkbox(options->caption.c_str(), &check); + std::string cur = check ? "1" : "0"; + (*cfg_entries)[options->cfg_name] = cur; + } + else if (options->type == ::list) + { + if (ImGui::BeginCombo(options->caption.c_str(), value.c_str(), ImGuiComboFlags_None)) + { + bool is_selected = false; + std::vector list_items = options->list_callback(); + for (std::vector::iterator it = list_items.begin() ; it != list_items.end(); ++it) + { + std::string cur = (std::string)*it; + is_selected = (value == cur); + if (ImGui::Selectable(cur.c_str(), &is_selected)) + { + (*cfg_entries)[options->cfg_name] = cur; + } + + if (is_selected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } + } + else { + printf("Unknown option\n"); + } + + options++; + } + } + ImGui::PopStyleVar(); ImGui::EndTabItem(); } @@ -1012,10 +1203,33 @@ static void gui_display_settings() ShowHelpMarker("Do not optimize integer division. Recommended"); ImGui::Checkbox("Unstable Optimizations", &settings.dynarec.unstable_opt); ImGui::SameLine(); - ShowHelpMarker("Enable unsafe optimizations. May cause crash or environmental disaster"); + ShowHelpMarker("Enable unsafe optimizations. Will cause crash or environmental disaster"); ImGui::Checkbox("Idle Skip", &settings.dynarec.idleskip); ImGui::SameLine(); ShowHelpMarker("Skip wait loops. Recommended"); + ImGui::PushItemWidth(ImGui::CalcTextSize("Largeenough").x); + const char *preview = settings.dynarec.SmcCheckLevel == NoCheck ? "Faster" : settings.dynarec.SmcCheckLevel == FastCheck ? "Fast" : "Full"; + if (ImGui::BeginCombo("SMC Checks", preview , ImGuiComboFlags_None)) + { + bool is_selected = settings.dynarec.SmcCheckLevel == NoCheck; + if (ImGui::Selectable("Faster", &is_selected)) + settings.dynarec.SmcCheckLevel = NoCheck; + if (is_selected) + ImGui::SetItemDefaultFocus(); + is_selected = settings.dynarec.SmcCheckLevel == FastCheck; + if (ImGui::Selectable("Fast", &is_selected)) + settings.dynarec.SmcCheckLevel = FastCheck; + if (is_selected) + ImGui::SetItemDefaultFocus(); + is_selected = settings.dynarec.SmcCheckLevel == FullCheck; + if (ImGui::Selectable("Full", &is_selected)) + settings.dynarec.SmcCheckLevel = FullCheck; + if (is_selected) + ImGui::SetItemDefaultFocus(); + ImGui::EndCombo(); + } + ImGui::SameLine(); + ShowHelpMarker("How to detect self-modifying code. Full check recommended"); } if (ImGui::CollapsingHeader("Other", ImGuiTreeNodeFlags_DefaultOpen)) { @@ -1036,9 +1250,9 @@ static void gui_display_settings() ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, normal_padding); if (ImGui::CollapsingHeader("Reicast", ImGuiTreeNodeFlags_DefaultOpen)) { - ImGui::Text("Version: %s", version); - ImGui::Text("Git Hash: %s", git_hash); - ImGui::Text("Build Date: %s", build_date); + ImGui::Text("Version: %s", REICAST_VERSION); + ImGui::Text("Git Hash: %s", GIT_HASH); + ImGui::Text("Build Date: %s", BUILD_DATE); ImGui::Text("Target: %s", #if DC_PLATFORM == DC_PLATFORM_DREAMCAST "Dreamcast" @@ -1220,6 +1434,7 @@ static void gui_start_game(const std::string& path) { gui_state = Main; game_started = false; + cfgSetVirtual("config", "image", ""); switch (rc) { case -3: error_msg = "Audio/video initialization failed"; @@ -1270,6 +1485,17 @@ static void gui_display_content() { ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8 * scaling, 20 * scaling)); // from 8, 4 +#if DC_PLATFORM == DC_PLATFORM_DREAMCAST + ImGui::PushID("bios"); + if (ImGui::Selectable("Dreamcast BIOS")) + { + gui_state = ClosedNoResume; + cfgSetVirtual("config", "image", ""); + gui_start_game(""); + } + ImGui::PopID(); +#endif + for (auto game : game_list) if (filter.PassFilter(game.name.c_str())) { @@ -1366,24 +1592,6 @@ void gui_display_ui() gui_state = Closed; } -void gui_display_fps(const char *string) -{ - ImGui_Impl_NewFrame(); - ImGui::NewFrame(); - - ImGui::SetNextWindowBgAlpha(0); - ImGui::SetNextWindowPos(ImVec2(0, screen_height), ImGuiCond_Always, ImVec2(0.f, 1.f)); // Lower left corner - - ImGui::Begin("##fps", NULL, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoNav - | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoBackground); - ImGui::SetWindowFontScale(2); - ImGui::TextColored(ImVec4(1, 1, 0, 0.7), "%s", string); - ImGui::End(); - - ImGui::Render(); - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); -} - static float LastFPSTime; static int lastFrameCount = 0; static float fps = -1; @@ -1431,26 +1639,32 @@ void gui_display_osd() if (osd_message.empty()) { message = getFPSNotification(); - if (message.empty()) - return; } else message = osd_message; - ImGui_Impl_NewFrame(); - ImGui::NewFrame(); + if (!message.empty() || settings.rend.FloatVMUs) + { + ImGui_Impl_NewFrame(); + ImGui::NewFrame(); - ImGui::SetNextWindowBgAlpha(0); - ImGui::SetNextWindowPos(ImVec2(0, screen_height), ImGuiCond_Always, ImVec2(0.f, 1.f)); // Lower left corner + if (!message.empty()) + { + ImGui::SetNextWindowBgAlpha(0); + ImGui::SetNextWindowPos(ImVec2(0, screen_height), ImGuiCond_Always, ImVec2(0.f, 1.f)); // Lower left corner - ImGui::Begin("##osd", NULL, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoNav - | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoBackground); - ImGui::SetWindowFontScale(1.5); - ImGui::TextColored(ImVec4(1, 1, 0, 0.7), "%s", message.c_str()); - ImGui::End(); + ImGui::Begin("##osd", NULL, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoNav + | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoBackground); + ImGui::SetWindowFontScale(1.5); + ImGui::TextColored(ImVec4(1, 1, 0, 0.7), "%s", message.c_str()); + ImGui::End(); + } + if (settings.rend.FloatVMUs) + display_vmus(); - ImGui::Render(); - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + ImGui::Render(); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + } } void gui_open_onboarding() @@ -1461,6 +1675,7 @@ void gui_open_onboarding() void gui_term() { inited = false; + term_vmus(); ImGui_ImplOpenGL3_Shutdown(); ImGui::DestroyContext(); } @@ -1472,10 +1687,108 @@ int msgboxf(const wchar* text, unsigned int type, ...) { va_start(args, type); vsnprintf(temp, sizeof(temp), text, args); va_end(args); - printf("%s", temp); + printf("%s\n", temp); gui_display_notification(temp, 2000); return 1; } +extern bool subfolders_read; + +void gui_refresh_files() +{ + game_list_done = false; + subfolders_read = false; +} + +#define VMU_WIDTH (70 * 48 * scaling / 32) +#define VMU_HEIGHT (70 * scaling) +#define VMU_PADDING (8 * scaling) +static u32 vmu_lcd_data[8][48 * 32]; +static bool vmu_lcd_status[8]; +static ImTextureID vmu_lcd_tex_ids[8]; + +void push_vmu_screen(int bus_id, int bus_port, u8* buffer) +{ + int vmu_id = bus_id * 2 + bus_port; + if (vmu_id < 0 || vmu_id >= ARRAY_SIZE(vmu_lcd_data)) + return; + u32 *p = &vmu_lcd_data[vmu_id][0]; + for (int i = 0; i < ARRAY_SIZE(vmu_lcd_data[vmu_id]); i++, buffer++) + *p++ = *buffer != 0 ? 0xFFFFFFFFu : 0xFF000000u; + vmu_lcd_status[vmu_id] = true; +} + +static const int vmu_coords[8][2] = { + { 0 , 0 }, + { 0 , 0 }, + { 1 , 0 }, + { 1 , 0 }, + { 0 , 1 }, + { 0 , 1 }, + { 1 , 1 }, + { 1 , 1 }, +}; + +static void display_vmus() +{ + if (!game_started) + return; + ImGui::SetNextWindowBgAlpha(0); + ImGui::SetNextWindowPos(ImVec2(0, 0)); + ImGui::SetNextWindowSize(ImVec2(screen_width, screen_height)); + + ImGui::Begin("vmu-window", NULL, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoInputs + | ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoFocusOnAppearing); + for (int i = 0; i < 8; i++) + { + if (!vmu_lcd_status[i]) + continue; + + if (vmu_lcd_tex_ids[i] != (ImTextureID)0) + ImGui_ImplOpenGL3_DeleteVmuTexture(vmu_lcd_tex_ids[i]); + vmu_lcd_tex_ids[i] = ImGui_ImplOpenGL3_CreateVmuTexture(vmu_lcd_data[i]); + + int x = vmu_coords[i][0]; + int y = vmu_coords[i][1]; + ImVec2 pos; + if (x == 0) + pos.x = VMU_PADDING; + else + pos.x = screen_width - VMU_WIDTH - VMU_PADDING; + if (y == 0) + { + pos.y = VMU_PADDING; + if (i & 1) + pos.y += VMU_HEIGHT + VMU_PADDING; + } + else + { + pos.y = screen_height - VMU_HEIGHT - VMU_PADDING; + if (i & 1) + pos.y -= VMU_HEIGHT + VMU_PADDING; + } + ImVec2 pos_b(pos.x + VMU_WIDTH, pos.y + VMU_HEIGHT); + ImGui::GetWindowDrawList()->AddImage(vmu_lcd_tex_ids[i], pos, pos_b, ImVec2(0, 1), ImVec2(1, 0), 0xC0ffffff); + } + ImGui::End(); +} + +static void reset_vmus() +{ + for (int i = 0; i < ARRAY_SIZE(vmu_lcd_status); i++) + vmu_lcd_status[i] = false; +} + +static void term_vmus() +{ + for (int i = 0; i < ARRAY_SIZE(vmu_lcd_status); i++) + { + if (vmu_lcd_tex_ids[i] != (ImTextureID)0) + { + ImGui_ImplOpenGL3_DeleteVmuTexture(vmu_lcd_tex_ids[i]); + vmu_lcd_tex_ids[i] = (ImTextureID)0; + } + } +} diff --git a/core/rend/gui.h b/core/rend/gui.h index 3c463a9e6..bfa0c063a 100644 --- a/core/rend/gui.h +++ b/core/rend/gui.h @@ -25,6 +25,7 @@ void gui_display_notification(const char *msg, int duration); void gui_display_osd(); void gui_open_onboarding(); void gui_term(); +void gui_refresh_files(); extern int screen_dpi; diff --git a/core/rend/gui_util.cpp b/core/rend/gui_util.cpp index 96d37d63b..5fb9e9d02 100644 --- a/core/rend/gui_util.cpp +++ b/core/rend/gui_util.cpp @@ -21,8 +21,16 @@ #include #include #include +#ifdef _MSC_VER +#include +#include "dirent/dirent.h" +#define S_ISDIR(mode) (((mode) & _S_IFMT) == _S_IFDIR) +#define access _access +#define R_OK 4 +#else #include #include +#endif #include #include "types.h" @@ -33,7 +41,7 @@ extern int screen_width, screen_height; static std::string select_current_directory; static std::vector select_subfolders; -static bool subfolders_read; +bool subfolders_read; #ifdef _WIN32 static const std::string separators = "/\\"; static const std::string native_separator = "\\"; @@ -183,7 +191,7 @@ void select_directory_popup(const char *prompt, float scaling, StringCallback ca } ImGui::Text("%s", error_message.empty() ? select_current_directory.c_str() : error_message.c_str()); - ImGui::BeginChild(ImGui::GetID("dir_list"), ImVec2(0, -ImGui::CalcTextSize("Cancel").y - ImGui::GetStyle().FramePadding. y * 2 - ImGui::GetStyle().ItemSpacing.y), true); + ImGui::BeginChild(ImGui::GetID("dir_list"), ImVec2(0, - 30 * scaling - ImGui::GetStyle().ItemSpacing.y), true); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8 * scaling, 20 * scaling)); // from 8, 4 @@ -245,14 +253,14 @@ void select_directory_popup(const char *prompt, float scaling, StringCallback ca } ImGui::PopStyleVar(); ImGui::EndChild(); - if (ImGui::Button("Select Current Directory")) + if (ImGui::Button("Select Current Directory", ImVec2(0, 30 * scaling))) { subfolders_read = false; callback(false, select_current_directory); ImGui::CloseCurrentPopup(); } ImGui::SameLine(); - if (ImGui::Button("Cancel")) + if (ImGui::Button("Cancel", ImVec2(0, 30 * scaling))) { subfolders_read = false; callback(true, ""); diff --git a/core/sdl/sdl.cpp b/core/sdl/sdl.cpp index 100a888eb..6dc0eea52 100644 --- a/core/sdl/sdl.cpp +++ b/core/sdl/sdl.cpp @@ -72,7 +72,7 @@ void input_sdl_init() { if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0) { - die("error initializing SDL Joystick subsystem"); + die("SDL: error initializing Joystick subsystem"); } } if (SDL_WasInit(SDL_INIT_HAPTIC) == 0) diff --git a/core/sdl/sdl_gamepad.h b/core/sdl/sdl_gamepad.h index 4d970a700..edf23ae9d 100644 --- a/core/sdl/sdl_gamepad.h +++ b/core/sdl/sdl_gamepad.h @@ -50,6 +50,13 @@ public: _name = SDL_JoystickName(sdl_joystick); sdl_joystick_instance = SDL_JoystickInstanceID(sdl_joystick); printf("SDL: Opened joystick on port %d: '%s' ", maple_port, _name.c_str()); + SDL_JoystickGUID guid = SDL_JoystickGetGUID(sdl_joystick); + char buf[33]; + SDL_JoystickGetGUIDString(guid, buf, sizeof(buf)); + _unique_id = buf; + if (_unique_id.empty()) + _unique_id = _name; + if (!find_mapping()) { if (_name == "Microsoft X-Box 360 pad") @@ -175,6 +182,7 @@ public: SDLKbGamepadDevice(int maple_port) : GamepadDevice(maple_port, "SDL") { _name = "Keyboard"; + _unique_id = "sdl_keyboard"; if (!find_mapping()) input_mapper = new KbInputMapping(); } @@ -201,6 +209,7 @@ public: SDLMouseGamepadDevice(int maple_port) : GamepadDevice(maple_port, "SDL") { _name = "Mouse"; + _unique_id = "sdl_mouse"; if (!find_mapping()) input_mapper = new MouseInputMapping(); } diff --git a/core/serialize.cpp b/core/serialize.cpp index 67a099f83..5ec48ad62 100644 --- a/core/serialize.cpp +++ b/core/serialize.cpp @@ -55,9 +55,6 @@ extern bool armFiqEnable; extern int armMode; extern bool Arm7Enabled; extern u8 cpuBitsSet[256]; -extern bool intState ; -extern bool stopState ; -extern bool holdState ; /* if AREC dynarec enabled: vector ops; @@ -102,7 +99,7 @@ extern AicaTimer timers[3]; //./core/hw/aica/aica_if.o -extern VArray2 aica_ram; +extern VLockedMemory aica_ram; extern u32 VREG;//video reg =P extern u32 ARMRST;//arm reset reg extern u32 rtc_EN; @@ -258,15 +255,6 @@ extern u32 FrameCount; //extern cThread rthd; extern bool pend_rend; -//these will all get cleared out after a few frames - no need to serialize -//static bool render_called = false; -//u32 fb1_watch_addr_start; -//u32 fb1_watch_addr_end; -//u32 fb2_watch_addr_start; -//u32 fb2_watch_addr_end; -//bool fb_dirty; - - //maybe //extern u32 memops_t,memops_l; @@ -382,7 +370,7 @@ extern DECL_ALIGN(4) u32 SFaceOffsColor; //extern vector VramLocks[/*VRAM_SIZE*/(16*1024*1024)/PAGE_SIZE]; //maybe - probably not - just a locking mechanism //extern cMutex vramlist_lock; -extern VArray2 vram; +extern VLockedMemory vram; @@ -404,7 +392,7 @@ extern Array SCIF; //SCIF : 10 registers //./core/hw/sh4/sh4_mem.o -extern VArray2 mem_b; +extern VLockedMemory mem_b; //one-time init //extern _vmem_handler area1_32b; //one-time init @@ -445,7 +433,7 @@ extern fpscr_t saved_fpscr; //./core/hw/sh4/sh4_sched.o extern u64 sh4_sched_ffb; extern u32 sh4_sched_intr; -extern vector list; +extern vector sch_list; //extern int sh4_sched_next_id; @@ -620,7 +608,6 @@ extern u8 q_subchannel[96]; //./core/hw/naomi/naomi.o extern u32 naomi_updates; -extern u32 BoardID; extern u32 GSerialBuffer; extern u32 BSerialBuffer; extern int GBufPos; @@ -726,18 +713,6 @@ extern u32 fallback_blocks; extern u32 total_blocks; extern u32 REMOVED_OPS; - - - -//./core/linux-dist/main.cpp, ./core/windows/winmain.cpp , ... -extern u16 kcode[4]; -extern u8 rt[4]; -extern u8 lt[4]; -extern u32 vks[4]; -extern s8 joyx[4]; -extern s8 joyy[4]; - - bool rc_serialize(void *src, unsigned int src_size, void **dest, unsigned int *total_size) { if ( *dest != NULL ) @@ -816,9 +791,10 @@ bool dc_serialize(void **data, unsigned int *total_size) REICAST_S(armMode); REICAST_S(Arm7Enabled); REICAST_SA(cpuBitsSet,256); - REICAST_S(intState); - REICAST_S(stopState); - REICAST_S(holdState); + bool dummy; + REICAST_S(dummy); + REICAST_S(dummy); + REICAST_S(dummy); REICAST_S(dsp); @@ -979,8 +955,6 @@ bool dc_serialize(void **data, unsigned int *total_size) REICAST_S(decoded_srimask); - - //default to nommu_full i = 3 ; if ( do_sqw_nommu == &do_sqw_nommu_area_3) @@ -1013,61 +987,63 @@ bool dc_serialize(void **data, unsigned int *total_size) //extern vector list; - REICAST_S(list[aica_schid].tag) ; - REICAST_S(list[aica_schid].start) ; - REICAST_S(list[aica_schid].end) ; - REICAST_S(list[rtc_schid].tag) ; - REICAST_S(list[rtc_schid].start) ; - REICAST_S(list[rtc_schid].end) ; + REICAST_S(sch_list[aica_schid].tag) ; + REICAST_S(sch_list[aica_schid].start) ; + REICAST_S(sch_list[aica_schid].end) ; - REICAST_S(list[gdrom_schid].tag) ; - REICAST_S(list[gdrom_schid].start) ; - REICAST_S(list[gdrom_schid].end) ; + REICAST_S(sch_list[rtc_schid].tag) ; + REICAST_S(sch_list[rtc_schid].start) ; + REICAST_S(sch_list[rtc_schid].end) ; - REICAST_S(list[maple_schid].tag) ; - REICAST_S(list[maple_schid].start) ; - REICAST_S(list[maple_schid].end) ; + REICAST_S(sch_list[gdrom_schid].tag) ; + REICAST_S(sch_list[gdrom_schid].start) ; + REICAST_S(sch_list[gdrom_schid].end) ; - REICAST_S(list[dma_sched_id].tag) ; - REICAST_S(list[dma_sched_id].start) ; - REICAST_S(list[dma_sched_id].end) ; + REICAST_S(sch_list[maple_schid].tag) ; + REICAST_S(sch_list[maple_schid].start) ; + REICAST_S(sch_list[maple_schid].end) ; + + REICAST_S(sch_list[dma_sched_id].tag) ; + REICAST_S(sch_list[dma_sched_id].start) ; + REICAST_S(sch_list[dma_sched_id].end) ; for (int i = 0; i < 3; i++) { - REICAST_S(list[tmu_sched[i]].tag) ; - REICAST_S(list[tmu_sched[i]].start) ; - REICAST_S(list[tmu_sched[i]].end) ; + REICAST_S(sch_list[tmu_sched[i]].tag) ; + REICAST_S(sch_list[tmu_sched[i]].start) ; + REICAST_S(sch_list[tmu_sched[i]].end) ; } - REICAST_S(list[render_end_schid].tag) ; - REICAST_S(list[render_end_schid].start) ; - REICAST_S(list[render_end_schid].end) ; + REICAST_S(sch_list[render_end_schid].tag) ; + REICAST_S(sch_list[render_end_schid].start) ; + REICAST_S(sch_list[render_end_schid].end) ; - REICAST_S(list[vblank_schid].tag) ; - REICAST_S(list[vblank_schid].start) ; - REICAST_S(list[vblank_schid].end) ; - - REICAST_S(list[time_sync].tag) ; - REICAST_S(list[time_sync].start) ; - REICAST_S(list[time_sync].end) ; - - REICAST_S(list[modem_sched].tag) ; - REICAST_S(list[modem_sched].start) ; - REICAST_S(list[modem_sched].end) ; + REICAST_S(sch_list[vblank_schid].tag) ; + REICAST_S(sch_list[vblank_schid].start) ; + REICAST_S(sch_list[vblank_schid].end) ; + REICAST_S(sch_list[time_sync].tag) ; + REICAST_S(sch_list[time_sync].start) ; + REICAST_S(sch_list[time_sync].end) ; + #ifdef ENABLE_MODEM + REICAST_S(sch_list[modem_sched].tag) ; + REICAST_S(sch_list[modem_sched].start) ; + REICAST_S(sch_list[modem_sched].end) ; + #else + int modem_dummy = 0; + REICAST_S(modem_dummy); + REICAST_S(modem_dummy); + REICAST_S(modem_dummy); + #endif REICAST_S(SCIF_SCFSR2); REICAST_S(SCIF_SCFRDR2); REICAST_S(SCIF_SCFDR2); - REICAST_S(BSC_PDTRA); - - - REICAST_SA(tmu_shift,3); REICAST_SA(tmu_mask,3); REICAST_SA(tmu_mask64,3); @@ -1075,14 +1051,8 @@ bool dc_serialize(void **data, unsigned int *total_size) REICAST_SA(tmu_ch_base,3); REICAST_SA(tmu_ch_base64,3); - - - REICAST_SA(CCN_QACR_TR,2); - - - REICAST_SA(UTLB,64); REICAST_SA(ITLB,4); #if defined(NO_MMU) @@ -1091,13 +1061,11 @@ bool dc_serialize(void **data, unsigned int *total_size) REICAST_SA(ITLB_LRU_USE,64); #endif - - REICAST_S(NullDriveDiscType); REICAST_SA(q_subchannel,96); REICAST_S(naomi_updates); - REICAST_S(BoardID); + REICAST_S(i); //BoardID REICAST_S(GSerialBuffer); REICAST_S(BSerialBuffer); REICAST_S(GBufPos); @@ -1134,28 +1102,26 @@ bool dc_serialize(void **data, unsigned int *total_size) REICAST_S(div_som_reg2); REICAST_S(div_som_reg3); - - REICAST_S(LastAddr); REICAST_S(LastAddr_min); REICAST_SA(block_hash,1024); - REICAST_SA(RegisterWrite,sh4_reg_count); REICAST_SA(RegisterRead,sh4_reg_count); REICAST_S(fallback_blocks); REICAST_S(total_blocks); REICAST_S(REMOVED_OPS); - - - - REICAST_SA(kcode,4); - REICAST_SA(rt,4); - REICAST_SA(lt,4); - REICAST_SA(vks,4); - REICAST_SA(joyx,4); - REICAST_SA(joyy,4); + REICAST_S(i); //REICAST_SA(kcode,4); + REICAST_S(i); + REICAST_S(i); //REICAST_SA(rt,4); + REICAST_S(i); //REICAST_SA(lt,4); + REICAST_S(i); //REICAST_SA(vks,4); + REICAST_S(i); + REICAST_S(i); + REICAST_S(i); + REICAST_S(i); //REICAST_SA(joyx,4); + REICAST_S(i); //REICAST_SA(joyy,4); REICAST_S(settings.dreamcast.broadcast); REICAST_S(settings.dreamcast.cable); @@ -1184,9 +1150,10 @@ static bool dc_unserialize_libretro(void **data, unsigned int *total_size) REICAST_US(armMode); REICAST_US(Arm7Enabled); REICAST_USA(cpuBitsSet,256); - REICAST_US(intState); - REICAST_US(stopState); - REICAST_US(holdState); + bool dummy; + REICAST_US(dummy); + REICAST_US(dummy); + REICAST_US(dummy); REICAST_US(dsp); @@ -1250,12 +1217,7 @@ static bool dc_unserialize_libretro(void **data, unsigned int *total_size) //this is one-time init, no updates - don't need to serialize //extern _vmem_handler area0_handler; - - - - REICAST_USA(reply_11,16) ; - - + REICAST_USA(reply_11,16); REICAST_US(sns_asc); REICAST_US(sns_ascq); @@ -1369,8 +1331,6 @@ static bool dc_unserialize_libretro(void **data, unsigned int *total_size) REICAST_USA(mem_b.data, mem_b.size); - - REICAST_US(IRLPriority); REICAST_USA(InterruptEnvId,32); REICAST_USA(InterruptBit,32); @@ -1379,9 +1339,6 @@ static bool dc_unserialize_libretro(void **data, unsigned int *total_size) REICAST_US(interrupt_vmask); REICAST_US(decoded_srimask); - - - REICAST_US(i) ; if ( i == 0 ) do_sqw_nommu = &do_sqw_nommu_area_3 ; @@ -1406,69 +1363,67 @@ static bool dc_unserialize_libretro(void **data, unsigned int *total_size) REICAST_US(old_rm); REICAST_US(old_dn); - - - REICAST_US(sh4_sched_ffb); REICAST_US(sh4_sched_intr); - //extern vector list; + //extern vector sch_list; - REICAST_US(list[aica_schid].tag) ; - REICAST_US(list[aica_schid].start) ; - REICAST_US(list[aica_schid].end) ; + REICAST_US(sch_list[aica_schid].tag) ; + REICAST_US(sch_list[aica_schid].start) ; + REICAST_US(sch_list[aica_schid].end) ; - REICAST_US(list[rtc_schid].tag) ; - REICAST_US(list[rtc_schid].start) ; - REICAST_US(list[rtc_schid].end) ; + REICAST_US(sch_list[rtc_schid].tag) ; + REICAST_US(sch_list[rtc_schid].start) ; + REICAST_US(sch_list[rtc_schid].end) ; - REICAST_US(list[gdrom_schid].tag) ; - REICAST_US(list[gdrom_schid].start) ; - REICAST_US(list[gdrom_schid].end) ; + REICAST_US(sch_list[gdrom_schid].tag) ; + REICAST_US(sch_list[gdrom_schid].start) ; + REICAST_US(sch_list[gdrom_schid].end) ; - REICAST_US(list[maple_schid].tag) ; - REICAST_US(list[maple_schid].start) ; - REICAST_US(list[maple_schid].end) ; + REICAST_US(sch_list[maple_schid].tag) ; + REICAST_US(sch_list[maple_schid].start) ; + REICAST_US(sch_list[maple_schid].end) ; - REICAST_US(list[dma_sched_id].tag) ; - REICAST_US(list[dma_sched_id].start) ; - REICAST_US(list[dma_sched_id].end) ; + REICAST_US(sch_list[dma_sched_id].tag) ; + REICAST_US(sch_list[dma_sched_id].start) ; + REICAST_US(sch_list[dma_sched_id].end) ; for (int i = 0; i < 3; i++) { - REICAST_US(list[tmu_sched[i]].tag) ; - REICAST_US(list[tmu_sched[i]].start) ; - REICAST_US(list[tmu_sched[i]].end) ; + REICAST_US(sch_list[tmu_sched[i]].tag) ; + REICAST_US(sch_list[tmu_sched[i]].start) ; + REICAST_US(sch_list[tmu_sched[i]].end) ; } - REICAST_US(list[render_end_schid].tag) ; - REICAST_US(list[render_end_schid].start) ; - REICAST_US(list[render_end_schid].end) ; + REICAST_US(sch_list[render_end_schid].tag) ; + REICAST_US(sch_list[render_end_schid].start) ; + REICAST_US(sch_list[render_end_schid].end) ; - REICAST_US(list[vblank_schid].tag) ; - REICAST_US(list[vblank_schid].start) ; - REICAST_US(list[vblank_schid].end) ; - - REICAST_US(list[time_sync].tag) ; - REICAST_US(list[time_sync].start) ; - REICAST_US(list[time_sync].end) ; - - REICAST_US(list[modem_sched].tag) ; - REICAST_US(list[modem_sched].start) ; - REICAST_US(list[modem_sched].end) ; + REICAST_US(sch_list[vblank_schid].tag) ; + REICAST_US(sch_list[vblank_schid].start) ; + REICAST_US(sch_list[vblank_schid].end) ; + REICAST_US(sch_list[time_sync].tag) ; + REICAST_US(sch_list[time_sync].start) ; + REICAST_US(sch_list[time_sync].end) ; + #ifdef ENABLE_MODEM + REICAST_US(sch_list[modem_sched].tag) ; + REICAST_US(sch_list[modem_sched].start) ; + REICAST_US(sch_list[modem_sched].end) ; + #else + int modem_dummy; + REICAST_US(modem_dummy); + REICAST_US(modem_dummy); + REICAST_US(modem_dummy); + #endif REICAST_US(SCIF_SCFSR2); REICAST_US(SCIF_SCFRDR2); REICAST_US(SCIF_SCFDR2); - REICAST_US(BSC_PDTRA); - - - REICAST_USA(tmu_shift,3); REICAST_USA(tmu_mask,3); REICAST_USA(tmu_mask64,3); @@ -1476,14 +1431,8 @@ static bool dc_unserialize_libretro(void **data, unsigned int *total_size) REICAST_USA(tmu_ch_base,3); REICAST_USA(tmu_ch_base64,3); - - - REICAST_USA(CCN_QACR_TR,2); - - - REICAST_USA(UTLB,64); REICAST_USA(ITLB,4); #if defined(NO_MMU) @@ -1492,9 +1441,6 @@ static bool dc_unserialize_libretro(void **data, unsigned int *total_size) REICAST_USA(ITLB_LRU_USE,64); #endif - - - REICAST_US(NullDriveDiscType); REICAST_USA(q_subchannel,96); @@ -1511,7 +1457,7 @@ static bool dc_unserialize_libretro(void **data, unsigned int *total_size) REICAST_US(naomi_updates); - REICAST_US(BoardID); + REICAST_US(i); // BoardID REICAST_US(GSerialBuffer); REICAST_US(BSerialBuffer); REICAST_US(GBufPos); @@ -1549,9 +1495,6 @@ static bool dc_unserialize_libretro(void **data, unsigned int *total_size) REICAST_US(div_som_reg2); REICAST_US(div_som_reg3); - - - //REICAST_USA(CodeCache,CODE_SIZE) ; //REICAST_USA(SH4_TCB,CODE_SIZE+4096); REICAST_US(LastAddr); @@ -1603,9 +1546,10 @@ bool dc_unserialize(void **data, unsigned int *total_size) REICAST_US(armMode); REICAST_US(Arm7Enabled); REICAST_USA(cpuBitsSet,256); - REICAST_US(intState); - REICAST_US(stopState); - REICAST_US(holdState); + bool dummy; + REICAST_US(dummy); + REICAST_US(dummy); + REICAST_US(dummy); REICAST_US(dsp); @@ -1615,7 +1559,6 @@ bool dc_unserialize(void **data, unsigned int *total_size) REICAST_US(timers[i].m_step); } - REICAST_USA(aica_ram.data,aica_ram.size) ; REICAST_US(VREG); REICAST_US(ARMRST); @@ -1623,8 +1566,6 @@ bool dc_unserialize(void **data, unsigned int *total_size) REICAST_USA(aica_reg,0x8000); - - REICAST_USA(volume_lut,16); REICAST_USA(tl_lut,256 + 768); REICAST_USA(AEG_ATT_SPS,64); @@ -1639,14 +1580,11 @@ bool dc_unserialize(void **data, unsigned int *total_size) REICAST_USA(mxlr,64); REICAST_US(samples_gen); - register_unserialize(sb_regs, data, total_size) ; REICAST_US(SB_ISTNRM); REICAST_US(SB_FFST_rc); REICAST_US(SB_FFST); - - //this is one-time init, no updates - don't need to serialize //extern RomChip sys_rom; REICAST_US(sys_nvmem.size); @@ -1758,8 +1696,6 @@ bool dc_unserialize(void **data, unsigned int *total_size) REICAST_USA(mem_b.data, mem_b.size); - - REICAST_US(IRLPriority); REICAST_USA(InterruptEnvId,32); REICAST_USA(InterruptBit,32); @@ -1768,9 +1704,6 @@ bool dc_unserialize(void **data, unsigned int *total_size) REICAST_US(interrupt_vmask); REICAST_US(decoded_srimask); - - - REICAST_US(i) ; if ( i == 0 ) do_sqw_nommu = &do_sqw_nommu_area_3 ; @@ -1806,50 +1739,55 @@ bool dc_unserialize(void **data, unsigned int *total_size) //extern vector list; - REICAST_US(list[aica_schid].tag) ; - REICAST_US(list[aica_schid].start) ; - REICAST_US(list[aica_schid].end) ; + REICAST_US(sch_list[aica_schid].tag) ; + REICAST_US(sch_list[aica_schid].start) ; + REICAST_US(sch_list[aica_schid].end) ; - REICAST_US(list[rtc_schid].tag) ; - REICAST_US(list[rtc_schid].start) ; - REICAST_US(list[rtc_schid].end) ; + REICAST_US(sch_list[rtc_schid].tag) ; + REICAST_US(sch_list[rtc_schid].start) ; + REICAST_US(sch_list[rtc_schid].end) ; - REICAST_US(list[gdrom_schid].tag) ; - REICAST_US(list[gdrom_schid].start) ; - REICAST_US(list[gdrom_schid].end) ; + REICAST_US(sch_list[gdrom_schid].tag) ; + REICAST_US(sch_list[gdrom_schid].start) ; + REICAST_US(sch_list[gdrom_schid].end) ; - REICAST_US(list[maple_schid].tag) ; - REICAST_US(list[maple_schid].start) ; - REICAST_US(list[maple_schid].end) ; + REICAST_US(sch_list[maple_schid].tag) ; + REICAST_US(sch_list[maple_schid].start) ; + REICAST_US(sch_list[maple_schid].end) ; - REICAST_US(list[dma_sched_id].tag) ; - REICAST_US(list[dma_sched_id].start) ; - REICAST_US(list[dma_sched_id].end) ; + REICAST_US(sch_list[dma_sched_id].tag) ; + REICAST_US(sch_list[dma_sched_id].start) ; + REICAST_US(sch_list[dma_sched_id].end) ; for (int i = 0; i < 3; i++) { - REICAST_US(list[tmu_sched[i]].tag) ; - REICAST_US(list[tmu_sched[i]].start) ; - REICAST_US(list[tmu_sched[i]].end) ; + REICAST_US(sch_list[tmu_sched[i]].tag) ; + REICAST_US(sch_list[tmu_sched[i]].start) ; + REICAST_US(sch_list[tmu_sched[i]].end) ; } - REICAST_US(list[render_end_schid].tag) ; - REICAST_US(list[render_end_schid].start) ; - REICAST_US(list[render_end_schid].end) ; + REICAST_US(sch_list[render_end_schid].tag) ; + REICAST_US(sch_list[render_end_schid].start) ; + REICAST_US(sch_list[render_end_schid].end) ; - REICAST_US(list[vblank_schid].tag) ; - REICAST_US(list[vblank_schid].start) ; - REICAST_US(list[vblank_schid].end) ; - - REICAST_US(list[time_sync].tag) ; - REICAST_US(list[time_sync].start) ; - REICAST_US(list[time_sync].end) ; - - REICAST_US(list[modem_sched].tag) ; - REICAST_US(list[modem_sched].start) ; - REICAST_US(list[modem_sched].end) ; + REICAST_US(sch_list[vblank_schid].tag) ; + REICAST_US(sch_list[vblank_schid].start) ; + REICAST_US(sch_list[vblank_schid].end) ; + REICAST_US(sch_list[time_sync].tag) ; + REICAST_US(sch_list[time_sync].start) ; + REICAST_US(sch_list[time_sync].end) ; + #ifdef ENABLE_MODEM + REICAST_US(sch_list[modem_sched].tag) ; + REICAST_US(sch_list[modem_sched].start) ; + REICAST_US(sch_list[modem_sched].end) ; + #else + int modem_dummy; + REICAST_US(modem_dummy); + REICAST_US(modem_dummy); + REICAST_US(modem_dummy); + #endif REICAST_US(SCIF_SCFSR2); REICAST_US(SCIF_SCFRDR2); @@ -1885,8 +1823,6 @@ bool dc_unserialize(void **data, unsigned int *total_size) #endif - - REICAST_US(NullDriveDiscType); REICAST_USA(q_subchannel,96); @@ -1902,9 +1838,8 @@ bool dc_unserialize(void **data, unsigned int *total_size) // REICAST_US(i); // VRAM_MASK - REICAST_US(naomi_updates); - REICAST_US(BoardID); + REICAST_US(i); // BoardID REICAST_US(GSerialBuffer); REICAST_US(BSerialBuffer); REICAST_US(GBufPos); @@ -1941,31 +1876,28 @@ bool dc_unserialize(void **data, unsigned int *total_size) REICAST_US(div_som_reg2); REICAST_US(div_som_reg3); - - - //REICAST_USA(CodeCache,CODE_SIZE) ; //REICAST_USA(SH4_TCB,CODE_SIZE+4096); REICAST_US(LastAddr); REICAST_US(LastAddr_min); REICAST_USA(block_hash,1024); - REICAST_USA(RegisterWrite,sh4_reg_count); REICAST_USA(RegisterRead,sh4_reg_count); REICAST_US(fallback_blocks); REICAST_US(total_blocks); REICAST_US(REMOVED_OPS); - - - - REICAST_USA(kcode,4); - REICAST_USA(rt,4); - REICAST_USA(lt,4); - REICAST_USA(vks,4); - REICAST_USA(joyx,4); - REICAST_USA(joyy,4); + REICAST_US(i); //REICAST_USA(kcode,4); + REICAST_US(i); + REICAST_US(i); //REICAST_USA(rt,4); + REICAST_US(i); //REICAST_USA(lt,4); + REICAST_US(i); //REICAST_USA(vks,4); + REICAST_US(i); + REICAST_US(i); + REICAST_US(i); + REICAST_US(i); //REICAST_USA(joyx,4); + REICAST_US(i); //REICAST_USA(joyy,4); REICAST_US(settings.dreamcast.broadcast); diff --git a/core/stdclass.cpp b/core/stdclass.cpp index 5f689b72e..76921d170 100644 --- a/core/stdclass.cpp +++ b/core/stdclass.cpp @@ -4,9 +4,16 @@ #include #include "types.h" #include "cfg/cfg.h" +#include "stdclass.h" +#if HOST_OS == OS_DARWIN +#include +#include +#endif -#if BUILD_COMPILER==COMPILER_VC + +#if COMPILER_VC_OR_CLANG_WIN32 #include + #include #define access _access #define R_OK 4 #else @@ -140,62 +147,156 @@ string get_game_dir() bool make_directory(const string& path) { - return mkdir(path.c_str() -#ifndef _WIN32 - , 0755 +#if COMPILER_VC_OR_CLANG_WIN32 +#define mkdir _mkdir +#endif + +#ifdef _WIN32 + return mkdir(path.c_str()) == 0; +#else + return mkdir(path.c_str(), 0755) == 0; #endif - ) == 0; } -#if 0 -//File Enumeration -void FindAllFiles(FileFoundCB* callback,wchar* dir,void* param) -{ - WIN32_FIND_DATA FindFileData; - HANDLE hFind = INVALID_HANDLE_VALUE; - wchar DirSpec[MAX_PATH + 1]; // directory specification - DWORD dwError; +// Thread & related platform dependant code +#if !defined(HOST_NO_THREADS) - strncpy (DirSpec, dir, strlen(dir)+1); - //strncat (DirSpec, "\\*", 3); - - hFind = FindFirstFile( DirSpec, &FindFileData); - - if (hFind == INVALID_HANDLE_VALUE) - { - return; - } - else - { - - if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)==0) - { - callback(FindFileData.cFileName,param); - } -u32 rv; - while ( (rv=FindNextFile(hFind, &FindFileData)) != 0) - { - if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)==0) - { - callback(FindFileData.cFileName,param); - } - } - dwError = GetLastError(); - FindClose(hFind); - if (dwError != ERROR_NO_MORE_FILES) - { - return ; - } +#if HOST_OS==OS_WINDOWS +void cThread::Start() { + verify(hThread == NULL); + hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)entry, param, 0, NULL); + ResumeThread(hThread); +} +void cThread::WaitToEnd() { + WaitForSingleObject(hThread,INFINITE); + CloseHandle(hThread); + hThread = NULL; +} +#else +void cThread::Start() { + verify(hThread == NULL); + hThread = new pthread_t; + if (pthread_create( hThread, NULL, entry, param)) + die("Thread creation failed"); +} +void cThread::WaitToEnd() { + if (hThread) { + pthread_join(*hThread,0); + delete hThread; + hThread = NULL; } - return ; } #endif -/* -#include "dc\sh4\rec_v1\compiledblock.h" -#include "dc\sh4\rec_v1\blockmanager.h" +#endif + + +#if HOST_OS==OS_WINDOWS +cResetEvent::cResetEvent() { + hEvent = CreateEvent( + NULL, // default security attributes + FALSE, // auto-reset event? + FALSE, // initial state is State + NULL // unnamed object + ); +} +cResetEvent::~cResetEvent() +{ + //Destroy the event object ? + CloseHandle(hEvent); +} +void cResetEvent::Set()//Signal +{ + #if defined(DEBUG_THREADS) + Sleep(rand() % 10); + #endif + SetEvent(hEvent); +} +void cResetEvent::Reset()//reset +{ + #if defined(DEBUG_THREADS) + Sleep(rand() % 10); + #endif + ResetEvent(hEvent); +} +bool cResetEvent::Wait(u32 msec)//Wait for signal , then reset +{ + #if defined(DEBUG_THREADS) + Sleep(rand() % 10); + #endif + return WaitForSingleObject(hEvent,msec) == WAIT_OBJECT_0; +} +void cResetEvent::Wait()//Wait for signal , then reset +{ + #if defined(DEBUG_THREADS) + Sleep(rand() % 10); + #endif + WaitForSingleObject(hEvent,(u32)-1); +} +#else +cResetEvent::cResetEvent() { + pthread_mutex_init(&mutx, NULL); + pthread_cond_init(&cond, NULL); +} +cResetEvent::~cResetEvent() { +} +void cResetEvent::Set()//Signal +{ + pthread_mutex_lock( &mutx ); + state=true; + pthread_cond_signal( &cond); + pthread_mutex_unlock( &mutx ); +} +void cResetEvent::Reset()//reset +{ + pthread_mutex_lock( &mutx ); + state=false; + pthread_mutex_unlock( &mutx ); +} +bool cResetEvent::Wait(u32 msec)//Wait for signal , then reset +{ + pthread_mutex_lock( &mutx ); + if (!state) + { + struct timespec ts; +#if HOST_OS == OS_DARWIN + // OSX doesn't have clock_gettime. + clock_serv_t cclock; + mach_timespec_t mts; + + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + ts.tv_sec = mts.tv_sec; + ts.tv_nsec = mts.tv_nsec; +#else + clock_gettime(CLOCK_REALTIME, &ts); +#endif + ts.tv_sec += msec / 1000; + ts.tv_nsec += (msec % 1000) * 1000000; + while (ts.tv_nsec > 1000000000) + { + ts.tv_nsec -= 1000000000; + ts.tv_sec++; + } + pthread_cond_timedwait( &cond, &mutx, &ts ); + } + bool rc = state; + state=false; + pthread_mutex_unlock( &mutx ); + + return rc; +} +void cResetEvent::Wait()//Wait for signal , then reset +{ + pthread_mutex_lock( &mutx ); + if (!state) + { + pthread_cond_wait( &cond, &mutx ); + } + state=false; + pthread_mutex_unlock( &mutx ); +} +#endif -bool VramLockedWrite(u8* address); -bool RamLockedWrite(u8* address,u32* sp); -*/ diff --git a/core/stdclass.h b/core/stdclass.h index 0c30dd534..39640f5d8 100644 --- a/core/stdclass.h +++ b/core/stdclass.h @@ -7,7 +7,7 @@ #if HOST_OS!=OS_WINDOWS #include #else -#include +#include #endif @@ -166,38 +166,41 @@ public: #if !defined(HOST_NO_THREADS) typedef void* ThreadEntryFP(void* param); -typedef void* THREADHANDLE; - -class cThread -{ +class cThread { private: - ThreadEntryFP* Entry; + ThreadEntryFP* entry; void* param; public : - THREADHANDLE hThread; - cThread(ThreadEntryFP* function,void* param); - + #if HOST_OS==OS_WINDOWS + HANDLE hThread; + #else + pthread_t *hThread; + #endif + + cThread(ThreadEntryFP* function, void* param) + :entry(function), param(param), hThread(NULL) {} + ~cThread() { WaitToEnd(); } void Start(); void WaitToEnd(); }; #endif + + //Wait Events typedef void* EVENTHANDLE; class cResetEvent { - private: #if HOST_OS==OS_WINDOWS EVENTHANDLE hEvent; #else pthread_mutex_t mutx; pthread_cond_t cond; - + bool state; #endif public : - bool state; - cResetEvent(bool State,bool Auto); + cResetEvent(); ~cResetEvent(); void Set(); //Set state to signaled void Reset(); //Set state to non signaled @@ -276,29 +279,52 @@ string get_game_save_prefix(); string get_game_basename(); string get_game_dir(); -class VArray2 -{ +bool mem_region_lock(void *start, size_t len); +bool mem_region_unlock(void *start, size_t len); +bool mem_region_set_exec(void *start, size_t len); +void *mem_region_reserve(void *start, size_t len); +bool mem_region_release(void *start, size_t len); +void *mem_region_map_file(void *file_handle, void *dest, size_t len, size_t offset, bool readwrite); +bool mem_region_unmap_file(void *start, size_t len); + +// Locked memory class, used for texture invalidation purposes. +class VLockedMemory { public: - u8* data; - u32 size; - //void Init(void* data,u32 sz); - //void Term(); - void LockRegion(u32 offset,u32 size); - void UnLockRegion(u32 offset,u32 size); + unsigned size; - void Zero() + void SetRegion(void* ptr, unsigned size) { + this->data = (u8*)ptr; + this->size = size; + } + void *getPtr() const { return data; } + unsigned getSize() const { return size; } + + #ifdef TARGET_NO_EXCEPTIONS + void LockRegion(unsigned offset, unsigned size_bytes) {} + void UnLockRegion(unsigned offset, unsigned size_bytes) {} + #else + void LockRegion(unsigned offset, unsigned size_bytes) { - UnLockRegion(0,size); - memset(data,0,size); + mem_region_lock(&data[offset], size_bytes); } - INLINE u8& operator [](const u32 i) - { + void UnLockRegion(unsigned offset, unsigned size_bytes) + { + mem_region_unlock(&data[offset], size_bytes); + } + #endif + + void Zero() { + UnLockRegion(0, size); + memset(data, 0, size); + } + + INLINE u8& operator [](unsigned i) { #ifdef MEM_BOUND_CHECK - if (i>=size) + if (i >= size) { - printf("Error: VArray2 , index out of range (%d>%d)\n",i,size-1); + printf("Error: VLockedMemory , index out of range (%d > %d)\n", i, size-1); MEM_DO_BREAK; } #endif diff --git a/core/types.h b/core/types.h index a94766055..a11fc3082 100644 --- a/core/types.h +++ b/core/types.h @@ -39,7 +39,7 @@ #undef _CRT_SECURE_NO_DEPRECATE #endif -#define _CRT_SECURE_NO_DEPRECATE +#define _CRT_SECURE_NO_DEPRECATE //unnamed struncts/unions #pragma warning( disable : 4201) @@ -123,7 +123,7 @@ enum HollyInterruptType }; enum HollyInterruptID -{ +{ // asic9a /sh4 external holly normal [internal] holly_RENDER_DONE_vd = holly_nrm | 0, //bit 0 = End of Render interrupt : Video holly_RENDER_DONE_isp = holly_nrm | 1, //bit 1 = End of Render interrupt : ISP @@ -132,11 +132,11 @@ enum HollyInterruptID holly_SCANINT1 = holly_nrm | 3, //bit 3 = V Blank-in interrupt holly_SCANINT2 = holly_nrm | 4, //bit 4 = V Blank-out interrupt holly_HBLank = holly_nrm | 5, //bit 5 = H Blank-in interrupt - + holly_YUV_DMA = holly_nrm | 6, //bit 6 = End of Transferring interrupt : YUV holly_OPAQUE = holly_nrm | 7, //bit 7 = End of Transferring interrupt : Opaque List holly_OPAQUEMOD = holly_nrm | 8, //bit 8 = End of Transferring interrupt : Opaque Modifier Volume List - + holly_TRANS = holly_nrm | 9, //bit 9 = End of Transferring interrupt : Translucent List holly_TRANSMOD = holly_nrm | 10, //bit 10 = End of Transferring interrupt : Translucent Modifier Volume List holly_PVR_DMA = holly_nrm | 11, //bit 11 = End of DMA interrupt : PVR-DMA @@ -145,12 +145,12 @@ enum HollyInterruptID holly_MAPLE_VBOI = holly_nrm | 13, //bit 13 = Maple V blank over interrupt holly_GDROM_DMA = holly_nrm | 14, //bit 14 = End of DMA interrupt : GD-DMA holly_SPU_DMA = holly_nrm | 15, //bit 15 = End of DMA interrupt : AICA-DMA - + holly_EXT_DMA1 = holly_nrm | 16, //bit 16 = End of DMA interrupt : Ext-DMA1(External 1) holly_EXT_DMA2 = holly_nrm | 17, //bit 17 = End of DMA interrupt : Ext-DMA2(External 2) holly_DEV_DMA = holly_nrm | 18, //bit 18 = End of DMA interrupt : Dev-DMA(Development tool DMA) - - holly_CH2_DMA = holly_nrm | 19, //bit 19 = End of DMA interrupt : ch2-DMA + + holly_CH2_DMA = holly_nrm | 19, //bit 19 = End of DMA interrupt : ch2-DMA holly_PVR_SortDMA = holly_nrm | 20, //bit 20 = End of DMA interrupt : Sort-DMA (Transferring for alpha sorting) holly_PUNCHTHRU = holly_nrm | 21, //bit 21 = End of Transferring interrupt : Punch Through List @@ -188,8 +188,8 @@ enum HollyInterruptID //bit 23 = G2 : AICA-DMA Time out //bit 24 = G2 : Ext-DMA1 Time out //bit 25 = G2 : Ext-DMA2 Time out - //bit 26 = G2 : Dev-DMA Time out - //bit 27 = G2 : Time out in CPU accessing + //bit 26 = G2 : Dev-DMA Time out + //bit 27 = G2 : Time out in CPU accessing }; @@ -200,116 +200,17 @@ struct vram_block u32 end; u32 len; u32 type; - + void* userdata; }; - -#if (DC_PLATFORM==DC_PLATFORM_DREAMCAST) - - #define BUILD_DREAMCAST 1 - - //DC : 16 mb ram, 8 mb vram, 2 mb aram, 2 mb bios, 128k flash - #define RAM_SIZE (16*1024*1024) - #define VRAM_SIZE (8*1024*1024) - #define ARAM_SIZE (2*1024*1024) - #define BIOS_SIZE (2*1024*1024) - #define FLASH_SIZE (128*1024) - - #define ROM_PREFIX "dc_" - #define ROM_NAMES - #define NVR_OPTIONAL 0 - -#elif (DC_PLATFORM==DC_PLATFORM_DEV_UNIT) - - #define BUILD_DEV_UNIT 1 - - //Devkit : 32 mb ram, 8? mb vram, 2? mb aram, 2? mb bios, ? flash - #define RAM_SIZE (32*1024*1024) - #define VRAM_SIZE (8*1024*1024) - #define ARAM_SIZE (2*1024*1024) - #define BIOS_SIZE (2*1024*1024) - #define FLASH_SIZE (128*1024) - - #define ROM_PREFIX "hkt_" - #define ROM_NAMES - #define NVR_OPTIONAL 0 - -#elif (DC_PLATFORM==DC_PLATFORM_NAOMI) - - //Naomi : 32 mb ram, 16 mb vram, 8 mb aram, 2 mb bios, ? flash - #define RAM_SIZE (32*1024*1024) - #define VRAM_SIZE (16*1024*1024) - #define ARAM_SIZE (8*1024*1024) - #define BIOS_SIZE (2*1024*1024) - #define BBSRAM_SIZE (32*1024) - - #define ROM_PREFIX "naomi_" - #define ROM_NAMES ";epr-21576d.bin" - #define NVR_OPTIONAL 1 - -#elif (DC_PLATFORM==DC_PLATFORM_NAOMI2) - - //Naomi2 : 32 mb ram, 16 mb vram, 8 mb aram, 2 mb bios, ? flash - #define RAM_SIZE (32*1024*1024) - #define VRAM_SIZE (16*1024*1024) - #define ARAM_SIZE (8*1024*1024) - #define BIOS_SIZE (2*1024*1024) - #define BBSRAM_SIZE (32*1024) - - #define ROM_PREFIX "n2_" - #define ROM_NAMES - #define NVR_OPTIONAL 1 - -#elif (DC_PLATFORM==DC_PLATFORM_ATOMISWAVE) - - #define BUILD_ATOMISWAVE 1 - - //Atomiswave : 16 mb ram, 8 mb vram, 8 mb aram, 128kb bios on flash, 128kb battery-backed ram - #define RAM_SIZE (16*1024*1024) - #define VRAM_SIZE (8*1024*1024) - #define ARAM_SIZE (8*1024*1024) - #define BIOS_SIZE (128*1024) - #define BBSRAM_SIZE (128*1024) - - #define ROM_PREFIX "aw_" - #define ROM_NAMES ";bios.ic23_l" - #define NVR_OPTIONAL 1 - -#else - #error invalid build config -#endif - -#define RAM_MASK (RAM_SIZE-1) -#define VRAM_MASK (VRAM_SIZE-1) -#define ARAM_MASK (ARAM_SIZE-1) -#define BIOS_MASK (BIOS_SIZE-1) - -#ifdef FLASH_SIZE -#define FLASH_MASK (FLASH_SIZE-1) -#endif - -#ifdef BBSRAM_SIZE -#define BBSRAM_MASK (BBSRAM_SIZE-1) -#endif - -#define GD_CLOCK 33868800 //GDROM XTAL -- 768fs - -#define AICA_CORE_CLOCK (GD_CLOCK*4/3) //[45158400] GD->PLL 3:4 -> AICA CORE -- 1024fs -#define ADAC_CLOCK (AICA_CORE_CLOCK/2) //[11289600] 44100*256, AICA CORE -> PLL 4:1 -> ADAC -- 256fs -#define AICA_ARM_CLOCK (AICA_CORE_CLOCK/2) //[22579200] AICA CORE -> PLL 2:1 -> ARM -#define AICA_SDRAM_CLOCK (GD_CLOCK*2) //[67737600] GD-> PLL 2 -> SDRAM -#define SH4_MAIN_CLOCK (200*1000*1000) //[200000000] XTal(13.5) -> PLL (33.3) -> PLL 1:6 (200) -#define SH4_RAM_CLOCK (100*1000*1000) //[100000000] XTal(13.5) -> PLL (33.3) -> PLL 1:3 (100) , also suplied to HOLLY chip -#define G2_BUS_CLOCK (25*1000*1000) //[25000000] from Holly, from SH4_RAM_CLOCK w/ 2 2:1 plls - - enum ndc_error_codes { - rv_ok = 0, //no error + rv_ok = 0, //no error + rv_cli_finish=69, //clean exit after -help or -version , should we just use rv_ok? - rv_error=-2, //error - rv_serror=-1, //silent error , it has been reported to the user + rv_error=-2, //error + rv_serror=-1, //silent error , it has been reported to the user }; //Simple struct to store window rect ;) @@ -328,7 +229,7 @@ struct NDC_WINDOW_RECT //****************************************************** //*********************** PowerVR ********************** //****************************************************** - + void libCore_vramlock_Unlock_block (vram_block* block); void libCore_vramlock_Unlock_block_wb (vram_block* block); vram_block* libCore_vramlock_Lock(u32 start_offset,u32 end_offset,void* userdata); @@ -345,7 +246,7 @@ enum DiscType CdRom_XA=0x20, CdRom_Extra=0x30, CdRom_CDI=0x40, - GdRom=0x80, + GdRom=0x80, NoDisk=0x1, //These are a bit hacky .. but work for now ... Open=0x2, //tray is open :) @@ -443,6 +344,7 @@ int darw_printf(const wchar* Text,...); //includes from c++rt #include #include +#include using namespace std; //used for asm-olny functions @@ -484,9 +386,9 @@ using namespace std; #include "stdclass.h" #ifndef RELEASE -#define EMUERROR(format, ...) printf("Error in %s:%s:%d: " format "\n", \ - strlen(__FILE__) <= 20 ? __FILE__ : __FILE__ + strlen(__FILE__) - 20, \ - __FUNCTION__, __LINE__, ##__VA_ARGS__) +#define EMUERROR(format, ...) printf("Error in %20s:%s:%d: " format "\n", \ + __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__) +//strlen(__FILE__) <= 20 ? __FILE__ : __FILE__ + strlen(__FILE__) - 20, #else #define EMUERROR(format, ...) #endif @@ -617,7 +519,7 @@ typedef union -#if COMPILER_VC==BUILD_COMPILER +#if COMPILER_VC_OR_CLANG_WIN32 #pragma warning( disable : 4127 4996 /*4244*/) #else #define stricmp strcasecmp @@ -675,7 +577,7 @@ enum RegIO RIO_RO = REG_RO | REG_WF, RIO_RO_FUNC = REG_RO | REG_RF | REG_WF, RIO_CONST = REG_RO | REG_WF, - RIO_WO_FUNC = REG_WF | REG_RF | REG_WO, + RIO_WO_FUNC = REG_WF | REG_RF | REG_WO, RIO_NO_ACCESS = REG_WF | REG_RF | REG_NO_ACCESS }; @@ -700,6 +602,16 @@ struct RegisterStruct u32 flags; //Access flags ! }; +enum SmcCheckEnum { + FullCheck = 0, + FastCheck = 1, + NoCheck = 2 +}; +enum LimitFPSEnum { + LimitFPSDisabled = 0, + LimitFPSAuto = 1, + LimitFPSEnabled = 2 +}; struct settings_t { @@ -726,7 +638,12 @@ struct settings_t f32 ExtraDepthScale; bool CustomTextures; bool DumpTextures; - int ScreenScaling; // in percent. 50 means half the native resolution + int ScreenScaling; // in percent. 50 means half the native resolution + int ScreenStretching; // in percent. 150 means stretch from 4/3 to 6/3 + bool Fog; + bool FloatVMUs; + bool Rotate90; // Rotate the screen 90 deg CC + bool PerStripSorting; } rend; struct @@ -737,8 +654,9 @@ struct settings_t bool safemode; bool disable_nvmem; bool disable_vmem32; + SmcCheckEnum SmcCheckLevel; } dynarec; - + struct { u32 run_counts; @@ -747,7 +665,6 @@ struct settings_t struct { u32 cable; // 0 -> VGA, 1 -> VGA, 2 -> RGB, 3 -> TV - u32 RTC; u32 region; // 0 -> JP, 1 -> USA, 2 -> EU, 3 -> default u32 broadcast; // 0 -> NTSC, 1 -> PAL, 2 -> PAL/M, 3 -> PAL/N, 4 -> default u32 language; // 0 -> JP, 1 -> EN, 2 -> DE, 3 -> FR, 4 -> SP, 5 -> IT, 6 -> default @@ -759,16 +676,25 @@ struct settings_t { u32 HW_mixing; //(0) -> SW , 1 -> HW , 2 -> Auto u32 BufferSize; //In samples ,*4 for bytes (1024) - bool LimitFPS; // defaults to true + LimitFPSEnum LimitFPS; u32 GlobalFocus; //0 -> only hwnd , (1) -> Global u32 BufferCount; //BufferCount+2 buffers used , max 60 , default 0 u32 CDDAMute; u32 GlobalMute; bool DSPEnabled; + bool OldSyncronousDma; // 1 -> sync dma (old behavior), 0 -> async dma (fixes some games, partial implementation) bool NoBatch; bool NoSound; } aica; + struct{ + std::string backend; + + // slug<> + std::map> options; + } audio; + + #if USE_OMX struct { @@ -798,7 +724,7 @@ struct settings_t { u32 ta_skip; u32 rend; - + u32 MaxThreads; bool SynchronousRender; } pvr; @@ -841,7 +767,7 @@ static inline void do_nada(...) { } #ifdef _ANDROID #include -#ifdef printf +#ifdef printf #undef printf #endif @@ -948,7 +874,7 @@ void libARM_Update(u32 cycles); else if (sz==2) \ return *(u16*)&arr[addr]; \ else if (sz==4) \ - return *(u32*)&arr[addr];} + return *(u32*)&arr[addr];} #define WriteMemArr(arr,addr,data,sz) \ {if(sz==1) \ @@ -956,7 +882,7 @@ void libARM_Update(u32 cycles); else if (sz==2) \ {*(u16*)&arr[addr]=(u16)data;} \ else if (sz==4) \ - {*(u32*)&arr[addr]=data;}} + {*(u32*)&arr[addr]=data;}} #define WriteMemArrRet(arr,addr,data,sz) \ {if(sz==1) \ @@ -964,7 +890,7 @@ void libARM_Update(u32 cycles); else if (sz==2) \ {*(u16*)&arr[addr]=(u16)data;return;} \ else if (sz==4) \ - {*(u32*)&arr[addr]=data;return;}} + {*(u32*)&arr[addr]=data;return;}} struct OnLoad { diff --git a/core/version.h.in b/core/version.h.in new file mode 100644 index 000000000..81a831a05 --- /dev/null +++ b/core/version.h.in @@ -0,0 +1,8 @@ +/* + * reicast: version.h + */ +#pragma once + +#define REICAST_VERSION "@GIT_VERSION@" +#define GIT_HASH "@GIT_HASH@" +#define BUILD_DATE __DATE__ \ No newline at end of file diff --git a/core/version/.gitignore b/core/version/.gitignore deleted file mode 100644 index 1946cee8c..000000000 --- a/core/version/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/version.cpp diff --git a/core/version/version.h b/core/version/version.h deleted file mode 100644 index 3c9912aa7..000000000 --- a/core/version/version.h +++ /dev/null @@ -1,3 +0,0 @@ -extern const char *version; -extern const char *git_hash; -extern const char *build_date; diff --git a/core/windows/win_vmem.cpp b/core/windows/win_vmem.cpp new file mode 100644 index 000000000..682d6b8c4 --- /dev/null +++ b/core/windows/win_vmem.cpp @@ -0,0 +1,221 @@ + +#define _WIN32_WINNT 0x0500 +#include +#include + +#include "hw/mem/_vmem.h" + +// Implementation of the vmem related function for Windows platforms. +// For now this probably does some assumptions on the CPU/platform. + +// The implementation allows it to be empty (that is, to not lock memory). + +bool mem_region_lock(void *start, size_t len) +{ + DWORD old; + if (!VirtualProtect(start, len, PAGE_READONLY, &old)) + die("VirtualProtect failed ..\n"); + return true; +} + +bool mem_region_unlock(void *start, size_t len) +{ + DWORD old; + if (!VirtualProtect(start, len, PAGE_READWRITE, &old)) + die("VirtualProtect failed ..\n"); + return true; +} + +bool mem_region_set_exec(void *start, size_t len) +{ + DWORD old; + if (!VirtualProtect(start, len, PAGE_EXECUTE_READWRITE, &old)) + die("VirtualProtect failed ..\n"); + return true; +} + +void *mem_region_reserve(void *start, size_t len) +{ + return VirtualAlloc(start, len, MEM_RESERVE, PAGE_NOACCESS); +} + +bool mem_region_release(void *start, size_t len) +{ + return VirtualFree(start, 0, MEM_RELEASE); +} + +void *mem_region_map_file(void *file_handle, void *dest, size_t len, size_t offset, bool readwrite) +{ + return MapViewOfFileEx((HANDLE)file_handle, readwrite ? FILE_MAP_WRITE : FILE_MAP_READ, (DWORD)(offset >> 32), (DWORD)offset, len, dest); +} + +bool mem_region_unmap_file(void *start, size_t len) +{ + return UnmapViewOfFile(start); +} + +static HANDLE mem_handle = INVALID_HANDLE_VALUE, mem_handle2 = INVALID_HANDLE_VALUE; +static char * base_alloc = NULL; + +// Implement vmem initialization for RAM, ARAM, VRAM and SH4 context, fpcb etc. +// The function supports allocating 512MB or 4GB addr spaces. + +// Plase read the POSIX implementation for more information. On Windows this is +// rather straightforward. +VMemType vmem_platform_init(void **vmem_base_addr, void **sh4rcb_addr) { + // Firt let's try to allocate the in-memory file + mem_handle = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, RAM_SIZE_MAX + VRAM_SIZE_MAX + ARAM_SIZE_MAX, 0); + + // Now allocate the actual address space (it will be 64KB aligned on windows). + unsigned memsize = 512*1024*1024 + sizeof(Sh4RCB) + ARAM_SIZE_MAX; + base_alloc = (char*)mem_region_reserve(NULL, memsize); + + // Calculate pointers now + *sh4rcb_addr = &base_alloc[0]; + *vmem_base_addr = &base_alloc[sizeof(Sh4RCB)]; + + return MemType512MB; +} + +// Just tries to wipe as much as possible in the relevant area. +void vmem_platform_destroy() { + VirtualFree(base_alloc, 0, MEM_RELEASE); + CloseHandle(mem_handle); +} + +// Resets a chunk of memory by deleting its data and setting its protection back. +void vmem_platform_reset_mem(void *ptr, unsigned size_bytes) { + VirtualFree(ptr, size_bytes, MEM_DECOMMIT); +} + +// Allocates a bunch of memory (page aligned and page-sized) +void vmem_platform_ondemand_page(void *address, unsigned size_bytes) { + verify(VirtualAlloc(address, size_bytes, MEM_COMMIT, PAGE_READWRITE)); +} + +/// Creates mappings to the underlying file including mirroring sections +void vmem_platform_create_mappings(const vmem_mapping *vmem_maps, unsigned nummaps) { + // Since this is tricky to get right in Windows (in posix one can just unmap sections and remap later) + // we unmap the whole thing only to remap it later. + + // Unmap the whole section + VirtualFree(base_alloc, 0, MEM_RELEASE); + + // Map the SH4CB block too + void *base_ptr = VirtualAlloc(base_alloc, sizeof(Sh4RCB), MEM_RESERVE, PAGE_NOACCESS); + verify(base_ptr == base_alloc); + void *cntx_ptr = VirtualAlloc((u8*)p_sh4rcb + sizeof(p_sh4rcb->fpcb), sizeof(Sh4RCB) - sizeof(p_sh4rcb->fpcb), MEM_COMMIT, PAGE_READWRITE); + verify(cntx_ptr == (u8*)p_sh4rcb + sizeof(p_sh4rcb->fpcb)); + + for (unsigned i = 0; i < nummaps; i++) { + unsigned address_range_size = vmem_maps[i].end_address - vmem_maps[i].start_address; + DWORD protection = vmem_maps[i].allow_writes ? (FILE_MAP_READ | FILE_MAP_WRITE) : FILE_MAP_READ; + + if (!vmem_maps[i].memsize) { + // Unmapped stuff goes with a protected area or memory. Prevent anything from allocating here + void *ptr = VirtualAlloc(&virt_ram_base[vmem_maps[i].start_address], address_range_size, MEM_RESERVE, PAGE_NOACCESS); + verify(ptr == &virt_ram_base[vmem_maps[i].start_address]); + } + else { + // Calculate the number of mirrors + unsigned num_mirrors = (address_range_size) / vmem_maps[i].memsize; + verify((address_range_size % vmem_maps[i].memsize) == 0 && num_mirrors >= 1); + + // Remap the views one by one + for (unsigned j = 0; j < num_mirrors; j++) { + unsigned offset = vmem_maps[i].start_address + j * vmem_maps[i].memsize; + + void *ptr = MapViewOfFileEx(mem_handle, protection, 0, vmem_maps[i].memoffset, + vmem_maps[i].memsize, &virt_ram_base[offset]); + verify(ptr == &virt_ram_base[offset]); + } + } + } +} + +typedef void* (*mapper_fn) (void *addr, unsigned size); + +// This is a tempalted function since it's used twice +static void* vmem_platform_prepare_jit_block_template(void *code_area, unsigned size, mapper_fn mapper) { + // Several issues on Windows: can't protect arbitrary pages due to (I guess) the way + // kernel tracks mappings, so only stuff that has been allocated with VirtualAlloc can be + // protected (the entire allocation IIUC). + + // Strategy: ignore code_area and allocate a new one. Protect it properly. + // More issues: the area should be "close" to the .text stuff so that code gen works. + // Remember that on x64 we have 4 byte jump/load offset immediates, no issues on x86 :D + + // Take this function addr as reference. + uintptr_t base_addr = reinterpret_cast(&vmem_platform_init) & ~0xFFFFF; + + // Probably safe to assume reicast code is <200MB (today seems to be <16MB on every platform I've seen). + for (unsigned i = 0; i < 1800*1024*1024; i += 8*1024*1024) { // Some arbitrary step size. + uintptr_t try_addr_above = base_addr + i; + uintptr_t try_addr_below = base_addr - i; + + // We need to make sure there's no address wrap around the end of the addrspace (meaning: int overflow). + if (try_addr_above > base_addr) { + void *ptr = mapper((void*)try_addr_above, size); + if (ptr) + return ptr; + } + if (try_addr_below < base_addr) { + void *ptr = mapper((void*)try_addr_below, size); + if (ptr) + return ptr; + } + } + return NULL; +} + +static void* mem_alloc(void *addr, unsigned size) { + return VirtualAlloc(addr, size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); +} + +// Prepares the code region for JIT operations, thus marking it as RWX +bool vmem_platform_prepare_jit_block(void *code_area, unsigned size, void **code_area_rwx) { + // Get the RWX page close to the code_area + void *ptr = vmem_platform_prepare_jit_block_template(code_area, size, &mem_alloc); + if (!ptr) + return false; + + *code_area_rwx = ptr; + printf("Found code area at %p, not too far away from %p\n", *code_area_rwx, &vmem_platform_init); + + // We should have found some area in the addrspace, after all size is ~tens of megabytes. + // Pages are already RWX, all done + return true; +} + + +static void* mem_file_map(void *addr, unsigned size) { + // Maps the entire file at the specified addr. + void *ptr = VirtualAlloc(addr, size, MEM_RESERVE, PAGE_NOACCESS); + if (!ptr) + return NULL; + VirtualFree(ptr, 0, MEM_RELEASE); + if (ptr != addr) + return NULL; + + return MapViewOfFileEx(mem_handle2, FILE_MAP_READ | FILE_MAP_EXECUTE, 0, 0, size, addr); +} + +// Use two addr spaces: need to remap something twice, therefore use CreateFileMapping() +bool vmem_platform_prepare_jit_block(void *code_area, unsigned size, void **code_area_rw, uintptr_t *rx_offset) { + mem_handle2 = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_EXECUTE_READWRITE, 0, size, 0); + + // Get the RX page close to the code_area + void *ptr_rx = vmem_platform_prepare_jit_block_template(code_area, size, &mem_file_map); + if (!ptr_rx) + return false; + + // Ok now we just remap the RW segment at any position (dont' care). + void *ptr_rw = MapViewOfFileEx(mem_handle2, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, size, NULL); + + *code_area_rw = ptr_rw; + *rx_offset = (char*)ptr_rx - (char*)ptr_rw; + printf("Info: Using NO_RWX mode, rx ptr: %p, rw ptr: %p, offset: %lu\n", ptr_rx, ptr_rw, (unsigned long)*rx_offset); + + return (ptr_rw != NULL); +} + diff --git a/core/windows/winmain.cpp b/core/windows/winmain.cpp index fe7b9f137..0c3ed0a12 100644 --- a/core/windows/winmain.cpp +++ b/core/windows/winmain.cpp @@ -1,7 +1,9 @@ -#include "oslib\oslib.h" -#include "oslib\audiostream.h" -#include "imgread\common.h" -#include "hw\mem\vmem32.h" +#include "oslib/oslib.h" +#include "oslib/audiostream.h" +#include "imgread/common.h" +#include "hw/mem/vmem32.h" +#include "stdclass.h" +#include "cfg/cfg.h" #include "xinput_gamepad.h" #include "win_keyboard.h" @@ -9,8 +11,8 @@ #include #include -#include -#include "hw\maple\maple_cfg.h" +#include +#include "hw/maple/maple_cfg.h" #pragma comment(lib, "XInput9_1_0.lib") PCHAR* @@ -152,12 +154,10 @@ LONG ExeptionHandler(EXCEPTION_POINTERS *ExceptionInfo) { return EXCEPTION_CONTINUE_EXECUTION; } -#ifndef TARGET_NO_NVMEM else if (BM_LockedWrite(address)) { return EXCEPTION_CONTINUE_EXECUTION; } -#endif #if FEAT_SHREC == DYNAREC_JIT #if HOST_CPU == CPU_X86 else if ( ngen_Rewrite((unat&)ep->ContextRecord->Eip,*(unat*)ep->ContextRecord->Esp,ep->ContextRecord->Eax) ) @@ -177,7 +177,7 @@ LONG ExeptionHandler(EXCEPTION_POINTERS *ExceptionInfo) #endif else { - printf("[GPF]Unhandled access to : 0x%X\n",address); + printf("[GPF]Unhandled access to : 0x%X\n",(unat)address); } return EXCEPTION_CONTINUE_SEARCH; @@ -199,8 +199,6 @@ u16 kcode[4] = { 0xffff, 0xffff, 0xffff, 0xffff }; u32 vks[4]; s8 joyx[4],joyy[4]; u8 rt[4],lt[4]; -extern bool coin_chute; -extern bool naomi_test_button; // Mouse extern s32 mo_x_abs; extern s32 mo_y_abs; @@ -211,6 +209,10 @@ extern f32 mo_wheel_delta; // Keyboard static Win32KeyboardDevice keyboard(0); + +void ToggleFullscreen(); + + void UpdateInputState(u32 port) { /* @@ -231,21 +233,16 @@ void UpdateInputState(u32 port) std::shared_ptr gamepad = XInputGamepadDevice::GetXInputDevice(port); if (gamepad != NULL) gamepad->ReadInput(); - -#if DC_PLATFORM == DC_PLATFORM_NAOMI || DC_PLATFORM == DC_PLATFORM_ATOMISWAVE - // FIXME - coin_chute = GetAsyncKeyState(VK_F8); - naomi_test_button = GetAsyncKeyState(VK_F7); -#endif } // Windows class name to register #define WINDOW_CLASS "nilDC" // Width and height of the window -#define WINDOW_WIDTH 1280 -#define WINDOW_HEIGHT 720 - +#define DEFAULT_WINDOW_WIDTH 1280 +#define DEFAULT_WINDOW_HEIGHT 720 +extern int screen_width, screen_height; +static bool window_maximized = false; LRESULT CALLBACK WndProc2(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { @@ -270,6 +267,12 @@ LRESULT CALLBACK WndProc2(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) PostQuitMessage(0); return 1; + case WM_SIZE: + screen_width = LOWORD(lParam); + screen_height = HIWORD(lParam); + window_maximized = (wParam & SIZE_MAXIMIZED) != 0; + return 0; + case WM_LBUTTONDOWN: case WM_LBUTTONUP: case WM_MBUTTONDOWN: @@ -304,8 +307,8 @@ LRESULT CALLBACK WndProc2(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) static int prev_y = -1; int xPos = GET_X_LPARAM(lParam); int yPos = GET_Y_LPARAM(lParam); - mo_x_abs = (xPos - (WINDOW_WIDTH - 640 * WINDOW_HEIGHT / 480) / 2) * 480 / WINDOW_HEIGHT; - mo_y_abs = yPos * 480 / WINDOW_HEIGHT; + mo_x_abs = (xPos - (screen_width - 640 * screen_height / 480) / 2) * 480 / screen_height; + mo_y_abs = yPos * 480 / screen_height; mo_buttons = 0xffffffff; if (wParam & MK_LBUTTON) mo_buttons &= ~(1 << 2); @@ -343,6 +346,14 @@ LRESULT CALLBACK WndProc2(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) keyboard.keyboard_input(keycode, message == WM_KEYDOWN); } break; + + case WM_SYSKEYDOWN: + if (wParam == VK_RETURN) + if ((HIWORD(lParam) & KF_ALTDOWN)) + ToggleFullscreen(); + + break; + case WM_CHAR: keyboard.keyboard_character((char)wParam); return 0; @@ -365,12 +376,13 @@ void os_CreateWindow() sWC.cbWndExtra = 0; sWC.hInstance = (HINSTANCE)GetModuleHandle(0); sWC.hIcon = 0; - sWC.hCursor = 0; + sWC.hCursor = LoadCursor(NULL, IDC_ARROW); sWC.lpszMenuName = 0; sWC.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); sWC.lpszClassName = WINDOW_CLASS; - unsigned int nWidth = WINDOW_WIDTH; - unsigned int nHeight = WINDOW_HEIGHT; + screen_width = cfgLoadInt("windows", "width", DEFAULT_WINDOW_WIDTH); + screen_height = cfgLoadInt("windows", "height", DEFAULT_WINDOW_HEIGHT); + window_maximized = cfgLoadBool("windows", "maximized", false); ATOM registerClass = RegisterClass(&sWC); if (!registerClass) @@ -380,9 +392,10 @@ void os_CreateWindow() // Create the eglWindow RECT sRect; - SetRect(&sRect, 0, 0, nWidth, nHeight); - AdjustWindowRectEx(&sRect, WS_CAPTION | WS_SYSMENU, false, 0); - HWND hWnd = CreateWindow( WINDOW_CLASS, VER_FULLNAME, WS_VISIBLE | WS_SYSMENU, + SetRect(&sRect, 0, 0, screen_width, screen_height); + AdjustWindowRectEx(&sRect, WS_OVERLAPPEDWINDOW, false, 0); + + HWND hWnd = CreateWindow( WINDOW_CLASS, VER_FULLNAME, WS_VISIBLE | WS_OVERLAPPEDWINDOW | (window_maximized ? WS_MAXIMIZE : 0), 0, 0, sRect.right-sRect.left, sRect.bottom-sRect.top, NULL, NULL, sWC.hInstance, NULL); window_win=hWnd; @@ -398,6 +411,45 @@ void* libPvr_GetRenderSurface() return GetDC((HWND)window_win); } + +void ToggleFullscreen() +{ + static RECT rSaved; + static bool fullscreen=false; + HWND hWnd = (HWND)window_win; + + fullscreen = !fullscreen; + + + if (fullscreen) + { + GetWindowRect(hWnd, &rSaved); + + MONITORINFO mi = { sizeof(mi) }; + HMONITOR hmon = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST); + if (GetMonitorInfo(hmon, &mi)) { + + SetWindowLongPtr(hWnd, GWL_EXSTYLE, WS_EX_APPWINDOW | WS_EX_TOPMOST); + SetWindowLongPtr(hWnd, GWL_STYLE, WS_POPUP | WS_VISIBLE); + + SetWindowPos(hWnd, HWND_TOPMOST, mi.rcMonitor.left, mi.rcMonitor.top, + mi.rcMonitor.right - mi.rcMonitor.left, mi.rcMonitor.bottom - mi.rcMonitor.top, + SWP_SHOWWINDOW|SWP_FRAMECHANGED|SWP_ASYNCWINDOWPOS); + } + } + else { + + SetWindowLongPtr(hWnd, GWL_EXSTYLE, WS_EX_APPWINDOW | WS_EX_TOPMOST); + SetWindowLongPtr(hWnd, GWL_STYLE, WS_VISIBLE | WS_OVERLAPPEDWINDOW | (window_maximized ? WS_MAXIMIZE : 0)); + + SetWindowPos(hWnd, NULL, rSaved.left, rSaved.top, + rSaved.right - rSaved.left, rSaved.bottom - rSaved.top, + SWP_SHOWWINDOW|SWP_FRAMECHANGED|SWP_ASYNCWINDOWPOS|SWP_NOZORDER); + } + +} + + BOOL CtrlHandler( DWORD fdwCtrlType ) { switch( fdwCtrlType ) @@ -426,12 +478,6 @@ void os_SetWindowText(const char* text) } } -void os_MakeExecutable(void* ptr, u32 sz) -{ - DWORD old; - VirtualProtect(ptr,sizeof(sz),PAGE_EXECUTE_READWRITE,&old); -} - void ReserveBottomMemory() { #if defined(_WIN64) && defined(_DEBUG) @@ -628,34 +674,65 @@ void setup_seh() { } #endif -int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShowCmd) -{ - ReserveBottomMemory(); + + +// DEF_CONSOLE allows you to override linker subsystem and therefore default console // +// : pragma isn't pretty but def's are configurable +#ifdef DEF_CONSOLE +#pragma comment(linker, "/subsystem:console") + +int main(int argc, char **argv) +{ + +#else +#pragma comment(linker, "/subsystem:windows") + +int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShowCmd) + +{ int argc=0; wchar* cmd_line=GetCommandLineA(); wchar** argv=CommandLineToArgvA(cmd_line,&argc); - if(strstr(cmd_line,"NoConsole")==0) + for (int i = 0; i < argc; i++) { - if (AllocConsole()) + if (!stricmp(argv[i], "-console")) { - freopen("CON","w",stdout); - freopen("CON","w",stderr); - freopen("CON","r",stdin); + if (AllocConsole()) + { + freopen("CON", "w", stdout); + freopen("CON", "w", stderr); + freopen("CON", "r", stdin); + } + SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE); + } + else if (!stricmp(argv[i], "-log")) + { + const char *logfile; + if (i < argc - 1) + { + logfile = argv[i + 1]; + i++; + } + else + logfile = "reicast-log.txt"; + freopen(logfile, "w", stdout); + freopen(logfile, "w", stderr); } - SetConsoleCtrlHandler( (PHANDLER_ROUTINE) CtrlHandler, TRUE ); } +#endif + + ReserveBottomMemory(); SetupPath(); -#ifndef __GNUC__ - __try -#else #ifdef _WIN64 AddVectoredExceptionHandler(1, ExeptionHandler); #else - SetUnhandledExceptionFilter(&ExeptionHandler); + SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)&ExeptionHandler); #endif +#ifndef __GNUC__ + __try #endif { int reicast_init(int argc, char* argv[]); @@ -680,6 +757,12 @@ int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine } #endif SetUnhandledExceptionFilter(0); + cfgSaveBool("windows", "maximized", window_maximized); + if (!window_maximized && screen_width != 0 && screen_width != 0) + { + cfgSaveInt("windows", "width", screen_width); + cfgSaveInt("windows", "height", screen_height); + } return 0; } @@ -722,90 +805,5 @@ void os_DoEvents() } } - - - -//Windoze Code implementation of commong classes from here and after .. - -//Thread class -cThread::cThread(ThreadEntryFP* function,void* prm) -{ - Entry=function; - param=prm; -} - - -void cThread::Start() -{ - hThread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)Entry,param,0,NULL); - ResumeThread(hThread); -} - -void cThread::WaitToEnd() -{ - WaitForSingleObject(hThread,INFINITE); -} -//End thread class - -//cResetEvent Calss -cResetEvent::cResetEvent(bool State,bool Auto) -{ - hEvent = CreateEvent( - NULL, // default security attributes - Auto?FALSE:TRUE, // auto-reset event? - State?TRUE:FALSE, // initial state is State - NULL // unnamed object - ); -} -cResetEvent::~cResetEvent() -{ - //Destroy the event object ? - CloseHandle(hEvent); -} -void cResetEvent::Set()//Signal -{ - #if defined(DEBUG_THREADS) - Sleep(rand() % 10); - #endif - SetEvent(hEvent); -} -void cResetEvent::Reset()//reset -{ - #if defined(DEBUG_THREADS) - Sleep(rand() % 10); - #endif - ResetEvent(hEvent); -} -bool cResetEvent::Wait(u32 msec)//Wait for signal , then reset -{ - #if defined(DEBUG_THREADS) - Sleep(rand() % 10); - #endif - return WaitForSingleObject(hEvent,msec) == WAIT_OBJECT_0; -} -void cResetEvent::Wait()//Wait for signal , then reset -{ - #if defined(DEBUG_THREADS) - Sleep(rand() % 10); - #endif - WaitForSingleObject(hEvent,(u32)-1); -} -//End AutoResetEvent - -void VArray2::LockRegion(u32 offset,u32 size) -{ - //verify(offset+sizesize); - verify(size!=0); - DWORD old; - VirtualProtect(((u8*)data)+offset , size, PAGE_READONLY,&old); -} -void VArray2::UnLockRegion(u32 offset,u32 size) -{ - //verify(offset+size<=this->size); - verify(size!=0); - DWORD old; - VirtualProtect(((u8*)data)+offset , size, PAGE_READWRITE,&old); -} - int get_mic_data(u8* buffer) { return 0; } int push_vmu_screen(u8* buffer) { return 0; } diff --git a/core/windows/xinput_gamepad.h b/core/windows/xinput_gamepad.h index 5aa9f70e7..beb311413 100644 --- a/core/windows/xinput_gamepad.h +++ b/core/windows/xinput_gamepad.h @@ -1,4 +1,4 @@ -#include +#include #include "input/gamepad_device.h" #include "rend/gui.h" @@ -36,6 +36,9 @@ public: XInputGamepadDevice(int maple_port, int xinput_port) : GamepadDevice(maple_port, "xinput"), _xinput_port(xinput_port) { + char buf[32]; + sprintf(buf, "xinput-%d", xinput_port + 1); + _unique_id = buf; } void ReadInput() @@ -178,7 +181,6 @@ protected: private: void do_rumble(float power) { - printf("do_rumble %f\n", power); XINPUT_VIBRATION vib; vib.wLeftMotorSpeed = (u16)(65535 * power); @@ -231,6 +233,7 @@ public: WinKbGamepadDevice(int maple_port) : GamepadDevice(maple_port, "win32") { _name = "Keyboard"; + _unique_id = "win_keyboard"; if (!find_mapping()) input_mapper = new KbInputMapping(); } @@ -257,6 +260,7 @@ public: WinMouseGamepadDevice(int maple_port) : GamepadDevice(maple_port, "win32") { _name = "Mouse"; + _unique_id = "win_mouse"; if (!find_mapping()) input_mapper = new MouseInputMapping(); } diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md new file mode 100644 index 000000000..c2295c5f6 --- /dev/null +++ b/docs/CONTRIBUTING.md @@ -0,0 +1,24 @@ +[WIP] + +## What are nillware's plans for the project? + +- We want **reicast** to be the best dreamcast emulator, available _on every possible platform_, with the source **public**, and at _no cost of the end users_. +- We are not really interested in making any money off of it, apart from guaranteeing its developers' sustainability. +- We'd really like that every regular/"important" contributor to get paid a reasonable amount of money for their work. + +## CLA Stuff +**reicast** has a CLA, which typically requires the commit author's signature. This means that **any commit returned upstream from a fork of reicast** also requires a CLA signature. +- This leaves two options for handling the commit. + 1. Either attempt to get the original author to put in the extra work of modifying and resubmitting it to reicast or + 2. alter the commit's recognized author by resubmitting it with a reference to the original commit. + +### Here's the current copyright structure of reicast +- Parts written independently (this covers over 95% of the source) are owned by nillware. +- Parts that were developed by skmp under contract, for which he doesn't have the copyright, but are licensed as GPL. +- Parts developed by the rest of the **nulldc team (ZeZu, PsyMan)**. This code is under GPL, but the copyright belongs to them. +- 3rd party contributions to nulldc, before 2008-2009. We use them under GPL. +- Contributions to the reicast project. Copyright belongs to the contributors, we use them via GPL. +- 3rd party libraries. + +We'll have to rewrite the parts that we can't get permission for though. +And be advised, we are really good at "refactoring" code! diff --git a/docs/DreamCast_Specs.md b/docs/DreamCast_Specs.md new file mode 100644 index 000000000..2d84d16c1 --- /dev/null +++ b/docs/DreamCast_Specs.md @@ -0,0 +1,22 @@ +# SEGA Dreamcast Specifications + +### CPU Specifications: +- **Processor**: Hitachi SH-4 (128-bit) +- **Processor Speed**: 200 MHz +- **Millions of instructions per second**: 360 +- **Floating-point operations per seccond**: 1.4 billion +- **Main RAM**: 16MB SDRAM + +### GPU Specifications: +- **Processor**: NEC PowerVR2 DC +- **Polygons per second**: 7.0 Million +- **Video RAM**: 8 MiB 4x16 Bit +- **Fillrate**: 100 MPixels/s + +### Audio Specifications: +- **Processor**: Yamaha AICA (32-bit) +- **Processor Speed**: 22.5 MHz +- **Co-Processor**: ARM7 RISC +- **Co-Processor Speed**: 45 MHz +- **Channels**: 64 +- **Audio RAM**: 2 MiB (16-bit) diff --git a/docs/Dynarec Architecture.md b/docs/Dynarec Architecture.md new file mode 100644 index 000000000..9b2acd359 --- /dev/null +++ b/docs/Dynarec Architecture.md @@ -0,0 +1,79 @@ +Import from skmp's old dead blog, https://web.archive.org/web/20110812025705/http://drk.emudev.org:80/blog/?page_id=18 + +Dynarec Architecture +=== + +Design document for the rec_v2 dynarec. This is WIP but i’m bored so i’l make it public :) + +The rec_v2 dynarec is designed to be modular and require minimum work for porting to different host cpus. Its also designed to be simple (simpler than v1x) to allow for simpler optimization passes and for relatively easy mitigation to ssa-based model. For reference both v1 and v2 are based on an IL that is kinda independent from both the source and destination arch (i’l call it shil from now on). + +Some history first +--- + +The original rec_v1 for mainline was designed for translation to x86 (and only that). The shil opcodes are more-or-less x86 opcodes but use sh4 registers. Flags can be saved/set with two opcodes (SaveT and LoadT) to the T register using any of the SetCC conditions. Other special opcodes are memory opcodes. These were expanded to memory moves/calls depending on a 16bit lookup table after calculating the memory address.Loads from known memory addresses were converted to direct calls/moves depending on their location. Register allocation was dynamic for 4 registers (ebp,esi,edi,ebx) and fixed for the entire block. This was the dynarec used on the nullDC 1.0.0 beta 1 release. + +For nullDC 1.0.0 b1.5/1.6 rec_v1x (x for extended :p) was used. It had a much more complex memory mapping scheme allowing for direct (no-lookup) memory access for any buffer mapped (ram/vram/aram). It also included code to more accurately detect memory constants and convert em to direct moves, as well as memory opcode ‘patching’ using exceptions to ensure correct logical flow in the case on an unpredicted memory access. X86 page protection and windows file mapping were the base for all that stuff. There were also many other fixes and optimizations such as the addition of a multilevel call stack cache, indirect jump/calls multilevel prediction and other fun stuff, mainly targeting the ‘glue’ code of the dynarec.Also a lot of fat was cut down from the block prologue/epilogue (like not loading registers when their initial value isn’t writen, smaller code for interrupt checks, etc) + +nullDC 1.0.3 used r1x with some more work to implement more sh4 opcodes (like pref for faster SQ writebacks) and some heuristics to detect blocks that didn’t work with constant-memory-read-to-constant forwarding.As far as i remember there weren’t any remarkable changes apart from that :p + +nullDC 1.0.4 added support for block relocation to better utilize/organize dynarec memory + +Some good things about v1/v1x +--- +- It is fast. On a core2-level cpu it gets 1:7 to 1:1.2 sh4:x86 cycle ratio.It generates about 30 bytes per sh4 opcode (including all linking/block overhead). The x86 code executes at around 1.2 opcodes per cycle. +- It is fairly compact and modular (~ 3k lines for the il compiler, around 10k lines for most of the important code). Its logically organised into the block manager, the dynarec driver, the sh4 analyzer/decoder, the optimizer and the il-compiler. It also supports super-blocks and hot-spot detection but these features were never used. +- A full block graph is used so blocks can be easily and safely removed undoing any static linking between em :) +- Full support for loaded/smc code with page protection and block verification (similar to the system i implemented for pcsx2) + +Some bad things +--- +- It's modeled around x86 (ie, x86 flags are supported and can be accessed any time with the SaveT/LoadT opcodes). While this allows a cheap way to generate fast code it makes interpretation of the IL almost impossible.It also makes porting to flag-less cpu architectures much much much much harder. +- It’s not as compact as it could be +- It doesn’t provide a fallback for its opcodes +- It doesn’t use 3-way opcodes (these can simplify optimization a lot on the IL level) +- The full block graph is a bit memory intesive + +When the psp port started there was the need for some dynarec that could easyly run on both x86 and mips and share as much code as possible to catch bugs on the easy & nice development platofrm (pc). This after some rewrites resulted to the rec_v2 as it is presented on this document :) + +rec_v2 features +--- +- A very modular design (frontend/decoder are data driven, the block manager is simple, the compiler is tiny) +- ALL platform dependant code is isolated with the ngen interface (native generator) +- There is no block graph or block descriptors, each address is mapped to a single code pointer. +- Heuristic based support for code modifications, as well as icache flush support. Single block invalidation isn’t possible leading to a much simpler model for the dynarec +- A default implementation for most of the dynarec opcodes (only 7 opcodes _must_ be provided natively) +- All opcodes have fixed input and output fields, with full description on which physical registers they use so data flow computation is simple. +- Support for x86, mips, Cortex-A8 and powerpc ! + +Well, there are a few drawbacks to the design as wel +--- +- Block modification needs special detection for some games (like Doa2le) +- The dynarec cache can be flushed only ‘at once’ and not at block level. This isn’t usualy a problem +- It’s nowhere as complete as rec_v1 (but its slowly getting there !) +- It’s noticeably slower than rec_v1, up to 3x on some points. Build in idle loop detection makes that problem less visible and more work on the opcodes will make it faster + +The shil format +--- +Shil (for sh il) is the fully ‘expanded’ form of the opcodes that the dynarec uses internally. Its a simple struct with 2 possible outputs (rd and rd1) and 3 possible inputs (rs1,rs2,rs3). Each input can be an register or a 32 bit imm. Currently there are C implementations for almost all the shil opcodes (only 7 opcodes are required to be implemented by the ngen backend).There were in total 36 shil opcodes last time i counted em ;) + +Sh4 decoder (dec_*) +--- +The sh4 decoder gets a stream of sh4 opcodes and converts em to shil. The decoder supports both ‘native’ opcodes (they have a function to handle the translation) and a data driven mapping (this part is really hacky). The decoder currently stops at a branch or when a maximum amount of cycles is reached. While the code is a bit overcomplicated the uglyness is self-contained as the shil format is fairly clean and nice :) + +Shil optimiser +--- +The shil optimiser gets the block as output from the decoder and performs some basic optimisations.Currently it only does dead code elimination. + +Native generator/Shil compiler (ngen_*) +--- +The compiler is invoked to compile a block after it has been processed by the optimiser. The compiler is isolated from the rest of the code with the ngen interface (ngen_* are supplied by each dynarec implementation). It communicates with the dynarec driver and the block manager to complete the task. The compiler usualy consists of a case() to implement the opcodes, some code to handle block header/footer, glue code for the dynarec driver and the dynarec mainloop. This is the only cpu isa depedant part of the dynarec. + +Currently the x86 implementation is around 900 lines, the psp one 1300 (but includes a lot of emitter helpers) the arm one around 600 (but misses many opcodes) and ppc around 400 (but its not working reliably yet) + +Block Manager (bm_*) +--- +The block manager is repsosible for keeping the sh4 addr <-> code mappings and replying to queries about em to the dynarec code. It has a full implementation in C but some parts can be also implemented as part of the dynarec loop to improve performance. Right now it uses cached hashed lists to do the mappings. While the bm is fairly fast (the cache takes care of that) the big idea is that it shound’t be used a lot as extensibe prediction can remove most of the queries :) + +Dynarec Driver (rdv_*) +--- +Its the small glue code that ‘drives’ the emulation and provides functions needed by the ngen interface. Generally any function that doesn’t belong clearly to one of the other parts and it is portable across all shil compilers ends up in here :) diff --git a/docs/Naomi_Overview.md b/docs/Naomi_Overview.md new file mode 100644 index 000000000..1233dd209 --- /dev/null +++ b/docs/Naomi_Overview.md @@ -0,0 +1,43 @@ +## Presentation: +- First demonstrated in November 1998 at JAMMA, since just before the release of The House of the Dead 2 in Japan. +- The Sega Naomi (New Arcade Operation Machine Idea) is the successor to the Sega Model 3 hardware. + +### A development of the Dreamcast home game console, the NAOMI and Dreamcast share the same hardware components: +- Hitachi SH-4 CPU, +- PowerVR Series 2 GPU (PVR2DC), and +- Yamaha AICA based sound system. + +NAOMI has twice as much system memory, twice as much video memory, and 4X as much sound memory. +Multiple NAOMI boards can be 'stacked' together to improve graphics performance, or to support multiple-monitor output. +A special game cabinet for the NAOMI, **NAOMI Universal Cabinet**, houses up to sixteen boards for this purpose. + +_The other key difference between NAOMI and Dreamcast lies in the game media._ + +The Dreamcast reads game data from GD-ROM optical disc, while the NAOMI arcade board features 168 MB of solid-state ROMs or GD-ROMs using a custom DIMM board and GD-ROM drive. + +- In operation, the Naomi GD-ROM is read only once at system power up, loading the disc's contents to the DIMM Board RAM. +Once loading is complete, the game executes only from RAM, thereby reducing mechanical wear on the GD-ROM drive. +Unlike Sega's previous arcade platforms (and most other arcade platforms in the industry), + +NAOMI is widely licensed for use by other game publishers including Sega, Namco Bandai, Capcom, Sammy and Tecmo Koei. +Games such as Mazan, Marvel Vs. Capcom 2, Dead or Alive 2 and Guilty Gear XX were all developed by third-party licensees of the NAOMI platform. + +**An offshoot version of the NAOMI hardware is Atomiswave by Sammy Corporation.** + +After nine years of hardware production, and with new game titles coming in 2008 like Melty Blood: Actress Again and Akatsuki Blitzkampf AC, +NAOMI is considered to be one of the longest running arcade platforms ever and is comparable in longevity with the Neo-Geo MVS. + + +## Technical Specification: + +- Main Processor : Hitachi SH-4 (128 bits) RISC 360 MIPS / 1.4 GFLOPS clocked 200 MHz +- Video Processor : PowerVR 2 clocked 100 MHz made by Nec (Nec CLX2) + - Texture mapping + - Trilinear filtering + - Micro texturing + - Specular reflection + - Gouraud shading + - Flat shading + - Anti-aliasing + - Alpha blending +- Audio Processor : ARM7 Yamaha XG AICA RISC 32 bits clocked 25 MHz diff --git a/docs/Notable game bugs.md b/docs/Notable game bugs.md new file mode 100644 index 000000000..84dcbaf59 --- /dev/null +++ b/docs/Notable game bugs.md @@ -0,0 +1,100 @@ +Somehwat outdated. Imported from skmp's old dead blog, https://web.archive.org/web/20110809053548/http://drk.emudev.org/blog/?page_id=4 + +List of bugs and fixes +==== + +Its been a while since i wanted to make a list of known bugs with known fixes/hacks/debug states to keep as a reference for future bug checking/help other people ;) After many delays here it is ! Most of these bugs are nullDC/Dreamcast emulation related, but they may apply to other emulators as well.If you know a bug that is not on the list contact me to add it (sources will be credited, unless otherwise mentioned i found/debugged em :) ) + +Also check PsyMan’s list, i don’t have his fixes duplicated here :) + +Last update : 25/03/2009 + +- Thps2 locks with a black screen : Pending interrupt never gets a chance to raise.Thps2 enables interrupts on the status register, and shortly after disables em.Make sure to check for interrupts on writes to the status register.Also it uses Yuv decoder on TA, make sure its emulated. +- Echelon intros +-- The logo on the DOA2LE intro rotates wrongly : fpu double opcodes(used to build the sin/cos table on the intro) are wrong (remember, they are stored on hi:low order on register pairs).Check fabs.d and fneg.d. +-- Some intros abuse Cache, and expect it to maintain the mem data when mem is updated externaly (using the non cached mirror).Emulating the cache side effects or skiping the routines that do that fixes the problem.The values to patch are 0x8c01313a:0x490b=0×0009 and 0x8c0130f8:0x490b=0×0009 (thanks CaH4e3 for the patches !).Also, invalidating the code cache only on icache sync opcodes propably will fix the problem. +-- Framebuffer emulation doesn’t detect size properly: When interlacing is disabled, the screen height varies based on the video clock divider (iirc its on FB_R_*). +- Sonic Adventure 2, +-- Characters collide ‘oddly’ with the walls : fpu code doesnt properly handle nans/infinitives on compares.Check fcmp/eq/gt. +-- Random polygons appear all around : SA2 sends polygons with x/y/z +/- infintive.Different cards handle em differently.Checking manualy for em and removing em fixes the problem :) +- Ikaruga +-- Ika and Many newer jap games wont work : Newer versions of the katana sdk use mmu to remap the SQ write-back space.Thankfully, 1mb pages are used so the implementation is quite fast (i use an 64 entry remap table).Only SQ writes are mapped, mem accesses use the non-mmu mapped mirror. +-- (Some) Bullets are invisible on level 2 : The FSCA opcode is not accurate, it is expected to return 0 (exactly) for cos(90)/sin(180).Its a good idea to dump the fsca table from a dreamcast and use that, its just 128 kb :) . +- Some games have clicking on the streaming music (SA2, Crazy Taxi, thps1/2, more …) : The sound decompression code on the sh4 uses ftrc (float -> int) to convert the samples.Make sure you saturate the value when converting (x86 defaults to using 0×8000 0000 for values that dont fit). +- Test Drive Le Mans 24h, OVERLOAD shows up and game won’t progress : The game checks the power usage of maple devices.Make sure you return valid values for the current (mA) usage on the get device info command ;) +- Linux/dc locks after doing a lot work (including user space, i cant exactly remember where atm) .Also netbsd/dc locks after initing aica : Check the gdrom code, on nullDC a bug made read aborts to raise interrupts for ever. +- Netbsd crashes as soon as it boots: Make sure you have connected a keyboard, it needs it =P. +- Soul Reaver has no 3d ingame yet you can hear the sound and menus work : Check mac.w, clrmac(also check mac.l, C has a nasty habit of doing 32 bit muls unless you cast to 64 bits before the mul). +- Backgrounds on Soul Calibur seem to use wrong size textures/textures on SC seem to get only some colors right : Soul Calibur does varius tricks to optimise vram usage.It uses textures with different data fortmats ‘overlapping’ on memory (to take advantage of unused regions on a teuxtre and store other textures there).It also uses varius sizes of the same data.The fix is to identify a texture by the tcw:tsp pair , not just tcw/start address. +- Resident Evil 3 loops after starting a new game but doesnt hang : It uses rtc to time the intro text, emulate it =P. +- Sonic Shuffle locks after stating a new game : Emulate/Check PVR-DMA transfers & interrupt. +- Crazi Taxi locks after some time (~30 secs) of gameplay, music gets corrupted right before the lockup : Its AEG emulation on aica, fix/hack it to return proper values. +- Gauntlet Legends locks while loading first level, Mat Hoffman BMX locks on logo, more locks on places that loading happens : These games use multy trasnfer pio on gdrom to trasnfer > 64kb on one PIO request.Emulate that :) +- Many games run faster than normal, including RE:CV, Time Stalkers : Make sure to emulate the field register on SPG/Interlaced mode. +- Time Stalkers music is wrong , especialy when hi-pitched : This game does synth using adpcm sound bank(s).Make sure adpcm is decoded correctly on hi sample rates (all adpcm samples must be decoded no matter the pitch). +- Some capcom games(mvsc2,more) run slow using normal TMU timings : Capcom game engine expects pvr rendering to take a while(well, some versions of it at least).Make sure you have a delay betwen the StartRender write and the pvr end of render interrupts, that wll fix it.A common way to hack this is to edit tmu timings (like capcom hack on chankast). +- Sword of the Berserk, the ingame sfx stop working after a few seconds : When looping is disabled on an aica channel the loop end flag must still be set properly.SOTB ‘free’ channel detection code requires that to work. +- Resident Evil Code Veronica, the statue wont turn; Soul Calibur , Yoshimitsu exhibition mode crashing at fade out : The speed of gdrom transfers needs to be limited to about 1MB/s for these games to work +- Prince Of Persia 3D : the textures are totaly wrong: Check the dynamic shift opcodes, the game depends on only using the lower 5 bits. +- Skies Of Arcadia crashes “randomly” after exiting the ingame menu: For some reason, everytime that you exit it writes stuff to the flash.When the flash is full, it clears a sector.Make sure the flash clear command is handled properly. + + +PsyMan's List + +Last update 9/22/09 + +» Beats of Rage: + +Problem: Code execution seems to loop after the Sega license screen. Game appears to be frozen. + +Cause: It starts multiple maple DMA transfers before the previous ones end. + +Solution: Make sure to allow that instead of ending each DMA before starting the other. + +» Record of Lodoss War: + +Problem: Code execution seems to loop after beating certain enemies or getting certain items, etc. Game appears to be frozen. + +Cause: Division by zero. + +Solution: Handle division by zero on the related Div μcode. + +» D2: + +Problem: Disc swapping does not work on this game. + +Cause: GD-ROM error control is missing. + +Solution: Implement GD-ROM error control. Another option is to hack things up to generate an error and return the appropriate sense key (6h) and sense code (28h) when error info is requested. + +» Dreamcast BIOS: + +Problem: Dreamcast logo appears as if the tray is open when the tray is closed while the emulators starts and there is no disc in the drive. It should appear as when there is a disc in the drive. + +Cause: GD-ROM error control is missing. + +Solution: Implement GD-ROM error control. Another option is to hack things up to generate an error and return the appropriate sense key (6h) and sense code (29h) when error info is requested. + +» Various games, VMU/Memory card: + +Problem: Various games fail to detect the connected memory card(s). + +Cause: There can be various causes for that. + +Some games (ie: Dynamite Cop) expect that an LCD and/or a real time clock is present on the storage device (”simple” memory cards lack those two, VMUs have them). + +Some games (ie: Spawn) look for a very specific device name for the connected VMUs (”Visual Memory”, padded with spaces having a specific string length) and will fail to detect the connected memory cards if the name is different. + +Some WinCE games seem to do VMU detection using a timer. More on that later. + +Solution: Be sure to give correct VMU device information. More on the timer thing some other time. :p + +» Various games/Disc Swapping: + +Problem: Disc swapping fails for some games, “fixing” it breaks other games (ie: Swapping on Utopia Boot Disc works but fails on Xploder DC, or works for Xploder DC and fails on Utopia Boot Disc). + +Cause: The state of the drive is wrong, it seems that after the command 71h the drive state ends up being set to “pause” for GD-ROMs and to “standby” for the rest disc types. + +Solution: Be sure to set the drive state accordingly. Figuring out how the responses on command 71h are generated and why the drive state ends up like that is a plus. :p + +More to come… diff --git a/docs/Release schedule and structure.md b/docs/Release schedule and structure.md new file mode 100644 index 000000000..9e384e02c --- /dev/null +++ b/docs/Release schedule and structure.md @@ -0,0 +1,26 @@ +Release Naming +--- +Our release names are time-based and follow a `yy.mm(.respin)` notation. +Eg `Release 18.10` should happen in October of 2018. +Hotfix/respins are denoted by an increasing trailing number if needed. Eg `Release 18.10.1`, `Release 18.10.2` and so on. + +Release Schedule +--- +We aim for a release on the first Monday of each month. This is manual right now, but ideally, it will be automated. +Monday was selected because emulator use peaks on the weekends, so we have 5 days to fix things up/rollback if a bad release happens. + +Release Planning +--- +We have milestones for 3 releases ahead, then [Mid Term Goals](https://github.com/reicast/reicast-emulator/milestone/4) for 3-9 months ahead, and [Long Term Goals](https://github.com/reicast/reicast-emulator/milestone/2) for 9+ months ahead. + +Tickets are assigned to milestones based on a combination of feature planning and developer availability. + +Feature "Freeze" Windows +--- +We do a soft "feature freeze" on the week before release, to allow for the beta builds to be tested. +During this window, experimental/untested changes should not be merged. + +Release Testing / QA +--- +We depend on the public beta and people complaining right now. +If you're interested to do QA testing around releases, please let us know in #1225, and/or join our [discord](http://chat.reicast.com) diff --git a/docs/neil_corlett_aica_notes.txt b/docs/neil_corlett_aica_notes.txt new file mode 100644 index 000000000..3e79a20a6 --- /dev/null +++ b/docs/neil_corlett_aica_notes.txt @@ -0,0 +1,1066 @@ +----------------------------------------------------------------------------- +Neill Corlett's Yamaha AICA notes +Based on: +- The official AICA spec +- Yamato's aica_v08.txt (yamato@20to4.net) +- Much experimentation +----------------------------------------------------------------------------- + +LOGARITHMIC VOLUMES + +Like many Yamaha sound chips, the AICA uses logarithmic volume levels. +Here's how that works: + +A value of 0 means no attenuation, or full volume. From there, every +increment of a constant amount decreases the volume level by 3dB. Of course, +"3dB" in the literature is just a fancy way of saying "half". And the values +aren't logarithmic at all, but a log/linear approximation. + +To convert a "logarithmic" attenuation level to a volume multiplier: +1. You first need to know what increment constitutes a 3dB decrease. We'll + call this I. This is going to always be a power of 2. +2. Divide (by which I mean shift) the attenuation level by this constant to + get the "exponent", and the remaining lowest bits are the "mantissa". +3. Invert all bits in the mantissa, then add I to it. +4. Shift the mantissa left to whatever precision you want. +5. Shift the mantissa right by the exponent. + +One of the advantages of logarithmic attenuation levels is that you can apply +several volumes on top of each other by adding them, and this has the same +effect as if you'd multiplied the linear volume levels together. I suspect +this is what's happening, though I haven't tested specifically for it yet. + +----------------------------------------------------------------------------- + +RESAMPLING + +...appears to be linear. + +----------------------------------------------------------------------------- + +AMPLITUDE ENVELOPE CALCULATION + +This is a typical 4-stage envelope: Attack, Decay, Sustain, Release. The +envelope gets louder during attack, and can only remain the same or quieter +during Decay, Sustain, and Release. (There is no increase sustain mode.) + +The envelope uses a logarithmic attenuation level where 0 means no +attenuation (full volume) and every increment of 0x40 decreases the volume +by 3dB. The level can range from 0x000 to 0x3BF. Note that if the +attenuation level exceeds 0x3BF _at any time_, the channel will become +inactive and the attenuation level will appear as 0x1FFF. + +"EFFECTIVE RATE" refers to the following: +- If KRS == 0xF: EFFECTIVE RATE = RATE*2 +- If KRS < 0xF: EFFECTIVE RATE = (KRS+OCT+RATE)*2+FNS +(where FNS is bit 9 of SampleRatePitch) +EFFECTIVE RATE is truncated to the range of 0x00-0x3C if necessary. + +After a key on, the attack attenuation level begins at 0x280. Note that a +key on event is completely ignored unless the channel is either inactive +(attenuation level exceeds 0x3BF) or is presently in the release state. + +During attack, subtract ((currentlevel >> N)+1) from the current level at +each step, where N is the following: + + STEP mod 4 is +EFFECTIVE RATE 0 1 2 3 +------------------------------------ +0x30 or less 4 4 4 4 +0x31 3 4 4 4 +0x32 3 4 3 4 +0x33 3 3 3 4 +0x34 3 3 3 3 +0x35 2 3 3 3 +0x36 2 3 2 3 +0x37 2 2 2 3 +0x38 2 2 2 2 +0x39 1 2 2 2 +0x3A 1 2 1 2 +0x3B 1 1 1 2 +0x3C or greater 1 1 1 1 + +Once the envelope level reaches 0, transition to the decay state. + +Decay is linear in the attenuation level. Simply add the following constants +to the attenuation level at each step: + + STEP mod 4 is +EFFECTIVE RATE 0 1 2 3 +------------------------------------ +0x30 or less 1 1 1 1 +0x31 2 1 1 1 +0x32 2 1 2 1 +0x33 2 2 2 1 +0x34 2 2 2 2 +0x35 4 2 2 2 +0x36 4 2 4 2 +0x37 4 4 4 2 +0x38 4 4 4 4 +0x39 8 4 4 4 +0x3A 8 4 8 4 +0x3B 8 8 8 4 +0x3C or greater 8 8 8 8 + +The step timing, for both attack and decay, is as follows: + +EFFECTIVE RATE Step occurs every N samples +-------------------------------------------------- +0x00 Never +0x01 Never +0x02 8192, 4096, 4096 +0x03 8192, 4096, 4096, 4096, 4096, 4096, 4096 +0x04 4096 +0x05 4096, 4096, 4096, 2048, 2048 +0x06 (same as 0x02 shifted right by 1) +... ... +0x30 and up 2 + +During decay, when the attenuation level reaches the "decay level", defined +as DL << 5, transition to the sustain state. + +Sustain follows the same formula as decay and continues until either the +envelope attenuation level exceeds 0x3BF, or a key off event occurs. + +After a key off, transition to the release state. Release also follows the +same formula as decay. When the envelope attenuation level exceeds 0x3BF, +the channel becomes inactive. + +If "voff" is set, then this envelope is still computed, but the attenuation +has no effect. However, the channel will still end abruptly once the level +exceeds 0x3BF. + +----------------------------------------------------------------------------- + +FILTER ENVELOPE CALCULATION + +Filter envelopes follow the same step timings as amplitude envelopes (key +scaling is also shared between filter and amplitude), and likely the same +step sizes as in the decay phases of amplitude envelopes (based on a few +trials). + +The only difference is that instead of an attenuation level, it uses a 13-bit +lowpass value. + +- Start at FLV0 +- Attack: Add or subtract the attack step size until you reach FLV1 +- Decay: Add or subtract the decay (1) step size until you reach FLV2 +- Sustain: Add or subtract the sustain (2) step size until you reach FLV3 +- Wait for a key off +- Release: Add or subtract the release step size + +If "lpoff" is set, then this envelope is still computed, but the filter +itself has no effect. + +----------------------------------------------------------------------------- + +LOW FREQUENCY OSCILLATOR (LFO) + +There are 6 components to the LFO: + +- The "reset" bit, meaning the LFO phase is zeroed on every sample loop start +- The LFO frequency (0x00-0x1F) +- Pitch modulation waveform type +- Pitch modulation depth +- Amplitude modulation waveform type +- Amplitude modulation depth + +Here is the LFO period, in samples, for every frequency value: + +f=0x00:0x3FC00 f=0x08:0x0FC00 f=0x10:0x03C00 f=0x18:0x00C00 +f=0x01:0x37C00 f=0x09:0x0BC00 f=0x11:0x03400 f=0x19:0x00A00 +f=0x02:0x2FC00 f=0x0A:0x0DC00 f=0x12:0x02C00 f=0x1A:0x00800 +f=0x03:0x27C00 f=0x0B:0x09C00 f=0x13:0x02400 f=0x1B:0x00600 +f=0x04:0x1FC00 f=0x0C:0x07C00 f=0x14:0x01C00 f=0x1C:0x00400 +f=0x05:0x1BC00 f=0x0D:0x06C00 f=0x15:0x01800 f=0x1D:0x00300 +f=0x06:0x17C00 f=0x0E:0x05C00 f=0x16:0x01400 f=0x1E:0x00200 +f=0x07:0x13C00 f=0x0F:0x04C00 f=0x17:0x01000 f=0x1F:0x00100 + +LFO pitch depth: + +The pitch can vary by a maximum of plus or minus (base phaseinc)>>(10-d) +d=0: do not apply pitch modulation +d=1: base phaseinc of 0x40000 can sway from 0x3FE00 to 0x40200 +... +d=7: base phaseinc of 0x40000 can sway from 0x38000 to 0x48000 + +LFO pitch waveform: + +0: sawtooth, starting in the middle, going higher +1: square, high pitch for first half, low pitch for second half +2: triangle, starting in the middle, going higher, much like sin(x) +3: noise + +LFO amplitude depth: + +Amplitude modulation adds positive values to the envelope attenuation, so it +can only make the resulting output softer, not louder. +d=7: can add up to 24dB or 0x200 to envelope attenuation +d=6: can add up to 12dB or 0x100 to envelope attenuation +d=5: can add up to 6dB or 0x080 to envelope attenuation +d=4: can add up to 3dB or 0x040 to envelope attenuation +d=3: can add up to 0x020 to envelope attenuation +d=2: can add up to 0x010 to envelope attenuation +d=1: can add up to 0x008 to envelope attenuation +d=0: do not apply amplitude modulation + +LFO amplitude waveform: + +0: sawtooth, starting at 0 (no attenuation), going up (more attenuation) +1: square, 0 for first half, max attenuation second half +2: triangle, starting at 0, going down then up +3: noise + +LFO phase resets on every sample loop start if the reset bit is set. +It never resets in any other circumstances (not even on key on). + +----------------------------------------------------------------------------- + +RESONANT LOWPASS FILTER + +If lpoff=0, the channel output is run through a resonant lowpass filter. The +algorithm works like this: + +out = f * in + (1.0 - f + q) * prev_out - q * prev_prev_out + +Where f and q are coefficients. Exactly how f and q are computed, I'm still +not 100% sure. I have a fairly good idea for f: + +f = (((filtervalue & 0xFF) | 0x100) << 4) >> ((filtervalue>>8)^0x1F) + +Where filtervalue is the filter envelope level, and f becomes a linear value +in the range of 0x0000 (0.0) to 0x1FFF (almost 1.0). + +I'm less certain about q. For now, I intend to simply use the Q register +value, where 0x00=0.0 and 0x20=1.0 (max is 0x1F). + +----------------------------------------------------------------------------- + +ADPCM SAMPLE FORMAT + +The ADPCM sample format is standard Yamaha ADPCM. It's the same format used +in the YMZ280B. + +----------------------------------------------------------------------------- + +REGISTERS + +The AICA registers are accessible starting at 0x800000 on the ARM7 side and +0xA0700000 on the SH4 side. Register addresses in this document will usually +refer to an offset from either 0x800000 or 0xA0700000. + +There are 64 channels and a set of registers associated with each, a set of +common registers, and a set of registers associated with the effects DSP. + +----------------------------------------------------------------------------- + +CHANNEL REGISTERS + +The area at 0x0000-0x1FFF contains the channel registers. There are 32 +32-bit registers for each channel, but only the first 18 are used, and only +the lowest 16 bits have any effect. + +Channel 0's registers start at 0x0000, channel 1's at 0x0080, and so on. + +All register writes take effect immediately. NONE of the channel registers +are temporarily stored until key-on (as was the case on the PSX SPU). + +When you read from a channel register, you'll simply get the last value that +was written to it, EXCEPT for bits which are not saved (always return 0). +These bits are clearly labeled below. + +----------------------------------------------------------------------------- +0x00 - PlayControl + +bit 15 = key on execute (KYONEX) [NOY SAVED, always 0 when read] + Writing a 1 bit here will execute a key on for every channel that + has KYONB=1, and a key off for every channel that has KYONB=0, + simultaneously. There's no way to single out individual + channels. (Though it works out since redundant key on/key off + events are ignored.) +bit 14 = key on bit (KYONB) + Set this to either 1 or 0 to "mark" a channel for key on or off. +bit 13-11 = unused [NOT SAVED, always 0 when read] +bit 10 = noise enable (SSCTL) + 0 means read sample data from RAM, as per usual + 1 means use random data instead of actually reading RAM. + This seems to only affect what bytes are read. If you have the + sample format set to 16-bit, you'll get random 16-bit samples. + If you have the format set to ADPCM, you'll get random ADPCM + samples, but I think they're still run through the ADPCM decoder. +bit 9 = sample loop (LPCTL) 1=enabled, 0=disabled +bit 8-7 = sample format (PCMS) + 0 = 16-bit signed little-endian + 1 = 8-bit signed + 2 = 4-bit Yamaha ADPCM + 3 = prohibited + Actual behavior seems to be "sample data is all zeroes". +bit 6-0 = highest 7 bits of sample start address (SA) (in bytes) + +----------------------------------------------------------------------------- +0x04 - SampleAddrLow + +bit 15-0 = lowest 7 bits of sample start address (SA) (in bytes) + +----------------------------------------------------------------------------- +0x08 - LoopStart + +bit 15-0 = Loop start address (in samples) + Very low values of this may not work depending on the pitch and + loop mode. + +----------------------------------------------------------------------------- +0x0C - LoopEnd + +bit 15-0 = Loop end address (in samples) + If sample looping is disabled, this acts as the total sample size. + Very high values may not work depending on the pitch and loop + mode. + Also, you can't set this to 0xFFFF if you expect the interpolation + to work. + +----------------------------------------------------------------------------- +0x10 - AmpEnv1 + +bit 15-11 = sustain rate (D2R) rate, 0x00-0x1F +bit 10-6 = decay rate (D1R), 0x00-0x1F +bit 5 = unused [NOT SAVED, always 0 when read] +bit 4-0 = attack rate (AR), 0x00-0x1F + +----------------------------------------------------------------------------- +0x14 - AmpEnv2 + +bit 15 = unused [NOT SAVED, always 0 when read] +bit 14 = link (LPSLNK), 1=on 0=off + If this bit is set, then the envelope transitions to the decay + state when the sample loop start address is exceeded. +bit 13-10 = key rate scaling (KRS), 0x0-0xF + See the amplitude envelope notes for details on this. + 0xF means that all scaling is OFF. +bit 9-5 = decay level (DL), 0x00-0x1F +bit 4-0 = release rate (RR), 0x00-0x1F + +----------------------------------------------------------------------------- +0x18 - SampleRatePitch + +bit 15 = unused [SAVED] +bit 14-11 = octave (OCT), signed value, -0x8 to 0x7 +bit 10-0 = FNS + +This is basically a phase increment value, just computed in a peculiar way. +phaseinc = (FNS ^ 0x400) << ((unsigned OCT value) ^ 0x8) +There's 18 fractional phase bits, so phaseinc=0x40000 is the base frequency, +or 44100Hz. + +Supposedly if the signed OCT value is 2 or higher, and the sample format is +ADPCM, the actual octave is one higher. + +----------------------------------------------------------------------------- +0x1C - LFOControl + +bit 15 = reset (LFORE) 1=on, 0=off + If set, the LFO phase is reset at the start of EACH SAMPLE LOOP. +bit 14-10 = frequency (LFOF) +bit 9-8 = pitch modulation waveform (PLFOWS) +bit 5-7 = pitch modulation depth (PLFOS) +bit 4-3 = amplitude modulation waveform (ALFOWS) +bit 2-0 = amplitude modulation depth (ALFOS) + +See the LFO notes for more details. + +----------------------------------------------------------------------------- +0x20 - DSPChannelSend + +bit 15-8 = unused [NOT SAVED, always 0 when read] +bit 7-4 = DSP send level, 0x0-0xF + Scales the output of this channel to one of the effect send buses. + 0xF is full volume (no attenuation), every level beneath that adds + 3dB, and 0x0 means no output. +bit 3-0 = DSP send channel, 0x0-0xF + This affects which DSP MIXS register will receive this channel's + output. + I have verified that bit 19 of MIXS corresponds to bit 15 of a + single 16-bit sample played at the maximum possible volume. + +----------------------------------------------------------------------------- +0x24 - DirectPanVolSend + +bit 15-12 = unused [SAVED] +bit 11-8 = Direct send volume (DISDL), 0x0-0xF + Affects how much of this channel is being sent directly to the + dry output. 0xF is full volume (no attenuation), every level + beneath that adds 3dB, and 0x0 means no output. (I have verified + this.) +bit 7-5 = unused [SAVED] +bit 4-0 = Direct send pan (DIPAN), 0x00-0x1F + 0x00 or 0x10 is center, 0x0F is full right, 0x1F is full left + 0x00-0x0F: each step beyond 0x00 attenuates the left side by 3db + (right side remains at same volume) + 0x10-0x1F: each step beyond 0x10 attenuates the right side by 3db + (left side remains at same volume) + +----------------------------------------------------------------------------- +0x28 - LPF1Volume + +bit 15-8 = constant attenuation (I think this may be TL) + this value *4 seems to be added to the envelope attenuation + (as in, 0x00-0xFF here corresponds to 0x000-0x3FF when referring + to the envelope attenuation) + every 0x10 on the constant attenuation is 3dB +bit 7 = unknown [SAVED] +bit 6 = voff + if this bit is set to 1, the constant attenuation, envelope, and + LFO volumes will not take effect. however, the note will still + end when the envelope level reaches zero in the release state. +bit 5 = lpoff + 1 = turn off lowpass filter. +bit 4-0 = Q, 0x00-0x1F + filter resonance value Q = (0.75*value)-3, from -3 to 20.25 db + 0x1F = 20.25dB + 0x1C = 18dB + 0x18 = 15dB + 0x10 = 9dB + 0x0C = 6dB + 0x08 = 3dB + 0x04 = 0dB + 0x00 = -3dB + +----------------------------------------------------------------------------- +0x2C - LPF2 + +bit 15-13 = unused [NOT SAVED, always 0 when read] +bit 12-0 = filterValue0 (FLV0): starting filter frequency + attack start + +----------------------------------------------------------------------------- +0x30 - LPF3 + +bit 15-13 = unused [NOT SAVED, always 0 when read] +bit 12-0 = filterValue1 (FLV1): stage 1 filter frequency + decay start time + +----------------------------------------------------------------------------- +0x34 - LPF4 + +bit 15-13 = unused [NOT SAVED, always 0 when read] +bit 12-0 = filterValue2 (FLV2): stage 2 filter frequency + sustain start time + +----------------------------------------------------------------------------- +0x38 - LPF5 + +bit 15-13 = unused [NOT SAVED, always 0 when read] +bit 12-0 = filterValue3 (FLV3): stage 3 filter frequency + KeyOff time + +----------------------------------------------------------------------------- +0x3C - LPF6 + +bit 15-13 = unused [NOT SAVED, always 0 when read] +bit 12-0 = filterValue4 (FLV4): release filter frequency + +----------------------------------------------------------------------------- +0x40 - LPF7 + +bit 15-13 = unused [NOT SAVED, always 0 when read] +bit 12-8 = LPF attack rate (FAR), 0x00-0x1F +bit 7-5 = unused [NOT SAVED, always 0 when read] +bit 4-0 = LPF decay rate (FD1R), 0x00-0x1F + +---------------------------------------------------------------------------- +0x44 - LPF8 + +bit 15-13 = unused [NOT SAVED, always 0 when read] +bit 12-8 = LPF sustain rate (FD2R), 0x00-0x1F +bit 7-5 = unused [NOT SAVED, always 0 when read] +bit 4-0 = LPF release rate (FRR), 0x00-0x1F + +----------------------------------------------------------------------------- +0x48 to 0x7C - unused + +----------------------------------------------------------------------------- + +COMMON REGISTERS + +----------------------------------------------------------------------------- +0x2000-0x2044 - DSP Output Mixer registers + +These determine how loudly the DSP effect output channels are played. + +bit 15-12 = unused +bit 11-8 = Effect output level (EFSDL), 0x0-0xF + 0xF is no attenuation (full volume), and each value below that + increases the attenuation by 3dB. +bit 7-5 = unused +bit 4-0 = Effect output pan (EFPAN), 0x00-0x1F + This works the same as the direct output pan register. + +The first 16 registers (0x2000-0x203C) correspond to mixer outputs 0-15. +0x2040 is CDDA output left. 0x2044 is CDDA output right. + +----------------------------------------------------------------------------- +0x2800 - MasterVolume + +bit 15 = MONO (1=mono, 0=stereo) + In mono mode, pan attenuation values do not take effect. +bit 14-10 = unused +bit 9 = MEM8MB (1=8MB memory, 0=2MB memory) +bit 8 = DAC18B (1=use 18-bit DAC, 0=use 16-bit DAC) +bit 7-4 = LSI version (VER) + Read this to get the LSI version of the chip. Should be 1. +bit 3-0 = Master volume (MVOL) + 0xF=no attenuation (full volume) + Every value beneath 0xF decreases the volume by 3dB. + +Whenever you read this register, the result should be 0x0010. + +----------------------------------------------------------------------------- +0x2804 - RingBufferAddress + +bit 15 = $T (used for LSI testing?) +bit 14-13 = Ring buffer size (RBL) + 0 = 8Kwords + 1 = 16Kwords + 2 = 32Kwords + 3 = 64Kwords (where "word" means 16-bit) +bit 12 = unused +bit 11-0 = Ring buffer pointer (RBP) + This corresponds to bits 22-11 of an actual RAM address. + Each increment of RBP represents 2Kbytes of memory. + +----------------------------------------------------------------------------- +0x2808 - MIDIInput + +bit 15-13 = unused +bit 12 = MIDI Output FIFO is full (MOFUL) 1=yes, 0=no +bit 11 = MIDI Output FIFO is empty (MOEMP) 1=yes, 0=no +bit 10 = MIDI Input FIFO has overflowed (MIOVF) 1=yes, 0=no +bit 9 = MIDI Input FIFO is full (MIFUL) 1=yes, 0=no +bit 8 = MIDI Input FIFO is empty (undocumented!) 1=yes, 0=no +bit 7-0 = MIDI Input Buffer (MIBUF) + Data coming in from the MIDI port in a 4-byte FIFO. + +Note that when you read this register all at once, the status flags represent +the state of the input FIFO _before_ you read that next byte. + +----------------------------------------------------------------------------- +0x280C - ChnInfoReq + +bit 15 = unused +bit 14 = Amplitude or Filter select (AFSEL) + 0 = we will be monitoring the amplitude envelope + 1 = we will be monitoring the filter envelope +bit 13-8 = Monitor select (MSLC), 0x00-0x3F + Selects a channel to monitor using the monitor regs below. +bit 7-0 = MIDI Output Buffer (MOBUF) + Probably best not to write stuff here under normal circumstances + +----------------------------------------------------------------------------- +0x2810 - PlayStatus + +This register refers to the channel selected by MSLC above. +Also it can refer to either the amplitude or filter envelope, depending on +the AFSEL bit. + +bit 15 = Loop end flag (LP) + Set to 1 when the sample loop end is reached. + Cleared to 0 upon reading. +bit 14-13 = Current envelope state (SGC) + 0=attack, 1=decay, 2=sustain, 3=release +bit 12-0 = Envelope level (EG) + For amplitude envelope, this is generally in the range of + 0x0000-0x03BF. (0x000 means full volume/no attenuation.) + Any higher attenuation level is returned as 0x1FFF. + For filter envelope, you'll get all 13 bits of the current + lowpass value. + +(every 0x40 on the envelope attenuation level is 3dB) + +----------------------------------------------------------------------------- +0x2814 - PlayPos + +bit 15-0 = Current sample position from the requested channel (CA) + +----------------------------------------------------------------------------- +0x2880 - ? + +has to do with DMA and other obscure stuff + +bit 15-9 = DMEA 22:16 +bit 8 = unused +bit 7-5 = TSCD 2:0 +bit 4 = $T (used for LSI testing?) +bit 3-0 = MRWINH + Setting any of the bits of MRWINH to 1 prohibits sound memory + access from various devices: + bit 3 = main CPU + bit 2 = sound CPU (yes, it will lock up if you set this to 1) + bit 1 = sound samples + bit 0 = effects processor + +----------------------------------------------------------------------------- +0x2884 - ? + +has to do with DMA and other obscure stuff + +bit 15-1 = DMEA 15:1 +bit 0 = unused + +----------------------------------------------------------------------------- +0x2888 - ? + +has to do with DMA and other obscure stuff + +bit 15 = GA +bit 14-1 = DRGA 14:1 +bit 0 = unused + +----------------------------------------------------------------------------- +0x288C - ? + +has to do with DMA and other obscure stuff + +bit 15 = DI +bit 14-1 = DLG 14:1 +bit 0 = EX + +----------------------------------------------------------------------------- +0x2890 - TimerAControl + +bit 15-11 = unused +bit 10-8 = Timer A prescale (TACTL) + 0 = Timer increments every sample + 1 = Timer increments every 2 samples + 2 = Timer increments every 4 samples + 3 = Timer increments every 8 samples + 4 = Timer increments every 16 samples + 5 = Timer increments every 32 samples + 6 = Timer increments every 64 samples + 7 = Timer increments every 128 samples +bit 7-0 = Timer A value (TIMA) + Can be written directly, maybe also read, not sure. + Counts up at the rate specified by TACTL. + When it overflows from 0xFF->0x00, an interrupt occurs. + +----------------------------------------------------------------------------- +0x2894 - TimerBControl + +bit 15-11 = unused +bit 10-8 = Timer B prescale (TBCTL), similar to TACTL +bit 7-0 = Timer B value (TIMB), similar to TIMA + +----------------------------------------------------------------------------- +0x2898 - TimerCControl + +bit 15-11 = unused +bit 10-8 = Timer C prescale (TCCTL), similar to TACTL +bit 7-0 = Timer C value (TIMC), similar to TIMA + +----------------------------------------------------------------------------- +0x289C - Sound CPU Interrupt Enable (SCIEB) +0x28A0 - Sound CPU Interrupt Pending (SCIPD) +0x28A4 - Sound CPU Interrupt Reset (SCIRE) + +The following bits apply to all 3 registers: + +bit 15-11 = unused +bit 10 = One-sample interval interrupt +bit 9 = MIDI output interrupt +bit 8 = Timer C interrupt +bit 7 = Timer B interrupt +bit 6 = Timer A interrupt +bit 5 = CPU interrupt +bit 4 = DMA end interrupt +bit 3 = MIDI input interrupt +bit 2 = reserved +bit 1 = reserved +bit 0 = External interrupt pin (used for SCSI in the devkit) + +Writing SCIEB determines which of the above interrupts are enabled. +(1=on, 0=off). Reading may also work, not sure. + +Reading SCIPD tells you which interrupts are pending (have been signaled). +(1=on, 0=off) + +Writing SCIPD allows you to signal interrupts yourself, but it only works for +bit 5, supposedly. (1=on, 0=off) + +Writing SCIRE resets (acknowledges) whichever interrupts are set to 1 in the +value you write. Probably meaning it clears the corresponding bits of SCIPD. +Reading this is unlikely to work. + +----------------------------------------------------------------------------- +0x28A8 - SCILV0 (usually initialized to 0x18) +0x28AC - SCILV1 (usually initialized to 0x50) +0x28B0 - SCILV2 (usually initialized to 0x08) + +bit 15-8 = unused +bit 7 = refers collectively to bits 7-10 of SCIEB/SCIPD/SCIRE. +bit 6-0 = refers to bits 6-0 of SCIEB/SCIPD/SCIRE. + +When an interrupt happens, the corresponding bits in SCILV0-2 are what forms +the INTREQ number. For instance: + +bit 7 6 5 4 3 2 1 0 + misc. TimerA SCPU DMA MIDI - - Ext. +-------------------------------------------------- +SCILV0 0 0 0 1 1 0 0 0 = 0x18 +SCILV1 0 1 0 1 0 0 0 0 = 0x50 +SCILV2 0 0 0 0 1 0 0 0 = 0x08 + | | | + | | +-----> MIDI INTREQ = 5 + | +----------> DMA INTREQ = 3 + +------------------------> Timer A INTREQ = 2 + +No, nothing to do with overclocking or burning. ;) + +----------------------------------------------------------------------------- +0x28B4 - Main CPU Interrupt Enable (MCIEB) +0x28B8 - Main CPU Interrupt Pending (MCIPD) +0x28BC - Main CPU Interrupt Reset (MCIRE) + +Same as SCIEB/SCIPD/SCIRE, but refers to the Main CPU. + +The usual application of this is to set bit 5 of MCIEB (enable CPU interrupt) +and then set bit 5 of MCIPD (set pending CPU interrupt). That sends an +interrupt to the SH4 side. + +----------------------------------------------------------------------------- +0x2C00: ARMReset + +bit 15-1 = unused +bit 0 = Reset ARM CPU + +There are actually other bits in this register, but they're inaccessible from +the ARM side, and undefined on the AICA. There's probably no use accessing +this reg on the ARM side anyway. + +----------------------------------------------------------------------------- +0x2D00 - INTRequest + +bit 15-8 = unused +bit 7-3 = unknown +bit 0-2 = Current interrupt request level (INTREQ) + +Whenever there's an interrupt, the INTREQ number (determined by SCILV0-2) is +stored here. + +----------------------------------------------------------------------------- +0x2D04 - INTClear + +bit 15-9 = unused +bit 8 = RP +bit 7-0 = M7-M0 + +Writing 0x01 to M7-M0 signals an end-of-interrupt to the AICA. +Sometimes this is done 4 times, perhaps to clear a queue. + +----------------------------------------------------------------------------- +0x2E00 - RTCHi + +bit 15-0 = RTC bits 31-16 +Realtime clock, counts seconds. + +----------------------------------------------------------------------------- +0x2E04 - RTCLo + +bit 15-0 = RTC bits 15-0 +Realtime clock, counts seconds. + +----------------------------------------------------------------------------- + +DSP REGISTERS + +All values used internally in the DSP are linear integers, signed, 2's +complement. By default, external ringbuffer memory is read and written in a +special floating-point format, but all internal calculations are integer. + +Every DSP register described here is a 32-bit register, but only the lowest +16 bits are used. + +0x3000-0x31FF: Coefficients (COEF), 128 registers, 13 bits each + 0x3000: bits 15-3 = COEF(0) + 0x3004: bits 15-3 = COEF(1) + ... + 0x31FC: bits 15-3 = COEF(127) + You could interpret these as 16-bit signed (2's complement) + coefficients with the lowest 3 bits are always 0. + Each of the 128 COEFs is used by the corresponding instruction + (MPRO). + +0x3200-0x32FF: External memory addresses (MADRS), 64 registers, 16 bits each + 0x3200: bits 15-0 = MADRS(0) + ... + 0x3204: bits 15-0 = MADRS(1) + 0x32FC: bits 15-0 = MADRS(63) + These are memory offsets that refer to locations in the + external ringbuffer. Every increment of a MADRS register + represents 2 bytes. + +0x3400-0x3BFF: DSP program (MPRO), 128 registers, 64 bits each + 0x3400: bits 15-0 = bits 63-48 of first instruction + 0x3404: bits 15-0 = bits 47-32 of first instruction + 0x3408: bits 15-0 = bits 31-16 of first instruction + 0x340C: bits 15-0 = bits 15-0 of first instruction + 0x3410: bits 15-0 = bits 63-48 of second instruction + ... + 0x3BFC: bits 15-0 = bits 15-0 of last instruction + +0x4000-0x43FF: Temp buffer (TEMP), 128 registers, 24 bits each + 0x4000: bits 7-0 = bits 7-0 of TEMP(0) + 0x4004: bits 15-0 = bits 23-8 of TEMP(0) + 0x4008: bits 7-0 = bits 7-0 of TEMP(1) + ... + 0x43FC: bits 15-0 = bits 23-8 of TEMP(127) + The temp buffer is configured as a ring buffer, so pointers + referring to it decrement by 1 each sample. + +0x4400-0x44FF: Memory data (MEMS), 32 registers, 24 bits each + 0x4400: bits 7-0 = bits 7-0 of MEMS(0) + 0x4404: bits 15-0 = bits 23-8 of MEMS(0) + 0x4408: bits 7-0 = bits 7-0 of MEMS(1) + ... + 0x44FC: bits 15-0 = bits 23-8 of MEMS(31) + Used for holding data that was read out of the ringbuffer. + +0x4500-0x457F: Mixer input data (MIXS), 16 registers, 20 bits each + 0x4500: bits 3-0 = bits 3-0 of MIXS(0) + 0x4504: bits 15-0 = bits 19-4 of MIXS(0) + 0x4508: bits 3-0 = bits 3-0 of MIXS(1) + ... + 0x457C: bits 15-0 = bits 19-4 of MIXS(15) + These are the 16 send buses coming from the 64 main channels. + +0x4580-0x45BF: Effect output data (EFREG), 16 registers, 16 bits each + 0x4580: bits 15-0 = EFREG(0) + 0x4584: bits 15-0 = EFREG(1) + ... + 0x45BC: bits 15-0 = EFREG(15) + These are the 16 sound outputs. + +0x45C0-0x45C7: External input data stack (EXTS), 2 registers, 16 bits each + 0x45C0: bits 15-0 = EXTS(0) + 0x45C4: bits 15-0 = EXTS(1) + These come from CDDA left and right, respectively. + +----------------------------------------------------------------------------- + +DSP PROGRAM + +The DSP contains a 128-step program. All 128 steps are executed in order on +every sample. There's no branching or looping. + +The instruction word format (MPRO) is as follows: + +bit 63-57 = TRA [6:0] +bit 56 = TWT +bit 55-49 = TWA [6:0] +bit 48 = unused +-------- +bit 47 = XSEL +bit 46-45 = YSEL [1:0] +bit 44-39 = IRA [5:0] +bit 38 = IWT +bit 37-33 = IWA [4:0] +bit 32 = unused +-------- +bit 31 = TABLE +bit 30 = MWT +bit 29 = MRD +bit 28 = EWT +bit 27-24 = EWA [3:0] +bit 23 = ADRL +bit 22 = FRCL +bit 21-20 = SHFT [1:0] +bit 19 = YRL +bit 18 = NEGB +bit 17 = ZERO +bit 16 = BSEL +-------- +bit 15 = NOFL +bit 14-9 = MASA [5:0] +bit 8 = ADREB +bit 7 = NXADR +bit 6-0 = unused + +Here's what happens on each step. In the following description, I will be +referring to these internal registers: + +MDEC_CT.... 16-bit unsigned register which is decremented on every sample +INPUTS..... 24-bit signed input value +B.......... 26-bit signed addend +X.......... 24-bit signed multiplicand +Y.......... 13-bit signed multiplier +ACC........ 26-bit signed accumulator +SHIFTED.... 24-bit signed shifted output value +Y_REG...... 24-bit signed latch register +FRC_REG.... 13-bit fractional address +ADRS_REG... 12-bit whole address + +In the course of one step, all of the following occur in order: + +(Beginning of step) + +- Input read + + A 24-bit value (INPUTS) is read from either MEMS, MIXS, or EXTS, depending + on the value of IRA: + + IRA 0x00-0x1F: one of the MEMS registers (0x00-0x1F) + IRA 0x20-0x2F: one of the MIXS registers (0x0-0xF) + IRA 0x30-0x31: one of the EXTS registers (0 or 1) + other values: undefined + + If the source register is less than 24 bits, it's promoted to 24-bit by + shifting left. + +- Input write + + If IWT is set, then the memory data from a MRD (either 2 or 3 instructions + ago) is copied into the MEMS register indicated by IWA (0x00-0x1F). + + If NOFL was set 2 instructions ago (only), the value is simply promoted + from 16 to 24-bit by shifting left. Otherwise, it's converted from 16-bit + float format to 24-bit integer. (See float notes) + +- B selection + + A 26-bit value (B) is read from one of two sources: + + If BSEL=0: The TEMP register indicated by ((TRA + MDEC_CT) & 0x7F). + This value is sign-extended from 24 to 26 bits, not shifted. + If BSEL=1: The accumulator (ACC), already 26 bits. + + If NEGB=1, this value is then made negative. (B becomes 0-B) + If ZERO=1, this value becomes zero, regardless of all other bits. + +- X selection + + A 24-bit value (X) is read from one of two sources: + + If XSEL=0: One of the TEMP registers indicated by ((TRA + MDEC_CT) & 0x7F). + If XSEL=1: The INPUTS register. + +- Y selection + + A 13-bit value (Y) is read from one of four sources: + If YSEL=0: Y becomes FRC_REG + If YSEL=1: Y becomes this instruction's COEF + If YSEL=2: Y becomes bits 23-11 of Y_REG + If YSEL=3: Y becomes bits 15-4 of Y_REG, ZERO-EXTENDED to 13 bits. + +- Y latch + + If YRL is set, Y_REG becomes INPUTS. + +- Shift of previous accumulator + + A 24-bit value (SHIFTED) is set to one of the following: + + If SHFT=0: SHIFTED becomes ACC, with saturation + If SHFT=1: SHIFTED becomes ACC*2, with saturation + If SHFT=2: SHIFTED becomes ACC*2, with the highest bits simply cut off + If SHFT=3: SHIFTED becomes ACC, with the highest bits simply cut off + + If saturation is enabled, SHIFTED is clipped to the range + -0x800000...0x7FFFFF. + +- Multiply and accumulate + + A 26-bit value (ACC) becomes ((X * Y) >> 12) + B. + The multiplication is signed. I don't think the addition includes + saturation. + +- Temp write + + If TWT is set, the value SHIFTED is written to the temp buffer address + indicated by ((TWA + MDEC_CT) & 0x7F). + +- Fractional address latch + + If FRCL is set, FRC_REG (13-bit) becomes one of the following: + + If SHFT=3: FRC_REG = lowest 12 bits of SHIFTED, ZERO-EXTENDED to 13 bits. + Other values of SHFT: FRC_REG = bits 23-11 of SHIFTED. + +- Memory operations + + If either MRD or MWT are set, we are performing a memory operation (on the + external ringbuffer) and we'll need to compute an address. + + Start with the 16-bit value of the MADRS register indicated by MASA + (0x00-0x3F). + If TABLE=0, add the 16-bit value MDEC_CT. + If ADREB=1, add the 12-bit value ADRS_REG, zero-extended. + If NXADR=1, add 1. + If TABLE=0, mod the address so it's within the proper ringbuffer size. + For a 8Kword ringbuffer: AND by 0x1FFF + For a 16Kword ringbuffer: AND by 0x3FFF + For a 32Kword ringbuffer: AND by 0x7FFF + For a 64Kword ringbuffer: AND by 0xFFFF + If TABLE=1, simply AND the address by 0xFFFF. + Shift the address left by 1 (so it's referring to a word offset). + Add (RBP<<11). + This is the address, in main memory, you'll be reading or writing. + + If MRD is set, read the 16-bit word at the calculated address and put it + in a temporary place. It will then be accessible by the instruction either + 2 or 3 steps ahead. That instruction will have to use a IWT to get the + result. Don't perform any conversions yet; let the IWT handle it later on. + + If MWT is set, write the value of SHIFTED into memory at the calculated + address. If NOFL=1, simply shift it right by 8 to make it 16-bit. + Otherwise, convert from 24-bit integer to 16-bit float (see float notes). + +- Address latch + + If ADRL is set, ADRS_REG (12-bit) becomes one of the following: + + If SHFT=3: ADRS_REG = INPUTS >> 16 (signed shift with sign extension). + Other values of SHFT: ADRS_REG = bits 23-12 of SHIFTED. + +- Effect output write + + If EWT is set, write (SHIFTED >> 8) into the EFREG register specified by + EWA (0x0-0xF). + +(End of step) + +Supposedly, external memory reads and writes (MRD/MWT) are only allowed on +odd steps (prohibited on step 0, allowed on step 1, etc.), but I found that +MWT seems to work on any step. I'm just not sure if it's guaranteed to write +anything meaningful. I doubt it would hurt to allow MRD/MWT on every step in +an emulator. + +----------------------------------------------------------------------------- + +DSP FLOATING-POINT FORMAT + +When reading/writing external memory, the default (unless NOFL is specified) +is to use a 16-bit floating-point format, as follows: + +bit 15 = sign +bit 14-11 = exponent +bit 10-0 = mantissa + +To convert from 16-bit float to 24-bit integer (on read): + +- Take the mantissa and shift it left by 11 +- Make bit 23 be the sign bit +- Make bit 22 be the reverse of the sign bit +- Shift right (signed) by the exponent + +To convert from 24-bit integer to 16-bit float (on write): + +- While the top 2 bits are the same, shift left + The number of times you have to shift becomes the exponent +- Shift right (signed) by 11 +- Set bits 14-11 to the exponent value + +----------------------------------------------------------------------------- + +TODO: + +- Obsess over the lowpass f and q coefficients some more + +----------------------------------------------------------------------------- diff --git a/shell/android-studio/psd/dark_texture.psd b/shell/android-studio/psd/dark_texture.psd new file mode 100644 index 000000000..e8b70f427 Binary files /dev/null and b/shell/android-studio/psd/dark_texture.psd differ diff --git a/shell/android-studio/psd/disk_mipmap.psd b/shell/android-studio/psd/disk_mipmap.psd new file mode 100644 index 000000000..f04647854 Binary files /dev/null and b/shell/android-studio/psd/disk_mipmap.psd differ diff --git a/shell/android-studio/psd/dctemplate.psd b/shell/android-studio/psd/icon_drawable.psd similarity index 100% rename from shell/android-studio/psd/dctemplate.psd rename to shell/android-studio/psd/icon_drawable.psd diff --git a/shell/android-studio/reicast/build.gradle b/shell/android-studio/reicast/build.gradle index 789c54b16..7ccb431ab 100644 --- a/shell/android-studio/reicast/build.gradle +++ b/shell/android-studio/reicast/build.gradle @@ -25,13 +25,13 @@ def getVersionName = { -> } android { - compileSdkVersion 25 + compileSdkVersion 26 buildToolsVersion "27.0.3" defaultConfig { applicationId "com.reicast.emulator" minSdkVersion 16 - targetSdkVersion 25 + targetSdkVersion 26 versionCode getBuildId() versionName getVersionName() vectorDrawables.useSupportLibrary = true @@ -39,8 +39,6 @@ android { ndk { moduleName "dc" abiFilters 'armeabi-v7a', 'arm64-v8a' - moduleName "sexplay" - abiFilters 'armeabi-v7a', 'arm64-v8a' } } @@ -56,11 +54,13 @@ android { buildTypes { debug { debuggable true + minifyEnabled true zipAlignEnabled true + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' } release { debuggable false - minifyEnabled false + minifyEnabled true zipAlignEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' signingConfig signingConfigs.release diff --git a/shell/android-studio/reicast/libs/dropbox-android-sdk-1.6.1.jar b/shell/android-studio/reicast/libs/dropbox-android-sdk-1.6.1.jar deleted file mode 100644 index 5e666751a..000000000 Binary files a/shell/android-studio/reicast/libs/dropbox-android-sdk-1.6.1.jar and /dev/null differ diff --git a/shell/android-studio/reicast/proguard-rules.txt b/shell/android-studio/reicast/proguard-rules.txt new file mode 100644 index 000000000..7b3d38a1d --- /dev/null +++ b/shell/android-studio/reicast/proguard-rules.txt @@ -0,0 +1,69 @@ +# This is a configuration file for ProGuard. +# http://proguard.sourceforge.net/index.html#manual/usage.html +# Optimizations: If you don't want to optimize, use the +# proguard-android.txt configuration file instead of this one, which +# turns off the optimization flags. Adding optimization introduces +# certain risks, since for example not all optimizations performed by +# ProGuard works on all versions of Dalvik. The following flags turn +# off various optimizations known to have issues, but the list may not +# be complete or up to date. (The "arithmetic" optimization can be +# used if you are only targeting Android 2.0 or later.) Make sure you +# test thoroughly if you go this route. +-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/* +-optimizationpasses 5 +-allowaccessmodification +-dontpreverify +# The remainder of this file is identical to the non-optimized version +# of the Proguard configuration file (except that the other file has +# flags to turn off optimization). +-dontusemixedcaseclassnames +-dontskipnonpubliclibraryclasses +-verbose +-keepattributes *Annotation* +# For native methods, see http://proguard.sourceforge.net/manual/examples.html#native +-keepclasseswithmembernames class * { + native ; +} +# keep setters in Views so that animations can still work. +# see http://proguard.sourceforge.net/manual/examples.html#beans +-keepclassmembers public class * extends android.view.View { + void set*(***); + *** get*(); +} +# We want to keep methods in Activity that could be used in the XML attribute onClick +-keepclassmembers class * extends android.app.Activity { + public void *(android.view.View); +} +# For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations +-keepclassmembers enum * { + public static **[] values(); + public static ** valueOf(java.lang.String); +} +-keepclassmembers class * implements android.os.Parcelable { + public static final android.os.Parcelable$Creator CREATOR; +} +-keepclassmembers class **.R$* { + public static ; +} + +-keep class com.reicast.** {*;} +-keepclassmembers class com.reicast.** {*;} + +# The support library contains references to newer platform versions. +# Don't warn about those in case this app is linking against an older +# platform version. We know about them, and they are safe. +-dontwarn android.support.** +-dontwarn okio.** +-dontwarn okhttp3.** +-dontwarn com.squareup.okhttp.** +-dontwarn com.google.appengine.** +-dontwarn java.io.** +-dontwarn java.nio.file.** +-dontwarn javax.naming.** +-dontwarn javax.servlet.** +-dontwarn junit.textui.** + +-keepattributes Signature +-keepattributes InnerClasses + +-keepattributes Exceptions,SourceFile,LineNumberTable diff --git a/shell/android-studio/reicast/src/dreamcast/AndroidManifest.xml b/shell/android-studio/reicast/src/dreamcast/AndroidManifest.xml index 2043b0397..d0a0cfb3b 100644 --- a/shell/android-studio/reicast/src/dreamcast/AndroidManifest.xml +++ b/shell/android-studio/reicast/src/dreamcast/AndroidManifest.xml @@ -3,54 +3,57 @@ xmlns:tools="http://schemas.android.com/tools"> - - - - - - - - - - - - - - - - + android:name="com.reicast.emulator.NativeGLActivity"/> + + + + + + + + + + + + + + + + + - \ No newline at end of file + diff --git a/shell/android-studio/reicast/src/main/AndroidManifest.xml b/shell/android-studio/reicast/src/main/AndroidManifest.xml index fb0b0ab3a..bc93a562f 100644 --- a/shell/android-studio/reicast/src/main/AndroidManifest.xml +++ b/shell/android-studio/reicast/src/main/AndroidManifest.xml @@ -37,7 +37,7 @@ android:name="com.reicast.emulator.Emulator" android:icon="@drawable/ic_launcher" android:label="@string/app_name" - android:theme="@style/AppTheme" + android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:roundIcon="@drawable/ic_launcher" android:banner="@drawable/ic_banner" android:logo="@drawable/ic_banner" @@ -45,42 +45,26 @@ android:isGame="true"> - + android:screenOrientation="sensorLandscape"> + + + - - - - - - - - - - - - - + listFiles(File directory, FilenameFilter[] filter, - int recurse) { - - Vector files = new Vector<>(); - - File[] entries = directory.listFiles(); - - if (entries != null) { - for (File entry : entries) { - for (FilenameFilter filefilter : filter) { - if (filefilter.accept(directory, entry.getName())) { - files.add(entry); - } - } - if ((recurse <= -1) || (recurse > 0 && entry.isDirectory())) { - recurse--; - files.addAll(listFiles(entry, filter, recurse)); - recurse++; - } - } - } - return files; - } - public static void saveScreenshot(final Context c, int w, int h, GL10 gl){ try { SharedPreferences mPrefs = PreferenceManager.getDefaultSharedPreferences(c); @@ -80,7 +46,7 @@ public class FileUtils { } //thank you stackoverflow - public static Bitmap savePixels(int x, int y, int w, int h, GL10 gl) + private static Bitmap savePixels(int x, int y, int w, int h, GL10 gl) { int b[]=new int[w*(y+h)]; int bt[]=new int[w*h]; diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/BaseGLActivity.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/BaseGLActivity.java index 1fbecb1a6..94fc19410 100644 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/BaseGLActivity.java +++ b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/BaseGLActivity.java @@ -2,9 +2,12 @@ package com.reicast.emulator; import android.Manifest; import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; +import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Environment; @@ -12,16 +15,13 @@ import android.os.Handler; import android.preference.PreferenceManager; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.support.design.widget.Snackbar; import android.support.v4.app.ActivityCompat; import android.util.Log; -import android.view.Gravity; import android.view.InputDevice; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; -import android.widget.TextView; import com.reicast.emulator.config.Config; import com.reicast.emulator.debug.GenerateLogs; @@ -35,17 +35,27 @@ import java.util.List; import tv.ouya.console.api.OuyaController; -public class BaseGLActivity extends Activity implements ActivityCompat.OnRequestPermissionsResultCallback { +public abstract class BaseGLActivity extends Activity implements ActivityCompat.OnRequestPermissionsResultCallback { + private static final int STORAGE_PERM_REQUEST = 1001; + private static final int AUDIO_PERM_REQUEST = 1002; + protected View mView; protected SharedPreferences prefs; protected float[][] vjoy_d_cached; // Used for VJoy editing private AudioBackend audioBackend; private Handler handler = new Handler(); public static byte[] syms; + private boolean audioPermissionRequested = false; + private boolean paused = true; + private boolean resumedCalled = false; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + if (!getFilesDir().exists()) { + getFilesDir().mkdir(); + } prefs = PreferenceManager.getDefaultSharedPreferences(this); Emulator app = (Emulator)getApplicationContext(); @@ -56,32 +66,124 @@ public class BaseGLActivity extends Activity implements ActivityCompat.OnRequest String home_directory = prefs.getString(Config.pref_home, ""); String result = JNIdc.initEnvironment((Emulator)getApplicationContext(), home_directory); - if (result != null) - showToastMessage("Initialization failed: " + result, Snackbar.LENGTH_LONG); + if (result != null) { + AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this); + dlgAlert.setMessage("Initialization failed. Please try again and/or reinstall.\n\n" + + "Error: " + result); + dlgAlert.setTitle("Reicast Error"); + dlgAlert.setPositiveButton("Exit", + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog,int id) { + BaseGLActivity.this.finish(); + } + }); + dlgAlert.setIcon(android.R.drawable.ic_dialog_alert); + dlgAlert.setCancelable(false); + dlgAlert.create().show(); + return; + } + + setStorageDirectories(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + ActivityCompat.requestPermissions(this, + new String[]{ + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE + }, + STORAGE_PERM_REQUEST); + } + + InputDeviceManager.getInstance().startListening(getApplicationContext()); + register(this); + + audioBackend = new AudioBackend(); + + // When viewing a resource, pass its URI to the native code for opening + Intent intent = getIntent(); + if (intent.getAction() != null) { + if (intent.getAction().equals(Intent.ACTION_VIEW)) { + Uri gameUri = Uri.parse(intent.getData().toString()); + // Flush the intent to prevent multiple calls + getIntent().setData(null); + setIntent(null); + if (gameUri != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + gameUri = Uri.parse(gameUri.toString().replace("content://" + + gameUri.getAuthority() + "/external_files", "/storage")); + } + if (gameUri != null) + JNIdc.setGameUri(gameUri.toString()); + } + } + } + + private void setStorageDirectories() + { String android_home_directory = Environment.getExternalStorageDirectory().getAbsolutePath(); List pathList = new ArrayList<>(); pathList.add(android_home_directory); pathList.addAll(FileBrowser.getExternalMounts()); Log.i("reicast", "External storage dirs: " + pathList); JNIdc.setExternalStorageDirectories(pathList.toArray()); - - InputDeviceManager.getInstance().startListening(getApplicationContext()); - register(this); - - audioBackend = new AudioBackend(); } @Override protected void onDestroy() { super.onDestroy(); + paused = true; InputDeviceManager.getInstance().stopListening(); register(null); - audioBackend.release(); + if (audioBackend != null) + audioBackend.release(); Emulator.setCurrentActivity(null); stopEmulator(); } + @Override + protected void onPause() { + super.onPause(); + resumedCalled = false; + handleStateChange(true); + } + + @Override + protected void onResume() { + super.onResume(); + resumedCalled = true; + handleStateChange(false); + } + + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + if (hasFocus) { + handleStateChange(false); + } else { + handleStateChange(true); + } + } + + protected abstract void doPause(); + protected abstract void doResume(); + protected abstract boolean isSurfaceReady(); + + public void handleStateChange(boolean paused) + { + if (paused == this.paused) + return; + if (!paused && (!resumedCalled || !isSurfaceReady())) + return; + this.paused = paused; + if (paused) { + doPause(); + } + else { + doResume(); + } + } + private boolean showMenu() { JNIdc.guiOpenSettings(); return true; @@ -126,70 +228,53 @@ public class BaseGLActivity extends Activity implements ActivityCompat.OnRequest @Override public boolean onKeyDown(int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_BACK) { - if (!JNIdc.guiIsOpen()) { - showMenu(); - return true; + if (event.getRepeatCount() == 0) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + if (!JNIdc.guiIsOpen()) { + showMenu(); + return true; + } + else if (JNIdc.guiIsContentBrowser()) { + finish(); + return true; + } } - else if (JNIdc.guiIsContentBrowser()) { - // Only if this is the main activity - //finish(); - // so for now we hack it - Intent startMain = new Intent(Intent.ACTION_MAIN); - startMain.addCategory(Intent.CATEGORY_HOME); - startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(startMain); + if (InputDeviceManager.getInstance().joystickButtonEvent(event.getDeviceId(), keyCode, true)) return true; - } - } - if (InputDeviceManager.getInstance().joystickButtonEvent(event.getDeviceId(), keyCode, true)) - return true; - if (ViewConfiguration.get(this).hasPermanentMenuKey()) { - if (keyCode == KeyEvent.KEYCODE_MENU) { - return showMenu(); + if (ViewConfiguration.get(this).hasPermanentMenuKey()) { + if (keyCode == KeyEvent.KEYCODE_MENU) { + return showMenu(); + } } } return super.onKeyDown(keyCode, event); } - private void showToastMessage(String message, int duration) { - View view = findViewById(android.R.id.content); - Snackbar snackbar = Snackbar.make(view, message, duration); - View snackbarLayout = snackbar.getView(); - TextView textView = (TextView) snackbarLayout.findViewById( - android.support.design.R.id.snackbar_text); - textView.setGravity(Gravity.CENTER_VERTICAL); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) - textView.setTextAlignment(View.TEXT_ALIGNMENT_GRAVITY); - textView.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_notification, 0, 0, 0); - textView.setCompoundDrawablePadding(getResources() - .getDimensionPixelOffset(R.dimen.snackbar_icon_padding)); - snackbar.show(); - } - protected void stopEmulator() { JNIdc.stop(); } void requestRecordAudioPermission() { + if (audioPermissionRequested) + return; + audioPermissionRequested = true; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { handler.post(new Runnable() { @Override public void run() { - mView.setVisibility(View.INVISIBLE); ActivityCompat.requestPermissions(BaseGLActivity.this, new String[]{ Manifest.permission.RECORD_AUDIO }, - 0); + AUDIO_PERM_REQUEST); } }); } else { - onRequestPermissionsResult(0, new String[] { Manifest.permission.RECORD_AUDIO }, + onRequestPermissionsResult(AUDIO_PERM_REQUEST, new String[] { Manifest.permission.RECORD_AUDIO }, new int[] { PackageManager.PERMISSION_GRANTED }); } } @@ -197,19 +282,40 @@ public class BaseGLActivity extends Activity implements ActivityCompat.OnRequest @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); - if (permissions.length > 0 && Manifest.permission.RECORD_AUDIO .equals(permissions[0]) && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + if (requestCode == AUDIO_PERM_REQUEST && permissions.length > 0 + && Manifest.permission.RECORD_AUDIO .equals(permissions[0]) && grantResults[0] == PackageManager.PERMISSION_GRANTED) { SipEmulator sip = new SipEmulator(); sip.startRecording(); JNIdc.setupMic(sip); } + else if (requestCode == STORAGE_PERM_REQUEST) { + setStorageDirectories(); + //setup mic + if (Emulator.micPluggedIn()) + requestRecordAudioPermission(); + } + } // Called from native code - private void generateErrorLog() { + protected void generateErrorLog() { try { new GenerateLogs(this).execute(getFilesDir().getAbsolutePath()); } catch (RuntimeException e) { - showToastMessage("An error occurred retrieving the log file: " + e.getMessage(), Snackbar.LENGTH_LONG); + AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this); + dlgAlert.setMessage("An error occurred retrieving the log file:\n\n" + + e.getMessage()); + dlgAlert.setTitle("Reicast Error"); + dlgAlert.setPositiveButton("Ok", + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog,int id) { + dialog.cancel(); + } + }); + dlgAlert.setIcon(android.R.drawable.ic_dialog_info); + dlgAlert.setCancelable(false); + dlgAlert.create().show(); } } diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/CloudFragment.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/CloudFragment.java deleted file mode 100644 index 6df27828f..000000000 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/CloudFragment.java +++ /dev/null @@ -1,350 +0,0 @@ -package com.reicast.emulator; - -/* - * File: CloudFragment.java - * Author: Luca D'Amico (Luca91) - * Last Edit: 11 May 2014 - * - * Reference: http://forums.reicast.com/index.php?topic=160.msg422 - */ - -import android.app.AlertDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.SharedPreferences; -import android.content.SharedPreferences.Editor; -import android.os.AsyncTask; -import android.os.Bundle; -import android.os.Environment; -import android.preference.PreferenceManager; -import android.support.v4.app.Fragment; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.Toast; - -import com.dropbox.client2.DropboxAPI; -import com.dropbox.client2.DropboxAPI.Entry; -import com.dropbox.client2.android.AndroidAuthSession; -import com.dropbox.client2.exception.DropboxException; -import com.dropbox.client2.session.AccessTokenPair; -import com.dropbox.client2.session.AppKeyPair; -import com.dropbox.client2.session.TokenPair; -import com.reicast.emulator.config.Config; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.concurrent.ExecutionException; - - -public class CloudFragment extends Fragment { - - Button uploadBtn; - Button downloadBtn; - AlertDialog.Builder confirmDialog = null; - boolean actionRequired=false; - public String task = ""; - DropBoxClient client = null; - private String home_directory; - - String[] vmus = {"vmu_save_A1.bin","vmu_save_A2.bin", - "vmu_save_B1.bin","vmu_save_B2.bin", - "vmu_save_C1.bin","vmu_save_C2.bin", - "vmu_save_D1.bin","vmu_save_D2.bin"}; - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - return inflater.inflate(R.layout.cloud_fragment, container, false); - } - - @Override - public void onViewCreated(View view, Bundle savedInstanceState) { - SharedPreferences mPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); - home_directory = mPrefs.getString(Config.pref_home, - Environment.getExternalStorageDirectory().getAbsolutePath()); - buttonListener(); - confirmDialog = new AlertDialog.Builder(getActivity()); - setClient(); - } - - public void setClient(){ - if(client==null) - client = new DropBoxClient(getActivity()); - } - - - public void buttonListener() { - uploadBtn = (Button) getView().findViewById(R.id.uploadBtn); - uploadBtn.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View arg0) { - confirmDialog.setMessage(R.string.uploadWarning); - confirmDialog.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - setClient(); - task = "Upload"; - client.startLogin(); - actionRequired = true; - } - }); - confirmDialog.setNegativeButton(R.string.cancel, null); - confirmDialog.show(); - - } - }); - - - downloadBtn = (Button) getView().findViewById(R.id.downloadBtn); - downloadBtn.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View arg0) { - confirmDialog.setMessage(R.string.downloadWarning); - confirmDialog.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - setClient(); - task = "Download"; - client.startLogin(); - actionRequired = true; - } - }); - confirmDialog.setNegativeButton(R.string.cancel, null); - confirmDialog.show(); - } - }); - } - - - @Override - public void onResume(){ - super.onResume(); - if (client.mDBApi != null) { - if (client.mDBApi.getSession().authenticationSuccessful()) { - try { - client.mDBApi.getSession().finishAuthentication(); - TokenPair tokens = client.mDBApi.getSession(). getAccessTokenPair(); - if(tokens == null) - Toast.makeText(getActivity(), "Failed to save session token!", Toast.LENGTH_SHORT).show(); - else - client.storeKeys(tokens.key, tokens.secret); - } catch (IllegalStateException e) { - Log.i("Dropbox", "Error authenticating", e); - } - } - if(actionRequired){ - for(int k=0;k mDBApi; - AndroidAuthSession session; - - public DropBoxClient(Context context){ - this.context = context; - session = buildSession(); - mDBApi = new DropboxAPI(session); - } - - public void startLogin(){ - mDBApi.getSession().startOAuth2Authentication(context); - } - - public String[] getKeys() { - SharedPreferences prefs = context.getSharedPreferences("ReicastVMUUploader", 0); - String key = prefs.getString("DBoxKey", null); - String secret = prefs.getString("DBoxSecret", null); - if (key != null && secret != null) { - String[] ret = new String[2]; - ret[0] = key; - ret[1] = secret; - return ret; - } else { - return null; - } - } - - - - public void storeKeys(String key, String secret) { - SharedPreferences prefs = context.getSharedPreferences("ReicastVMUUploader", 0); - Editor edit = prefs.edit(); - edit.putString("DBoxKey", key); - edit.putString("DBoxSecret", secret); - edit.apply(); - } - - private AndroidAuthSession buildSession() { - AppKeyPair appKeyPair = new AppKeyPair(APP_KEY, APP_SECRET); - AndroidAuthSession session; - - String[] stored = getKeys(); - if (stored != null) { - AccessTokenPair accessToken = new AccessTokenPair(stored[0], stored[1]); - session = new AndroidAuthSession(appKeyPair, accessToken); - } else { - session = new AndroidAuthSession(appKeyPair); - } - - return session; - } - -} - -class netOperation extends AsyncTask { - - DropBoxClient client = null; - private String home_directory; - - public netOperation(DropBoxClient client, String home_directory){ - this.client = client; - this.home_directory = home_directory; - } - - public boolean uploadFile(String filePath, String fileName) { - File file = new File(filePath); - FileInputStream inputStream = null; - try { - inputStream = new FileInputStream(file); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } - - DropboxAPI.Entry response = null; - try { - response = client.mDBApi.putFileOverwrite("/"+fileName, inputStream, file.length(), null); - } catch (DropboxException e) { - e.printStackTrace(); - } - Log.i("FileInfos", "The uploaded file's rev is: "+ response); - return true; - } - - public boolean downloadFile(String filePath, String fileName) { - DropboxAPI.DropboxFileInfo info = null; - try { - Entry remoteFile = client.mDBApi.metadata("/"+fileName, 1, null, false, null); - if((remoteFile.rev != null) && (remoteFile.bytes > 0)){ // Avoid to download 0 bytes vmus! - File file = new File(filePath); - if(file.exists()) - createBackupOfVmu(fileName); - FileOutputStream out = null; - try { - out = new FileOutputStream(file); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } - info = client.mDBApi.getFile("/"+fileName,null,out,null); - } - } catch (DropboxException e) { - e.printStackTrace(); - } - Log.i("FileInfos", "The downloaded file's rev is: "+ info); - return true; - } - - - @Override - protected void onPostExecute(String result) { - - } - - @Override - protected String doInBackground(String... strings) { - if(strings[0].equals("Upload")){ - if(uploadFile(strings[1],strings[2])) - return "Ok"; - else - return "No"; - } - else if(strings[0].equals("Download")){ - if(downloadFile(strings[1],strings[2])) - return "Ok"; - else - return "No"; - } - else - return "Unknown"; - } - - @Override - protected void onPreExecute() {} - - @Override - protected void onProgressUpdate(Void... values) {} - - - void createBackupOfVmu(String vmuName){ - File backupDir = new File(home_directory+"/VmuBackups/"); - if(!backupDir.exists()) { - backupDir.mkdirs(); - } - - File source = new File(home_directory+"/"+vmuName); - File destination = new File(home_directory+"/VmuBackups/"+vmuName); - if(!destination.exists()) { - try { - destination.createNewFile(); - } catch (IOException e) { - e.printStackTrace(); - } - } - try { - InputStream in = new FileInputStream(source); - OutputStream out = new FileOutputStream(destination); - byte[] buffer = new byte[1024]; - int length; - while ((length = in.read(buffer)) > 0){ - out.write(buffer, 0, length); - } - in.close(); - out.close(); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } - } - -} diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/Emulator.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/Emulator.java index df3e3f74a..40c6b9a75 100644 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/Emulator.java +++ b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/Emulator.java @@ -1,6 +1,5 @@ package com.reicast.emulator; -import android.app.Activity; import android.app.Application; import android.content.Context; import android.content.SharedPreferences; @@ -14,31 +13,23 @@ import com.reicast.emulator.emu.JNIdc; public class Emulator extends Application { private static Context context; - private static Activity currentActivity; + private static BaseGLActivity currentActivity; // see MapleDeviceType in hw/maple/maple_devs.h - public static final int MDT_SegaController = 0; - public static final int MDT_SegaVMU = 1; public static final int MDT_Microphone = 2; - public static final int MDT_PurupuruPack = 3; - public static final int MDT_Keyboard = 4; - public static final int MDT_Mouse = 5; - public static final int MDT_LightGun = 6; - public static final int MDT_NaomiJamma = 7; public static final int MDT_None = 8; - public static final int MDT_Count = 9; public static boolean nosound = false; public static int vibrationDuration = 20; public static int maple_devices[] = { - MDT_SegaController, + MDT_None, MDT_None, MDT_None, MDT_None }; public static int maple_expansion_devices[][] = { - { MDT_SegaVMU, MDT_None }, + { MDT_None, MDT_None }, { MDT_None, MDT_None }, { MDT_None, MDT_None }, { MDT_None, MDT_None }, @@ -56,7 +47,7 @@ public class Emulator extends Application { /** * Fetch current configuration settings from the emulator and save them - * + * Called from JNI code */ public void SaveAndroidSettings(String homeDirectory) { @@ -71,8 +62,8 @@ public class Emulator extends Application { AudioBackend.getInstance().enableSound(!Emulator.nosound); FileBrowser.installButtons(prefs); - if (micPluggedIn() && currentActivity instanceof NativeGLActivity) { - ((NativeGLActivity)currentActivity).requestRecordAudioPermission(); + if (micPluggedIn() && currentActivity instanceof BaseGLActivity) { + ((BaseGLActivity)currentActivity).requestRecordAudioPermission(); } } @@ -95,11 +86,11 @@ public class Emulator extends Application { return Emulator.context; } - public static Activity getCurrentActivity() { + public static BaseGLActivity getCurrentActivity() { return Emulator.currentActivity; } - public static void setCurrentActivity(Activity activity) { + public static void setCurrentActivity(BaseGLActivity activity) { Emulator.currentActivity = activity; } diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/FileBrowser.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/FileBrowser.java index 2a7444ef5..80cb99f54 100644 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/FileBrowser.java +++ b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/FileBrowser.java @@ -1,37 +1,9 @@ package com.reicast.emulator; -import android.app.Activity; -import android.content.Context; import android.content.SharedPreferences; -import android.graphics.Typeface; -import android.graphics.drawable.Drawable; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Build; -import android.os.Bundle; import android.os.Environment; -import android.os.Vibrator; -import android.preference.PreferenceManager; -import android.support.constraint.ConstraintLayout; -import android.support.design.widget.Snackbar; -import android.support.graphics.drawable.VectorDrawableCompat; -import android.support.v4.app.Fragment; -import android.support.v4.content.FileProvider; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.View.OnTouchListener; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.ScrollView; -import android.widget.TextView; -import com.android.util.FileUtils; import com.reicast.emulator.config.Config; -import com.reicast.emulator.emu.JNIdc; import org.apache.commons.lang3.StringUtils; @@ -39,58 +11,13 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; -import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -public class FileBrowser extends Fragment { - - private Vibrator vib; - private Drawable orig_bg; - private boolean games; - private String searchQuery = null; - private OnItemSelectedListener mCallback; - - private SharedPreferences mPrefs; - private File sdcard = Environment.getExternalStorageDirectory(); - private String home_directory = sdcard.getAbsolutePath(); - private String game_directory = sdcard.getAbsolutePath(); - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); - home_directory = mPrefs.getString(Config.pref_home, home_directory); - game_directory = mPrefs.getString(Config.pref_games, game_directory); - - Bundle b = getArguments(); - if (b != null) { - if (games = b.getBoolean("games_entry", false)) { - if (b.getString("path_entry") != null) { - home_directory = b.getString("path_entry"); - } - } else { - if (b.getString("path_entry") != null) { - game_directory = b.getString("path_entry"); - } - } - if (b.getString("search_params") != null) { - searchQuery = b.getString("search_params"); - } - } - } +public class FileBrowser { + android.support.v4.content.FileProvider provider; // To avoid ClassNotFoundException at runtime public static HashSet getExternalMounts() { final HashSet out = new HashSet<>(); @@ -130,61 +57,6 @@ public class FileBrowser extends Fragment { return out; } - // Container Activity must implement this interface - public interface OnItemSelectedListener { - void onGameSelected(Uri uri); - void onFolderSelected(Uri uri); - } - - @Override @SuppressWarnings("deprecation") - public void onAttach(Activity activity) { - super.onAttach(activity); - - // This makes sure that the container activity has implemented - // the callback interface. If not, it throws an exception - try { - mCallback = (OnItemSelectedListener) activity; - } catch (ClassCastException e) { - throw new ClassCastException(activity.toString() - + " must implement OnItemSelectedListener"); - } - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - return inflater.inflate(R.layout.browser_fragment, container, false); - } - - @Override - public void onViewCreated(View view, Bundle savedInstanceState) { - // setContentView(R.layout.activity_main); - - vib = (Vibrator) getActivity().getSystemService(Context.VIBRATOR_SERVICE); - - /* - * OnTouchListener viblist=new OnTouchListener() { - * - * public boolean onTouch(View v, MotionEvent event) { if - * (event.getActionMasked()==MotionEvent.ACTION_DOWN) vib.vibrate(50); - * return false; } }; - * - * findViewById(R.id.config).setOnTouchListener(viblist); - * findViewById(R.id.about).setOnTouchListener(viblist); - */ - - String temp = mPrefs.getString(Config.pref_home, null); - if (temp == null || !new File(temp).isDirectory()) { - showToastMessage(getActivity().getString(R.string.config_home), Snackbar.LENGTH_LONG); - } - installButtons(mPrefs); - if (!games) { - new LocateGames(this, R.array.flash).execute(home_directory); - } else { - new LocateGames(this, R.array.images).execute(game_directory); - } - } - public static void installButtons(SharedPreferences prefs) { try { File buttons = null; @@ -220,402 +92,4 @@ public class FileBrowser extends Fragment { ioe.printStackTrace(); } } - - private static final class LocateGames extends AsyncTask> { - - private WeakReference browser; - private int array; - - LocateGames(FileBrowser context, int arrayType) { - browser = new WeakReference<>(context); - this.array = arrayType; - } - - @Override - protected List doInBackground(String... paths) { - File storage = new File(paths[0]); - - // array of valid image file extensions - String[] mediaTypes = browser.get().getActivity().getResources().getStringArray(array); - FilenameFilter[] filter = new FilenameFilter[mediaTypes.length]; - - int i = 0; - for (final String type : mediaTypes) { - filter[i] = new FilenameFilter() { - - public boolean accept(File dir, String name) { - return !dir.getName().equals("obb") && !dir.getName().equals("cache") - && !dir.getName().startsWith(".") && !name.startsWith(".") - && (array != R.array.flash || name.startsWith("dc_")) - && (browser.get().searchQuery == null - || name.toLowerCase(Locale.getDefault()).contains( - browser.get().searchQuery.toLowerCase(Locale.getDefault()))) - && StringUtils.endsWithIgnoreCase(name, "." + type); - } - }; - i++; - } - - FileUtils fileUtils = new FileUtils(); - int recurse = 2; - if (array == R.array.flash) { - recurse = 4; - } - Collection files = fileUtils.listFiles(storage, filter, recurse); - return (List) files; - } - - @Override - protected void onPostExecute(List items) { - if (items != null && !items.isEmpty()) { - LinearLayout list = (LinearLayout) browser.get().getActivity().findViewById(R.id.game_list); - if (list.getChildCount() > 0) { - list.removeAllViews(); - } - - String heading = browser.get().getActivity().getString(R.string.games_listing); - browser.get().createListHeader(heading, list, array == R.array.images); - for (int i = 0; i < items.size(); i++) { - browser.get().createListItem(list, items.get(i), i, array == R.array.images); - } - list.invalidate(); - } else { - browser.get().browseStorage(array == R.array.images); - } - } - } - - private void browseStorage(boolean images) { - if (images) { - (new navigate(this)).executeOnExecutor( - AsyncTask.THREAD_POOL_EXECUTOR, new File(home_directory)); - } else { - if (game_directory.equals(sdcard.getAbsolutePath())) { - HashSet extStorage = FileBrowser.getExternalMounts(); - if (extStorage != null && !extStorage.isEmpty()) { - for (String sd : extStorage) { - String sdCardPath = sd.replace("mnt/media_rw", "storage"); - if (!sdCardPath.equals(sdcard.getAbsolutePath())) { - if (new File(sdCardPath).canRead()) { - (new navigate(this)).execute(new File(sdCardPath)); - return; - } - } - } - } - } - (new navigate(this)).executeOnExecutor( - AsyncTask.THREAD_POOL_EXECUTOR, new File(game_directory)); - } - } - - private void createListHeader(String header_text, View view, boolean hasBios) { - if (hasBios && getResources().getString(R.string.flavor).equals("dreamcast")) { - final View childview = getActivity().getLayoutInflater().inflate( - R.layout.bios_list_item, null, false); - - ((TextView) childview.findViewById(R.id.item_name)).setText(R.string.boot_bios); - - childview.setTag(null); - - orig_bg = childview.getBackground(); - - childview.findViewById(R.id.childview).setOnClickListener( - new OnClickListener() { - public void onClick(View view) { - vib.vibrate(50); - mCallback.onGameSelected(Uri.EMPTY); - vib.vibrate(250); - } - }); - - childview.findViewById(R.id.childview).setOnTouchListener( - new OnTouchListener() { - @SuppressWarnings("deprecation") - public boolean onTouch(View view, MotionEvent arg1) { - if (arg1.getActionMasked() == MotionEvent.ACTION_DOWN) { - view.setBackgroundColor(0xFF4F3FFF); - } else if (arg1.getActionMasked() == MotionEvent.ACTION_CANCEL - || arg1.getActionMasked() == MotionEvent.ACTION_UP) { - view.setBackgroundDrawable(orig_bg); - } - - return false; - - } - }); - ((ViewGroup) view).addView(childview); - } - - final View headerView = getActivity().getLayoutInflater().inflate( - R.layout.browser_fragment_header, null, false); - ((ImageView) headerView.findViewById(R.id.item_icon)) - .setImageResource(R.drawable.open_folder); - ((TextView) headerView.findViewById(R.id.item_name)) - .setText(header_text); - ((TextView) headerView.findViewById(R.id.item_name)) - .setTypeface(Typeface.DEFAULT_BOLD); - ((ViewGroup) view).addView(headerView); - - } - - private void createListItem(LinearLayout list, final File game, final int index, final boolean isGame) { - final View childview = getActivity().getLayoutInflater().inflate( - R.layout.browser_fragment_item, null, false); - - XMLParser xmlParser = new XMLParser(game, index, mPrefs); - xmlParser.setViewParent(getActivity(), childview, mCallback); - orig_bg = childview.getBackground(); - - childview.findViewById(R.id.childview).setOnClickListener( - new OnClickListener() { - public void onClick(View view) { - if (isGame) { - vib.vibrate(50); - Uri gameUri = Uri.EMPTY; - if (game != null) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - gameUri = FileProvider.getUriForFile(getActivity(), - "com.reicast.emulator.provider", game); - } else { - gameUri = Uri.fromFile(game); - } - } - mCallback.onGameSelected(gameUri); - vib.vibrate(250); - } else { - vib.vibrate(50); - home_directory = game.getAbsolutePath().substring(0, - game.getAbsolutePath().lastIndexOf(File.separator)) - .replace("/data", ""); - if (requireDataBIOS()) { - showToastMessage(getActivity().getString(R.string.config_data, - home_directory), Snackbar.LENGTH_LONG); - } - mPrefs.edit().putString(Config.pref_home, home_directory).apply(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - mCallback.onFolderSelected(FileProvider.getUriForFile(getActivity(), - "com.reicast.emulator.provider", new File(home_directory))); - } else { - mCallback.onFolderSelected( - Uri.fromFile(new File(home_directory))); - } - //JNIdc.config(home_directory); - } - } - }); - - childview.findViewById(R.id.childview).setOnTouchListener( - new OnTouchListener() { - @SuppressWarnings("deprecation") - public boolean onTouch(View view, MotionEvent arg1) { - if (arg1.getActionMasked() == MotionEvent.ACTION_DOWN) { - view.setBackgroundColor(0xFF4F3FFF); - } else if (arg1.getActionMasked() == MotionEvent.ACTION_CANCEL - || arg1.getActionMasked() == MotionEvent.ACTION_UP) { - view.setBackgroundDrawable(orig_bg); - } - return false; - - } - }); - list.addView(childview); - xmlParser.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, game.getName()); - } - - private static class navigate extends AsyncTask> { - - private WeakReference browser; - private String heading; - private File parent; - - navigate(FileBrowser context) { - browser = new WeakReference<>(context); - } - - private final class DirSort implements Comparator { - @Override - public int compare(File filea, File fileb) { - return ((filea.isFile() ? "a" : "b") + filea.getName().toLowerCase( - Locale.getDefault())) - .compareTo((fileb.isFile() ? "a" : "b") - + fileb.getName().toLowerCase(Locale.getDefault())); - } - } - - @Override - protected void onPreExecute() { - LinearLayout listView = (LinearLayout) - browser.get().getActivity().findViewById(R.id.game_list); - if (listView.getChildCount() > 0) - listView.removeAllViews(); - } - - @Override - protected List doInBackground(File... paths) { - heading = paths[0].getAbsolutePath(); - - ArrayList list = new ArrayList<>(); - - File flist[] = paths[0].listFiles(); - parent = paths[0].getParentFile(); - - list.add(null); - - if (parent != null) { - list.add(parent); - } - - if (flist != null) { - Arrays.sort(flist, new DirSort()); - Collections.addAll(list, flist); - } - return list; - } - - @Override - protected void onPostExecute(List list) { - if (list != null && !list.isEmpty()) { - LinearLayout listView = (LinearLayout) - browser.get().getActivity().findViewById(R.id.game_list); - browser.get().createListHeader(heading, listView, false); - for (final File file : list) { - if (file != null && !file.isDirectory() && !file.getAbsolutePath().equals("/data")) - continue; - final View childview = browser.get().getActivity().getLayoutInflater().inflate( - R.layout.browser_fragment_item, null, false); - - if (file == null) { - ((TextView) childview.findViewById(R.id.item_name)).setText(R.string.folder_select); - } else if (file == parent) - ((TextView) childview.findViewById(R.id.item_name)).setText(".."); - else - ((TextView) childview.findViewById(R.id.item_name)).setText(file.getName()); - - ((ImageView) childview.findViewById(R.id.item_icon)).setImageResource(file == null - ? R.drawable.ic_settings: file.isDirectory() - ? R.drawable.ic_folder_black_24dp : R.drawable.disk_unknown); - - childview.setTag(file); - - browser.get().orig_bg = childview.getBackground(); - - // vw.findViewById(R.id.childview).setBackgroundColor(0xFFFFFFFF); - - childview.findViewById(R.id.childview).setOnClickListener( - new OnClickListener() { - public void onClick(View view) { - if (file != null && file.isDirectory()) { - (new navigate(browser.get())).executeOnExecutor( - AsyncTask.THREAD_POOL_EXECUTOR, file); - ScrollView sv = (ScrollView) browser.get().getActivity() - .findViewById(R.id.game_scroller); - sv.scrollTo(0, 0); - browser.get().vib.vibrate(50); - } else if (view.getTag() == null) { - browser.get().vib.vibrate(250); - - if (browser.get().games) { - browser.get().game_directory = heading; - browser.get().mPrefs.edit().putString( - Config.pref_games, heading).apply(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - browser.get().mCallback.onFolderSelected(FileProvider - .getUriForFile(browser.get().getActivity(), - "com.reicast.emulator.provider", - new File(browser.get().game_directory))); - } else { - browser.get().mCallback.onFolderSelected(Uri.fromFile( - new File(browser.get().game_directory))); - } - - - } else { - browser.get().home_directory = heading - .replace("/data", ""); - browser.get().mPrefs.edit().putString( - Config.pref_home, browser.get().home_directory).apply(); - if (browser.get().requireDataBIOS()) { - browser.get().showToastMessage(browser.get().getActivity() - .getString(R.string.config_data, browser.get() - .home_directory), Snackbar.LENGTH_LONG); - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - browser.get().mCallback.onFolderSelected(FileProvider - .getUriForFile(browser.get().getActivity(), - "com.reicast.emulator.provider", - new File(browser.get().home_directory))); - } else { - browser.get().mCallback.onFolderSelected(Uri.fromFile( - new File(browser.get().home_directory))); - } - //JNIdc.config(browser.get().home_directory); - } - - } - } - }); - - childview.findViewById(R.id.childview).setOnTouchListener( - new OnTouchListener() { - @SuppressWarnings("deprecation") - public boolean onTouch(View view, MotionEvent event) { - if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { - view.setBackgroundColor(0xFF4F3FFF); - } else if (event.getActionMasked() == MotionEvent.ACTION_CANCEL - || event.getActionMasked() == MotionEvent.ACTION_UP) { - view.setBackgroundDrawable(browser.get().orig_bg); - } - - return false; - - } - }); - listView.addView(childview); - } - listView.invalidate(); - } - } - } - - private boolean requireDataBIOS() { - File data_directory = new File(home_directory, "data"); - if (data_directory.exists() && data_directory.isDirectory()) { - File bios = new File(home_directory, "data/dc_boot.bin"); - File flash = new File(home_directory, "data/dc_flash.bin"); - return !(bios.exists() && flash.exists()); - } else { - if (data_directory.mkdirs()) { - File bios = new File(home_directory, "dc_boot.bin"); - if (bios.renameTo(new File(home_directory, "data/dc_boot.bin"))) { - return !new File(home_directory, "dc_flash.bin").renameTo( - new File(home_directory, "data/dc_flash.bin")); - } - } - return true; - } - } - - private void showToastMessage(String message, int duration) { - ConstraintLayout layout = (ConstraintLayout) getActivity().findViewById(R.id.mainui_layout); - Snackbar snackbar = Snackbar.make(layout, message, duration); - View snackbarLayout = snackbar.getView(); - TextView textView = (TextView) snackbarLayout.findViewById( - android.support.design.R.id.snackbar_text); - textView.setGravity(Gravity.CENTER_VERTICAL); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) - textView.setTextAlignment(View.TEXT_ALIGNMENT_GRAVITY); - Drawable drawable; - if (android.os.Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { - drawable = getResources().getDrawable( - R.drawable.ic_subdirectory_arrow_right, getActivity().getTheme()); - } else { - drawable = VectorDrawableCompat.create(getResources(), - R.drawable.ic_subdirectory_arrow_right, getActivity().getTheme()); - } - textView.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null); - textView.setCompoundDrawablePadding(getResources() - .getDimensionPixelOffset(R.dimen.snackbar_icon_padding)); - snackbar.show(); - } } diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/GL2JNIActivity.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/GL2JNIActivity.java index 7b958fa71..79c60e653 100644 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/GL2JNIActivity.java +++ b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/GL2JNIActivity.java @@ -2,16 +2,19 @@ package com.reicast.emulator; import android.os.Bundle; import android.preference.PreferenceManager; -import android.support.v4.app.ActivityCompat; +import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; +import android.widget.RelativeLayout; import com.reicast.emulator.config.Config; import com.reicast.emulator.emu.GL2JNIView; import com.reicast.emulator.emu.JNIdc; import com.reicast.emulator.periph.VJoy; -public class GL2JNIActivity extends BaseGLActivity implements ActivityCompat.OnRequestPermissionsResultCallback { +public class GL2JNIActivity extends BaseGLActivity { + private static ViewGroup mLayout; // used for text input + @Override protected void onCreate(Bundle icicle) { requestWindowFeature(Window.FEATURE_NO_TITLE); @@ -28,11 +31,10 @@ public class GL2JNIActivity extends BaseGLActivity implements ActivityCompat.OnR // Create the actual GLES view mView = new GL2JNIView(GL2JNIActivity.this, false, prefs.getInt(Config.pref_renderdepth, 24), 8); - setContentView(mView); + mLayout = new RelativeLayout(this); + mLayout.addView(mView); - //setup mic - if (Emulator.micPluggedIn()) - requestRecordAudioPermission(); + setContentView(mLayout); } public void screenGrab() { @@ -40,23 +42,18 @@ public class GL2JNIActivity extends BaseGLActivity implements ActivityCompat.OnR } @Override - protected void onPause() { - super.onPause(); + protected void doPause() { ((GL2JNIView)mView).onPause(); JNIdc.pause(); } @Override - protected void onDestroy() { - super.onDestroy(); - if (mView != null) { - ((GL2JNIView)mView).onDestroy(); - } + protected boolean isSurfaceReady() { + return true; // FIXME } @Override - protected void onResume() { - super.onResume(); + protected void doResume() { ((GL2JNIView)mView).onResume(); JNIdc.resume(); } diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/MainActivity.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/MainActivity.java deleted file mode 100644 index 89aeb7e55..000000000 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/MainActivity.java +++ /dev/null @@ -1,195 +0,0 @@ -package com.reicast.emulator; - -import android.Manifest; -import android.app.AlertDialog; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.support.annotation.NonNull; -import android.support.v4.app.ActivityCompat; -import android.support.v7.app.AppCompatActivity; -import android.view.View; -import android.view.View.OnSystemUiVisibilityChangeListener; -import android.view.WindowManager; - -import com.reicast.emulator.debug.GenerateLogs; -import com.reicast.emulator.emu.JNIdc; - -import java.lang.Thread.UncaughtExceptionHandler; -import java.util.List; - -public class MainActivity extends AppCompatActivity { - private static final int PERMISSION_REQUEST = 1001; - - private boolean hasAndroidMarket = false; - private boolean renderer_started = false; - private Uri gameUri; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - getWindow().getDecorView().setOnSystemUiVisibilityChangeListener (new OnSystemUiVisibilityChangeListener() { - public void onSystemUiVisibilityChange(int visibility) { - if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) { - getWindow().getDecorView().setSystemUiVisibility( - View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_FULLSCREEN - | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); - } - } - }); - } else { - getWindow().setFlags (WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - } - - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - - String prior_error = prefs.getString("prior_error", null); - if (prior_error != null) { - displayLogOutput(prior_error); - prefs.edit().remove("prior_error").apply(); - } - UncaughtExceptionHandler mUEHandler = new Thread.UncaughtExceptionHandler() { - public void uncaughtException(Thread t, Throwable error) { - if (error != null) { - StringBuilder output = new StringBuilder(); - for (StackTraceElement trace : error.getStackTrace()) { - output.append(trace.toString()); - output.append("\n"); - } - prefs.edit().putString("prior_error", output.toString()).apply(); - error.printStackTrace(); - android.os.Process.killProcess(android.os.Process.myPid()); - System.exit(0); - } - } - }; - Thread.setDefaultUncaughtExceptionHandler(mUEHandler); - - Intent market = new Intent(Intent.ACTION_VIEW, Uri.parse("market://search?q=dummy")); - if (isCallable(market)) { - hasAndroidMarket = true; - } - - if (!getFilesDir().exists()) { - getFilesDir().mkdir(); - } - - // When viewing a resource, pass its URI to the native code for opening - Intent intent = getIntent(); - if (intent.getAction() != null) { - if (intent.getAction().equals(Intent.ACTION_VIEW)) { - gameUri = Uri.parse(intent.getData().toString()); - // Flush the intent to prevent multiple calls - getIntent().setData(null); - setIntent(null); - } - } - - if (prior_error == null) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - ActivityCompat.requestPermissions(MainActivity.this, - new String[]{ - Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.WRITE_EXTERNAL_STORAGE - }, - PERMISSION_REQUEST); - } else - startNativeRenderer(); - } - } - - public void generateErrorLog() { - new GenerateLogs(MainActivity.this).execute(getFilesDir().getAbsolutePath()); - } - - /** - * Display a dialog to notify the user of prior crash - * - * @param error - * A generalized summary of the crash cause - */ - private void displayLogOutput(final String error) { - AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); - builder.setTitle(R.string.report_issue); - builder.setMessage(error); - builder.setPositiveButton(R.string.report, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - generateErrorLog(); - startNativeRenderer(); - } - }); - builder.setNegativeButton(R.string.dismiss, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - startNativeRenderer(); - } - }); - builder.create(); - builder.show(); - } - - private void startNativeRenderer() { - if (renderer_started) - return; - renderer_started = true; - - if (gameUri != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - gameUri = Uri.parse(gameUri.toString().replace("content://" - + gameUri.getAuthority() + "/external_files", "/storage")); - } - - if (gameUri != null) - JNIdc.setGameUri(gameUri.toString()); - Intent intent = new Intent("com.reicast.EMULATOR", - //gameUri, getApplicationContext(), GL2JNIActivity.class); - gameUri, getApplicationContext(), NativeGLActivity.class); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); - startActivity(intent); - } - - @Override - public void onWindowFocusChanged(boolean hasFocus) { - super.onWindowFocusChanged(hasFocus); - if (hasFocus && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - getWindow().getDecorView().setSystemUiVisibility( - View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_FULLSCREEN - | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); - } - } - - private boolean isCallable(Intent intent) { - List list = getPackageManager().queryIntentActivities( - intent, PackageManager.MATCH_DEFAULT_ONLY); - return list.size() > 0; - } - - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - if (permissions.length > 0 && (Manifest.permission.READ_EXTERNAL_STORAGE.equals(permissions[0]) || Manifest.permission.WRITE_EXTERNAL_STORAGE.equals(permissions[0])) - && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - startNativeRenderer(); - } - } - -} diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/NativeGLActivity.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/NativeGLActivity.java index 5a02ff309..830aba1ac 100644 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/NativeGLActivity.java +++ b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/NativeGLActivity.java @@ -2,7 +2,9 @@ package com.reicast.emulator; import android.os.Bundle; import android.support.annotation.Nullable; +import android.view.ViewGroup; import android.view.Window; +import android.widget.RelativeLayout; import com.reicast.emulator.emu.JNIdc; import com.reicast.emulator.emu.NativeGLView; @@ -10,6 +12,8 @@ import com.reicast.emulator.periph.VJoy; public final class NativeGLActivity extends BaseGLActivity { + private static ViewGroup mLayout; // used for text input + @Override protected void onCreate(@Nullable Bundle savedInstanceState) { requestWindowFeature(Window.FEATURE_NO_TITLE); @@ -17,28 +21,28 @@ public final class NativeGLActivity extends BaseGLActivity { super.onCreate(savedInstanceState); // Create the actual GL view -// mView = new NativeGLView(this); -// setContentView(mView); - setContentView(R.layout.nativegl_content); - mView = findViewById(R.id.glView); + mView = new NativeGLView(this); + mLayout = new RelativeLayout(this); + mLayout.addView(mView); - //setup mic - if (Emulator.micPluggedIn()) - requestRecordAudioPermission(); + setContentView(mLayout); } @Override - protected void onPause() { - super.onPause(); + protected void doPause() { ((NativeGLView)mView).pause(); } @Override - protected void onResume() { - super.onResume(); + protected void doResume() { ((NativeGLView)mView).resume(); } + @Override + public boolean isSurfaceReady() { + return ((NativeGLView)mView).isSurfaceReady(); + } + // Called from native code private void VJoyStartEditing() { vjoy_d_cached = VJoy.readCustomVjoyValues(getApplicationContext()); diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/XMLParser.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/XMLParser.java deleted file mode 100644 index 9b2c2be25..000000000 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/XMLParser.java +++ /dev/null @@ -1,367 +0,0 @@ -package com.reicast.emulator; - -import android.app.AlertDialog; -import android.content.Context; -import android.content.SharedPreferences; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.os.AsyncTask; -import android.os.Build; -import android.view.View; -import android.view.View.OnLongClickListener; -import android.widget.ImageView; -import android.widget.TextView; - -import com.reicast.emulator.FileBrowser.OnItemSelectedListener; -import com.reicast.emulator.config.Config; - -import org.apache.commons.io.FilenameUtils; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; - -import java.io.BufferedInputStream; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.StringReader; -import java.io.UnsupportedEncodingException; -import java.lang.ref.WeakReference; -import java.net.HttpURLConnection; -import java.net.URL; -import java.net.URLConnection; -import java.net.URLEncoder; -import java.util.Locale; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; - -public class XMLParser extends AsyncTask { - - private SharedPreferences mPrefs; - private File game; - private int index; - private OnItemSelectedListener mCallback; - private String game_name; - private Drawable game_icon; - private String gameId; - private String game_details; - - private WeakReference mContext; - private WeakReference childview; - - XMLParser(File game, int index, SharedPreferences mPrefs) { - this.mPrefs = mPrefs; - this.game = game; - this.index = index; - } - - public void setViewParent(Context reference, View childview, OnItemSelectedListener mCallback) { - this.mContext = new WeakReference<>(reference); - this.childview = new WeakReference<>(childview); - this.mCallback = mCallback; - initializeDefaults(); - } - - private void setGameID(String id) { - this.gameId = id; - } - - @Override - protected String doInBackground(String... params) { - String filename = game_name = params[0]; - if (isNetworkAvailable() && mPrefs.getBoolean(Config.pref_gamedetails, false)) { - String xmlUrl; - if (gameId != null) { - xmlUrl = "http://legacy.thegamesdb.net/api/GetGame.php?platform=sega+dreamcast&id=" + gameId; - } else { - filename = filename.substring(0, filename.lastIndexOf(".")); - try { - filename = URLEncoder.encode(filename, "UTF-8"); - } catch (UnsupportedEncodingException e) { - filename = filename.replace(" ", "+"); - } - xmlUrl = "http://legacy.thegamesdb.net/api/GetGamesList.php?platform=sega+dreamcast&name=" + filename; - } - - try { - HttpURLConnection conn = (HttpURLConnection) new URL(xmlUrl).openConnection(); - conn.setRequestMethod("POST"); - conn.setDoInput(true); - - InputStream in = conn.getInputStream(); - BufferedReader streamReader = new BufferedReader( - new InputStreamReader(in, "UTF-8")); - StringBuilder responseStrBuilder = new StringBuilder(); - - String inputStr; - while ((inputStr = streamReader.readLine()) != null) - responseStrBuilder.append(inputStr); - - in.close(); - return responseStrBuilder.toString(); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } - } - return null; - } - - @Override - protected void onPostExecute(String gameData) { - if (gameData != null) { - try { - Document doc = getDomElement(gameData); - if (doc.getElementsByTagName("Game") != null) { - Element root = (Element) doc.getElementsByTagName("Game").item(0); - if (gameId == null) { - XMLParser xmlParser = new XMLParser(game, index, mPrefs); - xmlParser.setViewParent(mContext.get(), childview.get(), mCallback); - xmlParser.setGameID(getValue(root, "id")); - xmlParser.execute(game_name); - } else if (root != null) { - String name = getValue(root, "GameTitle"); - if (!name.equals("")) - game_name = name + " [" + FilenameUtils.getExtension( - game.getName()).toUpperCase(Locale.getDefault()) + "]"; - game_details = getValue(root, "Overview"); - getBoxart((Element) root.getElementsByTagName("Images").item(0)); - } - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - if (childview.get() != null) { - ((TextView) childview.get().findViewById(R.id.item_name)).setText(game_name); - - if (mPrefs.getBoolean(Config.pref_gamedetails, false)) { - childview.get().findViewById(R.id.childview).setOnLongClickListener( - new OnLongClickListener() { - public boolean onLongClick(View view) { - new AlertDialog.Builder(mContext.get()).setCancelable(true).setIcon(game_icon) - .setTitle(mContext.get().getString(R.string.game_details, game_name)) - .setMessage(game_details).create().show(); - return true; - } - }); - } - - childview.get().setTag(game_name); - } - } - - private void getBoxart(Element images) { - Element boxart = null; - if (images.getElementsByTagName("boxart").getLength() > 1) { - boxart = (Element) images.getElementsByTagName("boxart").item(1); - } else if (images.getElementsByTagName("boxart").getLength() == 1) { - boxart = (Element) images.getElementsByTagName("boxart").item(0); - } - if (boxart != null) { - decodeBitmapIcon icon = new decodeBitmapIcon(mContext.get()); - icon.setListener(new decodeBitmapIcon.decodeBitmapIconListener() { - @Override - public void onDecodeBitmapIconFinished(Bitmap gameImage) { - if (childview.get() != null && gameImage != null) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - ((ImageView) childview.get().findViewById( - R.id.item_icon)).setImageTintList(null); - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - game_icon = new BitmapDrawable( - mContext.get().getResources(), gameImage); - } else { - //noinspection deprecation - game_icon = new BitmapDrawable(gameImage); - } - ((ImageView) childview.get().findViewById( - R.id.item_icon)).setImageDrawable(game_icon); - } - } - }); - icon.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, - "https://cdn.thegamesdb.net/images/thumb/" - + getElementValue(boxart) - .replace("original/", "")); - } - } - - private void initializeDefaults() { - game_details = mContext.get().getString(R.string.info_unavailable); - final String nameLower = game.getName().toLowerCase(Locale.getDefault()); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - game_icon = mContext.get().getResources().getDrawable( - game.isDirectory() ? R.drawable.open_folder - : nameLower.endsWith(".gdi") ? R.mipmap.disk_gdi - : nameLower.endsWith(".chd") ? R.mipmap.disk_chd - : nameLower.endsWith(".cdi") ? R.mipmap.disk_cdi - : R.mipmap.disk_unknown); - } else { - game_icon = mContext.get().getResources().getDrawable( - game.isDirectory() ? R.drawable.open_folder - : nameLower.endsWith(".gdi") ? R.drawable.gdi - : nameLower.endsWith(".chd") ? R.drawable.chd - : nameLower.endsWith(".cdi") ? R.drawable.cdi - : R.drawable.disk_unknown); - } - ((ImageView) childview.get().findViewById(R.id.item_icon)).setImageDrawable(game_icon); - } - - private boolean isNetworkAvailable() { - ConnectivityManager connectivityManager = (ConnectivityManager) mContext.get() - .getSystemService(Context.CONNECTIVITY_SERVICE); - NetworkInfo mWifi = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); - NetworkInfo mMobile = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); - NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo(); - if (mMobile != null && mWifi != null) { - return mMobile.isAvailable() || mWifi.isAvailable(); - } else { - return activeNetworkInfo != null && activeNetworkInfo.isConnected(); - } - } - - private Document getDomElement(String xml) { - Document doc = null; - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - try { - - DocumentBuilder db = dbf.newDocumentBuilder(); - - InputSource is = new InputSource(); - is.setCharacterStream(new StringReader(xml)); - doc = db.parse(is); - - } catch (ParserConfigurationException e) { - - return null; - } catch (SAXException e) { - - return null; - } catch (IOException e) { - - return null; - } - - return doc; - } - - private String getValue(Element item, String str) { - if (item != null) { - NodeList n = item.getElementsByTagName(str); - return this.getElementValue(n.item(0)); - } else { - return ""; - } - } - - private String getElementValue(Node elem) { - Node child; - if (elem != null) { - if (elem.hasChildNodes()) { - for (child = elem.getFirstChild(); child != null; child = child - .getNextSibling()) { - if (child.getNodeType() == Node.TEXT_NODE) { - return child.getNodeValue(); - } - } - } - } - return ""; - } - - private static class decodeBitmapIcon extends AsyncTask { - private WeakReference mContext; - private decodeBitmapIconListener listener; - - decodeBitmapIcon(Context reference) { - this.mContext = new WeakReference<>(reference); - } - - @Override - protected Bitmap doInBackground(String... params) { - try { - String index = params[0].substring(params[0].lastIndexOf( - "/") + 1, params[0].lastIndexOf(".")); - File file = new File(mContext.get().getExternalFilesDir( - null) + "/images", index + ".png"); - if (file.exists()) { - return BitmapFactory.decodeFile(file.getAbsolutePath()); - } else { - URL updateURL = new URL(params[0]); - URLConnection conn1 = updateURL.openConnection(); - InputStream im = conn1.getInputStream(); - BufferedInputStream bis = new BufferedInputStream(im, 512); - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; - BitmapFactory.decodeStream(bis, null, options); - int heightRatio = (int) Math.ceil(options.outHeight / (float) 72); - int widthRatio = (int) Math.ceil(options.outWidth / (float) 72); - if (heightRatio > 1 || widthRatio > 1) { - if (heightRatio > widthRatio) { - options.inSampleSize = heightRatio; - } else { - options.inSampleSize = widthRatio; - } - } - options.inJustDecodeBounds = false; - bis.close(); - im.close(); - conn1 = updateURL.openConnection(); - im = conn1.getInputStream(); - bis = new BufferedInputStream(im, 512); - Bitmap bitmap = BitmapFactory.decodeStream(bis, null, options); - bis.close(); - im.close(); - OutputStream fOut; - if (!file.getParentFile().exists()) { - file.getParentFile().mkdir(); - } - try { - fOut = new FileOutputStream(file, false); - bitmap.compress(Bitmap.CompressFormat.PNG, 100, fOut); - fOut.flush(); - fOut.close(); - } catch (Exception ex) { - ex.printStackTrace(); - } - return bitmap; - } - } catch (IOException e) { - e.printStackTrace(); - } - return null; - } - - @Override - protected void onPostExecute(Bitmap gameImage) { - super.onPostExecute(gameImage); - if (listener != null) { - listener.onDecodeBitmapIconFinished(gameImage); - } - } - - void setListener(decodeBitmapIconListener listener) { - this.listener = listener; - } - - public interface decodeBitmapIconListener { - void onDecodeBitmapIconFinished(Bitmap gameImage); - } - } -} diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/config/Compat.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/config/Compat.java new file mode 100644 index 000000000..4105c50a1 --- /dev/null +++ b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/config/Compat.java @@ -0,0 +1,515 @@ +package com.reicast.emulator.config; + +public class Compat { + public int isVGACompatible(String gameId) { + int vgaMode; // 0 = VGA, 1 = Patchable, 3 = TV + switch (gameId) { + // VGA Compatible + case "T36803N": //102 Dalmatians puppies to the Rescue + case "T36813D05": //102 Dalmatians puppies to the Rescue + case "51064": // 18 Wheeler American Pro Trucker + case "MK51064": // 18 Wheeler American Pro Trucker + case "T9708N": // 4 Wheel Thunder + case "T9706D": // 4 Wheel Thunder + case "T41903N": // 4x4 Evolution + case "MK51190": // 90 Minutes Championship Football + case "T40201N": // Aerowings + case "T40202D50": // Aerowings + case "T40210N": // Aerowings 2 + case "MK51171": // Alien Front Online + case "T15117N": // Alone in the Dark The New Nightmare + case "T15112D05": // Alone in the Dark The New Nightmare + case "T40301N": // Armada + case "T15130N": // Atari Aniversary Edition + case "T44102N": // Bang! Gunship Elite + case "T13001N": // Blue Stinger + case "T13001D58": // Blue Stinger + case "51065": // Bomberman Online + case "T13007N": // Buzz Lightyear of Star Command + case "T13005D05": // Buzz Lightyear of Star Command + case "T1215N": // Cannon Spike + case "T46601D50": // Cannon Spike + case "T1218N": // Capcom Vs. SNK + case "T5701N": // Carrier + case "T44901D50": // Carrier + case "T40602N": // Centepede + case "T41403N": // Championship Surfer + case "T41402D50": // Championship Surfer + case "T15127N": // Charge'N Blast + case "T44902D50": // Charge'N Blast + case "T36811N": // Chicken Run + case "T36814D05": // Chicken Run + case "51049": // ChuChu Rocket! + case "MK5104950": // ChuChu Rocket! + case "51160": // Confidential Mission + case "MK5116050": // Confidential Mission + case "51035": // Crazy Taxi + case "MK5103550": // Crazy Taxi + case "51136": // Crazy Taxi 2 + case "MK5113650": // Crazy Taxi 2 + case "51036": // D-2 + case "T8120N": // Dave Mirra BMX + case "T8120D59": // Dave Mirra BMX + case "51037": // Daytona USA + case "MK5103750": // Daytona USA 2001 + case "T3601N": // Dead or Alive 2 + case "T8116D05": // Dead or Alive 2 & ECW Hardcore Revolution + case "T2401N": // Death Crimson OX + case "T15112N": // Demolition Racer + case "T17717N": // Dinosaur + case "T17714D05": // Donald Duck: Quack Attack + case "T40203N": // Draconus: Cult of the Wyrm + case "T17720N": // Dragon Riders: Chronicles of Pern + case "T17716D91": // Dragon Riders: Chronicles of Pern + case "T8113N": // Ducati World Racing Challenge + case "T8121D05": // Ducati World + case "51013": // Dynamite Cop! + case "MK5101350": // Dynamite Cop! + case "51033": // Ecco the Dolphin: Defender of the Future + case "MK5103350": // Ecco the Dolphin: Defender of the Future + case "T41601N": // Elemental Gimmick Gear + case "T9504D50": // ESPN International Track n Field + case "T9505N": // ESPN NBA2Night + case "T7015D50": // European Super League + case "T46605D71": // Evil Twin Cyprien's Chronicles + case "T17706N": // Evolution: The World of Sacred Device + case "T17711N": // Evolution 2: Far Off Promise + case "T45005D50": // Evolution 2: Far Off Promise + case "T22903D50": // Exhibition of Speed + case "T17706D50": // F1 Racing Championship + case "T3001N": // F1 World Grand Prix + case "T3001D50": // F1 World Grand Prix + case "T3002D50": // F1 World Grand Prix II + case "T8119N": // F355 Challenge: Passione Rossa + case "T8118D05": // F355 Challenge: Passione Rossa + case "T44306N": // Fatal Fury: Mark of the Wolf + case "T35801N": // Fighting Force 2 + case "T36802D05": // Fighting Force 2 + case "MK5115450": // Fighting Vipers 2 + case "51007": // Flag to Flag + case "51114": // Floigan Brothers Episode 1 + case "MK5111450": // Floigan Brothers Episode 1 + case "T40604N": // Frogger 2 Swampies Revenge + case "T8107N": // Fur Fighters + case "T8113D05": // Fur Fighters + case "T9710N": // Gauntlet Legends + case "T9707D51": // Gauntlet Legends + case "T1209N": // Giga Wing + case "T7008D50": // Giga Wing + case "T1222N": // Giga Wing 2 + case "T42102N": // Grand Theft Auto II + case "T40502D61": // Grand Theft Auto II + case "T17716N": // Grandia II + case "T17715D05": // Grandia II + case "T9512N": // Grintch, The + case "T9503D76": // Grintch, The + case "T13301N": // Gundam Side Story 0079 + case "51041": // Headhunter + case "T1223N": // Heavy Metal: Geomatrix + case "MK5104550": // House of the Dead 2 + case "T11008N": // Hoyle Casino + case "T46001N": // Illbleed + case "T12503N": // Incoming + case "T40701D50": // Incoming + case "T41302N": // Industrial Spy: Operation Espionage + case "T8104N": // Jeremy McGrath Supercross 2000 + case "T8114D05": // Jeremy McGrath Supercross 2000 + case "BKL8317601ENG": // Jeremy McGrath Supercross 2000 + case "51058": // Jet Grind Radio + case "MK5105850": // Jet Set Radio + case "T7001D": // Jimmy White's 2: Cueball + case "T22903N": // Kao the Kangeroo + case "T22902D50": // Kao the Kangeroo + case "T41901N": // KISS: Psycho Circus: The Nightmare Child + case "T40506D50": // KISS: Psycho Circus: The Nightmare Child + case "T36802N": // Legacy of Kain: Soul Reaver + case "T36803D05": // Legacy of Kain: Soul Reaver + case "T15108D50": // Loony Toons Space Race + case "T40208N": // Mag Force Racing + case "T40207D50": // Mag Force Racing + case "T36804N": // Magical Racing Tour + case "T36809D50": // Magical Racing Tour + case "51050": // Maken X + case "MK5105050": // Maken X + case "T1221N": // Mars Matrix + case "T1202N": // Marvel vs. Capcom: Clash of Super Heroes + case "T7002D61": // Marvel vs. Capcom: Clash of Super Heroes + case "T1212N": // Marvel vs. Capcom 2 + case "T7010D50": // Marvel vs. Capcom 2: New Age of Heroes + case "T13005N": // Mat Hoffman's Pro BMX + case "T41402N": // Max Steel Covert Missions + case "T11002N": // Maximum Pool + case "T12502N": // MDK2 + case "T12501D61": // MDK2 + case "51012": // Metropolis Street Racer + case "MK5102250": // Metropolis Street Racer + case "T9713N": // Midway Greatest Arcade Hits Volume 1 + case "T9714N": // Midway Greatest Arcade Hits Volume 2 + case "T40508D": // Moho (Ball Breakers) + case "T17701N": // Monaco Grand Prix + case "T45006D50": // Racing Simulation 2: On-line Monaco Grand Prix + case "T9701D61": // Mortal Kombat Gold + case "T1402N": // Mr Driller + case "T7020D50": // Mr Driller + case "T1403N": // Namco Museum + case "51004": // NBA 2K + case "MK5100453": // NBA 2K + case "51063": // NBA 2K1 + case "51178": // NBA 2K2 + case "MK5117850": // NBA 2K2 + case "T9709N": // NBA Hoopz + case "51176": // NCAA College Football 2K2 + case "51003": // NFL 2K + case "51062": // NFL 2K1 + case "51168": // NFL 2K2 + case "T9703N": // NFL Blitz 2000 + case "T9712N": // NFL Blitz 2001 + case "T8101N": // NFL Quarterback Club 2000 + case "T8102D05": // NFL Quarterback Club 2000 + case "T8115N": // NFL Quarterback Club 2001 + case "MK5102589": // NHL 2K + case "51182": // NHL 2K2 + case "T9504N": // Nightmare Creatures II + case "T9502D76": // Nightmare Creatures II + case "T36807N": // Omikron The Nomad Soul + case "T36805D09": // Nomad Soul, The + case "51140": // Ooga Booga + case "51102": // OutTrigger: International Counter Terrorism Special Force + case "MK5110250": // OutTrigger: International Counter Terrorism Special Force + case "T15105N": // Pen Pen TriIcelon + case "51100": // Phantasy Star Online + case "MK5110050": // Phantasy Star Online + case "51193": // Phantasy Star Online Ver.2 + case "MK5119350": // Phantasy Star Online Ver.2 + case "MK5114864": // Planet Ring + case "T17713N": // POD: Speedzone + case "T17710D50": // Pod 2 Multiplayer Online + case "T1201N": // Power Stone + case "T36801D64": // Power Stone + case "T1211N": // Power Stone 2 + case "T36812D61": // Power Stone 2 + case "T41405N": // Prince of Persia Arabian Nights + case "T30701D": // Pro Pinball Trilogy + case "T1219N": // Project Justice + case "T7022D50": // Project Justice: Rival Schools 2 + case "51061": // Quake III Arena + case "MK5106150": // Quake III Arena + case "T41902N": // Railroad Tycoon II: Gold Edition + case "T17704N": // Rayman 2 + case "T17707D50": // Rayman 2 + case "T40219N": // Razor Freestyle Scooter + case "T8109N": // Re-Volt + case "T8107D05": // Re-Volt + case "T9704N": // Ready 2 Rumble Boxing + case "T9704D50": // Ready 2 Rumble Boxing + case "T9717N": // Ready 2 Rumble Boxing Round 2 + case "T9711D50": // Ready 2 Rumble Boxing Round 2 + case "T7012D97": // Record of Lodoss War + case "T40215N": // Red Dog: Superior Firepower + case "MK5102150": // Red Dog: Superior Firepower + case "T1205N": // Resident Evil 2 + case "T7004D61": // Resident Evil 2 + case "T1220N": // Resident Evil 3: Nemesis + case "T7021D56": // Resident Evil 3: Nemesis + case "T1204N": // Resident Evil Code: Veronica + case "MK5119250": // REZ + case "T22901D05": // Roadsters + case "51092": // Samba De Amigo + case "MK5109250": // Samba De Amigo + case "51048": // SeaMan + case "MK5100605": // Sega Bass Fishing + case "51166": // Sega Bass Fishing 2 + case "51053": // Sega GT + case "MK5105350": // Sega GT + case "51096": // Sega Marine Fishing + case "MK5101950": // Sega Rally 2 + case "51146": // Sega Smash Pack 1 + case "MK5108350": // Sega Worldwide Soccer 2000 Euro Edition + case "T41301N": // Seventh Cross Evolution + case "T8106N": // Shadowman + case "51059": // Shenmue + case "MK5105950": // Shenmue + case "MK5118450": // Shenmue 2 + case "T9505D76": // Silent Scope + case "T15108N": // Silver + case "T15109D91": // Silver + case "51052": // Skies of Arcadia + case "T15106N": // Slave Zero + case "T15104D59": // Slave Zero + case "T40207N": // Sno-Cross Championship Racing + case "T40212N": // Soldier of Fortune + case "T17726D50": // Soldier of Fortune + case "51000": // Sonic Adventure + case "MK5100053": // Sonic Adventure + case "51014": // Sonic Adventure (Limited Edition) + case "51117": // Sonic Adventure 2 + case "MK5111750": // Sonic Adventure 2 + case "51060": // Sonic Shuffle + case "MK5106050": // Sonic Shuffle + case "T1401D50": // SoulCalibur + case "T8112D05": // South Park Rally + case "T8105N": // South Park: Chef's Luv Shack + case "51051": // Space Channel 5 + case "MK5105150": // Space Channel 5 + case "T1216N": // Spawn: In the Demon's Hand + case "T41704N": // Spec Ops II: Omega Squad + case "T45004D50": // Spec Ops II: Omega Squad + case "T17702N": // Speed Devils + case "T17702D50": // Speed Devils + case "T17718N": // Speed Devils Online Racing + case "T13008N": // Spider-man + case "T13011D05": // Spider-man + case "T8118N": // Spirit of Speed 1937 + case "T8117D59": // Spirit of Speed 1937 + case "T44304N": // Sports Jam + case "T23003N": // Star Wars: Demolition + case "T23002N": // Star Wars: Episode 1 Jedi Power Battles + case "T23001N": // Star Wars: Episode 1 Racer + case "T40209N": // StarLancer + case "T17723D50": // StarLancer + case "T1203N": // Street Fighter Alpha3 + case "T7005D50": // Street Fighter Alpha3 + case "T1213N": // Street Fighter III: 3rd Strike + case "T1210N": // Street Fighter III: Double Impact + case "T7006D50": // Street Fighter III: Double Impact + case "T15111N": // Striker Pro 2000 + case "T15102D50": // UEFA Striker + case "T22904D": // Stunt GP + case "T17708N": // Stupid Invaders + case "T17711D71": // Stupid Invaders + case "T40206N": // Super Magnetic Neo + case "T40206D50": // Super Magnetic Neo + case "T12511N": // Super Runabout: San Francisco Edition + case "T7014D50": // Super Runabout: The Golden State + case "T40216N": // Surf Rocket Racers + case "T17721D50": // Surf Rocket Racers + case "T17703N": // Suzuki Alstare Extreme Racing + case "T36805N": // Sword of the Berserk: Guts' Rage + case "T36807D05": // Sword of the Berserk: Guts' Rage + case "T17708D": // Taxi 2 + case "T1208N": // Tech Romancer + case "T8108N": // Tee Off + case "51186": // Tennis 2K2 + case "MK5118650": // Virtua Tennis 2 + case "T15102N": // Test Drive 6 + case "T15123N": // Test Drive Le Mans + case "T15111D91": // Le Mans 24 Hours + case "T15110N": // Test Drive V-Rally + case "T15105D05": // V-Rally 2: Expert Edition + case "51011": // Time Stalkers + case "MK5101153": // Time Stalkers + case "T13701N": // TNN Motorsports HardCore Heat + case "T40202N": // Tokyo Extreme Racer + case "T40201D50": // Tokyo Highway Challenge + case "T40211N": // Tokyo Extreme Racer 2 + case "T17724D50": // Tokyo Highway Challenge 2 + case "T40402N": // Tom Clancy's Rainbow Six Rouge Spear + case "T45002D61": // Tom Clancy's Rainbow Six Rouge Spear + case "T36812N": // Tomb Raider: Chronicles + case "T36815D05": // Tomb Raider: Chronicles + case "T36806N": // Tomb Raider: The Last Revelation + case "T36804D05": // Tomb Raider: The Last Revelation + case "T40205N": // Tony Hawks Pro Skater + case "T40204D50": // Tony Hawk's Skateboarding + case "T13006N": // Tony Hawks Pro Skater 2 + case "T13008D05": // Tony Hawks Pro Skater 2 + case "MK5102050": // Toy Comander + case "51149": // Toy Racer + case "T13003D05": // Toy Story 2: Buzz Lightyear to the Rescue! + case "T8102N": // TrickStyle + case "51144": // Typing of the Dead + case "MK5109505": // UEFA Dream Soccer + case "T40204N": // Ultimate Fighting Championship + case "T40203D50": // Ultimate Fighting Championship + case "T15125N": // Unreal Tornament + case "T36801D50": // Unreal Tornament + case "T36810N": // Urban Chaos + case "T8110N": // Vanishing Point + case "T8110D05": // Vanishing Point + case "T13002D71": // Vigilante 8: 2nd Offense + case "T44301N": // Virtua Athlete 2000 + case "MK5109450": // Virtua Athlete 2K + case "51001": // Virtua Fighter 3tb + case "MK5100153": // Virtua Fighter 3tb + case "51028": // Virtua Striker 2 + case "MK5102850": // Virtua Striker 2 Ver. 2000.1 + case "51054": // Virtua Tennis + case "MK5105450": // Virtua Tennis + case "T13004N": // Virtual On: Oratorio Tangram + case "T15113N": // Wacky Races + case "T15106D05": // Wacky Races + case "T40504D64": // Wetrix+ + case "T36811D": // Who Wants To Be A Millianare + case "T42101N": // Wild Metal + case "T40501D64": // Wild Metal + case "51055": // World Series Baseball 2K1 + case "51152": // World Series Baseball 2K2 + case "T40601N": // Worms Armageddon + case "T40601D79": // Worms Armageddon + case "T22904N": // Worms World Party + case "T7016D50": // Worms World Party + case "T10005N": // WWF Royal Rumble + case "T10003D50": // WWF Royal Rumble + case "MK5108150": // Sega Extreme Sports + case "MK5103850": // Zombie Revenge + // Unreleased Games + case "T26702N": // PBA Tour Bowling 2001 + // Unlicensed Games + case "43011": // Bleem!Cast - Gran Turismo 2 + case "43021": // Bleem!Cast - Metal Gear Solid + case "43031": // Bleem!Cast - Tekken 3 + case "DUX-SE-1": // DUX Special Edition + case "DXCE-15": // DUX Collector's Edition + case "DXJC-1": // DUX Jewel Case + case "RRRR-RE": // Rush Rush Rally Racing + case "RRRR-DX": // Rush Rush Rally Racing (Deluxe Edition) + case "YW-015DC": // Wind and Water: Puzzle Battles + // Other Software + /*case "T?": // Codebreaker & Loader */ + case "T0000": // DC VCD Player (Joy Pad hack) + // Published by "The GOAT Store" (No IDs) + vgaMode = 0; + + // VGA Patchable + case "T40509D50": // Aqua GT + case "T9715N": // Army Men Sarges Heroes + case "T9708D61": // Army Men Sarges Heroes + case "T8117N": // BusTA-Move 4 + case "T8109D05": // BusTA-Move 4 + case "T44903D50": // Coaster Works + case "T17721N": // Conflict Zone + case "T1217N": // Dino Crisis + case "T7019D05": // Dino Crisis + case "T12503D61": // Dragon's Blood + case "T10003N": // Evil Dead Hail to the King + case "T10005D05": // Evil Dead Hail to the King + case "T17705SD50": // Evolution: The World of Sacred Device + case "T15104N": // Expendable + case "T45401D50": // Giant Killers + case "T1214N": // Gun Bird 2 + case "T7018D50": // Gun Bird 2 + case "T40502N": // Hidden and Dangerous + case "T40503D64": // Hidden and Dangerous + case "T9702D61": // Hydro Thunder + case "T1404N": // Ms Pacman Maze Madness + case "T10001D50": // MTV Sports: Skateboarding feat. Andy McDonald + case "T9706N": // NBA Showdown: NBA on NBC + case "T9705D50": // NBA Showdown: NBA on NBC + case "T40214N": // Next Tetris, The + case "T9703D50": // NFL Blitz 2000 + case "T40403N": // Q*bert + case "T44303N": // Reel Fishing: Wild + case "51010": // Rippin' Riders + case "T9707N": // San Fransisco Rush 2049 + case "MK5103150": // Sega Worldwide Soccer 2000 + case "T8104D05": // Shadowman + case "MK5105250": // Skies of Arcadia + case "T17722D50": // Sno-Cross Championship Racing + case "T1401N": // SoulCalibur + case "T8116N": // South Park Rally + case "T8105D50": // South Park: Chef's Luv Shack + case "T36816D05": // Spawn: In the Demon's Hand + case "T36808D05": // Sydney 2000 + case "T8108D05": // Tee Off + case "MK5404050": // TNN Motorsports Buggy Heat + case "T40401N": // Tom Clancy's Rainbow Six + case "T45001D05": // Tom Clancy's Rainbow Six + case "T11011N": // Who Wants To Beat Up A Millianare + vgaMode = 1; + + // VGA Incompatible + case "T40209D50": // Aerowings 2 + case "T9501N": // Air Force Delta + case "T9501D76": // Air Force Delta + case "T40217N": // Bangai-O + case "T7011D": // Bangai-O + case "T12504N": // Ceasars Palace 2000: Millennium Gold Edition + case "T12502D61": // Ceasars Palace 2000: Millennium Gold Edition + case "T7017D50": // Capcom Vs. SNK + case "T15128N": // Coaster Works + case "T17718D84": // Dinosaur + case "T8114N": // ECW Anarchy Rulz + case "T8119D59": // ECW Anarchy Rulz + case "T8112N": // ECW Hardcore Revolution + case "BKL8320301ENG": // ECW Hardcore Revolution + case "T9702N": // Hydro Thunder + case "T15129N": // Iron Aces + case "T44904D50": // Iron Aces + case "T1206N": // Jojo's Bizarre Adventure + case "T7007D50": // Jojo's Bizarre Adventure + case "T44302N": // The King of Fighters '99 Evolution + case "T3101N": // The King of Fighters: Dream Match 1999 + case "T44305N": // Last Blade II Heart of a Samarai + case "T10004N": // MTV Sports: Skateboarding feat. Andy McDonald + case "T17717D50": // Next Tetris, The + case "T15103D61": // Pen Pen + case "T1207N": // Plasma Sword + case "T7003D50": // Plasma Sword + case "T31101N": // Psychic Force 2012 + case "T8106D05": // Psychic Force 2012 + case "T40505D50": // Railroad Tycoon II: Gold Edition + case "T36806D05": // Resident Evil Code: Veronica + case "T15122N": // Ring, The: Terror's Realm + case "MK5101050": // Rippin' Riders + case "T41401N": // Soul Fighter + case "T41401D61": // Soul Fighter + case "T36808N": // Sydney 2000 + case "T8103N": // WWF Attitude + case "T8103D50": // WWF Attitude + vgaMode = 3; + + // VGA Unverified + case "T46603D71": // Conflict Zone + case "T17719N": // Donald Duck: Goin' Quackers + case "T9509N": // ESPN International Track n Field + case "T15101D05": // Millenium Soldier: Expendable + case "T46602D50": // Heavy Metal: Geomatrix + case "51002": // House of the Dead 2 + case "T15116N": // Loony Toons Space Race + case "T9710D50": // Midway Greatest Arcade Hits Volume 1 + case "T9701N": // Mortal Kombat Gold + case "T9713D61": // NBA Hoopz + case "51025": // NHL 2K + case "T46604D50": // Freestyle Scooter + case "T40218N": // Record of Lodoss War + case "T22901N": // Roadsters + case "T9709D61": // San Fransisco Rush 2049 + case "51006": // Sega Bass Fishing + case "51019": // Sega Rally Championship 2 + case "T9507N": // Silent Scope + case "T17713D50": // Speed Devils Online Racing + case "T13010D50": // Star Wars: Demolition + case "T23002D50": // Star Wars: Episode 1 Jedi Power Battles + case "T13006D50": // Star Wars: Episode 1 Racer + case "T7013D50": // Street Fighter III: 3rd Strike + case "T17703D05": // Suzuki Alstare Extreme Racing + case "T7009D50": // Tech Romancer + case "51020": // Toy Comander + case "T13003N": // Toy Story 2: Buzz Lightyear to the Rescue! + case "T8101D05": // TrickStyle + case "T36810D50": // Urban Chaos + case "T13002N": // Vigilante 8: 2nd Offense + case "T8111N": // Wetrix+ + case "T15126N": // Xtreme Sports + case "51038": // Zombie Revenge + + default: + vgaMode = 3; // Emulator default + } + return vgaMode; + } + + public boolean useSafeMode(String gameId) { + switch (gameId) { + case "T40218N": // Record of Lodoss War + case "T7012D": // Record of Lodoss War + case "T23001D" : // Star Wars Episode I: Racer + case "T30701D" : // Pro Pinball Trilogy + case "T15112N" : // Demolition Racer + case "T40216N" : // Surf Rocket Racers + + default: + return false; + } + } +} diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/config/Config.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/config/Config.java index 57ba6249d..ac1330cd0 100644 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/config/Config.java +++ b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/config/Config.java @@ -3,11 +3,8 @@ package com.reicast.emulator.config; public class Config { public static final String pref_home = "home_directory"; - public static final String pref_games = "game_directory"; public static final String pref_theme = "button_theme"; - public static final String pref_gamedetails = "game_details"; - public static final String pref_rendertype = "render_type"; public static final String pref_renderdepth = "depth_render"; diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/config/InputModFragment.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/config/InputModFragment.java deleted file mode 100644 index 7ae8ebb2f..000000000 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/config/InputModFragment.java +++ /dev/null @@ -1,653 +0,0 @@ -package com.reicast.emulator.config; - -import android.app.AlertDialog; -import android.app.Dialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.SharedPreferences; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Matrix; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.os.Build; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.support.v4.app.Fragment; -import android.view.InputDevice; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemSelectedListener; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.CompoundButton; -import android.widget.CompoundButton.OnCheckedChangeListener; -import android.widget.ImageView; -import android.widget.Spinner; -import android.widget.TextView; - -import com.reicast.emulator.R; -import com.reicast.emulator.periph.Gamepad; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; - -import tv.ouya.console.api.OuyaController; - -public class InputModFragment extends Fragment { - - private SharedPreferences mPrefs; - - private CompoundButton switchModifiedLayoutEnabled; - private CompoundButton switchCompatibilityEnabled; - private Spinner right_stick_spinner; - private CompoundButton switchJoystickDpadEnabled; - - private TextView a_button_text; - private TextView b_button_text; - private TextView x_button_text; - private TextView y_button_text; - private TextView l_button_text; - private TextView r_button_text; - private TextView dpad_up_text; - private TextView dpad_down_text; - private TextView dpad_left_text; - private TextView dpad_right_text; - private TextView start_button_text; - private TextView select_button_text; - private TextView joystick_x_text; - private TextView joystick_y_text; - - private String player = "_A"; - private int sS = 2; - private int playerNum = -1; - private mapKeyCode mKey; - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - // Inflate the layout for this fragment - return inflater.inflate(R.layout.input_mod_fragment, container, false); - } - - @Override - public void onViewCreated(View view, Bundle savedInstanceState) { - - mPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); - - final String[] controllers = getResources().getStringArray(R.array.controllers); - - Bundle b = getArguments(); - if (b != null) { - playerNum = b.getInt("portNumber", -1); - } - - switchJoystickDpadEnabled = (CompoundButton) getView().findViewById( - R.id.switchJoystickDpadEnabled); - switchModifiedLayoutEnabled = (CompoundButton) getView().findViewById( - R.id.switchModifiedLayoutEnabled); - switchCompatibilityEnabled = (CompoundButton) getView().findViewById( - R.id.switchCompatibilityEnabled); - - OnCheckedChangeListener joystick_mode = new OnCheckedChangeListener() { - public void onCheckedChanged(CompoundButton buttonView, - boolean isChecked) { - mPrefs.edit().putBoolean(Gamepad.pref_js_merged + player, isChecked).apply(); - } - }; - - switchJoystickDpadEnabled.setOnCheckedChangeListener(joystick_mode); - - String[] rstick = getResources().getStringArray(R.array.right_stick); - right_stick_spinner = (Spinner) getView().findViewById(R.id.rstick_spinner); - ArrayAdapter rstickAdapter = new ArrayAdapter<>( - getActivity(), android.R.layout.simple_spinner_item, rstick); - rstickAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - right_stick_spinner.setAdapter(rstickAdapter); - right_stick_spinner.setOnItemSelectedListener(new OnItemSelectedListener() { - - public void onItemSelected(AdapterView parent, View view, int pos, long id) { - mPrefs.edit().putInt(Gamepad.pref_js_rstick + player, pos).apply(); - } - - public void onNothingSelected(AdapterView arg0) { - - } - - }); - - OnCheckedChangeListener modified_layout = new OnCheckedChangeListener() { - public void onCheckedChanged(CompoundButton buttonView, - boolean isChecked) { - mPrefs.edit().putBoolean(Gamepad.pref_js_modified + player, isChecked).apply(); - } - }; - - switchModifiedLayoutEnabled.setOnCheckedChangeListener(modified_layout); - - OnCheckedChangeListener compat_mode = new OnCheckedChangeListener() { - public void onCheckedChanged(CompoundButton buttonView, - boolean isChecked) { - mPrefs.edit().putBoolean(Gamepad.pref_js_compat + player, isChecked).apply(); - if (isChecked) { - selectController(); - } - } - }; - - switchCompatibilityEnabled.setOnCheckedChangeListener(compat_mode); - - mKey = new mapKeyCode(getActivity()); - - ImageView a_button_icon = (ImageView) getView().findViewById( - R.id.a_button_icon); - a_button_icon.setImageDrawable(getButtonImage(448 / sS, 0)); - a_button_text = (TextView) getView().findViewById(R.id.a_button_key); - Button a_button = (Button) getView().findViewById(R.id.a_button_edit); - a_button.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - mKey.intiateSearch(Gamepad.pref_button_a, a_button_text); - } - }); - Button a_remove = (Button) getView().findViewById(R.id.remove_a_button); - a_remove.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - remKeyCode(Gamepad.pref_button_a, a_button_text); - } - }); - - ImageView b_button_icon = (ImageView) getView().findViewById( - R.id.b_button_icon); - b_button_icon.setImageDrawable(getButtonImage(384 / sS, 0)); - b_button_text = (TextView) getView().findViewById(R.id.b_button_key); - Button b_button = (Button) getView().findViewById(R.id.b_button_edit); - b_button.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - mKey.intiateSearch(Gamepad.pref_button_b, b_button_text); - } - }); - Button b_remove = (Button) getView().findViewById(R.id.remove_b_button); - b_remove.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - remKeyCode(Gamepad.pref_button_b, b_button_text); - } - }); - - ImageView x_button_icon = (ImageView) getView().findViewById( - R.id.x_button_icon); - x_button_icon.setImageDrawable(getButtonImage(256 / sS, 0)); - x_button_text = (TextView) getView().findViewById(R.id.x_button_key); - Button x_button = (Button) getView().findViewById(R.id.x_button_edit); - x_button.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - mKey.intiateSearch(Gamepad.pref_button_x, x_button_text); - } - }); - Button x_remove = (Button) getView().findViewById(R.id.remove_x_button); - x_remove.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - remKeyCode(Gamepad.pref_button_x, x_button_text); - } - }); - - ImageView y_button_icon = (ImageView) getView().findViewById( - R.id.y_button_icon); - y_button_icon.setImageDrawable(getButtonImage(320 / sS, 0)); - y_button_text = (TextView) getView().findViewById(R.id.y_button_key); - Button y_button = (Button) getView().findViewById(R.id.y_button_edit); - y_button.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - mKey.intiateSearch(Gamepad.pref_button_y, y_button_text); - } - }); - Button y_remove = (Button) getView().findViewById(R.id.remove_y_button); - y_remove.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - remKeyCode(Gamepad.pref_button_y, y_button_text); - } - }); - - ImageView l_button_icon = (ImageView) getView().findViewById( - R.id.l_button_icon); - l_button_icon.setImageDrawable(getButtonImage(78 / sS, 64 / sS)); - l_button_text = (TextView) getView().findViewById(R.id.l_button_key); - Button l_button = (Button) getView().findViewById(R.id.l_button_edit); - l_button.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - mKey.intiateSearch(Gamepad.pref_button_l, l_button_text); - } - }); - Button l_remove = (Button) getView().findViewById(R.id.remove_l_button); - l_remove.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - remKeyCode(Gamepad.pref_button_l, l_button_text); - } - }); - - ImageView r_button_icon = (ImageView) getView().findViewById( - R.id.r_button_icon); - r_button_icon.setImageDrawable(getButtonImage(162 / sS, 64 / sS)); - r_button_text = (TextView) getView().findViewById(R.id.r_button_key); - Button r_button = (Button) getView().findViewById(R.id.r_button_edit); - r_button.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - mKey.intiateSearch(Gamepad.pref_button_r, r_button_text); - } - }); - Button r_remove = (Button) getView().findViewById(R.id.remove_r_button); - r_remove.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - remKeyCode(Gamepad.pref_button_r, r_button_text); - } - }); - - dpad_up_text = (TextView) getView().findViewById(R.id.dpad_up_key); - Button dpad_up = (Button) getView().findViewById(R.id.dpad_up_edit); - dpad_up.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - mKey.intiateSearch(Gamepad.pref_dpad_up, dpad_up_text); - } - }); - Button up_remove = (Button) getView().findViewById(R.id.remove_dpad_up); - up_remove.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - remKeyCode(Gamepad.pref_dpad_up, dpad_up_text); - } - }); - - dpad_down_text = (TextView) getView().findViewById(R.id.dpad_down_key); - Button dpad_down = (Button) getView().findViewById(R.id.dpad_down_edit); - dpad_down.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - mKey.intiateSearch(Gamepad.pref_dpad_down, dpad_down_text); - } - }); - Button down_remove = (Button) getView().findViewById( - R.id.remove_dpad_down); - down_remove.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - remKeyCode(Gamepad.pref_dpad_down, dpad_down_text); - } - }); - - dpad_left_text = (TextView) getView().findViewById(R.id.dpad_left_key); - Button dpad_left = (Button) getView().findViewById(R.id.dpad_left_edit); - dpad_left.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - mKey.intiateSearch(Gamepad.pref_dpad_left, dpad_left_text); - } - }); - Button left_remove = (Button) getView().findViewById( - R.id.remove_dpad_left); - left_remove.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - remKeyCode(Gamepad.pref_dpad_left, dpad_left_text); - } - }); - - dpad_right_text = (TextView) getView() - .findViewById(R.id.dpad_right_key); - Button dpad_right = (Button) getView().findViewById( - R.id.dpad_right_edit); - dpad_right.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - mKey.intiateSearch(Gamepad.pref_dpad_right, dpad_right_text); - } - }); - Button right_remove = (Button) getView().findViewById( - R.id.remove_dpad_right); - right_remove.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - remKeyCode(Gamepad.pref_dpad_right, dpad_right_text); - } - }); - - joystick_x_text = (TextView) getView().findViewById( - R.id.joystick_x_axis); - Button joystick_x = (Button) getView().findViewById( - R.id.joystick_x_edit); - joystick_x.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - mKey.mapAxis(Gamepad.pref_axis_x, joystick_x_text); - } - }); - Button joystick_x_remove = (Button) getView().findViewById( - R.id.remove_joystick_x); - joystick_x_remove.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - remKeyCode(Gamepad.pref_axis_x, joystick_x_text); - } - }); - - joystick_y_text = (TextView) getView().findViewById( - R.id.joystick_y_axis); - Button joystick_y = (Button) getView().findViewById( - R.id.joystick_y_edit); - joystick_y.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - mKey.mapAxis(Gamepad.pref_axis_y, joystick_y_text); - } - }); - Button joystick_y_remove = (Button) getView().findViewById( - R.id.remove_joystick_y); - joystick_y_remove.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - remKeyCode(Gamepad.pref_axis_y, joystick_y_text); - } - }); - - ImageView start_button_icon = (ImageView) getView().findViewById( - R.id.start_button_icon); - start_button_icon.setImageDrawable(getButtonImage(0, 64 / sS)); - start_button_text = (TextView) getView().findViewById( - R.id.start_button_key); - Button start_button = (Button) getView().findViewById( - R.id.start_button_edit); - start_button.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - mKey.intiateSearch(Gamepad.pref_button_start, start_button_text); - } - }); - Button start_remove = (Button) getView() - .findViewById(R.id.remove_start); - start_remove.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - remKeyCode(Gamepad.pref_button_start, start_button_text); - } - }); - - ImageView select_button_icon = (ImageView) getView().findViewById( - R.id.select_button_icon); - select_button_icon.setImageResource(R.drawable.ic_drawer); - select_button_text = (TextView) getView().findViewById( - R.id.select_button_key); - Button select_button = (Button) getView().findViewById( - R.id.select_button_edit); - select_button.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - mKey.intiateSearch(Gamepad.pref_button_select, - select_button_text); - } - }); - Button select_remove = (Button) getView().findViewById( - R.id.remove_select); - select_remove.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - remKeyCode(Gamepad.pref_button_select, select_button_text); - } - }); - - Spinner player_spnr = (Spinner) getView().findViewById( - R.id.player_spinner); - ArrayAdapter playerAdapter = new ArrayAdapter<>( - getActivity(), android.R.layout.simple_spinner_item, - controllers); - playerAdapter - .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - player_spnr.setAdapter(playerAdapter); - if (playerNum != -1) { - player_spnr.setSelection(playerNum, true); - } - player_spnr.setOnItemSelectedListener(new OnItemSelectedListener() { - - public void onItemSelected(AdapterView parent, View view, - int pos, long id) { - String selection = parent.getItemAtPosition(pos).toString(); - player = "_" - + selection.substring(selection.lastIndexOf(" ") + 1, - selection.length()); - playerNum = pos; - updateController(player); - } - - public void onNothingSelected(AdapterView arg0) { - - } - - }); - updateController(player); - } - - /** - * Retrieve an image to serve as a visual representation - * - * @param x - * The x start value of the image within the atlas - * @param y - * The y start value of the image within the atlas - */ - private Drawable getButtonImage(int x, int y) { - Bitmap image = null; - try { - File buttons = null; - InputStream bitmap; - String theme = mPrefs.getString(Config.pref_theme, null); - if (theme != null) { - buttons = new File(theme); - } - if (buttons != null && buttons.exists()) { - bitmap = new FileInputStream(buttons); - } else { - bitmap = getResources().getAssets().open("buttons.png"); - } - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inSampleSize = sS; - image = BitmapFactory.decodeStream(bitmap, null, options); - bitmap.close(); - Matrix matrix = new Matrix(); - matrix.postScale(32, 32); - Bitmap resizedBitmap = Bitmap.createBitmap(image, x, y, 64 / sS, - 64 / sS, matrix, true); - @SuppressWarnings("deprecation") - BitmapDrawable bmd = new BitmapDrawable(resizedBitmap); - image.recycle(); - image = null; - return bmd; - } catch (IOException e1) { - e1.printStackTrace(); - } catch (OutOfMemoryError E) { - if (sS == 2) { - if (image != null) { - image.recycle(); - } - sS = 4; - return getButtonImage(x, y); - } else { - E.printStackTrace(); - } - } - return getResources().getDrawable(R.drawable.input); - } - - /** - * Prompt the user to specify the controller to modify - * - */ - private void selectController() { - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - builder.setTitle(R.string.select_controller_title); - builder.setMessage(getString(R.string.select_controller_message, player.replace("_", ""))); - builder.setNegativeButton(R.string.cancel, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - } - }); - builder.setOnKeyListener(new Dialog.OnKeyListener() { - public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { - mPrefs.edit().putInt("controller" + player, event.getDeviceId()).apply(); - dialog.dismiss(); - return true; - } - }); - builder.create(); - builder.show(); - } - - private class mapKeyCode extends AlertDialog.Builder { - - mapKeyCode(Context c) { - super(c); - } - - /** - * Prompt the user for the button to be assigned - * - * @param button - * The name of the emulator button being defined - * @param output - * The output display for the assigned button value - */ - public void intiateSearch(final String button, final TextView output) { - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - builder.setTitle(R.string.map_keycode_title); - builder.setMessage(getString(R.string.map_keycode_message, button.replace("_", " "))); - builder.setNegativeButton(R.string.cancel, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - } - }); - builder.setOnKeyListener(new Dialog.OnKeyListener() { - public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { - mapButton(keyCode, button); - dialog.dismiss(); - return getKeyCode(button, output); - } - }); - builder.create(); - builder.show(); - } - - /** - * Assign the user button to the emulator button - * - * @param keyCode - * The keycode generated by the button being assigned - * @param button - * The label of the button being assigned - */ - private void mapButton(int keyCode, String button) { - if (Build.MODEL.startsWith("R800")) { - if (keyCode == KeyEvent.KEYCODE_MENU) - return; - } else { - if (keyCode == KeyEvent.KEYCODE_BACK) - return; - } - mPrefs.edit().putInt(button + player, keyCode).apply(); - } - - private void mapAxis(final String button, final TextView output) { - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - builder.setTitle(R.string.map_keycode_title); - builder.setMessage(getString(R.string.map_keycode_message, button.replace("_", " "))); - View view = getLayoutInflater().inflate(R.layout.joystick_dialog, null); - builder.setView(view); - builder.setCancelable(false); - builder.create(); - final Dialog dialog = builder.show(); - view.findViewById(R.id.joystick_cancel_btn).setOnClickListener(new View.OnClickListener() { - public void onClick(View view) { - dialog.dismiss(); - } - }); - view.requestFocusFromTouch(); - view.setOnGenericMotionListener(new View.OnGenericMotionListener() { - @Override - public boolean onGenericMotion(View view, MotionEvent event) { - int axis = -1; - if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) == - InputDevice.SOURCE_JOYSTICK && - event.getAction() == MotionEvent.ACTION_MOVE) { - if (event.getAxisValue(MotionEvent.AXIS_X) != 0) { - axis = MotionEvent.AXIS_X; - } - if (event.getAxisValue(MotionEvent.AXIS_Y) != 0) { - axis = MotionEvent.AXIS_Y; - } - if (event.getAxisValue(MotionEvent.AXIS_RX) != 0) { - axis = MotionEvent.AXIS_RX; - } - if (event.getAxisValue(MotionEvent.AXIS_RY) != 0) { - axis = MotionEvent.AXIS_RY; - } - if (event.getAxisValue(MotionEvent.AXIS_HAT_X) != 0) { - axis = MotionEvent.AXIS_HAT_X; - } - if (event.getAxisValue(MotionEvent.AXIS_HAT_Y) != 0) { - axis = MotionEvent.AXIS_HAT_Y; - } - if (new Gamepad().IsOuyaOrTV(getActivity(), true)) { - if (event.getAxisValue(OuyaController.AXIS_LS_X) != 0) { - axis = OuyaController.AXIS_LS_X; - } - if (event.getAxisValue(OuyaController.AXIS_LS_Y) != 0) { - axis = OuyaController.AXIS_LS_Y; - } - } - mPrefs.edit().putInt(button + player, axis).apply(); - dialog.dismiss(); - return getKeyCode(button, output); - } - return true; - } - }); - } - } - - private void updateController(String player) { - switchJoystickDpadEnabled.setChecked(mPrefs.getBoolean( - Gamepad.pref_js_merged + player, false)); - right_stick_spinner.setSelection(mPrefs.getInt(Gamepad.pref_js_rstick + player, 0)); - switchModifiedLayoutEnabled.setChecked(mPrefs.getBoolean( - Gamepad.pref_js_modified + player, false)); - switchCompatibilityEnabled.setChecked(mPrefs.getBoolean( - Gamepad.pref_js_compat + player, false)); - getKeyCode(Gamepad.pref_button_a, a_button_text); - getKeyCode(Gamepad.pref_button_b, b_button_text); - getKeyCode(Gamepad.pref_button_x, x_button_text); - getKeyCode(Gamepad.pref_button_y, y_button_text); - getKeyCode(Gamepad.pref_button_l, l_button_text); - getKeyCode(Gamepad.pref_button_r, r_button_text); - getKeyCode(Gamepad.pref_dpad_up, dpad_up_text); - getKeyCode(Gamepad.pref_dpad_down, dpad_down_text); - getKeyCode(Gamepad.pref_dpad_left, dpad_left_text); - getKeyCode(Gamepad.pref_dpad_right, dpad_right_text); - getKeyCode(Gamepad.pref_axis_x, joystick_x_text); - getKeyCode(Gamepad.pref_axis_y, joystick_y_text); - getKeyCode(Gamepad.pref_button_start, start_button_text); - getKeyCode(Gamepad.pref_button_select, select_button_text); - } - - private boolean getKeyCode(final String button, final TextView output) { - int keyCode = mPrefs.getInt(button + player, -1); - if (keyCode != -1) { - String label = output.getText().toString(); - if (label.contains(":")) { - label = label.substring(0, label.indexOf(":")); - } - label += ": " + keyCode; - output.setText(label); - return true; - } else { - String label = output.getText().toString(); - if (label.contains(":")) { - label = label.substring(0, label.indexOf(":")); - } - output.setText(label); - return false; - } - } - - private void remKeyCode(final String button, TextView output) { - mPrefs.edit().remove(button + player).apply(); - getKeyCode(button, output); - } -} diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/config/OptionsFragment.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/config/OptionsFragment.java deleted file mode 100644 index 08adeeecf..000000000 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/config/OptionsFragment.java +++ /dev/null @@ -1,457 +0,0 @@ -package com.reicast.emulator.config; - -import android.app.Activity; -import android.app.AlertDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.SharedPreferences; -import android.graphics.drawable.Drawable; -import android.os.AsyncTask; -import android.os.Build; -import android.os.Bundle; -import android.os.Environment; -import android.preference.PreferenceManager; -import android.support.constraint.ConstraintLayout; -import android.support.design.widget.Snackbar; -import android.support.graphics.drawable.VectorDrawableCompat; -import android.support.v4.app.Fragment; -import android.view.Gravity; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputMethodManager; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemSelectedListener; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.CompoundButton; -import android.widget.CompoundButton.OnCheckedChangeListener; -import android.widget.EditText; -import android.widget.Spinner; -import android.widget.TextView; - -import com.android.util.FileUtils; -import com.reicast.emulator.Emulator; -import com.reicast.emulator.FileBrowser; -import com.reicast.emulator.R; -import com.reicast.emulator.emu.GL2JNIView; -import com.reicast.emulator.emu.JNIdc; - -import org.apache.commons.lang3.StringUtils; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FilenameFilter; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.lang.ref.WeakReference; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; - -public class OptionsFragment extends Fragment { - - private Spinner mSpnrThemes; - private OnClickListener mCallback; - - private SharedPreferences mPrefs; - private File sdcard = Environment.getExternalStorageDirectory(); - private String home_directory = sdcard.getAbsolutePath(); - private String game_directory = sdcard.getAbsolutePath(); - - private String[] codes; - - // Container Activity must implement this interface - public interface OnClickListener { - void onMainBrowseSelected(String path_entry, boolean games, String query); - void launchBIOSdetection(); - } - - @Override @SuppressWarnings("deprecation") - public void onAttach(Activity activity) { - super.onAttach(activity); - - // This makes sure that the container activity has implemented - // the callback interface. If not, it throws an exception - try { - mCallback = (OnClickListener) activity; - } catch (ClassCastException e) { - throw new ClassCastException(activity.toString() - + " must implement OnClickListener"); - } - } - - @Override - public void onAttach(Context context) { - super.onAttach(context); - - // This makes sure that the container activity has implemented - // the callback interface. If not, it throws an exception - try { - mCallback = (OnClickListener) context; - } catch (ClassCastException e) { - throw new ClassCastException(context.getClass().toString() - + " must implement OnClickListener"); - } - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - // Inflate the layout for this fragment - return inflater.inflate(R.layout.configure_fragment, container, false); - } - - @Override - public void onViewCreated(View view, Bundle savedInstanceState) { - - mPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); - - // FIXME Specialized handler for devices with an extSdCard mount for external - HashSet extStorage = FileBrowser.getExternalMounts(); - if (extStorage != null && !extStorage.isEmpty()) { - for (String sd : extStorage) { - String sdCardPath = sd.replace("mnt/media_rw", "storage"); - if (!sdCardPath.equals(sdcard.getAbsolutePath())) { - game_directory = sdCardPath; - } - } - } - - home_directory = mPrefs.getString(Config.pref_home, home_directory); - Emulator app = (Emulator) getActivity().getApplicationContext(); - app.getConfigurationPrefs(); - - // Generate the menu options and fill in existing settings - - Button mainBrowse = (Button) getView().findViewById(R.id.browse_main_path); - mSpnrThemes = (Spinner) getView().findViewById(R.id.pick_button_theme); - new LocateThemes(this).execute(home_directory + "/themes"); - - final EditText editBrowse = (EditText) getView().findViewById(R.id.main_path); - editBrowse.setText(home_directory); - - mainBrowse.setOnClickListener(new View.OnClickListener() { - public void onClick(View view) { - mPrefs.edit().remove(Config.pref_home).apply(); - hideSoftKeyBoard(); - mCallback.launchBIOSdetection(); - } - }); - editBrowse.setOnEditorActionListener(new EditText.OnEditorActionListener() { - @Override - public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { - if (actionId == EditorInfo.IME_ACTION_DONE - || (event != null && event.getKeyCode() == KeyEvent.KEYCODE_ENTER - && event.getAction() == KeyEvent.ACTION_DOWN)) { - if (event == null || !event.isShiftPressed()) { - if (v.getText() != null) { - home_directory = v.getText().toString(); - if (home_directory.endsWith("/data")) { - home_directory.replace("/data", ""); - showToastMessage(getActivity().getString(R.string.data_folder), - Snackbar.LENGTH_SHORT); - } - mPrefs.edit().putString(Config.pref_home, home_directory).apply(); - //JNIdc.config(home_directory); - new LocateThemes(OptionsFragment.this).execute(home_directory + "/themes"); - } - hideSoftKeyBoard(); - return true; - } - } - return false; - } - }); - - OnCheckedChangeListener details_options = new OnCheckedChangeListener() { - - public void onCheckedChanged(CompoundButton buttonView, - boolean isChecked) { - mPrefs.edit().putBoolean(Config.pref_gamedetails, isChecked).apply(); - if (!isChecked) { - File dir = new File(getActivity().getExternalFilesDir(null), "images"); - File[] files = dir == null ? null : dir.listFiles(); - if (files != null) { - for (File file : files) { - if (!file.isDirectory()) { - file.delete(); - } - } - } - } - } - }; - CompoundButton details_opt = (CompoundButton) getView().findViewById(R.id.details_option); - details_opt.setChecked(mPrefs.getBoolean(Config.pref_gamedetails, false)); - details_opt.setOnCheckedChangeListener(details_options); - - Button gameBrowse = (Button) getView().findViewById(R.id.browse_game_path); - - final EditText editGames = (EditText) getView().findViewById(R.id.game_path); - game_directory = mPrefs.getString(Config.pref_games, game_directory); - editGames.setText(game_directory); - - gameBrowse.setOnClickListener(new View.OnClickListener() { - public void onClick(View view) { - mPrefs.edit().remove(Config.pref_games).apply(); - if (editBrowse.getText() != null) { - game_directory = editGames.getText().toString(); - } - hideSoftKeyBoard(); - mCallback.onMainBrowseSelected(game_directory, true, null); - } - }); - editGames.setOnEditorActionListener(new EditText.OnEditorActionListener() { - @Override - public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { - if (actionId == EditorInfo.IME_ACTION_DONE - || (event != null && event.getKeyCode() == KeyEvent.KEYCODE_ENTER - && event.getAction() == KeyEvent.ACTION_DOWN)) { - if (event == null || !event.isShiftPressed()) { - if (v.getText() != null) { - game_directory = v.getText().toString(); - mPrefs.edit().putString(Config.pref_games, game_directory).apply(); - } - hideSoftKeyBoard(); - return true; - } - } - return false; - } - }); - - String[] bios = getResources().getStringArray(R.array.bios); - codes = getResources().getStringArray(R.array.bioscode); - Spinner bios_spnr = (Spinner) getView().findViewById(R.id.bios_spinner); - ArrayAdapter biosAdapter = new ArrayAdapter<>( - getActivity(), android.R.layout.simple_spinner_item, bios); - biosAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - bios_spnr.setAdapter(biosAdapter); - String region = mPrefs.getString("localized", codes[4]); - bios_spnr.setSelection(biosAdapter.getPosition(region), true); - bios_spnr.setOnItemSelectedListener(new OnItemSelectedListener() { - - public void onItemSelected(AdapterView parent, View view, int pos, long id) { - flashBios(codes[pos]); - } - - public void onNothingSelected(AdapterView arg0) { - - } - - }); - - CompoundButton force_software_opt = (CompoundButton) getView().findViewById( - R.id.software_option); - OnCheckedChangeListener force_software = new OnCheckedChangeListener() { - - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - mPrefs.edit().putInt(Config.pref_rendertype, isChecked ? - GL2JNIView.LAYER_TYPE_SOFTWARE : GL2JNIView.LAYER_TYPE_HARDWARE).apply(); - } - }; - int software = mPrefs.getInt(Config.pref_rendertype, GL2JNIView.LAYER_TYPE_HARDWARE); - force_software_opt.setChecked(software == GL2JNIView.LAYER_TYPE_SOFTWARE); - force_software_opt.setOnCheckedChangeListener(force_software); - - String[] depths = getResources().getStringArray(R.array.depth); - - Spinner depth_spnr = (Spinner) getView().findViewById(R.id.depth_spinner); - ArrayAdapter depthAdapter = new ArrayAdapter<>( - getActivity(), R.layout.spinner_selected, depths); - depthAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - depth_spnr.setAdapter(depthAdapter); - - String depth = String.valueOf(mPrefs.getInt(Config.pref_renderdepth, 24)); - depth_spnr.setSelection(depthAdapter.getPosition(depth), true); - - depth_spnr.setOnItemSelectedListener(new OnItemSelectedListener() { - - public void onItemSelected(AdapterView parent, View view, int pos, long id) { - int render = Integer.parseInt(parent.getItemAtPosition(pos).toString()); - mPrefs.edit().putInt(Config.pref_renderdepth, render).apply(); - } - - public void onNothingSelected(AdapterView arg0) { - - } - }); - - Button resetEmu = (Button) getView().findViewById(R.id.reset_emu_btn); - resetEmu.setOnClickListener(new View.OnClickListener() { - public void onClick(View view) { - AlertDialog.Builder b = new AlertDialog.Builder(getActivity()); - b.setIcon(android.R.drawable.ic_dialog_alert); - b.setTitle(getActivity().getString(R.string.reset_emu_title) + "?"); - b.setMessage(getActivity().getString(R.string.reset_emu_details)); - b.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - resetEmuSettings(); - } - }); - b.setNegativeButton(android.R.string.no, null); - b.show(); - } - }); - } - - private static class LocateThemes extends AsyncTask> { - private WeakReference options; - - LocateThemes(OptionsFragment context) { - options = new WeakReference<>(context); - } - - @Override - protected List doInBackground(String... paths) { - File storage = new File(paths[0]); - String[] mediaTypes = options.get().getResources().getStringArray(R.array.themes); - FilenameFilter[] filter = new FilenameFilter[mediaTypes.length]; - int i = 0; - for (final String type : mediaTypes) { - filter[i] = new FilenameFilter() { - public boolean accept(File dir, String name) { - return !dir.getName().startsWith(".") && !name.startsWith(".") - && StringUtils.endsWithIgnoreCase(name, "." + type); - } - }; - i++; - } - FileUtils fileUtils = new FileUtils(); - Collection files = fileUtils.listFiles(storage, filter, 0); - return (List) files; - } - - @Override - protected void onPostExecute(List items) { - if (items != null && !items.isEmpty()) { - String[] themes = new String[items.size() + 1]; - for (int i = 0; i < items.size(); i ++) { - themes[i] = items.get(i).getName(); - } - themes[items.size()] = "None"; - ArrayAdapter themeAdapter = new ArrayAdapter<>( - options.get().getActivity(), android.R.layout.simple_spinner_item, themes); - themeAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - options.get().mSpnrThemes.setAdapter(themeAdapter); - options.get().mSpnrThemes.setOnItemSelectedListener(new OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - String theme = String.valueOf(parentView.getItemAtPosition(position)); - if (theme.equals("None")) { - options.get().mPrefs.edit().remove(Config.pref_theme).apply(); - } else { - String theme_path = options.get().home_directory + "/themes/" + theme; - options.get().mPrefs.edit().putString(Config.pref_theme, theme_path).apply(); - } - } - @Override - public void onNothingSelected(AdapterView parentView) { - - } - }); - } else { - options.get().mSpnrThemes.setEnabled(false); - } - } - } - - private void hideSoftKeyBoard() { - try { - InputMethodManager iMm = (InputMethodManager) getActivity() - .getSystemService(Context.INPUT_METHOD_SERVICE); - if (iMm != null && iMm.isAcceptingText()) { - iMm.hideSoftInputFromWindow(getActivity().getCurrentFocus().getWindowToken(), 0); - } - } catch (NullPointerException e) { - // Keyboard may still be visible - } - } - - private void copy(File src, File dst) throws IOException { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - try (InputStream in = new FileInputStream(src)) { - try (OutputStream out = new FileOutputStream(dst)) { - // Transfer bytes from in to out - byte[] buf = new byte[1024]; - int len; - while ((len = in.read(buf)) > 0) { - out.write(buf, 0, len); - } - } - } - } else { - InputStream in = new FileInputStream(src); - OutputStream out = new FileOutputStream(dst); - try { - // Transfer bytes from in to out - byte[] buf = new byte[1024]; - int len; - while ((len = in.read(buf)) > 0) { - out.write(buf, 0, len); - } - } finally { - in.close(); - out.close(); - } - } - } - - private void flashBios(String localized) { - File local = new File(home_directory, "data/dc_flash[" + localized - + "].bin"); - File flash = new File(home_directory, "data/dc_flash.bin"); - - if (local.exists()) { - if (flash.exists()) { - flash.delete(); - } - try { - copy(local, flash); - } catch (IOException ex) { - ex.printStackTrace(); - local.renameTo(flash); - } - mPrefs.edit().putString("localized", localized).apply(); - } - } - - private void resetEmuSettings() { - mPrefs.edit().remove(Config.pref_gamedetails).apply(); - mPrefs.edit().remove(Config.pref_rendertype).apply(); - mPrefs.edit().remove(Config.pref_renderdepth).apply(); - mPrefs.edit().remove(Config.pref_theme).apply(); - - Emulator.nosound = false; - - getActivity().finish(); - } - - private void showToastMessage(String message, int duration) { - ConstraintLayout layout = (ConstraintLayout) getActivity().findViewById(R.id.mainui_layout); - Snackbar snackbar = Snackbar.make(layout, message, duration); - View snackbarLayout = snackbar.getView(); - TextView textView = (TextView) snackbarLayout.findViewById( - android.support.design.R.id.snackbar_text); - textView.setGravity(Gravity.CENTER_VERTICAL); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) - textView.setTextAlignment(View.TEXT_ALIGNMENT_GRAVITY); - Drawable drawable; - if (android.os.Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { - drawable = getResources().getDrawable( - R.drawable.ic_settings, getActivity().getTheme()); - } else { - drawable = VectorDrawableCompat.create(getResources(), - R.drawable.ic_settings, getActivity().getTheme()); - } - textView.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null); - textView.setCompoundDrawablePadding(getResources() - .getDimensionPixelOffset(R.dimen.snackbar_icon_padding)); - snackbar.show(); - } -} diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/debug/GitAdapter.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/debug/GitAdapter.java deleted file mode 100644 index 97e618f8d..000000000 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/debug/GitAdapter.java +++ /dev/null @@ -1,165 +0,0 @@ -package com.reicast.emulator.debug; - -import android.annotation.SuppressLint; -import android.annotation.TargetApi; -import android.app.Activity; -import android.app.AlertDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.graphics.PorterDuff; -import android.os.Build; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.webkit.CookieManager; -import android.webkit.CookieSyncManager; -import android.webkit.WebSettings; -import android.webkit.WebSettings.PluginState; -import android.webkit.WebView; -import android.webkit.WebViewClient; -import android.widget.BaseAdapter; -import android.widget.ImageView; -import android.widget.RelativeLayout; -import android.widget.TextView; - -import com.nostra13.universalimageloader.core.DisplayImageOptions; -import com.nostra13.universalimageloader.core.ImageLoader; -import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; -import com.nostra13.universalimageloader.core.assist.ImageScaleType; -import com.reicast.emulator.R; - -import java.util.ArrayList; -import java.util.HashMap; - -public class GitAdapter extends BaseAdapter { - - private ArrayList> data; - private LayoutInflater inflater = null; - private DisplayImageOptions options; - - public GitAdapter(Activity activity, ArrayList> d) { - this.data = d; - this.inflater = (LayoutInflater) activity - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - ImageLoaderConfiguration configicon = new ImageLoaderConfiguration.Builder( - activity).memoryCacheExtraOptions(96, 96).build(); - this.options = new DisplayImageOptions.Builder() - .showStubImage(R.drawable.ic_github) - .showImageForEmptyUri(R.drawable.ic_github) - .imageScaleType(ImageScaleType.EXACTLY_STRETCHED).build(); - - ImageLoader.getInstance().init(configicon); - - } - - public int getCount() { - return this.data.size(); - } - - public Object getItem(int position) { - return position; - } - - public long getItemId(int position) { - return position; - } - - public View getView(final int position, View convertView, ViewGroup parent) { - View vi = convertView; - if (convertView == null) - vi = this.inflater.inflate(R.layout.change_item, null); - TextView dateText = (TextView) vi.findViewById(R.id.date); - TextView committerText = (TextView) vi.findViewById(R.id.committer); - TextView titleText = (TextView) vi.findViewById(R.id.title); - ImageView avatarIcon = (ImageView) vi.findViewById(R.id.avatar); - - final HashMap commit = this.data.get(position); - final String date = commit.get("Date"); - final String committer = commit.get("Committer"); - final String title = commit.get("Title"); - final String message = commit.get("Message"); - final String sha = commit.get("Sha"); - final String url = commit.get("Url"); - final String author = commit.get("Author"); - final String avatar = commit.get("Avatar"); - final String current = commit.get("Build"); - - RelativeLayout item = (RelativeLayout) vi.findViewById(R.id.change); - if (current != null && current.equals(sha.substring(0, 7))) { - item.getBackground().setColorFilter(0xFF00FF00, - PorterDuff.Mode.MULTIPLY); - } else { - item.getBackground().setColorFilter(null); - } - - dateText.setText(date); - committerText.setText(committer); - titleText.setText(title); - ImageLoader.getInstance().displayImage(avatar, avatarIcon, this.options); - - vi.setOnClickListener(new OnClickListener() { - public void onClick(View v) { - System.gc(); - String output = title + "\n\n" + message + "\n\n" + " - " + author; - displayCommit(sha, output, url, v.getContext()); - } - }); - // Handle clicking individual item from list - - return vi; - } - - private void displayCommit(final String sha, String message, String url, - Context context) { - final AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setCancelable(true); - builder.setTitle(sha.substring(0,7)); - builder.setMessage(message); - LayoutInflater infalter = LayoutInflater.from(context); - final View popWebView = infalter.inflate(R.layout.webview, null); - WebView mWebView = (WebView) popWebView.findViewById(R.id.webframe); - mWebView = configureWebview(url, context, mWebView); - builder.setView(popWebView); - builder.setPositiveButton("Close", - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - return; - } - }); - builder.create().show(); - } - - @TargetApi(Build.VERSION_CODES.HONEYCOMB) - @SuppressLint("SetJavaScriptEnabled") - @SuppressWarnings("deprecation") - private WebView configureWebview(String url, Context context, - WebView mWebView) { - mWebView.getSettings().setSupportZoom(true); - mWebView.getSettings().setBuiltInZoomControls(true); - mWebView.getSettings().setDisplayZoomControls(false); - mWebView.setInitialScale(1); - mWebView.getSettings().setUseWideViewPort(true); - mWebView.getSettings().setLoadWithOverviewMode(true); - mWebView.getSettings().setJavaScriptEnabled(true); - mWebView.getSettings().setPluginState(PluginState.ON); - mWebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE); - mWebView.clearHistory(); - mWebView.clearFormData(); - mWebView.clearCache(true); - CookieSyncManager.createInstance(context); - CookieManager cookieManager = CookieManager.getInstance(); - cookieManager.removeAllCookie(); - CookieSyncManager.getInstance().stopSync(); - mWebView.setWebViewClient(new WebViewClient() { - @Override - public boolean shouldOverrideUrlLoading(WebView view, String url) { - view.loadUrl(url); - return true; - } - }); - mWebView.loadUrl(url); - return mWebView; - } -} \ No newline at end of file diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/GL2JNIView.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/GL2JNIView.java index e2d9b2acb..11396d918 100644 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/GL2JNIView.java +++ b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/GL2JNIView.java @@ -2,35 +2,21 @@ package com.reicast.emulator.emu; import android.annotation.TargetApi; -import android.app.Activity; import android.content.Context; import android.content.SharedPreferences; -import android.content.res.Configuration; import android.graphics.PixelFormat; import android.opengl.GLSurfaceView; import android.os.Build; -import android.os.Environment; import android.os.Handler; -import android.os.Vibrator; import android.preference.PreferenceManager; import android.util.AttributeSet; import android.util.DisplayMetrics; -import android.util.Log; -import android.view.InputDevice; import android.view.MotionEvent; -import android.view.ScaleGestureDetector; -import android.view.ScaleGestureDetector.SimpleOnScaleGestureListener; import android.view.View; -import android.widget.Toast; import com.android.util.FileUtils; -import com.reicast.emulator.Emulator; import com.reicast.emulator.GL2JNIActivity; -import com.reicast.emulator.R; import com.reicast.emulator.config.Config; -import com.reicast.emulator.periph.Gamepad; -import com.reicast.emulator.periph.InputDeviceManager; -import com.reicast.emulator.periph.VJoy; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; @@ -189,12 +175,7 @@ public class GL2JNIView extends GLSurfaceView public void onSurfaceChanged(GL10 gl,int width,int height) { gl.glViewport(0, 0, width, height); - // TODO Is this required? The renderer sets the viewport and scissor test correctly -// if (Emulator.widescreen) { - JNIdc.rendinitJava(width, height); -// } else { -// JNIdc.rendinitJava(height * (4 / 3), height); -// } + JNIdc.rendinitJava(width, height); } public void onSurfaceCreated(GL10 gl,EGLConfig config) @@ -203,16 +184,10 @@ public class GL2JNIView extends GLSurfaceView } } - public void onDestroy() { - /* - // Workaround for ANR when returning to menu - System.exit(0); - try { - ethd.join(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - */ + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + JNIdc.rendtermJava(); } @TargetApi(19) diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/JNIdc.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/JNIdc.java index 6262fb27a..fef8b9dfa 100644 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/JNIdc.java +++ b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/JNIdc.java @@ -21,8 +21,7 @@ public final class JNIdc public static native int send(int cmd, int opt); public static native int data(int cmd, byte[] data); - public static native void rendinitNative(Surface surface, int w, int h); - public static native boolean rendframeNative(); + public static native void rendinitNative(Surface surface); public static native void rendinitJava(int w, int h); public static native boolean rendframeJava(); public static native void rendtermJava(); diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/NativeGLView.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/NativeGLView.java index 4aabf8c4d..383a8684a 100644 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/NativeGLView.java +++ b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/NativeGLView.java @@ -14,12 +14,12 @@ import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; +import com.reicast.emulator.Emulator; import com.reicast.emulator.NativeGLActivity; import com.reicast.emulator.config.Config; public class NativeGLView extends SurfaceView implements SurfaceHolder.Callback { - private Handler handler = new Handler(); - + private boolean surfaceReady = false; private boolean paused = false; VirtualJoystickDelegate vjoyDelegate; @@ -36,6 +36,9 @@ public class NativeGLView extends SurfaceView implements SurfaceHolder.Callback super(context, attrs); getHolder().addCallback(this); setKeepScreenOn(true); + setFocusable(true); + setFocusableInTouchMode(true); + requestFocus(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { setOnSystemUiVisibilityChangeListener (new OnSystemUiVisibilityChangeListener() { @@ -61,23 +64,6 @@ public class NativeGLView extends SurfaceView implements SurfaceHolder.Callback if (NativeGLActivity.syms != null) JNIdc.data(1, NativeGLActivity.syms); - - startRendering(); - } - - private void startRendering() { - // Continuously render frames - handler.removeCallbacksAndMessages(null); - handler.postAtTime(new Runnable() { - @Override - public void run() { - if (!paused) - { - JNIdc.rendframeNative(); - handler.post(this); - } - } - }, SystemClock.uptimeMillis() + 500); } @Override @@ -105,13 +91,21 @@ public class NativeGLView extends SurfaceView implements SurfaceHolder.Callback @Override public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int w, int h) { //Log.i("reicast", "NativeGLView.surfaceChanged: " + w + "x" + h); - JNIdc.rendinitNative(surfaceHolder.getSurface(), w, h); + surfaceReady = true; + JNIdc.rendinitNative(surfaceHolder.getSurface()); + Emulator.getCurrentActivity().handleStateChange(false); } @Override public void surfaceDestroyed(SurfaceHolder surfaceHolder) { //Log.i("reicast", "NativeGLView.surfaceDestroyed"); - JNIdc.rendinitNative(null, 0, 0); + surfaceReady = false; + JNIdc.rendinitNative(null); + Emulator.getCurrentActivity().handleStateChange(true); + } + + public boolean isSurfaceReady() { + return surfaceReady; } public void pause() { @@ -124,9 +118,11 @@ public class NativeGLView extends SurfaceView implements SurfaceHolder.Callback if (paused) { //Log.i("reicast", "NativeGLView.resume"); paused = false; + setFocusable(true); + setFocusableInTouchMode(true); + requestFocus(); JNIdc.resume(); } - startRendering(); } @TargetApi(19) diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/OnScreenMenu.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/OnScreenMenu.java deleted file mode 100644 index f309aabc8..000000000 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/OnScreenMenu.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.reicast.emulator.emu; - -import android.content.Context; - -public class OnScreenMenu { - - public static int getPixelsFromDp(float dps, Context context) { - return (int) (dps * context.getResources().getDisplayMetrics().density + 0.5f); - } -} diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/VirtualJoystickDelegate.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/VirtualJoystickDelegate.java index 68f35545b..e3e142adf 100644 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/VirtualJoystickDelegate.java +++ b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/VirtualJoystickDelegate.java @@ -15,7 +15,7 @@ import com.reicast.emulator.periph.InputDeviceManager; import com.reicast.emulator.periph.VJoy; public class VirtualJoystickDelegate { - private Vibrator vib; + private VibratorThread vibratorThread; private boolean editVjoyMode = false; private int selectedVjoyElement = -1; @@ -39,7 +39,10 @@ public class VirtualJoystickDelegate { public VirtualJoystickDelegate(View view) { this.view = view; this.context = view.getContext(); - vib = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE); + + vibratorThread = new VibratorThread(context); + vibratorThread.start(); + readCustomVjoyValues(); scaleGestureDetector = new ScaleGestureDetector(context, new OscOnScaleGestureListener()); } @@ -224,8 +227,9 @@ public class VirtualJoystickDelegate { if (y > vjoy[j][1] && y <= (vjoy[j][1] + vjoy[j][3])) { if (vjoy[j][4] >= -2) { if (vjoy[j][5] == 0) - if (!editVjoyMode && Emulator.vibrationDuration > 0) - vib.vibrate(Emulator.vibrationDuration); + if (!editVjoyMode) { + vibratorThread.vibrate(); + } vjoy[j][5] = 2; } @@ -397,4 +401,51 @@ public class VirtualJoystickDelegate { selectedVjoyElement = -1; } } + + private class VibratorThread extends Thread + { + private Vibrator vibrator; + private boolean vibrate = false; + private boolean stopping = false; + + VibratorThread(Context context) { + vibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE); + } + + @Override + public void run() { + while (!stopping) { + boolean doVibrate; + synchronized (this) { + doVibrate = false; + try { + this.wait(); + } catch (InterruptedException e) { + } + if (vibrate) { + doVibrate = true; + vibrate = false; + } + } + if (doVibrate) + vibrator.vibrate(Emulator.vibrationDuration); + } + } + + public void stopVibrator() { + synchronized (this) { + stopping = true; + notify(); + } + } + + public void vibrate() { + if (Emulator.vibrationDuration > 0) { + synchronized (this) { + vibrate = true; + notify(); + } + } + } + } } diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/periph/Gamepad.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/periph/Gamepad.java deleted file mode 100644 index 9b549a4bb..000000000 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/periph/Gamepad.java +++ /dev/null @@ -1,210 +0,0 @@ -package com.reicast.emulator.periph; - -import android.app.UiModeManager; -import android.content.Context; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.content.res.Configuration; -import android.util.SparseArray; -import android.util.SparseIntArray; -import android.view.KeyEvent; - -import java.util.HashMap; - -import tv.ouya.console.api.OuyaController; -import tv.ouya.console.api.OuyaFacade; - -public class Gamepad { - - public static final String pref_player1 = "device_descriptor_player_1"; - public static final String pref_player2 = "device_descriptor_player_2"; - public static final String pref_player3 = "device_descriptor_player_3"; - public static final String pref_player4 = "device_descriptor_player_4"; - private static final String pref_pad = "controller"; - - public static final String pref_mic = "mic_plugged_in"; - - public static final String p2_peripheral = "p2_peripheral"; - public static final String p3_peripheral = "p3_peripheral"; - public static final String p4_peripheral = "p4_peripheral"; - - public static final String pref_js_modified = "modified_key_layout"; - public static final String pref_js_compat = "controller_compat"; - public static final String pref_js_merged = "merged_joystick"; - public static final String pref_js_rstick = "right_joystick"; - - public static final String pref_button_a = "a_button"; - public static final String pref_button_b = "b_button"; - public static final String pref_button_x = "x_button"; - public static final String pref_button_y = "y_button"; - - public static final String pref_button_l = "l_button"; - public static final String pref_button_r = "r_button"; - - public static final String pref_dpad_up = "dpad_up"; - public static final String pref_dpad_down = "dpad_down"; - public static final String pref_dpad_left = "dpad_left"; - public static final String pref_dpad_right = "dpad_right"; - - public static final String pref_axis_x = "x_axis"; - public static final String pref_axis_y = "y_axis"; - - public static final String pref_button_start = "start_button"; - public static final String pref_button_select = "select_button"; - - public static final String controllers_sony = "Sony PLAYSTATION(R)3 Controller"; - public static final String controllers_xbox = "Microsoft X-Box 360 pad"; - public static final String controllers_shield = "NVIDIA Corporation NVIDIA Controller"; - public static final String controllers_gamekey = "gamekeyboard"; - public static final String controllers_moga = "Moga"; - - - public String[] portId = { "_A", "_B", "_C", "_D" }; - public boolean[] compat = { false, false, false, false }; - public boolean[] custom = { false, false, false, false }; - public boolean[] joystick = { false, false, false, false }; - public int[] name = { -1, -1, -1, -1 }; - public float[] globalLS_X = new float[4], globalLS_Y = new float[4], - previousLS_X = new float[4], previousLS_Y = new float[4]; - public int map[][] = new int[4][]; - - public SparseArray deviceId_deviceDescriptor = new SparseArray<>(); - public HashMap deviceDescriptor_PlayerNum = new HashMap<>(); - - public boolean isOuyaOrTV; - - private static final int key_CONT_B = 0x0002; - private static final int key_CONT_A = 0x0004; - private static final int key_CONT_START = 0x0008; - private static final int key_CONT_DPAD_UP = 0x0010; - private static final int key_CONT_DPAD_DOWN = 0x0020; - private static final int key_CONT_DPAD_LEFT = 0x0040; - private static final int key_CONT_DPAD_RIGHT = 0x0080; - private static final int key_CONT_Y = 0x0200; - private static final int key_CONT_X = 0x0400; - - public int[] getConsoleController() { - return new int[] { - OuyaController.BUTTON_O, key_CONT_A, - OuyaController.BUTTON_A, key_CONT_B, - OuyaController.BUTTON_U, key_CONT_X, - OuyaController.BUTTON_Y, key_CONT_Y, - - OuyaController.BUTTON_DPAD_UP, key_CONT_DPAD_UP, - OuyaController.BUTTON_DPAD_DOWN, key_CONT_DPAD_DOWN, - OuyaController.BUTTON_DPAD_LEFT, key_CONT_DPAD_LEFT, - OuyaController.BUTTON_DPAD_RIGHT, key_CONT_DPAD_RIGHT, - - getStartButtonCode(), key_CONT_START, - getSelectButtonCode(), getSelectButtonCode() - // Redundant, but verifies it is mapped properly - }; - } - - public int[] getOUYAController() { - return new int[] { - OuyaController.BUTTON_O, key_CONT_A, - OuyaController.BUTTON_A, key_CONT_B, - OuyaController.BUTTON_U, key_CONT_X, - OuyaController.BUTTON_Y, key_CONT_Y, - - OuyaController.BUTTON_DPAD_UP, key_CONT_DPAD_UP, - OuyaController.BUTTON_DPAD_DOWN, key_CONT_DPAD_DOWN, - OuyaController.BUTTON_DPAD_LEFT, key_CONT_DPAD_LEFT, - OuyaController.BUTTON_DPAD_RIGHT, key_CONT_DPAD_RIGHT, - - getStartButtonCode(), key_CONT_START, - OuyaController.BUTTON_R3, key_CONT_START - }; - } - - public int[] getMogaController() { - return new int[] { - KeyEvent.KEYCODE_BUTTON_A, key_CONT_A, - KeyEvent.KEYCODE_BUTTON_B, key_CONT_B, - KeyEvent.KEYCODE_BUTTON_X, key_CONT_X, - KeyEvent.KEYCODE_BUTTON_Y, key_CONT_Y, - - KeyEvent.KEYCODE_DPAD_UP, key_CONT_DPAD_UP, - KeyEvent.KEYCODE_DPAD_DOWN, key_CONT_DPAD_DOWN, - KeyEvent.KEYCODE_DPAD_LEFT, key_CONT_DPAD_LEFT, - KeyEvent.KEYCODE_DPAD_RIGHT, key_CONT_DPAD_RIGHT, - - getStartButtonCode(), key_CONT_START, - getSelectButtonCode(), getSelectButtonCode() - }; - } - - private int[] setModifiedKeys(String id, SharedPreferences mPrefs) { - return new int[] { - mPrefs.getInt(pref_button_a + id, OuyaController.BUTTON_O), key_CONT_A, - mPrefs.getInt(pref_button_b + id, OuyaController.BUTTON_A), key_CONT_B, - mPrefs.getInt(pref_button_x + id, OuyaController.BUTTON_U), key_CONT_X, - mPrefs.getInt(pref_button_y + id, OuyaController.BUTTON_Y), key_CONT_Y, - - mPrefs.getInt(pref_dpad_up + id, OuyaController.BUTTON_DPAD_UP), key_CONT_DPAD_UP, - mPrefs.getInt(pref_dpad_down + id, OuyaController.BUTTON_DPAD_DOWN), key_CONT_DPAD_DOWN, - mPrefs.getInt(pref_dpad_left + id, OuyaController.BUTTON_DPAD_LEFT), key_CONT_DPAD_LEFT, - mPrefs.getInt(pref_dpad_right + id, OuyaController.BUTTON_DPAD_RIGHT), key_CONT_DPAD_RIGHT, - - mPrefs.getInt(pref_button_start + id, getStartButtonCode()), key_CONT_START, - mPrefs.getInt(pref_button_select + id, getSelectButtonCode()), getSelectButtonCode() - }; - } - - public boolean IsOuyaOrTV(Context context, boolean ouya) { - if (ouya) { - return OuyaFacade.getInstance().isRunningOnOUYAHardware(); - } else { - try { - UiModeManager uiModeManager = (UiModeManager) - context.getSystemService(Context.UI_MODE_SERVICE); - if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) { - return true; - } - } catch (Exception e) { - // Not entirely important - } - PackageManager pMan = context.getPackageManager(); - return pMan.hasSystemFeature(PackageManager.FEATURE_TELEVISION) - || OuyaFacade.getInstance().isRunningOnOUYAHardware(); - } - } - - private int getStartButtonCode() { - return KeyEvent.KEYCODE_BUTTON_START; - } - - public int getSelectButtonCode() { - return KeyEvent.KEYCODE_BUTTON_SELECT; - } - - public void setCustomMapping(String id, int playerNum, SharedPreferences prefs) { - map[playerNum] = setModifiedKeys(id, prefs); - } - - public void initJoyStickLayout(int playerNum) { - if (!joystick[playerNum]) { - globalLS_X[playerNum] = previousLS_X[playerNum] = 0.0f; - globalLS_Y[playerNum] = previousLS_Y[playerNum] = 0.0f; - } - } - - public void fullCompatibilityMode(SharedPreferences prefs) { - for (int joy = 0; joy < 4; joy++) { - if (compat[joy]) { - String id = portId[joy]; - joystick[joy] = prefs.getBoolean(Gamepad.pref_js_merged + id, false); - getCompatibilityMap(joy, portId[joy], prefs); - initJoyStickLayout(joy); - } - } - } - - public void getCompatibilityMap(int playerNum, String id, SharedPreferences prefs) { - name[playerNum] = prefs.getInt(Gamepad.pref_pad + id, -1); - if (name[playerNum] != -1) { - map[playerNum] = setModifiedKeys(id, prefs); - } - } -} diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/periph/InputDeviceManager.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/periph/InputDeviceManager.java index ec533e842..cfe754612 100644 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/periph/InputDeviceManager.java +++ b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/periph/InputDeviceManager.java @@ -24,7 +24,7 @@ public final class InputDeviceManager implements InputManager.InputDeviceListene { maple_port = 0; if (applicationContext.getPackageManager().hasSystemFeature("android.hardware.touchscreen")) - joystickAdded(VIRTUAL_GAMEPAD_ID, "Virtual Gamepad", maple_port == 3 ? 3 : maple_port++); + joystickAdded(VIRTUAL_GAMEPAD_ID, "Virtual Gamepad", maple_port == 3 ? 3 : maple_port++, "virtual_gamepad_uid"); int[] ids = InputDevice.getDeviceIds(); for (int id : ids) onInputDeviceAdded(id); @@ -34,8 +34,10 @@ public final class InputDeviceManager implements InputManager.InputDeviceListene public void stopListening() { - inputManager.unregisterInputDeviceListener(this); - inputManager = null; + if (inputManager != null) { + inputManager.unregisterInputDeviceListener(this); + inputManager = null; + } joystickRemoved(VIRTUAL_GAMEPAD_ID); } @@ -47,7 +49,7 @@ public final class InputDeviceManager implements InputManager.InputDeviceListene if ((device.getSources() & InputDevice.SOURCE_CLASS_JOYSTICK) == InputDevice.SOURCE_CLASS_JOYSTICK) { port = this.maple_port == 3 ? 3 : this.maple_port++; } - joystickAdded(i, device.getName(), port); + joystickAdded(i, device.getName(), port, device.getDescriptor()); } } @@ -94,6 +96,6 @@ public final class InputDeviceManager implements InputManager.InputDeviceListene public native boolean joystickButtonEvent(int id, int button, boolean pressed); public native boolean joystickAxisEvent(int id, int button, int value); public native void mouseEvent(int xpos, int ypos, int buttons); - private native void joystickAdded(int id, String name, int maple_port); + private native void joystickAdded(int id, String name, int maple_port, String uniqueId); private native void joystickRemoved(int id); } diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/periph/VmuLcd.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/periph/VmuLcd.java deleted file mode 100644 index 13522c6ef..000000000 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/periph/VmuLcd.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.reicast.emulator.periph; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.util.Log; -import android.view.View; - -import com.reicast.emulator.emu.OnScreenMenu; - -public class VmuLcd extends View { - - public final static int w = 48; - public final static int h = 32; - - private int[] image = new int[w*h]; - private Bitmap current = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); - private float scale; - private Paint paint; - - public VmuLcd(Context context) { - super(context); - paint = new Paint(); - scale = (float)OnScreenMenu.getPixelsFromDp(60, getContext()) / w; - Log.d("VmuLcd", "scale: "+scale); - } - - public void configureScale(int dp) { - scale = (float)OnScreenMenu.getPixelsFromDp(dp, getContext()) / w; - } - - public void updateBytes(byte[] data){ - for(int i=0; iDeleteLocalRef(dir); } setenv("REICAST_HOME", paths.c_str(), 1); + gui_refresh_files(); } JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_bootdisk(JNIEnv *env,jobject obj, jstring disk) { @@ -314,10 +314,6 @@ JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_diskSwap(JNIEnv *env, //stuff for microphone jobject sipemu; jmethodID getmicdata; -//stuff for vmu lcd -jobject vmulcd = NULL; -jbyteArray jpix = NULL; -jmethodID updatevmuscreen; extern bool game_started; //stuff for audio @@ -332,12 +328,6 @@ JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_setupMic(JNIEnv *env, getmicdata = env->GetMethodID(env->GetObjectClass(sipemu),"getData","()[B"); } -JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_setupVmu(JNIEnv *env,jobject obj,jobject vmu) -{ - vmulcd = env->NewGlobalRef(vmu); - updatevmuscreen = env->GetMethodID(env->GetObjectClass(vmu),"updateBytes","([B)V"); -} - JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_pause(JNIEnv *env,jobject obj) { if (game_started) @@ -361,18 +351,6 @@ JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_destroy(JNIEnv *env,j dc_term(); } -JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_vmuSwap(JNIEnv *env,jobject obj) -{ - maple_device* olda = MapleDevices[0][0]; - maple_device* oldb = MapleDevices[0][1]; - MapleDevices[0][0] = NULL; - MapleDevices[0][1] = NULL; - usleep(50000);//50 ms, wait for host to detect disconnect - - MapleDevices[0][0] = oldb; - MapleDevices[0][1] = olda; -} - JNIEXPORT jint JNICALL Java_com_reicast_emulator_emu_JNIdc_send(JNIEnv *env,jobject obj,jint cmd, jint param) { if (cmd==0) @@ -425,35 +403,57 @@ JNIEXPORT jint JNICALL Java_com_reicast_emulator_emu_JNIdc_data(JNIEnv *env, job extern void gl_swap(); extern void egl_stealcntx(); +volatile static bool render_running; +volatile static bool render_reinit; -JNIEXPORT jboolean JNICALL Java_com_reicast_emulator_emu_JNIdc_rendframeNative(JNIEnv *env,jobject obj) +void *render_thread_func(void *) { - if (g_window == NULL) - return false; - if (!egl_makecurrent()) - return false; - jboolean ret = (jboolean)rend_single_frame(); - if (ret) - gl_swap(); - return ret; + render_running = true; + + rend_init_renderer(); + + while (render_running) { + if (render_reinit) + { + render_reinit = false; + rend_init_renderer(); + } + else + if (!egl_makecurrent()) + break;; + + bool ret = rend_single_frame(); + if (ret) + gl_swap(); + } + egl_makecurrent(); + rend_term_renderer(); + ANativeWindow_release(g_window); + g_window = NULL; + render_running = false; + + return NULL; } -JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_rendinitNative(JNIEnv * env, jobject obj, jobject surface, jint width, jint height) +static cThread render_thread(render_thread_func, NULL); + +JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_rendinitNative(JNIEnv * env, jobject obj, jobject surface) { - if (g_window != NULL) - { - egl_makecurrent(); - rend_term_renderer(); - ANativeWindow_release(g_window); - g_window = NULL; - } - if (surface != NULL) - { + if (render_thread.hThread != NULL) + { + if (surface == NULL) + { + render_running = false; + render_thread.WaitToEnd(); + } + else + render_reinit = true; + } + else if (surface != NULL) + { g_window = ANativeWindow_fromSurface(env, surface); - rend_init_renderer(); - screen_width = width; - screen_height = height; - } + render_thread.Start(); + } } JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_rendinitJava(JNIEnv * env, jobject obj, jint width, jint height) @@ -547,9 +547,13 @@ audiobackend_t audiobackend_android = { "Android Audio", // Name &androidaudio_init, &androidaudio_push, - &androidaudio_term + &androidaudio_term, + NULL }; +static bool android = RegisterAudioBackend(&audiobackend_android); + + JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_AudioBackend_setInstance(JNIEnv *env, jobject obj, jobject instance) { if (g_audioBackend != NULL) @@ -583,26 +587,13 @@ int get_mic_data(u8* buffer) return 1; } -int push_vmu_screen(u8* buffer) -{ - if(vmulcd==NULL){ - return 0; - } - if(jpix==NULL){ - jpix = jvm_attacher.getEnv()->NewByteArray(1536); - } - jvm_attacher.getEnv()->SetByteArrayRegion(jpix, 0, 1536, (jbyte*)buffer); - jvm_attacher.getEnv()->CallVoidMethod(vmulcd, updatevmuscreen, jpix); - return 1; -} - void os_DebugBreak() { // TODO: notify the parent thread about it ... raise(SIGABRT); //pthread_exit(NULL); - + // Attach debugger here to figure out what went wrong for(;;) ; } @@ -621,13 +612,14 @@ JNIEXPORT void JNICALL Java_com_reicast_emulator_periph_InputDeviceManager_init( input_device_manager_rumble = env->GetMethodID(env->GetObjectClass(obj), "rumble", "(IFFI)Z"); } -JNIEXPORT void JNICALL Java_com_reicast_emulator_periph_InputDeviceManager_joystickAdded(JNIEnv *env, jobject obj, jint id, jstring name, jint maple_port) +JNIEXPORT void JNICALL Java_com_reicast_emulator_periph_InputDeviceManager_joystickAdded(JNIEnv *env, jobject obj, jint id, jstring name, jint maple_port, jstring junique_id) { const char* joyname = env->GetStringUTFChars(name,0); - std::shared_ptr gamepad = std::make_shared(maple_port, id, joyname); + const char* unique_id = env->GetStringUTFChars(junique_id, 0); + std::shared_ptr gamepad = std::make_shared(maple_port, id, joyname, unique_id); AndroidGamepadDevice::AddAndroidGamepad(gamepad); env->ReleaseStringUTFChars(name, joyname); - + env->ReleaseStringUTFChars(name, unique_id); } JNIEXPORT void JNICALL Java_com_reicast_emulator_periph_InputDeviceManager_joystickRemoved(JNIEnv *env, jobject obj, jint id) { diff --git a/shell/android-studio/reicast/src/main/jni/src/android_gamepad.h b/shell/android-studio/reicast/src/main/jni/src/android_gamepad.h index 18e7fcf9e..c1fe2b465 100644 --- a/shell/android-studio/reicast/src/main/jni/src/android_gamepad.h +++ b/shell/android-studio/reicast/src/main/jni/src/android_gamepad.h @@ -67,13 +67,32 @@ public: } }; +class ShieldRemoteInputMapping : public InputMapping +{ +public: + ShieldRemoteInputMapping() + { + name = "Default"; + set_button(DC_BTN_A, 23); + set_button(DC_DPAD_UP, 19); + set_button(DC_DPAD_DOWN, 20); + set_button(DC_DPAD_LEFT, 21); + set_button(DC_DPAD_RIGHT, 22); + set_button(EMU_BTN_MENU, 4); + + dirty = false; + } +}; + class AndroidGamepadDevice : public GamepadDevice { public: - AndroidGamepadDevice(int maple_port, int id, const char *name) : GamepadDevice(maple_port, "Android", id != VIRTUAL_GAMEPAD_ID), android_id(id) + AndroidGamepadDevice(int maple_port, int id, const char *name, const char *unique_id) + : GamepadDevice(maple_port, "Android", id != VIRTUAL_GAMEPAD_ID), android_id(id) { _name = name; - printf("Android: Opened joystick %d on port %d: '%s' ", id, maple_port, _name.c_str()); + _unique_id = unique_id; + printf("Android: Opened joystick %d on port %d: '%s' descriptor '%s'", id, maple_port, _name.c_str(), _unique_id.c_str()); if (id == VIRTUAL_GAMEPAD_ID) { input_mapper = new IdentityInputMapping(); @@ -89,7 +108,10 @@ public: } else if (!find_mapping()) { - input_mapper = new DefaultInputMapping(); + if (_name == "SHIELD Remote") + input_mapper = new ShieldRemoteInputMapping(); + else + input_mapper = new DefaultInputMapping(); save_mapping(); printf("using default mapping\n"); } @@ -193,6 +215,7 @@ public: AndroidMouseGamepadDevice(int maple_port) : GamepadDevice(maple_port, "Android") { _name = "Mouse"; + _unique_id = "android_mouse"; if (!find_mapping()) input_mapper = new MouseInputMapping(); } diff --git a/shell/android-studio/reicast/src/main/jni/src/utils.cpp b/shell/android-studio/reicast/src/main/jni/src/utils.cpp index 5c888e819..699170708 100644 --- a/shell/android-studio/reicast/src/main/jni/src/utils.cpp +++ b/shell/android-studio/reicast/src/main/jni/src/utils.cpp @@ -1,27 +1,6 @@ -#include -#include -extern "C" { -#include "deps/libpng/png.h" -} #include "types.h" #include "deps/libzip/zip.h" -#define TEXTURE_LOAD_ERROR 0 - -//Taken from http://en.wikibooks.org/wiki/OpenGL_Programming/Intermediate/Textures -/** loadTexture - * loads a png file into an opengl texture object, using cstdio , libpng, and opengl. - * - * \param filename : the png file to be loaded - * \param width : width of png, to be updated as a side effect of this function - * \param height : height of png, to be updated as a side effect of this function - * - * \return GLuint : an opengl texture id. Will be 0 if there is a major error, - * should be validated by the client of this function. - * - */ -zip_file* file; - zip* APKArchive; void setAPK (const char* apkPath) { LOGI("Loading APK %s", apkPath); @@ -42,133 +21,3 @@ void setAPK (const char* apkPath) { LOGI("File %i : %s\n", i, name); } } - -void png_zip_read(png_structp png_ptr, png_bytep data, png_size_t length) { - zip_fread(file, data, length); -} - -GLuint loadTextureFromPNG(const char* filename, int &width, int &height) { - file = zip_fopen(APKArchive, filename, 0); - if (!file) { - LOGE("Error opening %s from APK", filename); - return TEXTURE_LOAD_ERROR; - } - - //header for testing if it is a png - png_byte header[8]; - - //read the header - zip_fread(file, header, 8); - - //test if png - int is_png = !png_sig_cmp(header, 0, 8); - if (!is_png) { - zip_fclose(file); - LOGE("Not a png file : %s", filename); - return TEXTURE_LOAD_ERROR; - } - - //create png struct - png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!png_ptr) { - zip_fclose(file); - LOGE("Unable to create png struct : %s", filename); - return (TEXTURE_LOAD_ERROR); - } - - //create png info struct - png_infop info_ptr = png_create_info_struct(png_ptr); - if (!info_ptr) { - png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL); - LOGE("Unable to create png info : %s", filename); - zip_fclose(file); - return (TEXTURE_LOAD_ERROR); - } - - //create png info struct - png_infop end_info = png_create_info_struct(png_ptr); - if (!end_info) { - png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL); - LOGE("Unable to create png end info : %s", filename); - zip_fclose(file); - return (TEXTURE_LOAD_ERROR); - } - - //png error stuff, not sure libpng man suggests this. - if (setjmp(png_jmpbuf(png_ptr))) { - zip_fclose(file); - LOGE("Error during setjmp : %s", filename); - png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); - return (TEXTURE_LOAD_ERROR); - } - - //init png reading - //png_init_io(png_ptr, fp); - png_set_read_fn(png_ptr, NULL, png_zip_read); - - //let libpng know you already read the first 8 bytes - png_set_sig_bytes(png_ptr, 8); - - // read all the info up to the image data - png_read_info(png_ptr, info_ptr); - - //variables to pass to get info - int bit_depth, color_type; - png_uint_32 twidth, theight; - - // get info about png - png_get_IHDR(png_ptr, info_ptr, &twidth, &theight, &bit_depth, &color_type, NULL, NULL, NULL); - - //update width and height based on png info - width = twidth; - height = theight; - - // Update the png info struct. - png_read_update_info(png_ptr, info_ptr); - - // Row size in bytes. - int rowbytes = png_get_rowbytes(png_ptr, info_ptr); - - // Allocate the image_data as a big block, to be given to opengl - png_byte *image_data = new png_byte[rowbytes * height]; - if (!image_data) { - //clean up memory and close stuff - png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); - LOGE("Unable to allocate image_data while loading %s ", filename); - zip_fclose(file); - return TEXTURE_LOAD_ERROR; - } - - //row_pointers is for pointing to image_data for reading the png with libpng - png_bytep *row_pointers = new png_bytep[height]; - if (!row_pointers) { - //clean up memory and close stuff - png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); - delete[] image_data; - LOGE("Unable to allocate row_pointer while loading %s ", filename); - zip_fclose(file); - return TEXTURE_LOAD_ERROR; - } - // set the individual row_pointers to point at the correct offsets of image_data - for (int i = 0; i < height; ++i) - row_pointers[height - 1 - i] = image_data + i * rowbytes; - - //read the png into image_data through row_pointers - png_read_image(png_ptr, row_pointers); - - //Now generate the OpenGL texture object - GLuint texture; - glGenTextures(1, &texture); - glBindTexture(GL_TEXTURE_2D, texture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, - GL_UNSIGNED_BYTE, (GLvoid*) image_data); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - - //clean up memory and close stuff - png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); - delete[] image_data; - delete[] row_pointers; - zip_fclose(file); - - return texture; -} diff --git a/shell/android-studio/reicast/src/main/jni/src/utils.h b/shell/android-studio/reicast/src/main/jni/src/utils.h index 50c4011cf..b22d581d7 100644 --- a/shell/android-studio/reicast/src/main/jni/src/utils.h +++ b/shell/android-studio/reicast/src/main/jni/src/utils.h @@ -2,7 +2,5 @@ #define UTILS_H_ void setAPK (const char* apkPath); -//Filename will be looked up in the apk (should start with assets/ or res/ -GLuint loadTextureFromPNG (const char* filename, int &width, int &height); #endif /* UTILS_H_ */ diff --git a/shell/android-studio/reicast/src/main/res/drawable-hdpi/ic_drawer.png b/shell/android-studio/reicast/src/main/res/drawable-hdpi/ic_drawer.png deleted file mode 100644 index b9bc3d70f..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable-hdpi/ic_drawer.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable-hdpi/ic_github.png b/shell/android-studio/reicast/src/main/res/drawable-hdpi/ic_github.png deleted file mode 100644 index 5166982de..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable-hdpi/ic_github.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable-hdpi/ic_notification.png b/shell/android-studio/reicast/src/main/res/drawable-hdpi/ic_notification.png deleted file mode 100644 index 51e968ac8..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable-hdpi/ic_notification.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable-ldpi/ic_drawer.png b/shell/android-studio/reicast/src/main/res/drawable-ldpi/ic_drawer.png deleted file mode 100644 index fb681ba26..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable-ldpi/ic_drawer.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable-ldpi/ic_github.png b/shell/android-studio/reicast/src/main/res/drawable-ldpi/ic_github.png deleted file mode 100644 index 95c9da554..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable-ldpi/ic_github.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable-ldpi/ic_notification.png b/shell/android-studio/reicast/src/main/res/drawable-ldpi/ic_notification.png deleted file mode 100644 index c1fba896e..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable-ldpi/ic_notification.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable-mdpi/ic_action_search.png b/shell/android-studio/reicast/src/main/res/drawable-mdpi/ic_action_search.png deleted file mode 100644 index 134d5490b..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable-mdpi/ic_action_search.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable-mdpi/ic_drawer.png b/shell/android-studio/reicast/src/main/res/drawable-mdpi/ic_drawer.png deleted file mode 100644 index ff7b1def9..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable-mdpi/ic_drawer.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable-mdpi/ic_github.png b/shell/android-studio/reicast/src/main/res/drawable-mdpi/ic_github.png deleted file mode 100644 index 416a94299..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable-mdpi/ic_github.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable-mdpi/ic_notification.png b/shell/android-studio/reicast/src/main/res/drawable-mdpi/ic_notification.png deleted file mode 100644 index 15d210961..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable-mdpi/ic_notification.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable-mdpi/side_nav_bar.xml b/shell/android-studio/reicast/src/main/res/drawable-mdpi/side_nav_bar.xml deleted file mode 100644 index 458b4b07d..000000000 --- a/shell/android-studio/reicast/src/main/res/drawable-mdpi/side_nav_bar.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - \ No newline at end of file diff --git a/shell/android-studio/reicast/src/main/res/drawable-xhdpi/ic_action_search.png b/shell/android-studio/reicast/src/main/res/drawable-xhdpi/ic_action_search.png deleted file mode 100644 index d699c6b37..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable-xhdpi/ic_action_search.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable-xhdpi/ic_github.png b/shell/android-studio/reicast/src/main/res/drawable-xhdpi/ic_github.png deleted file mode 100644 index 3b4ec1b3a..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable-xhdpi/ic_github.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable-xhdpi/ic_notification.png b/shell/android-studio/reicast/src/main/res/drawable-xhdpi/ic_notification.png deleted file mode 100644 index 6658defef..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable-xhdpi/ic_notification.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable-xhdpi/ouya_icon.png b/shell/android-studio/reicast/src/main/res/drawable-xhdpi/ouya_icon.png deleted file mode 100644 index c9832b6dc..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable-xhdpi/ouya_icon.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable-xxhdpi/ic_github.png b/shell/android-studio/reicast/src/main/res/drawable-xxhdpi/ic_github.png deleted file mode 100644 index 150875853..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable-xxhdpi/ic_github.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable-xxhdpi/ic_notification.png b/shell/android-studio/reicast/src/main/res/drawable-xxhdpi/ic_notification.png deleted file mode 100644 index 3ff8d6862..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable-xxhdpi/ic_notification.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/apply.png b/shell/android-studio/reicast/src/main/res/drawable/apply.png deleted file mode 100644 index e9a922f78..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/apply.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/background.xml b/shell/android-studio/reicast/src/main/res/drawable/background.xml deleted file mode 100644 index ae833c037..000000000 --- a/shell/android-studio/reicast/src/main/res/drawable/background.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/shell/android-studio/reicast/src/main/res/drawable/background_dark.xml b/shell/android-studio/reicast/src/main/res/drawable/background_dark.xml new file mode 100644 index 000000000..c57a13744 --- /dev/null +++ b/shell/android-studio/reicast/src/main/res/drawable/background_dark.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/shell/android-studio/reicast/src/main/res/drawable/cd.png b/shell/android-studio/reicast/src/main/res/drawable/cd.png deleted file mode 100644 index 3d7f847a8..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/cd.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/cdi.png b/shell/android-studio/reicast/src/main/res/drawable/cdi.png deleted file mode 100644 index 6e39d4370..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/cdi.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/chd.png b/shell/android-studio/reicast/src/main/res/drawable/chd.png deleted file mode 100644 index 428608c09..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/chd.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/clear_cache.png b/shell/android-studio/reicast/src/main/res/drawable/clear_cache.png deleted file mode 100644 index 8cd4a771a..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/clear_cache.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/close.png b/shell/android-studio/reicast/src/main/res/drawable/close.png deleted file mode 100644 index c326c0e79..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/close.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/cloud.png b/shell/android-studio/reicast/src/main/res/drawable/cloud.png deleted file mode 100644 index dd0e3f8f6..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/cloud.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/cloud_big.png b/shell/android-studio/reicast/src/main/res/drawable/cloud_big.png deleted file mode 100644 index ed95038a5..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/cloud_big.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/config.png b/shell/android-studio/reicast/src/main/res/drawable/config.png deleted file mode 100644 index 807c4a922..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/config.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/debug.png b/shell/android-studio/reicast/src/main/res/drawable/debug.png deleted file mode 100644 index 3856cdccf..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/debug.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/disc.png b/shell/android-studio/reicast/src/main/res/drawable/disc.png deleted file mode 100644 index 3671b6d90..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/disc.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/disk_swap.png b/shell/android-studio/reicast/src/main/res/drawable/disk_swap.png deleted file mode 100644 index 97450c0b3..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/disk_swap.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/disk_unknown.png b/shell/android-studio/reicast/src/main/res/drawable/disk_unknown.png deleted file mode 100644 index 56afe630c..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/disk_unknown.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/enable_sound.png b/shell/android-studio/reicast/src/main/res/drawable/enable_sound.png deleted file mode 100644 index ca73d733c..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/enable_sound.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/frames_down.png b/shell/android-studio/reicast/src/main/res/drawable/frames_down.png deleted file mode 100644 index faf15cb75..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/frames_down.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/frames_limit_off.png b/shell/android-studio/reicast/src/main/res/drawable/frames_limit_off.png deleted file mode 100644 index 3f8684ddf..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/frames_limit_off.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/frames_limit_on.png b/shell/android-studio/reicast/src/main/res/drawable/frames_limit_on.png deleted file mode 100644 index 2abe5b723..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/frames_limit_on.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/frames_up.png b/shell/android-studio/reicast/src/main/res/drawable/frames_up.png deleted file mode 100644 index a0980bb1a..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/frames_up.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/game_selector.xml b/shell/android-studio/reicast/src/main/res/drawable/game_selector.xml deleted file mode 100644 index 87c9c9bc8..000000000 --- a/shell/android-studio/reicast/src/main/res/drawable/game_selector.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/shell/android-studio/reicast/src/main/res/drawable/gdi.png b/shell/android-studio/reicast/src/main/res/drawable/gdi.png deleted file mode 100644 index 668acbfcb..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/gdi.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/gradient_bg.xml b/shell/android-studio/reicast/src/main/res/drawable/gradient_bg.xml deleted file mode 100644 index 1a64c34a5..000000000 --- a/shell/android-studio/reicast/src/main/res/drawable/gradient_bg.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/shell/android-studio/reicast/src/main/res/drawable/gradient_bg_hover.xml b/shell/android-studio/reicast/src/main/res/drawable/gradient_bg_hover.xml deleted file mode 100644 index aab77af2e..000000000 --- a/shell/android-studio/reicast/src/main/res/drawable/gradient_bg_hover.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/shell/android-studio/reicast/src/main/res/drawable/ic_cloud_queue.xml b/shell/android-studio/reicast/src/main/res/drawable/ic_cloud_queue.xml deleted file mode 100644 index 0ca5119d0..000000000 --- a/shell/android-studio/reicast/src/main/res/drawable/ic_cloud_queue.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/shell/android-studio/reicast/src/main/res/drawable/ic_folder_black_24dp.xml b/shell/android-studio/reicast/src/main/res/drawable/ic_folder_black_24dp.xml deleted file mode 100644 index 3e6904682..000000000 --- a/shell/android-studio/reicast/src/main/res/drawable/ic_folder_black_24dp.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - diff --git a/shell/android-studio/reicast/src/main/res/drawable/ic_info_outline.xml b/shell/android-studio/reicast/src/main/res/drawable/ic_info_outline.xml deleted file mode 100644 index cf53e145c..000000000 --- a/shell/android-studio/reicast/src/main/res/drawable/ic_info_outline.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/shell/android-studio/reicast/src/main/res/drawable/ic_input.xml b/shell/android-studio/reicast/src/main/res/drawable/ic_input.xml deleted file mode 100644 index fea69dfb7..000000000 --- a/shell/android-studio/reicast/src/main/res/drawable/ic_input.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/shell/android-studio/reicast/src/main/res/drawable/ic_rate_review.xml b/shell/android-studio/reicast/src/main/res/drawable/ic_rate_review.xml deleted file mode 100644 index df82c525d..000000000 --- a/shell/android-studio/reicast/src/main/res/drawable/ic_rate_review.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/shell/android-studio/reicast/src/main/res/drawable/ic_send.xml b/shell/android-studio/reicast/src/main/res/drawable/ic_send.xml deleted file mode 100644 index e145ca83c..000000000 --- a/shell/android-studio/reicast/src/main/res/drawable/ic_send.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/shell/android-studio/reicast/src/main/res/drawable/ic_settings.xml b/shell/android-studio/reicast/src/main/res/drawable/ic_settings.xml deleted file mode 100644 index ace746c40..000000000 --- a/shell/android-studio/reicast/src/main/res/drawable/ic_settings.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/shell/android-studio/reicast/src/main/res/drawable/ic_subdirectory_arrow_right.xml b/shell/android-studio/reicast/src/main/res/drawable/ic_subdirectory_arrow_right.xml deleted file mode 100644 index 65acc6989..000000000 --- a/shell/android-studio/reicast/src/main/res/drawable/ic_subdirectory_arrow_right.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/shell/android-studio/reicast/src/main/res/drawable/image_bg.xml b/shell/android-studio/reicast/src/main/res/drawable/image_bg.xml deleted file mode 100644 index a1004de21..000000000 --- a/shell/android-studio/reicast/src/main/res/drawable/image_bg.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/shell/android-studio/reicast/src/main/res/drawable/input.png b/shell/android-studio/reicast/src/main/res/drawable/input.png deleted file mode 100644 index 019545823..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/input.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/list_item_border.xml b/shell/android-studio/reicast/src/main/res/drawable/list_item_border.xml deleted file mode 100644 index 573b55f80..000000000 --- a/shell/android-studio/reicast/src/main/res/drawable/list_item_border.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/shell/android-studio/reicast/src/main/res/drawable/list_selector.xml b/shell/android-studio/reicast/src/main/res/drawable/list_selector.xml deleted file mode 100644 index fb1560c2e..000000000 --- a/shell/android-studio/reicast/src/main/res/drawable/list_selector.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/shell/android-studio/reicast/src/main/res/drawable/menuback.png b/shell/android-studio/reicast/src/main/res/drawable/menuback.png deleted file mode 100644 index 44bbf1fc3..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/menuback.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/menutile.png b/shell/android-studio/reicast/src/main/res/drawable/menutile.png deleted file mode 100644 index 38d7cb48c..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/menutile.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/mic_icon.png b/shell/android-studio/reicast/src/main/res/drawable/mic_icon.png deleted file mode 100644 index 532a3c900..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/mic_icon.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/mute_sound.png b/shell/android-studio/reicast/src/main/res/drawable/mute_sound.png deleted file mode 100644 index 18e7f21ee..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/mute_sound.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/normal_view.png b/shell/android-studio/reicast/src/main/res/drawable/normal_view.png deleted file mode 100644 index f71f8016b..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/normal_view.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/open_folder.png b/shell/android-studio/reicast/src/main/res/drawable/open_folder.png deleted file mode 100644 index 2df8929ed..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/open_folder.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/print_stats.png b/shell/android-studio/reicast/src/main/res/drawable/print_stats.png deleted file mode 100644 index 9875cfd0d..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/print_stats.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/profiler.png b/shell/android-studio/reicast/src/main/res/drawable/profiler.png deleted file mode 100644 index 3babd0a4a..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/profiler.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/question.png b/shell/android-studio/reicast/src/main/res/drawable/question.png deleted file mode 100644 index 3ca2a313d..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/question.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/reset.png b/shell/android-studio/reicast/src/main/res/drawable/reset.png deleted file mode 100644 index 00d40b11a..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/reset.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/rounded.xml b/shell/android-studio/reicast/src/main/res/drawable/rounded.xml deleted file mode 100644 index e3f741d9d..000000000 --- a/shell/android-studio/reicast/src/main/res/drawable/rounded.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/shell/android-studio/reicast/src/main/res/drawable/shadow.xml b/shell/android-studio/reicast/src/main/res/drawable/shadow.xml deleted file mode 100644 index 67bb1c57f..000000000 --- a/shell/android-studio/reicast/src/main/res/drawable/shadow.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/shell/android-studio/reicast/src/main/res/drawable/star.png b/shell/android-studio/reicast/src/main/res/drawable/star.png deleted file mode 100644 index 3a653fcd9..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/star.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/toggle_a_b.png b/shell/android-studio/reicast/src/main/res/drawable/toggle_a_b.png deleted file mode 100644 index 95fcf23e6..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/toggle_a_b.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/toggle_r_l.png b/shell/android-studio/reicast/src/main/res/drawable/toggle_r_l.png deleted file mode 100644 index cfe053647..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/toggle_r_l.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/up.png b/shell/android-studio/reicast/src/main/res/drawable/up.png deleted file mode 100644 index de4d469be..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/up.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/vmu_swap.png b/shell/android-studio/reicast/src/main/res/drawable/vmu_swap.png deleted file mode 100644 index 44fcb5e15..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/vmu_swap.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/drawable/widescreen.png b/shell/android-studio/reicast/src/main/res/drawable/widescreen.png deleted file mode 100644 index d48120ac0..000000000 Binary files a/shell/android-studio/reicast/src/main/res/drawable/widescreen.png and /dev/null differ diff --git a/shell/android-studio/reicast/src/main/res/layout-v14/configure_fragment.xml b/shell/android-studio/reicast/src/main/res/layout-v14/configure_fragment.xml deleted file mode 100644 index 1c0ba5f90..000000000 --- a/shell/android-studio/reicast/src/main/res/layout-v14/configure_fragment.xml +++ /dev/null @@ -1,275 +0,0 @@ - - - - - - - - - - - - - - -