Compare commits
301 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7534c5bb3c | |||
| 7d5eec2d41 | |||
| 69b8461a87 | |||
| 32d2a12331 | |||
| ce8de27fb5 | |||
| 9c64d647ad | |||
| 726861dfaa | |||
| 51dea638c9 | |||
| 298a717225 | |||
| 27b2bc8387 | |||
| 6f6310c776 | |||
| e45fcdfb3e | |||
| ea02f68abe | |||
| 65d7d149f1 | |||
| c5f23f8b71 | |||
| ba327ce1c6 | |||
| 583687cfe1 | |||
| 19eda69468 | |||
| 4b3f979a74 | |||
| d171d62609 | |||
| 5ba195d32e | |||
| 950aa60fca | |||
| 3547e9bf53 | |||
| 5dbf6cfb2e | |||
| fb12f6432b | |||
| 5b61f811da | |||
| fe0fa0b527 | |||
| ba371f1349 | |||
| 38ec62c238 | |||
| 942c2ee1b0 | |||
| 2a7d8aa4d2 | |||
| b166eac200 | |||
| c06316f4aa | |||
| 09d96d9e4d | |||
| 66703970b6 | |||
| 0d3314a39b | |||
| 748492cc68 | |||
| e18ad31c53 | |||
| 0dd297f0f8 | |||
| 0da4becd46 | |||
| d284911d4a | |||
| e772eb3906 | |||
| 577ee4c3e0 | |||
| c534df8303 | |||
| a36c8f7de8 | |||
| dfb5f1b687 | |||
| d8eea015fd | |||
| f17b7eb2c9 | |||
| 33146626a9 | |||
| 78e12f774d | |||
| 689d8debb1 | |||
| 8be1ed8af5 | |||
| 0fabdb3f3a | |||
| 7a64e7e479 | |||
| 8c1589d7cd | |||
| 5a74b137f2 | |||
| 52ba182610 | |||
| b07eb79609 | |||
| 4e3915f527 | |||
| 9f620f276d | |||
| e3787b5ccf | |||
| 3d81a052c6 | |||
| 6353be1599 | |||
| bc6ee4877b | |||
| 6b38cc3740 | |||
| 9285fb9fdf | |||
| f5c609413a | |||
| a304f11b1d | |||
| e10ac1086f | |||
| 4533ed61ac | |||
| 235e41653d | |||
| 433479b1c3 | |||
| 2b4736f224 | |||
| 41f1dcf9d2 | |||
| 45b97cf39b | |||
| 95b1225219 | |||
| 70f6c40b26 | |||
| 43bc54fca9 | |||
| a5ec31ffef | |||
| 81cbf82ddc | |||
| 6cea506c6f | |||
| d7d49f261d | |||
| 4692a5c0b7 | |||
| d32ea32f1e | |||
| f5f053cc4e | |||
| bb3c6dc9fc | |||
| 496d4de1de | |||
| 71f8437ecb | |||
| e7767105d6 | |||
| 9cfda8514e | |||
| e0c6700a4d | |||
| 12bf158398 | |||
| ea01c86821 | |||
| 96459432a6 | |||
| 089194c663 | |||
| 7a70227535 | |||
| 7adfb3123b | |||
| 1d28075601 | |||
| af7aa688bb | |||
| beb524f523 | |||
| 18c9c151a8 | |||
| 83820baf79 | |||
| abf50350e7 | |||
| 6588d0d17a | |||
| 648736df48 | |||
| 6278a443df | |||
| 615295f1f9 | |||
| 593135d02b | |||
| 78ea41a2ca | |||
| 105600878e | |||
| 415202e15f | |||
| 618a6f6c9f | |||
| af70f35bf2 | |||
| 43bb0a3a1c | |||
| 8d9f83b1cc | |||
| 7d713afbdd | |||
| 5044ef8ab1 | |||
| 5b4fe5667a | |||
| a4d3efa087 | |||
| 9cb8adce55 | |||
| c061981de0 | |||
| b2f76fc436 | |||
| dc58fbe900 | |||
| 0e80142da1 | |||
| ee77bc7c9b | |||
| b978248c03 | |||
| 85d2b5be5a | |||
| cee35297d6 | |||
| edde08849f | |||
| f649053e00 | |||
| 6c6b4bcd3a | |||
| 1fef816572 | |||
| a6203cf6e9 | |||
| 200bb0f6e3 | |||
| 1dd2fca3db | |||
| 5444f3b807 | |||
| 897d81a188 | |||
| eae18c446a | |||
| fb7402e394 | |||
| 63dc9cbe7c | |||
| ac63f4809f | |||
| 0bdf3ada25 | |||
| aa684b903b | |||
| db0ae36702 | |||
| 3d5e812c6f | |||
| 07f6810835 | |||
| b3e2a43cf8 | |||
| 96f4abbc07 | |||
| d96746c933 | |||
| 4391ebd94a | |||
| 081029e3d1 | |||
| 541724b63c | |||
| 304afbb50d | |||
| 1ba88f48bb | |||
| 8f8c9c35f1 | |||
| 1c07d032ef | |||
| c500a17b1a | |||
| 6d0347b48a | |||
| 83af0ff4af | |||
| 32c13eee1d | |||
| 42597a76f0 | |||
| 7c59a520aa | |||
| 3c124d8519 | |||
| 76282a11da | |||
| 5249bf6a56 | |||
| 12e0dac777 | |||
| b4e3bede3b | |||
| 679731f004 | |||
| 3bbd29916b | |||
| 6516330289 | |||
| 0628ea920b | |||
| 40a4133565 | |||
| b4425fe1be | |||
| 809cbcbc21 | |||
| 2ae2b994a8 | |||
| 8ce36cdc03 | |||
| 3707a86c79 | |||
| eea3ec7f95 | |||
| 4a2c6761fc | |||
| 7a4e179f4c | |||
| 11eaadb739 | |||
| 41b77b54ea | |||
| 22689bd9ee | |||
| 831535c86f | |||
| ce43955784 | |||
| e85f4f4309 | |||
| 00b3f33530 | |||
| 1b0116a0cb | |||
| 486281427c | |||
| 8ccdc415bc | |||
| 008049265d | |||
| fef407b839 | |||
| 084570bc4b | |||
| 3d91e0c3e3 | |||
| b5a942b4fe | |||
| 5c5cc8453d | |||
| 30495795cd | |||
| 073cdd2815 | |||
| fe21c73f75 | |||
| a6cba2ce2a | |||
| 9afd971b43 | |||
| b0f9136fe8 | |||
| b513439565 | |||
| 42494e5019 | |||
| 8653234cb3 | |||
| 6a14761770 | |||
| 399a5c93b2 | |||
| be2db3c95a | |||
| 7f7ac4c5fb | |||
| 7ca98bb496 | |||
| e9cf27411b | |||
| d8f1a5a118 | |||
| 07ea2d8ec4 | |||
| 2c63963134 | |||
| c9d9e71631 | |||
| 69726baa48 | |||
| c02a44d020 | |||
| 1c0c3eb6aa | |||
| 7f9f30181e | |||
| 2bc788beea | |||
| 4a0d20c1a1 | |||
| 2ea9efd622 | |||
| 723f4ab16b | |||
| a242ae78eb | |||
| c037e1465f | |||
| 3e8578f2fd | |||
| 0050646ad7 | |||
| f2ef7e345f | |||
| 3c0698d14b | |||
| ec677c8d26 | |||
| f115c82512 | |||
| 9f4f8ab15b | |||
| 532d8bcaee | |||
| 2978b8443c | |||
| 3b1fbcd9d3 | |||
| de4d4c27c7 | |||
| 91bf6ad996 | |||
| 312885bec1 | |||
| 8641e1b3ba | |||
| ca203d96b0 | |||
| 6774c8731a | |||
| b603f3b75c | |||
| b546826b67 | |||
| a39263aeb5 | |||
| ecaa5b9dee | |||
| 7fcc468353 | |||
| 52952942e9 | |||
| 81dc449637 | |||
| ce25524ed9 | |||
| 30a3522def | |||
| f62de70aa5 | |||
| ec190adc51 | |||
| 7095824874 | |||
| b243f48fff | |||
| 3bc052e948 | |||
| 482e237d08 | |||
| 86f38ab024 | |||
| c837d736bb | |||
| ab94fd484f | |||
| 1508b11044 | |||
| b4e18d1b63 | |||
| f49272288d | |||
| f1dd82c65e | |||
| 7aa7eaffb9 | |||
| 6577fa9810 | |||
| 6aabc4eca8 | |||
| bf5b3d3c99 | |||
| 420eb39c16 | |||
| 6dd764c04c | |||
| 95d5141a2b | |||
| 51c6c3904d | |||
| 32a39afc75 | |||
| fd967cc2a3 | |||
| 8c0bb09667 | |||
| 8c753d6cfe | |||
| 276e681f29 | |||
| f23b17e1b8 | |||
| f1e10fa4b1 | |||
| be67cd03cb | |||
| d7dfe12fb4 | |||
| be437afd2f | |||
| f72688773a | |||
| fb328d8d92 | |||
| 36e6d2fcc7 | |||
| 7b3293341b | |||
| 0a7b72a51b | |||
| 6265973cc6 | |||
| f1edc47a80 | |||
| 8f60d5c80f | |||
| c42489fa81 | |||
| 66b60c988a | |||
| 5d20e98aba | |||
| 617ee3f96c | |||
| 083f7c2e4f | |||
| a8ea1795bf | |||
| 8a6cb4b5a8 | |||
| 53edf065a5 | |||
| bf65be483d | |||
| 1e90047368 | |||
| abd1160f87 | |||
| 55d7dd9d89 |
@@ -4,7 +4,7 @@ on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
Linux:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
@@ -14,20 +14,33 @@ jobs:
|
||||
key: Linux
|
||||
- name: Install Packages
|
||||
run: |
|
||||
sudo apt-get purge grub\* --yes --allow-remove-essential
|
||||
sudo add-apt-repository ppa:okirby/qt6-backports --yes
|
||||
sudo apt-get -qq update
|
||||
sudo apt-get upgrade
|
||||
sudo apt-get -y install cmake libhidapi-dev libsamplerate0-dev libspeex-dev libminizip-dev libsdl2-dev libfreetype6-dev libgl1-mesa-dev libglu1-mesa-dev pkg-config zlib1g-dev binutils-dev libspeexdsp-dev qt6-base-dev libqt6svg6-dev build-essential nasm git zip
|
||||
sudo apt-get -y install cmake ninja-build libhidapi-dev libsamplerate0-dev libspeex-dev libminizip-dev libsdl2-dev libfreetype6-dev libgl1-mesa-dev libglu1-mesa-dev pkg-config zlib1g-dev binutils-dev libspeexdsp-dev qt6-base-dev libqt6svg6-dev build-essential nasm git zip appstream
|
||||
- name: Prepare Environment
|
||||
run: |
|
||||
echo "GIT_REVISION=$(git describe --tags --always)" >> $GITHUB_ENV
|
||||
- name: Build RMG (Portable)
|
||||
run: ./Source/Script/Build.sh Release
|
||||
- name: Build RMG (AppImage)
|
||||
run: |
|
||||
mkdir Build Build/AppImage Bin/ -p
|
||||
export src_dir="$(pwd)"
|
||||
export build_dir="$(pwd)/build"
|
||||
export bin_dir="$(pwd)/Bin/AppImage"
|
||||
cmake -S "$src_dir" -B "$build_dir" -DCMAKE_BUILD_TYPE="Release" -DPORTABLE_INSTALL="OFF" -DUPDATER=ON -DAPPIMAGE_UPDATER=ON -DCMAKE_INSTALL_PREFIX="/usr" -G "Ninja"
|
||||
cmake --build "$build_dir"
|
||||
cmake --install "$build_dir" --prefix="$bin_dir/usr"
|
||||
shell: bash
|
||||
- name: Upload RMG (Portable)
|
||||
- name: Create AppImage
|
||||
run: |
|
||||
./Package/AppImage/Create.sh
|
||||
shell: bash
|
||||
- name: Upload RMG (AppImage)
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: RMG-Portable-Linux64-${{ env.GIT_REVISION }}
|
||||
path: Bin/Release/*
|
||||
path: Bin/*.AppImage
|
||||
|
||||
Windows:
|
||||
runs-on: windows-2019
|
||||
@@ -45,19 +58,17 @@ jobs:
|
||||
install: >-
|
||||
make
|
||||
mingw-w64-x86_64-cmake
|
||||
mingw-w64-x86_64-ninja
|
||||
mingw-w64-x86_64-gcc
|
||||
mingw-w64-x86_64-hidapi
|
||||
mingw-w64-x86_64-freetype
|
||||
mingw-w64-x86_64-libpng
|
||||
mingw-w64-x86_64-SDL2
|
||||
mingw-w64-x86_64-jasper
|
||||
mingw-w64-x86_64-qt6
|
||||
mingw-w64-x86_64-SDL2
|
||||
mingw-w64-x86_64-hidapi
|
||||
mingw-w64-x86_64-speexdsp
|
||||
mingw-w64-x86_64-libsamplerate
|
||||
mingw-w64-x86_64-python3-pip
|
||||
mingw-w64-x86_64-python3-pyopenssl
|
||||
mingw-w64-x86_64-nasm
|
||||
mingw-w64-x86_64-minizip
|
||||
git
|
||||
@@ -69,9 +80,6 @@ jobs:
|
||||
- name: Build RMG (Portable)
|
||||
run: ./Source/Script/Build.sh Release
|
||||
shell: msys2 {0}
|
||||
- name: Download Styles
|
||||
run: ./Source/Script/DownloadStyles.sh
|
||||
shell: msys2 {0}
|
||||
- name: Create Installer
|
||||
run: .\Build\Release\Source\Installer\CreateInstaller.bat
|
||||
- name: Upload RMG (Portable)
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
# CMake build directory
|
||||
Build/
|
||||
Bin/
|
||||
# VSCode Configuration
|
||||
.vscode/
|
||||
# Clangd cache
|
||||
.cache/
|
||||
.cache/
|
||||
# Archlinux Package
|
||||
Package/ArchLinux/pkg/
|
||||
Package/ArchLinux/rmg/
|
||||
Package/ArchLinux/src/
|
||||
Package/ArchLinux/*.pkg.*
|
||||
# Clangd compile_commands.json
|
||||
compile_commands.json
|
||||
# AppImage
|
||||
squashfs-root
|
||||
*.AppImage
|
||||
@@ -6,9 +6,12 @@ cmake_minimum_required(VERSION 3.15)
|
||||
option(PORTABLE_INSTALL "Portable Installation" ON)
|
||||
option(NO_GIT_CLONE "Disables git clone usage for 3rdParty dependencies" OFF)
|
||||
option(UPDATER "Enables updater" ${PORTABLE_INSTALL})
|
||||
option(APPIMAGE_UPDATER "Enables AppImage updater" OFF)
|
||||
option(DISCORD_RPC "Enables Discord Rich Presence" ON)
|
||||
option(USE_QT5 "Enables Qt5 instead of Qt6 (will be removed in the future)" OFF)
|
||||
option(VRU "Enables VRU support in RMG-Input" ON)
|
||||
option(USE_CCACHE "Enables usage of ccache when ccache has been found" ON)
|
||||
option(FORCE_XCB "Forces Qt to use the xcb platform on linux" ${LINUX})
|
||||
option(NO_RUST "Disables the building of rust subprojects" OFF)
|
||||
|
||||
project(RMG)
|
||||
|
||||
@@ -35,21 +38,40 @@ if (NOT PORTABLE_INSTALL AND (WIN32 OR MSYS))
|
||||
set(PORTABLE_INSTALL ON)
|
||||
endif()
|
||||
|
||||
if (NOT PORTABLE_INSTALL AND UPDATER)
|
||||
if (NOT PORTABLE_INSTALL AND UPDATER AND NOT APPIMAGE_UPDATER)
|
||||
message(WARNING "Not-Portable builds don't support the updater, disabling UPDATER")
|
||||
set(UPDATER OFF)
|
||||
endif()
|
||||
|
||||
if (APPIMAGE_UPDATER AND (WIN32 OR MSYS))
|
||||
message(WARNING "AppImage updater not supported on windows, disabling APPIMAGE_UPDATER!")
|
||||
set(APPIMAGE_UPDATER OFF)
|
||||
endif()
|
||||
|
||||
if (UPDATER AND NOT (WIN32 OR MSYS) AND NOT APPIMAGE_UPDATER)
|
||||
message(WARNING "Only AppImage updater is supported on linux, disabling UPDATER!")
|
||||
set(UPDATER OFF)
|
||||
endif()
|
||||
|
||||
if (FORCE_XCB AND (WIN32 OR MSYS))
|
||||
message(WARNING "XCB Qt platform not supported on windows, disabling FORCE_XCB!")
|
||||
set(FORCE_XCB OFF)
|
||||
endif()
|
||||
|
||||
if (PORTABLE_INSTALL)
|
||||
set(CMAKE_INSTALL_PREFIX "")
|
||||
set(INSTALL_PATH "Bin/${CMAKE_BUILD_TYPE}")
|
||||
set(RMG_INSTALL_PATH "${INSTALL_PATH}")
|
||||
set(SYSTEM_LIB_INSTALL_PATH "${INSTALL_PATH}")
|
||||
set(LIB_INSTALL_PATH "${INSTALL_PATH}")
|
||||
set(CORE_INSTALL_PATH "${INSTALL_PATH}/Core")
|
||||
set(PLUGIN_INSTALL_PATH "${INSTALL_PATH}/Plugin")
|
||||
set(DATA_INSTALL_PATH "${INSTALL_PATH}/Data")
|
||||
set(INSTALL_DESKTOP_FILE OFF)
|
||||
else()
|
||||
set(RMG_INSTALL_PATH "bin")
|
||||
set(SYSTEM_LIB_INSTALL_PATH "lib")
|
||||
set(LIB_INSTALL_PATH "lib/RMG")
|
||||
set(CORE_INSTALL_PATH "lib/RMG/Core")
|
||||
set(PLUGIN_INSTALL_PATH "lib/RMG/Plugin")
|
||||
set(DATA_INSTALL_PATH "share/RMG")
|
||||
@@ -59,20 +81,19 @@ else()
|
||||
set(ICON_INSTALL_PATH "share/icons/hicolor/scalable/apps/")
|
||||
endif()
|
||||
|
||||
if (USE_QT5)
|
||||
set(QT_VERSION "Qt5")
|
||||
set(USE_QT6 OFF)
|
||||
else(USE_QT5)
|
||||
set(QT_VERSION "Qt6")
|
||||
set(USE_QT6 ON)
|
||||
endif(USE_QT5)
|
||||
|
||||
add_subdirectory(Source/3rdParty)
|
||||
add_subdirectory(Source/3rdParty/7-Zip)
|
||||
if (VRU)
|
||||
add_subdirectory(Source/3rdParty/vosk-api)
|
||||
endif(VRU)
|
||||
|
||||
add_subdirectory(Source/RMG-Core)
|
||||
add_subdirectory(Source/RMG)
|
||||
add_subdirectory(Source/RMG-Audio)
|
||||
add_subdirectory(Source/RMG-Input)
|
||||
install(TARGETS RMG-Core
|
||||
DESTINATION ${SYSTEM_LIB_INSTALL_PATH}
|
||||
)
|
||||
install(TARGETS RMG
|
||||
DESTINATION ${RMG_INSTALL_PATH}
|
||||
)
|
||||
@@ -85,11 +106,11 @@ install(TARGETS RMG-Input
|
||||
|
||||
if (WIN32)
|
||||
add_subdirectory(Source/Installer)
|
||||
|
||||
|
||||
add_custom_target(bundle_dependencies
|
||||
COMMAND "${CMAKE_SOURCE_DIR}/Source/Script/BundleDependencies.sh" "${CMAKE_SOURCE_DIR}/Bin/${CMAKE_BUILD_TYPE}/RMG.exe" "${CMAKE_SOURCE_DIR}/Bin/${CMAKE_BUILD_TYPE}/" "/mingw64/bin"
|
||||
COMMAND bash "${CMAKE_SOURCE_DIR}/Source/Script/BundleDependencies.sh" "${CMAKE_SOURCE_DIR}/Bin/${CMAKE_BUILD_TYPE}/RMG.exe" "${CMAKE_SOURCE_DIR}/Bin/${CMAKE_BUILD_TYPE}" "/mingw64/bin"
|
||||
)
|
||||
endif()
|
||||
endif(WIN32)
|
||||
|
||||
install(FILES ${MUPEN64PLUSCORE_LIB}
|
||||
DESTINATION ${CORE_INSTALL_PATH}
|
||||
@@ -97,6 +118,17 @@ install(FILES ${MUPEN64PLUSCORE_LIB}
|
||||
install(FILES ${MUPEN64PLUSCORE_INI} Data/font.ttf
|
||||
DESTINATION ${DATA_INSTALL_PATH}
|
||||
)
|
||||
if (VRU)
|
||||
install(FILES ${VOSK_API_LIBRARY}
|
||||
DESTINATION ${LIB_INSTALL_PATH}
|
||||
)
|
||||
install(FILES ${VOSK_API_MODEL}
|
||||
DESTINATION ${DATA_INSTALL_PATH}
|
||||
)
|
||||
endif(VRU)
|
||||
install(FILES ${SDL_GAMECONTROLLERDB}
|
||||
DESTINATION ${DATA_INSTALL_PATH}
|
||||
)
|
||||
file(GLOB GENERATED_CHEAT_FILES ${CMAKE_SOURCE_DIR}/Data/Cheats/Generated/*.cht)
|
||||
file(GLOB CUSTOM_CHEAT_FILES ${CMAKE_SOURCE_DIR}/Data/Cheats/Custom/*.cht)
|
||||
install(FILES ${GENERATED_CHEAT_FILES} ${CUSTOM_CHEAT_FILES}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
[1FBAF161-2C1C54F1-C:41]
|
||||
Name=1080 Snowboarding (JU) (M2)
|
||||
|
||||
$Enable All Levels & Boarders
|
||||
80255089 00FF
|
||||
8025508B 0005
|
||||
81255096 FCFF
|
||||
80255098 0007
|
||||
|
||||
$Have All\Contest First Place Done
|
||||
80244098 0007
|
||||
|
||||
$Have All\Match Levels Completed
|
||||
8025508A 0005
|
||||
|
||||
$Have All\Trick Attacks Done
|
||||
81255096 FCFF
|
||||
|
||||
$Infinite Damage All Players & Levels
|
||||
803F4C3C 0024
|
||||
807F4C3C 0024
|
||||
|
||||
$Infinite Lives & Continues
|
||||
8026B1CB 0003
|
||||
|
||||
$Always First Place (Player 1)
|
||||
81267B88 2400
|
||||
Note=The game will still show you in 2nd when you are behind. But when you cross the finish line, you will win!
|
||||
|
||||
$Open Level Character Select
|
||||
Note=With these codes, you have to press the buttons listed below when you are viewing a certain boarders stats. Highlight Akari Hayami's stats, and then hold down the C-Left button and then press the A button to be the Crystal Boarder. Highlight Kensuke Kimachi's stats, and then hold down the C-Up button and then press the A button to be the Metal Boarder. Hightlight Rob Haywood's stats, and then hold down the C-Right button and then press the A button to be the Panda Boarder
|
||||
8125508A 00??
|
||||
01 Easy Level
|
||||
02 Easy & Normal Level
|
||||
03 Easy Normal & Hard Levels
|
||||
04 Easy Normal Hard Levels & Crystal Boarder
|
||||
05 Easy Normal Hard Levels & Crystal & Metal Boarders
|
||||
|
||||
$Play As
|
||||
800200E7 00??
|
||||
00 Dion Blaster
|
||||
01 Kensuke Kimachi
|
||||
02 Akari Hayami
|
||||
03 Ricky Winterborn
|
||||
04 Rob Haywood
|
||||
05 Silver Boarder
|
||||
06 Gold Boarder
|
||||
07 Panda Boarder
|
||||
|
||||
$Freeze Timer In\Match Race
|
||||
D1267B20 05E1
|
||||
81267B20 1000
|
||||
|
||||
$Freeze Timer In\Time Attack
|
||||
D12660CC 05E1
|
||||
812660CC 1000
|
||||
|
||||
$Freeze Timer In\Trick Attack
|
||||
D1266138 05E1
|
||||
81266138 1000
|
||||
|
||||
$Freeze Timer In\Contest
|
||||
D126628C 05E1
|
||||
8126628C 1000
|
||||
@@ -0,0 +1,55 @@
|
||||
[58FD3F25-D92EAA8D-C:50]
|
||||
Name=1080 Snowboarding (E) (M4)
|
||||
|
||||
$Enable All Levels & Boarders
|
||||
80250AB9 00FF
|
||||
80250ABB 0005
|
||||
81250AC6 FCFF
|
||||
80250AC8 0007
|
||||
|
||||
$Infinite Damage All Players and Levels
|
||||
803F4C00 0024
|
||||
807F4C00 0024
|
||||
|
||||
$Infinite Lives & Continues
|
||||
802648A4 0003
|
||||
|
||||
$Open Level Character Select
|
||||
Note=With these codes, you have to press the buttons listed below when you are viewing a certain boarders stats. Highlight Akari Hayami's stats, and then hold down the C-Left button and then press the A button to be the Crystal Boarder. Highlight Kensuke Kimachi's stats, and then hold down the C-Up button and then press the A button to be the Metal Boarder. Hightlight Rob Haywood's stats, and then hold down the C-Right button and then press the A button to be the Panda Boarder
|
||||
8125508A 00??
|
||||
01 Easy Level
|
||||
02 Easy & Normal Level
|
||||
03 Easy Normal & Hard Levels
|
||||
04 Easy Normal Hard Levels & Crystal Boarder
|
||||
05 Easy Normal Hard Levels & Crystal & Metal Boarders
|
||||
|
||||
$Play As
|
||||
8001A0E7 00??
|
||||
00 Dion Blaster
|
||||
01 Kensuke Kimachi
|
||||
02 Akari Hayami
|
||||
03 Ricky Winterborn
|
||||
04 Rob Haywood
|
||||
05 Silver Boarder
|
||||
06 Gold Boarder
|
||||
07 Panda Boarder
|
||||
|
||||
$Always First Place (Player 1)
|
||||
812679BC 2400
|
||||
Note=The game will still show you in 2nd when you are behind. But when you cross the finish line, you will win!
|
||||
|
||||
$Freeze Timer In\Match Race
|
||||
D1267954 05E1
|
||||
81267954 1000
|
||||
|
||||
$Freeze Timer In\Time Attack
|
||||
D1265D4C 05E1
|
||||
81265D4C 1000
|
||||
|
||||
$Freeze Timer In\Trick Attack
|
||||
D1265DB8 05E1
|
||||
81265DB8 1000
|
||||
|
||||
$Freeze Timer In\Contest
|
||||
D1265F48 05E1
|
||||
81265F48 1000
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
cur_dir="$(readlink -f "$(dirname "$0")")"
|
||||
|
||||
exec "$cur_dir/usr/bin/RMG" \
|
||||
--lib-path="$cur_dir/usr/lib/RMG" \
|
||||
--core-path="$cur_dir/usr/lib/RMG/Core" \
|
||||
--plugin-path="$cur_dir/usr/lib/RMG/Plugin" \
|
||||
--shared-data-path="$cur_dir/usr/share/RMG" \
|
||||
"$@"
|
||||
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env bash
|
||||
set -ex
|
||||
|
||||
script_dir="$(dirname "$0")"
|
||||
toplvl_dir="$(realpath "$script_dir/../../")"
|
||||
bin_dir="$toplvl_dir/Bin/AppImage" # RMG should be installed here
|
||||
|
||||
export QMAKE="$(which qmake6)"
|
||||
export EXTRA_QT_PLUGINS="imageformats;iconengines;"
|
||||
export VERSION="$(git describe --tags --always)"
|
||||
export OUTPUT="$bin_dir/../RMG-Portable-Linux64-$VERSION.AppImage"
|
||||
export LD_LIBRARY_PATH="$toplvl_dir/build/Source/RMG-Core" # hack
|
||||
|
||||
if [ ! -f "$script_dir/linuxdeploy-x86_64.AppImage" ]
|
||||
then
|
||||
curl -L https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage \
|
||||
-o "$script_dir/linuxdeploy-x86_64.AppImage"
|
||||
chmod +x "$script_dir/linuxdeploy-x86_64.AppImage"
|
||||
fi
|
||||
|
||||
if [ ! -f "$script_dir/linuxdeploy-plugin-qt-x86_64.AppImage" ]
|
||||
then
|
||||
curl -L https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage \
|
||||
-o "$script_dir/linuxdeploy-plugin-qt-x86_64.AppImage"
|
||||
chmod +x "$script_dir/linuxdeploy-plugin-qt-x86_64.AppImage"
|
||||
fi
|
||||
|
||||
"$script_dir/linuxdeploy-plugin-qt-x86_64.AppImage" --appimage-extract
|
||||
"$script_dir/linuxdeploy-x86_64.AppImage" --appimage-extract
|
||||
|
||||
# delete appimages
|
||||
rm "$script_dir/linuxdeploy-x86_64.AppImage" \
|
||||
"$script_dir/linuxdeploy-plugin-qt-x86_64.AppImage"
|
||||
|
||||
"$(pwd)/squashfs-root/AppRun" \
|
||||
--plugin=qt \
|
||||
--appdir="$bin_dir" \
|
||||
--custom-apprun="$script_dir/AppRun" \
|
||||
--output=appimage \
|
||||
--desktop-file="$bin_dir/usr/share/applications/com.github.Rosalie241.RMG.desktop"
|
||||
@@ -1,51 +1,36 @@
|
||||
# Maintainer: Rosalie Wanders <rosalie@mailbox.org>
|
||||
_pkgname=rmg
|
||||
pkgname=${_pkgname}-git
|
||||
pkgver=git
|
||||
pkgname=rmg
|
||||
pkgver=0.4.2
|
||||
pkgrel=1
|
||||
pkgdesc="Rosalie's Mupen GUI"
|
||||
arch=('x86_64')
|
||||
url="https://github.com/Rosalie241/${_pkgname}"
|
||||
license=('GPLv3')
|
||||
url="https://github.com/Rosalie241/$pkgname"
|
||||
license=('GPL3')
|
||||
|
||||
provides=("$_pkgname")
|
||||
conflicts=("$_pkgname")
|
||||
depends=("hidapi" "libsamplerate" "speexdsp" "minizip" "sdl2" "zlib" "freetype2" "qt6-base" "qt6-svg" "xdg-user-dirs")
|
||||
makedepends=("git" "nasm" "cmake" "ninja")
|
||||
|
||||
depends=("hidapi" "libsamplerate" "minizip" "sdl2" "zlib" "freetype2" "qt6-base" "qt6-svg" "xdg-user-dirs")
|
||||
makedepends=("git" "pkg-config" "nasm" "cmake" "cargo")
|
||||
|
||||
source=("git+https://github.com/Rosalie241/${_pkgname}.git")
|
||||
source=("git+https://github.com/Rosalie241/$pkgname.git#tag=v$pkgver")
|
||||
sha256sums=('SKIP')
|
||||
|
||||
pkgver()
|
||||
{
|
||||
cd "$_pkgname"
|
||||
|
||||
git describe --tags --always | sed -e 's|^v||;s|-|.|g'
|
||||
}
|
||||
|
||||
prepare()
|
||||
{
|
||||
cd "$srcdir/${_pkgname}"
|
||||
|
||||
mkdir -p build
|
||||
mkdir -p "$srcdir/$pkgname/build"
|
||||
}
|
||||
|
||||
build()
|
||||
{
|
||||
cd "$srcdir/${_pkgname}/build"
|
||||
|
||||
cmake -S "$srcdir/${_pkgname}" -B "$srcdir/${_pkgname}/build" \
|
||||
cmake -S "$srcdir/$pkgname" -B "$srcdir/$pkgname/build" \
|
||||
-DCMAKE_BUILD_TYPE="Release" \
|
||||
-DPORTABLE_INSTALL="OFF" \
|
||||
-DCMAKE_INSTALL_PREFIX="/usr" \
|
||||
-G "Unix Makefiles"
|
||||
make
|
||||
-DNO_RUST="ON" \
|
||||
-G "Ninja"
|
||||
|
||||
cmake --build "$srcdir/$pkgname/build"
|
||||
}
|
||||
|
||||
package()
|
||||
{
|
||||
cd "$srcdir/${_pkgname}/build"
|
||||
|
||||
make install DESTDIR="$pkgdir"
|
||||
cmake --install "$srcdir/$pkgname/build" --prefix="$pkgdir/usr"
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 445 KiB After Width: | Height: | Size: 278 KiB |
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 121 KiB |
|
Before Width: | Height: | Size: 274 KiB After Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 57 KiB |
@@ -1,4 +1,5 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<!-- Copyright 2022 Rosalie Wanders -->
|
||||
<component type="desktop">
|
||||
<!--Created with jdAppdataEdit 2.1-->
|
||||
<id>com.github.Rosalie241.RMG</id>
|
||||
@@ -11,28 +12,257 @@
|
||||
<description><p>Rosalie's Mupen GUI is an easy to use yet customizable mupen64plus front-end, allowing you to easily play N64 games.</p></description>
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<caption>The Rom Browser</caption>
|
||||
<caption>01 - The Rom Browser</caption>
|
||||
<image type="source">https://github.com/Rosalie241/RMG/raw/master/Package/Screenshots/RomBrowser.png</image>
|
||||
</screenshot>
|
||||
<screenshot type="default">
|
||||
<caption>During A Game</caption>
|
||||
<caption>02 - During A Game</caption>
|
||||
<image type="source">https://github.com/Rosalie241/RMG/raw/master/Package/Screenshots/InGame.png</image>
|
||||
</screenshot>
|
||||
<screenshot type="default">
|
||||
<caption>Cheats</caption>
|
||||
<caption>03 - Cheats</caption>
|
||||
<image type="source">https://github.com/Rosalie241/RMG/raw/master/Package/Screenshots/Cheats.png</image>
|
||||
</screenshot>
|
||||
<screenshot type="default">
|
||||
<caption>Settings</caption>
|
||||
<caption>04 - Settings</caption>
|
||||
<image type="source">https://github.com/Rosalie241/RMG/raw/master/Package/Screenshots/Settings.png</image>
|
||||
</screenshot>
|
||||
<screenshot type="default">
|
||||
<caption>Input Settings</caption>
|
||||
<caption>05 - Input Settings</caption>
|
||||
<image type="source">https://github.com/Rosalie241/RMG/raw/master/Package/Screenshots/InputSettings.png</image>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
<description/>
|
||||
<releases>
|
||||
<release version="v0.4.2" date="2023-07-07" type="stable">
|
||||
<description>
|
||||
<p>Changes:</p>
|
||||
<ul>
|
||||
<li>Fix window behavior issues when saving GLideN64 settings while in-game</li>
|
||||
<li>Add VRU support to RMG-Input</li>
|
||||
<li>Add controller hotkey support to RMG-Input</li>
|
||||
<li>Improve window title by using the ROM filename when the ROM isn't in the database</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="v0.4.1" date="2023-06-19" type="stable">
|
||||
<description>
|
||||
<p>Changes:</p>
|
||||
<ul>
|
||||
<li>Fix debug callbacks not printing before GUI has been started</li>
|
||||
<li>Fix emulation error being incorrect in some cases</li>
|
||||
<li>Fix user cheat file location (new ones will be placed in the user config directory instead of the user data directory)</li>
|
||||
<li>Fix mupen64plus-input-gca crashing in v0.4.0 due to updating to a broken commit</li>
|
||||
<li>Fix Paper Mario crashing when using the hammer on a specific tree in-game by implementing RDRAM address range 0x0800000-0x03EFFFFF</li>
|
||||
<li>Improve accuracy of the pure and cached interpreter</li>
|
||||
<li>Add *.st* and *.pj* to the file filter when loading a save state from a file</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="v0.4.0" date="2023-05-04" type="stable">
|
||||
<description>
|
||||
<p>Changes:</p>
|
||||
<ul>
|
||||
<li>Fix updater on windows always installing the desktop icon</li>
|
||||
<li>Fix updater on windows not re-launching RMG after installation</li>
|
||||
<li>Fix the speed limiter toggle and speed factor options not working on linux on some systems</li>
|
||||
<li>Fix the 1080 Snowboarding cheats (thank you Gent)</li>
|
||||
<li>Add support for moving the toolbar (changes to the docked position are persistent across application runs)</li>
|
||||
<li>Update GLideN64 and mupen64plus-input-gca</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="v0.3.9" date="2023-04-06" type="stable">
|
||||
<description>
|
||||
<p>Changes:</p>
|
||||
<ul>
|
||||
<li>Fix "Don't check for updates again" checkbox appearing when requesting an update check through Help -> Check For Updates</li>
|
||||
<li>Add game format, file name, file extension, file size, game I.D. and game region columns to the list view of the ROM browser which can be toggled in the "Show/Hide Columns" menu of the context menu in the list view of the ROM browser (thank you RetraCarteR)</li>
|
||||
<li>Add "Reset Column Sizes" to context menu of the list view of the ROM browser (thank you RetraCarteR)</li>
|
||||
<li>Add "Analog Stick Sensitivity" setting to RMG-Input (thank you JaxonWasTaken)</li>
|
||||
<li>Add "Save Filename Format" setting to the Core tab of the settings dialog</li>
|
||||
<li>Add "Edit Game Input Settings" to the context menu of the ROM browser</li>
|
||||
<li>Add support for 7z files</li>
|
||||
<li>Add progress status to the loading screen in the ROM browser when loading takes more than 5 seconds</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="v0.3.8" date="2023-02-24" type="stable">
|
||||
<description>
|
||||
<p>Changes:</p>
|
||||
<ul>
|
||||
<li>Fix RMG not having the correct video size in some cases (i.e on a WM where resizing is not allowed to be done by an application)</li>
|
||||
<li>Fix translations not working in GLideN64</li>
|
||||
<li>Add -n/--nogui commandline option</li>
|
||||
<li>Add -d/--debug-messages commandline option</li>
|
||||
<li>Add hotkeys for graphics, audio, RSP and input settings</li>
|
||||
<li>Add hotkey for viewing the log</li>
|
||||
<li>Add SIGINT and SIGTERM handling on linux</li>
|
||||
<li>Add "Check For Updates" to "View" in the toolbar</li>
|
||||
<li>Add information about LLE plugins in the settings dialog</li>
|
||||
<li>Add support for toggling visibility of columns in the ROM browser list view (thank you RetraCarteR)</li>
|
||||
<li>Add date and time to the save state slot items in "Current Save State" menu in the menubar</li>
|
||||
<li>Add date and time to the OSD when selecting a save state slot</li>
|
||||
<li>Add failure messages to the OSD when loading/saving a save state fails</li>
|
||||
<li>Improve updater behavior by checking for an update on startup with at least an hour in-between checks</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="v0.3.7" date="2023-02-19" type="stable">
|
||||
<description>
|
||||
<p>Changes:</p>
|
||||
<ul>
|
||||
<li>Fix icons not showing up in some cases when migrating from a version before v0.3.6</li>
|
||||
<li>Fix plugin settings not being loaded properly in the settings dialog when migrating from a version before v0.3.6</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="v0.3.6" date="2023-02-19" type="stable">
|
||||
<description>
|
||||
<p>Changes:</p>
|
||||
<ul>
|
||||
<li>Fix RMG not using xwayland when using wayland (wayland has lots of issues, can be disabled by building with the "-DFORCE_XCB=OFF" option)</li>
|
||||
<li>Fix some non-fatal mupen64plus errors showing a pop-up</li>
|
||||
<li>Fix window not returning to ROM browser when launching a game through the commandline and the plugins aren't ready</li>
|
||||
<li>Add support for Qt stylesheets (place them in the 'user data directory/Styles' directory with the '.qss' extension, they'll show up in "Themes")</li>
|
||||
<li>Add "Icon Theme" setting</li>
|
||||
<li>Add "Speed Factor" to System menu bar</li>
|
||||
<li>Add support for re-ordering columns in the list view of the ROM browser (thank you RetraCarteR)</li>
|
||||
<li>Add AppImage to github releases (this replaces the old portable linux builds on github releases)</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="v0.3.5" date="2023-02-14" type="stable">
|
||||
<description>
|
||||
<p>Changes:</p>
|
||||
<ul>
|
||||
<li>Fix reset button not working properly in RMG-Input</li>
|
||||
<li>Fix profiles not being deleted properly when clicking on cancel/close in some cases in RMG-Input</li>
|
||||
<li>Fix paraLLEl not logging to the log window</li>
|
||||
<li>Fix paraLLEl's odd window behavior by not saving the window size when emulation ends</li>
|
||||
<li>Fix GLideN64 initializing a noise texture being slow</li>
|
||||
<li>Fix issue where an unbound mapping in RMG-Input would respond to an A controller button press in-game</li>
|
||||
<li>Fix issue where RMG-Input wouldn't save anything but the currently opened profile</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="v0.3.4" date="2023-02-11" type="stable">
|
||||
<description>
|
||||
<p>Changes:</p>
|
||||
<ul>
|
||||
<li>Fix mapping button size increasing when reloading settings in RMG-Input</li>
|
||||
<li>Fix analog stick mapping in-game when having buttons and axis bound in RMG-Input</li>
|
||||
<li>Fix height mismatch of "Profile" and "Input Device" UI elements in RMG-Input</li>
|
||||
<li>Fix updater script not using the correct path seperators on Windows</li>
|
||||
<li>Fix GLideN64's per game settings being overwritten by the updater on windows (or not being updated on linux)</li>
|
||||
<li>Fix "Automatic" input device not opening controller device when clicking on 'Refresh' in RMG-Input</li>
|
||||
<li>Fix game specific profile having unexpected UI behavior in RMG-Input</li>
|
||||
<li>Fix cancel and close button not working correctly in RMG-Input</li>
|
||||
<li>Fix profile saving not working correctly when having the same profile selected for multiple players in RMG-Input</li>
|
||||
<li>Add "Filter Events Based On Joystick Type For Buttons" setting to RMG-Input</li>
|
||||
<li>Add "Filter Events Based On Joystick Type For Axis" setting to RMG-Input</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="v0.3.3" date="2023-02-09" type="stable">
|
||||
<description>
|
||||
<p>Changes:</p>
|
||||
<ul>
|
||||
<li>Remove "Controller Is Plugged In" checkbox in RMG-Input</li>
|
||||
<li>Remove text from 'Remove' button in RMG-Input</li>
|
||||
<li>Add "None" input device to RMG-Input (replacing the "Controller Is Plugged In" checkbox)</li>
|
||||
<li>Add "Automatic" input device to RMG-Input</li>
|
||||
<li>Add support for creating profiles in RMG-Input</li>
|
||||
<li>Add icons to 'Remove', 'Refresh', 'Reset' and 'Options' buttons in RMG-Input</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="v0.3.2" date="2023-02-06" type="stable">
|
||||
<description>
|
||||
<p>Changes:</p>
|
||||
<ul>
|
||||
<li>Fix ndd/d64 files in zip files not working when the disk file is inside a subdirectory inside the zip file</li>
|
||||
<li>Fix ndd/d64 files in zip files not working correctly on windows</li>
|
||||
<li>Fix controller image in RMG-Input sometimes being stuck when having a trigger bound to a button</li>
|
||||
<li>Fix trigger mapping in RMG-Input</li>
|
||||
<li>Remove "Invert Axis Mapped To LRZ" setting in RMG-Input</li>
|
||||
<li>Add "Sort Results After Search" ROM Browser setting</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="v0.3.1" date="2023-02-04" type="stable">
|
||||
<description>
|
||||
<p>Changes:</p>
|
||||
<ul>
|
||||
<li>Fix taking a screenshot not working in some cases</li>
|
||||
<li>Fix some games being unable to save due to having an illegal character in the save filename</li>
|
||||
<li>Fix OSD tab crash in GLideN64</li>
|
||||
<li>Fix an issue where the graphics plugin would show the 64DD IPL as ROM name in the settings even after booting a cartridge</li>
|
||||
<li>Fix "Refresh ROM List" in Hotkeys tab in settings dialog not reverting back to defaults when clicking on 'restore defaults'</li>
|
||||
<li>Fix "Remove Duplicate Keybindings" in settings dialog not behaving correctly in some cases</li>
|
||||
<li>Fix fallback application icons not respecting system icon theme on linux</li>
|
||||
<li>Add support for ndd/d64 files in zip files</li>
|
||||
<li>Add support for using the basename of the ROM filename for covers</li>
|
||||
<li>Add support for binding multiple inputs in RMG-Input</li>
|
||||
<li>Improve behavior of window when launching RMG with a ROM using the commandline</li>
|
||||
<li>Improve behavior of error message when emulation fails</li>
|
||||
<li>Improve behavior of hotkey buttons in settings dialog</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="v0.3.0" date="2023-01-23" type="stable">
|
||||
<description>
|
||||
<p>Changes:</p>
|
||||
<ul>
|
||||
<li>Fix GLideN64 not being able to load texture packs from relative paths</li>
|
||||
<li>Fix "Save State Slot" keybindings not working in fullscreen</li>
|
||||
<li>Fix deadzone percentage being incorrect when set to 0% in RMG-Input</li>
|
||||
<li>Fix low deadzone value not working in RMG-Input (thank you porcino)</li>
|
||||
<li>Fix toolbar being hidden when ending emulation while in fullscreen</li>
|
||||
<li>Fix zip files with japanese characters in the file name not being able to be opened on windows</li>
|
||||
<li>Fix ROM Browser not returning to a maximized state when ending emulation</li>
|
||||
<li>Fix ROM Browser not opening maximized when closing RMG with it maximized</li>
|
||||
<li>Fix window not being able to be maximized during emulation when scaling is enabled</li>
|
||||
<li>Fix read-only ROM files not being able to be opened on linux</li>
|
||||
<li>Remove "Allow Manual Window Resizing" setting due to it being always enabled</li>
|
||||
<li>Remove Styles tab from settings dialog on windows</li>
|
||||
<li>Add "Play Game with Cartridge" to context menu when a 64DD disk is selected in the ROM Browser</li>
|
||||
<li>Add "Open ROM Directory" to context menu in the ROM Browser</li>
|
||||
<li>Add an On-Screen Display (see the OSD tab in the settings dialog for settings)</li>
|
||||
<li>Add Theme setting to settings dialog (see Interface -> Miscellaneous, this is a replacement for the previous Styles settings)</li>
|
||||
<li>Improve error message when opening a zip file fails</li>
|
||||
<li>Improve tab order and naming in settings dialog</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="v0.2.9" date="2023-01-03" type="stable">
|
||||
<description>
|
||||
<p>Changes:</p>
|
||||
<ul>
|
||||
<li>Fix wrong 64DD Disk extension being listed in empty ROM browser</li>
|
||||
<li>Fix memory corruption when having 64DD Disk files</li>
|
||||
<li>Fix possible crash when render window wasn't visible yet</li>
|
||||
<li>Fix RMG changing the current directory on non-portable installs</li>
|
||||
<li>Remove Qt warning messages (you can view them by setting RMG_SHOW_DEBUG_QT_MESSAGES to 1)</li>
|
||||
<li>Change "Choose ROM Directory" to "Change ROM Directory"</li>
|
||||
<li>Add -q/--quit-after-emulation commandline option</li>
|
||||
<li>Add 2 more icons to menubar menus/items</li>
|
||||
<li>Add "Current Save State" tab to Hotkeys tab in settings dialog (this allows you to use a hotkey to change save state slots)</li>
|
||||
<li>Improve behavior of Hotkeys tab in settings dialog</li>
|
||||
<li>Improve behavior of window when exiting RMG while in-game</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="v0.2.8" date="2023-01-01" type="stable">
|
||||
<description>
|
||||
<p>Changes:</p>
|
||||
<ul>
|
||||
<li>Fix 'Uniform Size (Grid View)' not being disabled during emulation</li>
|
||||
<li>Fix toolbar not respecting the view setting</li>
|
||||
<li>Fix being stuck at the loading screen when having more ROMs than the ROM search limit</li>
|
||||
<li>Improve error message when opening a ROM fails</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="v0.2.7" date="2022-12-29" type="stable">
|
||||
<description>
|
||||
<p>Changes:</p>
|
||||
|
||||
@@ -4,55 +4,66 @@ Rosalie's Mupen GUI is a free and open-source mupen64plus GUI written in C++.
|
||||
|
||||
It offers a simple-to-use user interface.
|
||||
|
||||
## System Requirements
|
||||
## Download
|
||||
|
||||
* Operating System
|
||||
* Windows Or Linux
|
||||
* CPU
|
||||
* Intel or AMD processor with SSE2 support
|
||||
* RAM
|
||||
* 512MB or more
|
||||
* Graphics card
|
||||
* OpenGL 3.3 capable
|
||||
#### Windows
|
||||
You can download Rosalie's Mupen GUI on 
|
||||
|
||||
#### Linux
|
||||
You can download Rosalie's Mupen GUI on
|
||||
* [Github Releases (AppImage)](https://github.com/Rosalie241/RMG/releases)
|
||||
* [Flathub (Flatpak)](https://flathub.org/apps/details/com.github.Rosalie241.RMG)
|
||||
* [Arch User Repository](https://aur.archlinux.org/packages/rmg)
|
||||
|
||||
## Support
|
||||
|
||||
You can ask for help or report issues on
|
||||
* [Github](https://github.com/Rosalie241/RMG/issues/new)
|
||||
* [Discord](https://discord.gg/k9GuyJ2PpF)
|
||||
|
||||
## License
|
||||
|
||||
Rosalie's Mupen GUI is licensed under the [GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.en.html).
|
||||
|
||||
## Showcase
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
## Building
|
||||
|
||||
#### Linux
|
||||
* Portable Debian/Ubuntu
|
||||
|
||||
```shell
|
||||
sudo apt-get -y install cmake libhidapi-dev libsamplerate0-dev libminizip-dev libsdl2-dev libfreetype6-dev libgl1-mesa-dev libglu1-mesa-dev pkg-config zlib1g-dev binutils-dev libspeexdsp-dev qt5-default build-essential nasm git zip
|
||||
```bash
|
||||
sudo apt-get -y install cmake libhidapi-dev libsamplerate0-dev libspeex-dev libminizip-dev libsdl2-dev libfreetype6-dev libgl1-mesa-dev libglu1-mesa-dev pkg-config zlib1g-dev binutils-dev libspeexdsp-dev qt6-base-dev libqt6svg6-dev build-essential nasm git zip ninja-build
|
||||
./Source/Script/Build.sh Release
|
||||
```
|
||||
|
||||
* Portable Fedora
|
||||
```shell
|
||||
sudo dnf install hidapi-devel libsamplerate-devel minizip-compat-devel SDL2-devel freetype-devel mesa-libGL-devel mesa-libGLU-devel zlib-ng-devel binutils-devel speexdsp-devel qt5-qtbase-devel qt5-qtsvg-devel gcc-c++ nasm git
|
||||
./Source/Script/Build.sh Release
|
||||
```bash
|
||||
sudo dnf install hidapi-devel libsamplerate-devel minizip-compat-devel SDL2-devel freetype-devel mesa-libGL-devel mesa-libGLU-devel zlib-ng-devel binutils-devel speexdsp-devel qt6-qtbase-devel qt6-qtsvg-devel gcc-c++ nasm git ninja-build
|
||||
./Source/Script/Build.sh Release
|
||||
```
|
||||
|
||||
When it's done building, executables can be found in `Bin/Release`
|
||||
|
||||
* Installation/Packaging
|
||||
```
|
||||
mkdir build && cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE="Release" -DPORTABLE_INSTALL="OFF" -G "Unix Makefiles"
|
||||
make install
|
||||
```bash
|
||||
export src_dir="$(pwd)"
|
||||
export build_dir="$(pwd)/build"
|
||||
mkdir -p "$build_dir"
|
||||
cmake -S "$src_dir" -B "$build_dir" -DCMAKE_BUILD_TYPE="Release" -DPORTABLE_INSTALL="OFF" -DCMAKE_INSTALL_PREFIX="/usr" -G "Ninja"
|
||||
cmake --build "$build_dir"
|
||||
cmake --install "$build_dir" --prefix="/usr"
|
||||
```
|
||||
|
||||
#### Windows
|
||||
* Download & Install [MSYS2](https://www.msys2.org/)
|
||||
```
|
||||
sudo pacman -S git make mingw-w64-x86_64-cmake mingw-w64-x86_64-freetype mingw-w64-x86_64-gcc mingw-w64-x86_64-hidapi mingw-w64-x86_64-jasper mingw-w64-x86_64-libpng mingw-w64-x86_64-libsamplerate mingw-w64-x86_64-nasm mingw-w64-x86_64-python3-pip mingw-w64-x86_64-python3-pyopenssl mingw-w64-x86_64-qt5 mingw-w64-x86_64-rust mingw-w64-x86_64-SDL2
|
||||
```bash
|
||||
pacman -S --needed make mingw-w64-x86_64-cmake mingw-w64-x86_64-gcc mingw-w64-x86_64-hidapi mingw-w64-x86_64-freetype mingw-w64-x86_64-libpng mingw-w64-x86_64-SDL2 mingw-w64-x86_64-qt6 mingw-w64-x86_64-SDL2 mingw-w64-x86_64-hidapi mingw-w64-x86_64-speexdsp mingw-w64-x86_64-libsamplerate mingw-w64-x86_64-nasm mingw-w64-x86_64-minizip git
|
||||
./Source/Script/Build.sh Release
|
||||
```
|
||||
|
||||
When it's done building, executables can be found in `Bin/Release`
|
||||
|
||||
## Support
|
||||
|
||||
Join the [Rosalie's Mupen GUI Discord Server](https://discord.gg/k9GuyJ2PpF) for support
|
||||
|
||||
## License
|
||||
|
||||
Rosalie's Mupen GUI is licensed under the [GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.en.html).
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
#
|
||||
# 7-Zip CMakeLists.txt
|
||||
#
|
||||
project(7Zip)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(7ZIP_SOURCES
|
||||
${CMAKE_CURRENT_BINARY_DIR}/C/Bcj2.c
|
||||
${CMAKE_CURRENT_BINARY_DIR}/C/Bra.c
|
||||
${CMAKE_CURRENT_BINARY_DIR}/C/Bra86.c
|
||||
${CMAKE_CURRENT_BINARY_DIR}/C/BraIA64.c
|
||||
${CMAKE_CURRENT_BINARY_DIR}/C/CpuArch.c
|
||||
${CMAKE_CURRENT_BINARY_DIR}/C/Delta.c
|
||||
${CMAKE_CURRENT_BINARY_DIR}/C/Lzma2Dec.c
|
||||
${CMAKE_CURRENT_BINARY_DIR}/C/LzmaDec.c
|
||||
${CMAKE_CURRENT_BINARY_DIR}/C/Ppmd7.c
|
||||
${CMAKE_CURRENT_BINARY_DIR}/C/Ppmd7Dec.c
|
||||
${CMAKE_CURRENT_BINARY_DIR}/C/7zCrc.c
|
||||
${CMAKE_CURRENT_BINARY_DIR}/C/7zCrcOpt.c
|
||||
${CMAKE_CURRENT_BINARY_DIR}/C/Sha256.c
|
||||
${CMAKE_CURRENT_BINARY_DIR}/C/Sha256Opt.c
|
||||
${CMAKE_CURRENT_BINARY_DIR}/C/7zAlloc.c
|
||||
${CMAKE_CURRENT_BINARY_DIR}/C/7zArcIn.c
|
||||
${CMAKE_CURRENT_BINARY_DIR}/C/7zBuf.c
|
||||
${CMAKE_CURRENT_BINARY_DIR}/C/7zBuf2.c
|
||||
${CMAKE_CURRENT_BINARY_DIR}/C/7zDec.c
|
||||
${CMAKE_CURRENT_BINARY_DIR}/C/7zFile.c
|
||||
${CMAKE_CURRENT_BINARY_DIR}/C/7zStream.c
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${7ZIP_SOURCES}
|
||||
COMMAND ${CMAKE_COMMAND} -E tar "xvz" "${CMAKE_CURRENT_SOURCE_DIR}/lzma2301.tar.gz"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
add_definitions(-D_7ZIP_PPMD_SUPPPORT -DNDEBUG -D_REENTRANT -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE)
|
||||
|
||||
add_library(7Zip STATIC ${7ZIP_SOURCES})
|
||||
@@ -0,0 +1,2 @@
|
||||
# 7-Zip SDK v23.01
|
||||
Downloaded from https://www.7-zip.org/sdk.html
|
||||
@@ -6,7 +6,9 @@ include(ExternalProject)
|
||||
set(THIRDPARTY_DIR ${CMAKE_BINARY_DIR}/Source/3rdParty)
|
||||
|
||||
find_program(RUST_CARGO cargo)
|
||||
if(${RUST_CARGO} STREQUAL "RUST_CARGO-NOTFOUND")
|
||||
if(NO_RUST)
|
||||
set(BUILD_INPUT_GCA OFF)
|
||||
elseif(${RUST_CARGO} STREQUAL "RUST_CARGO-NOTFOUND")
|
||||
message(WARNING "mupen64plus-input-gca will not be built due to not having cargo installed")
|
||||
set(BUILD_INPUT_GCA OFF)
|
||||
else()
|
||||
@@ -37,34 +39,40 @@ endif(USE_CCACHE)
|
||||
|
||||
if(NOT NO_GIT_CLONE)
|
||||
set(MUPEN64PLUSCORE_URL "https://github.com/Rosalie241/mupen64plus-core")
|
||||
set(MUPEN64PLUSCORE_TAG "3b6523edd467eb9224310e0862005c7e5ae9c290")
|
||||
|
||||
set(MUPEN64PLUSCORE_TAG "a231cb5e9a741e1893b0d7669c363d57550295a8")
|
||||
|
||||
set(MUPEN64PLUS_RSP_CXD4_URL "https://github.com/mupen64plus/mupen64plus-rsp-cxd4")
|
||||
set(MUPEN64PLUS_RSP_CXD4_TAG "39f79201baa15890c4cbae92f2215a634cc3ee6d")
|
||||
|
||||
|
||||
set(MUPEN64PLUS_RSP_HLE_URL "https://github.com/mupen64plus/mupen64plus-rsp-hle")
|
||||
set(MUPEN64PLUS_RSP_HLE_TAG "88093cb43499eff53d343653cddcae2132af17ef")
|
||||
|
||||
|
||||
set(MUPEN64PLUS_RSP_PARALLEL_URL "https://github.com/Rosalie241/parallel-rsp")
|
||||
set(MUPEN64PLUS_RSP_PARALLEL_TAG "e67cee3131651c3e48343294d94fa68a6f8ec14c")
|
||||
|
||||
set(MUPEN64PLUS_RSP_PARALLEL_TAG "dbecbe3a120e428436c5972fa7b872cfc2c9db3e")
|
||||
|
||||
set(MUPEN64PLUS_INPUT_RAPHNET_URL "https://github.com/raphnet/mupen64plus-input-raphnetraw")
|
||||
set(MUPEN64PLUS_INPUT_RAPHNET_TAG "86112413e98a8648edb11d199673cc24d5799af8")
|
||||
|
||||
|
||||
set(MUPEN64PLUS_INPUT_GCA_URL "https://github.com/amatho/mupen64plus-input-gca")
|
||||
set(MUPEN64PLUS_INPUT_GCA_TAG "21639fb13dfa797a7c0949ffd9bbda9a3456fc69")
|
||||
|
||||
set(MUPEN64PLUS_GFX_ANGRYLION_URL "https://github.com/Rosalie241/angrylion-rdp-plus")
|
||||
set(MUPEN64PLUS_GFX_ANGRYLION_TAG "158e74d4ec1c818289cd19c3a05e498c3ceff758")
|
||||
set(MUPEN64PLUS_GFX_ANGRYLION_TAG "670abbc972bd430fa77291b0967dd73128314317")
|
||||
|
||||
set(MUPEN64PLUS_GFX_GLIDEN64_URL "https://github.com/gonetz/GLideN64")
|
||||
set(MUPEN64PLUS_GFX_GLIDEN64_TAG "7bbde56cf08ff96108efb592848e5b6eaac293c9")
|
||||
set(MUPEN64PLUS_GFX_GLIDEN64_URL "https://github.com/Rosalie241/GLideN64")
|
||||
set(MUPEN64PLUS_GFX_GLIDEN64_TAG "34da878ce8dd2fce7135ee27fa4786613320740e")
|
||||
|
||||
set(MUPEN64PLUS_GFX_PARALLEL_URL "https://github.com/Rosalie241/parallel-rdp-standalone")
|
||||
set(MUPEN64PLUS_GFX_PARALLEL_TAG "88d3626362e41aa9b011fca1f65e1c1237ddb3b2")
|
||||
set(MUPEN64PLUS_GFX_PARALLEL_TAG "2c2226517c4c8929e08ec944654867e26efe0cf5")
|
||||
|
||||
set(DISCORD_RPC_URL "https://github.com/discord/discord-rpc")
|
||||
set(DISCORD_RPC_TAG "963aa9f3e5ce81a4682c6ca3d136cddda614db33")
|
||||
|
||||
set(IMGUI_URL "https://github.com/ocornut/imgui")
|
||||
set(IMGUI_TAG "d7c8516a4b848c0291e3d75b627c0843f515f591")
|
||||
|
||||
set(SDL_GAMECONTROLLERDB_URL "https://github.com/gabomdq/SDL_GameControllerDB")
|
||||
set(SDL_GAMECONTROLLERDB_TAG "5e25aeccb84fb166451443c4cd9fff349e8b0360")
|
||||
endif(NOT NO_GIT_CLONE)
|
||||
|
||||
ExternalProject_Add(mupen64plus-core
|
||||
@@ -76,11 +84,11 @@ ExternalProject_Add(mupen64plus-core
|
||||
GIT_REPOSITORY ${MUPEN64PLUSCORE_URL}
|
||||
GIT_TAG ${MUPEN64PLUSCORE_TAG}
|
||||
|
||||
BUILD_COMMAND $(MAKE) all OSD=0 NEW_DYNAREC=1 TARGET=${CORE_FILE} DEBUG=$<CONFIG:Debug> KEYBINDINGS=0 CC=${MAKE_CC_COMPILER} CXX=${MAKE_CXX_COMPILER}
|
||||
BUILD_COMMAND make all OSD=0 NEW_DYNAREC=1 KEYBINDINGS=0 ACCURATE_FPU=1 TARGET=${CORE_FILE} DEBUG=$<CONFIG:Debug> CC=${MAKE_CC_COMPILER} CXX=${MAKE_CXX_COMPILER}
|
||||
BUILD_IN_SOURCE False
|
||||
|
||||
BINARY_DIR ${M64P_CORE_DIR}/projects/unix
|
||||
|
||||
|
||||
BUILD_BYPRODUCTS ${M64P_CORE_DIR}/projects/unix/${CORE_FILE}
|
||||
)
|
||||
|
||||
@@ -95,11 +103,11 @@ ExternalProject_Add(mupen64plus-rsp-cxd4
|
||||
GIT_REPOSITORY ${MUPEN64PLUS_RSP_CXD4_URL}
|
||||
GIT_TAG ${MUPEN64PLUS_RSP_CXD4_TAG}
|
||||
|
||||
BUILD_COMMAND $(MAKE) all APIDIR=${APIDIR} DEBUG=$<CONFIG:Debug> POSTFIX= CC=${MAKE_CC_COMPILER} CXX=${MAKE_CXX_COMPILER}
|
||||
BUILD_COMMAND make all APIDIR=${APIDIR} DEBUG=$<CONFIG:Debug> POSTFIX= CC=${MAKE_CC_COMPILER} CXX=${MAKE_CXX_COMPILER}
|
||||
BUILD_IN_SOURCE False
|
||||
|
||||
BINARY_DIR ${THIRDPARTY_DIR}/mupen64plus-rsp-cxd4/projects/unix
|
||||
|
||||
|
||||
BUILD_BYPRODUCTS ${THIRDPARTY_DIR}/mupen64plus-rsp-cxd4/projects/unix/mupen64plus-rsp-cxd4.${SO_EXT}
|
||||
DEPENDS mupen64plus-core
|
||||
)
|
||||
@@ -113,7 +121,7 @@ ExternalProject_Add(mupen64plus-rsp-hle
|
||||
GIT_REPOSITORY ${MUPEN64PLUS_RSP_HLE_URL}
|
||||
GIT_TAG ${MUPEN64PLUS_RSP_HLE_TAG}
|
||||
|
||||
BUILD_COMMAND $(MAKE) all APIDIR=${APIDIR} DEBUG=$<CONFIG:Debug> CC=${MAKE_CC_COMPILER} CXX=${MAKE_CXX_COMPILER}
|
||||
BUILD_COMMAND make all APIDIR=${APIDIR} DEBUG=$<CONFIG:Debug> CC=${MAKE_CC_COMPILER} CXX=${MAKE_CXX_COMPILER}
|
||||
BUILD_IN_SOURCE False
|
||||
|
||||
BINARY_DIR ${THIRDPARTY_DIR}/mupen64plus-rsp-hle/projects/unix
|
||||
@@ -131,7 +139,7 @@ ExternalProject_Add(mupen64plus-rsp-parallel
|
||||
INSTALL_COMMAND ""
|
||||
|
||||
BUILD_IN_SOURCE True
|
||||
CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
|
||||
CMAKE_ARGS ${CMAKE_CONFIGURE_ARGS}
|
||||
|
||||
BUILD_BYPRODUCTS ${THIRDPARTY_DIR}/mupen64plus-rsp-parallel/mupen64plus-rsp-parallel.${SO_EXT}
|
||||
DEPENDS mupen64plus-core
|
||||
@@ -146,11 +154,11 @@ ExternalProject_Add(mupen64plus-input-raphnetraw
|
||||
GIT_REPOSITORY ${MUPEN64PLUS_INPUT_RAPHNET_URL}
|
||||
GIT_TAG ${MUPEN64PLUS_INPUT_RAPHNET_TAG}
|
||||
|
||||
BUILD_COMMAND $(MAKE) all APIDIR=${APIDIR} DEBUG=$<CONFIG:Debug> CC=${MAKE_CC_COMPILER} CXX=${MAKE_CXX_COMPILER}
|
||||
BUILD_COMMAND make all APIDIR=${APIDIR} DEBUG=$<CONFIG:Debug> CC=${MAKE_CC_COMPILER} CXX=${MAKE_CXX_COMPILER}
|
||||
BUILD_IN_SOURCE False
|
||||
|
||||
BINARY_DIR ${THIRDPARTY_DIR}/mupen64plus-input-raphnetraw/projects/unix
|
||||
|
||||
|
||||
BUILD_BYPRODUCTS ${THIRDPARTY_DIR}/mupen64plus-input-raphnetraw/projects/unix/mupen64plus-input-raphnetraw.${SO_EXT}
|
||||
DEPENDS mupen64plus-core
|
||||
)
|
||||
@@ -175,11 +183,12 @@ endif(BUILD_INPUT_GCA)
|
||||
|
||||
set(GLIDEN64_DIR "${THIRDPARTY_DIR}/mupen64plus-video-GLideN64")
|
||||
set(GLIDEN64_BUILD_DIR "${GLIDEN64_DIR}/projects/cmake")
|
||||
if (MSYS)
|
||||
set(GLIDEN64_CUSTOM_INI "${GLIDEN64_DIR}/ini/GLideN64.custom.ini")
|
||||
if (WIN32)
|
||||
set(GLIDEN64_LIB "${GLIDEN64_BUILD_DIR}/mupen64plus-video-GLideN64.${SO_EXT}")
|
||||
else()
|
||||
else(WIN32)
|
||||
set(GLIDEN64_LIB "${GLIDEN64_BUILD_DIR}/plugin/${CMAKE_BUILD_TYPE}/mupen64plus-video-GLideN64.${SO_EXT}")
|
||||
endif()
|
||||
endif(WIN32)
|
||||
|
||||
set(GLIDENUI_TRANSLATIONS_LANGS "de;es;fr;it;ja;pl;pt_BR")
|
||||
foreach(LANG ${GLIDENUI_TRANSLATIONS_LANGS})
|
||||
@@ -195,7 +204,7 @@ ExternalProject_Add(mupen64plus-video-angrylion-plus
|
||||
INSTALL_COMMAND ""
|
||||
|
||||
BUILD_IN_SOURCE True
|
||||
CMAKE_ARGS -DBUILD_MUPEN64PLUS=ON -DBUILD_PROJECT64=OFF -DUSE_QT5=${USE_QT5} ${CMAKE_CONFIGURE_ARGS}
|
||||
CMAKE_ARGS -DBUILD_MUPEN64PLUS=ON -DBUILD_PROJECT64=OFF -DUSE_QT5=OFF ${CMAKE_CONFIGURE_ARGS}
|
||||
|
||||
BUILD_BYPRODUCTS ${THIRDPARTY_DIR}/mupen64plus-video-angrylion-plus/mupen64plus-video-angrylion-plus.${SO_EXT}
|
||||
DEPENDS mupen64plus-core
|
||||
@@ -210,14 +219,12 @@ ExternalProject_Add(mupen64plus-video-GLideN64
|
||||
INSTALL_COMMAND ""
|
||||
|
||||
BUILD_IN_SOURCE False
|
||||
CMAKE_ARGS -DMUPENPLUSAPI=ON -DMUPENPLUSAPI_GLIDENUI=ON -DGLIDENUI_QT6=${USE_QT6} -DUSE_SYSTEM_LIBS=ON ${CMAKE_CONFIGURE_ARGS}
|
||||
CMAKE_ARGS -DMUPENPLUSAPI=ON -DMUPENPLUSAPI_GLIDENUI=ON -DGLIDENUI_QT6=ON -DUSE_SYSTEM_LIBS=ON ${CMAKE_CONFIGURE_ARGS}
|
||||
|
||||
SOURCE_SUBDIR ./src/
|
||||
BINARY_DIR ${GLIDEN64_BUILD_DIR}
|
||||
|
||||
BUILD_BYPRODUCTS ${GLIDEN64_LIB}
|
||||
BUILD_BYPRODUCTS ${GLIDEN64_DIR}/ini/GLideN64.custom.ini
|
||||
BUILD_BYPRODUCTS ${GLIDENUI_TRANSLATIONS}
|
||||
)
|
||||
|
||||
ExternalProject_Add(mupen64plus-video-parallel
|
||||
@@ -229,7 +236,7 @@ ExternalProject_Add(mupen64plus-video-parallel
|
||||
INSTALL_COMMAND ""
|
||||
|
||||
BUILD_IN_SOURCE True
|
||||
CMAKE_ARGS -DUSE_QT5=${USE_QT5} ${CMAKE_CONFIGURE_ARGS}
|
||||
CMAKE_ARGS -DUSE_QT5=OFF ${CMAKE_CONFIGURE_ARGS}
|
||||
|
||||
BUILD_BYPRODUCTS ${THIRDPARTY_DIR}/mupen64plus-video-parallel/mupen64plus-video-parallel.${SO_EXT}
|
||||
DEPENDS mupen64plus-core
|
||||
@@ -251,6 +258,60 @@ if (DISCORD_RPC)
|
||||
)
|
||||
endif(DISCORD_RPC)
|
||||
|
||||
set(IMGUI_DIR ${THIRDPARTY_DIR}/imgui)
|
||||
set(IMGUI_BUILD_DIR ${IMGUI_DIR}/build)
|
||||
set(IMGUI_SOURCES_IN
|
||||
${IMGUI_DIR}/imgui_draw.cpp
|
||||
${IMGUI_DIR}/imgui_tables.cpp
|
||||
${IMGUI_DIR}/imgui_widgets.cpp
|
||||
${IMGUI_DIR}/imgui.cpp
|
||||
${IMGUI_DIR}/backends/imgui_impl_opengl3.cpp
|
||||
${IMGUI_DIR}/backends/imgui_impl_opengl3.h
|
||||
${IMGUI_DIR}/backends/imgui_impl_opengl3_loader.h
|
||||
)
|
||||
set(IMGUI_SOURCES_OUT
|
||||
${IMGUI_BUILD_DIR}/imgui_draw.cpp
|
||||
${IMGUI_BUILD_DIR}/imgui_tables.cpp
|
||||
${IMGUI_BUILD_DIR}/imgui_widgets.cpp
|
||||
${IMGUI_BUILD_DIR}/imgui.cpp
|
||||
${IMGUI_BUILD_DIR}/imgui_impl_opengl3.cpp
|
||||
${IMGUI_BUILD_DIR}/imgui_impl_opengl3.h
|
||||
${IMGUI_BUILD_DIR}/imgui_impl_opengl3_loader.h
|
||||
)
|
||||
ExternalProject_Add(imgui
|
||||
SOURCE_DIR imgui
|
||||
|
||||
GIT_REPOSITORY ${IMGUI_URL}
|
||||
GIT_TAG ${IMGUI_TAG}
|
||||
|
||||
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E make_directory ${IMGUI_BUILD_DIR}
|
||||
BUILD_COMMAND ${CMAKE_COMMAND} -E copy ${IMGUI_SOURCES_IN} ${IMGUI_BUILD_DIR}
|
||||
INSTALL_COMMAND ""
|
||||
|
||||
BUILD_BYPRODUCTS ${IMGUI_SOURCES_OUT}
|
||||
)
|
||||
|
||||
set(SDL_GAMECONTROLLERDB_DIR ${THIRDPARTY_DIR}/SDL_GameControllerDB)
|
||||
set(SDL_GAMECONTROLLERDB_BUILD_DIR ${SDL_GAMECONTROLLERDB_DIR}/build)
|
||||
set(SDL_GAMECONTROLLERDB_SOURCES_IN
|
||||
${SDL_GAMECONTROLLERDB_DIR}/gamecontrollerdb.txt
|
||||
)
|
||||
set(SDL_GAMECONTROLLERDB_SOURCES_OUT
|
||||
${SDL_GAMECONTROLLERDB_BUILD_DIR}/gamecontrollerdb.txt
|
||||
)
|
||||
ExternalProject_Add(SDL_GameControllerDB
|
||||
SOURCE_DIR SDL_GameControllerDB
|
||||
|
||||
GIT_REPOSITORY ${SDL_GAMECONTROLLERDB_URL}
|
||||
GIT_TAG ${SDL_GAMECONTROLLERDB_TAG}
|
||||
|
||||
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E make_directory ${SDL_GAMECONTROLLERDB_BUILD_DIR}
|
||||
BUILD_COMMAND ${CMAKE_COMMAND} -E copy ${SDL_GAMECONTROLLERDB_SOURCES_IN} ${SDL_GAMECONTROLLERDB_SOURCES_OUT}
|
||||
INSTALL_COMMAND ""
|
||||
|
||||
BUILD_BYPRODUCTS ${SDL_GAMECONTROLLERDB_SOURCES_OUT}
|
||||
)
|
||||
|
||||
ExternalProject_Get_property(mupen64plus-core BUILD_BYPRODUCTS)
|
||||
set(MUPEN64PLUSCORE_LIB ${BUILD_BYPRODUCTS} PARENT_SCOPE)
|
||||
set(MUPEN64PLUSCORE_INI ${M64P_CORE_DIR}/data/mupen64plus.ini PARENT_SCOPE)
|
||||
@@ -276,12 +337,19 @@ ExternalProject_Get_property(mupen64plus-video-angrylion-plus BUILD_BYPRODUCTS)
|
||||
set(MUPEN64PLUS_PLUGIN_GFX_ANGRYLION ${BUILD_BYPRODUCTS} PARENT_SCOPE)
|
||||
|
||||
ExternalProject_Get_property(mupen64plus-video-GLideN64 BUILD_BYPRODUCTS)
|
||||
set(MUPEN64PLUS_PLUGIN_GFX_GLIDEN64 ${BUILD_BYPRODUCTS} PARENT_SCOPE)
|
||||
set(MUPEN64PLUS_PLUGIN_GFX_GLIDEN64 ${BUILD_BYPRODUCTS} ${GLIDENUI_TRANSLATIONS} ${GLIDEN64_CUSTOM_INI} PARENT_SCOPE)
|
||||
|
||||
ExternalProject_Get_property(mupen64plus-video-parallel BUILD_BYPRODUCTS)
|
||||
set(MUPEN64PLUS_PLUGIN_GFX_PARALLEL ${BUILD_BYPRODUCTS} PARENT_SCOPE)
|
||||
|
||||
if (DISCORD_RPC)
|
||||
ExternalProject_Get_property(discord-rpc BUILD_BYPRODUCTS)
|
||||
set(DISCORD_RPC_LIB ${BUILD_BYPRODUCTS} PARENT_SCOPE)
|
||||
ExternalProject_Get_property(discord-rpc BUILD_BYPRODUCTS)
|
||||
set(DISCORD_RPC_LIB ${BUILD_BYPRODUCTS} PARENT_SCOPE)
|
||||
endif(DISCORD_RPC)
|
||||
|
||||
ExternalProject_Get_property(imgui BUILD_BYPRODUCTS)
|
||||
set(IMGUI_SOURCES ${BUILD_BYPRODUCTS} PARENT_SCOPE)
|
||||
set(IMGUI_DIR ${THIRDPARTY_DIR}/imgui PARENT_SCOPE)
|
||||
|
||||
ExternalProject_Get_property(SDL_GameControllerDB BUILD_BYPRODUCTS)
|
||||
set(SDL_GAMECONTROLLERDB ${BUILD_BYPRODUCTS} PARENT_SCOPE)
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#
|
||||
# 7-Zip CMakeLists.txt
|
||||
#
|
||||
project(vosk-api)
|
||||
|
||||
set(VOSK_API_VERSION "0.3.45")
|
||||
|
||||
if (WIN32)
|
||||
set(VOSK_API_PLATFORM "win64")
|
||||
set(SO_EXT "dll")
|
||||
else(WIN32)
|
||||
set(SO_EXT "so")
|
||||
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
|
||||
set(VOSK_API_PLATFORM "linux-aarch64")
|
||||
else()
|
||||
set(VOSK_API_PLATFORM "linux-x86_64")
|
||||
endif()
|
||||
endif(WIN32)
|
||||
|
||||
set(VOSK_API_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/vosk-${VOSK_API_PLATFORM}-${VOSK_API_VERSION}/libvosk.${SO_EXT}
|
||||
)
|
||||
|
||||
add_library(vosk-api IMPORTED SHARED ${VOSK_API_SOURCES})
|
||||
|
||||
set(VOSK_API_LIBRARY ${VOSK_API_SOURCES} PARENT_SCOPE)
|
||||
set(VOSK_API_MODEL "${CMAKE_CURRENT_SOURCE_DIR}/vosk-model-small-en-us-0.15.zip" PARENT_SCOPE)
|
||||
@@ -0,0 +1,4 @@
|
||||
# vosk-api
|
||||
Downloaded from https://github.com/alphacep/vosk-api/releases/tag/v0.3.45
|
||||
# vosk-model-small-en-us-0.15.zip
|
||||
Downloaded from https://alphacephei.com/vosk/models
|
||||
@@ -0,0 +1,361 @@
|
||||
// Copyright 2020-2021 Alpha Cephei Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/* This header contains the C API for Vosk speech recognition system */
|
||||
|
||||
#ifndef VOSK_API_H
|
||||
#define VOSK_API_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Model stores all the data required for recognition
|
||||
* it contains static data and can be shared across processing
|
||||
* threads. */
|
||||
typedef struct VoskModel VoskModel;
|
||||
|
||||
|
||||
/** Speaker model is the same as model but contains the data
|
||||
* for speaker identification. */
|
||||
typedef struct VoskSpkModel VoskSpkModel;
|
||||
|
||||
|
||||
/** Recognizer object is the main object which processes data.
|
||||
* Each recognizer usually runs in own thread and takes audio as input.
|
||||
* Once audio is processed recognizer returns JSON object as a string
|
||||
* which represent decoded information - words, confidences, times, n-best lists,
|
||||
* speaker information and so on */
|
||||
typedef struct VoskRecognizer VoskRecognizer;
|
||||
|
||||
|
||||
/**
|
||||
* Batch model object
|
||||
*/
|
||||
typedef struct VoskBatchModel VoskBatchModel;
|
||||
|
||||
/**
|
||||
* Batch recognizer object
|
||||
*/
|
||||
typedef struct VoskBatchRecognizer VoskBatchRecognizer;
|
||||
|
||||
|
||||
/** Loads model data from the file and returns the model object
|
||||
*
|
||||
* @param model_path: the path of the model on the filesystem
|
||||
* @returns model object or NULL if problem occured */
|
||||
VoskModel *vosk_model_new(const char *model_path);
|
||||
|
||||
|
||||
/** Releases the model memory
|
||||
*
|
||||
* The model object is reference-counted so if some recognizer
|
||||
* depends on this model, model might still stay alive. When
|
||||
* last recognizer is released, model will be released too. */
|
||||
void vosk_model_free(VoskModel *model);
|
||||
|
||||
|
||||
/** Check if a word can be recognized by the model
|
||||
* @param word: the word
|
||||
* @returns the word symbol if @param word exists inside the model
|
||||
* or -1 otherwise.
|
||||
* Reminding that word symbol 0 is for <epsilon> */
|
||||
int vosk_model_find_word(VoskModel *model, const char *word);
|
||||
|
||||
|
||||
/** Loads speaker model data from the file and returns the model object
|
||||
*
|
||||
* @param model_path: the path of the model on the filesystem
|
||||
* @returns model object or NULL if problem occured */
|
||||
VoskSpkModel *vosk_spk_model_new(const char *model_path);
|
||||
|
||||
|
||||
/** Releases the model memory
|
||||
*
|
||||
* The model object is reference-counted so if some recognizer
|
||||
* depends on this model, model might still stay alive. When
|
||||
* last recognizer is released, model will be released too. */
|
||||
void vosk_spk_model_free(VoskSpkModel *model);
|
||||
|
||||
/** Creates the recognizer object
|
||||
*
|
||||
* The recognizers process the speech and return text using shared model data
|
||||
* @param model VoskModel containing static data for recognizer. Model can be
|
||||
* shared across recognizers, even running in different threads.
|
||||
* @param sample_rate The sample rate of the audio you going to feed into the recognizer.
|
||||
* Make sure this rate matches the audio content, it is a common
|
||||
* issue causing accuracy problems.
|
||||
* @returns recognizer object or NULL if problem occured */
|
||||
VoskRecognizer *vosk_recognizer_new(VoskModel *model, float sample_rate);
|
||||
|
||||
|
||||
/** Creates the recognizer object with speaker recognition
|
||||
*
|
||||
* With the speaker recognition mode the recognizer not just recognize
|
||||
* text but also return speaker vectors one can use for speaker identification
|
||||
*
|
||||
* @param model VoskModel containing static data for recognizer. Model can be
|
||||
* shared across recognizers, even running in different threads.
|
||||
* @param sample_rate The sample rate of the audio you going to feed into the recognizer.
|
||||
* Make sure this rate matches the audio content, it is a common
|
||||
* issue causing accuracy problems.
|
||||
* @param spk_model speaker model for speaker identification
|
||||
* @returns recognizer object or NULL if problem occured */
|
||||
VoskRecognizer *vosk_recognizer_new_spk(VoskModel *model, float sample_rate, VoskSpkModel *spk_model);
|
||||
|
||||
|
||||
/** Creates the recognizer object with the phrase list
|
||||
*
|
||||
* Sometimes when you want to improve recognition accuracy and when you don't need
|
||||
* to recognize large vocabulary you can specify a list of phrases to recognize. This
|
||||
* will improve recognizer speed and accuracy but might return [unk] if user said
|
||||
* something different.
|
||||
*
|
||||
* Only recognizers with lookahead models support this type of quick configuration.
|
||||
* Precompiled HCLG graph models are not supported.
|
||||
*
|
||||
* @param model VoskModel containing static data for recognizer. Model can be
|
||||
* shared across recognizers, even running in different threads.
|
||||
* @param sample_rate The sample rate of the audio you going to feed into the recognizer.
|
||||
* Make sure this rate matches the audio content, it is a common
|
||||
* issue causing accuracy problems.
|
||||
* @param grammar The string with the list of phrases to recognize as JSON array of strings,
|
||||
* for example "["one two three four five", "[unk]"]".
|
||||
*
|
||||
* @returns recognizer object or NULL if problem occured */
|
||||
VoskRecognizer *vosk_recognizer_new_grm(VoskModel *model, float sample_rate, const char *grammar);
|
||||
|
||||
|
||||
/** Adds speaker model to already initialized recognizer
|
||||
*
|
||||
* Can add speaker recognition model to already created recognizer. Helps to initialize
|
||||
* speaker recognition for grammar-based recognizer.
|
||||
*
|
||||
* @param spk_model Speaker recognition model */
|
||||
void vosk_recognizer_set_spk_model(VoskRecognizer *recognizer, VoskSpkModel *spk_model);
|
||||
|
||||
|
||||
/** Reconfigures recognizer to use grammar
|
||||
*
|
||||
* @param recognizer Already running VoskRecognizer
|
||||
* @param grammar Set of phrases in JSON array of strings or "[]" to use default model graph.
|
||||
* See also vosk_recognizer_new_grm
|
||||
*/
|
||||
void vosk_recognizer_set_grm(VoskRecognizer *recognizer, char const *grammar);
|
||||
|
||||
|
||||
/** Configures recognizer to output n-best results
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* "alternatives": [
|
||||
* { "text": "one two three four five", "confidence": 0.97 },
|
||||
* { "text": "one two three for five", "confidence": 0.03 },
|
||||
* ]
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @param max_alternatives - maximum alternatives to return from recognition results
|
||||
*/
|
||||
void vosk_recognizer_set_max_alternatives(VoskRecognizer *recognizer, int max_alternatives);
|
||||
|
||||
|
||||
/** Enables words with times in the output
|
||||
*
|
||||
* <pre>
|
||||
* "result" : [{
|
||||
* "conf" : 1.000000,
|
||||
* "end" : 1.110000,
|
||||
* "start" : 0.870000,
|
||||
* "word" : "what"
|
||||
* }, {
|
||||
* "conf" : 1.000000,
|
||||
* "end" : 1.530000,
|
||||
* "start" : 1.110000,
|
||||
* "word" : "zero"
|
||||
* }, {
|
||||
* "conf" : 1.000000,
|
||||
* "end" : 1.950000,
|
||||
* "start" : 1.530000,
|
||||
* "word" : "zero"
|
||||
* }, {
|
||||
* "conf" : 1.000000,
|
||||
* "end" : 2.340000,
|
||||
* "start" : 1.950000,
|
||||
* "word" : "zero"
|
||||
* }, {
|
||||
* "conf" : 1.000000,
|
||||
* "end" : 2.610000,
|
||||
* "start" : 2.340000,
|
||||
* "word" : "one"
|
||||
* }],
|
||||
* </pre>
|
||||
*
|
||||
* @param words - boolean value
|
||||
*/
|
||||
void vosk_recognizer_set_words(VoskRecognizer *recognizer, int words);
|
||||
|
||||
/** Like above return words and confidences in partial results
|
||||
*
|
||||
* @param partial_words - boolean value
|
||||
*/
|
||||
void vosk_recognizer_set_partial_words(VoskRecognizer *recognizer, int partial_words);
|
||||
|
||||
/** Set NLSML output
|
||||
* @param nlsml - boolean value
|
||||
*/
|
||||
void vosk_recognizer_set_nlsml(VoskRecognizer *recognizer, int nlsml);
|
||||
|
||||
|
||||
/** Accept voice data
|
||||
*
|
||||
* accept and process new chunk of voice data
|
||||
*
|
||||
* @param data - audio data in PCM 16-bit mono format
|
||||
* @param length - length of the audio data
|
||||
* @returns 1 if silence is occured and you can retrieve a new utterance with result method
|
||||
* 0 if decoding continues
|
||||
* -1 if exception occured */
|
||||
int vosk_recognizer_accept_waveform(VoskRecognizer *recognizer, const char *data, int length);
|
||||
|
||||
|
||||
/** Same as above but the version with the short data for language bindings where you have
|
||||
* audio as array of shorts */
|
||||
int vosk_recognizer_accept_waveform_s(VoskRecognizer *recognizer, const short *data, int length);
|
||||
|
||||
|
||||
/** Same as above but the version with the float data for language bindings where you have
|
||||
* audio as array of floats */
|
||||
int vosk_recognizer_accept_waveform_f(VoskRecognizer *recognizer, const float *data, int length);
|
||||
|
||||
|
||||
/** Returns speech recognition result
|
||||
*
|
||||
* @returns the result in JSON format which contains decoded line, decoded
|
||||
* words, times in seconds and confidences. You can parse this result
|
||||
* with any json parser
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* "text" : "what zero zero zero one"
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* If alternatives enabled it returns result with alternatives, see also vosk_recognizer_set_max_alternatives().
|
||||
*
|
||||
* If word times enabled returns word time, see also vosk_recognizer_set_word_times().
|
||||
*/
|
||||
const char *vosk_recognizer_result(VoskRecognizer *recognizer);
|
||||
|
||||
|
||||
/** Returns partial speech recognition
|
||||
*
|
||||
* @returns partial speech recognition text which is not yet finalized.
|
||||
* result may change as recognizer process more data.
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* "partial" : "cyril one eight zero"
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
const char *vosk_recognizer_partial_result(VoskRecognizer *recognizer);
|
||||
|
||||
|
||||
/** Returns speech recognition result. Same as result, but doesn't wait for silence
|
||||
* You usually call it in the end of the stream to get final bits of audio. It
|
||||
* flushes the feature pipeline, so all remaining audio chunks got processed.
|
||||
*
|
||||
* @returns speech result in JSON format.
|
||||
*/
|
||||
const char *vosk_recognizer_final_result(VoskRecognizer *recognizer);
|
||||
|
||||
|
||||
/** Resets the recognizer
|
||||
*
|
||||
* Resets current results so the recognition can continue from scratch */
|
||||
void vosk_recognizer_reset(VoskRecognizer *recognizer);
|
||||
|
||||
|
||||
/** Releases recognizer object
|
||||
*
|
||||
* Underlying model is also unreferenced and if needed released */
|
||||
void vosk_recognizer_free(VoskRecognizer *recognizer);
|
||||
|
||||
/** Set log level for Kaldi messages
|
||||
*
|
||||
* @param log_level the level
|
||||
* 0 - default value to print info and error messages but no debug
|
||||
* less than 0 - don't print info messages
|
||||
* greather than 0 - more verbose mode
|
||||
*/
|
||||
void vosk_set_log_level(int log_level);
|
||||
|
||||
/**
|
||||
* Init, automatically select a CUDA device and allow multithreading.
|
||||
* Must be called once from the main thread.
|
||||
* Has no effect if HAVE_CUDA flag is not set.
|
||||
*/
|
||||
void vosk_gpu_init();
|
||||
|
||||
/**
|
||||
* Init CUDA device in a multi-threaded environment.
|
||||
* Must be called for each thread.
|
||||
* Has no effect if HAVE_CUDA flag is not set.
|
||||
*/
|
||||
void vosk_gpu_thread_init();
|
||||
|
||||
/** Creates the batch recognizer object
|
||||
*
|
||||
* @returns model object or NULL if problem occured */
|
||||
VoskBatchModel *vosk_batch_model_new(const char *model_path);
|
||||
|
||||
/** Releases batch model object */
|
||||
void vosk_batch_model_free(VoskBatchModel *model);
|
||||
|
||||
/** Wait for the processing */
|
||||
void vosk_batch_model_wait(VoskBatchModel *model);
|
||||
|
||||
/** Creates batch recognizer object
|
||||
* @returns recognizer object or NULL if problem occured */
|
||||
VoskBatchRecognizer *vosk_batch_recognizer_new(VoskBatchModel *model, float sample_rate);
|
||||
|
||||
/** Releases batch recognizer object */
|
||||
void vosk_batch_recognizer_free(VoskBatchRecognizer *recognizer);
|
||||
|
||||
/** Accept batch voice data */
|
||||
void vosk_batch_recognizer_accept_waveform(VoskBatchRecognizer *recognizer, const char *data, int length);
|
||||
|
||||
/** Set NLSML output
|
||||
* @param nlsml - boolean value
|
||||
*/
|
||||
void vosk_batch_recognizer_set_nlsml(VoskBatchRecognizer *recognizer, int nlsml);
|
||||
|
||||
/** Closes the stream */
|
||||
void vosk_batch_recognizer_finish_stream(VoskBatchRecognizer *recognizer);
|
||||
|
||||
/** Return results */
|
||||
const char *vosk_batch_recognizer_front_result(VoskBatchRecognizer *recognizer);
|
||||
|
||||
/** Release and free first retrieved result */
|
||||
void vosk_batch_recognizer_pop(VoskBatchRecognizer *recognizer);
|
||||
|
||||
/** Get amount of pending chunks for more intelligent waiting */
|
||||
int vosk_batch_recognizer_get_pending_chunks(VoskBatchRecognizer *recognizer);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VOSK_API_H */
|
||||
@@ -55,5 +55,5 @@ Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
|
||||
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
|
||||
|
||||
[Run]
|
||||
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
|
||||
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
|
||||
find_package(${QT_VERSION} COMPONENTS Gui Widgets Core REQUIRED)
|
||||
find_package(Qt6 COMPONENTS Gui Widgets Core REQUIRED)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(SDL2 REQUIRED sdl2)
|
||||
@@ -36,7 +36,7 @@ target_link_libraries(RMG-Audio
|
||||
${SDL2_LIBRARIES}
|
||||
${SPEEX_LIBRARIES}
|
||||
${SAMPLERATE_LIBRARIES}
|
||||
${QT_VERSION}::Gui ${QT_VERSION}::Widgets
|
||||
Qt6::Gui Qt6::Widgets
|
||||
)
|
||||
|
||||
target_include_directories(RMG-Audio PRIVATE
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
project(RMG-Core)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(SDL2 REQUIRED sdl2)
|
||||
@@ -24,6 +23,7 @@ set(RMG_CORE_SOURCES
|
||||
ConvertStringEncoding.cpp
|
||||
Settings/Settings.cpp
|
||||
SpeedLimiter.cpp
|
||||
SpeedFactor.cpp
|
||||
RomSettings.cpp
|
||||
Directories.cpp
|
||||
MediaLoader.cpp
|
||||
@@ -67,7 +67,7 @@ if (PORTABLE_INSTALL)
|
||||
add_definitions(-DPORTABLE_INSTALL)
|
||||
endif(PORTABLE_INSTALL)
|
||||
|
||||
add_library(RMG-Core STATIC ${RMG_CORE_SOURCES})
|
||||
add_library(RMG-Core SHARED ${RMG_CORE_SOURCES})
|
||||
|
||||
if(UNIX)
|
||||
target_link_libraries(RMG-Core dl)
|
||||
@@ -79,6 +79,7 @@ endif(WIN32)
|
||||
|
||||
target_link_libraries(RMG-Core
|
||||
${MINIZIP_LIBRARIES}
|
||||
7Zip
|
||||
)
|
||||
|
||||
if (DISCORD_RPC)
|
||||
|
||||
@@ -36,11 +36,13 @@
|
||||
#define ROMHEADER_NAME_LEN 256
|
||||
#define GOODNAME_LEN 256
|
||||
#define MD5_LEN 33
|
||||
#define GAMEID_LEN 5
|
||||
#define REGION_LEN 18
|
||||
|
||||
#ifdef _WIN32
|
||||
#define CACHE_FILE_MAGIC "RMGCoreHeaderAndSettingsCacheWindows_04"
|
||||
#define CACHE_FILE_MAGIC "RMGCoreHeaderAndSettingsCacheWindows_06"
|
||||
#else // Linux
|
||||
#define CACHE_FILE_MAGIC "RMGCoreHeaderAndSettingsCacheLinux_04"
|
||||
#define CACHE_FILE_MAGIC "RMGCoreHeaderAndSettingsCacheLinux_06"
|
||||
#endif // _WIN32
|
||||
#define CACHE_FILE_ITEMS_MAX 10000
|
||||
|
||||
@@ -53,6 +55,7 @@ struct l_CacheEntry
|
||||
std::filesystem::path fileName;
|
||||
osal_files_file_time fileTime;
|
||||
|
||||
CoreRomType type;
|
||||
CoreRomHeader header;
|
||||
CoreRomSettings settings;
|
||||
};
|
||||
@@ -102,6 +105,8 @@ void CoreReadRomHeaderAndSettingsCache(void)
|
||||
char magicBuf[sizeof(CACHE_FILE_MAGIC)];
|
||||
wchar_t fileNameBuf[MAX_FILENAME_LEN];
|
||||
char headerNameBuf[ROMHEADER_NAME_LEN];
|
||||
char gameIDBuf[GAMEID_LEN];
|
||||
char regionBuf[REGION_LEN];
|
||||
char goodNameBuf[GOODNAME_LEN];
|
||||
char md5Buf[MD5_LEN];
|
||||
uint32_t size;
|
||||
@@ -130,6 +135,8 @@ void CoreReadRomHeaderAndSettingsCache(void)
|
||||
size = 0;
|
||||
memset(fileNameBuf, 0, sizeof(fileNameBuf));
|
||||
memset(headerNameBuf, 0, sizeof(headerNameBuf));
|
||||
memset(gameIDBuf, 0, sizeof(gameIDBuf));
|
||||
memset(regionBuf, 0, sizeof(regionBuf));
|
||||
memset(goodNameBuf, 0, sizeof(goodNameBuf));
|
||||
memset(md5Buf, 0, sizeof(md5Buf));
|
||||
|
||||
@@ -138,10 +145,18 @@ void CoreReadRomHeaderAndSettingsCache(void)
|
||||
FREAD_STR(fileNameBuf, size);
|
||||
cacheEntry.fileName = std::filesystem::path(fileNameBuf);
|
||||
FREAD(cacheEntry.fileTime);
|
||||
// type
|
||||
FREAD(cacheEntry.type);
|
||||
// header
|
||||
FREAD(size);
|
||||
FREAD_STR(headerNameBuf, size);
|
||||
FREAD(size);
|
||||
FREAD_STR(gameIDBuf, size);
|
||||
FREAD(size);
|
||||
FREAD_STR(regionBuf, size);
|
||||
cacheEntry.header.Name = std::string(headerNameBuf);
|
||||
cacheEntry.header.GameID = std::string(gameIDBuf);
|
||||
cacheEntry.header.Region = std::string(regionBuf);
|
||||
FREAD(cacheEntry.header.CRC1);
|
||||
FREAD(cacheEntry.header.CRC2);
|
||||
// (partial) settings
|
||||
@@ -166,6 +181,8 @@ bool CoreSaveRomHeaderAndSettingsCache(void)
|
||||
std::ofstream outputStream;
|
||||
wchar_t fileNameBuf[MAX_FILENAME_LEN];
|
||||
char headerNameBuf[ROMHEADER_NAME_LEN];
|
||||
char gameIDBuf[GAMEID_LEN];
|
||||
char regionBuf[REGION_LEN];
|
||||
char goodNameBuf[GOODNAME_LEN];
|
||||
char md5Buf[MD5_LEN];
|
||||
uint32_t size;
|
||||
@@ -197,12 +214,16 @@ bool CoreSaveRomHeaderAndSettingsCache(void)
|
||||
size = 0;
|
||||
memset(fileNameBuf, 0, sizeof(fileNameBuf));
|
||||
memset(headerNameBuf, 0, sizeof(headerNameBuf));
|
||||
memset(gameIDBuf, 0, sizeof(gameIDBuf));
|
||||
memset(regionBuf, 0, sizeof(regionBuf));
|
||||
memset(goodNameBuf, 0, sizeof(goodNameBuf));
|
||||
memset(md5Buf, 0, sizeof(md5Buf));
|
||||
|
||||
// copy strings into buffers
|
||||
wcsncpy(fileNameBuf, cacheEntry.fileName.wstring().c_str(), MAX_FILENAME_LEN);
|
||||
strncpy(headerNameBuf, cacheEntry.header.Name.c_str(), sizeof(headerNameBuf));
|
||||
strncpy(gameIDBuf, cacheEntry.header.GameID.c_str(), sizeof(gameIDBuf));
|
||||
strncpy(regionBuf, cacheEntry.header.Region.c_str(), sizeof(regionBuf));
|
||||
strncpy(goodNameBuf, cacheEntry.settings.GoodName.c_str(), sizeof(goodNameBuf));
|
||||
strncpy(md5Buf, cacheEntry.settings.MD5.c_str(), sizeof(md5Buf));
|
||||
|
||||
@@ -211,10 +232,18 @@ bool CoreSaveRomHeaderAndSettingsCache(void)
|
||||
FWRITE(size);
|
||||
FWRITE_STR(fileNameBuf, size);
|
||||
FWRITE(cacheEntry.fileTime);
|
||||
// type
|
||||
FWRITE(cacheEntry.type);
|
||||
// header
|
||||
size = cacheEntry.header.Name.size();
|
||||
FWRITE(size);
|
||||
FWRITE_STR(headerNameBuf, size);
|
||||
size = cacheEntry.header.GameID.size();
|
||||
FWRITE(size);
|
||||
FWRITE_STR(gameIDBuf, size);
|
||||
size = cacheEntry.header.Region.size();
|
||||
FWRITE(size);
|
||||
FWRITE_STR(regionBuf, size);
|
||||
FWRITE(cacheEntry.header.CRC1);
|
||||
FWRITE(cacheEntry.header.CRC2);
|
||||
// (partial) settings
|
||||
@@ -237,7 +266,7 @@ bool CoreHasRomHeaderAndSettingsCached(std::filesystem::path file)
|
||||
return get_cache_entry_iter(file) != l_CacheEntries.end();
|
||||
}
|
||||
|
||||
bool CoreGetCachedRomHeaderAndSettings(std::filesystem::path file, CoreRomHeader& header, CoreRomSettings& settings)
|
||||
bool CoreGetCachedRomHeaderAndSettings(std::filesystem::path file, CoreRomType& type, CoreRomHeader& header, CoreRomSettings& settings)
|
||||
{
|
||||
auto iter = get_cache_entry_iter(file);
|
||||
if (iter == l_CacheEntries.end())
|
||||
@@ -245,12 +274,13 @@ bool CoreGetCachedRomHeaderAndSettings(std::filesystem::path file, CoreRomHeader
|
||||
return false;
|
||||
}
|
||||
|
||||
type = (*iter).type;
|
||||
header = (*iter).header;
|
||||
settings = (*iter).settings;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CoreAddCachedRomHeaderAndSettings(std::filesystem::path file, CoreRomHeader header, CoreRomSettings settings)
|
||||
bool CoreAddCachedRomHeaderAndSettings(std::filesystem::path file, CoreRomType type, CoreRomHeader header, CoreRomSettings settings)
|
||||
{
|
||||
l_CacheEntry cacheEntry;
|
||||
|
||||
@@ -268,6 +298,7 @@ bool CoreAddCachedRomHeaderAndSettings(std::filesystem::path file, CoreRomHeader
|
||||
|
||||
cacheEntry.fileName = file;
|
||||
cacheEntry.fileTime = osal_files_get_file_time(file);
|
||||
cacheEntry.type = type;
|
||||
cacheEntry.header = header;
|
||||
cacheEntry.settings = settings;
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#include "Rom.hpp"
|
||||
#include "RomHeader.hpp"
|
||||
#include "RomSettings.hpp"
|
||||
|
||||
@@ -30,11 +31,11 @@ bool CoreHasRomHeaderAndSettingsCached(std::filesystem::path file);
|
||||
|
||||
// returns whether retrieving the rom header & settings
|
||||
// for given filename succeeds
|
||||
bool CoreGetCachedRomHeaderAndSettings(std::filesystem::path file, CoreRomHeader& header, CoreRomSettings& settings);
|
||||
bool CoreGetCachedRomHeaderAndSettings(std::filesystem::path file, CoreRomType& type, CoreRomHeader& header, CoreRomSettings& settings);
|
||||
|
||||
// returns whether adding cached rom header & settings
|
||||
// for given filename succeeds
|
||||
bool CoreAddCachedRomHeaderAndSettings(std::filesystem::path file, CoreRomHeader header, CoreRomSettings settings);
|
||||
bool CoreAddCachedRomHeaderAndSettings(std::filesystem::path file, CoreRomType type, CoreRomHeader header, CoreRomSettings settings);
|
||||
|
||||
// returns whether clearing rom header & settings cache
|
||||
// succeeds
|
||||
|
||||
@@ -11,12 +11,20 @@
|
||||
#include "Core.hpp"
|
||||
#include "ConvertStringEncoding.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
//
|
||||
// Local Variables
|
||||
//
|
||||
|
||||
static bool l_SetupCallbacks = false;
|
||||
static std::function<void(enum CoreDebugMessageType, std::string, std::string)> l_DebugCallbackFunc;
|
||||
static std::function<void(enum CoreStateCallbackType, int)> l_StateCallbackFunc;
|
||||
static bool l_PrintCallbacks = false;
|
||||
|
||||
static bool l_SetupDebugMessageCallback = false;
|
||||
static std::function<void(void*, int, const char*)> l_DebugMessageCallbackFunc;
|
||||
static void* l_DebugMessageCallbackFuncContext;
|
||||
|
||||
//
|
||||
// Internal Functions
|
||||
@@ -24,14 +32,19 @@ static std::function<void(enum CoreDebugMessageType, std::string, std::string)>
|
||||
|
||||
void CoreDebugCallback(void* context, int level, const char* message)
|
||||
{
|
||||
std::string contextString((const char*)context);
|
||||
std::string messageString(message);
|
||||
|
||||
if (l_PrintCallbacks)
|
||||
{
|
||||
std::cout << contextString << messageString << std::endl;
|
||||
}
|
||||
|
||||
if (!l_SetupCallbacks)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::string contextString((const char*)context);
|
||||
std::string messageString(message);
|
||||
|
||||
// convert string encoding accordingly
|
||||
if (messageString.starts_with("IS64:"))
|
||||
{
|
||||
@@ -47,16 +60,45 @@ void CoreDebugCallback(void* context, int level, const char* message)
|
||||
|
||||
void CoreStateCallback(void* context, m64p_core_param param, int value)
|
||||
{
|
||||
// TODO
|
||||
if (!l_SetupCallbacks)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
l_StateCallbackFunc((CoreStateCallbackType)param, value);
|
||||
}
|
||||
|
||||
//
|
||||
// Exported Functions
|
||||
//
|
||||
|
||||
bool CoreSetupCallbacks(std::function<void(enum CoreDebugMessageType, std::string, std::string)> debugCallbackFunc)
|
||||
bool CoreSetupCallbacks(std::function<void(enum CoreDebugMessageType, std::string, std::string)> debugCallbackFunc,
|
||||
std::function<void(enum CoreStateCallbackType, int)> stateCallbackFunc)
|
||||
{
|
||||
l_DebugCallbackFunc = debugCallbackFunc;
|
||||
l_StateCallbackFunc = stateCallbackFunc;
|
||||
l_SetupCallbacks = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CoreSetPrintDebugCallback(bool enabled)
|
||||
{
|
||||
l_PrintCallbacks = enabled;
|
||||
}
|
||||
|
||||
void CoreSetupDebugCallbackMessage(std::function<void(void*, int, const char*)> debugCallbackFunc, void* debugCallbackFuncContext)
|
||||
{
|
||||
l_DebugMessageCallbackFunc = debugCallbackFunc;
|
||||
l_DebugMessageCallbackFuncContext = debugCallbackFuncContext;
|
||||
l_SetupDebugMessageCallback = true;
|
||||
}
|
||||
|
||||
void CoreDebugCallbackMessage(CoreDebugMessageType type, std::string message)
|
||||
{
|
||||
if (!l_SetupDebugMessageCallback)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
l_DebugMessageCallbackFunc(l_DebugMessageCallbackFuncContext, (int)type, message.c_str());
|
||||
}
|
||||
|
||||
@@ -32,6 +32,34 @@ enum class CoreDebugMessageType
|
||||
Verbose = 5
|
||||
};
|
||||
|
||||
bool CoreSetupCallbacks(std::function<void(enum CoreDebugMessageType, std::string, std::string)> debugCallbackFunc);
|
||||
enum class CoreStateCallbackType
|
||||
{
|
||||
EmulationState = 1,
|
||||
VideoMode,
|
||||
SaveStateSlot,
|
||||
SpeedFactor,
|
||||
SpeedLimiter,
|
||||
VideoSize,
|
||||
AudioVolume,
|
||||
AudioMute,
|
||||
GamesharkInput,
|
||||
SaveStateLoaded,
|
||||
SaveStateSaved,
|
||||
};
|
||||
|
||||
// attempts to setup callbacks with the provided functions
|
||||
bool CoreSetupCallbacks(std::function<void(enum CoreDebugMessageType, std::string, std::string)> debugCallbackFunc,
|
||||
std::function<void(enum CoreStateCallbackType, int)> stateCallbackFunc);
|
||||
|
||||
// sets whether the debug callbacks will be printed to stdout
|
||||
void CoreSetPrintDebugCallback(bool enabled);
|
||||
|
||||
#ifdef CORE_PLUGIN
|
||||
// attempts to setup up the debug message debug callback
|
||||
void CoreSetupDebugCallbackMessage(std::function<void(void*, int, const char*)> debugCallbackFunc, void* debugCallbackFuncContext);
|
||||
|
||||
// sends a debug message to the debug callback (if it exists)
|
||||
void CoreDebugCallbackMessage(CoreDebugMessageType type, std::string message);
|
||||
#endif // CORE_PLUGIN
|
||||
|
||||
#endif // CORE_CALLBACK_HPP
|
||||
|
||||
@@ -118,9 +118,16 @@ static std::filesystem::path get_shared_cheat_file_path(CoreRomHeader romHeader,
|
||||
|
||||
static std::filesystem::path get_user_cheat_file_path(CoreRomHeader romHeader, CoreRomSettings romSettings)
|
||||
{
|
||||
std::filesystem::path oldCheatFilePath;
|
||||
std::filesystem::path cheatFilePath;
|
||||
|
||||
cheatFilePath = CoreGetUserDataDirectory();
|
||||
oldCheatFilePath = CoreGetUserDataDirectory();
|
||||
oldCheatFilePath += OSAL_FILES_DIR_SEPERATOR_STR;
|
||||
oldCheatFilePath += "Cheats-User";
|
||||
oldCheatFilePath += OSAL_FILES_DIR_SEPERATOR_STR;
|
||||
oldCheatFilePath += get_cheat_file_name(romHeader, romSettings);
|
||||
|
||||
cheatFilePath = CoreGetUserConfigDirectory();
|
||||
cheatFilePath += OSAL_FILES_DIR_SEPERATOR_STR;
|
||||
cheatFilePath += "Cheats-User";
|
||||
cheatFilePath += OSAL_FILES_DIR_SEPERATOR_STR;
|
||||
@@ -140,6 +147,12 @@ static std::filesystem::path get_user_cheat_file_path(CoreRomHeader romHeader, C
|
||||
// we'll fail later...
|
||||
}
|
||||
|
||||
// keep compatability with <v0.4.1
|
||||
if (std::filesystem::is_regular_file(oldCheatFilePath))
|
||||
{
|
||||
return oldCheatFilePath;
|
||||
}
|
||||
|
||||
return cheatFilePath;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#define CORE_VERSION "${GIT_VERSION}"
|
||||
|
||||
#ifndef PORTABLE_INSTALL
|
||||
#define CORE_INSTAlL_PREFIX "${CMAKE_INSTALL_PREFIX}"
|
||||
#define CORE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}"
|
||||
#endif // PORTABLE_INSTALL
|
||||
|
||||
#endif // CORE_CONFIG_HPP
|
||||
|
||||
@@ -30,18 +30,19 @@ static char l_CoreContextString[20];
|
||||
// Local Functions
|
||||
//
|
||||
|
||||
std::string find_core_lib(void)
|
||||
std::filesystem::path find_core_lib(void)
|
||||
{
|
||||
for (const auto& entry : std::filesystem::recursive_directory_iterator(CoreGetCoreDirectory()))
|
||||
{
|
||||
std::string path = entry.path().string();
|
||||
if (path.ends_with(OSAL_DYNLIB_LIB_EXT_STR))
|
||||
std::filesystem::path path = entry.path();
|
||||
if (path.has_extension() &&
|
||||
path.extension() == OSAL_DYNLIB_LIB_EXT_STR)
|
||||
{
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
return std::string();
|
||||
return std::filesystem::path();
|
||||
}
|
||||
|
||||
bool config_override_user_dirs(void)
|
||||
@@ -78,9 +79,9 @@ bool config_override_user_dirs(void)
|
||||
|
||||
bool CoreInit(void)
|
||||
{
|
||||
std::string error;
|
||||
std::string core_file;
|
||||
m64p_error m64p_ret;
|
||||
std::string error;
|
||||
std::filesystem::path core_file;
|
||||
m64p_error m64p_ret;
|
||||
bool ret = false;
|
||||
|
||||
// initialize context string
|
||||
@@ -94,7 +95,7 @@ bool CoreInit(void)
|
||||
return false;
|
||||
}
|
||||
|
||||
l_CoreLibHandle = osal_dynlib_open(core_file.c_str());
|
||||
l_CoreLibHandle = osal_dynlib_open(core_file);
|
||||
if (l_CoreLibHandle == nullptr)
|
||||
{
|
||||
error = "osal_dynlib_open Failed: ";
|
||||
@@ -173,7 +174,7 @@ bool CoreInit(m64p_dynlib_handle handle)
|
||||
std::string error;
|
||||
bool ret = false;
|
||||
|
||||
ret = m64p::Core.Hook(handle);
|
||||
ret = m64p::Core.IsHooked() || m64p::Core.Hook(handle);
|
||||
if (!ret)
|
||||
{
|
||||
error = m64p::Core.GetLastError();
|
||||
@@ -181,7 +182,7 @@ bool CoreInit(m64p_dynlib_handle handle)
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = m64p::Config.Hook(handle);
|
||||
ret = m64p::Config.IsHooked() || m64p::Config.Hook(handle);
|
||||
if (!ret)
|
||||
{
|
||||
error = m64p::Config.GetLastError();
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "CachedRomHeaderAndSettings.hpp"
|
||||
#include "Settings/Settings.hpp"
|
||||
#include "SpeedLimiter.hpp"
|
||||
#include "SpeedFactor.hpp"
|
||||
#include "RomSettings.hpp"
|
||||
#include "Directories.hpp"
|
||||
#include "MediaLoader.hpp"
|
||||
|
||||
@@ -18,6 +18,15 @@
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
|
||||
//
|
||||
// Local Variables
|
||||
//
|
||||
|
||||
static std::filesystem::path l_LibraryPathOverride;
|
||||
static std::filesystem::path l_CorePathOverride;
|
||||
static std::filesystem::path l_PluginPathOverride;
|
||||
static std::filesystem::path l_SharedDataPathOverride;
|
||||
|
||||
//
|
||||
// Local Functions
|
||||
//
|
||||
@@ -117,14 +126,40 @@ bool CoreCreateDirectories(void)
|
||||
return true;
|
||||
}
|
||||
|
||||
std::filesystem::path CoreGetLibraryDirectory(void)
|
||||
{
|
||||
std::filesystem::path directory;
|
||||
#ifdef PORTABLE_INSTALL
|
||||
directory = ".";
|
||||
#else // Not Portable
|
||||
if (!l_LibraryPathOverride.empty())
|
||||
{
|
||||
directory = l_LibraryPathOverride;
|
||||
}
|
||||
else
|
||||
{
|
||||
directory = CORE_INSTALL_PREFIX;
|
||||
directory += "/lib/RMG/";
|
||||
}
|
||||
#endif // PORTABLE_INSTALL
|
||||
return directory;
|
||||
}
|
||||
|
||||
std::filesystem::path CoreGetCoreDirectory(void)
|
||||
{
|
||||
std::filesystem::path directory;
|
||||
#ifdef PORTABLE_INSTALL
|
||||
directory = "Core";
|
||||
#else // Not Portable
|
||||
directory = CORE_INSTAlL_PREFIX;
|
||||
directory += "/lib/RMG/Core";
|
||||
if (!l_CorePathOverride.empty())
|
||||
{
|
||||
directory = l_CorePathOverride;
|
||||
}
|
||||
else
|
||||
{
|
||||
directory = CORE_INSTALL_PREFIX;
|
||||
directory += "/lib/RMG/Core";
|
||||
}
|
||||
#endif // PORTABLE_INSTALL
|
||||
return directory;
|
||||
}
|
||||
@@ -135,8 +170,15 @@ std::filesystem::path CoreGetPluginDirectory(void)
|
||||
#ifdef PORTABLE_INSTALL
|
||||
directory = "Plugin";
|
||||
#else // Not Portable
|
||||
directory = CORE_INSTAlL_PREFIX;
|
||||
directory += "/lib/RMG/Plugin";
|
||||
if (!l_PluginPathOverride.empty())
|
||||
{
|
||||
directory = l_PluginPathOverride;
|
||||
}
|
||||
else
|
||||
{
|
||||
directory = CORE_INSTALL_PREFIX;
|
||||
directory += "/lib/RMG/Plugin";
|
||||
}
|
||||
#endif // PORTABLE_INSTALL
|
||||
return directory;
|
||||
}
|
||||
@@ -233,8 +275,15 @@ std::filesystem::path CoreGetSharedDataDirectory(void)
|
||||
#ifdef PORTABLE_INSTALL
|
||||
directory = "Data";
|
||||
#else // Not Portable
|
||||
directory = CORE_INSTAlL_PREFIX;
|
||||
directory += "/share/RMG";
|
||||
if (!l_SharedDataPathOverride.empty())
|
||||
{
|
||||
directory = l_SharedDataPathOverride;
|
||||
}
|
||||
else
|
||||
{
|
||||
directory = CORE_INSTALL_PREFIX;
|
||||
directory += "/share/RMG";
|
||||
}
|
||||
#endif // PORTABLE_INSTALL
|
||||
return directory;
|
||||
}
|
||||
@@ -253,3 +302,25 @@ std::filesystem::path CoreGetScreenshotDirectory(void)
|
||||
{
|
||||
return CoreSettingsGetStringValue(SettingsID::Core_ScreenshotPath);
|
||||
}
|
||||
|
||||
#ifndef PORTABLE_INSTALL
|
||||
void CoreSetLibraryPathOverride(std::filesystem::path path)
|
||||
{
|
||||
l_LibraryPathOverride = path;
|
||||
}
|
||||
|
||||
void CoreSetCorePathOverride(std::filesystem::path path)
|
||||
{
|
||||
l_CorePathOverride = path;
|
||||
}
|
||||
|
||||
void CoreSetPluginPathOverride(std::filesystem::path path)
|
||||
{
|
||||
l_PluginPathOverride = path;
|
||||
}
|
||||
|
||||
void CoreSetSharedDataPathOverride(std::filesystem::path path)
|
||||
{
|
||||
l_SharedDataPathOverride = path;
|
||||
}
|
||||
#endif // PORTABLE_INSTALL
|
||||
|
||||
@@ -21,6 +21,10 @@ bool CoreCreateDirectories(void);
|
||||
// located
|
||||
std::filesystem::path CoreGetCoreDirectory(void);
|
||||
|
||||
// returns the library directory where
|
||||
// misc libraries should be located
|
||||
std::filesystem::path CoreGetLibraryDirectory(void);
|
||||
|
||||
// returns the plugin directory where
|
||||
// plugins should be located
|
||||
std::filesystem::path CoreGetPluginDirectory(void);
|
||||
@@ -73,4 +77,16 @@ std::filesystem::path CoreGetSaveStateDirectory(void);
|
||||
// screenshots should be located
|
||||
std::filesystem::path CoreGetScreenshotDirectory(void);
|
||||
|
||||
// changes the library path to the given path
|
||||
void CoreSetLibraryPathOverride(std::filesystem::path path);
|
||||
|
||||
// changes the core path to the given path
|
||||
void CoreSetCorePathOverride(std::filesystem::path path);
|
||||
|
||||
// changes the plugin path to the given path
|
||||
void CoreSetPluginPathOverride(std::filesystem::path path);
|
||||
|
||||
// changes the shared data path to the given path
|
||||
void CoreSetSharedDataPathOverride(std::filesystem::path path);
|
||||
|
||||
#endif // DIRECTORIES_HPP
|
||||
|
||||
@@ -54,6 +54,7 @@ static void apply_coresettings_overlay(void)
|
||||
CoreSettingsSetValue(SettingsID::Core_CountPerOp, CoreSettingsGetIntValue(SettingsID::CoreOverlay_CountPerOp));
|
||||
CoreSettingsSetValue(SettingsID::Core_CountPerOpDenomPot, CoreSettingsGetIntValue(SettingsID::CoreOverlay_CountPerOpDenomPot));
|
||||
CoreSettingsSetValue(SettingsID::Core_SiDmaDuration, CoreSettingsGetIntValue(SettingsID::CoreOverlay_SiDmaDuration));
|
||||
CoreSettingsSetValue(SettingsID::Core_SaveFileNameFormat, CoreSettingsGetIntValue(SettingsID::CoreOverLay_SaveFileNameFormat));
|
||||
}
|
||||
|
||||
static void apply_game_coresettings_overlay(void)
|
||||
@@ -90,7 +91,8 @@ static void apply_game_coresettings_overlay(void)
|
||||
bool CoreStartEmulation(std::filesystem::path n64rom, std::filesystem::path n64ddrom)
|
||||
{
|
||||
std::string error;
|
||||
m64p_error ret;
|
||||
m64p_error ret;
|
||||
CoreRomType type;
|
||||
|
||||
if (!CoreOpenRom(n64rom))
|
||||
{
|
||||
@@ -126,8 +128,17 @@ bool CoreStartEmulation(std::filesystem::path n64rom, std::filesystem::path n64d
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CoreGetRomType(type))
|
||||
{
|
||||
CoreClearCheats();
|
||||
CoreDetachPlugins();
|
||||
CoreApplyPluginSettings();
|
||||
CoreCloseRom();
|
||||
return false;
|
||||
}
|
||||
|
||||
// set disk file in media loader when ROM is a cartridge
|
||||
if (CoreGetRomType() == CoreRomType::Cartridge)
|
||||
if (type == CoreRomType::Cartridge)
|
||||
{
|
||||
CoreMediaLoaderSetDiskFile(n64ddrom);
|
||||
}
|
||||
@@ -147,7 +158,6 @@ bool CoreStartEmulation(std::filesystem::path n64rom, std::filesystem::path n64d
|
||||
{
|
||||
error = "CoreStartEmulation m64p::Core.DoCommand(M64CMD_EXECUTE) Failed: ";
|
||||
error += m64p::Core.ErrorMessage(ret);
|
||||
CoreSetError(error);
|
||||
}
|
||||
|
||||
CoreClearCheats();
|
||||
@@ -157,10 +167,18 @@ bool CoreStartEmulation(std::filesystem::path n64rom, std::filesystem::path n64d
|
||||
// restore plugin settings
|
||||
CoreApplyPluginSettings();
|
||||
|
||||
// reset media loader state
|
||||
CoreResetMediaLoader();
|
||||
|
||||
#ifdef DISCORD_RPC
|
||||
CoreDiscordRpcUpdate(false);
|
||||
#endif // DISCORD_RPC
|
||||
|
||||
// we need to set the emulation error last,
|
||||
// to prevent the other functions from
|
||||
// overriding the emulation error
|
||||
CoreSetError(error);
|
||||
|
||||
return ret == M64ERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,13 @@
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
enum class CoreEmulationState
|
||||
{
|
||||
Stopped = 1,
|
||||
Running,
|
||||
Paused
|
||||
};
|
||||
|
||||
// starts emulation with given ROM
|
||||
bool CoreStartEmulation(std::filesystem::path n64rom, std::filesystem::path n64ddrom);
|
||||
|
||||
|
||||
@@ -139,7 +139,12 @@ bool CoreSetupMediaLoader(void)
|
||||
return ret == M64ERR_SUCCESS;
|
||||
}
|
||||
|
||||
void CoreMediaLoaderSetDiskFile(std::filesystem::path ddRom)
|
||||
void CoreResetMediaLoader(void)
|
||||
{
|
||||
l_DdDiskFile = ddRom;
|
||||
l_DdRomFile = "";
|
||||
}
|
||||
|
||||
void CoreMediaLoaderSetDiskFile(std::filesystem::path disk)
|
||||
{
|
||||
l_DdDiskFile = disk;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,10 @@
|
||||
// sets up the medialoader
|
||||
bool CoreSetupMediaLoader(void);
|
||||
|
||||
// resets media loader state
|
||||
void CoreResetMediaLoader(void);
|
||||
|
||||
// sets the media loader disk file
|
||||
void CoreMediaLoaderSetDiskFile(std::filesystem::path ddRom);
|
||||
void CoreMediaLoaderSetDiskFile(std::filesystem::path disk);
|
||||
|
||||
#endif // CORE_MEDIALOADER_HPP
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
#include "RomSettings.hpp"
|
||||
#include "Settings/Settings.hpp"
|
||||
|
||||
#include "m64p/PluginApi.hpp"
|
||||
#include "osal/osal_dynlib.hpp"
|
||||
#include "osal/osal_files.hpp"
|
||||
|
||||
#include "m64p/PluginApi.cpp"
|
||||
#include "m64p/PluginApi.hpp"
|
||||
#include "m64p/Api.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
@@ -131,6 +131,54 @@ std::string get_plugin_context_name(CorePluginType type)
|
||||
return name;
|
||||
}
|
||||
|
||||
std::string get_plugin_path(CorePluginType type, std::string settingsValue)
|
||||
{
|
||||
std::string pluginPath;
|
||||
std::string path;
|
||||
std::string typeName;
|
||||
|
||||
// return an empty string when the value is empty
|
||||
if (settingsValue.empty())
|
||||
{
|
||||
return std::string();
|
||||
}
|
||||
|
||||
pluginPath = CoreGetPluginDirectory().string();
|
||||
|
||||
// if the full plugin path is in the settings value,
|
||||
// we know it's the old type
|
||||
if (settingsValue.find(pluginPath) != std::string::npos)
|
||||
{
|
||||
return settingsValue;
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case CorePluginType::Rsp:
|
||||
typeName = "RSP";
|
||||
break;
|
||||
case CorePluginType::Gfx:
|
||||
typeName = "GFX";
|
||||
break;
|
||||
case CorePluginType::Audio:
|
||||
typeName = "Audio";
|
||||
break;
|
||||
case CorePluginType::Input:
|
||||
typeName = "Input";
|
||||
break;
|
||||
default:
|
||||
return path;
|
||||
}
|
||||
|
||||
path = pluginPath;
|
||||
path += OSAL_FILES_DIR_SEPERATOR_STR;
|
||||
path += typeName;
|
||||
path += OSAL_FILES_DIR_SEPERATOR_STR;
|
||||
path += settingsValue;
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
bool apply_plugin_settings(std::string pluginSettings[4])
|
||||
{
|
||||
std::string error;
|
||||
@@ -142,15 +190,14 @@ bool apply_plugin_settings(std::string pluginSettings[4])
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
settingValue = pluginSettings[i];
|
||||
pluginType = (CorePluginType)(i + 1);
|
||||
settingValue = get_plugin_path(pluginType, pluginSettings[i]);
|
||||
if (settingValue.empty() ||
|
||||
!std::filesystem::is_regular_file(settingValue))
|
||||
{ // skip invalid setting value
|
||||
continue;
|
||||
}
|
||||
|
||||
pluginType = (CorePluginType)(i + 1);
|
||||
|
||||
// copy context string to a c string using strcpy
|
||||
std::strcpy(l_PluginContext[(int)pluginType], get_plugin_context_name(pluginType).c_str());
|
||||
|
||||
@@ -237,6 +284,78 @@ bool apply_plugin_settings(std::string pluginSettings[4])
|
||||
return true;
|
||||
}
|
||||
|
||||
bool open_plugin_config(CorePluginType type, bool romConfig)
|
||||
{
|
||||
std::string error;
|
||||
m64p_error ret;
|
||||
bool resumeEmulation = false;
|
||||
m64p::PluginApi* plugin;
|
||||
std::string functionName;
|
||||
|
||||
if (!romConfig && !CorePluginsHasConfig(type))
|
||||
{
|
||||
error = "open_plugin_config Failed: ";
|
||||
error += get_plugin_type_name(type);
|
||||
error += " doesn't have the config or config2 function!";
|
||||
CoreSetError(error);
|
||||
return false;
|
||||
}
|
||||
else if (romConfig && !CorePluginsHasROMConfig(type))
|
||||
{
|
||||
error = "open_plugin_config Failed: ";
|
||||
error += get_plugin_type_name(type);
|
||||
error += " doesn't support ROM specific configuration!";
|
||||
CoreSetError(error);
|
||||
return false;
|
||||
}
|
||||
|
||||
// try to pause emulation,
|
||||
// when emulation is running
|
||||
// and try to resume afterwards
|
||||
if (CoreIsEmulationRunning())
|
||||
{
|
||||
// only resume emulation
|
||||
// after running the config function
|
||||
// when pausing succeeds
|
||||
resumeEmulation = CorePauseEmulation();
|
||||
}
|
||||
|
||||
plugin = get_plugin(type);
|
||||
|
||||
// check if the plugin has the Config2
|
||||
// or Config function, the Config2 function
|
||||
// has priority
|
||||
if (plugin->Config2 != nullptr)
|
||||
{
|
||||
ret = plugin->Config2(romConfig ? 1 : 0);
|
||||
functionName = "Config2";
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = plugin->Config();
|
||||
functionName = "Config";
|
||||
}
|
||||
|
||||
if (ret != M64ERR_SUCCESS)
|
||||
{
|
||||
error = "CorePluginsOpenConfig (";
|
||||
error += get_plugin_type_name(type);
|
||||
error += ")->";
|
||||
error += functionName;
|
||||
error += "() Failed: ";
|
||||
error += m64p::Core.ErrorMessage(ret);
|
||||
CoreSetError(error);
|
||||
}
|
||||
|
||||
// try to resume emulation when needed
|
||||
if (resumeEmulation)
|
||||
{
|
||||
CoreResumeEmulation();
|
||||
}
|
||||
|
||||
return ret == M64ERR_SUCCESS;
|
||||
}
|
||||
|
||||
//
|
||||
// Exported Functions
|
||||
//
|
||||
@@ -252,6 +371,7 @@ std::vector<CorePlugin> CoreGetAllPlugins(void)
|
||||
for (const auto& entry : std::filesystem::recursive_directory_iterator(CoreGetPluginDirectory()))
|
||||
{
|
||||
std::string path = entry.path().string();
|
||||
std::string file = entry.path().filename().string();
|
||||
if (!entry.is_directory() &&
|
||||
path.ends_with(OSAL_DYNLIB_LIB_EXT_STR))
|
||||
{
|
||||
@@ -272,7 +392,7 @@ std::vector<CorePlugin> CoreGetAllPlugins(void)
|
||||
continue;
|
||||
}
|
||||
|
||||
CorePlugin corePlugin = {path, plugin_name, plugin_type};
|
||||
CorePlugin corePlugin = {file, plugin_name, plugin_type};
|
||||
plugins.emplace_back(corePlugin);
|
||||
}
|
||||
}
|
||||
@@ -335,52 +455,33 @@ bool CoreArePluginsReady(void)
|
||||
|
||||
bool CorePluginsHasConfig(CorePluginType type)
|
||||
{
|
||||
return get_plugin(type)->Config != nullptr;
|
||||
m64p::PluginApi* plugin;
|
||||
|
||||
plugin = get_plugin(type);
|
||||
|
||||
return plugin->Config != nullptr ||
|
||||
plugin->Config2 != nullptr;
|
||||
}
|
||||
|
||||
bool CorePluginsOpenConfig(CorePluginType type)
|
||||
{
|
||||
std::string error;
|
||||
m64p_error ret;
|
||||
bool resumeEmulation = false;
|
||||
return open_plugin_config(type, false);
|
||||
}
|
||||
|
||||
if (!CorePluginsHasConfig(type))
|
||||
{
|
||||
error = "CorePluginsOpenConfig Failed: ";
|
||||
error += get_plugin_type_name(type);
|
||||
error += " doesn't have config function!";
|
||||
CoreSetError(error);
|
||||
return false;
|
||||
}
|
||||
bool CorePluginsHasROMConfig(CorePluginType type)
|
||||
{
|
||||
m64p::PluginApi* plugin;
|
||||
|
||||
// try to pause emulation,
|
||||
// when emulation is running
|
||||
// and try to resume afterwards
|
||||
if (CoreIsEmulationRunning())
|
||||
{
|
||||
// only resume emulation
|
||||
// after running the config function
|
||||
// when pausing succeeds
|
||||
resumeEmulation = CorePauseEmulation();
|
||||
}
|
||||
plugin = get_plugin(type);
|
||||
|
||||
ret = get_plugin(type)->Config();
|
||||
if (ret != M64ERR_SUCCESS)
|
||||
{
|
||||
error = "CorePluginsOpenConfig (";
|
||||
error += get_plugin_type_name(type);
|
||||
error += ")->Config() Failed: ";
|
||||
error += m64p::Core.ErrorMessage(ret);
|
||||
CoreSetError(error);
|
||||
}
|
||||
return plugin->Config2 != nullptr &&
|
||||
plugin->Config2HasRomConfig != nullptr &&
|
||||
plugin->Config2HasRomConfig() > 0;
|
||||
}
|
||||
|
||||
// try to resume emulation when needed
|
||||
if (resumeEmulation)
|
||||
{
|
||||
CoreResumeEmulation();
|
||||
}
|
||||
|
||||
return ret == M64ERR_SUCCESS;
|
||||
bool CorePluginsOpenROMConfig(CorePluginType type)
|
||||
{
|
||||
return open_plugin_config(type, true);
|
||||
}
|
||||
|
||||
bool CoreAttachPlugins(void)
|
||||
|
||||
@@ -52,6 +52,14 @@ bool CorePluginsHasConfig(CorePluginType type);
|
||||
// used plugin of given type
|
||||
bool CorePluginsOpenConfig(CorePluginType type);
|
||||
|
||||
// returns wether the currently used plugin
|
||||
// of the given type has a ROM config GUI
|
||||
bool CorePluginsHasROMConfig(CorePluginType type);
|
||||
|
||||
// opens the ROM config GUI for the currently
|
||||
// used plugin of given type
|
||||
bool CorePluginsOpenROMConfig(CorePluginType type);
|
||||
|
||||
// attaches all used plugins
|
||||
bool CoreAttachPlugins(void);
|
||||
|
||||
|
||||
@@ -8,12 +8,24 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "Rom.hpp"
|
||||
#include "Directories.hpp"
|
||||
#include "Error.hpp"
|
||||
#include "MediaLoader.hpp"
|
||||
#include "m64p/Api.hpp"
|
||||
#include "RomSettings.hpp"
|
||||
#include "Cheats.hpp"
|
||||
#include "osal/osal_files.hpp"
|
||||
|
||||
// 7-Zip includes
|
||||
#include "../3rdParty/7-Zip/C/7zTypes.h"
|
||||
#include "../3rdParty/7-Zip/C/7z.h"
|
||||
#include "../3rdParty/7-Zip/C/7zAlloc.h"
|
||||
#include "../3rdParty/7-Zip/C/7zBuf.h"
|
||||
#include "../3rdParty/7-Zip/C/7zCrc.h"
|
||||
#include "../3rdParty/7-Zip/C/7zFile.h"
|
||||
#include "../3rdParty/7-Zip/C/7zVersion.h"
|
||||
|
||||
#include <string>
|
||||
#include <unzip.h>
|
||||
#include <fstream>
|
||||
#include <cstdlib>
|
||||
@@ -30,8 +42,11 @@
|
||||
// Local Variables
|
||||
//
|
||||
|
||||
static bool l_HasRomOpen = false;
|
||||
static bool l_HasDisk = false;
|
||||
static bool l_HasRomOpen = false;
|
||||
static bool l_HasDisk = false;
|
||||
static bool l_HasExtractedDisk = false;
|
||||
static std::filesystem::path l_ExtractedDiskPath;
|
||||
static std::filesystem::path l_RomPath;
|
||||
|
||||
//
|
||||
// Local Functions
|
||||
@@ -51,25 +66,113 @@ static std::string to_lower_str(std::string str)
|
||||
return resultString;
|
||||
}
|
||||
|
||||
static bool read_zip_file(std::filesystem::path file, char** buf, int* size)
|
||||
static voidpf zlib_filefunc_open(voidpf opaque, const void* filename, int mode)
|
||||
{
|
||||
std::filesystem::path path = *(std::filesystem::path*)filename;
|
||||
|
||||
// we need a static filestream
|
||||
static std::ifstream fileStream;
|
||||
|
||||
// attempt to open file
|
||||
fileStream.open(path, std::ios::binary);
|
||||
if (!fileStream.is_open())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return (voidpf)&fileStream;
|
||||
}
|
||||
|
||||
static uLong zlib_filefunc_read(voidpf opaque, voidpf stream, void* buf, uLong size)
|
||||
{
|
||||
std::ifstream* fileStream = (std::ifstream*)stream;
|
||||
fileStream->read((char*)buf, size);
|
||||
return fileStream->fail() ? 0 : size;
|
||||
}
|
||||
|
||||
static ZPOS64_T zlib_filefunc_tell(voidpf opaque, voidpf stream)
|
||||
{
|
||||
std::ifstream* fileStream = (std::ifstream*)stream;
|
||||
return fileStream->tellg();
|
||||
}
|
||||
|
||||
static long zlib_filefunc_seek(voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)
|
||||
{
|
||||
std::ifstream* fileStream = (std::ifstream*)stream;
|
||||
std::ios_base::seekdir seekOrigin;
|
||||
|
||||
switch (origin)
|
||||
{
|
||||
default:
|
||||
return 0;
|
||||
case ZLIB_FILEFUNC_SEEK_CUR:
|
||||
seekOrigin = std::ios::cur;
|
||||
break;
|
||||
case ZLIB_FILEFUNC_SEEK_END:
|
||||
seekOrigin = std::ios::end;
|
||||
break;
|
||||
case ZLIB_FILEFUNC_SEEK_SET:
|
||||
seekOrigin = std::ios::beg;
|
||||
break;
|
||||
}
|
||||
|
||||
fileStream->seekg(offset, seekOrigin);
|
||||
return fileStream->fail() ? -1 : 0;
|
||||
}
|
||||
|
||||
static int zlib_filefunc_close(voidpf opaque, voidpf stream)
|
||||
{
|
||||
std::ifstream* fileStream = (std::ifstream*)stream;
|
||||
fileStream->close();
|
||||
return fileStream->fail() ? -1 : 0;
|
||||
}
|
||||
|
||||
static int zlib_filefunc_testerror(voidpf opaque, voidpf stream)
|
||||
{
|
||||
return errno;
|
||||
}
|
||||
|
||||
static bool read_zip_file(std::filesystem::path file, std::filesystem::path* extractedFileName, bool* isDisk, char** buf, int* size)
|
||||
{
|
||||
std::string error;
|
||||
std::fstream fileStream;
|
||||
int fileStreamLen;
|
||||
char* fileStreamBuf;
|
||||
|
||||
unzFile zipFile;
|
||||
unz_global_info zipInfo;
|
||||
unzFile zipFile;
|
||||
unz_global_info64 zipInfo;
|
||||
|
||||
zipFile = unzOpen(file.string().c_str());
|
||||
zlib_filefunc64_def filefuncs;
|
||||
filefuncs.zopen64_file = zlib_filefunc_open;
|
||||
filefuncs.zread_file = zlib_filefunc_read;
|
||||
filefuncs.zwrite_file = nullptr;
|
||||
filefuncs.ztell64_file = zlib_filefunc_tell;
|
||||
filefuncs.zseek64_file = zlib_filefunc_seek;
|
||||
filefuncs.zclose_file = zlib_filefunc_close;
|
||||
filefuncs.zerror_file = zlib_filefunc_testerror;
|
||||
filefuncs.opaque = nullptr;
|
||||
|
||||
zipFile = unzOpen2_64((const void*)&file, &filefuncs);
|
||||
if (zipFile == nullptr)
|
||||
{
|
||||
error = "read_zip_file: unzOpen Failed!";
|
||||
if (errno == 0)
|
||||
{
|
||||
error = "read_zip_file: unzOpen Failed!";
|
||||
}
|
||||
else
|
||||
{
|
||||
error = "read_zip_file: unzOpen Failed: ";
|
||||
error += "failed to open file: ";
|
||||
error += strerror(errno);
|
||||
error += " (";
|
||||
error += std::to_string(errno);
|
||||
error += ")";
|
||||
}
|
||||
CoreSetError(error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (unzGetGlobalInfo(zipFile, &zipInfo) != UNZ_OK)
|
||||
if (unzGetGlobalInfo64(zipFile, &zipInfo) != UNZ_OK)
|
||||
{
|
||||
error = "read_zip_file: unzGetGlobalInfo Failed!";
|
||||
CoreSetError(error);
|
||||
@@ -95,7 +198,9 @@ static bool read_zip_file(std::filesystem::path file, char** buf, int* size)
|
||||
fileExtension = to_lower_str(fileExtension);
|
||||
if (fileExtension == ".z64" ||
|
||||
fileExtension == ".v64" ||
|
||||
fileExtension == ".n64")
|
||||
fileExtension == ".n64" ||
|
||||
fileExtension == ".ndd" ||
|
||||
fileExtension == ".d64")
|
||||
{
|
||||
char* buffer;
|
||||
char* outBuffer;
|
||||
@@ -167,8 +272,10 @@ static bool read_zip_file(std::filesystem::path file, char** buf, int* size)
|
||||
}
|
||||
} while (bytes_read > 0);
|
||||
|
||||
*size = total_bytes_read;
|
||||
*buf = outBuffer;
|
||||
*size = total_bytes_read;
|
||||
*buf = outBuffer;
|
||||
*extractedFileName = fileNamePath;
|
||||
*isDisk = (fileExtension == ".ndd" || fileExtension == ".d64");
|
||||
unzCloseCurrentFile(zipFile);
|
||||
unzClose(zipFile);
|
||||
free(buffer);
|
||||
@@ -197,19 +304,172 @@ static bool read_zip_file(std::filesystem::path file, char** buf, int* size)
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool read_raw_file(std::filesystem::path file, char** buf, int* size)
|
||||
static bool read_7zip_file(std::filesystem::path file, std::filesystem::path* extractedFileName, bool* isDisk, char** buf, int* size)
|
||||
{
|
||||
std::string error;
|
||||
std::fstream fileStream;
|
||||
int fileStreamLen;
|
||||
char* fileStreamBuf;
|
||||
|
||||
const ISzAlloc alloc = { SzAlloc, SzFree };
|
||||
const size_t bufSize = ((size_t)1 << 18);
|
||||
|
||||
ISzAlloc allocImp;
|
||||
ISzAlloc allocTempImp;
|
||||
|
||||
CFileInStream archiveStream;
|
||||
CLookToRead2 lookStream;
|
||||
CSzArEx db;
|
||||
SRes res;
|
||||
|
||||
// initialize allocator
|
||||
allocImp = alloc;
|
||||
allocTempImp = alloc;
|
||||
|
||||
// try to open file
|
||||
#ifdef _WIN32
|
||||
WRes wres = InFile_OpenW(&archiveStream.file, file.wstring().c_str());
|
||||
#else
|
||||
WRes wres = InFile_Open(&archiveStream.file, file.string().c_str());
|
||||
#endif // _WIN32
|
||||
if (wres != 0)
|
||||
{
|
||||
error = "read_7zip_file Failed: InFile_Open Failed: ";
|
||||
error += std::to_string(wres);
|
||||
CoreSetError(error);
|
||||
return false;
|
||||
}
|
||||
|
||||
// create vtables for streams
|
||||
FileInStream_CreateVTable(&archiveStream);
|
||||
archiveStream.wres = 0;
|
||||
LookToRead2_CreateVTable(&lookStream, 0);
|
||||
lookStream.buf = nullptr;
|
||||
|
||||
// allocate memory look reader
|
||||
lookStream.buf = (Byte*)ISzAlloc_Alloc(&allocImp, bufSize);
|
||||
if (lookStream.buf == nullptr)
|
||||
{
|
||||
error = "read_7zip_file Failed: ISzAlloc_Alloc Failed!";
|
||||
CoreSetError(error);
|
||||
return false;
|
||||
}
|
||||
|
||||
// initialize look reader
|
||||
lookStream.bufSize = bufSize;
|
||||
lookStream.realStream = &archiveStream.vt;
|
||||
LookToRead2_INIT(&lookStream);
|
||||
|
||||
// initialize CRC table
|
||||
CrcGenerateTable();
|
||||
|
||||
// initialize archive
|
||||
SzArEx_Init(&db);
|
||||
|
||||
// try to open file
|
||||
res = SzArEx_Open(&db, &lookStream.vt, &allocImp, &allocTempImp);
|
||||
if (res != SZ_OK)
|
||||
{
|
||||
error = "read_7zip_file Failed: SzArEx_Open Failed: ";
|
||||
error += std::to_string(res);
|
||||
CoreSetError(error);
|
||||
SzArEx_Free(&db, &allocImp);
|
||||
ISzAlloc_Free(&allocImp, lookStream.buf);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < db.NumFiles; i++)
|
||||
{
|
||||
size_t filename_size = 0;
|
||||
uint16_t fileName[PATH_MAX];
|
||||
|
||||
// skip directories
|
||||
if (SzArEx_IsDir(&db, i))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip when filename size exceeds our buffer size
|
||||
filename_size = SzArEx_GetFileNameUtf16(&db, i, nullptr);
|
||||
if (filename_size > PATH_MAX)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
SzArEx_GetFileNameUtf16(&db, i, fileName);
|
||||
|
||||
std::filesystem::path fileNamePath((char16_t*)fileName);
|
||||
std::string fileExtension = fileNamePath.has_extension() ? fileNamePath.extension().string() : "";
|
||||
fileExtension = to_lower_str(fileExtension);
|
||||
if (fileExtension == ".z64" ||
|
||||
fileExtension == ".v64" ||
|
||||
fileExtension == ".n64" ||
|
||||
fileExtension == ".ndd" ||
|
||||
fileExtension == ".d64")
|
||||
{
|
||||
uint32_t blockIndex = 0xFFFFFFFF;
|
||||
uint8_t* outBuffer = nullptr;
|
||||
size_t outBufferSize = 0;
|
||||
size_t offset = 0;
|
||||
size_t outSizeProcessed = 0;
|
||||
|
||||
res = SzArEx_Extract(&db, &lookStream.vt, i,
|
||||
&blockIndex, &outBuffer, &outBufferSize,
|
||||
&offset, &outSizeProcessed,
|
||||
&allocImp, &allocTempImp);
|
||||
if (res != SZ_OK)
|
||||
{
|
||||
error = "read_7zip_file Failed: SzArEx_Extract Failed: ";
|
||||
error += std::to_string(res);
|
||||
CoreSetError(error);
|
||||
|
||||
SzArEx_Free(&db, &allocImp);
|
||||
ISzAlloc_Free(&allocImp, lookStream.buf);
|
||||
File_Close(&archiveStream.file);
|
||||
ISzAlloc_Free(&allocImp, outBuffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
*size = outSizeProcessed;
|
||||
// we have to memcpy it into buf, otherwise
|
||||
// the free() in CoreOpenRom() crashes
|
||||
*buf = (char*)malloc(outSizeProcessed);
|
||||
memcpy(*buf, (outBuffer + offset), outSizeProcessed);
|
||||
*extractedFileName = fileNamePath;
|
||||
*isDisk = (fileExtension == ".ndd" || fileExtension == ".d64");
|
||||
|
||||
SzArEx_Free(&db, &allocImp);
|
||||
ISzAlloc_Free(&allocImp, lookStream.buf);
|
||||
File_Close(&archiveStream.file);
|
||||
ISzAlloc_Free(&allocImp, outBuffer);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// cleanup
|
||||
SzArEx_Free(&db, &allocImp);
|
||||
ISzAlloc_Free(&allocImp, lookStream.buf);
|
||||
File_Close(&archiveStream.file);
|
||||
|
||||
error = "read_7zip_file Failed: no valid ROMs found in 7zip!";
|
||||
CoreSetError(error);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool read_raw_file(std::filesystem::path file, char** buf, int* size)
|
||||
{
|
||||
std::string error;
|
||||
std::ifstream fileStream;
|
||||
int fileStreamLen;
|
||||
char* fileStreamBuf;
|
||||
|
||||
// attempt to open file
|
||||
fileStream.open(file);
|
||||
fileStream.open(file, std::ios::binary);
|
||||
if (!fileStream.is_open())
|
||||
{
|
||||
error = "read_raw_file Failed: ";
|
||||
error += "failed to open file!";
|
||||
error += "failed to open file: ";
|
||||
error += strerror(errno);
|
||||
error += " (";
|
||||
error += std::to_string(errno);
|
||||
error += ")";
|
||||
CoreSetError(error);
|
||||
return false;
|
||||
}
|
||||
@@ -241,6 +501,31 @@ static bool read_raw_file(std::filesystem::path file, char** buf, int* size)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool write_file(std::filesystem::path file, char* buf, int size)
|
||||
{
|
||||
std::string error;
|
||||
std::ofstream fileStream;
|
||||
|
||||
// attempt to open file
|
||||
fileStream.open(file, std::ios::binary);
|
||||
if (!fileStream.is_open())
|
||||
{
|
||||
error = "write_file Failed: ";
|
||||
error += "failed to open file: ";
|
||||
error += strerror(errno);
|
||||
error += " (";
|
||||
error += std::to_string(errno);
|
||||
error += ")";
|
||||
CoreSetError(error);
|
||||
return false;
|
||||
}
|
||||
|
||||
// write buffer to file
|
||||
fileStream.write(buf, size);
|
||||
fileStream.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Exported Functions
|
||||
//
|
||||
@@ -249,8 +534,8 @@ bool CoreOpenRom(std::filesystem::path file)
|
||||
{
|
||||
std::string error;
|
||||
m64p_error ret;
|
||||
char* buf;
|
||||
int buf_size;
|
||||
char* buf = nullptr;
|
||||
int buf_size = 0;
|
||||
std::string file_extension;
|
||||
|
||||
if (!m64p::Core.IsHooked())
|
||||
@@ -269,20 +554,75 @@ bool CoreOpenRom(std::filesystem::path file)
|
||||
file_extension = file.has_extension() ? file.extension().string() : "";
|
||||
file_extension = to_lower_str(file_extension);
|
||||
|
||||
if (file_extension == ".zip")
|
||||
if (file_extension == ".zip" ||
|
||||
file_extension == ".7z")
|
||||
{
|
||||
if (!read_zip_file(file, &buf, &buf_size))
|
||||
std::filesystem::path extracted_file;
|
||||
bool is_disk = false;
|
||||
|
||||
if (file_extension == ".zip")
|
||||
{
|
||||
return false;
|
||||
if (!read_zip_file(file, &extracted_file, &is_disk, &buf, &buf_size))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!read_7zip_file(file, &extracted_file, &is_disk, &buf, &buf_size))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
l_HasDisk = false;
|
||||
if (is_disk)
|
||||
{
|
||||
std::filesystem::path disk_file;
|
||||
disk_file = CoreGetUserCacheDirectory();
|
||||
disk_file += "/extracted_disks/";
|
||||
disk_file += extracted_file.filename();
|
||||
|
||||
// attempt to create extraction directory
|
||||
try
|
||||
{
|
||||
if (!std::filesystem::exists(disk_file.parent_path()))
|
||||
{
|
||||
if (!std::filesystem::create_directory(disk_file.parent_path()))
|
||||
{
|
||||
throw std::exception();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
error = "CoreOpenRom Failed: ";
|
||||
error += "Failed to create \"";
|
||||
error += disk_file.parent_path().string();
|
||||
error += "\"!";
|
||||
CoreSetError(error);
|
||||
return false;
|
||||
}
|
||||
|
||||
// attempt to write temporary file
|
||||
if (!write_file(disk_file, buf, buf_size))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CoreMediaLoaderSetDiskFile(disk_file);
|
||||
|
||||
l_ExtractedDiskPath = disk_file;
|
||||
}
|
||||
|
||||
l_HasDisk = is_disk;
|
||||
l_HasExtractedDisk = is_disk;
|
||||
}
|
||||
else if (file_extension == ".d64" ||
|
||||
file_extension == ".ndd")
|
||||
{
|
||||
CoreMediaLoaderSetDiskFile(file);
|
||||
l_HasDisk = true;
|
||||
l_HasDisk = true;
|
||||
l_HasExtractedDisk = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -291,7 +631,8 @@ bool CoreOpenRom(std::filesystem::path file)
|
||||
return false;
|
||||
}
|
||||
|
||||
l_HasDisk = false;
|
||||
l_HasDisk = false;
|
||||
l_HasExtractedDisk = false;
|
||||
}
|
||||
|
||||
if (l_HasDisk)
|
||||
@@ -303,6 +644,7 @@ bool CoreOpenRom(std::filesystem::path file)
|
||||
{
|
||||
ret = m64p::Core.DoCommand(M64CMD_ROM_OPEN, buf_size, buf);
|
||||
error = "CoreOpenRom: m64p::Core.DoCommand(M64CMD_ROM_OPEN) Failed: ";
|
||||
free(buf);
|
||||
}
|
||||
|
||||
if (ret != M64ERR_SUCCESS)
|
||||
@@ -311,11 +653,12 @@ bool CoreOpenRom(std::filesystem::path file)
|
||||
CoreSetError(error);
|
||||
}
|
||||
|
||||
free(buf);
|
||||
l_HasRomOpen = (ret == M64ERR_SUCCESS);
|
||||
|
||||
if (l_HasRomOpen)
|
||||
{
|
||||
// store ROM path
|
||||
l_RomPath = file;
|
||||
// store default ROM settings
|
||||
CoreStoreCurrentDefaultRomSettings();
|
||||
// apply rom settings overlay
|
||||
@@ -330,9 +673,36 @@ bool CoreHasRomOpen(void)
|
||||
return l_HasRomOpen;
|
||||
}
|
||||
|
||||
CoreRomType CoreGetRomType(void)
|
||||
bool CoreGetRomType(CoreRomType& type)
|
||||
{
|
||||
return l_HasDisk ? CoreRomType::Disk : CoreRomType::Cartridge;
|
||||
std::string error;
|
||||
|
||||
if (!l_HasRomOpen)
|
||||
{
|
||||
error = "CoreGetRomType Failed: ";
|
||||
error += "cannot retrieve ROM type when no ROM has been opened!";
|
||||
CoreSetError(error);
|
||||
return false;
|
||||
}
|
||||
|
||||
type = l_HasDisk ? CoreRomType::Disk : CoreRomType::Cartridge;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CoreGetRomPath(std::filesystem::path& path)
|
||||
{
|
||||
std::string error;
|
||||
|
||||
if (!l_HasRomOpen)
|
||||
{
|
||||
error = "CoreGetRomPath Failed: ";
|
||||
error += "cannot retrieve ROM path when no ROM has been opened!";
|
||||
CoreSetError(error);
|
||||
return false;
|
||||
}
|
||||
|
||||
path = l_RomPath;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CoreCloseRom(void)
|
||||
@@ -379,6 +749,32 @@ bool CoreCloseRom(void)
|
||||
// clear default ROM settings
|
||||
CoreClearCurrentDefaultRomSettings();
|
||||
|
||||
// if removing the temporary extracted disk file
|
||||
// fails, then we shouldn't break the state of
|
||||
// whether we currently have an open ROM/disk,
|
||||
// so we can safely say the ROM/disk isn't open
|
||||
// anymore here
|
||||
l_HasRomOpen = false;
|
||||
|
||||
// attempt to clean temporary extracted disk file
|
||||
if (l_HasExtractedDisk)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!std::filesystem::remove(l_ExtractedDiskPath))
|
||||
{
|
||||
throw std::exception();
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
error = "CoreCloseRom: Failed to remove \"";
|
||||
error += l_ExtractedDiskPath.string();
|
||||
error += "\"!";
|
||||
CoreSetError(error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
|
||||
enum class CoreRomType
|
||||
{
|
||||
Cartridge,
|
||||
Disk
|
||||
Cartridge = 0,
|
||||
Disk = 1
|
||||
};
|
||||
|
||||
// opens the given file as ROM
|
||||
@@ -24,8 +24,11 @@ bool CoreOpenRom(std::filesystem::path file);
|
||||
// returns whether core has a ROM opened
|
||||
bool CoreHasRomOpen(void);
|
||||
|
||||
// returns the opened ROM's type
|
||||
CoreRomType CoreGetRomType(void);
|
||||
// attempts to retrieve the ROM type
|
||||
bool CoreGetRomType(CoreRomType& type);
|
||||
|
||||
// attempts to retrieve ROM path
|
||||
bool CoreGetRomPath(std::filesystem::path& path);
|
||||
|
||||
// closes ROM
|
||||
bool CoreCloseRom(void);
|
||||
|
||||
@@ -21,6 +21,97 @@
|
||||
#include "Error.hpp"
|
||||
#include "Rom.hpp"
|
||||
|
||||
//
|
||||
// Local Functions
|
||||
//
|
||||
|
||||
static std::string get_gameid_from_header(m64p_rom_header header)
|
||||
{
|
||||
std::string gameID;
|
||||
|
||||
if (header.Manufacturer_ID == 0)
|
||||
{
|
||||
return "????";
|
||||
}
|
||||
|
||||
gameID.push_back(char(ntohl(header.Manufacturer_ID)));
|
||||
gameID.push_back(char(header.Cartridge_ID % 256));
|
||||
gameID.push_back(char(header.Cartridge_ID / 256));
|
||||
gameID.push_back(char(header.Country_code));
|
||||
|
||||
return gameID;
|
||||
}
|
||||
|
||||
static std::string get_region_from_countrycode(char countryCode)
|
||||
{
|
||||
std::string region;
|
||||
|
||||
switch (countryCode)
|
||||
{
|
||||
case 'A':
|
||||
region = "Region-Free";
|
||||
break;
|
||||
case 'B':
|
||||
region = "Brazil";
|
||||
break;
|
||||
case 'C':
|
||||
region = "China";
|
||||
break;
|
||||
case 'D':
|
||||
region = "Germany";
|
||||
break;
|
||||
case 'E':
|
||||
region = "North America";
|
||||
break;
|
||||
case 'F':
|
||||
region = "France";
|
||||
break;
|
||||
case 'G':
|
||||
region = "Gateway 64 (NTSC)";
|
||||
break;
|
||||
case 'H':
|
||||
region = "Netherlands";
|
||||
break;
|
||||
case 'I':
|
||||
region = "Italy";
|
||||
break;
|
||||
case 'J':
|
||||
region = "Japan";
|
||||
break;
|
||||
case 'K':
|
||||
region = "Korea";
|
||||
break;
|
||||
case 'L':
|
||||
region = "Gateway 64 (PAL)";
|
||||
break;
|
||||
case 'N':
|
||||
region = "Canada";
|
||||
break;
|
||||
case 'P':
|
||||
case 'X':
|
||||
region = "Europe/Australia";
|
||||
break;
|
||||
case 'S':
|
||||
region = "Spain";
|
||||
break;
|
||||
case 'U':
|
||||
region = "Australia";
|
||||
break;
|
||||
case 'W':
|
||||
region = "Scandanavia";
|
||||
break;
|
||||
case 'Y':
|
||||
case 'Z':
|
||||
region = "Europe";
|
||||
break;
|
||||
default:
|
||||
region = "Unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
return region;
|
||||
}
|
||||
|
||||
//
|
||||
// Exported Functions
|
||||
//
|
||||
@@ -49,6 +140,8 @@ bool CoreGetCurrentRomHeader(CoreRomHeader& header)
|
||||
header.CRC2 = ntohl(m64p_header.CRC2);
|
||||
header.CountryCode = m64p_header.Country_code;
|
||||
header.Name = CoreConvertStringEncoding((char*)m64p_header.Name, CoreStringEncoding::Shift_JIS);
|
||||
|
||||
header.GameID = get_gameid_from_header(m64p_header);
|
||||
header.Region = get_region_from_countrycode((char)header.CountryCode);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ struct CoreRomHeader
|
||||
uint32_t CRC2;
|
||||
uint32_t CountryCode;
|
||||
std::string Name;
|
||||
std::string GameID;
|
||||
std::string Region;
|
||||
};
|
||||
|
||||
// retrieves the currently opened ROM header
|
||||
|
||||
@@ -8,10 +8,33 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "SaveState.hpp"
|
||||
#include "Directories.hpp"
|
||||
#include "RomSettings.hpp"
|
||||
#include "RomHeader.hpp"
|
||||
#include "Error.hpp"
|
||||
|
||||
#include "Settings/Settings.hpp"
|
||||
#include "m64p/Api.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
//
|
||||
// Local Functions
|
||||
//
|
||||
|
||||
// replaces all occurences of any chars in replace with c inside str
|
||||
static void str_replace_chars(std::string& str, const std::string replace, const char c)
|
||||
{
|
||||
for (int i = 0; i < str.size(); i++)
|
||||
{
|
||||
char str_char = str.at(i);
|
||||
if (replace.find(str_char) != std::string::npos)
|
||||
{
|
||||
str.at(i) = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Exported Functions
|
||||
//
|
||||
@@ -59,6 +82,92 @@ int CoreGetSaveStateSlot(void)
|
||||
return slot;
|
||||
}
|
||||
|
||||
bool CoreGetSaveStatePath(int slot, std::filesystem::path& path)
|
||||
{
|
||||
// TODO: this should probably be an API function
|
||||
// in mupen64plus-core instead
|
||||
|
||||
std::filesystem::path saveStatePath;
|
||||
std::filesystem::path oldSaveStatePath;
|
||||
std::string saveStateFileName;
|
||||
std::filesystem::path saveStateExtension;
|
||||
CoreRomHeader romHeader;
|
||||
CoreRomSettings romSettings;
|
||||
|
||||
// attempt to retrieve the current
|
||||
// rom header and settings
|
||||
if (!CoreGetCurrentRomHeader(romHeader) ||
|
||||
!CoreGetCurrentRomSettings(romSettings))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// retrieve save state directory
|
||||
saveStatePath = CoreGetSaveStateDirectory();
|
||||
saveStatePath += "/";
|
||||
|
||||
// construct extension
|
||||
saveStateExtension = ".st";
|
||||
saveStateExtension += std::to_string(slot);
|
||||
|
||||
// construct old save state path
|
||||
oldSaveStatePath = saveStatePath;
|
||||
oldSaveStatePath += romSettings.GoodName;
|
||||
oldSaveStatePath += saveStateExtension;
|
||||
|
||||
// use old filename if it exists
|
||||
if (std::filesystem::is_regular_file(oldSaveStatePath))
|
||||
{
|
||||
path = oldSaveStatePath;
|
||||
return true;
|
||||
}
|
||||
|
||||
// else use new filename
|
||||
int format = CoreSettingsGetIntValue(SettingsID::Core_SaveFileNameFormat);
|
||||
if (format == 0)
|
||||
{
|
||||
saveStatePath += romHeader.Name;
|
||||
saveStatePath += saveStateExtension;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (romSettings.GoodName.find("(unknown rom)") == std::string::npos)
|
||||
{
|
||||
if (romSettings.GoodName.size() < 32)
|
||||
{
|
||||
saveStatePath += romSettings.GoodName;
|
||||
}
|
||||
else
|
||||
{
|
||||
saveStatePath += romSettings.GoodName.substr(0, 32);
|
||||
}
|
||||
}
|
||||
else if (!romHeader.Name.empty())
|
||||
{
|
||||
saveStatePath += romHeader.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
saveStatePath += "unknown";
|
||||
}
|
||||
saveStatePath += "-";
|
||||
saveStatePath += romSettings.MD5.substr(0, 8);
|
||||
saveStatePath += saveStateExtension;
|
||||
}
|
||||
|
||||
// retrieve filename from path
|
||||
saveStateFileName = saveStatePath.filename().string();
|
||||
|
||||
// sanitize filename
|
||||
str_replace_chars(saveStateFileName, ":<>\"/\\|?*", '_');
|
||||
|
||||
// replace filename in path
|
||||
saveStatePath.replace_filename(saveStateFileName);
|
||||
|
||||
path = saveStatePath;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CoreSaveState(void)
|
||||
{
|
||||
std::string error;
|
||||
|
||||
@@ -19,6 +19,10 @@ bool CoreSetSaveStateSlot(int slot);
|
||||
// returns -1 on error
|
||||
int CoreGetSaveStateSlot(void);
|
||||
|
||||
// retrieves the file path for
|
||||
// the save state in the given slot
|
||||
bool CoreGetSaveStatePath(int slot, std::filesystem::path& path);
|
||||
|
||||
// saves state
|
||||
bool CoreSaveState(void);
|
||||
|
||||
|
||||
@@ -122,6 +122,7 @@ static std::vector<std::string> l_keyList;
|
||||
#define SETTING_SECTION_GB SETTING_SECTION_CORE " Gameboy"
|
||||
#define SETTING_SECTION_M64P "Core"
|
||||
#define SETTING_SECTION_AUDIO SETTING_SECTION_GUI " - Audio Plugin"
|
||||
#define SETTING_SECTION_INPUT SETTING_SECTION_GUI " - Input Plugin"
|
||||
|
||||
// retrieves l_Setting from settingId
|
||||
static l_Setting get_setting(SettingsID settingId)
|
||||
@@ -140,9 +141,6 @@ static l_Setting get_setting(SettingsID settingId)
|
||||
case SettingsID::GUI_SettingsDialogHeight:
|
||||
setting = {SETTING_SECTION_GUI, "SettingsDialogHeight", 0};
|
||||
break;
|
||||
case SettingsID::GUI_AllowManualResizing:
|
||||
setting = {SETTING_SECTION_GUI, "AllowManualResizing", true};
|
||||
break;
|
||||
case SettingsID::GUI_HideCursorInEmulation:
|
||||
setting = {SETTING_SECTION_GUI, "HideCursorInEmulation", false};
|
||||
break;
|
||||
@@ -164,21 +162,45 @@ static l_Setting get_setting(SettingsID settingId)
|
||||
case SettingsID::GUI_ShowVerboseLogMessages:
|
||||
setting = {SETTING_SECTION_GUI, "ShowVerboseLogMessages", false};
|
||||
break;
|
||||
case SettingsID::GUI_OnScreenDisplayEnabled:
|
||||
setting = {SETTING_SECTION_GUI, "OnScreenDisplayEnabled", true};
|
||||
break;
|
||||
case SettingsID::GUI_OnScreenDisplayLocation:
|
||||
setting = {SETTING_SECTION_GUI, "OnScreenDisplayLocation", 0};
|
||||
break;
|
||||
case SettingsID::GUI_OnScreenDisplayPaddingX:
|
||||
setting = {SETTING_SECTION_GUI, "OnScreenDisplayPaddingX", 20};
|
||||
break;
|
||||
case SettingsID::GUI_OnScreenDisplayPaddingY:
|
||||
setting = {SETTING_SECTION_GUI, "OnScreenDisplayPaddingY", 20};
|
||||
break;
|
||||
case SettingsID::GUI_OnScreenDisplayOpacity:
|
||||
setting = {SETTING_SECTION_GUI, "OnScreenDisplayOpacity", 0.5f};
|
||||
break;
|
||||
case SettingsID::GUI_OnScreenDisplayDuration:
|
||||
setting = {SETTING_SECTION_GUI, "OnScreenDisplayDuration", 3};
|
||||
break;
|
||||
case SettingsID::GUI_Toolbar:
|
||||
setting = {SETTING_SECTION_GUI, "Toolbar", true};
|
||||
break;
|
||||
case SettingsID::GUI_ToolbarArea:
|
||||
setting = {SETTING_SECTION_GUI, "ToolbarArea", 0};
|
||||
break;
|
||||
case SettingsID::GUI_StatusBar:
|
||||
setting = {SETTING_SECTION_GUI, "StatusBar", true};
|
||||
break;
|
||||
case SettingsID::GUI_Style:
|
||||
setting = {SETTING_SECTION_GUI, "Style", ""};
|
||||
case SettingsID::GUI_Theme:
|
||||
setting = {SETTING_SECTION_GUI, "Theme", "Native"};
|
||||
break;
|
||||
case SettingsID::GUI_IconTheme:
|
||||
setting = {SETTING_SECTION_GUI, "IconTheme", "black"};
|
||||
setting = {SETTING_SECTION_GUI, "IconTheme", "Automatic"};
|
||||
break;
|
||||
case SettingsID::GUI_CheckForUpdates:
|
||||
setting = {SETTING_SECTION_GUI, "CheckForUpdates", true};
|
||||
break;
|
||||
case SettingsID::GUI_LastUpdateCheck:
|
||||
setting = {SETTING_SECTION_GUI, "LastUpdateCheck", ""};
|
||||
break;
|
||||
case SettingsID::GUI_DiscordRpc:
|
||||
setting = {SETTING_SECTION_GUI, "DiscordRpc", true};
|
||||
break;
|
||||
@@ -189,36 +211,36 @@ static l_Setting get_setting(SettingsID settingId)
|
||||
case SettingsID::Core_GFX_Plugin:
|
||||
setting = {SETTING_SECTION_CORE, "GFX_Plugin",
|
||||
#ifdef _WIN32
|
||||
CoreGetPluginDirectory().string() + "\\GFX\\mupen64plus-video-GLideN64.dll",
|
||||
"mupen64plus-video-GLideN64.dll",
|
||||
#else
|
||||
CoreGetPluginDirectory().string() + "/GFX/mupen64plus-video-GLideN64.so",
|
||||
"mupen64plus-video-GLideN64.so",
|
||||
#endif // _WIN32
|
||||
};
|
||||
break;
|
||||
case SettingsID::Core_AUDIO_Plugin:
|
||||
setting = {SETTING_SECTION_CORE, "AUDIO_Plugin",
|
||||
#ifdef _WIN32
|
||||
CoreGetPluginDirectory().string() + "\\Audio\\RMG-Audio.dll",
|
||||
"RMG-Audio.dll",
|
||||
#else
|
||||
CoreGetPluginDirectory().string() + "/Audio/RMG-Audio.so",
|
||||
"RMG-Audio.so",
|
||||
#endif // _WIN32
|
||||
};
|
||||
break;
|
||||
case SettingsID::Core_INPUT_Plugin:
|
||||
setting = {SETTING_SECTION_CORE, "INPUT_Plugin",
|
||||
#ifdef _WIN32
|
||||
CoreGetPluginDirectory().string() + "\\Input\\RMG-Input.dll",
|
||||
"RMG-Input.dll",
|
||||
#else
|
||||
CoreGetPluginDirectory().string() + "/Input/RMG-Input.so",
|
||||
"RMG-Input.so",
|
||||
#endif // _WIN32
|
||||
};
|
||||
break;
|
||||
case SettingsID::Core_RSP_Plugin:
|
||||
setting = {SETTING_SECTION_CORE, "RSP_Plugin",
|
||||
#ifdef _WIN32
|
||||
CoreGetPluginDirectory().string() + "\\RSP\\mupen64plus-rsp-hle.dll",
|
||||
"mupen64plus-rsp-hle.dll",
|
||||
#else
|
||||
CoreGetPluginDirectory().string() + "/RSP/mupen64plus-rsp-hle.so",
|
||||
"mupen64plus-rsp-hle.so",
|
||||
#endif // _WIN32
|
||||
};
|
||||
break;
|
||||
@@ -258,6 +280,9 @@ static l_Setting get_setting(SettingsID settingId)
|
||||
case SettingsID::Core_SiDmaDuration:
|
||||
setting = {SETTING_SECTION_M64P, "SiDmaDuration", -1};
|
||||
break;
|
||||
case SettingsID::Core_SaveFileNameFormat:
|
||||
setting = {SETTING_SECTION_M64P, "SaveFilenameFormat", 1};
|
||||
break;
|
||||
|
||||
case SettingsID::CoreOverlay_RandomizeInterrupt:
|
||||
setting = {SETTING_SECTION_OVERLAY, "RandomizeInterrupt", true};
|
||||
@@ -280,6 +305,9 @@ static l_Setting get_setting(SettingsID settingId)
|
||||
case SettingsID::CoreOverlay_SiDmaDuration:
|
||||
setting = {SETTING_SECTION_OVERLAY, "SiDmaDuration", -1};
|
||||
break;
|
||||
case SettingsID::CoreOverLay_SaveFileNameFormat:
|
||||
setting = {SETTING_SECTION_OVERLAY, "SaveFilenameFormat", 1};
|
||||
break;
|
||||
|
||||
case SettingsID::Core_ScreenshotPath:
|
||||
setting = {SETTING_SECTION_M64P, "ScreenshotPath", CoreGetDefaultScreenshotDirectory(), "", true};
|
||||
@@ -404,6 +432,42 @@ static l_Setting get_setting(SettingsID settingId)
|
||||
case SettingsID::KeyBinding_LimitFPS:
|
||||
setting = {SETTING_SECTION_KEYBIND, "LimitFPS", "F4"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SpeedFactor25:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SpeedFactor25", "Alt+0"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SpeedFactor50:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SpeedFactor50", "Alt+1"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SpeedFactor75:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SpeedFactor75", "Alt+2"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SpeedFactor100:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SpeedFactor100", "Alt+3"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SpeedFactor125:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SpeedFactor125", "Alt+4"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SpeedFactor150:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SpeedFactor150", "Alt+5"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SpeedFactor175:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SpeedFactor175", "Alt+6"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SpeedFactor200:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SpeedFactor200", "Alt+7"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SpeedFactor225:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SpeedFactor225", "Alt+8"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SpeedFactor250:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SpeedFactor250", "Alt+9"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SpeedFactor275:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SpeedFactor275", "Alt+["};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SpeedFactor300:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SpeedFactor300", "Alt+]"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SaveState:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SaveState", "F5"};
|
||||
break;
|
||||
@@ -422,9 +486,54 @@ static l_Setting get_setting(SettingsID settingId)
|
||||
case SettingsID::KeyBinding_GSButton:
|
||||
setting = {SETTING_SECTION_KEYBIND, "GSButton", "F9"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SaveStateSlot0:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SaveStateSlot0", "Ctrl+0"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SaveStateSlot1:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SaveStateSlot1", "Ctrl+1"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SaveStateSlot2:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SaveStateSlot2", "Ctrl+2"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SaveStateSlot3:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SaveStateSlot3", "Ctrl+3"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SaveStateSlot4:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SaveStateSlot4", "Ctrl+4"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SaveStateSlot5:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SaveStateSlot5", "Ctrl+5"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SaveStateSlot6:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SaveStateSlot6", "Ctrl+6"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SaveStateSlot7:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SaveStateSlot7", "Ctrl+7"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SaveStateSlot8:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SaveStateSlot8", "Ctrl+8"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SaveStateSlot9:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SaveStateSlot9", "Ctrl+9"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_Fullscreen:
|
||||
setting = {SETTING_SECTION_KEYBIND, "Fullscreen", "Alt+Return"};
|
||||
break;
|
||||
case SettingsID::Keybinding_ViewLog:
|
||||
setting = {SETTING_SECTION_KEYBIND, "ViewLog", "Ctrl+L"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_GraphicsSettings:
|
||||
setting = {SETTING_SECTION_KEYBIND, "GraphicsSettings", "Ctrl+G"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_AudioSettings:
|
||||
setting = {SETTING_SECTION_KEYBIND, "AudioSettings", "Ctrl+A"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_RspSettings:
|
||||
setting = {SETTING_SECTION_KEYBIND, "RspSettings", "Ctrl+R"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_InputSettings:
|
||||
setting = {SETTING_SECTION_KEYBIND, "InputSettings", "Ctrl+I"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_Settings:
|
||||
setting = {SETTING_SECTION_KEYBIND, "Settings", "Ctrl+T"};
|
||||
break;
|
||||
@@ -435,17 +544,26 @@ static l_Setting get_setting(SettingsID settingId)
|
||||
case SettingsID::RomBrowser_Geometry:
|
||||
setting = {SETTING_SECTION_ROMBROWSER, "Geometry", ""};
|
||||
break;
|
||||
case SettingsID::RomBrowser_Maximized:
|
||||
setting = {SETTING_SECTION_ROMBROWSER, "Maximized", false};
|
||||
break;
|
||||
case SettingsID::RomBrowser_Recursive:
|
||||
setting = {SETTING_SECTION_ROMBROWSER, "Recursive", true};
|
||||
break;
|
||||
case SettingsID::RomBrowser_MaxItems:
|
||||
setting = {SETTING_SECTION_ROMBROWSER, "MaxItems", 250};
|
||||
break;
|
||||
case SettingsID::RomBrowser_Columns:
|
||||
setting = {SETTING_SECTION_ROMBROWSER, "Columns", std::vector<int>({0, 1, 2})};
|
||||
case SettingsID::RomBrowser_ColumnVisibility:
|
||||
setting = {SETTING_SECTION_ROMBROWSER, "ColumnVisibility", std::vector<int>({1, 1, 1, 0, 0, 0, 0, 0, 0})};
|
||||
break;
|
||||
case SettingsID::RomBrowser_ColumnOrder:
|
||||
setting = {SETTING_SECTION_ROMBROWSER, "ColumnOrder", std::vector<int>({0, 1, 2, 3, 4, 5, 6, 7, 8})};
|
||||
break;
|
||||
case SettingsID::RomBrowser_ColumnSizes:
|
||||
setting = {SETTING_SECTION_ROMBROWSER, "ColumnSizes", std::vector<int>({-1, -1, -1})};
|
||||
setting = {SETTING_SECTION_ROMBROWSER, "ColumnSizes", std::vector<int>({-1, -1, -1, -1, -1, -1, -1, -1, -1})};
|
||||
break;
|
||||
case SettingsID::RomBrowser_SortAfterSearch:
|
||||
setting = {SETTING_SECTION_ROMBROWSER, "SortAfterSearch", true};
|
||||
break;
|
||||
case SettingsID::RomBrowser_ViewMode:
|
||||
setting = {SETTING_SECTION_ROMBROWSER, "ViewMode", 0};
|
||||
@@ -498,6 +616,15 @@ static l_Setting get_setting(SettingsID settingId)
|
||||
setting = {SETTING_SECTION_AUDIO, "Synchronize", false};
|
||||
break;
|
||||
|
||||
case SettingsID::Input_Profiles:
|
||||
setting = {SETTING_SECTION_INPUT, "Profiles", ""};
|
||||
break;
|
||||
case SettingsID::Input_UseProfile:
|
||||
setting = {"", "UseProfile"};
|
||||
break;
|
||||
case SettingsID::Input_UseGameProfile:
|
||||
setting = {"", "UseGameProfile"};
|
||||
break;
|
||||
case SettingsID::Input_PluggedIn:
|
||||
setting = {"", "PluggedIn"};
|
||||
break;
|
||||
@@ -513,6 +640,9 @@ static l_Setting get_setting(SettingsID settingId)
|
||||
case SettingsID::Input_Deadzone:
|
||||
setting = {"", "Deadzone"};
|
||||
break;
|
||||
case SettingsID::Input_Sensitivity:
|
||||
setting = {"", "Sensitivity"};
|
||||
break;
|
||||
case SettingsID::Input_Pak:
|
||||
setting = {"", "Pak"};
|
||||
break;
|
||||
@@ -525,8 +655,11 @@ static l_Setting get_setting(SettingsID settingId)
|
||||
case SettingsID::Input_RemoveDuplicateMappings:
|
||||
setting = {"", "RemoveDuplicateMappings"};
|
||||
break;
|
||||
case SettingsID::Input_InvertAxis:
|
||||
setting = {"", "InvertAxis"};
|
||||
case SettingsID::Input_FilterEventsForButtons:
|
||||
setting = {"", "FilterEventsForButtons"};
|
||||
break;
|
||||
case SettingsID::Input_FilterEventsForAxis:
|
||||
setting = {"", "FilterEventsForAxis"};
|
||||
break;
|
||||
case SettingsID::Input_A_InputType:
|
||||
setting = {"", "A_InputType"};
|
||||
@@ -744,6 +877,402 @@ static l_Setting get_setting(SettingsID settingId)
|
||||
case SettingsID::Input_AnalogStickRight_ExtraData:
|
||||
setting = {"", "AnalogStickRight_ExtraData"};
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_Shutdown_InputType:
|
||||
setting = {"", "Hotkey_Shutdown_InputType" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_Shutdown_Name:
|
||||
setting = {"", "Hotkey_Shutdown_Name" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_Shutdown_Data:
|
||||
setting = {"", "Hotkey_Shutdown_Data" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_Shutdown_ExtraData:
|
||||
setting = {"", "Hotkey_Shutdown_ExtraData" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_Exit_InputType:
|
||||
setting = {"", "Hotkey_Exit_InputType" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_Exit_Name:
|
||||
setting = {"", "Hotkey_Exit_Name" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_Exit_Data:
|
||||
setting = {"", "Hotkey_Exit_Data" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_Exit_ExtraData:
|
||||
setting = {"", "Hotkey_Exit_ExtraData" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SoftReset_InputType:
|
||||
setting = {"", "Hotkey_SoftReset_InputType" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SoftReset_Name:
|
||||
setting = {"", "Hotkey_SoftReset_Name" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SoftReset_Data:
|
||||
setting = {"", "Hotkey_SoftReset_Data" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SoftReset_ExtraData:
|
||||
setting = {"", "Hotkey_SoftReset_ExtraData" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_HardReset_InputType:
|
||||
setting = {"", "Hotkey_HardReset_InputType" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_HardReset_Name:
|
||||
setting = {"", "Hotkey_HardReset_Name" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_HardReset_Data:
|
||||
setting = {"", "Hotkey_HardReset_Data" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_HardReset_ExtraData:
|
||||
setting = {"", "Hotkey_HardReset_ExtraData" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_Resume_InputType:
|
||||
setting = {"", "Hotkey_Resume_InputType" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_Resume_Name:
|
||||
setting = {"", "Hotkey_Resume_Name" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_Resume_Data:
|
||||
setting = {"", "Hotkey_Resume_Data" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_Resume_ExtraData:
|
||||
setting = {"", "Hotkey_Resume_ExtraData" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_Screenshot_InputType:
|
||||
setting = {"", "Hotkey_Screenshot_InputType" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_Screenshot_Name:
|
||||
setting = {"", "Hotkey_Screenshot_Name" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_Screenshot_Data:
|
||||
setting = {"", "Hotkey_Screenshot_Data" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_Screenshot_ExtraData:
|
||||
setting = {"", "Hotkey_Screenshot_ExtraData" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_LimitFPS_InputType:
|
||||
setting = {"", "Hotkey_LimitFPS_InputType" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_LimitFPS_Name:
|
||||
setting = {"", "Hotkey_LimitFPS_Name" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_LimitFPS_Data:
|
||||
setting = {"", "Hotkey_LimitFPS_Data" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_LimitFPS_ExtraData:
|
||||
setting = {"", "Hotkey_LimitFPS_ExtraData" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor25_InputType:
|
||||
setting = {"", "Hotkey_SpeedFactor25_InputType" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor25_Name:
|
||||
setting = {"", "Hotkey_SpeedFactor25_Name" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor25_Data:
|
||||
setting = {"", "Hotkey_SpeedFactor25_Data" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor25_ExtraData:
|
||||
setting = {"", "Hotkey_SpeedFactor25_ExtraData" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor50_InputType:
|
||||
setting = {"", "Hotkey_SpeedFactor50_InputType" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor50_Name:
|
||||
setting = {"", "Hotkey_SpeedFactor50_Name" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor50_Data:
|
||||
setting = {"", "Hotkey_SpeedFactor50_Data" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor50_ExtraData:
|
||||
setting = {"", "Hotkey_SpeedFactor50_ExtraData" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor75_InputType:
|
||||
setting = {"", "Hotkey_SpeedFactor75_InputType" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor75_Name:
|
||||
setting = {"", "Hotkey_SpeedFactor75_Name" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor75_Data:
|
||||
setting = {"", "Hotkey_SpeedFactor75_Data" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor75_ExtraData:
|
||||
setting = {"", "Hotkey_SpeedFactor75_ExtraData" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor100_InputType:
|
||||
setting = {"", "Hotkey_SpeedFactor100_InputType" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor100_Name:
|
||||
setting = {"", "Hotkey_SpeedFactor100_Name" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor100_Data:
|
||||
setting = {"", "Hotkey_SpeedFactor100_Data" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor100_ExtraData:
|
||||
setting = {"", "Hotkey_SpeedFactor100_ExtraData" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor125_InputType:
|
||||
setting = {"", "Hotkey_SpeedFactor125_InputType" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor125_Name:
|
||||
setting = {"", "Hotkey_SpeedFactor125_Name" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor125_Data:
|
||||
setting = {"", "Hotkey_SpeedFactor125_Data" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor125_ExtraData:
|
||||
setting = {"", "Hotkey_SpeedFactor125_ExtraData" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor150_InputType:
|
||||
setting = {"", "Hotkey_SpeedFactor150_InputType" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor150_Name:
|
||||
setting = {"", "Hotkey_SpeedFactor150_Name" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor150_Data:
|
||||
setting = {"", "Hotkey_SpeedFactor150_Data" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor150_ExtraData:
|
||||
setting = {"", "Hotkey_SpeedFactor150_ExtraData" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor175_InputType:
|
||||
setting = {"", "Hotkey_SpeedFactor175_InputType" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor175_Name:
|
||||
setting = {"", "Hotkey_SpeedFactor175_Name" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor175_Data:
|
||||
setting = {"", "Hotkey_SpeedFactor175_Data" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor175_ExtraData:
|
||||
setting = {"", "Hotkey_SpeedFactor175_ExtraData" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor200_InputType:
|
||||
setting = {"", "Hotkey_SpeedFactor200_InputType" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor200_Name:
|
||||
setting = {"", "Hotkey_SpeedFactor200_Name" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor200_Data:
|
||||
setting = {"", "Hotkey_SpeedFactor200_Data" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor200_ExtraData:
|
||||
setting = {"", "Hotkey_SpeedFactor200_ExtraData" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor225_InputType:
|
||||
setting = {"", "Hotkey_SpeedFactor225_InputType" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor225_Name:
|
||||
setting = {"", "Hotkey_SpeedFactor225_Name" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor225_Data:
|
||||
setting = {"", "Hotkey_SpeedFactor225_Data" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor225_ExtraData:
|
||||
setting = {"", "Hotkey_SpeedFactor225_ExtraData" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor250_InputType:
|
||||
setting = {"", "Hotkey_SpeedFactor250_InputType" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor250_Name:
|
||||
setting = {"", "Hotkey_SpeedFactor250_Name" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor250_Data:
|
||||
setting = {"", "Hotkey_SpeedFactor250_Data" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor250_ExtraData:
|
||||
setting = {"", "Hotkey_SpeedFactor250_ExtraData" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor275_InputType:
|
||||
setting = {"", "Hotkey_SpeedFactor275_InputType" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor275_Name:
|
||||
setting = {"", "Hotkey_SpeedFactor275_Name" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor275_Data:
|
||||
setting = {"", "Hotkey_SpeedFactor275_Data" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor275_ExtraData:
|
||||
setting = {"", "Hotkey_SpeedFactor275_ExtraData" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor300_InputType:
|
||||
setting = {"", "Hotkey_SpeedFactor300_InputType" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor300_Name:
|
||||
setting = {"", "Hotkey_SpeedFactor300_Name" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor300_Data:
|
||||
setting = {"", "Hotkey_SpeedFactor300_Data" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SpeedFactor300_ExtraData:
|
||||
setting = {"", "Hotkey_SpeedFactor300_ExtraData" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveState_InputType:
|
||||
setting = {"", "Hotkey_SaveState_InputType" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveState_Name:
|
||||
setting = {"", "Hotkey_SaveState_Name" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveState_Data:
|
||||
setting = {"", "Hotkey_SaveState_Data" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveState_ExtraData:
|
||||
setting = {"", "Hotkey_SaveState_ExtraData" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_LoadState_InputType:
|
||||
setting = {"", "Hotkey_LoadState_InputType" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_LoadState_Name:
|
||||
setting = {"", "Hotkey_LoadState_Name" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_LoadState_Data:
|
||||
setting = {"", "Hotkey_LoadState_Data" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_LoadState_ExtraData:
|
||||
setting = {"", "Hotkey_LoadState_ExtraData" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_GSButton_InputType:
|
||||
setting = {"", "Hotkey_GSButton_InputType" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_GSButton_Name:
|
||||
setting = {"", "Hotkey_GSButton_Name" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_GSButton_Data:
|
||||
setting = {"", "Hotkey_GSButton_Data" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_GSButton_ExtraData:
|
||||
setting = {"", "Hotkey_GSButton_ExtraData" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot0_InputType:
|
||||
setting = {"", "Hotkey_SaveStateSlot0_InputType" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot0_Name:
|
||||
setting = {"", "Hotkey_SaveStateSlot0_Name" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot0_Data:
|
||||
setting = {"", "Hotkey_SaveStateSlot0_Data" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot0_ExtraData:
|
||||
setting = {"", "Hotkey_SaveStateSlot0_ExtraData" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot1_InputType:
|
||||
setting = {"", "Hotkey_SaveStateSlot1_InputType" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot1_Name:
|
||||
setting = {"", "Hotkey_SaveStateSlot1_Name" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot1_Data:
|
||||
setting = {"", "Hotkey_SaveStateSlot1_Data" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot1_ExtraData:
|
||||
setting = {"", "Hotkey_SaveStateSlot1_ExtraData" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot2_InputType:
|
||||
setting = {"", "Hotkey_SaveStateSlot2_InputType" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot2_Name:
|
||||
setting = {"", "Hotkey_SaveStateSlot2_Name" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot2_Data:
|
||||
setting = {"", "Hotkey_SaveStateSlot2_Data" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot2_ExtraData:
|
||||
setting = {"", "Hotkey_SaveStateSlot2_ExtraData" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot3_InputType:
|
||||
setting = {"", "Hotkey_SaveStateSlot3_InputType" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot3_Name:
|
||||
setting = {"", "Hotkey_SaveStateSlot3_Name" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot3_Data:
|
||||
setting = {"", "Hotkey_SaveStateSlot3_Data" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot3_ExtraData:
|
||||
setting = {"", "Hotkey_SaveStateSlot3_ExtraData" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot4_InputType:
|
||||
setting = {"", "Hotkey_SaveStateSlot4_InputType" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot4_Name:
|
||||
setting = {"", "Hotkey_SaveStateSlot4_Name" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot4_Data:
|
||||
setting = {"", "Hotkey_SaveStateSlot4_Data" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot4_ExtraData:
|
||||
setting = {"", "Hotkey_SaveStateSlot4_ExtraData" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot5_InputType:
|
||||
setting = {"", "Hotkey_SaveStateSlot5_InputType" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot5_Name:
|
||||
setting = {"", "Hotkey_SaveStateSlot5_Name" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot5_Data:
|
||||
setting = {"", "Hotkey_SaveStateSlot5_Data" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot5_ExtraData:
|
||||
setting = {"", "Hotkey_SaveStateSlot5_ExtraData" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot6_InputType:
|
||||
setting = {"", "Hotkey_SaveStateSlot6_InputType" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot6_Name:
|
||||
setting = {"", "Hotkey_SaveStateSlot6_Name" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot6_Data:
|
||||
setting = {"", "Hotkey_SaveStateSlot6_Data" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot6_ExtraData:
|
||||
setting = {"", "Hotkey_SaveStateSlot6_ExtraData" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot7_InputType:
|
||||
setting = {"", "Hotkey_SaveStateSlot7_InputType" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot7_Name:
|
||||
setting = {"", "Hotkey_SaveStateSlot7_Name" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot7_Data:
|
||||
setting = {"", "Hotkey_SaveStateSlot7_Data" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot7_ExtraData:
|
||||
setting = {"", "Hotkey_SaveStateSlot7_ExtraData" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot8_InputType:
|
||||
setting = {"", "Hotkey_SaveStateSlot8_InputType" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot8_Name:
|
||||
setting = {"", "Hotkey_SaveStateSlot8_Name" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot8_Data:
|
||||
setting = {"", "Hotkey_SaveStateSlot8_Data" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot8_ExtraData:
|
||||
setting = {"", "Hotkey_SaveStateSlot8_ExtraData" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot9_InputType:
|
||||
setting = {"", "Hotkey_SaveStateSlot9_InputType" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot9_Name:
|
||||
setting = {"", "Hotkey_SaveStateSlot9_Name" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot9_Data:
|
||||
setting = {"", "Hotkey_SaveStateSlot9_Data" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_SaveStateSlot9_ExtraData:
|
||||
setting = {"", "Hotkey_SaveStateSlot9_ExtraData" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_Fullscreen_InputType:
|
||||
setting = {"", "Hotkey_Fullscreen_InputType" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_Fullscreen_Name:
|
||||
setting = {"", "Hotkey_Fullscreen_Name" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_Fullscreen_Data:
|
||||
setting = {"", "Hotkey_Fullscreen_Data" };
|
||||
break;
|
||||
case SettingsID::Input_Hotkey_Fullscreen_ExtraData:
|
||||
setting = {"", "Hotkey_Fullscreen_ExtraData" };
|
||||
break;
|
||||
}
|
||||
|
||||
return setting;
|
||||
@@ -950,6 +1479,94 @@ static bool config_option_default_set(std::string section, std::string key, m64p
|
||||
return ret == M64ERR_SUCCESS;
|
||||
}
|
||||
|
||||
static bool int_list_to_string(std::vector<int> intList, std::string& string)
|
||||
{
|
||||
for (int i = 0; i < intList.size(); i++)
|
||||
{
|
||||
int num = intList.at(i);
|
||||
string += std::to_string(num);
|
||||
|
||||
// don't add seperator at end
|
||||
// of the list
|
||||
if (i < (intList.size() - 1))
|
||||
{
|
||||
string += ";";
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool string_to_int_list(std::string string, std::vector<int>& intList)
|
||||
{
|
||||
std::string error;
|
||||
|
||||
// split string by ';'
|
||||
// and append list with each item
|
||||
std::stringstream value_str_stream(string);
|
||||
std::string tmp_str;
|
||||
while (std::getline(value_str_stream, tmp_str, ';'))
|
||||
{
|
||||
try
|
||||
{
|
||||
intList.emplace_back(std::stoi(tmp_str));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
error = "string_to_int_list: std::stroi threw an exception!";
|
||||
CoreSetError(error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool string_list_to_string(std::vector<std::string> stringList, std::string& string)
|
||||
{
|
||||
std::string error;
|
||||
|
||||
for (int i = 0; i < stringList.size(); i++)
|
||||
{
|
||||
std::string str = stringList.at(i);
|
||||
|
||||
// ensure the string doesn't contain
|
||||
// the separator character
|
||||
if (str.find(";") != std::string::npos)
|
||||
{
|
||||
error = "string_list_to_string: string cannot contain ';'!";
|
||||
CoreSetError(error);
|
||||
return false;
|
||||
}
|
||||
|
||||
string += str;
|
||||
|
||||
// don't add separator at end
|
||||
// of the list
|
||||
if (i < (stringList.size() - 1))
|
||||
{
|
||||
string += ";";
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool string_to_string_list(std::string string, std::vector<std::string>& stringList)
|
||||
{
|
||||
std::string error;
|
||||
|
||||
// split string by ';'
|
||||
// and append list with each item
|
||||
std::stringstream value_str_stream(string);
|
||||
std::string tmp_str;
|
||||
while (std::getline(value_str_stream, tmp_str, ';'))
|
||||
{
|
||||
stringList.emplace_back(tmp_str);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Exported Functions
|
||||
//
|
||||
@@ -1064,7 +1681,7 @@ bool CoreSettingsSetupDefaults(void)
|
||||
ret = config_option_default_set(setting.Section, setting.Key, M64TYPE_BOOL, &setting.DefaultValue.boolValue, setting.Description.c_str());
|
||||
break;
|
||||
case M64TYPE_FLOAT:
|
||||
ret = config_option_default_set(setting.Section, setting.Key, M64TYPE_FLOAT, &setting.DefaultValue.intValue, setting.Description.c_str());
|
||||
ret = config_option_default_set(setting.Section, setting.Key, M64TYPE_FLOAT, &setting.DefaultValue.floatValue, setting.Description.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1087,6 +1704,27 @@ bool CoreSettingsSectionExists(std::string section)
|
||||
return config_section_exists(section);
|
||||
}
|
||||
|
||||
bool CoreSettingsRevertSection(std::string section)
|
||||
{
|
||||
std::string error;
|
||||
m64p_error ret;
|
||||
|
||||
if (!m64p::Config.IsHooked())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = m64p::Config.RevertChanges(section.c_str());
|
||||
if (ret != M64ERR_SUCCESS)
|
||||
{
|
||||
error = "CoreSettingsRevertSection m64p::Config.RevertChanges() Failed: ";
|
||||
error = m64p::Core.ErrorMessage(ret);
|
||||
CoreSetError(error);
|
||||
}
|
||||
|
||||
return ret == M64ERR_SUCCESS;
|
||||
}
|
||||
|
||||
bool CoreSettingsDeleteSection(std::string section)
|
||||
{
|
||||
std::string error;
|
||||
@@ -1148,10 +1786,19 @@ bool CoreSettingsSetValue(SettingsID settingId, std::string value)
|
||||
bool CoreSettingsSetValue(SettingsID settingId, std::vector<int> value)
|
||||
{
|
||||
std::string value_str;
|
||||
for (const int& num : value)
|
||||
if (!int_list_to_string(value, value_str))
|
||||
{
|
||||
value_str += std::to_string(num);
|
||||
value_str += ";";
|
||||
return false;
|
||||
}
|
||||
return CoreSettingsSetValue(settingId, value_str);
|
||||
}
|
||||
|
||||
bool CoreSettingsSetValue(SettingsID settingId, std::vector<std::string> value)
|
||||
{
|
||||
std::string value_str;
|
||||
if (!string_list_to_string(value, value_str))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return CoreSettingsSetValue(settingId, value_str);
|
||||
}
|
||||
@@ -1184,10 +1831,19 @@ bool CoreSettingsSetValue(SettingsID settingId, std::string section, std::string
|
||||
bool CoreSettingsSetValue(SettingsID settingId, std::string section, std::vector<int> value)
|
||||
{
|
||||
std::string value_str;
|
||||
for (const int& num : value)
|
||||
if (!int_list_to_string(value, value_str))
|
||||
{
|
||||
value_str += std::to_string(num);
|
||||
value_str += ";";
|
||||
return false;
|
||||
}
|
||||
return CoreSettingsSetValue(settingId, section, value_str);
|
||||
}
|
||||
|
||||
bool CoreSettingsSetValue(SettingsID settingId, std::string section, std::vector<std::string> value)
|
||||
{
|
||||
std::string value_str;
|
||||
if (!string_list_to_string(value, value_str))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return CoreSettingsSetValue(settingId, section, value_str);
|
||||
}
|
||||
@@ -1216,10 +1872,19 @@ bool CoreSettingsSetValue(std::string section, std::string key, std::string valu
|
||||
bool CoreSettingsSetValue(std::string section, std::string key, std::vector<int> value)
|
||||
{
|
||||
std::string value_str;
|
||||
for (const int& num : value)
|
||||
if (!int_list_to_string(value, value_str))
|
||||
{
|
||||
value_str += std::to_string(num);
|
||||
value_str += ";";
|
||||
return false;
|
||||
}
|
||||
return CoreSettingsSetValue(section, key, value_str);
|
||||
}
|
||||
|
||||
bool CoreSettingsSetValue(std::string section, std::string key, std::vector<std::string> value)
|
||||
{
|
||||
std::string value_str;
|
||||
if (!string_list_to_string(value, value_str))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return CoreSettingsSetValue(section, key, value_str);
|
||||
}
|
||||
@@ -1292,6 +1957,12 @@ std::vector<int> CoreSettingsGetIntListValue(SettingsID settingId)
|
||||
return CoreSettingsGetIntListValue(settingId, setting.Section);
|
||||
}
|
||||
|
||||
std::vector<std::string> CoreSettingsGetStringListValue(SettingsID settingId)
|
||||
{
|
||||
l_Setting setting = get_setting(settingId);
|
||||
return CoreSettingsGetStringListValue(settingId, setting.Section);
|
||||
}
|
||||
|
||||
int CoreSettingsGetIntValue(SettingsID settingId, std::string section)
|
||||
{
|
||||
l_Setting setting = get_setting(settingId);
|
||||
@@ -1332,25 +2003,31 @@ std::vector<int> CoreSettingsGetIntListValue(SettingsID settingId, std::string s
|
||||
std::string value_str;
|
||||
value_str = CoreSettingsGetStringValue(settingId, section);
|
||||
|
||||
// split string by ';'
|
||||
// and append list with each item
|
||||
std::stringstream value_str_stream(value_str);
|
||||
std::string tmp_str;
|
||||
while (std::getline(value_str_stream, tmp_str, ';'))
|
||||
if (!string_to_int_list(value_str, value))
|
||||
{
|
||||
try
|
||||
{
|
||||
value.emplace_back(std::stoi(tmp_str));
|
||||
}
|
||||
catch (...)
|
||||
{ // ignore exception
|
||||
continue;
|
||||
}
|
||||
return std::vector<int>();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
std::vector<std::string> CoreSettingsGetStringListValue(SettingsID settingId, std::string section)
|
||||
{
|
||||
l_Setting setting = get_setting(settingId);
|
||||
std::vector<std::string> value;
|
||||
|
||||
std::string value_str;
|
||||
value_str = CoreSettingsGetStringValue(settingId, section);
|
||||
|
||||
if (!string_to_string_list(value_str, value))
|
||||
{
|
||||
return std::vector<std::string>();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
int CoreSettingsGetIntValue(std::string section, std::string key, int defaultValue)
|
||||
{
|
||||
int value = defaultValue;
|
||||
@@ -1379,27 +2056,31 @@ std::string CoreSettingsGetStringValue(std::string section, std::string key)
|
||||
return std::string(value);
|
||||
}
|
||||
|
||||
std::vector<int> CoreSettingsGetIntListValue(std::string section, std::string key, std::vector<int> defaultValue)
|
||||
std::vector<int> CoreSettingsGetIntListValue(std::string section, std::string key)
|
||||
{
|
||||
std::vector<int> value;
|
||||
|
||||
std::string value_str;
|
||||
value_str = CoreSettingsGetStringValue(section, key);
|
||||
|
||||
// split string by ';'
|
||||
// and append list with each item
|
||||
std::stringstream value_str_stream(value_str);
|
||||
std::string tmp_str;
|
||||
while (std::getline(value_str_stream, tmp_str, ';'))
|
||||
if (!string_to_int_list(value_str, value))
|
||||
{
|
||||
try
|
||||
{
|
||||
value.emplace_back(std::stoi(tmp_str));
|
||||
}
|
||||
catch (...)
|
||||
{ // ignore exception
|
||||
continue;
|
||||
}
|
||||
return std::vector<int>();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
std::vector<std::string> CoreSettingsGetStringListValue(std::string section, std::string key)
|
||||
{
|
||||
std::vector<std::string> value;
|
||||
|
||||
std::string value_str;
|
||||
value_str = CoreSettingsGetStringValue(section, key);
|
||||
|
||||
if (!string_to_string_list(value_str, value))
|
||||
{
|
||||
return std::vector<std::string>();
|
||||
}
|
||||
|
||||
return value;
|
||||
|
||||
@@ -27,6 +27,9 @@ bool CoreSettingsSetupDefaults(void);
|
||||
// returns whether a section exists
|
||||
bool CoreSettingsSectionExists(std::string section);
|
||||
|
||||
// reverts section to the one from the settings
|
||||
bool CoreSettingsRevertSection(std::string section);
|
||||
|
||||
// deletes given section
|
||||
bool CoreSettingsDeleteSection(std::string section);
|
||||
|
||||
@@ -43,6 +46,8 @@ bool CoreSettingsSetValue(SettingsID settingId, float value);
|
||||
bool CoreSettingsSetValue(SettingsID settingId, std::string value);
|
||||
// sets setting as int list value
|
||||
bool CoreSettingsSetValue(SettingsID settingId, std::vector<int> value);
|
||||
// sets setting as string list value
|
||||
bool CoreSettingsSetValue(SettingsID settingId, std::vector<std::string> value);
|
||||
|
||||
// sets setting in section as int value
|
||||
bool CoreSettingsSetValue(SettingsID settingId, std::string section, int value);
|
||||
@@ -54,6 +59,8 @@ bool CoreSettingsSetValue(SettingsID settingId, std::string section, float value
|
||||
bool CoreSettingsSetValue(SettingsID settingId, std::string section, std::string value);
|
||||
// sets setting in section as int list value
|
||||
bool CoreSettingsSetValue(SettingsID settingId, std::string section, std::vector<int> value);
|
||||
// sets setting as section as string list value
|
||||
bool CoreSettingsSetValue(SettingsID settingId, std::string section, std::vector<std::string> value);
|
||||
|
||||
#ifdef CORE_INTERNAL
|
||||
// sets key in section as int value
|
||||
@@ -66,6 +73,8 @@ bool CoreSettingsSetValue(std::string section, std::string key, float value);
|
||||
bool CoreSettingsSetValue(std::string section, std::string key, std::string value);
|
||||
// sets key in section as int list value
|
||||
bool CoreSettingsSetValue(std::string section, std::string key, std::vector<int> value);
|
||||
// sets key in section as string list value
|
||||
bool CoreSettingsSetValue(std::string section, std::string key, std::vector<std::string> value);
|
||||
#endif // CORE_INTERNAL
|
||||
|
||||
// retrieves default setting as int
|
||||
@@ -78,6 +87,8 @@ float CoreSettingsGetDefaultFloatValue(SettingsID settingId);
|
||||
std::string CoreSettingsGetDefaultStringValue(SettingsID settingId);
|
||||
// retrieves default setting as int list
|
||||
std::vector<int> CoreSettingsGetDefaultIntListValue(SettingsID settingId);
|
||||
// retrieves default setting as string list
|
||||
std::vector<std::string> CoreSettingsGetDefaultStringListValue(SettingsID settingId);
|
||||
|
||||
// retrieves setting as int
|
||||
int CoreSettingsGetIntValue(SettingsID settingId);
|
||||
@@ -89,6 +100,8 @@ float CoreSettingsGetFloatValue(SettingsID settingId);
|
||||
std::string CoreSettingsGetStringValue(SettingsID settingId);
|
||||
// retrieves setting as int list
|
||||
std::vector<int> CoreSettingsGetIntListValue(SettingsID settingId);
|
||||
// retrieves setting as string list
|
||||
std::vector<std::string> CoreSettingsGetStringListValue(SettingsID settingId);
|
||||
|
||||
// retrieves setting in section as int
|
||||
int CoreSettingsGetIntValue(SettingsID settingId, std::string section);
|
||||
@@ -100,6 +113,8 @@ float CoreSettingsGetFloatValue(SettingsID settingId, std::string section);
|
||||
std::string CoreSettingsGetStringValue(SettingsID settingId, std::string section);
|
||||
// retrieves setting in section as int list
|
||||
std::vector<int> CoreSettingsGetIntListValue(SettingsID settingId, std::string section);
|
||||
// retrieves setting in section as string list
|
||||
std::vector<std::string> CoreSettingsGetStringListValue(SettingsID settingId, std::string section);
|
||||
|
||||
#ifdef CORE_INTERNAL
|
||||
// retrieves key in section as int
|
||||
@@ -112,6 +127,8 @@ float CoreSettingsGetFloatValue(std::string section, std::string key, float defa
|
||||
std::string CoreSettingsGetStringValue(std::string section, std::string key);
|
||||
// retrieves key in section as int list
|
||||
std::vector<int> CoreSettingsGetIntListValue(std::string section, std::string key);
|
||||
// retrieves key in section as string list
|
||||
std::vector<std::string> CoreSettingsGetStringListValue(std::string section, std::string key);
|
||||
#endif // CORE_INTERNAL
|
||||
|
||||
#endif // CORE_SETTINGS_HPP
|
||||
|
||||
@@ -6,7 +6,6 @@ enum class SettingsID
|
||||
// GUI Settings
|
||||
GUI_SettingsDialogWidth = 0,
|
||||
GUI_SettingsDialogHeight,
|
||||
GUI_AllowManualResizing,
|
||||
GUI_HideCursorInEmulation,
|
||||
GUI_HideCursorInFullscreenEmulation,
|
||||
GUI_StatusbarMessageDuration,
|
||||
@@ -14,14 +13,22 @@ enum class SettingsID
|
||||
GUI_ResumeEmulationOnFocus,
|
||||
GUI_AutomaticFullscreen,
|
||||
GUI_ShowVerboseLogMessages,
|
||||
GUI_OnScreenDisplayEnabled,
|
||||
GUI_OnScreenDisplayLocation,
|
||||
GUI_OnScreenDisplayPaddingX,
|
||||
GUI_OnScreenDisplayPaddingY,
|
||||
GUI_OnScreenDisplayOpacity,
|
||||
GUI_OnScreenDisplayDuration,
|
||||
GUI_Toolbar,
|
||||
GUI_ToolbarArea,
|
||||
GUI_StatusBar,
|
||||
GUI_Style,
|
||||
GUI_Theme,
|
||||
GUI_IconTheme,
|
||||
GUI_CheckForUpdates,
|
||||
GUI_LastUpdateCheck,
|
||||
GUI_DiscordRpc,
|
||||
GUI_Version,
|
||||
|
||||
|
||||
// Core Plugin Settings
|
||||
Core_GFX_Plugin,
|
||||
Core_AUDIO_Plugin,
|
||||
@@ -58,6 +65,7 @@ enum class SettingsID
|
||||
Core_CountPerOp,
|
||||
Core_CountPerOpDenomPot,
|
||||
Core_SiDmaDuration,
|
||||
Core_SaveFileNameFormat,
|
||||
|
||||
// (mupen64plus) Overlay Core Settings
|
||||
CoreOverlay_RandomizeInterrupt,
|
||||
@@ -67,6 +75,7 @@ enum class SettingsID
|
||||
CoreOverlay_CountPerOp,
|
||||
CoreOverlay_CountPerOpDenomPot,
|
||||
CoreOverlay_SiDmaDuration,
|
||||
CoreOverLay_SaveFileNameFormat,
|
||||
|
||||
// (mupen64plus) Core Directory Settings
|
||||
Core_ScreenshotPath,
|
||||
@@ -104,22 +113,52 @@ enum class SettingsID
|
||||
KeyBinding_Resume,
|
||||
KeyBinding_Screenshot,
|
||||
KeyBinding_LimitFPS,
|
||||
KeyBinding_SpeedFactor25,
|
||||
KeyBinding_SpeedFactor50,
|
||||
KeyBinding_SpeedFactor75,
|
||||
KeyBinding_SpeedFactor100,
|
||||
KeyBinding_SpeedFactor125,
|
||||
KeyBinding_SpeedFactor150,
|
||||
KeyBinding_SpeedFactor175,
|
||||
KeyBinding_SpeedFactor200,
|
||||
KeyBinding_SpeedFactor225,
|
||||
KeyBinding_SpeedFactor250,
|
||||
KeyBinding_SpeedFactor275,
|
||||
KeyBinding_SpeedFactor300,
|
||||
KeyBinding_SaveState,
|
||||
KeyBinding_SaveAs,
|
||||
KeyBinding_LoadState,
|
||||
KeyBinding_Load,
|
||||
KeyBinding_Cheats,
|
||||
KeyBinding_GSButton,
|
||||
KeyBinding_SaveStateSlot0,
|
||||
KeyBinding_SaveStateSlot1,
|
||||
KeyBinding_SaveStateSlot2,
|
||||
KeyBinding_SaveStateSlot3,
|
||||
KeyBinding_SaveStateSlot4,
|
||||
KeyBinding_SaveStateSlot5,
|
||||
KeyBinding_SaveStateSlot6,
|
||||
KeyBinding_SaveStateSlot7,
|
||||
KeyBinding_SaveStateSlot8,
|
||||
KeyBinding_SaveStateSlot9,
|
||||
KeyBinding_Fullscreen,
|
||||
Keybinding_ViewLog,
|
||||
KeyBinding_GraphicsSettings,
|
||||
KeyBinding_AudioSettings,
|
||||
KeyBinding_RspSettings,
|
||||
KeyBinding_InputSettings,
|
||||
KeyBinding_Settings,
|
||||
|
||||
// RomBrowser Settings
|
||||
RomBrowser_Directory,
|
||||
RomBrowser_Geometry,
|
||||
RomBrowser_Maximized,
|
||||
RomBrowser_Recursive,
|
||||
RomBrowser_MaxItems,
|
||||
RomBrowser_Columns,
|
||||
RomBrowser_ColumnVisibility,
|
||||
RomBrowser_ColumnOrder,
|
||||
RomBrowser_ColumnSizes,
|
||||
RomBrowser_SortAfterSearch,
|
||||
RomBrowser_ViewMode,
|
||||
RomBrowser_ListViewSortSection,
|
||||
RomBrowser_ListViewSortOrder,
|
||||
@@ -142,16 +181,21 @@ enum class SettingsID
|
||||
Audio_Synchronize,
|
||||
|
||||
// Input Plugin Settings
|
||||
Input_Profiles,
|
||||
Input_UseProfile,
|
||||
Input_UseGameProfile,
|
||||
Input_PluggedIn,
|
||||
Input_DeviceType,
|
||||
Input_DeviceName,
|
||||
Input_DeviceNum,
|
||||
Input_Deadzone,
|
||||
Input_Sensitivity,
|
||||
Input_Pak,
|
||||
Input_GameboyRom,
|
||||
Input_GameboySave,
|
||||
Input_RemoveDuplicateMappings,
|
||||
Input_InvertAxis,
|
||||
Input_FilterEventsForButtons,
|
||||
Input_FilterEventsForAxis,
|
||||
Input_A_InputType,
|
||||
Input_A_Name,
|
||||
Input_A_Data,
|
||||
@@ -224,6 +268,138 @@ enum class SettingsID
|
||||
Input_AnalogStickRight_Name,
|
||||
Input_AnalogStickRight_Data,
|
||||
Input_AnalogStickRight_ExtraData,
|
||||
Input_Hotkey_Shutdown_InputType,
|
||||
Input_Hotkey_Shutdown_Name,
|
||||
Input_Hotkey_Shutdown_Data,
|
||||
Input_Hotkey_Shutdown_ExtraData,
|
||||
Input_Hotkey_Exit_InputType,
|
||||
Input_Hotkey_Exit_Name,
|
||||
Input_Hotkey_Exit_Data,
|
||||
Input_Hotkey_Exit_ExtraData,
|
||||
Input_Hotkey_SoftReset_InputType,
|
||||
Input_Hotkey_SoftReset_Name,
|
||||
Input_Hotkey_SoftReset_Data,
|
||||
Input_Hotkey_SoftReset_ExtraData,
|
||||
Input_Hotkey_HardReset_InputType,
|
||||
Input_Hotkey_HardReset_Name,
|
||||
Input_Hotkey_HardReset_Data,
|
||||
Input_Hotkey_HardReset_ExtraData,
|
||||
Input_Hotkey_Resume_InputType,
|
||||
Input_Hotkey_Resume_Name,
|
||||
Input_Hotkey_Resume_Data,
|
||||
Input_Hotkey_Resume_ExtraData,
|
||||
Input_Hotkey_Screenshot_InputType,
|
||||
Input_Hotkey_Screenshot_Name,
|
||||
Input_Hotkey_Screenshot_Data,
|
||||
Input_Hotkey_Screenshot_ExtraData,
|
||||
Input_Hotkey_LimitFPS_InputType,
|
||||
Input_Hotkey_LimitFPS_Name,
|
||||
Input_Hotkey_LimitFPS_Data,
|
||||
Input_Hotkey_LimitFPS_ExtraData,
|
||||
Input_Hotkey_SpeedFactor25_InputType,
|
||||
Input_Hotkey_SpeedFactor25_Name,
|
||||
Input_Hotkey_SpeedFactor25_Data,
|
||||
Input_Hotkey_SpeedFactor25_ExtraData,
|
||||
Input_Hotkey_SpeedFactor50_InputType,
|
||||
Input_Hotkey_SpeedFactor50_Name,
|
||||
Input_Hotkey_SpeedFactor50_Data,
|
||||
Input_Hotkey_SpeedFactor50_ExtraData,
|
||||
Input_Hotkey_SpeedFactor75_InputType,
|
||||
Input_Hotkey_SpeedFactor75_Name,
|
||||
Input_Hotkey_SpeedFactor75_Data,
|
||||
Input_Hotkey_SpeedFactor75_ExtraData,
|
||||
Input_Hotkey_SpeedFactor100_InputType,
|
||||
Input_Hotkey_SpeedFactor100_Name,
|
||||
Input_Hotkey_SpeedFactor100_Data,
|
||||
Input_Hotkey_SpeedFactor100_ExtraData,
|
||||
Input_Hotkey_SpeedFactor125_InputType,
|
||||
Input_Hotkey_SpeedFactor125_Name,
|
||||
Input_Hotkey_SpeedFactor125_Data,
|
||||
Input_Hotkey_SpeedFactor125_ExtraData,
|
||||
Input_Hotkey_SpeedFactor150_InputType,
|
||||
Input_Hotkey_SpeedFactor150_Name,
|
||||
Input_Hotkey_SpeedFactor150_Data,
|
||||
Input_Hotkey_SpeedFactor150_ExtraData,
|
||||
Input_Hotkey_SpeedFactor175_InputType,
|
||||
Input_Hotkey_SpeedFactor175_Name,
|
||||
Input_Hotkey_SpeedFactor175_Data,
|
||||
Input_Hotkey_SpeedFactor175_ExtraData,
|
||||
Input_Hotkey_SpeedFactor200_InputType,
|
||||
Input_Hotkey_SpeedFactor200_Name,
|
||||
Input_Hotkey_SpeedFactor200_Data,
|
||||
Input_Hotkey_SpeedFactor200_ExtraData,
|
||||
Input_Hotkey_SpeedFactor225_InputType,
|
||||
Input_Hotkey_SpeedFactor225_Name,
|
||||
Input_Hotkey_SpeedFactor225_Data,
|
||||
Input_Hotkey_SpeedFactor225_ExtraData,
|
||||
Input_Hotkey_SpeedFactor250_InputType,
|
||||
Input_Hotkey_SpeedFactor250_Name,
|
||||
Input_Hotkey_SpeedFactor250_Data,
|
||||
Input_Hotkey_SpeedFactor250_ExtraData,
|
||||
Input_Hotkey_SpeedFactor275_InputType,
|
||||
Input_Hotkey_SpeedFactor275_Name,
|
||||
Input_Hotkey_SpeedFactor275_Data,
|
||||
Input_Hotkey_SpeedFactor275_ExtraData,
|
||||
Input_Hotkey_SpeedFactor300_InputType,
|
||||
Input_Hotkey_SpeedFactor300_Name,
|
||||
Input_Hotkey_SpeedFactor300_Data,
|
||||
Input_Hotkey_SpeedFactor300_ExtraData,
|
||||
Input_Hotkey_SaveState_InputType,
|
||||
Input_Hotkey_SaveState_Name,
|
||||
Input_Hotkey_SaveState_Data,
|
||||
Input_Hotkey_SaveState_ExtraData,
|
||||
Input_Hotkey_LoadState_InputType,
|
||||
Input_Hotkey_LoadState_Name,
|
||||
Input_Hotkey_LoadState_Data,
|
||||
Input_Hotkey_LoadState_ExtraData,
|
||||
Input_Hotkey_GSButton_InputType,
|
||||
Input_Hotkey_GSButton_Name,
|
||||
Input_Hotkey_GSButton_Data,
|
||||
Input_Hotkey_GSButton_ExtraData,
|
||||
Input_Hotkey_SaveStateSlot0_InputType,
|
||||
Input_Hotkey_SaveStateSlot0_Name,
|
||||
Input_Hotkey_SaveStateSlot0_Data,
|
||||
Input_Hotkey_SaveStateSlot0_ExtraData,
|
||||
Input_Hotkey_SaveStateSlot1_InputType,
|
||||
Input_Hotkey_SaveStateSlot1_Name,
|
||||
Input_Hotkey_SaveStateSlot1_Data,
|
||||
Input_Hotkey_SaveStateSlot1_ExtraData,
|
||||
Input_Hotkey_SaveStateSlot2_InputType,
|
||||
Input_Hotkey_SaveStateSlot2_Name,
|
||||
Input_Hotkey_SaveStateSlot2_Data,
|
||||
Input_Hotkey_SaveStateSlot2_ExtraData,
|
||||
Input_Hotkey_SaveStateSlot3_InputType,
|
||||
Input_Hotkey_SaveStateSlot3_Name,
|
||||
Input_Hotkey_SaveStateSlot3_Data,
|
||||
Input_Hotkey_SaveStateSlot3_ExtraData,
|
||||
Input_Hotkey_SaveStateSlot4_InputType,
|
||||
Input_Hotkey_SaveStateSlot4_Name,
|
||||
Input_Hotkey_SaveStateSlot4_Data,
|
||||
Input_Hotkey_SaveStateSlot4_ExtraData,
|
||||
Input_Hotkey_SaveStateSlot5_InputType,
|
||||
Input_Hotkey_SaveStateSlot5_Name,
|
||||
Input_Hotkey_SaveStateSlot5_Data,
|
||||
Input_Hotkey_SaveStateSlot5_ExtraData,
|
||||
Input_Hotkey_SaveStateSlot6_InputType,
|
||||
Input_Hotkey_SaveStateSlot6_Name,
|
||||
Input_Hotkey_SaveStateSlot6_Data,
|
||||
Input_Hotkey_SaveStateSlot6_ExtraData,
|
||||
Input_Hotkey_SaveStateSlot7_InputType,
|
||||
Input_Hotkey_SaveStateSlot7_Name,
|
||||
Input_Hotkey_SaveStateSlot7_Data,
|
||||
Input_Hotkey_SaveStateSlot7_ExtraData,
|
||||
Input_Hotkey_SaveStateSlot8_InputType,
|
||||
Input_Hotkey_SaveStateSlot8_Name,
|
||||
Input_Hotkey_SaveStateSlot8_Data,
|
||||
Input_Hotkey_SaveStateSlot8_ExtraData,
|
||||
Input_Hotkey_SaveStateSlot9_InputType,
|
||||
Input_Hotkey_SaveStateSlot9_Name,
|
||||
Input_Hotkey_SaveStateSlot9_Data,
|
||||
Input_Hotkey_SaveStateSlot9_ExtraData,
|
||||
Input_Hotkey_Fullscreen_InputType,
|
||||
Input_Hotkey_Fullscreen_Name,
|
||||
Input_Hotkey_Fullscreen_Data,
|
||||
Input_Hotkey_Fullscreen_ExtraData,
|
||||
|
||||
Invalid
|
||||
};
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
|
||||
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 3.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "SpeedFactor.hpp"
|
||||
#include "Error.hpp"
|
||||
|
||||
#include "m64p/Api.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
//
|
||||
// Exported Functions
|
||||
//
|
||||
|
||||
int CoreGetSpeedFactor(void)
|
||||
{
|
||||
std::string error;
|
||||
m64p_error ret;
|
||||
int value = 0;
|
||||
|
||||
if (!m64p::Core.IsHooked())
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
|
||||
ret = m64p::Core.DoCommand(M64CMD_CORE_STATE_QUERY, M64CORE_SPEED_FACTOR, &value);
|
||||
if (ret != M64ERR_SUCCESS)
|
||||
{
|
||||
error = "CoreGetSpeedFactor: m64p::Core.DoCommand(M64CMD_CORE_STATE_QUERY) Failed: ";
|
||||
error += m64p::Core.ErrorMessage(ret);
|
||||
CoreSetError(error);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
bool CoreSetSpeedFactor(int factor)
|
||||
{
|
||||
std::string error;
|
||||
m64p_error ret;
|
||||
int value = factor;
|
||||
|
||||
if (!m64p::Core.IsHooked())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = m64p::Core.DoCommand(M64CMD_CORE_STATE_SET, M64CORE_SPEED_FACTOR, &value);
|
||||
if (ret != M64ERR_SUCCESS)
|
||||
{
|
||||
error = "CoreSetSpeedFactor: m64p::Core.DoCommand(M64CMD_CORE_STATE_SET) Failed: ";
|
||||
error += m64p::Core.ErrorMessage(ret);
|
||||
CoreSetError(error);
|
||||
}
|
||||
|
||||
return ret == M64ERR_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
|
||||
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 3.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef CORE_SPEEDFACTOR_HPP
|
||||
#define CORE_SPEEDFACTOR_HPP
|
||||
|
||||
// returns the current speed factor
|
||||
int CoreGetSpeedFactor(void);
|
||||
|
||||
// sets the speed factor
|
||||
bool CoreSetSpeedFactor(int factor);
|
||||
|
||||
#endif // CORE_SPEEDFACTOR_HPP
|
||||
@@ -28,6 +28,8 @@ bool PluginApi::Hook(m64p_dynlib_handle handle)
|
||||
HOOK_FUNC(handle, Plugin, Startup);
|
||||
HOOK_FUNC(handle, Plugin, Shutdown);
|
||||
HOOK_FUNC_OPT(handle, Plugin, Config);
|
||||
HOOK_FUNC_OPT(handle, Plugin, Config2);
|
||||
HOOK_FUNC_OPT(handle, Plugin, Config2HasRomConfig);
|
||||
HOOK_FUNC(handle, Plugin, GetVersion);
|
||||
|
||||
this->handle = handle;
|
||||
@@ -40,6 +42,8 @@ bool PluginApi::Unhook(void)
|
||||
UNHOOK_FUNC(Plugin, Startup);
|
||||
UNHOOK_FUNC(Plugin, Shutdown);
|
||||
UNHOOK_FUNC(Plugin, Config);
|
||||
UNHOOK_FUNC(Plugin, Config2);
|
||||
UNHOOK_FUNC(Plugin, Config2HasRomConfig);
|
||||
UNHOOK_FUNC(Plugin, GetVersion);
|
||||
|
||||
this->handle = nullptr;
|
||||
|
||||
@@ -34,6 +34,8 @@ class PluginApi
|
||||
ptr_PluginStartup Startup;
|
||||
ptr_PluginShutdown Shutdown;
|
||||
ptr_PluginConfig Config;
|
||||
ptr_PluginConfig2 Config2;
|
||||
ptr_PluginConfig2HasRomConfig Config2HasRomConfig;
|
||||
ptr_PluginGetVersion GetVersion;
|
||||
|
||||
private:
|
||||
|
||||
@@ -21,6 +21,29 @@ typedef m64p_error (*ptr_PluginConfig)(void);
|
||||
EXPORT m64p_error CALL PluginConfig(void);
|
||||
#endif
|
||||
|
||||
/* PluginConfig2(int romConfig)
|
||||
*
|
||||
* This optional function opens a configuration GUI for the plugin
|
||||
* https://github.com/mupen64plus/mupen64plus-core/pull/774
|
||||
*
|
||||
* romConfig argument determines if it should open the ROM specific config
|
||||
*/
|
||||
typedef m64p_error (*ptr_PluginConfig2)(int);
|
||||
#if defined(M64P_PLUGIN_PROTOTYPES) || defined(M64P_CORE_PROTOTYPES)
|
||||
EXPORT m64p_error CALL PluginConfig2(int);
|
||||
#endif
|
||||
|
||||
/* PluginConfig2HasRomConfig(void)
|
||||
*
|
||||
* This optional function returns wether the plugin supports ROM specific config
|
||||
* using PluginConfig2
|
||||
*
|
||||
*/
|
||||
typedef int (*ptr_PluginConfig2HasRomConfig)(void);
|
||||
#if defined(M64P_PLUGIN_PROTOTYPES) || defined(M64P_CORE_PROTOTYPES)
|
||||
EXPORT int CALL PluginConfig2HasRomConfig(void);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#define OSAL_DYNLIB_HPP
|
||||
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
@@ -25,11 +26,11 @@ typedef void* osal_dynlib_lib_sym;
|
||||
|
||||
// returns library handle for given filename,
|
||||
// return nullptr when invalid library
|
||||
osal_dynlib_lib_handle osal_dynlib_open(const char *);
|
||||
osal_dynlib_lib_handle osal_dynlib_open(std::filesystem::path path);
|
||||
|
||||
// retrieves symbol handle for given library
|
||||
// handle, returns nullptr when not found
|
||||
osal_dynlib_lib_sym osal_dynlib_sym(osal_dynlib_lib_handle, const char *);
|
||||
osal_dynlib_lib_sym osal_dynlib_sym(osal_dynlib_lib_handle, const char* symbol);
|
||||
|
||||
// closes library handle
|
||||
void osal_dynlib_close(osal_dynlib_lib_handle);
|
||||
|
||||
@@ -11,14 +11,14 @@
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
osal_dynlib_lib_handle osal_dynlib_open(const char* file)
|
||||
osal_dynlib_lib_handle osal_dynlib_open(std::filesystem::path path)
|
||||
{
|
||||
return dlopen(file, RTLD_LAZY);
|
||||
return dlopen(path.string().c_str(), RTLD_LAZY);
|
||||
}
|
||||
|
||||
osal_dynlib_lib_sym osal_dynlib_sym(osal_dynlib_lib_handle handle, const char *sym)
|
||||
osal_dynlib_lib_sym osal_dynlib_sym(osal_dynlib_lib_handle handle, const char* symbol)
|
||||
{
|
||||
return dlsym(handle, sym);
|
||||
return dlsym(handle, symbol);
|
||||
}
|
||||
|
||||
void osal_dynlib_close(osal_dynlib_lib_handle handle)
|
||||
|
||||
@@ -9,14 +9,14 @@
|
||||
*/
|
||||
#include "osal_dynlib.hpp"
|
||||
|
||||
osal_dynlib_lib_handle osal_dynlib_open(const char* file)
|
||||
osal_dynlib_lib_handle osal_dynlib_open(std::filesystem::path path)
|
||||
{
|
||||
return LoadLibrary(file);
|
||||
return LoadLibraryW(path.wstring().c_str());
|
||||
}
|
||||
|
||||
osal_dynlib_lib_sym osal_dynlib_sym(osal_dynlib_lib_handle handle, const char *sym)
|
||||
osal_dynlib_lib_sym osal_dynlib_sym(osal_dynlib_lib_handle handle, const char* symbol)
|
||||
{
|
||||
return GetProcAddress(handle, sym);
|
||||
return GetProcAddress(handle, symbol);
|
||||
}
|
||||
|
||||
void osal_dynlib_close(osal_dynlib_lib_handle handle)
|
||||
|
||||
@@ -17,8 +17,9 @@ osal_files_file_time osal_files_get_file_time(std::filesystem::path file)
|
||||
BOOL ret;
|
||||
HANDLE file_handle;
|
||||
FILETIME file_time;
|
||||
ULARGE_INTEGER ularge_int;
|
||||
|
||||
file_handle = CreateFileA(file.string().c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
file_handle = CreateFileW(file.wstring().c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (file_handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
return -1;
|
||||
@@ -36,7 +37,8 @@ osal_files_file_time osal_files_get_file_time(std::filesystem::path file)
|
||||
return -1;
|
||||
}
|
||||
|
||||
// this is probably incorrect but it doesn't matter
|
||||
// for our use-case
|
||||
return file_time.dwLowDateTime + file_time.dwHighDateTime;
|
||||
ularge_int.LowPart = file_time.dwLowDateTime;
|
||||
ularge_int.HighPart = file_time.dwHighDateTime;
|
||||
|
||||
return ularge_int.QuadPart;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ set(CMAKE_AUTOUIC ON)
|
||||
# disable lib prefix
|
||||
set(CMAKE_SHARED_LIBRARY_PREFIX "")
|
||||
|
||||
find_package(${QT_VERSION} COMPONENTS Gui Widgets Core Svg REQUIRED)
|
||||
find_package(Qt6 COMPONENTS Gui Widgets Core Svg REQUIRED)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(SDL2 REQUIRED sdl2)
|
||||
@@ -17,19 +17,33 @@ set(RMG_INPUT_SOURCES
|
||||
UserInterface/Widget/ControllerWidget.ui
|
||||
UserInterface/Widget/ControllerWidget.cpp
|
||||
UserInterface/Widget/ControllerImageWidget.cpp
|
||||
UserInterface/Widget/CustomButton.cpp
|
||||
UserInterface/Widget/HotkeyButton.cpp
|
||||
UserInterface/Widget/MappingButton.cpp
|
||||
UserInterface/Widget/AddMappingButton.cpp
|
||||
UserInterface/Widget/RemoveMappingButton.cpp
|
||||
UserInterface/EventFilter.cpp
|
||||
UserInterface/OptionsDialog.ui
|
||||
UserInterface/OptionsDialog.cpp
|
||||
UserInterface/HotkeysDialog.ui
|
||||
UserInterface/HotkeysDialog.cpp
|
||||
UserInterface/MainDialog.ui
|
||||
UserInterface/MainDialog.cpp
|
||||
UserInterface/UIResources.qrc
|
||||
Utilities/QtKeyToSdl2Key.cpp
|
||||
Utilities/InputDevice.cpp
|
||||
Thread/SDLThread.cpp
|
||||
Thread/HotkeysThread.cpp
|
||||
main.cpp
|
||||
)
|
||||
|
||||
if (VRU)
|
||||
list(APPEND RMG_INPUT_SOURCES
|
||||
VRU.cpp
|
||||
VRUwords.cpp
|
||||
)
|
||||
add_definitions(-DVRU)
|
||||
endif(VRU)
|
||||
|
||||
add_library(RMG-Input SHARED ${RMG_INPUT_SOURCES})
|
||||
|
||||
target_link_libraries(RMG-Input RMG-Core ${SDL2_LIBRARIES})
|
||||
@@ -41,4 +55,4 @@ target_include_directories(RMG-Input PRIVATE
|
||||
${SDL2_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(RMG-Input ${QT_VERSION}::Gui ${QT_VERSION}::Widgets ${QT_VERSION}::Svg)
|
||||
target_link_libraries(RMG-Input Qt6::Gui Qt6::Widgets Qt6::Svg)
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
|
||||
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 3.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "HotkeysThread.hpp"
|
||||
|
||||
#include <RMG-Core/Core.hpp>
|
||||
|
||||
using namespace Thread;
|
||||
|
||||
HotkeysThread::HotkeysThread(std::function<void(int)> checkHotkeysFunc, QObject *parent) : QThread(parent)
|
||||
{
|
||||
this->checkHotkeysFunc = checkHotkeysFunc;
|
||||
}
|
||||
|
||||
HotkeysThread::~HotkeysThread()
|
||||
{
|
||||
if (this->isRunning())
|
||||
{
|
||||
this->StopLoop();
|
||||
}
|
||||
}
|
||||
|
||||
void HotkeysThread::SetState(HotkeysThreadState state)
|
||||
{
|
||||
this->state = state;
|
||||
}
|
||||
|
||||
void HotkeysThread::StopLoop(void)
|
||||
{
|
||||
this->keepLoopRunning = false;
|
||||
while (this->isRunning())
|
||||
{
|
||||
// wait until we're not running anymore
|
||||
}
|
||||
}
|
||||
|
||||
void HotkeysThread::run(void)
|
||||
{
|
||||
while (this->keepLoopRunning)
|
||||
{
|
||||
// sleep for 300ms when no ROM is opened
|
||||
if (this->state == HotkeysThreadState::RomClosed)
|
||||
{
|
||||
QThread::msleep(300);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (CoreIsEmulationPaused())
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
this->checkHotkeysFunc(i);
|
||||
}
|
||||
}
|
||||
|
||||
// sleep for 100ms
|
||||
QThread::msleep(100);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
|
||||
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 3.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef HOTKEYSTHREAD_HPP
|
||||
#define HOTKEYSTHREAD_HPP
|
||||
|
||||
#include <QThread>
|
||||
|
||||
enum class HotkeysThreadState
|
||||
{
|
||||
RomOpened,
|
||||
RomClosed,
|
||||
};
|
||||
|
||||
namespace Thread
|
||||
{
|
||||
class HotkeysThread : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
HotkeysThread(std::function<void(int)> checkHotkeysFunc, QObject *parent);
|
||||
~HotkeysThread(void);
|
||||
|
||||
void run(void) override;
|
||||
|
||||
void SetState(HotkeysThreadState state);
|
||||
|
||||
void StopLoop(void);
|
||||
|
||||
private:
|
||||
bool keepLoopRunning = true;
|
||||
std::function<void(int)> checkHotkeysFunc;
|
||||
HotkeysThreadState state = HotkeysThreadState::RomClosed;
|
||||
|
||||
};
|
||||
} // namespace Thread
|
||||
|
||||
#endif // HOTKEYSTHREAD_HPP
|
||||
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
|
||||
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 3.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "HotkeysDialog.hpp"
|
||||
#include "common.hpp"
|
||||
|
||||
#include <RMG-Core/Core.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace UserInterface;
|
||||
|
||||
HotkeysDialog::HotkeysDialog(QWidget* parent, QList<HotkeySettingMapping> hotkeySettingMappings,
|
||||
bool isGameController, SDL_JoystickID joystickId,
|
||||
bool filterEvents, bool removeDuplicates) : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint)
|
||||
{
|
||||
this->setupUi(this);
|
||||
|
||||
this->currentJoystickId = joystickId;
|
||||
this->isCurrentJoystickGameController = isGameController;
|
||||
this->filterEventsForButtons = filterEvents;
|
||||
this->removeDuplicates = removeDuplicates;
|
||||
|
||||
this->hotkeySettingMappings.append(
|
||||
{
|
||||
{ this->shutdownKeyButton, SettingsID::Input_Hotkey_Shutdown_InputType, SettingsID::Input_Hotkey_Shutdown_Name, SettingsID::Input_Hotkey_Shutdown_Data, SettingsID::Input_Hotkey_Shutdown_ExtraData },
|
||||
{ this->exitKeyButton, SettingsID::Input_Hotkey_Exit_InputType, SettingsID::Input_Hotkey_Exit_Name, SettingsID::Input_Hotkey_Exit_Data, SettingsID::Input_Hotkey_Exit_ExtraData },
|
||||
{ this->softResetKeyButton, SettingsID::Input_Hotkey_SoftReset_InputType, SettingsID::Input_Hotkey_SoftReset_Name, SettingsID::Input_Hotkey_SoftReset_Data, SettingsID::Input_Hotkey_SoftReset_ExtraData },
|
||||
{ this->hardResetKeyButton, SettingsID::Input_Hotkey_HardReset_InputType, SettingsID::Input_Hotkey_HardReset_Name, SettingsID::Input_Hotkey_HardReset_Data, SettingsID::Input_Hotkey_HardReset_ExtraData },
|
||||
{ this->pauseKeyButton, SettingsID::Input_Hotkey_Resume_InputType, SettingsID::Input_Hotkey_Resume_Name, SettingsID::Input_Hotkey_Resume_Data, SettingsID::Input_Hotkey_Resume_ExtraData },
|
||||
{ this->generateBitmapKeyButton, SettingsID::Input_Hotkey_Screenshot_InputType, SettingsID::Input_Hotkey_Screenshot_Name, SettingsID::Input_Hotkey_Screenshot_Data, SettingsID::Input_Hotkey_Screenshot_ExtraData },
|
||||
{ this->limitFPSKeyButton, SettingsID::Input_Hotkey_LimitFPS_InputType, SettingsID::Input_Hotkey_LimitFPS_Name, SettingsID::Input_Hotkey_LimitFPS_Data, SettingsID::Input_Hotkey_LimitFPS_ExtraData },
|
||||
{ this->speedFactor25KeyButton, SettingsID::Input_Hotkey_SpeedFactor25_InputType, SettingsID::Input_Hotkey_SpeedFactor25_Name, SettingsID::Input_Hotkey_SpeedFactor25_Data, SettingsID::Input_Hotkey_SpeedFactor25_ExtraData },
|
||||
{ this->speedFactor50KeyButton, SettingsID::Input_Hotkey_SpeedFactor50_InputType, SettingsID::Input_Hotkey_SpeedFactor50_Name, SettingsID::Input_Hotkey_SpeedFactor50_Data, SettingsID::Input_Hotkey_SpeedFactor50_ExtraData },
|
||||
{ this->speedFactor75KeyButton, SettingsID::Input_Hotkey_SpeedFactor75_InputType, SettingsID::Input_Hotkey_SpeedFactor75_Name, SettingsID::Input_Hotkey_SpeedFactor75_Data, SettingsID::Input_Hotkey_SpeedFactor75_ExtraData },
|
||||
{ this->speedFactor100KeyButton, SettingsID::Input_Hotkey_SpeedFactor100_InputType, SettingsID::Input_Hotkey_SpeedFactor100_Name, SettingsID::Input_Hotkey_SpeedFactor100_Data, SettingsID::Input_Hotkey_SpeedFactor100_ExtraData },
|
||||
{ this->speedFactor125KeyButton, SettingsID::Input_Hotkey_SpeedFactor125_InputType, SettingsID::Input_Hotkey_SpeedFactor125_Name, SettingsID::Input_Hotkey_SpeedFactor125_Data, SettingsID::Input_Hotkey_SpeedFactor125_ExtraData },
|
||||
{ this->speedFactor150KeyButton, SettingsID::Input_Hotkey_SpeedFactor150_InputType, SettingsID::Input_Hotkey_SpeedFactor150_Name, SettingsID::Input_Hotkey_SpeedFactor150_Data, SettingsID::Input_Hotkey_SpeedFactor150_ExtraData },
|
||||
{ this->speedFactor175KeyButton, SettingsID::Input_Hotkey_SpeedFactor175_InputType, SettingsID::Input_Hotkey_SpeedFactor175_Name, SettingsID::Input_Hotkey_SpeedFactor175_Data, SettingsID::Input_Hotkey_SpeedFactor175_ExtraData },
|
||||
{ this->speedFactor200KeyButton, SettingsID::Input_Hotkey_SpeedFactor200_InputType, SettingsID::Input_Hotkey_SpeedFactor200_Name, SettingsID::Input_Hotkey_SpeedFactor200_Data, SettingsID::Input_Hotkey_SpeedFactor200_ExtraData },
|
||||
{ this->speedFactor225KeyButton, SettingsID::Input_Hotkey_SpeedFactor225_InputType, SettingsID::Input_Hotkey_SpeedFactor225_Name, SettingsID::Input_Hotkey_SpeedFactor225_Data, SettingsID::Input_Hotkey_SpeedFactor225_ExtraData },
|
||||
{ this->speedFactor250KeyButton, SettingsID::Input_Hotkey_SpeedFactor250_InputType, SettingsID::Input_Hotkey_SpeedFactor250_Name, SettingsID::Input_Hotkey_SpeedFactor250_Data, SettingsID::Input_Hotkey_SpeedFactor250_ExtraData },
|
||||
{ this->speedFactor275KeyButton, SettingsID::Input_Hotkey_SpeedFactor275_InputType, SettingsID::Input_Hotkey_SpeedFactor275_Name, SettingsID::Input_Hotkey_SpeedFactor275_Data, SettingsID::Input_Hotkey_SpeedFactor275_ExtraData },
|
||||
{ this->speedFactor300KeyButton, SettingsID::Input_Hotkey_SpeedFactor300_InputType, SettingsID::Input_Hotkey_SpeedFactor300_Name, SettingsID::Input_Hotkey_SpeedFactor300_Data, SettingsID::Input_Hotkey_SpeedFactor300_ExtraData },
|
||||
{ this->saveStateKeyButton, SettingsID::Input_Hotkey_SaveState_InputType, SettingsID::Input_Hotkey_SaveState_Name, SettingsID::Input_Hotkey_SaveState_Data, SettingsID::Input_Hotkey_SaveState_ExtraData },
|
||||
{ this->loadStateKeyButton, SettingsID::Input_Hotkey_LoadState_InputType, SettingsID::Input_Hotkey_LoadState_Name, SettingsID::Input_Hotkey_LoadState_Data, SettingsID::Input_Hotkey_LoadState_ExtraData },
|
||||
{ this->gsButtonKeyButton, SettingsID::Input_Hotkey_GSButton_InputType, SettingsID::Input_Hotkey_GSButton_Name, SettingsID::Input_Hotkey_GSButton_Data, SettingsID::Input_Hotkey_GSButton_ExtraData },
|
||||
{ this->saveState0KeyButton, SettingsID::Input_Hotkey_SaveStateSlot0_InputType, SettingsID::Input_Hotkey_SaveStateSlot0_Name, SettingsID::Input_Hotkey_SaveStateSlot0_Data, SettingsID::Input_Hotkey_SaveStateSlot0_ExtraData },
|
||||
{ this->saveState1KeyButton, SettingsID::Input_Hotkey_SaveStateSlot1_InputType, SettingsID::Input_Hotkey_SaveStateSlot1_Name, SettingsID::Input_Hotkey_SaveStateSlot1_Data, SettingsID::Input_Hotkey_SaveStateSlot1_ExtraData },
|
||||
{ this->saveState2KeyButton, SettingsID::Input_Hotkey_SaveStateSlot2_InputType, SettingsID::Input_Hotkey_SaveStateSlot2_Name, SettingsID::Input_Hotkey_SaveStateSlot2_Data, SettingsID::Input_Hotkey_SaveStateSlot2_ExtraData },
|
||||
{ this->saveState3KeyButton, SettingsID::Input_Hotkey_SaveStateSlot3_InputType, SettingsID::Input_Hotkey_SaveStateSlot3_Name, SettingsID::Input_Hotkey_SaveStateSlot3_Data, SettingsID::Input_Hotkey_SaveStateSlot3_ExtraData },
|
||||
{ this->saveState4KeyButton, SettingsID::Input_Hotkey_SaveStateSlot4_InputType, SettingsID::Input_Hotkey_SaveStateSlot4_Name, SettingsID::Input_Hotkey_SaveStateSlot4_Data, SettingsID::Input_Hotkey_SaveStateSlot4_ExtraData },
|
||||
{ this->saveState5KeyButton, SettingsID::Input_Hotkey_SaveStateSlot5_InputType, SettingsID::Input_Hotkey_SaveStateSlot5_Name, SettingsID::Input_Hotkey_SaveStateSlot5_Data, SettingsID::Input_Hotkey_SaveStateSlot5_ExtraData },
|
||||
{ this->saveState6KeyButton, SettingsID::Input_Hotkey_SaveStateSlot6_InputType, SettingsID::Input_Hotkey_SaveStateSlot6_Name, SettingsID::Input_Hotkey_SaveStateSlot6_Data, SettingsID::Input_Hotkey_SaveStateSlot6_ExtraData },
|
||||
{ this->saveState7KeyButton, SettingsID::Input_Hotkey_SaveStateSlot7_InputType, SettingsID::Input_Hotkey_SaveStateSlot7_Name, SettingsID::Input_Hotkey_SaveStateSlot7_Data, SettingsID::Input_Hotkey_SaveStateSlot7_ExtraData },
|
||||
{ this->saveState8KeyButton, SettingsID::Input_Hotkey_SaveStateSlot8_InputType, SettingsID::Input_Hotkey_SaveStateSlot8_Name, SettingsID::Input_Hotkey_SaveStateSlot8_Data, SettingsID::Input_Hotkey_SaveStateSlot8_ExtraData },
|
||||
{ this->saveState9KeyButton, SettingsID::Input_Hotkey_SaveStateSlot9_InputType, SettingsID::Input_Hotkey_SaveStateSlot9_Name, SettingsID::Input_Hotkey_SaveStateSlot9_Data, SettingsID::Input_Hotkey_SaveStateSlot9_ExtraData },
|
||||
{ this->fullscreenKeyButton, SettingsID::Input_Hotkey_Fullscreen_InputType, SettingsID::Input_Hotkey_Fullscreen_Name, SettingsID::Input_Hotkey_Fullscreen_Data, SettingsID::Input_Hotkey_Fullscreen_ExtraData },
|
||||
});
|
||||
|
||||
// initialize buttons
|
||||
for (auto& mapping : this->hotkeySettingMappings)
|
||||
{
|
||||
mapping.button->Initialize(this);
|
||||
}
|
||||
|
||||
// initialize icons
|
||||
this->infoIconLabel->setPixmap(QIcon::fromTheme("information-line").pixmap(16, 16));
|
||||
this->infoIconLabel_2->setPixmap(QIcon::fromTheme("information-line").pixmap(16, 16));
|
||||
this->infoIconLabel_3->setPixmap(QIcon::fromTheme("information-line").pixmap(16, 16));
|
||||
this->infoIconLabel_4->setPixmap(QIcon::fromTheme("information-line").pixmap(16, 16));
|
||||
|
||||
// load settings from given mappings
|
||||
for (int i = 0; i < hotkeySettingMappings.size(); i++)
|
||||
{
|
||||
auto& buttonMapping = this->hotkeySettingMappings.at(i);
|
||||
auto& givenMapping = hotkeySettingMappings.at(i);
|
||||
|
||||
for (int y = 0; y < givenMapping.inputTypes.size(); y++)
|
||||
{
|
||||
buttonMapping.button->AddInputData(
|
||||
(InputType)givenMapping.inputTypes.at(y),
|
||||
givenMapping.inputData.at(y),
|
||||
givenMapping.extraInputData.at(y),
|
||||
QString::fromStdString(givenMapping.inputText.at(y))
|
||||
);
|
||||
}
|
||||
|
||||
buttonMapping.button->FinishState();
|
||||
}
|
||||
}
|
||||
|
||||
void HotkeysDialog::on_MainDialog_SdlEvent(SDL_Event* event)
|
||||
{
|
||||
switch (event->type)
|
||||
{
|
||||
default:
|
||||
break;
|
||||
|
||||
case SDL_CONTROLLERBUTTONDOWN:
|
||||
case SDL_JOYBUTTONDOWN:
|
||||
{
|
||||
SDL_JoystickID joystickId = -1;
|
||||
InputType inputType = InputType::Invalid;
|
||||
int sdlButton = 0;
|
||||
QString sdlButtonName;
|
||||
|
||||
if (event->type == SDL_CONTROLLERBUTTONDOWN)
|
||||
{ // gamepad button
|
||||
if (!this->isCurrentJoystickGameController &&
|
||||
this->filterEventsForButtons)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
joystickId = event->cbutton.which;
|
||||
inputType = InputType::GamepadButton;
|
||||
sdlButton = event->cbutton.button;
|
||||
sdlButtonName = SDL_GameControllerGetStringForButton((SDL_GameControllerButton)sdlButton);
|
||||
}
|
||||
else
|
||||
{ // joystick button
|
||||
if (this->isCurrentJoystickGameController &&
|
||||
this->filterEventsForButtons)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
joystickId = event->jbutton.which;
|
||||
inputType = InputType::JoystickButton;
|
||||
sdlButton = event->jbutton.button;
|
||||
sdlButtonName = "button " + QString::number(sdlButton);
|
||||
}
|
||||
|
||||
// make sure we have the right joystick
|
||||
if (joystickId != this->currentJoystickId)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (this->currentHotkeyButton != nullptr)
|
||||
{
|
||||
this->currentHotkeyButton->AddInputData(
|
||||
inputType,
|
||||
sdlButton,
|
||||
0,
|
||||
sdlButtonName
|
||||
);
|
||||
}
|
||||
} break;
|
||||
|
||||
case SDL_CONTROLLERBUTTONUP:
|
||||
case SDL_JOYBUTTONUP:
|
||||
{
|
||||
SDL_JoystickID joystickId = -1;
|
||||
|
||||
if (event->type == SDL_CONTROLLERBUTTONUP)
|
||||
{ // gamepad button
|
||||
if (!this->isCurrentJoystickGameController &&
|
||||
this->filterEventsForButtons)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
joystickId = event->cbutton.which;
|
||||
}
|
||||
else
|
||||
{ // joystick button
|
||||
if (this->isCurrentJoystickGameController &&
|
||||
this->filterEventsForButtons)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
joystickId = event->jbutton.which;
|
||||
}
|
||||
|
||||
// make sure we have the right joystick
|
||||
if (joystickId != this->currentJoystickId)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (this->currentHotkeyButton != nullptr)
|
||||
{
|
||||
this->currentHotkeyButton->StopTimer();
|
||||
this->currentHotkeyButton->FinishState();
|
||||
this->currentHotkeyButton->clearFocus();
|
||||
this->currentHotkeyButton = nullptr;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void HotkeysDialog::on_HotkeyButton_Released(Widget::HotkeyButton* button)
|
||||
{
|
||||
if (this->currentHotkeyButton != nullptr)
|
||||
{
|
||||
this->currentHotkeyButton->StopTimer();
|
||||
this->currentHotkeyButton->RestoreState();
|
||||
}
|
||||
|
||||
this->currentHotkeyButton = button;
|
||||
|
||||
button->SaveState();
|
||||
button->StartTimer();
|
||||
}
|
||||
|
||||
void HotkeysDialog::on_HotkeyButton_TimerFinished(Widget::HotkeyButton* button)
|
||||
{
|
||||
if (this->currentHotkeyButton == button)
|
||||
{
|
||||
this->currentHotkeyButton = nullptr;
|
||||
}
|
||||
|
||||
button->RestoreState();
|
||||
button->clearFocus();
|
||||
}
|
||||
|
||||
void HotkeysDialog::on_HotkeyButton_StateFinished(Widget::HotkeyButton* button)
|
||||
{
|
||||
// do nothing when the user
|
||||
// doesn't want us to remove
|
||||
// duplicates
|
||||
if (!this->removeDuplicates)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<int> inputType = button->GetInputType();
|
||||
std::vector<int> inputData = button->GetInputData();
|
||||
std::vector<int> extraInputData = button->GetExtraInputData();
|
||||
|
||||
for (auto& mapping : this->hotkeySettingMappings)
|
||||
{
|
||||
// skip ourselves
|
||||
if (mapping.button == button)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inputType == mapping.button->GetInputType() &&
|
||||
inputData == mapping.button->GetInputData() &&
|
||||
extraInputData == mapping.button->GetExtraInputData())
|
||||
{
|
||||
mapping.button->Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QList<HotkeySettingMapping> HotkeysDialog::GetSettingMappings()
|
||||
{
|
||||
QList<HotkeySettingMapping> mappings;
|
||||
|
||||
for (auto& mapping : this->hotkeySettingMappings)
|
||||
{
|
||||
mappings.append({
|
||||
mapping.button->GetInputType(),
|
||||
mapping.button->GetInputData(),
|
||||
mapping.button->GetExtraInputData(),
|
||||
mapping.button->GetInputText(),
|
||||
mapping.inputTypeSettingsId,
|
||||
mapping.nameSettingsId,
|
||||
mapping.dataSettingsId,
|
||||
mapping.extraDataSettingsId
|
||||
});
|
||||
}
|
||||
|
||||
return mappings;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
|
||||
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 3.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef HOTKEYSDIALOG_HPP
|
||||
#define HOTKEYSDIALOG_HPP
|
||||
|
||||
#include <QDialog>
|
||||
#include <string>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include <RMG-Core/Core.hpp>
|
||||
|
||||
#include "ui_HotkeysDialog.h"
|
||||
|
||||
namespace UserInterface
|
||||
{
|
||||
|
||||
struct HotkeySettingMapping
|
||||
{
|
||||
std::vector<int> inputTypes;
|
||||
std::vector<int> inputData;
|
||||
std::vector<int> extraInputData;
|
||||
std::vector<std::string> inputText;
|
||||
SettingsID inputTypeSettingsId;
|
||||
SettingsID nameSettingsId;
|
||||
SettingsID dataSettingsId;
|
||||
SettingsID extraDataSettingsId;
|
||||
};
|
||||
|
||||
class HotkeysDialog : public QDialog, private Ui::HotkeysDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
|
||||
SDL_JoystickID currentJoystickId = -1;
|
||||
bool isCurrentJoystickGameController = false;
|
||||
bool filterEventsForButtons = false;
|
||||
bool removeDuplicates = false;
|
||||
|
||||
struct hotkeySettingMapping
|
||||
{
|
||||
Widget::HotkeyButton* button;
|
||||
SettingsID inputTypeSettingsId;
|
||||
SettingsID nameSettingsId;
|
||||
SettingsID dataSettingsId;
|
||||
SettingsID extraDataSettingsId;
|
||||
};
|
||||
|
||||
QList<hotkeySettingMapping> hotkeySettingMappings;
|
||||
|
||||
Widget::HotkeyButton* currentHotkeyButton = nullptr;
|
||||
|
||||
public:
|
||||
HotkeysDialog(QWidget *parent, QList<HotkeySettingMapping> hotkeySettingMappings,
|
||||
bool isGameController, SDL_JoystickID joystickId,
|
||||
bool filterEvents, bool removeDuplicates);
|
||||
|
||||
QList<HotkeySettingMapping> GetSettingMappings();
|
||||
|
||||
public slots:
|
||||
void on_MainDialog_SdlEvent(SDL_Event* event);
|
||||
|
||||
void on_HotkeyButton_Released(Widget::HotkeyButton* button);
|
||||
void on_HotkeyButton_TimerFinished(Widget::HotkeyButton* button);
|
||||
void on_HotkeyButton_StateFinished(Widget::HotkeyButton* button);
|
||||
};
|
||||
}
|
||||
|
||||
#endif // HOTKEYSDIALOG_HPP
|
||||
@@ -0,0 +1,920 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>HotkeysDialog</class>
|
||||
<widget class="QDialog" name="HotkeysDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>377</width>
|
||||
<height>563</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Hotkeys</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QTabWidget" name="keybindingsMenuTabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="systemMenuTab">
|
||||
<attribute name="title">
|
||||
<string>System</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_72">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_69">
|
||||
<property name="text">
|
||||
<string>Shutdown</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UserInterface::Widget::HotkeyButton" name="shutdownKeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_75">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_72">
|
||||
<property name="text">
|
||||
<string>Soft Reset</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UserInterface::Widget::HotkeyButton" name="softResetKeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_76">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_73">
|
||||
<property name="text">
|
||||
<string>Hard Reset</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UserInterface::Widget::HotkeyButton" name="hardResetKeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_89">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_88">
|
||||
<property name="text">
|
||||
<string>Pause</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UserInterface::Widget::HotkeyButton" name="pauseKeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_77">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_74">
|
||||
<property name="text">
|
||||
<string>Capture Screenshot</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UserInterface::Widget::HotkeyButton" name="generateBitmapKeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_78">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_75">
|
||||
<property name="text">
|
||||
<string>Limit FPS</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UserInterface::Widget::HotkeyButton" name="limitFPSKeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_80">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_77">
|
||||
<property name="text">
|
||||
<string>Save State</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UserInterface::Widget::HotkeyButton" name="saveStateKeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_82">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_79">
|
||||
<property name="text">
|
||||
<string>Load State</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UserInterface::Widget::HotkeyButton" name="loadStateKeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_85">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_82">
|
||||
<property name="text">
|
||||
<string>GS Button</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UserInterface::Widget::HotkeyButton" name="gsButtonKeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_74">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_71">
|
||||
<property name="text">
|
||||
<string>Exit</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UserInterface::Widget::HotkeyButton" name="exitKeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_14">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="verticalLayout_0">
|
||||
<item>
|
||||
<widget class="QLabel" name="infoIconLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap>:/newPrefix/Resource/Info.ico</pixmap>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_86">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="midLineWidth">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>These hotkeys are only for controllers</string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::PlainText</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="speedFactorTab">
|
||||
<attribute name="title">
|
||||
<string>Speed Factor</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_105">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_102">
|
||||
<property name="text">
|
||||
<string>25%</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UserInterface::Widget::HotkeyButton" name="speedFactor25KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_106">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_103">
|
||||
<property name="text">
|
||||
<string>50%</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UserInterface::Widget::HotkeyButton" name="speedFactor50KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_107">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_104">
|
||||
<property name="text">
|
||||
<string>75%</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UserInterface::Widget::HotkeyButton" name="speedFactor75KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_108">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_105">
|
||||
<property name="text">
|
||||
<string>100%</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UserInterface::Widget::HotkeyButton" name="speedFactor100KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_109">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_106">
|
||||
<property name="text">
|
||||
<string>125%</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UserInterface::Widget::HotkeyButton" name="speedFactor125KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_110">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_107">
|
||||
<property name="text">
|
||||
<string>150%</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UserInterface::Widget::HotkeyButton" name="speedFactor150KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_111">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_108">
|
||||
<property name="text">
|
||||
<string>175%</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UserInterface::Widget::HotkeyButton" name="speedFactor175KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_112">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_109">
|
||||
<property name="text">
|
||||
<string>200%</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UserInterface::Widget::HotkeyButton" name="speedFactor200KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_113">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_110">
|
||||
<property name="text">
|
||||
<string>225%</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UserInterface::Widget::HotkeyButton" name="speedFactor225KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_114">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_111">
|
||||
<property name="text">
|
||||
<string>250%</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UserInterface::Widget::HotkeyButton" name="speedFactor250KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_115">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_112">
|
||||
<property name="text">
|
||||
<string>275%</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UserInterface::Widget::HotkeyButton" name="speedFactor275KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_116">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_113">
|
||||
<property name="text">
|
||||
<string>300%</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UserInterface::Widget::HotkeyButton" name="speedFactor300KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_9">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="verticalLayout_1">
|
||||
<item>
|
||||
<widget class="QLabel" name="infoIconLabel_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap>:/newPrefix/Resource/Info.ico</pixmap>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_87">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="midLineWidth">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>These hotkeys are only for controllers</string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::PlainText</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="currentSaveStateTab">
|
||||
<attribute name="title">
|
||||
<string>Current Save State</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_95">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_92">
|
||||
<property name="text">
|
||||
<string>Slot 0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UserInterface::Widget::HotkeyButton" name="saveState0KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_96">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_93">
|
||||
<property name="text">
|
||||
<string>Slot 1</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UserInterface::Widget::HotkeyButton" name="saveState1KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_97">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_94">
|
||||
<property name="text">
|
||||
<string>Slot 2</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UserInterface::Widget::HotkeyButton" name="saveState2KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_98">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_95">
|
||||
<property name="text">
|
||||
<string>Slot 3</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UserInterface::Widget::HotkeyButton" name="saveState3KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_99">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_96">
|
||||
<property name="text">
|
||||
<string>Slot 4</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UserInterface::Widget::HotkeyButton" name="saveState4KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_100">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_97">
|
||||
<property name="text">
|
||||
<string>Slot 5</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UserInterface::Widget::HotkeyButton" name="saveState5KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_101">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_98">
|
||||
<property name="text">
|
||||
<string>Slot 6</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UserInterface::Widget::HotkeyButton" name="saveState6KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_102">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_99">
|
||||
<property name="text">
|
||||
<string>Slot 7</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UserInterface::Widget::HotkeyButton" name="saveState7KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_104">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_101">
|
||||
<property name="text">
|
||||
<string>Slot 8</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UserInterface::Widget::HotkeyButton" name="saveState8KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_103">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_100">
|
||||
<property name="text">
|
||||
<string>Slot 9</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UserInterface::Widget::HotkeyButton" name="saveState9KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_13">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QLabel" name="infoIconLabel_3">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap>:/newPrefix/Resource/Info.ico</pixmap>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_89">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="midLineWidth">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>These hotkeys are only for controllers</string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::PlainText</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="viewMenuTab">
|
||||
<attribute name="title">
|
||||
<string>View</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_7">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_86">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_83">
|
||||
<property name="text">
|
||||
<string>Full Screen</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UserInterface::Widget::HotkeyButton" name="fullscreenKeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_16">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>419</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="verticalLayout_6">
|
||||
<item>
|
||||
<widget class="QLabel" name="infoIconLabel_4">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap>:/newPrefix/Resource/Info.ico</pixmap>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_90">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="midLineWidth">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>These hotkeys are only for controllers</string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::PlainText</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>UserInterface::Widget::HotkeyButton</class>
|
||||
<extends>QPushButton</extends>
|
||||
<header>UserInterface/Widget/HotkeyButton.hpp</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>HotkeysDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>HotkeysDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
using namespace UserInterface;
|
||||
|
||||
MainDialog::MainDialog(QWidget* parent, Thread::SDLThread* sdlThread) : QDialog(parent)
|
||||
MainDialog::MainDialog(QWidget* parent, Thread::SDLThread* sdlThread, bool romConfig) : QDialog(parent)
|
||||
{
|
||||
this->setupUi(this);
|
||||
this->setWindowIcon(QIcon(":Resource/RMG.png"));
|
||||
@@ -41,7 +41,8 @@ MainDialog::MainDialog(QWidget* parent, Thread::SDLThread* sdlThread) : QDialog(
|
||||
for (int i = 0; i < this->tabWidget->count(); i++)
|
||||
{
|
||||
Widget::ControllerWidget* widget = new Widget::ControllerWidget(this, this->eventFilter);
|
||||
widget->SetSettingsSection("Rosalie's Mupen GUI - Input Plugin Profile " + QString::number(i));
|
||||
widget->SetOnlyLoadGameProfile(romConfig);
|
||||
widget->SetSettingsSection("Player " + QString::number(i + 1), "Rosalie's Mupen GUI - Input Plugin Profile " + QString::number(i));
|
||||
widget->LoadSettings();
|
||||
this->tabWidget->widget(i)->layout()->addWidget(widget);
|
||||
controllerWidgets.push_back(widget);
|
||||
@@ -49,12 +50,28 @@ MainDialog::MainDialog(QWidget* parent, Thread::SDLThread* sdlThread) : QDialog(
|
||||
&MainDialog::on_ControllerWidget_CurrentInputDeviceChanged);
|
||||
connect(widget, &Widget::ControllerWidget::RefreshInputDevicesButtonClicked, this,
|
||||
&MainDialog::on_ControllerWidget_RefreshInputDevicesButtonClicked);
|
||||
connect(widget, &Widget::ControllerWidget::UserProfileAdded, this,
|
||||
&MainDialog::on_ControllerWidget_UserProfileAdded);
|
||||
connect(widget, &Widget::ControllerWidget::UserProfileRemoved, this,
|
||||
&MainDialog::on_ControllerWidget_UserProfileRemoved);
|
||||
}
|
||||
|
||||
// always add keyboard device
|
||||
for (auto& controllerWidget : this->controllerWidgets)
|
||||
{
|
||||
controllerWidget->AddInputDevice("Keyboard", -1);
|
||||
#ifdef VRU
|
||||
// both games which support the VRU,
|
||||
// require the VRU to be in port #4,
|
||||
// so we only have to expose it there
|
||||
if (controllerWidget == this->controllerWidgets.last())
|
||||
{
|
||||
controllerWidget->AddInputDevice("Voice Recognition Unit", (int)InputDeviceType::EmulateVRU);
|
||||
}
|
||||
#endif // VRU
|
||||
controllerWidget->AddInputDevice("None", (int)InputDeviceType::None);
|
||||
controllerWidget->AddInputDevice("Automatic", (int)InputDeviceType::Automatic);
|
||||
controllerWidget->AddInputDevice("Keyboard", (int)InputDeviceType::Keyboard);
|
||||
controllerWidget->SetInitialized(true);
|
||||
}
|
||||
|
||||
// fill device list at least once
|
||||
@@ -90,30 +107,50 @@ void MainDialog::openInputDevice(QString deviceName, int deviceNum)
|
||||
{
|
||||
SDL_JoystickID joystickId;
|
||||
Widget::ControllerWidget* controllerWidget;
|
||||
controllerWidget = controllerWidgets.at(this->tabWidget->currentIndex());
|
||||
controllerWidget = this->controllerWidgets.at(this->tabWidget->currentIndex());
|
||||
|
||||
// we don't need to open a keyboard
|
||||
if (deviceNum == -1)
|
||||
if (deviceNum == (int)InputDeviceType::None ||
|
||||
deviceNum == (int)InputDeviceType::Keyboard)
|
||||
{
|
||||
currentDeviceName = "";
|
||||
currentDeviceNum = -1;
|
||||
controllerWidget->SetCurrentJoystickID(-1);
|
||||
this->currentDeviceName = "";
|
||||
this->currentDeviceNum = deviceNum;
|
||||
controllerWidget->SetCurrentJoystickID(this->currentDeviceNum);
|
||||
return;
|
||||
}
|
||||
|
||||
// handle automatic mode
|
||||
if (deviceNum == (int)InputDeviceType::Automatic)
|
||||
{
|
||||
int currentIndex = this->tabWidget->currentIndex();
|
||||
if (currentIndex < this->inputDeviceList.size())
|
||||
{ // use device when there's one
|
||||
deviceNum = this->inputDeviceList.at(currentIndex).deviceNum;
|
||||
}
|
||||
else
|
||||
{ // no device found, fallback to keyboard
|
||||
this->currentDeviceName = "";
|
||||
this->currentDeviceNum = (int)InputDeviceType::Keyboard;
|
||||
controllerWidget->SetCurrentJoystickID(this->currentDeviceNum);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (SDL_IsGameController(deviceNum) == SDL_TRUE)
|
||||
{
|
||||
currentJoystick = nullptr;
|
||||
currentController = SDL_GameControllerOpen(deviceNum);
|
||||
this->currentJoystick = nullptr;
|
||||
this->currentController = SDL_GameControllerOpen(deviceNum);
|
||||
}
|
||||
else
|
||||
{
|
||||
currentJoystick = SDL_JoystickOpen(deviceNum);
|
||||
currentController = nullptr;
|
||||
this->currentJoystick = SDL_JoystickOpen(deviceNum);
|
||||
this->currentController = nullptr;
|
||||
}
|
||||
currentDeviceNum = deviceNum;
|
||||
|
||||
this->currentDeviceNum = deviceNum;
|
||||
joystickId = SDL_JoystickGetDeviceInstanceID(deviceNum);
|
||||
controllerWidget->SetCurrentJoystickID(joystickId);
|
||||
controllerWidget->SetIsCurrentJoystickGameController(currentController != nullptr);
|
||||
}
|
||||
|
||||
void MainDialog::closeInputDevice()
|
||||
@@ -193,8 +230,9 @@ void MainDialog::on_ControllerWidget_CurrentInputDeviceChanged(ControllerWidget*
|
||||
this->closeInputDevice();
|
||||
|
||||
// only open device,
|
||||
// when it's not a keyboard
|
||||
if (deviceNum != -1)
|
||||
// when needed
|
||||
if (deviceNum != (int)InputDeviceType::None &&
|
||||
deviceNum != (int)InputDeviceType::Keyboard)
|
||||
{
|
||||
this->openInputDevice(deviceName, deviceNum);
|
||||
}
|
||||
@@ -212,21 +250,54 @@ void MainDialog::on_ControllerWidget_RefreshInputDevicesButtonClicked()
|
||||
this->sdlThread->SetAction(SDLThreadAction::GetInputDevices);
|
||||
}
|
||||
|
||||
void MainDialog::on_ControllerWidget_UserProfileAdded(QString name, QString section)
|
||||
{
|
||||
for (auto& controllerWidget : this->controllerWidgets)
|
||||
{
|
||||
controllerWidget->AddUserProfile(name, section);
|
||||
}
|
||||
}
|
||||
|
||||
void MainDialog::on_ControllerWidget_UserProfileRemoved(QString name, QString section)
|
||||
{
|
||||
for (auto& controllerWidget : this->controllerWidgets)
|
||||
{
|
||||
controllerWidget->RemoveUserProfile(name, section);
|
||||
}
|
||||
}
|
||||
|
||||
void MainDialog::on_tabWidget_currentChanged(int index)
|
||||
{
|
||||
QString deviceName;
|
||||
int deviceNum;
|
||||
int deviceNum;
|
||||
Widget::ControllerWidget* controllerWidget;
|
||||
|
||||
Widget::ControllerWidget* controllerWidget = controllerWidgets.at(index);
|
||||
// save previous tab's user profile
|
||||
if (previousTabWidgetIndex != -1 &&
|
||||
previousTabWidgetIndex != index)
|
||||
{
|
||||
controllerWidget = controllerWidgets.at(previousTabWidgetIndex);
|
||||
controllerWidget->SaveUserProfileSettings();
|
||||
}
|
||||
|
||||
// update previous index
|
||||
previousTabWidgetIndex = index;
|
||||
|
||||
// prepare switched tab
|
||||
controllerWidget = controllerWidgets.at(index);
|
||||
controllerWidget->ClearControllerImage();
|
||||
|
||||
this->closeInputDevice();
|
||||
|
||||
// re-load user profile settings
|
||||
controllerWidget->LoadUserProfileSettings();
|
||||
|
||||
// retrieve current input device
|
||||
controllerWidget->GetCurrentInputDevice(deviceName, deviceNum);
|
||||
|
||||
// only open device,
|
||||
// when it's not a keyboard
|
||||
if (deviceNum != -1)
|
||||
// only open device when needed
|
||||
if (deviceNum != (int)InputDeviceType::None &&
|
||||
deviceNum != (int)InputDeviceType::Keyboard)
|
||||
{
|
||||
this->openInputDevice(deviceName, deviceNum);
|
||||
}
|
||||
@@ -309,19 +380,37 @@ void MainDialog::on_EventFilter_KeyReleased(QKeyEvent *event)
|
||||
SDL_PeepEvents(&sdlEvent, 1, SDL_ADDEVENT, 0, 0);
|
||||
}
|
||||
|
||||
void MainDialog::on_buttonBox_clicked(QAbstractButton *button)
|
||||
void MainDialog::accept(void)
|
||||
{
|
||||
QPushButton *pushButton = (QPushButton *)button;
|
||||
QPushButton *okButton = this->buttonBox->button(QDialogButtonBox::Ok);
|
||||
Widget::ControllerWidget* controllerWidget;
|
||||
int currentIndex = this->tabWidget->currentIndex();
|
||||
|
||||
if (pushButton == okButton)
|
||||
for (int i = 0; i < this->controllerWidgets.count(); i++)
|
||||
{
|
||||
for (auto& controllerWidget : this->controllerWidgets)
|
||||
if (i != currentIndex)
|
||||
{
|
||||
controllerWidget = this->controllerWidgets.at(i);
|
||||
controllerWidget->SaveSettings();
|
||||
}
|
||||
|
||||
CoreSettingsSave();
|
||||
}
|
||||
|
||||
// the widget which has focus should be saved last
|
||||
controllerWidget = this->controllerWidgets.at(currentIndex);
|
||||
controllerWidget->SaveSettings();
|
||||
|
||||
CoreSettingsSave();
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void MainDialog::reject(void)
|
||||
{
|
||||
for (auto& controllerWidget : this->controllerWidgets)
|
||||
{
|
||||
controllerWidget->RevertSettings();
|
||||
}
|
||||
|
||||
CoreSettingsSave();
|
||||
|
||||
QDialog::reject();
|
||||
}
|
||||
|
||||
@@ -43,11 +43,13 @@ private:
|
||||
bool updatingDeviceList = false;
|
||||
|
||||
QList<Widget::ControllerWidget*> controllerWidgets;
|
||||
SDL_Joystick* currentJoystick = nullptr;
|
||||
SDL_Joystick* currentJoystick = nullptr;
|
||||
SDL_GameController* currentController = nullptr;
|
||||
QString currentDeviceName;
|
||||
int currentDeviceNum = 0;
|
||||
|
||||
int previousTabWidgetIndex = 0;
|
||||
|
||||
EventFilter* eventFilter;
|
||||
|
||||
void addInputDevice(QString, int);
|
||||
@@ -57,7 +59,7 @@ private:
|
||||
void closeInputDevice();
|
||||
|
||||
public:
|
||||
MainDialog(QWidget *parent, Thread::SDLThread*);
|
||||
MainDialog(QWidget *parent, Thread::SDLThread*, bool);
|
||||
~MainDialog(void);
|
||||
|
||||
public slots:
|
||||
@@ -65,7 +67,10 @@ public slots:
|
||||
|
||||
void on_ControllerWidget_CurrentInputDeviceChanged(ControllerWidget*, QString, int);
|
||||
void on_ControllerWidget_RefreshInputDevicesButtonClicked();
|
||||
|
||||
|
||||
void on_ControllerWidget_UserProfileAdded(QString, QString);
|
||||
void on_ControllerWidget_UserProfileRemoved(QString, QString);
|
||||
|
||||
void on_tabWidget_currentChanged(int);
|
||||
|
||||
void on_SDLThread_DeviceFound(QString, int);
|
||||
@@ -75,7 +80,8 @@ private slots:
|
||||
void on_EventFilter_KeyPressed(QKeyEvent *);
|
||||
void on_EventFilter_KeyReleased(QKeyEvent *);
|
||||
|
||||
void on_buttonBox_clicked(QAbstractButton *);
|
||||
void accept(void) Q_DECL_OVERRIDE;
|
||||
void reject(void) Q_DECL_OVERRIDE;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,8 @@ OptionsDialog::OptionsDialog(QWidget* parent, OptionsDialogSettings settings) :
|
||||
this->gameboyRomLineEdit->setText(QString::fromStdString(settings.GameboyRom));
|
||||
this->gameboySaveLineEdit->setText(QString::fromStdString(settings.GameboySave));
|
||||
this->removeDuplicateMappingsCheckbox->setChecked(settings.RemoveDuplicateMappings);
|
||||
this->invertAxisCheckBox->setChecked(settings.InvertAxis);
|
||||
this->filterEventsForButtonsCheckBox->setChecked(settings.FilterEventsForButtons);
|
||||
this->filterEventsForAxisCheckBox->setChecked(settings.FilterEventsForAxis);
|
||||
|
||||
if (!CoreIsEmulationRunning() && !CoreIsEmulationPaused())
|
||||
{
|
||||
@@ -32,11 +33,6 @@ OptionsDialog::OptionsDialog(QWidget* parent, OptionsDialogSettings settings) :
|
||||
}
|
||||
}
|
||||
|
||||
bool OptionsDialog::HasSaved()
|
||||
{
|
||||
return this->hasSaved;
|
||||
}
|
||||
|
||||
OptionsDialogSettings OptionsDialog::GetSettings()
|
||||
{
|
||||
return this->settings;
|
||||
@@ -71,23 +67,16 @@ void OptionsDialog::hideEmulationInfoText(void)
|
||||
}
|
||||
}
|
||||
|
||||
void OptionsDialog::on_buttonBox_clicked(QAbstractButton *button)
|
||||
void OptionsDialog::accept()
|
||||
{
|
||||
this->settings.ControllerPak = this->controllerPakComboBox->currentIndex();
|
||||
this->settings.GameboyRom = this->gameboyRomLineEdit->text().toStdString();
|
||||
this->settings.GameboySave = this->gameboySaveLineEdit->text().toStdString();
|
||||
this->settings.RemoveDuplicateMappings = this->removeDuplicateMappingsCheckbox->isChecked();
|
||||
this->settings.FilterEventsForButtons = this->filterEventsForButtonsCheckBox->isChecked();
|
||||
this->settings.FilterEventsForAxis = this->filterEventsForAxisCheckBox->isChecked();
|
||||
|
||||
QPushButton *pushButton = (QPushButton *)button;
|
||||
QPushButton *okButton = this->buttonBox->button(QDialogButtonBox::Ok);
|
||||
|
||||
if (pushButton == okButton)
|
||||
{
|
||||
this->hasSaved = true;
|
||||
this->settings.ControllerPak = this->controllerPakComboBox->currentIndex();
|
||||
this->settings.GameboyRom = this->gameboyRomLineEdit->text().toStdString();
|
||||
this->settings.GameboySave = this->gameboySaveLineEdit->text().toStdString();
|
||||
this->settings.RemoveDuplicateMappings = this->removeDuplicateMappingsCheckbox->isChecked();
|
||||
this->settings.InvertAxis = this->invertAxisCheckBox->isChecked();
|
||||
}
|
||||
|
||||
this->accept();
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void OptionsDialog::on_changeGameboyRomButton_clicked()
|
||||
|
||||
@@ -24,7 +24,8 @@ struct OptionsDialogSettings
|
||||
|
||||
// UI settings
|
||||
bool RemoveDuplicateMappings = false;
|
||||
bool InvertAxis = false;
|
||||
bool FilterEventsForButtons = true;
|
||||
bool FilterEventsForAxis = true;
|
||||
};
|
||||
|
||||
namespace UserInterface
|
||||
@@ -36,19 +37,17 @@ Q_OBJECT
|
||||
public:
|
||||
OptionsDialog(QWidget *parent, OptionsDialogSettings settings);
|
||||
|
||||
bool HasSaved();
|
||||
OptionsDialogSettings GetSettings();
|
||||
|
||||
protected:
|
||||
void accept() Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
bool hasSaved = false;
|
||||
OptionsDialogSettings settings;
|
||||
|
||||
void setIconsForEmulationInfoText(void);
|
||||
void hideEmulationInfoText(void);
|
||||
|
||||
private slots:
|
||||
void on_buttonBox_clicked(QAbstractButton *);
|
||||
|
||||
void on_changeGameboyRomButton_clicked();
|
||||
void on_changeGameboySaveButton_clicked();
|
||||
};
|
||||
|
||||
@@ -203,9 +203,16 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="invertAxisCheckBox">
|
||||
<widget class="QCheckBox" name="filterEventsForButtonsCheckBox">
|
||||
<property name="text">
|
||||
<string>Invert Axes Mapped To LRZ</string>
|
||||
<string>Filter Events Based On Joystick Type For Buttons</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="filterEventsForAxisCheckBox">
|
||||
<property name="text">
|
||||
<string>Filter Events Based On Joystick Type For Axis</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -241,5 +248,38 @@
|
||||
<resources>
|
||||
<include location="UIResources.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>OptionsDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>OptionsDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
|
||||
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 3.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "AddMappingButton.hpp"
|
||||
#include "ControllerWidget.hpp"
|
||||
#include "MappingButton.hpp"
|
||||
|
||||
using namespace UserInterface::Widget;
|
||||
|
||||
AddMappingButton::AddMappingButton(QWidget* parent) : QPushButton(parent)
|
||||
{
|
||||
}
|
||||
|
||||
AddMappingButton::~AddMappingButton()
|
||||
{
|
||||
}
|
||||
|
||||
void AddMappingButton::Initialize(ControllerWidget* widget, MappingButton* button)
|
||||
{
|
||||
this->controllerWidget = widget;
|
||||
this->mappingButton = button;
|
||||
|
||||
connect(this, &QPushButton::released, [=]
|
||||
{
|
||||
emit this->controllerWidget->on_AddMappingButton_Released(this->mappingButton);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
|
||||
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 3.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef ADDMAPPINGBUTTON_HPP
|
||||
#define ADDMAPPINGBUTTON_HPP
|
||||
|
||||
#include <QPushButton>
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
namespace UserInterface
|
||||
{
|
||||
namespace Widget
|
||||
{
|
||||
|
||||
// we can't include ControllerWidget.hpp
|
||||
// because that header includes this header,
|
||||
// so forward-declare it to prevent issues
|
||||
class ControllerWidget;
|
||||
class MappingButton;
|
||||
|
||||
class AddMappingButton : public QPushButton
|
||||
{
|
||||
private:
|
||||
ControllerWidget* controllerWidget = nullptr;
|
||||
MappingButton* mappingButton = nullptr;
|
||||
|
||||
public:
|
||||
AddMappingButton(QWidget* parent);
|
||||
~AddMappingButton();
|
||||
|
||||
void Initialize(ControllerWidget* widget, MappingButton* button);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ADDMAPPINGBUTTON_HPP
|
||||
@@ -10,8 +10,8 @@
|
||||
#include "ControllerImageWidget.hpp"
|
||||
|
||||
#include <QSvgRenderer>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <QPainter>
|
||||
#include <QResizeEvent>
|
||||
|
||||
@@ -62,6 +62,15 @@ void ControllerImageWidget::SetDeadzone(int value)
|
||||
}
|
||||
}
|
||||
|
||||
void ControllerImageWidget::SetSensitivity(int value)
|
||||
{
|
||||
if (this->sensitivityValue != value)
|
||||
{
|
||||
this->sensitivityValue = value;
|
||||
this->needImageUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ControllerImageWidget::ClearControllerState()
|
||||
{
|
||||
// reset button state
|
||||
@@ -153,27 +162,35 @@ void ControllerImageWidget::paintEvent(QPaintEvent *event)
|
||||
renderer.setAspectRatioMode(Qt::AspectRatioMode::KeepAspectRatio);
|
||||
|
||||
QRectF rectF = renderer.viewBoxF();
|
||||
double offsetx = 0, offsety = 0;
|
||||
const int width = rectF.width();
|
||||
const int height = rectF.height();
|
||||
// we'll move the analog stick by a percentage
|
||||
// of the total width/height from the image
|
||||
const double maxOffsety = ((double)(height * 0.12265f) / 2);
|
||||
const double maxOffsetx = maxOffsety;
|
||||
const double dist = sqrt(pow(this->xAxisState, 2) + pow(this->yAxisState, 2));
|
||||
const double absoluteMaxOffset = ((double)(height * 0.12265f) / 2);
|
||||
// slope as in line gradient
|
||||
const double offsetSlope = absoluteMaxOffset / 100;
|
||||
// take sensitivity into account
|
||||
const double sensitivityFactor = this->sensitivityValue / 100.0;
|
||||
const double sensitivityAdjustedMaxOffset = absoluteMaxOffset * std::min(sensitivityFactor, 1.0);
|
||||
double offsetDeadzone = this->deadzoneValue * sensitivityFactor * offsetSlope;
|
||||
int offsetx = this->xAxisState * sensitivityFactor * offsetSlope;
|
||||
int offsety = this->yAxisState * sensitivityFactor * offsetSlope;
|
||||
const double offsetDist = sqrt(pow(offsetx, 2) + pow(offsety, 2));
|
||||
|
||||
// take deadzone into account
|
||||
if (dist > this->deadzoneValue)
|
||||
// deadzone grows with sensitivity such that the deadzone
|
||||
// is always a percentage of the real stick range
|
||||
if (offsetDist <= offsetDeadzone)
|
||||
{
|
||||
offsetx = (maxOffsetx / 100 * this->xAxisState);
|
||||
offsety = (maxOffsety / 100 * this->yAxisState);
|
||||
offsetx = 0;
|
||||
offsety = 0;
|
||||
}
|
||||
|
||||
// take circle range into account
|
||||
if (dist > 100)
|
||||
if (offsetDist > sensitivityAdjustedMaxOffset)
|
||||
{
|
||||
offsetx = (maxOffsetx / 100 * (this->xAxisState / dist * 100));
|
||||
offsety = (maxOffsety / 100 * (this->yAxisState / dist * 100));
|
||||
offsetx = (offsetx / offsetDist) * sensitivityAdjustedMaxOffset;
|
||||
offsety = (offsety / offsetDist) * sensitivityAdjustedMaxOffset;
|
||||
}
|
||||
|
||||
// adjust rect with offset
|
||||
|
||||
@@ -31,6 +31,8 @@ private:
|
||||
int yAxisState = 0;
|
||||
// deadzone value, 0-100
|
||||
int deadzoneValue = 0;
|
||||
// sensitivity value, 25-175
|
||||
int sensitivityValue = 100;
|
||||
|
||||
bool needImageUpdate = false;
|
||||
public:
|
||||
@@ -41,6 +43,7 @@ public:
|
||||
void SetXAxisState(int xAxis);
|
||||
void SetYAxisState(int yAxis);
|
||||
void SetDeadzone(int value);
|
||||
void SetSensitivity(int value);
|
||||
|
||||
void ClearControllerState();
|
||||
void UpdateImage();
|
||||
|
||||
@@ -10,11 +10,12 @@
|
||||
#ifndef CONTROLLERWIDGET_H
|
||||
#define CONTROLLERWIDGET_H
|
||||
|
||||
#include "CustomButton.hpp"
|
||||
#include "MappingButton.hpp"
|
||||
#include "ControllerImageWidget.hpp"
|
||||
|
||||
#include "UserInterface/EventFilter.hpp"
|
||||
#include "UserInterface/OptionsDialog.hpp"
|
||||
#include "UserInterface/HotkeysDialog.hpp"
|
||||
|
||||
using namespace UserInterface::Widget;
|
||||
|
||||
@@ -34,28 +35,35 @@ class ControllerWidget : public QWidget, Ui::ControllerWidget
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
bool initialized = false;
|
||||
QString settingsSection;
|
||||
|
||||
QString gameSection;
|
||||
|
||||
OptionsDialogSettings optionsDialogSettings;
|
||||
|
||||
QList<QString> inputDeviceNameList;
|
||||
CustomButton* currentButton = nullptr;
|
||||
MappingButton* currentButton = nullptr;
|
||||
bool addMappingToButton = false;
|
||||
|
||||
QList<QString> profiles;
|
||||
QList<QString> removedProfiles;
|
||||
QList<QString> addedProfiles;
|
||||
|
||||
struct buttonWidgetMapping
|
||||
{
|
||||
enum N64ControllerButton button;
|
||||
CustomButton* buttonWidget;
|
||||
MappingButton* buttonWidget;
|
||||
};
|
||||
|
||||
struct axisWidgetMapping
|
||||
{
|
||||
enum InputAxisDirection direction;
|
||||
CustomButton* buttonWidget;
|
||||
MappingButton* buttonWidget;
|
||||
};
|
||||
|
||||
struct buttonSettingMapping
|
||||
{
|
||||
CustomButton* button;
|
||||
MappingButton* button;
|
||||
SettingsID inputTypeSettingsId;
|
||||
SettingsID nameSettingsId;
|
||||
SettingsID dataSettingsId;
|
||||
@@ -65,11 +73,12 @@ private:
|
||||
QList<buttonWidgetMapping> buttonWidgetMappings;
|
||||
QList<axisWidgetMapping> joystickWidgetMappings;
|
||||
QList<buttonSettingMapping> buttonSettingMappings;
|
||||
QList<CustomButton*> setupButtonWidgets;
|
||||
int currentSetupButtonWidgetIndex = 0;
|
||||
bool currentInSetup = false;
|
||||
QList<HotkeySettingMapping> hotkeySettingMappings;
|
||||
QList<MappingButton*> setupButtonWidgets;
|
||||
|
||||
void initializeButtons();
|
||||
void initializeMappingButtons();
|
||||
void initializeProfileButtons();
|
||||
void initializeMiscButtons();
|
||||
|
||||
bool isCurrentDeviceKeyboard();
|
||||
bool isCurrentDeviceNotFound();
|
||||
@@ -77,11 +86,29 @@ private:
|
||||
void disableAllChildren();
|
||||
void enableAllChildren();
|
||||
|
||||
void removeDuplicates(CustomButton* button);
|
||||
void removeDuplicates(MappingButton* button);
|
||||
|
||||
QString getCurrentSettingsSection();
|
||||
|
||||
SDL_JoystickID currentJoystickId = -1;
|
||||
QString getUserProfileSectionName(QString profile);
|
||||
|
||||
bool isSectionUserProfile(QString section);
|
||||
bool isSectionGameProfile(QString section);
|
||||
|
||||
void setPluggedIn(bool value);
|
||||
|
||||
bool hasAnyGameSettingChanged(void);
|
||||
|
||||
void showErrorMessage(QString text, QString details = "");
|
||||
|
||||
SDL_JoystickID currentJoystickId = -1;
|
||||
bool isCurrentJoystickGameController = false;
|
||||
|
||||
int previousProfileComboBoxIndex = -1;
|
||||
|
||||
bool onlyLoadGameProfile = false;
|
||||
|
||||
HotkeysDialog* currentHotkeysDialog = nullptr;
|
||||
|
||||
public:
|
||||
ControllerWidget(QWidget* parent, EventFilter* eventFilter);
|
||||
@@ -97,40 +124,61 @@ public:
|
||||
void GetCurrentInputDevice(QString& deviceName, int& deviceNum, bool ignoreDeviceNotFound = false);
|
||||
bool IsPluggedIn();
|
||||
|
||||
void SetSettingsSection(QString section);
|
||||
void SetOnlyLoadGameProfile(bool value);
|
||||
|
||||
void SetSettingsSection(QString profile, QString section);
|
||||
void SetInitialized(bool value);
|
||||
|
||||
void LoadSettings();
|
||||
void LoadSettings(QString section);
|
||||
void LoadSettings(QString section, bool loadUserProfile = false);
|
||||
void LoadUserProfileSettings();
|
||||
|
||||
void SaveDefaultSettings();
|
||||
void SaveSettings();
|
||||
void SaveUserProfileSettings();
|
||||
void SaveSettings(QString section);
|
||||
|
||||
void RevertSettings();
|
||||
|
||||
void SetCurrentJoystickID(SDL_JoystickID joystickId);
|
||||
void SetIsCurrentJoystickGameController(bool isGameController);
|
||||
|
||||
void AddUserProfile(QString name, QString section);
|
||||
void RemoveUserProfile(QString name, QString section);
|
||||
|
||||
private slots:
|
||||
void on_deadZoneSlider_valueChanged(int value);
|
||||
void on_analogStickSensitivitySlider_valueChanged(int value);
|
||||
|
||||
void on_profileComboBox_currentIndexChanged(int value);
|
||||
|
||||
void on_inputDeviceComboBox_currentIndexChanged(int value);
|
||||
void on_inputDeviceRefreshButton_clicked();
|
||||
|
||||
void on_controllerPluggedCheckBox_toggled(bool value);
|
||||
|
||||
void on_addProfileButton_clicked();
|
||||
void on_removeProfileButton_clicked();
|
||||
void on_setupButton_clicked();
|
||||
|
||||
void on_resetButton_clicked();
|
||||
void on_optionsButton_clicked();
|
||||
void on_hotkeysButton_clicked();
|
||||
|
||||
public slots:
|
||||
void on_CustomButton_released(CustomButton* button);
|
||||
void on_CustomButton_TimerFinished(CustomButton* button);
|
||||
void on_CustomButton_DataSet(CustomButton* button);
|
||||
void on_MappingButton_Released(MappingButton* button);
|
||||
void on_MappingButton_TimerFinished(MappingButton* button);
|
||||
void on_MappingButton_DataSet(MappingButton* button);
|
||||
|
||||
void on_AddMappingButton_Released(MappingButton* button);
|
||||
void on_RemoveMappingButton_Released(MappingButton* button);
|
||||
|
||||
void on_MainDialog_SdlEvent(SDL_Event* event);
|
||||
void on_MainDialog_SdlEventPollFinished();
|
||||
|
||||
signals:
|
||||
void CurrentInputDeviceChanged(ControllerWidget* widget, QString deviceName, int deviceNum);
|
||||
void RefreshInputDevicesButtonClicked();
|
||||
|
||||
void UserProfileAdded(QString name, QString section);
|
||||
void UserProfileRemoved(QString name, QString section);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
|
||||
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 3.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "CustomButton.hpp"
|
||||
#include "ControllerWidget.hpp"
|
||||
|
||||
using namespace UserInterface::Widget;
|
||||
|
||||
CustomButton::CustomButton(QWidget* parent) : QPushButton(parent)
|
||||
{
|
||||
this->countDownTimer = new QTimer(this);
|
||||
connect(this->countDownTimer, &QTimer::timeout, this, &CustomButton::on_countDownTimer_triggered);
|
||||
}
|
||||
|
||||
CustomButton::~CustomButton()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void CustomButton::Initialize(QWidget* widget)
|
||||
{
|
||||
this->controllerWidget = (ControllerWidget*)widget;
|
||||
|
||||
connect(this, &QPushButton::released,
|
||||
[=]
|
||||
{
|
||||
emit this->controllerWidget->on_CustomButton_released(this);
|
||||
});
|
||||
}
|
||||
|
||||
void CustomButton::Clear()
|
||||
{
|
||||
this->inputType = InputType::Invalid;
|
||||
this->inputData = 0;
|
||||
this->extraInputData = 0;
|
||||
this->savedInputData = 0;
|
||||
this->savedExtraInputData = 0;
|
||||
this->setText(" ");
|
||||
}
|
||||
|
||||
void CustomButton::StartTimer()
|
||||
{
|
||||
this->currentCountDownNum = 5;
|
||||
this->on_countDownTimer_triggered();
|
||||
this->countDownTimer->start(1000);
|
||||
}
|
||||
|
||||
void CustomButton::StopTimer()
|
||||
{
|
||||
this->countDownTimer->stop();
|
||||
}
|
||||
|
||||
void CustomButton::SetInputData(enum InputType type, int value, int extraValue, QString name)
|
||||
{
|
||||
this->inputType = type;
|
||||
this->inputData = value;
|
||||
this->extraInputData = extraValue;
|
||||
this->setText(name);
|
||||
this->StopTimer();
|
||||
emit this->controllerWidget->on_CustomButton_DataSet(this);
|
||||
}
|
||||
|
||||
enum InputType CustomButton::GetInputType()
|
||||
{
|
||||
return this->inputType;
|
||||
}
|
||||
|
||||
int CustomButton::GetInputData()
|
||||
{
|
||||
return this->inputData;
|
||||
}
|
||||
|
||||
int CustomButton::GetExtraInputData()
|
||||
{
|
||||
return this->extraInputData;
|
||||
}
|
||||
|
||||
void CustomButton::SaveState()
|
||||
{
|
||||
this->savedInputType = this->inputType;
|
||||
this->savedInputData = this->inputData;
|
||||
this->savedExtraInputData = this->extraInputData;
|
||||
this->savedText = this->text();
|
||||
}
|
||||
|
||||
void CustomButton::RestoreState()
|
||||
{
|
||||
this->inputType = this->savedInputType;
|
||||
this->inputData = this->savedInputData;
|
||||
this->extraInputData = this->savedExtraInputData;
|
||||
this->setText(this->savedText);
|
||||
}
|
||||
|
||||
void CustomButton::on_countDownTimer_triggered()
|
||||
{
|
||||
this->setText(QString::number(this->currentCountDownNum));
|
||||
|
||||
if (this->currentCountDownNum == 0)
|
||||
{
|
||||
this->setText("");
|
||||
this->currentCountDownNum = 5;
|
||||
this->countDownTimer->stop();
|
||||
emit this->controllerWidget->on_CustomButton_TimerFinished(this);
|
||||
}
|
||||
|
||||
this->currentCountDownNum--;
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
|
||||
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 3.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef CUSTOMBUTTON_HPP
|
||||
#define CUSTOMBUTTON_HPP
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QTimer>
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
namespace UserInterface
|
||||
{
|
||||
namespace Widget
|
||||
{
|
||||
|
||||
// we can't include ControllerWidget.hpp
|
||||
// because that header includes this header,
|
||||
// so forward-declare it to prevent issues
|
||||
class ControllerWidget;
|
||||
|
||||
class CustomButton : public QPushButton
|
||||
{
|
||||
private:
|
||||
QTimer* countDownTimer = nullptr;
|
||||
ControllerWidget* controllerWidget = nullptr;
|
||||
int currentCountDownNum = 0;
|
||||
enum InputType inputType = InputType::Invalid;
|
||||
int inputData = 0;
|
||||
int extraInputData = 0;
|
||||
|
||||
enum InputType savedInputType = InputType::Invalid;
|
||||
int savedInputData = 0;
|
||||
int savedExtraInputData = 0;
|
||||
QString savedText;
|
||||
public:
|
||||
CustomButton(QWidget* parent);
|
||||
~CustomButton();
|
||||
void Initialize(QWidget* widget);
|
||||
void Clear();
|
||||
|
||||
void StartTimer();
|
||||
void StopTimer();
|
||||
|
||||
|
||||
void SetInputData(enum InputType type, int value, int extraValue, QString name);
|
||||
enum InputType GetInputType();
|
||||
int GetInputData();
|
||||
int GetExtraInputData();
|
||||
|
||||
void SaveState();
|
||||
void RestoreState();
|
||||
|
||||
private slots:
|
||||
void on_countDownTimer_triggered();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif // CUSTOMBUTTON_HPP
|
||||
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
|
||||
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 3.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "HotkeyButton.hpp"
|
||||
#include "UserInterface/HotkeysDialog.hpp"
|
||||
|
||||
#include <QMouseEvent>
|
||||
|
||||
using namespace UserInterface::Widget;
|
||||
|
||||
HotkeyButton::HotkeyButton(QWidget* parent) : QPushButton(parent)
|
||||
{
|
||||
this->countDownTimer = new QTimer(this);
|
||||
connect(this->countDownTimer, &QTimer::timeout, this, &HotkeyButton::on_countDownTimer_triggered);
|
||||
|
||||
this->initialSize = this->size();
|
||||
}
|
||||
|
||||
HotkeyButton::~HotkeyButton()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void HotkeyButton::Initialize(HotkeysDialog* dialog)
|
||||
{
|
||||
this->hotkeysDialog = dialog;
|
||||
|
||||
connect(this, &QPushButton::released, [=]
|
||||
{
|
||||
emit this->hotkeysDialog->on_HotkeyButton_Released(this);
|
||||
});
|
||||
|
||||
this->setText(" ");
|
||||
}
|
||||
|
||||
void HotkeyButton::Clear()
|
||||
{
|
||||
this->inputData.clear();
|
||||
this->savedInputData.clear();
|
||||
this->setText("");
|
||||
}
|
||||
|
||||
void HotkeyButton::StartTimer()
|
||||
{
|
||||
// clear current state
|
||||
this->inputData.clear();
|
||||
|
||||
this->currentCountDownNum = 5;
|
||||
this->on_countDownTimer_triggered();
|
||||
this->countDownTimer->start(1000);
|
||||
}
|
||||
|
||||
void HotkeyButton::StopTimer()
|
||||
{
|
||||
this->countDownTimer->stop();
|
||||
}
|
||||
|
||||
void HotkeyButton::AddInputData(enum InputType type, int value, int extraValue, QString name)
|
||||
{
|
||||
// don't add stub
|
||||
if (name.simplified().isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->HasInputData(type, value, extraValue))
|
||||
{
|
||||
this->inputData.push_back(
|
||||
{
|
||||
type,
|
||||
value,
|
||||
extraValue,
|
||||
name
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
bool HotkeyButton::HasInputData(enum InputType type, int value, int extraValue)
|
||||
{
|
||||
return this->getInputDataIter(type, value, extraValue) != this->inputData.end();
|
||||
}
|
||||
|
||||
std::vector<int> HotkeyButton::GetInputType()
|
||||
{
|
||||
std::vector<int> inputTypeList;
|
||||
|
||||
for (const auto& data : this->inputData)
|
||||
{
|
||||
inputTypeList.push_back((int)data.Type);
|
||||
}
|
||||
|
||||
return inputTypeList;
|
||||
}
|
||||
|
||||
std::vector<int> HotkeyButton::GetInputData()
|
||||
{
|
||||
std::vector<int> inputDataList;
|
||||
|
||||
for (const auto& data : this->inputData)
|
||||
{
|
||||
inputDataList.push_back(data.Data);
|
||||
}
|
||||
|
||||
return inputDataList;
|
||||
}
|
||||
|
||||
std::vector<int> HotkeyButton::GetExtraInputData()
|
||||
{
|
||||
std::vector<int> extraInputDataList;
|
||||
|
||||
for (const auto& data : this->inputData)
|
||||
{
|
||||
extraInputDataList.push_back(data.ExtraData);
|
||||
}
|
||||
|
||||
return extraInputDataList;
|
||||
}
|
||||
|
||||
std::vector<std::string> HotkeyButton::GetInputText()
|
||||
{
|
||||
std::vector<std::string> inputTextList;
|
||||
|
||||
for (const auto& data : this->inputData)
|
||||
{
|
||||
inputTextList.push_back(data.Text.toStdString());
|
||||
}
|
||||
|
||||
return inputTextList;
|
||||
}
|
||||
|
||||
void HotkeyButton::SaveState()
|
||||
{
|
||||
this->savedInputData = this->inputData;
|
||||
}
|
||||
|
||||
void HotkeyButton::RestoreState()
|
||||
{
|
||||
this->inputData = this->savedInputData;
|
||||
this->reloadText();
|
||||
}
|
||||
|
||||
void HotkeyButton::FinishState()
|
||||
{
|
||||
this->reloadText();
|
||||
emit this->hotkeysDialog->on_HotkeyButton_StateFinished(this);
|
||||
}
|
||||
|
||||
void HotkeyButton::mousePressEvent(QMouseEvent* event)
|
||||
{
|
||||
if (event->button() == Qt::RightButton)
|
||||
{
|
||||
this->Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
QPushButton::mousePressEvent(event);
|
||||
}
|
||||
|
||||
void HotkeyButton::on_countDownTimer_triggered()
|
||||
{
|
||||
this->setText(QString::number(this->currentCountDownNum));
|
||||
|
||||
if (this->currentCountDownNum == 0)
|
||||
{
|
||||
this->setText("");
|
||||
this->currentCountDownNum = 5;
|
||||
this->countDownTimer->stop();
|
||||
emit this->hotkeysDialog->on_HotkeyButton_TimerFinished(this);
|
||||
}
|
||||
|
||||
this->currentCountDownNum--;
|
||||
}
|
||||
|
||||
void HotkeyButton::reloadText()
|
||||
{
|
||||
QString text;
|
||||
QFontMetrics fontMetrics(this->font());
|
||||
|
||||
int size = this->inputData.size();
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
text += this->inputData.at(i).Text;
|
||||
|
||||
// only add comma when
|
||||
// not at the last item
|
||||
if (i < (size - 1))
|
||||
{
|
||||
text += "+";
|
||||
}
|
||||
}
|
||||
|
||||
QString elidedText = fontMetrics.elidedText(text, Qt::ElideRight, this->initialSize.width());
|
||||
|
||||
// needed for spacing
|
||||
if (elidedText.isEmpty())
|
||||
{
|
||||
elidedText = " ";
|
||||
}
|
||||
|
||||
this->setToolTip(text);
|
||||
this->setText(elidedText);
|
||||
}
|
||||
|
||||
std::vector<HotkeyButtonInputDataType>::iterator HotkeyButton::getInputDataIter(InputType type, int value, int extraValue)
|
||||
{
|
||||
auto predicate = [type, value, extraValue](const auto& entry)
|
||||
{
|
||||
return entry.Type == type &&
|
||||
(value == -1 || entry.Data == value) &&
|
||||
(extraValue == -1 || entry.ExtraData == extraValue);
|
||||
};
|
||||
|
||||
return std::find_if(this->inputData.begin(), this->inputData.end(), predicate);
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
|
||||
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 3.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef HOTKEYBUTTON_HPP
|
||||
#define HOTKEYBUTTON_HPP
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QTimer>
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
namespace UserInterface
|
||||
{
|
||||
|
||||
// we can't include HotkeysDialog.hpp
|
||||
// because that header includes this header,
|
||||
// so forward-declare it to prevent issues
|
||||
class HotkeysDialog;
|
||||
|
||||
namespace Widget
|
||||
{
|
||||
|
||||
struct HotkeyButtonInputDataType
|
||||
{
|
||||
InputType Type;
|
||||
int Data;
|
||||
int ExtraData;
|
||||
QString Text;
|
||||
};
|
||||
|
||||
class HotkeyButton : public QPushButton
|
||||
{
|
||||
private:
|
||||
QSize initialSize;
|
||||
QTimer* countDownTimer = nullptr;
|
||||
HotkeysDialog* hotkeysDialog = nullptr;
|
||||
int currentCountDownNum = 0;
|
||||
|
||||
std::vector<HotkeyButtonInputDataType> inputData;
|
||||
|
||||
std::vector<HotkeyButtonInputDataType> savedInputData;
|
||||
|
||||
void reloadText();
|
||||
|
||||
std::vector<HotkeyButtonInputDataType>::iterator getInputDataIter(InputType type, int value, int extraValue);
|
||||
|
||||
public:
|
||||
HotkeyButton(QWidget* parent);
|
||||
~HotkeyButton();
|
||||
|
||||
void Initialize(HotkeysDialog* dialog);
|
||||
void Clear();
|
||||
|
||||
void StartTimer();
|
||||
void StopTimer();
|
||||
|
||||
void AddInputData(InputType type, int value, int extraValue, QString name);
|
||||
|
||||
bool HasInputData(InputType type, int value = -1, int extraValue = -1);
|
||||
|
||||
std::vector<int> GetInputType();
|
||||
std::vector<int> GetInputData();
|
||||
std::vector<int> GetExtraInputData();
|
||||
std::vector<std::string> GetInputText();
|
||||
|
||||
void SaveState();
|
||||
void RestoreState();
|
||||
void FinishState();
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent* event);
|
||||
|
||||
private slots:
|
||||
void on_countDownTimer_triggered();
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // HOTKEYBUTTON_HPP
|
||||
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
|
||||
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 3.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "MappingButton.hpp"
|
||||
#include "ControllerWidget.hpp"
|
||||
|
||||
using namespace UserInterface::Widget;
|
||||
|
||||
MappingButton::MappingButton(QWidget* parent) : QPushButton(parent)
|
||||
{
|
||||
this->countDownTimer = new QTimer(this);
|
||||
connect(this->countDownTimer, &QTimer::timeout, this, &MappingButton::on_countDownTimer_triggered);
|
||||
|
||||
this->initialSize = this->size();
|
||||
}
|
||||
|
||||
MappingButton::~MappingButton()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void MappingButton::Initialize(ControllerWidget* widget)
|
||||
{
|
||||
this->controllerWidget = widget;
|
||||
|
||||
connect(this, &QPushButton::released, [=]
|
||||
{
|
||||
emit this->controllerWidget->on_MappingButton_Released(this);
|
||||
});
|
||||
|
||||
this->setText(" ");
|
||||
}
|
||||
|
||||
void MappingButton::Clear()
|
||||
{
|
||||
this->inputData.clear();
|
||||
this->savedInputData.clear();
|
||||
this->setText(" ");
|
||||
}
|
||||
|
||||
void MappingButton::StartTimer()
|
||||
{
|
||||
this->currentCountDownNum = 5;
|
||||
this->on_countDownTimer_triggered();
|
||||
this->countDownTimer->start(1000);
|
||||
}
|
||||
|
||||
void MappingButton::StopTimer()
|
||||
{
|
||||
this->countDownTimer->stop();
|
||||
}
|
||||
|
||||
void MappingButton::SetInputData(enum InputType type, int value, int extraValue, QString name)
|
||||
{
|
||||
// don't add stub
|
||||
if (name.simplified().isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this->inputData.clear();
|
||||
this->inputData.push_back(
|
||||
{
|
||||
type,
|
||||
value,
|
||||
extraValue,
|
||||
name
|
||||
}
|
||||
);
|
||||
|
||||
this->reloadText();
|
||||
this->StopTimer();
|
||||
|
||||
emit this->controllerWidget->on_MappingButton_DataSet(this);
|
||||
}
|
||||
|
||||
void MappingButton::AddInputData(enum InputType type, int value, int extraValue, QString name)
|
||||
{
|
||||
// don't add stub
|
||||
if (name.simplified().isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->HasInputData(type, value, extraValue))
|
||||
{
|
||||
this->inputData.push_back(
|
||||
{
|
||||
type,
|
||||
value,
|
||||
extraValue,
|
||||
name
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
this->reloadText();
|
||||
this->StopTimer();
|
||||
|
||||
emit this->controllerWidget->on_MappingButton_DataSet(this);
|
||||
}
|
||||
|
||||
void MappingButton::RemoveLastInputData(void)
|
||||
{
|
||||
if (!this->inputData.empty())
|
||||
{
|
||||
this->inputData.pop_back();
|
||||
}
|
||||
|
||||
this->reloadText();
|
||||
}
|
||||
|
||||
void MappingButton::RemoveInputData(InputType type, int value, int extraValue)
|
||||
{
|
||||
auto iter = this->getInputDataIter(type, value, extraValue);
|
||||
if (iter == this->inputData.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this->inputData.erase(iter);
|
||||
this->reloadText();
|
||||
}
|
||||
|
||||
bool MappingButton::HasInputData(enum InputType type, int value, int extraValue)
|
||||
{
|
||||
return this->getInputDataIter(type, value, extraValue) != this->inputData.end();
|
||||
}
|
||||
|
||||
std::vector<int> MappingButton::GetInputType()
|
||||
{
|
||||
std::vector<int> inputTypeList;
|
||||
|
||||
for (const auto& data : this->inputData)
|
||||
{
|
||||
inputTypeList.push_back((int)data.Type);
|
||||
}
|
||||
|
||||
return inputTypeList;
|
||||
}
|
||||
|
||||
std::vector<int> MappingButton::GetInputData()
|
||||
{
|
||||
std::vector<int> inputDataList;
|
||||
|
||||
for (const auto& data : this->inputData)
|
||||
{
|
||||
inputDataList.push_back(data.Data);
|
||||
}
|
||||
|
||||
return inputDataList;
|
||||
}
|
||||
|
||||
std::vector<int> MappingButton::GetExtraInputData()
|
||||
{
|
||||
std::vector<int> extraInputDataList;
|
||||
|
||||
for (const auto& data : this->inputData)
|
||||
{
|
||||
extraInputDataList.push_back(data.ExtraData);
|
||||
}
|
||||
|
||||
return extraInputDataList;
|
||||
}
|
||||
|
||||
std::vector<std::string> MappingButton::GetInputText()
|
||||
{
|
||||
std::vector<std::string> inputTextList;
|
||||
|
||||
for (const auto& data : this->inputData)
|
||||
{
|
||||
inputTextList.push_back(data.Text.toStdString());
|
||||
}
|
||||
|
||||
return inputTextList;
|
||||
}
|
||||
|
||||
void MappingButton::SaveState()
|
||||
{
|
||||
this->savedInputData = this->inputData;
|
||||
}
|
||||
|
||||
void MappingButton::RestoreState()
|
||||
{
|
||||
this->inputData = this->savedInputData;
|
||||
this->reloadText();
|
||||
}
|
||||
|
||||
void MappingButton::on_countDownTimer_triggered()
|
||||
{
|
||||
this->setText(QString::number(this->currentCountDownNum));
|
||||
|
||||
if (this->currentCountDownNum == 0)
|
||||
{
|
||||
this->setText("");
|
||||
this->currentCountDownNum = 5;
|
||||
this->countDownTimer->stop();
|
||||
emit this->controllerWidget->on_MappingButton_TimerFinished(this);
|
||||
}
|
||||
|
||||
this->currentCountDownNum--;
|
||||
}
|
||||
|
||||
void MappingButton::reloadText()
|
||||
{
|
||||
QString text;
|
||||
QFontMetrics fontMetrics(this->font());
|
||||
|
||||
int size = this->inputData.size();
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
text += this->inputData.at(i).Text;
|
||||
|
||||
// only add comma when
|
||||
// not at the last item
|
||||
if (i < (size - 1))
|
||||
{
|
||||
text += ", ";
|
||||
}
|
||||
}
|
||||
|
||||
QString elidedText = fontMetrics.elidedText(text, Qt::ElideRight, this->initialSize.width());
|
||||
|
||||
// needed for spacing
|
||||
if (elidedText.isEmpty())
|
||||
{
|
||||
elidedText = " ";
|
||||
}
|
||||
|
||||
this->setToolTip(text);
|
||||
this->setText(elidedText);
|
||||
}
|
||||
|
||||
std::vector<MappingButtonInputDataType>::iterator MappingButton::getInputDataIter(InputType type, int value, int extraValue)
|
||||
{
|
||||
auto predicate = [type, value, extraValue](const auto& entry)
|
||||
{
|
||||
return entry.Type == type &&
|
||||
(value == -1 || entry.Data == value) &&
|
||||
(extraValue == -1 || entry.ExtraData == extraValue);
|
||||
};
|
||||
|
||||
return std::find_if(this->inputData.begin(), this->inputData.end(), predicate);
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
|
||||
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 3.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef MAPPINGBUTTON_HPP
|
||||
#define MAPPINGBUTTON_HPP
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QTimer>
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
namespace UserInterface
|
||||
{
|
||||
namespace Widget
|
||||
{
|
||||
|
||||
// we can't include ControllerWidget.hpp
|
||||
// because that header includes this header,
|
||||
// so forward-declare it to prevent issues
|
||||
class ControllerWidget;
|
||||
|
||||
struct MappingButtonInputDataType
|
||||
{
|
||||
InputType Type;
|
||||
int Data;
|
||||
int ExtraData;
|
||||
QString Text;
|
||||
};
|
||||
|
||||
class MappingButton : public QPushButton
|
||||
{
|
||||
private:
|
||||
QSize initialSize;
|
||||
QTimer* countDownTimer = nullptr;
|
||||
ControllerWidget* controllerWidget = nullptr;
|
||||
int currentCountDownNum = 0;
|
||||
|
||||
std::vector<MappingButtonInputDataType> inputData;
|
||||
|
||||
std::vector<MappingButtonInputDataType> savedInputData;
|
||||
|
||||
void reloadText();
|
||||
|
||||
std::vector<MappingButtonInputDataType>::iterator getInputDataIter(InputType type, int value, int extraValue);
|
||||
|
||||
public:
|
||||
MappingButton(QWidget* parent);
|
||||
~MappingButton();
|
||||
|
||||
void Initialize(ControllerWidget* widget);
|
||||
void Clear();
|
||||
|
||||
void StartTimer();
|
||||
void StopTimer();
|
||||
|
||||
void SetInputData(InputType type, int value, int extraValue, QString name);
|
||||
void AddInputData(InputType type, int value, int extraValue, QString name);
|
||||
|
||||
void RemoveLastInputData(void);
|
||||
void RemoveInputData(InputType type, int value, int extraValue);
|
||||
|
||||
bool HasInputData(InputType type, int value = -1, int extraValue = -1);
|
||||
|
||||
std::vector<int> GetInputType();
|
||||
std::vector<int> GetInputData();
|
||||
std::vector<int> GetExtraInputData();
|
||||
std::vector<std::string> GetInputText();
|
||||
|
||||
void SaveState();
|
||||
void RestoreState();
|
||||
|
||||
private slots:
|
||||
void on_countDownTimer_triggered();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // MAPPINGBUTTON_HPP
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
|
||||
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 3.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "RemoveMappingButton.hpp"
|
||||
#include "ControllerWidget.hpp"
|
||||
#include "MappingButton.hpp"
|
||||
|
||||
using namespace UserInterface::Widget;
|
||||
|
||||
RemoveMappingButton::RemoveMappingButton(QWidget* parent) : QPushButton(parent)
|
||||
{
|
||||
}
|
||||
|
||||
RemoveMappingButton::~RemoveMappingButton()
|
||||
{
|
||||
}
|
||||
|
||||
void RemoveMappingButton::Initialize(ControllerWidget* widget, MappingButton* button)
|
||||
{
|
||||
this->controllerWidget = widget;
|
||||
this->mappingButton = button;
|
||||
|
||||
connect(this, &QPushButton::released, [=]
|
||||
{
|
||||
emit this->controllerWidget->on_RemoveMappingButton_Released(this->mappingButton);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
|
||||
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 3.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef REMOVEMAPPINGBUTTON_HPP
|
||||
#define REMOVEMAPPINGBUTTON_HPP
|
||||
|
||||
#include <QPushButton>
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
namespace UserInterface
|
||||
{
|
||||
namespace Widget
|
||||
{
|
||||
|
||||
// we can't include ControllerWidget.hpp
|
||||
// because that header includes this header,
|
||||
// so forward-declare it to prevent issues
|
||||
class ControllerWidget;
|
||||
class MappingButton;
|
||||
|
||||
class RemoveMappingButton : public QPushButton
|
||||
{
|
||||
private:
|
||||
ControllerWidget* controllerWidget = nullptr;
|
||||
MappingButton* mappingButton = nullptr;
|
||||
|
||||
public:
|
||||
RemoveMappingButton(QWidget* parent);
|
||||
~RemoveMappingButton();
|
||||
|
||||
void Initialize(ControllerWidget* widget, MappingButton* button);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // REMOVEMAPPINGBUTTON_HPP
|
||||
@@ -83,7 +83,7 @@ void InputDevice::OpenDevice(std::string name, int num)
|
||||
// wait until SDLThread is done first
|
||||
while (this->sdlThread->GetCurrentAction() != SDLThreadAction::None)
|
||||
{
|
||||
QThread::msleep(50);
|
||||
QThread::msleep(5);
|
||||
}
|
||||
|
||||
this->foundDevicesWithNameMatch.clear();
|
||||
|
||||
@@ -0,0 +1,603 @@
|
||||
/*
|
||||
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
|
||||
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 3.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#define CORE_PLUGIN
|
||||
#define M64P_PLUGIN_PROTOTYPES 1
|
||||
|
||||
#include <RMG-Core/Core.hpp>
|
||||
#include <RMG-Core/osal/osal_dynlib.hpp>
|
||||
|
||||
#include <../3rdParty/vosk-api/include/vosk_api.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QStringDecoder>
|
||||
#include <QStringList>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "VRU.hpp"
|
||||
#include "VRUwords.hpp"
|
||||
|
||||
//
|
||||
// Local Enums
|
||||
//
|
||||
|
||||
enum Language
|
||||
{
|
||||
English = 0,
|
||||
JapaneseOrDemo = 1
|
||||
};
|
||||
|
||||
//
|
||||
// Local Variables
|
||||
//
|
||||
|
||||
// vosk-api library variables
|
||||
//
|
||||
|
||||
typedef VoskModel* (*ptr_vosk_model_new)(const char *);
|
||||
typedef void (*ptr_vosk_model_free)(VoskModel *);
|
||||
typedef VoskRecognizer* (*ptr_vosk_recognizer_new_grm)(VoskModel *, float, const char *);
|
||||
typedef void (*ptr_vosk_recognizer_free)(VoskRecognizer *);
|
||||
typedef int (*ptr_vosk_recognizer_accept_waveform)(VoskRecognizer *, const char *, int );
|
||||
typedef const char * (*ptr_vosk_recognizer_final_result)(VoskRecognizer *);
|
||||
typedef void (*ptr_vosk_set_log_level)(int);
|
||||
typedef void (*ptr_vosk_recognizer_set_max_alternatives)(VoskRecognizer *, int);
|
||||
|
||||
static ptr_vosk_model_new l_vosk_model_new = nullptr;
|
||||
static ptr_vosk_model_free l_vosk_model_free = nullptr;
|
||||
static ptr_vosk_recognizer_new_grm l_vosk_recognizer_new_grm = nullptr;
|
||||
static ptr_vosk_recognizer_free l_vosk_recognizer_free = nullptr;
|
||||
static ptr_vosk_recognizer_accept_waveform l_vosk_recognizer_accept_waveform = nullptr;
|
||||
static ptr_vosk_recognizer_final_result l_vosk_recognizer_final_result = nullptr;
|
||||
static ptr_vosk_set_log_level l_vosk_set_log_level = nullptr;
|
||||
static ptr_vosk_recognizer_set_max_alternatives l_vosk_recognizer_set_max_alternatives = nullptr;
|
||||
|
||||
static osal_dynlib_lib_handle l_VoskLibHandle = nullptr;
|
||||
|
||||
static VoskModel* l_VoskModel = nullptr;
|
||||
static VoskRecognizer* l_VoskRecognizer = nullptr;
|
||||
|
||||
// VRU implementation variables
|
||||
//
|
||||
static bool l_Initialized = false;
|
||||
|
||||
static std::vector<VRUWordEntry> l_WordEntries;
|
||||
|
||||
static int l_MicState = 0;
|
||||
static int l_WordListSize = 0;
|
||||
static int l_WordListCount = 0;
|
||||
|
||||
static QStringList l_RegisteredWords;
|
||||
static QList<int> l_RegisteredWordsIndex;
|
||||
|
||||
static SDL_AudioDeviceID l_AudioDevice = 0;
|
||||
static SDL_AudioSpec l_AudioDeviceSpec;
|
||||
|
||||
//
|
||||
// Local Functions
|
||||
//
|
||||
|
||||
static bool hook_vosk(void)
|
||||
{
|
||||
std::string debugMessage;
|
||||
|
||||
std::filesystem::path voskApiPath;
|
||||
voskApiPath = CoreGetLibraryDirectory();
|
||||
voskApiPath += "/libvosk" OSAL_DYNLIB_LIB_EXT_STR;
|
||||
|
||||
l_VoskLibHandle = osal_dynlib_open(voskApiPath);
|
||||
if (l_VoskLibHandle == nullptr)
|
||||
{
|
||||
debugMessage = "VRU: Failed to open library \"";
|
||||
debugMessage += voskApiPath.string().c_str();
|
||||
debugMessage += "\": ";
|
||||
debugMessage += osal_dynlib_strerror();
|
||||
CoreDebugCallbackMessage(CoreDebugMessageType::Error, debugMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
l_vosk_model_new = (ptr_vosk_model_new) osal_dynlib_sym(l_VoskLibHandle, "vosk_model_new");
|
||||
l_vosk_model_free = (ptr_vosk_model_free) osal_dynlib_sym(l_VoskLibHandle, "vosk_model_free");
|
||||
l_vosk_recognizer_new_grm = (ptr_vosk_recognizer_new_grm) osal_dynlib_sym(l_VoskLibHandle, "vosk_recognizer_new_grm");
|
||||
l_vosk_recognizer_free = (ptr_vosk_recognizer_free) osal_dynlib_sym(l_VoskLibHandle, "vosk_recognizer_free");
|
||||
l_vosk_recognizer_accept_waveform = (ptr_vosk_recognizer_accept_waveform) osal_dynlib_sym(l_VoskLibHandle, "vosk_recognizer_accept_waveform");
|
||||
l_vosk_recognizer_final_result = (ptr_vosk_recognizer_final_result) osal_dynlib_sym(l_VoskLibHandle, "vosk_recognizer_final_result");
|
||||
l_vosk_set_log_level = (ptr_vosk_set_log_level) osal_dynlib_sym(l_VoskLibHandle, "vosk_set_log_level");
|
||||
l_vosk_recognizer_set_max_alternatives = (ptr_vosk_recognizer_set_max_alternatives) osal_dynlib_sym(l_VoskLibHandle, "vosk_recognizer_set_max_alternatives");
|
||||
|
||||
if (l_vosk_model_new == nullptr ||
|
||||
l_vosk_model_free == nullptr ||
|
||||
l_vosk_recognizer_new_grm == nullptr ||
|
||||
l_vosk_recognizer_free == nullptr ||
|
||||
l_vosk_recognizer_accept_waveform == nullptr ||
|
||||
l_vosk_recognizer_final_result == nullptr ||
|
||||
l_vosk_set_log_level == nullptr ||
|
||||
l_vosk_recognizer_set_max_alternatives == nullptr)
|
||||
{
|
||||
debugMessage = "VRU: Failed to open library: missing functions";
|
||||
CoreDebugCallbackMessage(CoreDebugMessageType::Error, debugMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void unhook_vosk(void)
|
||||
{
|
||||
l_vosk_model_new = nullptr;
|
||||
l_vosk_model_free = nullptr;
|
||||
l_vosk_recognizer_new_grm = nullptr;
|
||||
l_vosk_recognizer_free = nullptr;
|
||||
l_vosk_recognizer_accept_waveform = nullptr;
|
||||
l_vosk_recognizer_final_result = nullptr;
|
||||
l_vosk_set_log_level = nullptr;
|
||||
l_vosk_recognizer_set_max_alternatives = nullptr;
|
||||
|
||||
osal_dynlib_close(l_VoskLibHandle);
|
||||
}
|
||||
|
||||
static bool setup_vosk_model(void)
|
||||
{
|
||||
std::string debugMessage;
|
||||
|
||||
std::filesystem::path voskModelPath;
|
||||
voskModelPath = CoreGetSharedDataDirectory();
|
||||
voskModelPath += "/vosk-model-small-en-us-0.15.zip";
|
||||
|
||||
bool missingModelFile = false;
|
||||
|
||||
std::filesystem::path voskModelFiles[] =
|
||||
{
|
||||
"vosk-model-small-en-us-0.15/am/final.mdl",
|
||||
"vosk-model-small-en-us-0.15/conf/mfcc.conf",
|
||||
"vosk-model-small-en-us-0.15/conf/model.conf",
|
||||
"vosk-model-small-en-us-0.15/graph/disambig_tid.int",
|
||||
"vosk-model-small-en-us-0.15/graph/Gr.fst",
|
||||
"vosk-model-small-en-us-0.15/graph/HCLr.fst",
|
||||
"vosk-model-small-en-us-0.15/graph/phones/word_boundary.int",
|
||||
"vosk-model-small-en-us-0.15/ivector/final.dubm",
|
||||
"vosk-model-small-en-us-0.15/ivector/final.ie",
|
||||
"vosk-model-small-en-us-0.15/ivector/final.mat",
|
||||
"vosk-model-small-en-us-0.15/ivector/global_cmvn.stats",
|
||||
"vosk-model-small-en-us-0.15/ivector/online_cmvn.conf",
|
||||
"vosk-model-small-en-us-0.15/ivector/splice.conf",
|
||||
};
|
||||
|
||||
for (std::filesystem::path& file : voskModelFiles)
|
||||
{
|
||||
std::filesystem::path fullFilePath;
|
||||
fullFilePath = CoreGetUserDataDirectory();
|
||||
fullFilePath += "/";
|
||||
fullFilePath += file;
|
||||
|
||||
if (!std::filesystem::exists(fullFilePath))
|
||||
{
|
||||
missingModelFile = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// all required files exist,
|
||||
// so we don't need to extract the zip again
|
||||
if (!missingModelFile)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!std::filesystem::is_regular_file(voskModelPath))
|
||||
{
|
||||
debugMessage = "VRU: vosk model file \"";
|
||||
debugMessage += voskModelPath.string().c_str();
|
||||
debugMessage += "\" doesn't exist!";
|
||||
CoreDebugCallbackMessage(CoreDebugMessageType::Error, debugMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CoreUnzip(voskModelPath, CoreGetUserDataDirectory()))
|
||||
{
|
||||
debugMessage = "VRU: Failed to extract vosk model file \"";
|
||||
debugMessage += voskModelPath.string().c_str();
|
||||
debugMessage += "\": ";
|
||||
debugMessage += CoreGetError();
|
||||
CoreDebugCallbackMessage(CoreDebugMessageType::Error, debugMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool init_vosk(void)
|
||||
{
|
||||
std::filesystem::path voskModelPath;
|
||||
voskModelPath = CoreGetUserDataDirectory();
|
||||
voskModelPath += "/vosk-model-small-en-us-0.15";
|
||||
|
||||
l_vosk_set_log_level(-1);
|
||||
|
||||
l_VoskModel = l_vosk_model_new(voskModelPath.string().c_str());
|
||||
if (l_VoskModel == nullptr)
|
||||
{
|
||||
CoreDebugCallbackMessage(CoreDebugMessageType::Error, "VRU: vosk_model_new failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void quit_vosk(void)
|
||||
{
|
||||
if (l_VoskModel != nullptr)
|
||||
{
|
||||
l_vosk_model_free(l_VoskModel);
|
||||
l_VoskModel = nullptr;
|
||||
}
|
||||
|
||||
if (l_VoskRecognizer != nullptr)
|
||||
{
|
||||
l_vosk_recognizer_free(l_VoskRecognizer);
|
||||
l_VoskRecognizer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static bool init_mic(void)
|
||||
{
|
||||
std::string debugMessage;
|
||||
SDL_AudioSpec desiredAudioSpec;
|
||||
|
||||
desiredAudioSpec.freq = 44100;
|
||||
desiredAudioSpec.format = AUDIO_S16SYS;
|
||||
desiredAudioSpec.channels = 1;
|
||||
desiredAudioSpec.samples = 1024;
|
||||
desiredAudioSpec.callback = nullptr;
|
||||
desiredAudioSpec.userdata = nullptr;
|
||||
|
||||
l_AudioDevice = SDL_OpenAudioDevice(nullptr, 1, &desiredAudioSpec, &l_AudioDeviceSpec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
|
||||
if (l_AudioDevice == 0)
|
||||
{
|
||||
debugMessage = "VRU: SDL_OpenAudioDevice Failed: ";
|
||||
debugMessage += SDL_GetError();
|
||||
CoreDebugCallbackMessage(CoreDebugMessageType::Error, debugMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void quit_mic(void)
|
||||
{
|
||||
if (l_AudioDevice != 0)
|
||||
{
|
||||
SDL_CloseAudioDevice(l_AudioDevice);
|
||||
}
|
||||
}
|
||||
|
||||
static QByteArray words_to_byte_array(uint16_t* words, uint16_t size)
|
||||
{
|
||||
QByteArray byte_array;
|
||||
|
||||
uint8_t* bytes = (uint8_t*)words;
|
||||
|
||||
for (int i = 0; i < (size * 2); i += 2)
|
||||
{
|
||||
byte_array.push_back(bytes[i + 1]);
|
||||
byte_array.push_back(bytes[i]);
|
||||
}
|
||||
|
||||
return byte_array;
|
||||
}
|
||||
|
||||
static bool find_word_from_hex(std::string hex, std::string& word)
|
||||
{
|
||||
auto predicate = [hex](const VRUWordEntry& entry)
|
||||
{
|
||||
return entry.hex == hex;
|
||||
};
|
||||
|
||||
auto iter = std::find_if(l_WordEntries.begin(), l_WordEntries.end(), predicate);
|
||||
if (iter != l_WordEntries.end())
|
||||
{
|
||||
word = (*iter).words;
|
||||
}
|
||||
|
||||
return iter != l_WordEntries.end();
|
||||
}
|
||||
|
||||
//
|
||||
// Exported Functions
|
||||
//
|
||||
|
||||
bool InitVRU(void)
|
||||
{
|
||||
if (l_Initialized)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!hook_vosk())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!setup_vosk_model())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!init_vosk())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!init_mic())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
l_MicState = 0;
|
||||
|
||||
if (l_WordEntries.empty())
|
||||
{
|
||||
l_WordEntries = GetVRUWordEntries();
|
||||
}
|
||||
|
||||
l_Initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsVRUInit(void)
|
||||
{
|
||||
return l_Initialized;
|
||||
}
|
||||
|
||||
bool QuitVRU(void)
|
||||
{
|
||||
if (!l_Initialized)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
quit_mic();
|
||||
quit_vosk();
|
||||
unhook_vosk();
|
||||
|
||||
l_Initialized = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
int GetVRUMicState(void)
|
||||
{
|
||||
return l_MicState;
|
||||
}
|
||||
|
||||
//
|
||||
// VRU Plugin Functions
|
||||
//
|
||||
|
||||
EXPORT void CALL SendVRUWord(uint16_t length, uint16_t* word, uint8_t lang)
|
||||
{
|
||||
if (!l_Initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray byte_array = words_to_byte_array(word, length);
|
||||
std::string debugMessage;
|
||||
std::string word_str;
|
||||
|
||||
if (find_word_from_hex(byte_array.toHex().toUpper().toStdString(), word_str))
|
||||
{ // found word
|
||||
l_RegisteredWords.append(QString::fromStdString(word_str));
|
||||
l_RegisteredWordsIndex.append(l_WordListCount);
|
||||
}
|
||||
else
|
||||
{ // not found
|
||||
QStringDecoder utf8_decoder = QStringDecoder(QStringDecoder::Utf8);
|
||||
QString encoded_string = utf8_decoder.decode(byte_array);
|
||||
|
||||
if (utf8_decoder.hasError())
|
||||
{
|
||||
debugMessage = "VRU: unknown word: ";
|
||||
debugMessage += encoded_string.toStdString();
|
||||
CoreDebugCallbackMessage(CoreDebugMessageType::Error, debugMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
l_RegisteredWords.append(encoded_string);
|
||||
l_RegisteredWordsIndex.append(l_WordListCount);
|
||||
}
|
||||
}
|
||||
|
||||
l_WordListCount++;
|
||||
|
||||
// we've reached the end of the list
|
||||
if (l_WordListCount == l_WordListSize)
|
||||
{
|
||||
// needed for vosk-api, see vosk_api.h
|
||||
l_RegisteredWords.append("[unk]");
|
||||
l_RegisteredWordsIndex.append(l_WordListCount);
|
||||
|
||||
QJsonDocument json_document;
|
||||
json_document.setArray(QJsonArray::fromStringList(l_RegisteredWords));
|
||||
|
||||
// free existing recognizer if needed
|
||||
if (l_VoskRecognizer)
|
||||
{
|
||||
l_vosk_recognizer_free(l_VoskRecognizer);
|
||||
l_VoskRecognizer = nullptr;
|
||||
}
|
||||
|
||||
// try to create a new recognizer
|
||||
l_VoskRecognizer = l_vosk_recognizer_new_grm(l_VoskModel, (float)l_AudioDeviceSpec.freq, json_document.toJson().constData());
|
||||
if (l_VoskRecognizer != nullptr)
|
||||
{
|
||||
l_vosk_recognizer_set_max_alternatives(l_VoskRecognizer, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT void CALL SetMicState(int state)
|
||||
{
|
||||
if (!l_Initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (state)
|
||||
{
|
||||
SDL_ClearQueuedAudio(l_AudioDevice);
|
||||
SDL_PauseAudioDevice(l_AudioDevice, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
SDL_PauseAudioDevice(l_AudioDevice, 1);
|
||||
}
|
||||
|
||||
l_MicState = state;
|
||||
}
|
||||
|
||||
EXPORT void CALL ReadVRUResults(uint16_t* error_flags, uint16_t* num_results, uint16_t* mic_level, uint16_t* voice_level, uint16_t* voice_length, uint16_t* matches)
|
||||
{
|
||||
if (!l_Initialized || l_VoskRecognizer == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// initialize result data
|
||||
*num_results = 0;
|
||||
*mic_level = 0xBB8;
|
||||
*voice_level = 0xBB8;
|
||||
*voice_length = 0x8004;
|
||||
for (int i = 0; i < 10; i += 2)
|
||||
{
|
||||
matches[i] = 0x7FFF;
|
||||
matches[i + 1] = 0;
|
||||
}
|
||||
|
||||
// retrieve audio data from device
|
||||
SDL_PauseAudioDevice(l_AudioDevice, 1);
|
||||
|
||||
void* audio_buf = nullptr;
|
||||
uint32_t audio_size = SDL_GetQueuedAudioSize(l_AudioDevice);
|
||||
|
||||
audio_buf = malloc(audio_size);
|
||||
if (audio_buf == nullptr)
|
||||
{
|
||||
*error_flags = 0x8000;
|
||||
return;
|
||||
}
|
||||
|
||||
audio_size = SDL_DequeueAudio(l_AudioDevice, audio_buf, audio_size);
|
||||
|
||||
// hand audio data to vosk and retrieve results
|
||||
int ret = l_vosk_recognizer_accept_waveform(l_VoskRecognizer, (const char*)audio_buf, audio_size);
|
||||
if (ret == -1)
|
||||
{
|
||||
*error_flags = 0x8000;
|
||||
free(audio_buf);
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonDocument json_document = QJsonDocument::fromJson(l_vosk_recognizer_final_result(l_VoskRecognizer));
|
||||
|
||||
// parse results from vosk
|
||||
QJsonArray alternatives_array = json_document.object().value("alternatives").toArray();
|
||||
|
||||
QStringList alternative_words;
|
||||
QStringList found_words;
|
||||
std::string debugMessage;
|
||||
|
||||
// parse words
|
||||
for (int i = 0; i < alternatives_array.size(); i++)
|
||||
{
|
||||
QString word = alternatives_array.at(i).toObject().value("text").toString();
|
||||
if (!word.isEmpty())
|
||||
{
|
||||
alternative_words.append(word);
|
||||
}
|
||||
}
|
||||
|
||||
// check the registered words,
|
||||
// and see if any matches with the recognized words
|
||||
for (const QString& word : l_RegisteredWords)
|
||||
{
|
||||
if (word != "[unk]" &&
|
||||
!found_words.contains(word) &&
|
||||
alternative_words.filter(word).size() > 0)
|
||||
{
|
||||
found_words.append(word);
|
||||
if (found_words.size() == 5)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sort found words by length
|
||||
std::sort(found_words.begin(), found_words.end(), [](QString& a, QString& b)
|
||||
{
|
||||
return a.size() > b.size();
|
||||
});
|
||||
|
||||
int matches_index = 0;
|
||||
for (int i = 0; i < found_words.size(); i++)
|
||||
{
|
||||
matches[matches_index++] = l_RegisteredWordsIndex.at(l_RegisteredWords.indexOf(found_words.at(i)));
|
||||
matches[matches_index++] = i * 256;
|
||||
debugMessage = "VRU: found match ";
|
||||
debugMessage += std::to_string(i);
|
||||
debugMessage += ": ";
|
||||
debugMessage += found_words.at(i).toStdString();
|
||||
CoreDebugCallbackMessage(CoreDebugMessageType::Info, debugMessage);
|
||||
}
|
||||
|
||||
if (found_words.size() == 0 && alternative_words.size() > 0)
|
||||
{
|
||||
// heard something but it didn't match anything
|
||||
*error_flags = 0x4000;
|
||||
matches[0] = 0;
|
||||
found_words.append("0");
|
||||
debugMessage = "VRU: heard something but it didn't match anything";
|
||||
CoreDebugCallbackMessage(CoreDebugMessageType::Info, debugMessage);
|
||||
}
|
||||
|
||||
*num_results = found_words.size();
|
||||
|
||||
free(audio_buf);
|
||||
}
|
||||
|
||||
EXPORT void CALL ClearVRUWords(uint8_t length)
|
||||
{
|
||||
if (!l_Initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
l_WordListCount = 0;
|
||||
l_WordListSize = length;
|
||||
|
||||
l_RegisteredWords.clear();
|
||||
l_RegisteredWordsIndex.clear();
|
||||
|
||||
if (l_VoskRecognizer != nullptr)
|
||||
{
|
||||
l_vosk_recognizer_free(l_VoskRecognizer);
|
||||
l_VoskRecognizer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT void CALL SetVRUWordMask(uint8_t length, uint8_t* mask)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
|
||||
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 3.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef VRU_HPP
|
||||
#define VRU_HPP
|
||||
|
||||
// attempts to initialize VRU capabilities
|
||||
bool InitVRU(void);
|
||||
|
||||
// returns whether VRU has been initialized
|
||||
bool IsVRUInit(void);
|
||||
|
||||
// attempts to de-initialize VRY capabilities
|
||||
bool QuitVRU(void);
|
||||
|
||||
int GetVRUMicState(void);
|
||||
|
||||
#endif // VRU_HPP
|
||||
@@ -0,0 +1,739 @@
|
||||
/*
|
||||
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
|
||||
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 3.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "VRUwords.hpp"
|
||||
|
||||
//
|
||||
// Exported Functions
|
||||
//
|
||||
|
||||
std::vector<VRUWordEntry> GetVRUWordEntries(void)
|
||||
{
|
||||
std::vector<VRUWordEntry> entries;
|
||||
|
||||
entries.push_back({"03A50024000303CF00A80003036000EA", "pikachu"});
|
||||
entries.push_back({"03A50045000303CF00A80003036000EA", "pikachu" });
|
||||
entries.push_back({"03A50024000303C900450003036000EA", "pikachu" });
|
||||
entries.push_back({"03A8018F000303CF00A80003036000EA", "pikachu" });
|
||||
entries.push_back({"03B101B0000303CF00A80003036000EA", "pikachu" });
|
||||
entries.push_back({"03CF00A80003036000EA", "pikachu" });
|
||||
entries.push_back({"03A80066000303CF00A80003036000F900EA", "pikachu" });
|
||||
entries.push_back({"03A50024000303CF00A80003035D001200F900EA", "pikachu" });
|
||||
entries.push_back({"040801740024", "hey" });
|
||||
entries.push_back({"03CF00C603360405000F0234", "come here" });
|
||||
entries.push_back({"0369004803FC0318018F", "this way" });
|
||||
entries.push_back({"039C010B000603900006037B01B0", "good bye" });
|
||||
entries.push_back({"039900A80006037B01B0", "good bye" });
|
||||
entries.push_back({"03F900090309009F02E2018F000303BD0234", "see you later" });
|
||||
entries.push_back({"037B01B00006037B01B0", "bye bye" });
|
||||
entries.push_back({"03FC000303C30255000303C6", "start" });
|
||||
entries.push_back({"02E2006903FC000303B402E2018F", "lets play" });
|
||||
entries.push_back({"040B00C002EB0213", "hello" });
|
||||
entries.push_back({"043B0213000303AB00A5034803FC006903FC00A503270024", "open sesame" });
|
||||
entries.push_back({"043B0213000303A80063034803FC006903FC00A503270024", "open sesame" });
|
||||
entries.push_back({"039F01FE00990318018F", "go away" });
|
||||
entries.push_back({"039C010B00060390033302D603390042035A", "good morning" });
|
||||
entries.push_back({"039900A5033302D603390042035A", "good morning" });
|
||||
entries.push_back({"042F01AD033603FC023A0024", "im sorry" });
|
||||
entries.push_back({"042F01AD033603FC012602F10024", "im sorry" });
|
||||
entries.push_back({"03FC023A0024", "sorry" });
|
||||
entries.push_back({"03FC012602F10024", "sorry" });
|
||||
entries.push_back({"042F01B00003036000ED03DB030C00EA", "i choose you" });
|
||||
entries.push_back({"04080174030C00EA000303A50024000303CF00A80003036000EA", "hey you pikachu" });
|
||||
entries.push_back({"03A50024000303CF00A8000303A50024000303CF00A80003036000EA", "pika pikachu" });
|
||||
entries.push_back({"03A50024000303CF00A8000303A50024000303CF00A8000303A50024", "pika pika pi" });
|
||||
entries.push_back({"03A50024000303A50024000303CF00A80003036000EA", "pi pikachu" });
|
||||
entries.push_back({"03A50024000303A50024000303CF00A8000303A50024", "pi pika pi" });
|
||||
entries.push_back({"03A50024000303D8000303CF00C9", "pika" });
|
||||
entries.push_back({"03A50024000303CF00A8", "pika" });
|
||||
entries.push_back({"03A50024000303CF00A8000303A50024000303CF00A8", "pika pika" });
|
||||
entries.push_back({"03A503030024000303CF00A8", "pi ka" });
|
||||
entries.push_back({"03A50024000303CF00C9000303CF00C9", "pi ka ka" });
|
||||
entries.push_back({"03A50024000303CF00A8000303CF00A8000303A50024", "pi ka ka pi" });
|
||||
entries.push_back({"037E02F10042035A036C0087000303C604050297", "bring that here" });
|
||||
entries.push_back({"039F0213000603960066000303B70045000303C6", "go get it" });
|
||||
entries.push_back({"0393004803E70045000303C6000303C000E703270024", "give it to me" });
|
||||
entries.push_back({"0393004203270027036C0087000303C6", "gimme that" });
|
||||
entries.push_back({"03F600C603480006038702370402014D000303D8", "thunder shock" });
|
||||
entries.push_back({"03F600C603480006038702340006037B020A02EE000303C6", "thunderbolt" });
|
||||
entries.push_back({"03F600C60348000603870234", "thunder" });
|
||||
entries.push_back({"03F600C6033F0234", "thunder" });
|
||||
entries.push_back({"0432009F02E20066000303D8000303C602F10045000303D802F40087000303C6", "electric rat" });
|
||||
entries.push_back({"0426001B02E20066000303D8000303C602F10045000303D802F40087000303C6", "electric rat" });
|
||||
entries.push_back({"0432009F02E20066000303C602F10045000303D802F40087000303C6", "electric rat" });
|
||||
entries.push_back({"042F01A40408018F000303B7030C00EA", "i hate you" });
|
||||
entries.push_back({"03A202F4018F000303C600060366014D0006037E", "great job" });
|
||||
entries.push_back({"036C0087000303C3031B00AB03DE000603A202F4018F000303C6", "that was great" });
|
||||
entries.push_back({"0309023703FC0213000303C9030C00EA000303C6", "you're so cute" });
|
||||
entries.push_back({"03BD00A202F1004803ED0045000303D8", "terrific" });
|
||||
entries.push_back({"037500C9000303BD023703F002F10024", "butter free" });
|
||||
entries.push_back({"043200C603360006037E02F4005D02E500A8", "am brel a" });
|
||||
entries.push_back({"037B01CE035A0006037B01CE035A", "boing boing" });
|
||||
entries.push_back({"043801290348000303C000ED036C00A5033C0066000303D803FC000303C603FC000303BA018F00060366", "on to the next stage" });
|
||||
entries.push_back({"042F0087000303AB009F02EE", "apple" });
|
||||
entries.push_back({"043200A8000303AB009F02EE", "apple" });
|
||||
entries.push_back({"0426000C0087000303B402EE", "apple" });
|
||||
entries.push_back({"03A202F10021033C0087000303AB009F02EE", "green apple" });
|
||||
entries.push_back({"0372018F000303D8000303BA0087000303AB009F02EE", "baked apple" });
|
||||
entries.push_back({"039F020A02EE0006038700A5033C0087000303AB009F02EE", "golden apple" });
|
||||
entries.push_back({"03A5002400030360", "peach" });
|
||||
entries.push_back({"03FC000303C602FD012C00060372029D0024", "strawberry" });
|
||||
entries.push_back({"03FC000303BD0225012C00060372029D0024", "strawberry" });
|
||||
entries.push_back({"02F4008A03DE00060372029D0024", "raspberry" });
|
||||
entries.push_back({"02F4008A03FC0006037E02F10024", "raspberry" });
|
||||
entries.push_back({"037500A5033C0084033F00A8", "banana" });
|
||||
entries.push_back({"036F0042033C0084033F00C9", "banana" });
|
||||
entries.push_back({"037500C6033C0084033F00A8", "banana" });
|
||||
entries.push_back({"03CC0087000303C6000303BA018602EE", "cat tail" });
|
||||
entries.push_back({"032101A702EE00060390040B02340006037E", "wild herb" });
|
||||
entries.push_back({"03D502D60348", "corn" });
|
||||
entries.push_back({"03B1014D000303B4000303D502D60348", "popcorn" });
|
||||
entries.push_back({"03AB00C60336000303B4000303C900420348", "pumpkin" });
|
||||
entries.push_back({"03AB00C60336000303C900420348", "pumpkin" });
|
||||
entries.push_back({"03AB00C6035A000303CF00A50348", "pumpkin" });
|
||||
entries.push_back({"03C00273033F00A8000303B4", "turnip" });
|
||||
entries.push_back({"03C0027303390045000303B4", "turnip" });
|
||||
entries.push_back({"03CC00870006036F004500060366", "cabbage" });
|
||||
entries.push_back({"03CC008102F700A8000303C6", "carrot" });
|
||||
entries.push_back({"03CC02A300A8000303C6", "carrot" });
|
||||
entries.push_back({"043200C60339030900A50348", "onion" });
|
||||
entries.push_back({"043200A5033F00A50348", "onion" });
|
||||
entries.push_back({"043501080339030900A50348", "onion" });
|
||||
entries.push_back({"03FC031500240006037500C9000303BA018F0006038D0213", "sweet potato" });
|
||||
entries.push_back({"03FC03150024000303C6000303AB00A8000303C3016E0006038D0213", "sweet potato" });
|
||||
entries.push_back({"03FC03150024000303B4000303BA018F0006038700A8", "sweet potato" });
|
||||
entries.push_back({"0411014D000303C603FC03150024000303C6000303AB00C9000303BA018F0006038D0213", "hot sweet potato" });
|
||||
entries.push_back({"03FC032A005D02DF002703F002FA00EA000303C6", "smelly fruit" });
|
||||
entries.push_back({"03FC000303B70045000303C9002703F002FA00EA000303C6", "sticky fruit" });
|
||||
entries.push_back({"02FD021603FC000303BD00A80006039003F002FA00EA000303C6", "roasted fruit" });
|
||||
entries.push_back({"0321012302EE033F00A8000303C6", "walnut" });
|
||||
entries.push_back({"03D802F40087000303D50321012302EE033F00A8000303C6", "cracked walnut" });
|
||||
entries.push_back({"03FC000303B101B0000303C900270402005D02EE", "spiky shell" });
|
||||
entries.push_back({"03FC000303B101AD033900270402005D02EE", "spiny shell" });
|
||||
entries.push_back({"0360006903FC033F00A8000303C6", "chestnut" });
|
||||
entries.push_back({"036F002400030360033F00C9000303C6", "beach nut" });
|
||||
entries.push_back({"03D50213000303CF00A5033F00C9000303C6", "coconut" });
|
||||
entries.push_back({"042C018F000303D502D60348", "acorn" });
|
||||
entries.push_back({"03C3021603FC000303BD00A800060384018F000303D502D60348", "toasted acorn" });
|
||||
entries.push_back({"03F002EB01950042034E018F000303D502D60348", "flying acorn" });
|
||||
entries.push_back({"042C018F000303D502D60348000303C3014D000303B4", "acorn top" });
|
||||
entries.push_back({"036600C603360006037B0216040202FA00E70336", "jumbo mushroom" });
|
||||
entries.push_back({"02FD021603FC000303BD00A8000603900006036600A503360006037B0213", "roasted jumbo" });
|
||||
entries.push_back({"039002F1002103270027040202FA00E70336", "dreamy mushroom" });
|
||||
entries.push_back({"03C602F700CC03F0009F02EE000303C3014D000303B4", "truffle top" });
|
||||
entries.push_back({"037E02E800ED040202FA00E70336", "blue mushroom" });
|
||||
entries.push_back({"0411014D000303C6040202FA00E70336", "hot mushroom" });
|
||||
entries.push_back({"02F4006600060390040202FA00E70336", "red mushroom" });
|
||||
entries.push_back({"03FC00C603390027040202FA00E70336", "sunny mushroom" });
|
||||
entries.push_back({"032D00CC040202FA00E70336", "mushroom" });
|
||||
entries.push_back({"0411014D000303C6032D00CC040202FA00E70336", "hot mushroom" });
|
||||
entries.push_back({"02EB01B0000303C60006037500C002EE0006037E", "light bulb" });
|
||||
entries.push_back({"02EB01B0000303C600060378010202EE0006037E", "light bulb" });
|
||||
entries.push_back({"03CF00C9000303B4000303CC018F000303D8", "cupcake" });
|
||||
entries.push_back({"03840174002703DB0024", "daisy" });
|
||||
entries.push_back({"03A50045000303C000E70339030900A8", "petunia" });
|
||||
entries.push_back({"03C000E102E500A8000303B4", "tulip" });
|
||||
entries.push_back({"03C000E102DF0045000303B4", "tulip" });
|
||||
entries.push_back({"03840084034800060387009F02EB019B00A50348", "dandelion" });
|
||||
entries.push_back({"03840084034800060381001B02EB019B00A50348", "dandelion" });
|
||||
entries.push_back({"037E02E800EA00060372005D02EE", "bluebell" });
|
||||
entries.push_back({"03FC000303CF00C60348000303D80006037E02EB015003FC00A50336", "skunk blossom" });
|
||||
entries.push_back({"03FC00C6034803F002EB01DD0234", "sunflower" });
|
||||
entries.push_back({"02DF003C02DF0024", "lily" });
|
||||
entries.push_back({"02FD021603DE0006037500C900060390", "rose bud" });
|
||||
entries.push_back({"02F400660006039002FD021603DE", "red rose" });
|
||||
entries.push_back({"0375009F02E800E70348", "balloon" });
|
||||
entries.push_back({"03B10213000303CC018F00060381004803FC000303D8", "poke disc" });
|
||||
entries.push_back({"036F0024000303600006037B012302EE", "beach ball" });
|
||||
entries.push_back({"0321012C000303BD02190042035A000303CC00840348", "watering can" });
|
||||
entries.push_back({"0321012C000303BD02190042035A0006036600C9000603A2", "watering jug" });
|
||||
entries.push_back({"0306005D02EB02130006037B012302EE", "yellow ball" });
|
||||
entries.push_back({"03600255000303D5020A02EE", "charcoal" });
|
||||
entries.push_back({"02DF002703F004110315004803FC009F02EE", "leaf whistle" });
|
||||
entries.push_back({"041102520333014A03390045000303CF00A8", "harmonica" });
|
||||
entries.push_back({"03C602F700C60336000303A80066", "trumpet" });
|
||||
entries.push_back({"03C602FA01080336000303AB00A8000303C6", "trumpet" });
|
||||
entries.push_back({"037500A5033C0084033F00A8000303A5000F009F02EE", "banana peel" });
|
||||
entries.push_back({"032A018F0006039900AB03F002100348", "megaphone" });
|
||||
entries.push_back({"039F020A02EE00060390000303D501CE0348", "gold coin" });
|
||||
entries.push_back({"0360014D000303CF009F02E500A8000303C6000303D501CE0348", "chocolate coin" });
|
||||
entries.push_back({"0402014D000303D802E500A8000303C6000303D501CE0348", "chocolate coin" });
|
||||
entries.push_back({"03F9003C02EE03EA0234000303D501CE0348", "silver coin" });
|
||||
entries.push_back({"03D5014D000303AB0234000303D501CE0348", "copper coin" });
|
||||
entries.push_back({"02FA00EA0006036F0024", "ruby" });
|
||||
entries.push_back({"03FC008A03F001AA0300", "sapphire" });
|
||||
entries.push_back({"03FC008A03F0019B0234", "sapphire" });
|
||||
entries.push_back({"03C30213000303A8008A03DE", "topaz" });
|
||||
entries.push_back({"02F40066000603900333025500060375009F02EE", "red marble" });
|
||||
entries.push_back({"037E02E800E70333025500060375009F02EE", "blue marble" });
|
||||
entries.push_back({"0306005D02EB02100333025500060375009F02EE", "yellow marble" });
|
||||
entries.push_back({"02FA00EA0006036F001E02F10042035A", "ruby ring" });
|
||||
entries.push_back({"037B014D00060387009F02EE000303CC0087000303B402F10042035A", "bottle cap ring" });
|
||||
entries.push_back({"03B101AA02F700A8000303C603FC02D900060390", "pirate sword" });
|
||||
entries.push_back({"03B101AA02F10045000303C603FC02D900060390", "pirate sword" });
|
||||
entries.push_back({"03C301D403FC0126030000060390", "toy sword" });
|
||||
entries.push_back({"037502340006039003F00069036C0234", "bird feather" });
|
||||
entries.push_back({"037B020A02EE000303C6", "bolt" });
|
||||
entries.push_back({"032A0087000603A203390045000303C6", "magnet" });
|
||||
entries.push_back({"032A018F000603A203390045000303C6", "magnet" });
|
||||
entries.push_back({"0333025500060375009F02EE", "marble" });
|
||||
entries.push_back({"03D802F4018F000303C6", "crate" });
|
||||
entries.push_back({"03D50255000603900006037B02D9000603900006037B014D000303D803FC", "cardboard box" });
|
||||
entries.push_back({"03CF00A503360405000F0234", "come here" });
|
||||
entries.push_back({"043B021603EA022804050297", "over here" });
|
||||
entries.push_back({"040B009F02EB0213", "hello" });
|
||||
entries.push_back({"039C010B00060390034501950024000303C6", "good night" });
|
||||
entries.push_back({"039900A5034501B0000303C6", "good night" });
|
||||
entries.push_back({"03F6018C035A000303C9030C00EA", "thank you" });
|
||||
entries.push_back({"038A00ED03FC00C6033603F30042035A", "do something" });
|
||||
entries.push_back({"02DF004803FC00A50348000303BD00A503270024", "listen to me" });
|
||||
entries.push_back({"03A500090024000303CF00A8", "pi ka" });
|
||||
entries.push_back({"039F0213000603930045000303BD00A8000303C6", "go get it" });
|
||||
entries.push_back({"036C0087000303C603FC03F001AD0348", "thats fine" });
|
||||
entries.push_back({"043B0213000303CC01740024", "ok" });
|
||||
entries.push_back({"04020276", "sure" });
|
||||
entries.push_back({"02FD01B0000303C6", "right" });
|
||||
entries.push_back({"02E2006903FC0006038A00ED036C0087000303C6", "lets do that" });
|
||||
entries.push_back({"0345014D000303C6036C0087000303C3031B00C60348", "not that one" });
|
||||
entries.push_back({"036C0087000303C603DE02FD0129035A", "thats wrong" });
|
||||
entries.push_back({"0372008700060390000303B10213000303CC018C0333014A0348", "bad pokemon" });
|
||||
entries.push_back({"038D02100348000303C60006038A00ED036C0087000303C6", "dont do that" });
|
||||
entries.push_back({"042900420348000303CF021C0066000303D8000303C6", "incorrect" });
|
||||
entries.push_back({"03CF00C9000303BD00A8000303C301F2000303C6", "cut it out" });
|
||||
entries.push_back({"03450213", "no" });
|
||||
entries.push_back({"0318018F0006038700A80006039F0213", "way to go" });
|
||||
entries.push_back({"03F002EB01DD0234", "flower" });
|
||||
entries.push_back({"038400840348000303C603FC", "dance" });
|
||||
entries.push_back({"03F602FD01F80045000303C6", "throw it" });
|
||||
entries.push_back({"03C3015003F90045000303C6", "toss it" });
|
||||
entries.push_back({"030C00EA000303CC008403390024000303C6036C0087000303C6", "you can eat that" });
|
||||
entries.push_back({"03BA019203FC000303B70045000303C6", "taste it" });
|
||||
entries.push_back({"041101F503DB0045000303C6000303BA019203FC000303C6", "hows it taste" });
|
||||
entries.push_back({"041101F503DB0045000303C603FC032A005D02EE", "hows it smell" });
|
||||
entries.push_back({"041101F503DB0045000303C603FC01EF034800060390", "hows it sound" });
|
||||
entries.push_back({"0411031B00A8000303C60006038700AB03DB0045000303C60006038A00EA", "what does it do" });
|
||||
entries.push_back({"03B402E201830411031B00A8000303C6", "play what" });
|
||||
entries.push_back({"03C602FD01950045000303C6", "try it" });
|
||||
entries.push_back({"03B402E201740045000303C6", "play it" });
|
||||
entries.push_back({"039002FD014D000303A50045000303C6", "drop it" });
|
||||
entries.push_back({"03A80066000303CF00A80003036000EA", "pikachu" });
|
||||
entries.push_back({"030C00EA000303CC00840348000303BA019203FC000303B70045000303C6", "you can taste it" });
|
||||
entries.push_back({"041101F503DB0045000303C603FC01EF0348", "hows it sound" });
|
||||
entries.push_back({"03AE010B000303C6036C0087000303C6000603720087000303D8", "put that back" });
|
||||
entries.push_back({"03FC000303BA018003150048036C03270024", "stay with me" });
|
||||
entries.push_back({"03FC000303BA01770087000303C6033301A4041101F503FC", "stay at my house" });
|
||||
entries.push_back({"03F90009030C00E102E2018F000303BD0234", "see you later" });
|
||||
entries.push_back({"03F90009030C00EA", "see you" });
|
||||
entries.push_back({"037B01B0", "bye" });
|
||||
entries.push_back({"0411031B00A8000303BD0219030900A80006038A00CF0042035A", "what are you doing" });
|
||||
entries.push_back({"0411031B00A8000303C603FC00C9000303B4", "whats up" });
|
||||
entries.push_back({"0411031B00A8000303C603FC00C9000303B4036C02B8", "whats up there" });
|
||||
entries.push_back({"042F0087000303AB009F02EE", "apple" });
|
||||
entries.push_back({"033C006903EA0231033301AD034800060390", "never mind" });
|
||||
entries.push_back({"039C010B00060390034501B0000303C6", "good night" });
|
||||
entries.push_back({"03F60084035A000303C9030C00EA", "thank you" });
|
||||
entries.push_back({"04290045000303C603DE02F700C603390042035100990318018F", "its running away" });
|
||||
entries.push_back({"03F002EB01DD02340006037500C900060390", "flower bud" });
|
||||
entries.push_back({"03BA008700060393030902190045000303C6", "tag you're it" });
|
||||
entries.push_back({"039F01FE00BA0318018F", "go away" });
|
||||
entries.push_back({"02E8010B000303D5021603EA0237036C02B8", "look over there" });
|
||||
entries.push_back({"04290045000303C603FC021603EA0237036C02B8", "its over there" });
|
||||
entries.push_back({"02DF003C02DF0024", "lily" });
|
||||
entries.push_back({"03FC00C6034803F002EB01DD0234", "sunflower" });
|
||||
entries.push_back({"03FC000303CF00C60348000303D80006037E02EB015003FC014A0336", "skunk blossom" });
|
||||
entries.push_back({"03FC000303C602FD016E00060372029D0024", "strawberry" });
|
||||
entries.push_back({"02F4008A03FC000303B402F10024", "raspberry" });
|
||||
entries.push_back({"0438012C0006038100480402", "odd ish" });
|
||||
entries.push_back({"03A202E800E70336", "gloom" });
|
||||
entries.push_back({"03EA01A702EE000303B402E800E70336", "vile plume" });
|
||||
entries.push_back({"042F01AD033603FC02BE0024", "im sorry" });
|
||||
entries.push_back({"03FC02BE0024", "sorry" });
|
||||
entries.push_back({"03CC0084033F00A50348", "cannon" });
|
||||
entries.push_back({"03810045000603930045000303C301F2000303C6", "dig it out" });
|
||||
entries.push_back({"03810045000603A2036C017A0234", "dig there" });
|
||||
entries.push_back({"03810045000603A2", "dig" });
|
||||
entries.push_back({"03AE010202EE", "pull" });
|
||||
entries.push_back({"033F00C9000303C6", "nut" });
|
||||
entries.push_back({"03780108033C0084033F00C9", "banana" });
|
||||
entries.push_back({"03C602F4006903E4023400030360006903FC000303C6", "treasure chest" });
|
||||
entries.push_back({"03C602F4006903E40234", "treasure" });
|
||||
entries.push_back({"0372029D002400060390000303C602F4006903E40234", "buried treasure" });
|
||||
entries.push_back({"0408005D02EB0213", "hello" });
|
||||
entries.push_back({"042F01A702E500CC03E7030C00EA", "i love you" });
|
||||
entries.push_back({"04080084035701290348", "hang on" });
|
||||
entries.push_back({"03C602F40066000603660234", "treasure" });
|
||||
entries.push_back({"03960066000303C602EB015003FC000303C6", "get lost" });
|
||||
entries.push_back({"03A50024000303CF00BD040B00A8", "pee ka" });
|
||||
entries.push_back({"03A500090024000303CF00C9", "pi ka" });
|
||||
entries.push_back({"0375009C041101AD034800060381030C00EA", "behind you" });
|
||||
entries.push_back({"036F0018041101AD03480006036600EA", "behind you" });
|
||||
entries.push_back({"041101EF032A006303390024", "how many" });
|
||||
entries.push_back({"041101EF032A0063033900150258036C02B8", "how many are there" });
|
||||
entries.push_back({"03B1020A02DF001503180087000603A2", "poly wag" });
|
||||
entries.push_back({"03B1014402DF001503180087000603A2", "poly wag" });
|
||||
entries.push_back({"03B1020A02DF0015031B022B02EE", "poliwhirl" });
|
||||
entries.push_back({"03FC000303D5031B0234000303BD009F02EE", "squirt el" });
|
||||
entries.push_back({"0396008A03FC000303C602DF0024", "ghastly" });
|
||||
entries.push_back({"041101290348000303BD0234", "haunt er" });
|
||||
entries.push_back({"02FD0129035A", "wrong" });
|
||||
entries.push_back({"036C0087000303C603DE02FD0129035A", "thats wrong" });
|
||||
entries.push_back({"03D503150045000303C6", "quit" });
|
||||
entries.push_back({"03CF00C9000303B700450006038D01F2000303C6", "cut it out" });
|
||||
entries.push_back({"03CF00A503270297", "come here" });
|
||||
entries.push_back({"03CF00A5033604050297", "come here" });
|
||||
entries.push_back({"02E8010B000303D5021603EA022804050297", "look over here" });
|
||||
entries.push_back({"02E8010B000303D501F2000303C6", "look out" });
|
||||
entries.push_back({"0321012C0003036001F2000303C6", "watch out" });
|
||||
entries.push_back({"036C02B8", "there" });
|
||||
entries.push_back({"03FC03150042035A034501F2", "swing now" });
|
||||
entries.push_back({"03FC03150042035A", "swing" });
|
||||
entries.push_back({"039F020D02FD01B0000303C6", "go right" });
|
||||
entries.push_back({"02FD01B0000303C6", "right" });
|
||||
entries.push_back({"03F00258036C022E02FD01B0000303C6", "farther right" });
|
||||
entries.push_back({"039F020A02E2006903F0000303C6", "go left" });
|
||||
entries.push_back({"02E2006903F0000303C6", "left" });
|
||||
entries.push_back({"03F00258036C022B02E2006903F0000303C6", "farther left" });
|
||||
entries.push_back({"03FC000303C3014D000303B4", "stop" });
|
||||
entries.push_back({"03FC000303C3014D000303B4034501F2", "stop now" });
|
||||
entries.push_back({"03FC000303C3014D000303B4036C02B8", "stop there" });
|
||||
entries.push_back({"03FC000303A50042033F00A202FD01EF034800060390", "spin around" });
|
||||
entries.push_back({"02FD012903570318018F", "wrong way" });
|
||||
entries.push_back({"03BD0231033F00A202FD01EF034800060390", "turn around" });
|
||||
entries.push_back({"03720087000303CF00C9000303B4", "back up" });
|
||||
entries.push_back({"03C90024000303B40006039F01F80042035A", "keep going" });
|
||||
entries.push_back({"03FC000303C602F4018F00060387009C0408006600060390", "straight ahead" });
|
||||
entries.push_back({"037500C002EE0006037500AB03FC02D9", "bulb a saur" });
|
||||
entries.push_back({"03E70021033F00AB03FC02D9", "venus are" });
|
||||
entries.push_back({"03FC02EB0213000303B10213000303D8", "slowpoke" });
|
||||
entries.push_back({"03FC000303D5031B0234000303BD009F02EE", "squirt el" });
|
||||
entries.push_back({"03600252032A00840348000603870234", "charmander" });
|
||||
entries.push_back({"03C903060087000303BD0234000303A50024", "cater pie" });
|
||||
entries.push_back({"02EB014D000303B402F700AB03FC", "lapras" });
|
||||
entries.push_back({"02E20087000303B402F700AB03FC", "lapras" });
|
||||
entries.push_back({"03C302130006039900A8000303A50024", "togepi" });
|
||||
entries.push_back({"03C30213000603930045000303A500090024", "togepi" });
|
||||
entries.push_back({"03A500420339030F012C0006038700A8", "pinata" });
|
||||
entries.push_back({"03A500210339030F014D000303BD00A8", "pinata" });
|
||||
entries.push_back({"02DF004803F900420348000303BD00A503270024", "listen to me" });
|
||||
entries.push_back({"03F002FA00EA000303C6", "fruit" });
|
||||
entries.push_back({"03450213000303C6000303A8008700060390", "notepad" });
|
||||
entries.push_back({"042900420348000603A202F1002400060381000F00A50348000303C603FC", "ingredients" });
|
||||
entries.push_back({"02F4006903FC00A8000303A50024", "recipe" });
|
||||
entries.push_back({"02F4006903F90045000303A50024", "recipe" });
|
||||
entries.push_back({"0411014D000303C603FC03150024000303AB00C9000303BA018F0006038D0213", "hot sweet potato" });
|
||||
entries.push_back({"0411014D000303C603FC03150024000303B4000303BA018F0006038D0213", "hot sweet potato" });
|
||||
entries.push_back({"03D200E102EE", "cool" });
|
||||
entries.push_back({"03F001AD0348", "fine" });
|
||||
entries.push_back({"0309023703FC0213000303C9030C00EA000303C6", "youre so cute" });
|
||||
entries.push_back({"0393004803E70045000303C6000303C000E703270024", "give it to me" });
|
||||
entries.push_back({"0393004203270027036C0087000303C6", "gimme that" });
|
||||
entries.push_back({"03F602FD01F80045000303C6", "throw it" });
|
||||
entries.push_back({"03FC000303C000EA000303A5004500060390", "stupid" });
|
||||
entries.push_back({"02E20066000303C603FC000303B402E2018F", "lets play" });
|
||||
entries.push_back({"039C0108033302D603390042035A", "good morning" });
|
||||
entries.push_back({"0318018F000303CF00C9000303B4", "wake up" });
|
||||
entries.push_back({"03960066000303BD00C9000303B4", "get up" });
|
||||
entries.push_back({"04260024000303CF00A5034803DE", "e cans" });
|
||||
entries.push_back({"042C0066000303CF00A5034803DE", "e cans" });
|
||||
entries.push_back({"037E02F10042034B0045000303C604050297", "bring it here" });
|
||||
entries.push_back({"030C00EA000303CF00A503390024000303C6036C0087000303C6", "you can eat that" });
|
||||
entries.push_back({"03BA019203FC000303BD00A8000303C6", "taste it" });
|
||||
entries.push_back({"04260024000303B70045000303C6", "eat it" });
|
||||
entries.push_back({"032A0087000603A2033F00A8000303C6", "magnet" });
|
||||
entries.push_back({"03D501CE0348", "coin" });
|
||||
entries.push_back({"036600D5009F02EE", "jewel" });
|
||||
entries.push_back({"0333025500060375009F02EE", "marble" });
|
||||
entries.push_back({"03B101AA02F10045000303C603FC01", "pirate sword" });
|
||||
entries.push_back({"039C0108034501B0000303C6", "good night" });
|
||||
entries.push_back({"03D50315004803DE000303C301AD0336", "quiz time" });
|
||||
entries.push_back({"03B7002703E70024", "tv" });
|
||||
entries.push_back({"042F01B0000303D503150045000303C6", "i quit" });
|
||||
entries.push_back({"042F01AD03360006038700C60348", "im done" });
|
||||
entries.push_back({"030C00EA000303CF00A50348000303B402E2018F", "you can play" });
|
||||
entries.push_back({"03150024000303CF00A50348000303B402E2018F", "we can play" });
|
||||
entries.push_back({"0372008700060390", "bad" });
|
||||
entries.push_back({"0345014D000303C90045000303C3012F03F0", "knock it off" });
|
||||
entries.push_back({"03450213000303B402E201740042035A", "no playing" });
|
||||
entries.push_back({"03FC000303C3014D000303B4000303B402E201740042035A", "stop playing" });
|
||||
entries.push_back({"042F01AD03360345014D000303C6000303B402E201740042035A", "im not playing" });
|
||||
entries.push_back({"03D503150045000303C6", "quit" });
|
||||
entries.push_back({"03FC000303C3014D000303B4036C0087000303C6", "stop that" });
|
||||
entries.push_back({"03D802DF002103390045000303BD00C9000303B4", "clean it up" });
|
||||
entries.push_back({"03AE010B000303B70045000303BD00990318018F", "put it away" });
|
||||
entries.push_back({"03D802DF0021033F00C9000303B4", "clean up" });
|
||||
entries.push_back({"03D802DF00210348036C0087000303BD00C9000303B4", "clean that up" });
|
||||
entries.push_back({"03B70048040200EA", "tissue" });
|
||||
entries.push_back({"037500C002EE0006037500AB03FC02D9", "bulb a saur" });
|
||||
entries.push_back({"042F01B303E7002703FC02D9", "ivy saur" });
|
||||
entries.push_back({"042F01B303EA00AB03FC02D9", "ivy saur" });
|
||||
entries.push_back({"03E70021033F00AB03FC02D9", "venus are" });
|
||||
entries.push_back({"03600252032A00840348000603870234", "char mander" });
|
||||
entries.push_back({"036002520327001B02DF0009030900A50348", "char me leon" });
|
||||
entries.push_back({"0360025803DE025500060390", "chari z ard" });
|
||||
entries.push_back({"0360024000AB03DE025500060390", "chari z ard" });
|
||||
entries.push_back({"03FC000303D5031B0234000303BD009F02EE", "squirt el" });
|
||||
entries.push_back({"03210255000303BD0234000303BD009F02EE", "war tor tell" });
|
||||
entries.push_back({"03210255000303C302D9000303BD009F02EE", "war tor tell" });
|
||||
entries.push_back({"037E02E2008A03FC000303C301D403FC", "blast oise" });
|
||||
entries.push_back({"03CC0087000303BD0234000303A50024", "cater pie" });
|
||||
entries.push_back({"032A0066000303BD00A8000303B1014D00060390", "meta pod" });
|
||||
entries.push_back({"037500C9000303BD023703F002F10024", "butter free" });
|
||||
entries.push_back({"0315002400060387009F02EE", "weed el" });
|
||||
entries.push_back({"03D2010B000303D200E7033F00A8", "ka kuna" });
|
||||
entries.push_back({"03C90045000303D200E7033F00A8", "ka kuna" });
|
||||
entries.push_back({"036F00240006039002F1003C02EE", "bee drill" });
|
||||
entries.push_back({"036F00240006039002F7009F02EE", "bee drill" });
|
||||
entries.push_back({"03A5004500060366014D000303C6", "pid gee ot" });
|
||||
entries.push_back({"02F40087000303BD00A8000303BD00A8", "rat tata" });
|
||||
entries.push_back({"02F700A8000303BA00870006038700A8", "rat tata" });
|
||||
entries.push_back({"02F40087000303BA00870006038700A8", "rat tata" });
|
||||
entries.push_back({"02F40087000303B70045000303CC018F000303C6", "rat i cat" });
|
||||
entries.push_back({"03FC000303A502880213", "spear ow" });
|
||||
entries.push_back({"03ED02880213", "fear ow" });
|
||||
entries.push_back({"04260024000303CF00A5034803DE", "e cans" });
|
||||
entries.push_back({"042C0066000303CF00A5034803DE", "e cans" });
|
||||
entries.push_back({"043802550006037B014D000303D8", "ar bok" });
|
||||
entries.push_back({"03A50024000303CF00A80003036000EA", "pikachu" });
|
||||
entries.push_back({"02FD01B00003036000EA", "rye chu" });
|
||||
entries.push_back({"03FC00840348040202FA00EA", "sand shrew" });
|
||||
entries.push_back({"03FC0084034803FC02E2008A0402", "sand slash" });
|
||||
entries.push_back({"0339004500060387021F00A5034803ED0021032A018602EE", "ni doran female" });
|
||||
entries.push_back({"033900450006038D020D02F700A5034803ED0021032A017A009F02EE", "ni doran female" });
|
||||
entries.push_back({"033900450006038702190021033F00A8", "ni door ina" });
|
||||
entries.push_back({"033900240006038D0213000303D5031500210348", "nido queen" });
|
||||
entries.push_back({"033900450006038D020D02F700A50348032A018602EE", "ni doran male" });
|
||||
entries.push_back({"0339004500060387021F00A50348032A017A009F02EE", "ni doran male" });
|
||||
entries.push_back({"033900450006038D02BE002103450213", "ni dor ino" });
|
||||
entries.push_back({"033900240006038D020D02F1002103450213", "ni dor ino" });
|
||||
entries.push_back({"033900240006038D0213000303C90042035A", "ni do king" });
|
||||
entries.push_back({"03D802E2006903F0008102F10024", "cle fairy" });
|
||||
entries.push_back({"03D802E500AB03F0008102F10024", "cle fairy" });
|
||||
entries.push_back({"03D802E2006903F0018F00060375009F02EE", "cle fable" });
|
||||
entries.push_back({"03D802E500AB03F0018F00060375009F02EE", "cle fable" });
|
||||
entries.push_back({"03EA009F02EE000303A50045000303D803FC", "vol pix" });
|
||||
entries.push_back({"034501AD0348000303BA018602EE03DE", "nine tales" });
|
||||
entries.push_back({"03630045000603A202DF0024000303AB00CC03F0", "jiggly puff" });
|
||||
entries.push_back({"0363004500060399009F02DF0024000303AB00CC03F0", "jiggly puff" });
|
||||
entries.push_back({"03150045000603A202DF0024000303BD00CC03F0", "wiggly tuff" });
|
||||
entries.push_back({"03DE00EA000603720087000303C6", "zoo bat" });
|
||||
entries.push_back({"039F020A02EE000603720087000303C6", "goal bat" });
|
||||
entries.push_back({"0438012C0006038100480402", "odd ish" });
|
||||
entries.push_back({"03A202E800E70336", "gloom" });
|
||||
entries.push_back({"03EA01A702EE000303B402E800E70336", "vile plume" });
|
||||
entries.push_back({"03B1024000AB03FC", "paras" });
|
||||
entries.push_back({"03A802A300AB03FC", "paras" });
|
||||
entries.push_back({"03B1024000AB03FC0066000303D8000303C6", "para sect" });
|
||||
entries.push_back({"03A802A300AB03FC0066000303D8000303C6", "para sect" });
|
||||
entries.push_back({"03EA006303450210033C0087000303C6", "ven on at" });
|
||||
entries.push_back({"03EA0063034502100333012F03F6", "venom oth" });
|
||||
entries.push_back({"03810045000603A202E500A8000303C6", "dig let" });
|
||||
entries.push_back({"0327001501F503F6", "meow eth" });
|
||||
entries.push_back({"03AB023703E400A50348", "persian" });
|
||||
entries.push_back({"03FC01B00006038700C9000303D8", "psy duck" });
|
||||
entries.push_back({"039C010202EE0006038700C9000303D8", "goal duck" });
|
||||
entries.push_back({"032A0084035A000303C90024", "man key" });
|
||||
entries.push_back({"03B402FD01AD032A018F000303B4", "prime ape" });
|
||||
entries.push_back({"03A202FD020A02EB01B3036C", "growl ith" });
|
||||
entries.push_back({"03A202FD01E902DF0048036C", "growl ith" });
|
||||
entries.push_back({"04380255000303CC018C034501AD0348", "ar canine" });
|
||||
entries.push_back({"04380255000303CF00A5034501AD0348", "ar canine" });
|
||||
entries.push_back({"03B1020A02DF001503180087000603A2", "poly wag" });
|
||||
entries.push_back({"03B1020A02DF0015031B022B02EE", "poly whirl" });
|
||||
entries.push_back({"03B1020A02DF001E02F4008A03F6", "poly wrath" });
|
||||
entries.push_back({"03B1014402DF001E02F4008A03F6", "poly wrath" });
|
||||
entries.push_back({"042F00870006037E02F700A8", "abra" });
|
||||
entries.push_back({"03CF00A80006038400870006037E02F700A8", "ka dab ra" });
|
||||
entries.push_back({"042F007E02E500A8000303CF00AB03DE00840336", "ala ka zam" });
|
||||
entries.push_back({"032D00A800030360014D000303B4", "ma chop" });
|
||||
entries.push_back({"032D00A8000303600213000303D8", "ma choke" });
|
||||
entries.push_back({"032D00A80003036000840336000303B4", "ma champ" });
|
||||
entries.push_back({"0372005D02EE03FC000303B402FD01F2000303C6", "bell sprout" });
|
||||
entries.push_back({"03BA0063033F00A8000303CF00C002EE", "ten ta cool" });
|
||||
entries.push_back({"03BA0063033F00A8000303D802FA00E102EE", "ten ta cruel" });
|
||||
entries.push_back({"03630009030F02130006038A00EA00060390", "geo dude" });
|
||||
entries.push_back({"03A202F4008A03EA009F02E50234", "gravel er" });
|
||||
entries.push_back({"03A202F4008A03EA02E50234", "gravel er" });
|
||||
entries.push_back({"039F020A02E500A50336", "golem" });
|
||||
entries.push_back({"039F014402EE0336", "golem" });
|
||||
entries.push_back({"03B1021003390024000303BD00A8", "pony ta" });
|
||||
entries.push_back({"03B1021003390024000303C3016E", "pony ta" });
|
||||
entries.push_back({"02F40087000303A5004500060384008A0402", "rap e dash" });
|
||||
entries.push_back({"03FC02EB0213000303B10213000303D8", "slowpoke" });
|
||||
entries.push_back({"03FC02EB02130006037E02FD0213", "slow bro" });
|
||||
entries.push_back({"032A0087000603A2033F00A5033301B0000303C6", "mag ne mite" });
|
||||
entries.push_back({"032A0087000603A2033F00A8000303C3014A0348", "mag ne ton" });
|
||||
entries.push_back({"03F0025803F0006600030360000303C6", "far fetched" });
|
||||
entries.push_back({"038D02130006039002F100150213", "dod rio" });
|
||||
entries.push_back({"03F9001B02EE", "seel" });
|
||||
entries.push_back({"038A00EA0006039F014A035A", "dew gong" });
|
||||
entries.push_back({"038A00EA0006039F014A0348", "dew gong" });
|
||||
entries.push_back({"03A202FD01AD032D0234", "grim er" });
|
||||
entries.push_back({"032D00C9000303D8", "muck" });
|
||||
entries.push_back({"0402005D02EE000603870234", "shell der" });
|
||||
entries.push_back({"03D802EB01D403FC000303BD0234", "cloy ster" });
|
||||
entries.push_back({"0396008A03FC000303C602DF00", "ghastly" });
|
||||
entries.push_back({"0396008A03FC02DF0024", "ghastly" });
|
||||
entries.push_back({"041101290348000303BD0234", "haunt er" });
|
||||
entries.push_back({"03960084035A0006039F0255", "gen gar" });
|
||||
entries.push_back({"0396006303480006039F0255", "gen gar" });
|
||||
entries.push_back({"0438014A03390045000303D803FC", "on ix" });
|
||||
entries.push_back({"039002FD01F503DB0024", "drowsy" });
|
||||
entries.push_back({"039002FD021603DB0024", "drowsy" });
|
||||
entries.push_back({"04050045000303B403450213", "hip no" });
|
||||
entries.push_back({"03D802F400870006036F00", "crabby" });
|
||||
entries.push_back({"03C90042035A02E50234", "king ler" });
|
||||
entries.push_back({"03EA020A02EE000303C302D90006037E", "volt orb" });
|
||||
entries.push_back({"0426001B02E20066000303D8000303C602FD021300060390", "electrode" });
|
||||
entries.push_back({"0432009F02E20066000303D8000303C602FD021300060390", "electrode" });
|
||||
entries.push_back({"042C0066000603A203DE000303C9030C00EA000303C6", "execute" });
|
||||
entries.push_back({"042C0066000603A203DE0066000303C9030C00EA000303C302D9", "executor" });
|
||||
entries.push_back({"042C0066000603A203FC00A8000303C9030C00EA000603870234", "executor" });
|
||||
entries.push_back({"03C9030C00EA0006037B02100348", "cue bone" });
|
||||
entries.push_back({"032A02A9020403180087000303D8", "mar oh wack" });
|
||||
entries.push_back({"03330246020403180087000303D8", "mar oh wack" });
|
||||
entries.push_back({"04050045000303C603330210034802DF0024", "hit mon lee" });
|
||||
entries.push_back({"04050045000303C60333014A034802DF0024", "hit mon lee" });
|
||||
entries.push_back({"04050045000303C60333021003480003036000840348", "hit mon chan" });
|
||||
entries.push_back({"02DF0045000303C90024000303BD00A5035A", "lick eh tung" });
|
||||
entries.push_back({"02DF0045000303C90045000303BD00A5035A", "lick i tung" });
|
||||
entries.push_back({"02DF0045000303C90045000303C00108035A", "lick a tung" });
|
||||
entries.push_back({"03D5015003ED0042035A", "coughing" });
|
||||
entries.push_back({"0315002703DB0042035A", "wheezing" });
|
||||
entries.push_back({"02FD01A4041102D60348", "rye horn" });
|
||||
entries.push_back({"02FD01B00006038D01290348", "rye don" });
|
||||
entries.push_back({"03600084034803F90024", "chance e" });
|
||||
entries.push_back({"03BA0084034800060366005D02E500A8", "tangela" });
|
||||
entries.push_back({"03BA018C034800060396005D02E500A8", "tangela" });
|
||||
entries.push_back({"03CC018C035A0006039900AB03FC000303D5014A0348", "kang as khan" });
|
||||
entries.push_back({"041102DC03F90024", "horse sea" });
|
||||
entries.push_back({"03F900240006039002F700A8", "sea dra" });
|
||||
entries.push_back({"039F020A02EE0006038100210348", "gold een" });
|
||||
entries.push_back({"03F90024000303C90042035A", "seeking" });
|
||||
entries.push_back({"03FC000303C3023A030C00EA", "starry you" });
|
||||
entries.push_back({"03FC000303C3025203270024", "star me" });
|
||||
entries.push_back({"0327004803FC000303BD0231033301AD0336", "mister mime" });
|
||||
entries.push_back({"03FC01B3036C0234", "sigh ther" });
|
||||
entries.push_back({"03630042035A000303D803FC", "jynx" });
|
||||
entries.push_back({"0426001B02E20066000303D8000303BD00A80006037500CC03DE", "elect tra buzz" });
|
||||
entries.push_back({"0432009F02E20066000303D8000303BD00A80006037500CC03DE", "elect tra buzz" });
|
||||
entries.push_back({"032A0087000603A203330255", "mag mar" });
|
||||
entries.push_back({"03A50042034803FC0234", "pin sir" });
|
||||
entries.push_back({"03C3020D02FD012F03FC", "tar os" });
|
||||
entries.push_back({"03C30246021603FC", "tar os" });
|
||||
entries.push_back({"032A0087000603630045000303D50255000303B4", "magi karp" });
|
||||
entries.push_back({"039602A300A80006038D015003FC", "guy a rad os" });
|
||||
entries.push_back({"039602A300A80006038D021603FC", "guy a rad os" });
|
||||
entries.push_back({"02EB014D000303B402F700AB03FC", "lap ras" });
|
||||
entries.push_back({"02E20087000303B402F700AB03FC", "lap ras" });
|
||||
entries.push_back({"03810045000303C30213", "ditto" });
|
||||
entries.push_back({"0426002703E70024", "e v" });
|
||||
entries.push_back({"03EA018F000303B102BE0015014A0348", "vapor eon" });
|
||||
entries.push_back({"03EA00A8000303B102BE000F00A50348", "vapor eon" });
|
||||
entries.push_back({"0366020A02EE000303B70015014A0348", "jolt eon" });
|
||||
entries.push_back({"03F002E2029D0015014A0348", "flare eon" });
|
||||
entries.push_back({"03B1020D02F100240006039F014A0348", "por e gon" });
|
||||
entries.push_back({"03B1020D02F100240006039900A50348", "por e gon" });
|
||||
entries.push_back({"043B0210032D00C6034501B0000303C6", "oman ite" });
|
||||
entries.push_back({"04380129032D00A5034501B0000303C6", "oman ite" });
|
||||
entries.push_back({"043B0210032D00CC03FC000303C30255", "oma star" });
|
||||
entries.push_back({"04380129032D00AB03FC000303C30255", "oma star" });
|
||||
entries.push_back({"03CF00C90006037800EA0006038D0213", "ka boo tow" });
|
||||
entries.push_back({"03D5012C0006037800EA000303C3014D000303B403FC", "ka bu tops" });
|
||||
entries.push_back({"03D5012C0006037500A8000303C3014D000303B403FC", "ka bu tops" });
|
||||
entries.push_back({"03D5012C0006037500A8000303C3015003FC", "ka bu tops" });
|
||||
entries.push_back({"042C02A90213000603840087000303D8000303BD00C002EE", "aero dac tell" });
|
||||
entries.push_back({"03FC034502D002E20087000303D803FC", "snore lax" });
|
||||
entries.push_back({"04380255000303B70045000303D200E703450213", "art eh cuno" });
|
||||
entries.push_back({"03DE0087000303B40006038D015003FC", "zap dos" });
|
||||
entries.push_back({"03DE0087000303B40006038D021603FC", "zap dos" });
|
||||
entries.push_back({"0333020A02EE000303C602F4006903FC", "mol tres" });
|
||||
entries.push_back({"039002F700A8000303B7002103390024", "duh rat ini" });
|
||||
entries.push_back({"039002F400870006039900A5033C02B8", "dragonair" });
|
||||
entries.push_back({"039002F400870006039900A5034501B0000303C6", "dragon ite" });
|
||||
entries.push_back({"0327030C00EA000303C000EA", "mew two" });
|
||||
entries.push_back({"0327030C00EA", "mew" });
|
||||
entries.push_back({"03C3014D000303AB00AB03EA000603630045000603A202DF0024000303AB00AB03F0", "top of jiggly puff" });
|
||||
entries.push_back({"03C302130006039900A8000303A50024", "toge pi" });
|
||||
entries.push_back({"03C302130006039C010B000303A50024", "toge pi" });
|
||||
entries.push_back({"03C30213000603930045000303A500090024", "toge pi" });
|
||||
entries.push_back({"03B1014402DF001503180087000603A2", "poly wag" });
|
||||
entries.push_back({"03150024000303A50042034800060372005D02EE", "weeping bell" });
|
||||
entries.push_back({"03E70045000303D8000303C602F1002400060372005D02EE", "vic tree bell" });
|
||||
entries.push_back({"03BA0063033F00A8000303D200E102EE", "ten ta cool" });
|
||||
entries.push_back({"038700C9000603A2000303C602F100150213", "dug trio" });
|
||||
entries.push_back({"03A50045000603630024", "pid gee" });
|
||||
entries.push_back({"038D02130006038A00DB0213", "do duo" });
|
||||
entries.push_back({"03A50045000603660213000303C30213", "pid gee otto" });
|
||||
entries.push_back({"03CF00C9000303D200E7033F00A8", "ka kuna" });
|
||||
entries.push_back({"03BA00630348000303BD00A8000303D200E102EE", "ten ta cool" });
|
||||
entries.push_back({"03CC018C035A00060396008A03FC000303D5014A0348", "kang as khan" });
|
||||
entries.push_back({"043B0210032D00AB03FC000303C30255", "oma star" });
|
||||
entries.push_back({"03CF00A80006037800EA000303C3014D000303B403FC", "ka boo tops" });
|
||||
entries.push_back({"036C0087000303C603FC02FD01B0000303C6", "thats right" });
|
||||
entries.push_back({"036C0087000303C603FC0006039C010B00060390", "thats good" });
|
||||
entries.push_back({"036C0087000303C3031B00C60348", "that one" });
|
||||
entries.push_back({"036C008A03FC03F001AD0348", "thats fine" });
|
||||
entries.push_back({"036C0087000303C603DE03F001AD0348", "thats fine" });
|
||||
entries.push_back({"03CF021C0066000303D8000303C6", "correct" });
|
||||
entries.push_back({"03CF00A202F40066000303D8000303C6", "correct" });
|
||||
entries.push_back({"036F0042035A0006039F0213", "bingo" });
|
||||
entries.push_back({"03D5012302DF0045000303C6", "call it" });
|
||||
entries.push_back({"02E2006903FC000303B402E2018F", "lets play" });
|
||||
entries.push_back({"02FD0129035A", "wrong" });
|
||||
entries.push_back({"0372008700060390000303A50024000303CF00A80003036000EA", "bad pikachu" });
|
||||
entries.push_back({"0327004803FC", "miss" });
|
||||
entries.push_back({"038D02100348000303C6000303D5012302DF0045000303C6", "dont call it" });
|
||||
entries.push_back({"041101F503FC", "house" });
|
||||
entries.push_back({"041102100336", "home" });
|
||||
entries.push_back({"03EA00A202F1004500060381000F00C60348", "viridian" });
|
||||
entries.push_back({"03EA00A202F1004500060381000F00C6034803F002C400AB03FC000303C6", "viridian forest" });
|
||||
entries.push_back({"043B0213000303CF0234", "ochre" });
|
||||
entries.push_back({"043B0213000303CF0225031E010B0006039003DE", "ochre woods" });
|
||||
entries.push_back({"043B0213000303CF023703ED004803FF0042035A0411020A02EE", "ochre fishing hole" });
|
||||
entries.push_back({"03FC000303B402F10042035A02DF002703F0", "spring leaf" });
|
||||
entries.push_back({"03FC000303B402F10042035A02DF002703F003ED000F00C002EE00060390", "spring leaf field" });
|
||||
entries.push_back({"0438014402E500AB03EA01AD0348", "olivine" });
|
||||
entries.push_back({"0438014402E500AB03EA01AD034802E2018F000303D8", "olivine lake" });
|
||||
entries.push_back({"0438014402E500AB03EA01AD034803ED004803FF0042035A0411020A02EE", "olivine fishing hole" });
|
||||
entries.push_back({"03D502130006037B012302EE000303C6", "cobalt" });
|
||||
entries.push_back({"03D502130006037B012302EE000303C301A702E500C6034800060390", "cobalt island" });
|
||||
entries.push_back({"03D502130006037E03ED004803FF0042035A0411020A02EE", "cobalt fishing hole" });
|
||||
entries.push_back({"03D5021603FC000303C6", "coast" });
|
||||
entries.push_back({"03D502130006037B012302EE000303C6000303D5021603FC000303C6", "cobalt coast" });
|
||||
entries.push_back({"03FC000303A80069040200C002EE000303C602F4018C03390042035A", "special training" });
|
||||
entries.push_back({"03BD0231033F00A8000303B4", "turnip" });
|
||||
entries.push_back({"03BD02310348000303B4", "turnip" });
|
||||
entries.push_back({"03FC03150024000303C6000303AB00A8000303BA018F0006038D0213", "sweet potato" });
|
||||
entries.push_back({"0411014D000303C603FC03150024000303C6000303AE010B000303BA018F0006038D0213", "hot sweet potato" });
|
||||
entries.push_back({"03D501470300000603900006037B02D9000603900006037B014D000303D803FC", "cardboard box" });
|
||||
entries.push_back({"04080084034B00420348036C02B8", "hang in there" });
|
||||
entries.push_back({"03AE010202EE", "pull" });
|
||||
entries.push_back({"02F1001B02DF0045000303B700420348", "reel it in" });
|
||||
entries.push_back({"034501F2", "now" });
|
||||
entries.push_back({"03CC00870003035D0045000303C6", "catch it" });
|
||||
entries.push_back({"03960066000303B70045000303C6", "get it" });
|
||||
entries.push_back({"03AE010202DF0045000303C6", "pull it" });
|
||||
entries.push_back({"03AE010202EE04110255000603870234", "pull harder" });
|
||||
entries.push_back({"02E20066000303B70045000303C60006039F0213", "let it go" });
|
||||
entries.push_back({"02F1003C02DF002703F90045000303C6", "release it" });
|
||||
entries.push_back({"02E500C6034800030360", "lunch" });
|
||||
entries.push_back({"03FC033C0087000303D8", "snack" });
|
||||
entries.push_back({"02E2006903F90024000303C6", "lets eat" });
|
||||
entries.push_back({"03CF00C9000303B4000303CC018F000303D8", "cupcake" });
|
||||
entries.push_back({"042F019B00C6034800060387023703FC000303BA0084034800060390", "i understand" });
|
||||
entries.push_back({"042F01B0000603930045000303B70045000303C6", "i get it" });
|
||||
entries.push_back({"0411032101B0", "why" });
|
||||
entries.push_back({"041101F2000303CF00C60336", "how come" });
|
||||
entries.push_back({"039C010B0006037B01B0", "good bye" });
|
||||
entries.push_back({"03BA018F000303D8000303CC02B8", "take care" });
|
||||
entries.push_back({"042F019B009F02EE0327004803F9030C00EA", "ill miss you" });
|
||||
entries.push_back({"0438016502EE0327004803F9030C00EA", "ill miss you" });
|
||||
entries.push_back({"03FC000303C3014D000303B4036C0087000303C6", "stop that" });
|
||||
entries.push_back({"03D503150045000303C6", "quit" });
|
||||
entries.push_back({"03CF00C9000303B70045000303C301F2000303C6", "cut it out" });
|
||||
entries.push_back({"03CF00A503330129034802E20066000303C603DE0006039F0213", "come on lets go" });
|
||||
entries.push_back({"02E20066000303C603DE0006039F0213", "lets go" });
|
||||
entries.push_back({"031B0225012F03F0", "we re off" });
|
||||
entries.push_back({"03BA008700060393030902190045000303C6", "tag youre it" });
|
||||
entries.push_back({"03CF00C9000303B70045000303C301F2000303C6", "cut it out" });
|
||||
entries.push_back({"0411031B00A8000303C603FC02FD0129035A", "whats wrong" });
|
||||
entries.push_back({"0411031B00A8000303B7004803F90045000303C6", "what is it" });
|
||||
entries.push_back({"03CF00C603330129034802E20066000303C603DE0006039F0213", "come on lets go" });
|
||||
entries.push_back({"031502970006039F01F80042035A034501F2", "were going now" });
|
||||
entries.push_back({"02E20066000303C603FC000303600066000303C90045000303C301F2000303C6", "lets check it out" });
|
||||
entries.push_back({"03F600C6033F02370402014D000303D8", "thunder shock" });
|
||||
entries.push_back({"03F600C6033F02340006037B020A02EE000303C6", "thunderbolt" });
|
||||
entries.push_back({"043801290339030300420348", "onion" });
|
||||
entries.push_back({"03FC02DF0024000303B4000303C301B0000303C6", "sleep tight" });
|
||||
entries.push_back({"03F90009030C00EA000303C000E7033302460213", "see you tomorrow" });
|
||||
entries.push_back({"03F90009030C00CF00420348036C00C6033302D603390042035A", "see you in the morning" });
|
||||
entries.push_back({"039F0213000303BD00C6033302460213", "go tomorrow" });
|
||||
entries.push_back({"02FD014D000303D8", "rock" });
|
||||
entries.push_back({"03F9004803DE023703FC", "scissors" });
|
||||
entries.push_back({"03A8018F000303AB0234", "paper" });
|
||||
entries.push_back({"0381002A000303B10255000303C6", "depart" });
|
||||
entries.push_back({"02F10006038100EA03DE00030384002A000303B10255000303C6", "reduced depart" });
|
||||
entries.push_back({"03D5012F035D0063034800030381002A000303B10255000303C6", "caution depart" });
|
||||
entries.push_back({"02DF03FC03BD023A000603D8000303B700060381002A000303B10255000303C6", "restricted depart" });
|
||||
entries.push_back({"03B1008A03FC0042035A", "passing" });
|
||||
entries.push_back({"03FC000303C3014D000303AB0042035A", "stopping" });
|
||||
entries.push_back({"031E02DC03390042035A", "warning" });
|
||||
entries.push_back({"03AB02FA021603F9000900060390", "proceed" });
|
||||
entries.push_back({"02F10006038100EA03DE00060390", "reduced" });
|
||||
entries.push_back({"03D5012F035D00630348", "caution" });
|
||||
entries.push_back({"02DF03FC03BD023A000603D8000303B700060390", "restricted" });
|
||||
entries.push_back({"042C0066000603A203FC000303AB021C005A03DE", "express" });
|
||||
entries.push_back({"02E50042032A002A000303C6", "limit" });
|
||||
entries.push_back({"03450213000602E50042032A002A000303C6", "no limit" });
|
||||
entries.push_back({"03B7005A0348", "ten" });
|
||||
entries.push_back({"03E7002A03F6000303B700090348", "fifteen" });
|
||||
entries.push_back({"03C30318004B0339038A0024", "twenty" });
|
||||
entries.push_back({"03C30318004B0339038A002403EA019E03EA", "twenty five" });
|
||||
entries.push_back({"036C02AC03BA0027", "thirty" });
|
||||
entries.push_back({"036C02AC03BA002703EA019E03EA", "thirty five" });
|
||||
entries.push_back({"03EA02D6000603900048", "forty" });
|
||||
entries.push_back({"03EA02D600060390004803EA019E03EA", "forty five" });
|
||||
entries.push_back({"03F3005A03F0000303B70042", "fifty" });
|
||||
entries.push_back({"03F3005A03F0000303B7004203EA019E03EA", "fifty five" });
|
||||
entries.push_back({"03F9002A000303D803FC000303C00009", "sixty" });
|
||||
entries.push_back({"03F9002A000303D803FC000303C0000903EA019E03EA", "sixty five" });
|
||||
entries.push_back({"03FC004B036C00630348000303B70009", "seventy" });
|
||||
entries.push_back({"03FC004B036C00630348000303B7000903EA019E03EA", "seventy five" });
|
||||
entries.push_back({"042C018C03B70009", "eighty" });
|
||||
entries.push_back({"042C018C03B7000903EA019E03EA", "eighty five" });
|
||||
entries.push_back({"034501A103480006038A0048", "ninety" });
|
||||
entries.push_back({"034501A103480006038A004803EA019E03EA", "ninety five" });
|
||||
entries.push_back({"031B00C60348040B00BD0348038702F100060390", "one hundred" });
|
||||
entries.push_back({"031B00C60348021303F001AD03E7", "one oh five" });
|
||||
entries.push_back({"031B00C60348000303B7005A0348", "one ten" });
|
||||
entries.push_back({"031B00C60348000303E7002A03F6000303B700090348", "one fifteen" });
|
||||
entries.push_back({"031B00C60348000303C303180348000303B70024", "one twenty" });
|
||||
entries.push_back({"031B00C60348000303C303180348000303B7002403EA019E03EA", "one twenty five" });
|
||||
entries.push_back({"82B582E382C182CF82C282B582F182B182A4", "depart" });
|
||||
entries.push_back({"82B582E382C182CF82C282B082F182BB82AD", "reduced depart" });
|
||||
entries.push_back({"82B582E382C182CF82C282BF82E382A482A2", "caution depart" });
|
||||
entries.push_back({"82B582E382C182CF82C282AF82A282A982A2", "restricted depart" });
|
||||
entries.push_back({"82C282A482A9", "passing" });
|
||||
entries.push_back({"82C482A282B582E1", "stopping" });
|
||||
entries.push_back({"82AF82A282D982A482A082A9", "warning" });
|
||||
entries.push_back({"82B582F182B182A4", "proceed" });
|
||||
entries.push_back({"82B082F182BB82AD", "reduced" });
|
||||
entries.push_back({"82BF82E382A482A2", "caution" });
|
||||
entries.push_back({"82AF82A282A982A2", "restricted" });
|
||||
entries.push_back({"82C482A282B5", "stop" });
|
||||
entries.push_back({"82B182A482BB82AD", "no limit" });
|
||||
entries.push_back({"82B982A282B082F1", "limit" });
|
||||
entries.push_back({"82A982A282B682E5", "cancel" });
|
||||
entries.push_back({"82B682E382A4", "ten" });
|
||||
entries.push_back({"82B682E382A482B2", "fifteen" });
|
||||
entries.push_back({"82C982B682E382A4", "twenty" });
|
||||
entries.push_back({"82C982B682E382A482B2", "twenty five" });
|
||||
entries.push_back({"82B382F182B682E382A4", "thirty" });
|
||||
entries.push_back({"82B382F182B682E382A482B2", "thirty five" });
|
||||
entries.push_back({"82E682F182B682E382A4", "forty" });
|
||||
entries.push_back({"82E682F182B682E382A482B2", "forty five" });
|
||||
entries.push_back({"82B282B682E382A4", "fifty" });
|
||||
entries.push_back({"82B282B682E382A482B2", "fifty five" });
|
||||
entries.push_back({"82EB82AD82B682E382A4", "sixty" });
|
||||
entries.push_back({"82EB82AD82B682E382A482B2", "sixty five" });
|
||||
entries.push_back({"82C882C882B682E382A4", "seventy" });
|
||||
entries.push_back({"82C882C882B682E382A482B2", "seventy five" });
|
||||
entries.push_back({"82CD82BF82B682E382A4", "eighty" });
|
||||
entries.push_back({"82CD82BF82B682E382A482B2", "eighty five" });
|
||||
entries.push_back({"82AB82E382A482B682E382A4", "ninety" });
|
||||
entries.push_back({"82AB82E382A482B682E382A482B2", "ninety five" });
|
||||
entries.push_back({"82D082E182AD", "one hundred" });
|
||||
entries.push_back({"82D082E182AD82B2", "one oh five" });
|
||||
entries.push_back({"82D082E182AD82B682E382A4", "one ten" });
|
||||
entries.push_back({"82D082E182AD82B682E382A482B2", "one fifteen" });
|
||||
entries.push_back({"82D082E182AD82C982B682E382A4", "one twenty" });
|
||||
entries.push_back({"82D082E182AD82C982B682E382A482B2", "one twenty five" });
|
||||
|
||||
return entries;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
|
||||
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 3.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef VRU_WORDS_HPP
|
||||
#define VRU_WORDS_HPP
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct VRUWordEntry
|
||||
{
|
||||
std::string hex;
|
||||
std::string words;
|
||||
};
|
||||
|
||||
std::vector<VRUWordEntry> GetVRUWordEntries(void);
|
||||
|
||||
#endif // VRU_WORDS_HPP
|
||||
@@ -31,6 +31,14 @@ enum class N64ControllerButton
|
||||
Invalid
|
||||
};
|
||||
|
||||
enum class InputDeviceType
|
||||
{
|
||||
EmulateVRU = -4,
|
||||
None = -3,
|
||||
Automatic = -2,
|
||||
Keyboard = -1,
|
||||
};
|
||||
|
||||
enum class InputType
|
||||
{
|
||||
Keyboard = -1,
|
||||
|
||||
@@ -13,15 +13,20 @@
|
||||
|
||||
#include <UserInterface/MainDialog.hpp>
|
||||
#include "Thread/SDLThread.hpp"
|
||||
#include "Thread/HotkeysThread.hpp"
|
||||
#include "Utilities/InputDevice.hpp"
|
||||
#include "common.hpp"
|
||||
#ifdef VRU
|
||||
#include "VRU.hpp"
|
||||
#endif // VRU
|
||||
|
||||
#include <RMG-Core/Core.hpp>
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QApplication>
|
||||
#include <SDL.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
|
||||
//
|
||||
@@ -49,15 +54,18 @@
|
||||
|
||||
struct InputMapping
|
||||
{
|
||||
InputType Type = InputType::Invalid;
|
||||
int Data = 0;
|
||||
int ExtraData = 0;
|
||||
std::vector<std::string> Name;
|
||||
std::vector<int> Type;
|
||||
std::vector<int> Data;
|
||||
std::vector<int> ExtraData;
|
||||
int Count = 0;
|
||||
};
|
||||
|
||||
struct InputProfile
|
||||
{
|
||||
bool PluggedIn = false;
|
||||
int DeadzoneValue = 0;
|
||||
int SensitivityValue = 100;
|
||||
|
||||
N64ControllerPak ControllerPak = N64ControllerPak::None;
|
||||
|
||||
@@ -94,6 +102,54 @@ struct InputProfile
|
||||
InputMapping AnalogStick_Down;
|
||||
InputMapping AnalogStick_Left;
|
||||
InputMapping AnalogStick_Right;
|
||||
|
||||
// hotkeys
|
||||
bool Hotkey_Shutdown_Pressed = false;
|
||||
InputMapping Hotkey_Shutdown;
|
||||
bool Hotkey_Exit_Pressed = false;
|
||||
InputMapping Hotkey_Exit;
|
||||
bool Hotkey_SoftReset_Pressed = false;
|
||||
InputMapping Hotkey_SoftReset;
|
||||
bool Hotkey_HardReset_Pressed = false;
|
||||
InputMapping Hotkey_HardReset;
|
||||
bool Hotkey_Resume_Pressed = false;
|
||||
InputMapping Hotkey_Resume;
|
||||
bool Hotkey_Screenshot_Pressed = false;
|
||||
InputMapping Hotkey_Screenshot;
|
||||
bool Hotkey_LimitFPS_Pressed = false;
|
||||
InputMapping Hotkey_LimitFPS;
|
||||
bool Hotkey_SpeedFactor_Pressed = false;
|
||||
InputMapping Hotkey_SpeedFactor25;
|
||||
InputMapping Hotkey_SpeedFactor50;
|
||||
InputMapping Hotkey_SpeedFactor75;
|
||||
InputMapping Hotkey_SpeedFactor100;
|
||||
InputMapping Hotkey_SpeedFactor125;
|
||||
InputMapping Hotkey_SpeedFactor150;
|
||||
InputMapping Hotkey_SpeedFactor175;
|
||||
InputMapping Hotkey_SpeedFactor200;
|
||||
InputMapping Hotkey_SpeedFactor225;
|
||||
InputMapping Hotkey_SpeedFactor250;
|
||||
InputMapping Hotkey_SpeedFactor275;
|
||||
InputMapping Hotkey_SpeedFactor300;
|
||||
bool Hotkey_SaveState_Pressed = false;
|
||||
InputMapping Hotkey_SaveState;
|
||||
bool Hotkey_LoadState_Pressed = false;
|
||||
InputMapping Hotkey_LoadState;
|
||||
bool Hotkey_GSButton_Pressed = false;
|
||||
InputMapping Hotkey_GSButton;
|
||||
bool Hotkey_SaveStateSlot_Pressed = false;
|
||||
InputMapping Hotkey_SaveStateSlot0;
|
||||
InputMapping Hotkey_SaveStateSlot1;
|
||||
InputMapping Hotkey_SaveStateSlot2;
|
||||
InputMapping Hotkey_SaveStateSlot3;
|
||||
InputMapping Hotkey_SaveStateSlot4;
|
||||
InputMapping Hotkey_SaveStateSlot5;
|
||||
InputMapping Hotkey_SaveStateSlot6;
|
||||
InputMapping Hotkey_SaveStateSlot7;
|
||||
InputMapping Hotkey_SaveStateSlot8;
|
||||
InputMapping Hotkey_SaveStateSlot9;
|
||||
bool Hotkey_Fullscreen_Pressed = false;
|
||||
InputMapping Hotkey_Fullscreen;
|
||||
};
|
||||
|
||||
//
|
||||
@@ -103,6 +159,9 @@ struct InputProfile
|
||||
// SDL thread
|
||||
static Thread::SDLThread *l_SDLThread = nullptr;
|
||||
|
||||
// Hotkeys thread (for when paused)
|
||||
static Thread::HotkeysThread *l_HotkeysThread = nullptr;
|
||||
|
||||
// input profiles
|
||||
static InputProfile l_InputProfiles[NUM_CONTROLLERS];
|
||||
|
||||
@@ -120,11 +179,27 @@ static bool l_KeyboardState[SDL_NUM_SCANCODES];
|
||||
//
|
||||
|
||||
static void load_inputmapping_settings(InputMapping* mapping, std::string section,
|
||||
SettingsID inputTypeSettingsId, SettingsID dataSettingsId, SettingsID extraDataSettingsId)
|
||||
SettingsID inputNameSettingsId, SettingsID inputTypeSettingsId,
|
||||
SettingsID dataSettingsId, SettingsID extraDataSettingsId)
|
||||
{
|
||||
mapping->Type = (InputType)CoreSettingsGetIntValue(inputTypeSettingsId, section);
|
||||
mapping->Data = CoreSettingsGetIntValue(dataSettingsId, section);
|
||||
mapping->ExtraData = CoreSettingsGetIntValue(extraDataSettingsId, section);
|
||||
mapping->Name = CoreSettingsGetStringListValue(inputNameSettingsId, section);
|
||||
mapping->Type = CoreSettingsGetIntListValue(inputTypeSettingsId, section);
|
||||
mapping->Data = CoreSettingsGetIntListValue(dataSettingsId, section);
|
||||
mapping->ExtraData = CoreSettingsGetIntListValue(extraDataSettingsId, section);
|
||||
mapping->Count = std::min(mapping->Type.size(), std::min(mapping->Data.size(), mapping->ExtraData.size()));
|
||||
|
||||
// check if mapping is old profile type,
|
||||
// it should be true when the minimum of the lengths
|
||||
// of all int lists is 0, and the name isn't empty or whitespace
|
||||
if (mapping->Count == 0 &&
|
||||
!mapping->Name.empty() && !mapping->Name.at(0).empty() &&
|
||||
mapping->Name.at(0).find_first_not_of(' ') != std::string::npos)
|
||||
{
|
||||
mapping->Type.push_back(CoreSettingsGetIntValue(inputTypeSettingsId, section));
|
||||
mapping->Data.push_back(CoreSettingsGetIntValue(dataSettingsId, section));
|
||||
mapping->ExtraData.push_back(CoreSettingsGetIntValue(extraDataSettingsId, section));
|
||||
mapping->Count = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void load_settings(void)
|
||||
@@ -132,26 +207,60 @@ static void load_settings(void)
|
||||
std::string gameId;
|
||||
CoreRomSettings romSettings;
|
||||
|
||||
std::string userProfileSectionBase = "Rosalie's Mupen GUI - Input Plugin User Profile";
|
||||
std::vector<std::string> userProfiles;
|
||||
std::vector<std::string>::iterator userProfilesIter;
|
||||
|
||||
// try to retrieve current ROM settings
|
||||
if (CoreGetCurrentRomSettings(romSettings))
|
||||
{
|
||||
gameId = romSettings.MD5;
|
||||
}
|
||||
|
||||
// retrieve all user profiles
|
||||
userProfiles = CoreSettingsGetStringListValue(SettingsID::Input_Profiles);
|
||||
|
||||
for (int i = 0; i < NUM_CONTROLLERS; i++)
|
||||
{
|
||||
InputProfile* profile = &l_InputProfiles[i];
|
||||
std::string section = "Rosalie's Mupen GUI - Input Plugin Profile " + std::to_string(i);
|
||||
std::string section = "Rosalie's Mupen GUI - Input Plugin Profile " + std::to_string(i);
|
||||
std::string gameSection = section + " Game " + gameId;
|
||||
std::string userProfileSection;
|
||||
std::string userProfileName;
|
||||
|
||||
// when a user profile has been specified,
|
||||
// ensure it exists in the profile list and
|
||||
// in the settings, if it does,
|
||||
// use that user profile
|
||||
userProfileName = CoreSettingsGetStringValue(SettingsID::Input_UseProfile, section);
|
||||
if (!userProfileName.empty())
|
||||
{
|
||||
userProfilesIter = std::find(userProfiles.begin(), userProfiles.end(), userProfileName);
|
||||
if (userProfilesIter != userProfiles.end())
|
||||
{
|
||||
userProfileSection = userProfileSectionBase;
|
||||
userProfileSection += " \"" + userProfileName + "\"";
|
||||
if (CoreSettingsSectionExists(userProfileSection))
|
||||
{
|
||||
section = userProfileSection;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if game ID was retrieved,
|
||||
// check if game section exists, if it does
|
||||
// use that section instead of the main section
|
||||
// check if game section exists,
|
||||
// if it does, check if the 'UseGameProfile' key exists,
|
||||
// if it doesn't then use the profile, else
|
||||
// check if the value is true
|
||||
if (!gameId.empty())
|
||||
{
|
||||
if (CoreSettingsSectionExists(gameSection))
|
||||
{
|
||||
section = gameSection;
|
||||
if (!CoreSettingsKeyExists(gameSection, "UseGameProfile") ||
|
||||
CoreSettingsGetBoolValue(SettingsID::Input_UseGameProfile, gameSection))
|
||||
{
|
||||
section = gameSection;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,25 +280,70 @@ static void load_settings(void)
|
||||
profile->GameboyRom = CoreSettingsGetStringValue(SettingsID::Input_GameboyRom, section);
|
||||
profile->GameboySave = CoreSettingsGetStringValue(SettingsID::Input_GameboySave, section);
|
||||
|
||||
// keep compatibility with profiles before version v0.3.9
|
||||
if (CoreSettingsKeyExists(section, "Sensitivity"))
|
||||
{
|
||||
profile->SensitivityValue = CoreSettingsGetIntValue(SettingsID::Input_Sensitivity, section);
|
||||
}
|
||||
else
|
||||
{
|
||||
profile->SensitivityValue = 100;
|
||||
}
|
||||
|
||||
// load inputmapping settings
|
||||
load_inputmapping_settings(&profile->Button_A, section, SettingsID::Input_A_InputType, SettingsID::Input_A_Data, SettingsID::Input_A_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_B, section, SettingsID::Input_B_InputType, SettingsID::Input_B_Data, SettingsID::Input_B_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_Start, section, SettingsID::Input_Start_InputType, SettingsID::Input_Start_Data, SettingsID::Input_Start_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_DpadUp, section, SettingsID::Input_DpadUp_InputType, SettingsID::Input_DpadUp_Data, SettingsID::Input_DpadUp_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_DpadDown, section, SettingsID::Input_DpadDown_InputType, SettingsID::Input_DpadDown_Data, SettingsID::Input_DpadDown_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_DpadLeft, section, SettingsID::Input_DpadLeft_InputType, SettingsID::Input_DpadLeft_Data, SettingsID::Input_DpadLeft_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_DpadRight, section, SettingsID::Input_DpadRight_InputType, SettingsID::Input_DpadRight_Data, SettingsID::Input_DpadRight_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_CButtonUp, section, SettingsID::Input_CButtonUp_InputType, SettingsID::Input_CButtonUp_Data, SettingsID::Input_CButtonUp_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_CButtonDown, section, SettingsID::Input_CButtonDown_InputType, SettingsID::Input_CButtonDown_Data, SettingsID::Input_CButtonDown_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_CButtonLeft, section, SettingsID::Input_CButtonLeft_InputType, SettingsID::Input_CButtonLeft_Data, SettingsID::Input_CButtonLeft_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_CButtonRight, section, SettingsID::Input_CButtonRight_InputType, SettingsID::Input_CButtonRight_Data, SettingsID::Input_CButtonRight_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_LeftTrigger, section, SettingsID::Input_LeftTrigger_InputType, SettingsID::Input_LeftTrigger_Data, SettingsID::Input_LeftTrigger_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_RightTrigger, section, SettingsID::Input_RightTrigger_InputType, SettingsID::Input_RightTrigger_Data, SettingsID::Input_RightTrigger_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_ZTrigger, section, SettingsID::Input_ZTrigger_InputType, SettingsID::Input_ZTrigger_Data, SettingsID::Input_ZTrigger_ExtraData);
|
||||
load_inputmapping_settings(&profile->AnalogStick_Up, section, SettingsID::Input_AnalogStickUp_InputType, SettingsID::Input_AnalogStickUp_Data, SettingsID::Input_AnalogStickUp_ExtraData);
|
||||
load_inputmapping_settings(&profile->AnalogStick_Down, section, SettingsID::Input_AnalogStickDown_InputType, SettingsID::Input_AnalogStickDown_Data, SettingsID::Input_AnalogStickDown_ExtraData);
|
||||
load_inputmapping_settings(&profile->AnalogStick_Left, section, SettingsID::Input_AnalogStickLeft_InputType, SettingsID::Input_AnalogStickLeft_Data, SettingsID::Input_AnalogStickLeft_ExtraData);
|
||||
load_inputmapping_settings(&profile->AnalogStick_Right, section, SettingsID::Input_AnalogStickRight_InputType, SettingsID::Input_AnalogStickRight_Data, SettingsID::Input_AnalogStickRight_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_A, section, SettingsID::Input_A_Name, SettingsID::Input_A_InputType, SettingsID::Input_A_Data, SettingsID::Input_A_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_B, section, SettingsID::Input_B_Name, SettingsID::Input_B_InputType, SettingsID::Input_B_Data, SettingsID::Input_B_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_Start, section, SettingsID::Input_Start_Name, SettingsID::Input_Start_InputType, SettingsID::Input_Start_Data, SettingsID::Input_Start_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_DpadUp, section, SettingsID::Input_DpadUp_Name, SettingsID::Input_DpadUp_InputType, SettingsID::Input_DpadUp_Data, SettingsID::Input_DpadUp_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_DpadDown, section, SettingsID::Input_DpadDown_Name, SettingsID::Input_DpadDown_InputType, SettingsID::Input_DpadDown_Data, SettingsID::Input_DpadDown_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_DpadLeft, section, SettingsID::Input_DpadLeft_Name, SettingsID::Input_DpadLeft_InputType, SettingsID::Input_DpadLeft_Data, SettingsID::Input_DpadLeft_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_DpadRight, section, SettingsID::Input_DpadRight_Name, SettingsID::Input_DpadRight_InputType, SettingsID::Input_DpadRight_Data, SettingsID::Input_DpadRight_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_CButtonUp, section, SettingsID::Input_CButtonUp_Name, SettingsID::Input_CButtonUp_InputType, SettingsID::Input_CButtonUp_Data, SettingsID::Input_CButtonUp_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_CButtonDown, section, SettingsID::Input_CButtonDown_Name, SettingsID::Input_CButtonDown_InputType, SettingsID::Input_CButtonDown_Data, SettingsID::Input_CButtonDown_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_CButtonLeft, section, SettingsID::Input_CButtonLeft_Name, SettingsID::Input_CButtonLeft_InputType, SettingsID::Input_CButtonLeft_Data, SettingsID::Input_CButtonLeft_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_CButtonRight, section, SettingsID::Input_CButtonRight_Name, SettingsID::Input_CButtonRight_InputType, SettingsID::Input_CButtonRight_Data, SettingsID::Input_CButtonRight_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_LeftTrigger, section, SettingsID::Input_LeftTrigger_Name, SettingsID::Input_LeftTrigger_InputType, SettingsID::Input_LeftTrigger_Data, SettingsID::Input_LeftTrigger_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_RightTrigger, section, SettingsID::Input_RightTrigger_Name, SettingsID::Input_RightTrigger_InputType, SettingsID::Input_RightTrigger_Data, SettingsID::Input_RightTrigger_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_ZTrigger, section, SettingsID::Input_ZTrigger_Name, SettingsID::Input_ZTrigger_InputType, SettingsID::Input_ZTrigger_Data, SettingsID::Input_ZTrigger_ExtraData);
|
||||
load_inputmapping_settings(&profile->AnalogStick_Up, section, SettingsID::Input_AnalogStickUp_Name, SettingsID::Input_AnalogStickUp_InputType, SettingsID::Input_AnalogStickUp_Data, SettingsID::Input_AnalogStickUp_ExtraData);
|
||||
load_inputmapping_settings(&profile->AnalogStick_Down, section, SettingsID::Input_AnalogStickDown_Name, SettingsID::Input_AnalogStickDown_InputType, SettingsID::Input_AnalogStickDown_Data, SettingsID::Input_AnalogStickDown_ExtraData);
|
||||
load_inputmapping_settings(&profile->AnalogStick_Left, section, SettingsID::Input_AnalogStickLeft_Name, SettingsID::Input_AnalogStickLeft_InputType, SettingsID::Input_AnalogStickLeft_Data, SettingsID::Input_AnalogStickLeft_ExtraData);
|
||||
load_inputmapping_settings(&profile->AnalogStick_Right, section, SettingsID::Input_AnalogStickRight_Name, SettingsID::Input_AnalogStickRight_InputType, SettingsID::Input_AnalogStickRight_Data, SettingsID::Input_AnalogStickRight_ExtraData);
|
||||
|
||||
// load hotkeys settings
|
||||
load_inputmapping_settings(&profile->Hotkey_Shutdown, section, SettingsID::Input_Hotkey_Shutdown_Name, SettingsID::Input_Hotkey_Shutdown_InputType, SettingsID::Input_Hotkey_Shutdown_Data, SettingsID::Input_Hotkey_Shutdown_ExtraData);
|
||||
load_inputmapping_settings(&profile->Hotkey_Exit, section, SettingsID::Input_Hotkey_Exit_Name, SettingsID::Input_Hotkey_Exit_InputType, SettingsID::Input_Hotkey_Exit_Data, SettingsID::Input_Hotkey_Exit_ExtraData);
|
||||
load_inputmapping_settings(&profile->Hotkey_SoftReset, section, SettingsID::Input_Hotkey_SoftReset_Name, SettingsID::Input_Hotkey_SoftReset_InputType, SettingsID::Input_Hotkey_SoftReset_Data, SettingsID::Input_Hotkey_SoftReset_ExtraData);
|
||||
load_inputmapping_settings(&profile->Hotkey_HardReset, section, SettingsID::Input_Hotkey_HardReset_Name, SettingsID::Input_Hotkey_HardReset_InputType, SettingsID::Input_Hotkey_HardReset_Data, SettingsID::Input_Hotkey_HardReset_ExtraData);
|
||||
load_inputmapping_settings(&profile->Hotkey_Resume, section, SettingsID::Input_Hotkey_Resume_Name, SettingsID::Input_Hotkey_Resume_InputType, SettingsID::Input_Hotkey_Resume_Data, SettingsID::Input_Hotkey_Resume_ExtraData);
|
||||
load_inputmapping_settings(&profile->Hotkey_Screenshot, section, SettingsID::Input_Hotkey_Screenshot_Name, SettingsID::Input_Hotkey_Screenshot_InputType, SettingsID::Input_Hotkey_Screenshot_Data, SettingsID::Input_Hotkey_Screenshot_ExtraData);
|
||||
load_inputmapping_settings(&profile->Hotkey_LimitFPS, section, SettingsID::Input_Hotkey_LimitFPS_Name, SettingsID::Input_Hotkey_LimitFPS_InputType, SettingsID::Input_Hotkey_LimitFPS_Data, SettingsID::Input_Hotkey_LimitFPS_ExtraData);
|
||||
load_inputmapping_settings(&profile->Hotkey_SpeedFactor25, section, SettingsID::Input_Hotkey_SpeedFactor25_Name, SettingsID::Input_Hotkey_SpeedFactor25_InputType, SettingsID::Input_Hotkey_SpeedFactor25_Data, SettingsID::Input_Hotkey_SpeedFactor25_ExtraData);
|
||||
load_inputmapping_settings(&profile->Hotkey_SpeedFactor50, section, SettingsID::Input_Hotkey_SpeedFactor50_Name, SettingsID::Input_Hotkey_SpeedFactor50_InputType, SettingsID::Input_Hotkey_SpeedFactor50_Data, SettingsID::Input_Hotkey_SpeedFactor50_ExtraData);
|
||||
load_inputmapping_settings(&profile->Hotkey_SpeedFactor75, section, SettingsID::Input_Hotkey_SpeedFactor75_Name, SettingsID::Input_Hotkey_SpeedFactor75_InputType, SettingsID::Input_Hotkey_SpeedFactor75_Data, SettingsID::Input_Hotkey_SpeedFactor75_ExtraData);
|
||||
load_inputmapping_settings(&profile->Hotkey_SpeedFactor100, section, SettingsID::Input_Hotkey_SpeedFactor100_Name, SettingsID::Input_Hotkey_SpeedFactor100_InputType, SettingsID::Input_Hotkey_SpeedFactor100_Data, SettingsID::Input_Hotkey_SpeedFactor100_ExtraData);
|
||||
load_inputmapping_settings(&profile->Hotkey_SpeedFactor125, section, SettingsID::Input_Hotkey_SpeedFactor125_Name, SettingsID::Input_Hotkey_SpeedFactor125_InputType, SettingsID::Input_Hotkey_SpeedFactor125_Data, SettingsID::Input_Hotkey_SpeedFactor125_ExtraData);
|
||||
load_inputmapping_settings(&profile->Hotkey_SpeedFactor150, section, SettingsID::Input_Hotkey_SpeedFactor150_Name, SettingsID::Input_Hotkey_SpeedFactor150_InputType, SettingsID::Input_Hotkey_SpeedFactor150_Data, SettingsID::Input_Hotkey_SpeedFactor150_ExtraData);
|
||||
load_inputmapping_settings(&profile->Hotkey_SpeedFactor175, section, SettingsID::Input_Hotkey_SpeedFactor175_Name, SettingsID::Input_Hotkey_SpeedFactor175_InputType, SettingsID::Input_Hotkey_SpeedFactor175_Data, SettingsID::Input_Hotkey_SpeedFactor175_ExtraData);
|
||||
load_inputmapping_settings(&profile->Hotkey_SpeedFactor200, section, SettingsID::Input_Hotkey_SpeedFactor200_Name, SettingsID::Input_Hotkey_SpeedFactor200_InputType, SettingsID::Input_Hotkey_SpeedFactor200_Data, SettingsID::Input_Hotkey_SpeedFactor200_ExtraData);
|
||||
load_inputmapping_settings(&profile->Hotkey_SpeedFactor225, section, SettingsID::Input_Hotkey_SpeedFactor225_Name, SettingsID::Input_Hotkey_SpeedFactor225_InputType, SettingsID::Input_Hotkey_SpeedFactor225_Data, SettingsID::Input_Hotkey_SpeedFactor225_ExtraData);
|
||||
load_inputmapping_settings(&profile->Hotkey_SpeedFactor250, section, SettingsID::Input_Hotkey_SpeedFactor250_Name, SettingsID::Input_Hotkey_SpeedFactor250_InputType, SettingsID::Input_Hotkey_SpeedFactor250_Data, SettingsID::Input_Hotkey_SpeedFactor250_ExtraData);
|
||||
load_inputmapping_settings(&profile->Hotkey_SpeedFactor275, section, SettingsID::Input_Hotkey_SpeedFactor275_Name, SettingsID::Input_Hotkey_SpeedFactor275_InputType, SettingsID::Input_Hotkey_SpeedFactor275_Data, SettingsID::Input_Hotkey_SpeedFactor275_ExtraData);
|
||||
load_inputmapping_settings(&profile->Hotkey_SpeedFactor300, section, SettingsID::Input_Hotkey_SpeedFactor300_Name, SettingsID::Input_Hotkey_SpeedFactor300_InputType, SettingsID::Input_Hotkey_SpeedFactor300_Data, SettingsID::Input_Hotkey_SpeedFactor300_ExtraData);
|
||||
load_inputmapping_settings(&profile->Hotkey_SaveState, section, SettingsID::Input_Hotkey_SaveState_Name, SettingsID::Input_Hotkey_SaveState_InputType, SettingsID::Input_Hotkey_SaveState_Data, SettingsID::Input_Hotkey_SaveState_ExtraData);
|
||||
load_inputmapping_settings(&profile->Hotkey_LoadState, section, SettingsID::Input_Hotkey_LoadState_Name, SettingsID::Input_Hotkey_LoadState_InputType, SettingsID::Input_Hotkey_LoadState_Data, SettingsID::Input_Hotkey_LoadState_ExtraData);
|
||||
load_inputmapping_settings(&profile->Hotkey_GSButton, section, SettingsID::Input_Hotkey_GSButton_Name, SettingsID::Input_Hotkey_GSButton_InputType, SettingsID::Input_Hotkey_GSButton_Data, SettingsID::Input_Hotkey_GSButton_ExtraData);
|
||||
load_inputmapping_settings(&profile->Hotkey_SaveStateSlot0, section, SettingsID::Input_Hotkey_SaveStateSlot0_Name, SettingsID::Input_Hotkey_SaveStateSlot0_InputType, SettingsID::Input_Hotkey_SaveStateSlot0_Data, SettingsID::Input_Hotkey_SaveStateSlot0_ExtraData);
|
||||
load_inputmapping_settings(&profile->Hotkey_SaveStateSlot1, section, SettingsID::Input_Hotkey_SaveStateSlot1_Name, SettingsID::Input_Hotkey_SaveStateSlot1_InputType, SettingsID::Input_Hotkey_SaveStateSlot1_Data, SettingsID::Input_Hotkey_SaveStateSlot1_ExtraData);
|
||||
load_inputmapping_settings(&profile->Hotkey_SaveStateSlot2, section, SettingsID::Input_Hotkey_SaveStateSlot2_Name, SettingsID::Input_Hotkey_SaveStateSlot2_InputType, SettingsID::Input_Hotkey_SaveStateSlot2_Data, SettingsID::Input_Hotkey_SaveStateSlot2_ExtraData);
|
||||
load_inputmapping_settings(&profile->Hotkey_SaveStateSlot3, section, SettingsID::Input_Hotkey_SaveStateSlot3_Name, SettingsID::Input_Hotkey_SaveStateSlot3_InputType, SettingsID::Input_Hotkey_SaveStateSlot3_Data, SettingsID::Input_Hotkey_SaveStateSlot3_ExtraData);
|
||||
load_inputmapping_settings(&profile->Hotkey_SaveStateSlot4, section, SettingsID::Input_Hotkey_SaveStateSlot4_Name, SettingsID::Input_Hotkey_SaveStateSlot4_InputType, SettingsID::Input_Hotkey_SaveStateSlot4_Data, SettingsID::Input_Hotkey_SaveStateSlot4_ExtraData);
|
||||
load_inputmapping_settings(&profile->Hotkey_SaveStateSlot5, section, SettingsID::Input_Hotkey_SaveStateSlot5_Name, SettingsID::Input_Hotkey_SaveStateSlot5_InputType, SettingsID::Input_Hotkey_SaveStateSlot5_Data, SettingsID::Input_Hotkey_SaveStateSlot5_ExtraData);
|
||||
load_inputmapping_settings(&profile->Hotkey_SaveStateSlot6, section, SettingsID::Input_Hotkey_SaveStateSlot6_Name, SettingsID::Input_Hotkey_SaveStateSlot6_InputType, SettingsID::Input_Hotkey_SaveStateSlot6_Data, SettingsID::Input_Hotkey_SaveStateSlot6_ExtraData);
|
||||
load_inputmapping_settings(&profile->Hotkey_SaveStateSlot7, section, SettingsID::Input_Hotkey_SaveStateSlot7_Name, SettingsID::Input_Hotkey_SaveStateSlot7_InputType, SettingsID::Input_Hotkey_SaveStateSlot7_Data, SettingsID::Input_Hotkey_SaveStateSlot7_ExtraData);
|
||||
load_inputmapping_settings(&profile->Hotkey_SaveStateSlot8, section, SettingsID::Input_Hotkey_SaveStateSlot8_Name, SettingsID::Input_Hotkey_SaveStateSlot8_InputType, SettingsID::Input_Hotkey_SaveStateSlot8_Data, SettingsID::Input_Hotkey_SaveStateSlot8_ExtraData);
|
||||
load_inputmapping_settings(&profile->Hotkey_SaveStateSlot9, section, SettingsID::Input_Hotkey_SaveStateSlot9_Name, SettingsID::Input_Hotkey_SaveStateSlot9_InputType, SettingsID::Input_Hotkey_SaveStateSlot9_Data, SettingsID::Input_Hotkey_SaveStateSlot9_ExtraData);
|
||||
load_inputmapping_settings(&profile->Hotkey_Fullscreen, section, SettingsID::Input_Hotkey_Fullscreen_Name, SettingsID::Input_Hotkey_Fullscreen_InputType, SettingsID::Input_Hotkey_Fullscreen_Data, SettingsID::Input_Hotkey_Fullscreen_ExtraData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,6 +360,7 @@ static void apply_controller_profiles(void)
|
||||
{
|
||||
InputProfile* profile = &l_InputProfiles[i];
|
||||
int plugin = PLUGIN_NONE;
|
||||
bool emulateVRU = (profile->DeviceNum == (int)InputDeviceType::EmulateVRU);
|
||||
|
||||
switch (profile->ControllerPak)
|
||||
{
|
||||
@@ -215,7 +370,7 @@ static void apply_controller_profiles(void)
|
||||
case N64ControllerPak::RumblePak:
|
||||
plugin = PLUGIN_RAW;
|
||||
break;
|
||||
case N64ControllerPak::TransferPak:
|
||||
case N64ControllerPak::TransferPak:
|
||||
plugin = PLUGIN_TRANSFER_PAK;
|
||||
break;
|
||||
default:
|
||||
@@ -223,10 +378,26 @@ static void apply_controller_profiles(void)
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef VRU
|
||||
// attempt to try initializing VRU when needed,
|
||||
// if it fails, unplug the VRU
|
||||
if (emulateVRU && !IsVRUInit() && !InitVRU())
|
||||
{
|
||||
profile->PluggedIn = false;
|
||||
}
|
||||
#else
|
||||
// always unplug VRU when RMG-Input was built
|
||||
// without VRU support
|
||||
if (emulateVRU)
|
||||
{
|
||||
profile->PluggedIn = false;
|
||||
}
|
||||
#endif // VRU
|
||||
|
||||
l_ControlInfo.Controls[i].Present = profile->PluggedIn ? 1 : 0;
|
||||
l_ControlInfo.Controls[i].Plugin = plugin;
|
||||
l_ControlInfo.Controls[i].Plugin = emulateVRU ? PLUGIN_NONE : plugin;
|
||||
l_ControlInfo.Controls[i].RawData = 0;
|
||||
l_ControlInfo.Controls[i].Type = CONT_TYPE_STANDARD;
|
||||
l_ControlInfo.Controls[i].Type = emulateVRU ? CONT_TYPE_VRU : CONT_TYPE_STANDARD;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,15 +430,90 @@ static void apply_gameboy_settings(void)
|
||||
CoreSettingsSave();
|
||||
}
|
||||
|
||||
static void setup_device_automatic(int num, InputProfile* profile)
|
||||
{
|
||||
static int previousSdlDeviceNum = -1;
|
||||
|
||||
// reset variable when needed
|
||||
if (num == 0)
|
||||
{
|
||||
previousSdlDeviceNum = -1;
|
||||
}
|
||||
|
||||
bool foundSdlDevice = false;
|
||||
SDL_GameController* sdlGameController = nullptr;
|
||||
SDL_Joystick* sdlJoystick = nullptr;
|
||||
std::string sdlDeviceName;
|
||||
|
||||
for (int i = (previousSdlDeviceNum + 1); i < SDL_NumJoysticks(); i++)
|
||||
{
|
||||
if (SDL_IsGameController(i))
|
||||
{
|
||||
sdlGameController = SDL_GameControllerOpen(i);
|
||||
if (sdlGameController != nullptr)
|
||||
{
|
||||
sdlDeviceName = SDL_GameControllerName(sdlGameController);
|
||||
SDL_GameControllerClose(sdlGameController);
|
||||
profile->DeviceNum = i;
|
||||
profile->DeviceName = sdlDeviceName;
|
||||
previousSdlDeviceNum = i;
|
||||
foundSdlDevice = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sdlJoystick = SDL_JoystickOpen(i);
|
||||
if (sdlJoystick != nullptr)
|
||||
{
|
||||
sdlDeviceName = SDL_JoystickName(sdlJoystick);
|
||||
SDL_JoystickClose(sdlJoystick);
|
||||
profile->DeviceNum = i;
|
||||
profile->DeviceName = sdlDeviceName;
|
||||
previousSdlDeviceNum = i;
|
||||
foundSdlDevice = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundSdlDevice)
|
||||
{ // fallback to keyboard
|
||||
if (num == 0)
|
||||
{
|
||||
profile->DeviceNum = (int)InputDeviceType::Keyboard;
|
||||
}
|
||||
else
|
||||
{
|
||||
profile->PluggedIn = false;
|
||||
|
||||
// only override present in core
|
||||
// when we have the control info
|
||||
if (l_HasControlInfo)
|
||||
{
|
||||
l_ControlInfo.Controls[num].Present = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void open_controllers(void)
|
||||
{
|
||||
// force re-fresh joystick list
|
||||
SDL_JoystickUpdate();
|
||||
|
||||
for (int i = 0; i < NUM_CONTROLLERS; i++)
|
||||
{
|
||||
InputProfile* profile = &l_InputProfiles[i];
|
||||
|
||||
profile->InputDevice.CloseDevice();
|
||||
|
||||
if (profile->DeviceNum != -1)
|
||||
if (profile->DeviceNum == (int)InputDeviceType::Automatic)
|
||||
{
|
||||
setup_device_automatic(i, profile);
|
||||
}
|
||||
|
||||
if (profile->DeviceNum != (int)InputDeviceType::Keyboard)
|
||||
{
|
||||
profile->InputDevice.OpenDevice(profile->DeviceName, profile->DeviceNum);
|
||||
}
|
||||
@@ -279,95 +525,166 @@ static void close_controllers(void)
|
||||
for (int i = 0; i < NUM_CONTROLLERS; i++)
|
||||
{
|
||||
InputProfile* profile = &l_InputProfiles[i];
|
||||
|
||||
|
||||
profile->InputDevice.CloseDevice();
|
||||
}
|
||||
}
|
||||
|
||||
static int get_button_state(InputProfile* profile, InputMapping* inputMapping)
|
||||
static int get_button_state(InputProfile* profile, const InputMapping* inputMapping, const bool allPressed = false)
|
||||
{
|
||||
switch (inputMapping->Type)
|
||||
int state = 0;
|
||||
|
||||
for (int i = 0; i < inputMapping->Count; i++)
|
||||
{
|
||||
case InputType::GamepadButton:
|
||||
const int data = inputMapping->Data.at(i);
|
||||
const int extraData = inputMapping->ExtraData.at(i);
|
||||
|
||||
switch ((InputType)inputMapping->Type[i])
|
||||
{
|
||||
return SDL_GameControllerGetButton(profile->InputDevice.GetGameControllerHandle(), (SDL_GameControllerButton)inputMapping->Data);
|
||||
};
|
||||
case InputType::GamepadAxis:
|
||||
{
|
||||
int axis_value = SDL_GameControllerGetAxis(profile->InputDevice.GetGameControllerHandle(), (SDL_GameControllerAxis)inputMapping->Data);
|
||||
return (abs(axis_value) >= (SDL_AXIS_PEAK / 2) && (inputMapping->ExtraData ? axis_value > 0 : axis_value < 0)) ? 1 : 0;
|
||||
};
|
||||
case InputType::JoystickButton:
|
||||
{
|
||||
return SDL_JoystickGetButton(profile->InputDevice.GetJoystickHandle(), inputMapping->Data);
|
||||
};
|
||||
case InputType::JoystickAxis:
|
||||
{
|
||||
int axis_value = SDL_JoystickGetAxis(profile->InputDevice.GetJoystickHandle(), inputMapping->Data);
|
||||
return (abs(axis_value) >= (SDL_AXIS_PEAK / 2) && (inputMapping->ExtraData ? axis_value > 0 : axis_value < 0)) ? 1 : 0;
|
||||
case InputType::GamepadButton:
|
||||
{
|
||||
if (allPressed && i > 0)
|
||||
{
|
||||
state &= SDL_GameControllerGetButton(profile->InputDevice.GetGameControllerHandle(), (SDL_GameControllerButton)data);
|
||||
}
|
||||
else
|
||||
{
|
||||
state |= SDL_GameControllerGetButton(profile->InputDevice.GetGameControllerHandle(), (SDL_GameControllerButton)data);
|
||||
}
|
||||
} break;
|
||||
case InputType::GamepadAxis:
|
||||
{
|
||||
int axis_value = SDL_GameControllerGetAxis(profile->InputDevice.GetGameControllerHandle(), (SDL_GameControllerAxis)data);
|
||||
if (allPressed && i > 0)
|
||||
{
|
||||
state &= (abs(axis_value) >= (SDL_AXIS_PEAK / 2) && (extraData ? axis_value > 0 : axis_value < 0)) ? 1 : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
state |= (abs(axis_value) >= (SDL_AXIS_PEAK / 2) && (extraData ? axis_value > 0 : axis_value < 0)) ? 1 : 0;
|
||||
}
|
||||
} break;
|
||||
case InputType::JoystickButton:
|
||||
{
|
||||
if (allPressed && i > 0)
|
||||
{
|
||||
state &= SDL_JoystickGetButton(profile->InputDevice.GetJoystickHandle(), data);
|
||||
}
|
||||
else
|
||||
{
|
||||
state |= SDL_JoystickGetButton(profile->InputDevice.GetJoystickHandle(), data);
|
||||
}
|
||||
} break;
|
||||
case InputType::JoystickAxis:
|
||||
{
|
||||
int axis_value = SDL_JoystickGetAxis(profile->InputDevice.GetJoystickHandle(), data);
|
||||
if (allPressed && i > 0)
|
||||
{
|
||||
state &= (abs(axis_value) >= (SDL_AXIS_PEAK / 2) && (extraData ? axis_value > 0 : axis_value < 0)) ? 1 : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
state |= (abs(axis_value) >= (SDL_AXIS_PEAK / 2) && (extraData ? axis_value > 0 : axis_value < 0)) ? 1 : 0;
|
||||
}
|
||||
} break;
|
||||
case InputType::Keyboard:
|
||||
{
|
||||
if (allPressed && i > 0)
|
||||
{
|
||||
state &= l_KeyboardState[data] ? 1 : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
state |= l_KeyboardState[data] ? 1 : 0;
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
case InputType::Keyboard:
|
||||
|
||||
// early return when needed
|
||||
if (allPressed && !state)
|
||||
{
|
||||
return l_KeyboardState[inputMapping->Data] ? 1 : 0;
|
||||
};
|
||||
default:
|
||||
break;
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return state;
|
||||
}
|
||||
|
||||
// returns axis input scaled to the range [-1, 1]
|
||||
static double get_axis_state(InputProfile* profile, InputMapping* inputMapping, int direction, double value)
|
||||
static double get_axis_state(InputProfile* profile, const InputMapping* inputMapping, const int direction, const double value, bool& useButtonMapping)
|
||||
{
|
||||
switch (inputMapping->Type)
|
||||
double axis_state = value;
|
||||
bool button_state = false;
|
||||
|
||||
for (int i = 0; i < inputMapping->Count; i++)
|
||||
{
|
||||
case InputType::GamepadButton:
|
||||
const int data = inputMapping->Data.at(i);
|
||||
const int extraData = inputMapping->ExtraData.at(i);
|
||||
|
||||
switch ((InputType)inputMapping->Type[i])
|
||||
{
|
||||
int buttonState = SDL_GameControllerGetButton(profile->InputDevice.GetGameControllerHandle(), (SDL_GameControllerButton)inputMapping->Data);
|
||||
return buttonState ? direction : value;
|
||||
} break;
|
||||
case InputType::GamepadAxis:
|
||||
{
|
||||
double axis_value = SDL_GameControllerGetAxis(profile->InputDevice.GetGameControllerHandle(), (SDL_GameControllerAxis)inputMapping->Data);
|
||||
if (inputMapping->ExtraData ? axis_value > 0 : axis_value < 0)
|
||||
case InputType::GamepadButton:
|
||||
{
|
||||
axis_value = (axis_value / SDL_AXIS_PEAK);
|
||||
axis_value = abs(axis_value) * direction;
|
||||
return axis_value;
|
||||
}
|
||||
} break;
|
||||
case InputType::JoystickButton:
|
||||
{
|
||||
int buttonState = SDL_JoystickGetButton(profile->InputDevice.GetJoystickHandle(), inputMapping->Data);
|
||||
return buttonState ? direction : value;
|
||||
} break;
|
||||
case InputType::JoystickAxis:
|
||||
{
|
||||
double axis_value = SDL_JoystickGetAxis(profile->InputDevice.GetJoystickHandle(), inputMapping->Data);
|
||||
if (inputMapping->ExtraData ? axis_value > 0 : axis_value < 0)
|
||||
button_state |= SDL_GameControllerGetButton(profile->InputDevice.GetGameControllerHandle(), (SDL_GameControllerButton)data);;
|
||||
} break;
|
||||
case InputType::GamepadAxis:
|
||||
{
|
||||
axis_value = (axis_value / SDL_AXIS_PEAK);
|
||||
axis_value = abs(axis_value) * direction;
|
||||
return axis_value;
|
||||
}
|
||||
} break;
|
||||
case InputType::Keyboard:
|
||||
{
|
||||
return l_KeyboardState[inputMapping->Data] ? direction : value;
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
double axis_value = SDL_GameControllerGetAxis(profile->InputDevice.GetGameControllerHandle(), (SDL_GameControllerAxis)data);
|
||||
if (extraData ? axis_value > 0 : axis_value < 0)
|
||||
{
|
||||
axis_value = (axis_value / SDL_AXIS_PEAK);
|
||||
axis_value = abs(axis_value) * direction;
|
||||
axis_state = axis_value;
|
||||
}
|
||||
} break;
|
||||
case InputType::JoystickButton:
|
||||
{
|
||||
button_state |= SDL_JoystickGetButton(profile->InputDevice.GetJoystickHandle(), data);;
|
||||
} break;
|
||||
case InputType::JoystickAxis:
|
||||
{
|
||||
double axis_value = SDL_JoystickGetAxis(profile->InputDevice.GetJoystickHandle(), data);
|
||||
if (extraData ? axis_value > 0 : axis_value < 0)
|
||||
{
|
||||
axis_value = (axis_value / SDL_AXIS_PEAK);
|
||||
axis_value = abs(axis_value) * direction;
|
||||
axis_state = axis_value;
|
||||
}
|
||||
} break;
|
||||
case InputType::Keyboard:
|
||||
{
|
||||
button_state |= l_KeyboardState[data];
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
// when a button has been mapped
|
||||
// to an axis, we should prioritize
|
||||
// the button when it's been pressed
|
||||
if (button_state)
|
||||
{
|
||||
useButtonMapping = true;
|
||||
return direction;
|
||||
}
|
||||
else if (!useButtonMapping)
|
||||
{
|
||||
return axis_state;
|
||||
}
|
||||
else
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
static double simulate_deadzone(double n64InputAxis, double maxAxis, int deadzone, double axisRange)
|
||||
static double simulate_deadzone(const double n64InputAxis, const double maxAxis, const int deadzone, const double axisRange)
|
||||
{
|
||||
double axisAbsolute = std::abs(n64InputAxis);
|
||||
|
||||
if (axisAbsolute < deadzone)
|
||||
if (axisAbsolute <= deadzone)
|
||||
{
|
||||
axisAbsolute = 0; // No input when inside deadzone
|
||||
}
|
||||
@@ -380,16 +697,17 @@ static double simulate_deadzone(double n64InputAxis, double maxAxis, int deadzon
|
||||
return axisAbsolute;
|
||||
}
|
||||
|
||||
// Credit: MerryMage
|
||||
static void simulate_octagon(double inputX, double inputY, double deadzoneFactor, int& outputX, int& outputY)
|
||||
// Credit: MerryMage & fzurita
|
||||
static void simulate_octagon(const double inputX, const double inputY, const double deadzoneFactor, const double sensitivityFactor, int& outputX, int& outputY)
|
||||
{
|
||||
double maxAxis = N64_AXIS_PEAK;
|
||||
double maxDiagonal = MAX_DIAGONAL_VALUE;
|
||||
int deadzone = static_cast<int>(deadzoneFactor * N64_AXIS_PEAK);
|
||||
double axisRange = maxAxis - deadzone;
|
||||
// don't increase emulated range at higher than 100% sensitivity
|
||||
const double maxAxis = N64_AXIS_PEAK * std::min(sensitivityFactor, 1.0);
|
||||
const double maxDiagonal = MAX_DIAGONAL_VALUE * std::min(sensitivityFactor, 1.0);
|
||||
const int deadzone = (int)(deadzoneFactor * N64_AXIS_PEAK * sensitivityFactor);
|
||||
const double axisRange = maxAxis - deadzone;
|
||||
// scale to [-maxAxis, maxAxis]
|
||||
double ax = inputX * maxAxis;
|
||||
double ay = inputY * maxAxis;
|
||||
double ax = std::min(inputX * sensitivityFactor, 1.0) * maxAxis;
|
||||
double ay = std::min(inputY * sensitivityFactor, 1.0) * maxAxis;
|
||||
|
||||
// check whether (ax, ay) is within the circle of radius MAX_AXIS
|
||||
double len = std::sqrt(ax*ax + ay*ay);
|
||||
@@ -410,18 +728,18 @@ static void simulate_octagon(double inputX, double inputY, double deadzoneFactor
|
||||
// bound diagonals to an octagonal range [-69, 69]
|
||||
if (ax != 0.0 && ay != 0.0)
|
||||
{
|
||||
double slope = ay / ax;
|
||||
double edgex = copysign(maxAxis / (std::abs(slope) + (maxAxis - maxDiagonal) / maxDiagonal), ax);
|
||||
double edgey = copysign(std::min(std::abs(edgex * slope), maxAxis / (1.0 / std::abs(slope) + (maxAxis - maxDiagonal) / maxDiagonal)), ay);
|
||||
const double slope = ay / ax;
|
||||
double edgex = std::copysign(maxAxis / (std::abs(slope) + (maxAxis - maxDiagonal) / maxDiagonal), ax);
|
||||
const double edgey = std::copysign(std::min(std::abs(edgex * slope), maxAxis / (1.0 / std::abs(slope) + (maxAxis - maxDiagonal) / maxDiagonal)), ay);
|
||||
edgex = edgey / slope;
|
||||
|
||||
double scale = std::sqrt(edgex*edgex + edgey*edgey) / maxAxis;
|
||||
const double scale = std::sqrt(edgex*edgex + edgey*edgey) / maxAxis;
|
||||
ax *= scale;
|
||||
ay *= scale;
|
||||
}
|
||||
|
||||
outputX = static_cast<int>(ax);
|
||||
outputY = static_cast<int>(ay);
|
||||
outputX = (int)ax;
|
||||
outputY = (int)ay;
|
||||
}
|
||||
|
||||
static unsigned char data_crc(unsigned char *data, int length)
|
||||
@@ -448,20 +766,95 @@ static unsigned char data_crc(unsigned char *data, int length)
|
||||
return remainder;
|
||||
}
|
||||
|
||||
void sdl_init()
|
||||
static bool check_hotkeys(int Control)
|
||||
{
|
||||
for (int subsystem : {SDL_INIT_GAMECONTROLLER, SDL_INIT_VIDEO, SDL_INIT_HAPTIC})
|
||||
InputProfile* profile = &l_InputProfiles[Control];
|
||||
|
||||
int state = 0;
|
||||
|
||||
// we only have to check for hotkeys
|
||||
// when there's a controller opened
|
||||
if (!profile->InputDevice.HasOpenDevice())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#define DEFINE_HOTKEY(mapping, pressed, function, function2) \
|
||||
state = get_button_state(profile, &profile->mapping, true); \
|
||||
if (state) \
|
||||
{ \
|
||||
if (!profile->pressed) \
|
||||
{ \
|
||||
profile->pressed = true; \
|
||||
function; \
|
||||
} \
|
||||
return true; \
|
||||
} \
|
||||
else if (!state && profile->pressed) \
|
||||
{ \
|
||||
function2; \
|
||||
profile->pressed = false; \
|
||||
}
|
||||
|
||||
DEFINE_HOTKEY(Hotkey_Shutdown, Hotkey_Shutdown_Pressed, CoreStopEmulation(), );
|
||||
DEFINE_HOTKEY(Hotkey_Exit, Hotkey_Exit_Pressed, QGuiApplication::quit(), );
|
||||
DEFINE_HOTKEY(Hotkey_SoftReset, Hotkey_SoftReset_Pressed, CoreResetEmulation(false), );
|
||||
DEFINE_HOTKEY(Hotkey_Resume, Hotkey_Resume_Pressed, CoreIsEmulationPaused() ? CoreResumeEmulation() : CorePauseEmulation(), );
|
||||
DEFINE_HOTKEY(Hotkey_Screenshot, Hotkey_Screenshot_Pressed, CoreTakeScreenshot(), );
|
||||
DEFINE_HOTKEY(Hotkey_LimitFPS, Hotkey_LimitFPS_Pressed, CoreSetSpeedLimiterState(!CoreIsSpeedLimiterEnabled()), );
|
||||
DEFINE_HOTKEY(Hotkey_SpeedFactor25, Hotkey_SpeedFactor_Pressed, CoreSetSpeedFactor(25), );
|
||||
DEFINE_HOTKEY(Hotkey_SpeedFactor50, Hotkey_SpeedFactor_Pressed, CoreSetSpeedFactor(50), );
|
||||
DEFINE_HOTKEY(Hotkey_SpeedFactor75, Hotkey_SpeedFactor_Pressed, CoreSetSpeedFactor(75), );
|
||||
DEFINE_HOTKEY(Hotkey_SpeedFactor100, Hotkey_SpeedFactor_Pressed, CoreSetSpeedFactor(100), );
|
||||
DEFINE_HOTKEY(Hotkey_SpeedFactor125, Hotkey_SpeedFactor_Pressed, CoreSetSpeedFactor(125), );
|
||||
DEFINE_HOTKEY(Hotkey_SpeedFactor150, Hotkey_SpeedFactor_Pressed, CoreSetSpeedFactor(150), );
|
||||
DEFINE_HOTKEY(Hotkey_SpeedFactor175, Hotkey_SpeedFactor_Pressed, CoreSetSpeedFactor(175), );
|
||||
DEFINE_HOTKEY(Hotkey_SpeedFactor200, Hotkey_SpeedFactor_Pressed, CoreSetSpeedFactor(200), );
|
||||
DEFINE_HOTKEY(Hotkey_SpeedFactor225, Hotkey_SpeedFactor_Pressed, CoreSetSpeedFactor(225), );
|
||||
DEFINE_HOTKEY(Hotkey_SpeedFactor250, Hotkey_SpeedFactor_Pressed, CoreSetSpeedFactor(250), );
|
||||
DEFINE_HOTKEY(Hotkey_SpeedFactor275, Hotkey_SpeedFactor_Pressed, CoreSetSpeedFactor(275), );
|
||||
DEFINE_HOTKEY(Hotkey_SpeedFactor300, Hotkey_SpeedFactor_Pressed, CoreSetSpeedFactor(300), );
|
||||
DEFINE_HOTKEY(Hotkey_SaveState, Hotkey_SaveState_Pressed, CoreSaveState(), );
|
||||
DEFINE_HOTKEY(Hotkey_LoadState, Hotkey_LoadState_Pressed, CoreLoadSaveState(), );
|
||||
DEFINE_HOTKEY(Hotkey_GSButton, Hotkey_GSButton_Pressed, CorePressGamesharkButton(true), CorePressGamesharkButton(false));
|
||||
DEFINE_HOTKEY(Hotkey_SaveStateSlot0, Hotkey_SaveStateSlot_Pressed, CoreSetSaveStateSlot(0), );
|
||||
DEFINE_HOTKEY(Hotkey_SaveStateSlot1, Hotkey_SaveStateSlot_Pressed, CoreSetSaveStateSlot(1), );
|
||||
DEFINE_HOTKEY(Hotkey_SaveStateSlot2, Hotkey_SaveStateSlot_Pressed, CoreSetSaveStateSlot(2), );
|
||||
DEFINE_HOTKEY(Hotkey_SaveStateSlot3, Hotkey_SaveStateSlot_Pressed, CoreSetSaveStateSlot(3), );
|
||||
DEFINE_HOTKEY(Hotkey_SaveStateSlot4, Hotkey_SaveStateSlot_Pressed, CoreSetSaveStateSlot(4), );
|
||||
DEFINE_HOTKEY(Hotkey_SaveStateSlot5, Hotkey_SaveStateSlot_Pressed, CoreSetSaveStateSlot(5), );
|
||||
DEFINE_HOTKEY(Hotkey_SaveStateSlot6, Hotkey_SaveStateSlot_Pressed, CoreSetSaveStateSlot(6), );
|
||||
DEFINE_HOTKEY(Hotkey_SaveStateSlot7, Hotkey_SaveStateSlot_Pressed, CoreSetSaveStateSlot(7), );
|
||||
DEFINE_HOTKEY(Hotkey_SaveStateSlot8, Hotkey_SaveStateSlot_Pressed, CoreSetSaveStateSlot(8), );
|
||||
DEFINE_HOTKEY(Hotkey_SaveStateSlot9, Hotkey_SaveStateSlot_Pressed, CoreSetSaveStateSlot(9), );
|
||||
DEFINE_HOTKEY(Hotkey_Fullscreen, Hotkey_Fullscreen_Pressed, CoreToggleFullscreen(), );
|
||||
|
||||
#undef DEFINE_HOTKEY
|
||||
return false;
|
||||
}
|
||||
|
||||
static void sdl_init()
|
||||
{
|
||||
std::filesystem::path gameControllerDbPath;
|
||||
|
||||
for (const int subsystem : {SDL_INIT_GAMECONTROLLER, SDL_INIT_AUDIO, SDL_INIT_VIDEO, SDL_INIT_HAPTIC})
|
||||
{
|
||||
if (!SDL_WasInit(subsystem))
|
||||
{
|
||||
SDL_InitSubSystem(subsystem);
|
||||
}
|
||||
}
|
||||
|
||||
gameControllerDbPath = CoreGetSharedDataDirectory();
|
||||
gameControllerDbPath += "/gamecontrollerdb.txt";
|
||||
|
||||
// load mappings from file
|
||||
SDL_GameControllerAddMappingsFromFile(gameControllerDbPath.string().c_str());
|
||||
}
|
||||
|
||||
void sdl_quit()
|
||||
static void sdl_quit()
|
||||
{
|
||||
for (int subsystem : {SDL_INIT_GAMECONTROLLER, SDL_INIT_HAPTIC})
|
||||
for (const int subsystem : {SDL_INIT_GAMECONTROLLER, SDL_INIT_HAPTIC})
|
||||
{
|
||||
if (SDL_WasInit(subsystem))
|
||||
{
|
||||
@@ -486,6 +879,8 @@ EXPORT m64p_error CALL PluginStartup(m64p_dynlib_handle CoreLibHandle, void *Con
|
||||
return M64ERR_SYSTEM_FAIL;
|
||||
}
|
||||
|
||||
CoreSetupDebugCallbackMessage(DebugCallback, Context);
|
||||
|
||||
sdl_init();
|
||||
|
||||
l_SDLThread = new Thread::SDLThread(nullptr);
|
||||
@@ -496,6 +891,9 @@ EXPORT m64p_error CALL PluginStartup(m64p_dynlib_handle CoreLibHandle, void *Con
|
||||
l_InputProfiles[i].InputDevice.SetSDLThread(l_SDLThread);
|
||||
}
|
||||
|
||||
l_HotkeysThread = new Thread::HotkeysThread(check_hotkeys, nullptr);
|
||||
l_HotkeysThread->start();
|
||||
|
||||
load_settings();
|
||||
|
||||
return M64ERR_SUCCESS;
|
||||
@@ -514,6 +912,10 @@ EXPORT m64p_error CALL PluginShutdown(void)
|
||||
l_SDLThread->deleteLater();
|
||||
l_SDLThread = nullptr;
|
||||
|
||||
l_HotkeysThread->StopLoop();
|
||||
l_HotkeysThread->deleteLater();
|
||||
l_HotkeysThread = nullptr;
|
||||
|
||||
sdl_quit();
|
||||
|
||||
return M64ERR_SUCCESS;
|
||||
@@ -554,7 +956,7 @@ EXPORT m64p_error CALL PluginGetVersion(m64p_plugin_type *pluginType, int *plugi
|
||||
// Custom Plugin Functions
|
||||
//
|
||||
|
||||
EXPORT m64p_error CALL PluginConfig()
|
||||
EXPORT m64p_error CALL PluginConfig2(int romConfig)
|
||||
{
|
||||
if (l_SDLThread == nullptr)
|
||||
{
|
||||
@@ -566,11 +968,17 @@ EXPORT m64p_error CALL PluginConfig()
|
||||
|
||||
l_SDLThread->SetAction(SDLThreadAction::SDLPumpEvents);
|
||||
|
||||
UserInterface::MainDialog dialog(nullptr, l_SDLThread);
|
||||
UserInterface::MainDialog dialog(nullptr, l_SDLThread, romConfig);
|
||||
dialog.exec();
|
||||
|
||||
l_SDLThread->SetAction(SDLThreadAction::None);
|
||||
|
||||
|
||||
// wait until it's done pumping events
|
||||
while (l_SDLThread->GetCurrentAction() == SDLThreadAction::SDLPumpEvents)
|
||||
{
|
||||
QThread::msleep(5);
|
||||
}
|
||||
|
||||
// reload settings
|
||||
load_settings();
|
||||
|
||||
@@ -586,6 +994,11 @@ EXPORT m64p_error CALL PluginConfig()
|
||||
return M64ERR_SUCCESS;
|
||||
}
|
||||
|
||||
EXPORT int CALL PluginConfig2HasRomConfig(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
//
|
||||
// Input Plugin Functions
|
||||
//
|
||||
@@ -657,16 +1070,33 @@ EXPORT void CALL GetKeys(int Control, BUTTONS* Keys)
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef VRU
|
||||
// when we're emulating the VRU,
|
||||
// we need to check the mic state
|
||||
if (profile->DeviceNum == (int)InputDeviceType::EmulateVRU)
|
||||
{
|
||||
if (GetVRUMicState())
|
||||
{
|
||||
Keys->Value = 0x0020;
|
||||
}
|
||||
else
|
||||
{
|
||||
Keys->Value = 0x0000;
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif // VRU
|
||||
|
||||
// check if device has been disconnected,
|
||||
// if it has, try to open it again,
|
||||
// only do this every 2 seconds to prevent lag
|
||||
const auto currentTime = std::chrono::high_resolution_clock::now();
|
||||
const auto currentTime = std::chrono::high_resolution_clock::now();
|
||||
const int secondsPassed = std::chrono::duration_cast<std::chrono::seconds>(currentTime - profile->LastDeviceCheckTime).count();
|
||||
if (secondsPassed >= 2)
|
||||
{
|
||||
profile->LastDeviceCheckTime = currentTime;
|
||||
|
||||
if (profile->DeviceNum != -1)
|
||||
if (profile->DeviceNum != (int)InputDeviceType::Keyboard)
|
||||
{
|
||||
if (profile->InputDevice.IsOpeningDevice())
|
||||
{
|
||||
@@ -676,11 +1106,18 @@ EXPORT void CALL GetKeys(int Control, BUTTONS* Keys)
|
||||
if (!profile->InputDevice.HasOpenDevice() || !profile->InputDevice.IsAttached())
|
||||
{
|
||||
profile->InputDevice.OpenDevice(profile->DeviceName, profile->DeviceNum);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// when we've matched a hotkey,
|
||||
// we don't need to check anything
|
||||
// else
|
||||
if(check_hotkeys(Control))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Keys->A_BUTTON = get_button_state(profile, &profile->Button_A);
|
||||
Keys->B_BUTTON = get_button_state(profile, &profile->Button_B);
|
||||
Keys->START_BUTTON = get_button_state(profile, &profile->Button_Start);
|
||||
@@ -697,16 +1134,18 @@ EXPORT void CALL GetKeys(int Control, BUTTONS* Keys)
|
||||
Keys->Z_TRIG = get_button_state(profile, &profile->Button_ZTrigger);
|
||||
|
||||
double inputX = 0, inputY = 0;
|
||||
inputY = get_axis_state(profile, &profile->AnalogStick_Up, 1, inputY);
|
||||
inputY = get_axis_state(profile, &profile->AnalogStick_Down, -1, inputY);
|
||||
inputX = get_axis_state(profile, &profile->AnalogStick_Left, -1, inputX);
|
||||
inputX = get_axis_state(profile, &profile->AnalogStick_Right, 1, inputX);
|
||||
bool useButtonMapping = false;
|
||||
inputY = get_axis_state(profile, &profile->AnalogStick_Up, 1, inputY, useButtonMapping);
|
||||
inputY = get_axis_state(profile, &profile->AnalogStick_Down, -1, inputY, useButtonMapping);
|
||||
inputX = get_axis_state(profile, &profile->AnalogStick_Left, -1, inputX, useButtonMapping);
|
||||
inputX = get_axis_state(profile, &profile->AnalogStick_Right, 1, inputX, useButtonMapping);
|
||||
|
||||
int octagonX = 0, octagonY = 0;
|
||||
simulate_octagon(
|
||||
inputX, // inputX
|
||||
inputY, // inputY
|
||||
profile->DeadzoneValue / 100.0, // deadzoneFactor
|
||||
profile->SensitivityValue / 100.0, // sensitivityFactor
|
||||
octagonX, // outputX
|
||||
octagonY // outputY
|
||||
);
|
||||
@@ -722,7 +1161,7 @@ EXPORT void CALL InitiateControllers(CONTROL_INFO ControlInfo)
|
||||
l_KeyboardState[i] = 0;
|
||||
}
|
||||
|
||||
l_ControlInfo = ControlInfo;
|
||||
l_ControlInfo = ControlInfo;
|
||||
l_HasControlInfo = true;
|
||||
|
||||
// load settings
|
||||
@@ -741,12 +1180,18 @@ EXPORT void CALL ReadController(int Control, unsigned char *Command)
|
||||
|
||||
EXPORT int CALL RomOpen(void)
|
||||
{
|
||||
l_HotkeysThread->SetState(HotkeysThreadState::RomOpened);
|
||||
return 1;
|
||||
}
|
||||
|
||||
EXPORT void CALL RomClosed(void)
|
||||
{
|
||||
l_HotkeysThread->SetState(HotkeysThreadState::RomClosed);
|
||||
l_HasControlInfo = false;
|
||||
close_controllers();
|
||||
#ifdef VRU
|
||||
QuitVRU();
|
||||
#endif // VRU
|
||||
}
|
||||
|
||||
EXPORT void CALL SDL_KeyDown(int keymod, int keysym)
|
||||
|
||||
@@ -5,17 +5,29 @@ set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
|
||||
find_package(${QT_VERSION} COMPONENTS Gui Widgets Core OpenGL REQUIRED)
|
||||
find_package(Qt6 COMPONENTS Gui Widgets Core OpenGL REQUIRED)
|
||||
|
||||
if (PORTABLE_INSTALL)
|
||||
add_definitions(-DPORTABLE_INSTALL)
|
||||
endif(PORTABLE_INSTALL)
|
||||
|
||||
if (UPDATER)
|
||||
find_package(${QT_VERSION} COMPONENTS Network REQUIRED)
|
||||
add_definitions(-DUPDATER)
|
||||
find_package(Qt6 COMPONENTS Network REQUIRED)
|
||||
add_definitions(-DUPDATER)
|
||||
endif(UPDATER)
|
||||
|
||||
if (APPIMAGE_UPDATER)
|
||||
add_definitions(-DAPPIMAGE_UPDATER)
|
||||
endif(APPIMAGE_UPDATER)
|
||||
|
||||
if (DISCORD_RPC)
|
||||
add_definitions(-DDISCORD_RPC)
|
||||
add_definitions(-DDISCORD_RPC)
|
||||
endif(DISCORD_RPC)
|
||||
|
||||
if (FORCE_XCB)
|
||||
add_definitions(-DFORCE_XCB)
|
||||
endif(FORCE_XCB)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(SDL2 REQUIRED sdl2)
|
||||
|
||||
@@ -51,42 +63,56 @@ set(RMG_SOURCES
|
||||
Thread/RomSearcherThread.cpp
|
||||
Thread/EmulationThread.cpp
|
||||
Utilities/QtKeyToSdl2Key.cpp
|
||||
OnScreenDisplay.cpp
|
||||
Callbacks.cpp
|
||||
VidExt.cpp
|
||||
main.cpp
|
||||
)
|
||||
|
||||
if (UPDATER)
|
||||
list(APPEND RMG_SOURCES
|
||||
UserInterface/Dialog/UpdateDialog.cpp
|
||||
UserInterface/Dialog/UpdateDialog.ui
|
||||
UserInterface/Dialog/DownloadUpdateDialog.cpp
|
||||
UserInterface/Dialog/DownloadUpdateDialog.ui
|
||||
UserInterface/Dialog/InstallUpdateDialog.cpp
|
||||
UserInterface/Dialog/InstallUpdateDialog.ui
|
||||
)
|
||||
list(APPEND RMG_SOURCES
|
||||
UserInterface/Dialog/UpdateDialog.cpp
|
||||
UserInterface/Dialog/UpdateDialog.ui
|
||||
UserInterface/Dialog/DownloadUpdateDialog.cpp
|
||||
UserInterface/Dialog/DownloadUpdateDialog.ui
|
||||
UserInterface/Dialog/InstallUpdateDialog.cpp
|
||||
UserInterface/Dialog/InstallUpdateDialog.ui
|
||||
)
|
||||
endif(UPDATER)
|
||||
|
||||
set_source_files_properties(${IMGUI_SOURCES} PROPERTIES GENERATED TRUE)
|
||||
list(APPEND RMG_SOURCES ${IMGUI_SOURCES})
|
||||
|
||||
if (CMAKE_BUILD_TYPE MATCHES Release)
|
||||
add_executable(RMG WIN32 ${RMG_SOURCES})
|
||||
else()
|
||||
add_executable(RMG ${RMG_SOURCES})
|
||||
endif()
|
||||
|
||||
add_dependencies(RMG
|
||||
imgui
|
||||
)
|
||||
|
||||
target_link_libraries(RMG
|
||||
RMG-Core
|
||||
)
|
||||
|
||||
# needed for dynamically linked RMG-Core
|
||||
set_target_properties(RMG PROPERTIES
|
||||
INSTALL_RPATH "$ORIGIN"
|
||||
)
|
||||
|
||||
target_include_directories(RMG PRIVATE
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../
|
||||
${SDL2_INCLUDE_DIRS}
|
||||
${IMGUI_DIR}
|
||||
)
|
||||
|
||||
target_link_libraries(RMG ${QT_VERSION}::Gui ${QT_VERSION}::Widgets ${QT_VERSION}::OpenGL)
|
||||
target_link_libraries(RMG Qt6::Gui Qt6::Widgets Qt6::OpenGL)
|
||||
|
||||
if (UPDATER)
|
||||
target_link_libraries(RMG ${QT_VERSION}::Network)
|
||||
target_link_libraries(RMG Qt6::Network)
|
||||
endif(UPDATER)
|
||||
|
||||
|
||||
@@ -36,11 +36,12 @@ bool CoreCallbacks::Init(void)
|
||||
{
|
||||
// needed for Qt
|
||||
qRegisterMetaType<CoreDebugMessageType>("CoreDebugMessageType");
|
||||
qRegisterMetaType<CoreDebugMessageType>("CoreStateCallbackType");
|
||||
|
||||
this->LoadSettings();
|
||||
|
||||
l_CoreCallbacks = this;
|
||||
return CoreSetupCallbacks(this->coreDebugCallback);
|
||||
return CoreSetupCallbacks(this->coreDebugCallback, this->coreStateCallback);
|
||||
}
|
||||
|
||||
void CoreCallbacks::Stop(void)
|
||||
@@ -66,5 +67,25 @@ void CoreCallbacks::coreDebugCallback(CoreDebugMessageType type, std::string con
|
||||
return;
|
||||
}
|
||||
|
||||
// skip non-fatal interpreter errors
|
||||
if (type == CoreDebugMessageType::Error)
|
||||
{
|
||||
if (message == "DIV_S by 0" ||
|
||||
message == "DIV_D by 0")
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
emit l_CoreCallbacks->OnCoreDebugCallback(type, QString::fromStdString(context), QString::fromStdString(message));
|
||||
}
|
||||
|
||||
void CoreCallbacks::coreStateCallback(CoreStateCallbackType type, int value)
|
||||
{
|
||||
if (l_CoreCallbacks == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
emit l_CoreCallbacks->OnCoreStateCallback(type, value);
|
||||
}
|
||||
|
||||
@@ -31,9 +31,11 @@ public:
|
||||
|
||||
private:
|
||||
static void coreDebugCallback(CoreDebugMessageType type, std::string context, std::string message);
|
||||
static void coreStateCallback(CoreStateCallbackType type, int value);
|
||||
|
||||
signals:
|
||||
void OnCoreDebugCallback(CoreDebugMessageType type, QString context, QString message);
|
||||
void OnCoreStateCallback(CoreStateCallbackType type, int value);
|
||||
};
|
||||
|
||||
#endif // RMG_CALLBACKS_HPP
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
|
||||
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 3.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "OnScreenDisplay.hpp"
|
||||
|
||||
#include <RMG-Core/Core.hpp>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <backends/imgui_impl_opengl3.h>
|
||||
#include <chrono>
|
||||
|
||||
//
|
||||
// Local Variables
|
||||
//
|
||||
|
||||
static bool l_Initialized = false;
|
||||
static bool l_Enabled = false;
|
||||
static bool l_RenderingPaused = false;
|
||||
|
||||
static std::chrono::time_point<std::chrono::high_resolution_clock> l_MessageTime;
|
||||
static std::string l_Message;
|
||||
static int l_MessagePosition = 1;
|
||||
static float l_MessagePaddingX = 20.0f;
|
||||
static float l_MessagePaddingY = 20.0f;
|
||||
static float l_MessageOpacity = 1.0f;
|
||||
static int l_MessageDuration = 3;
|
||||
|
||||
//
|
||||
// Exported Functions
|
||||
//
|
||||
|
||||
bool OnScreenDisplayInit(void)
|
||||
{
|
||||
if (l_Initialized)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
ImGui::StyleColorsDark();
|
||||
|
||||
if (!ImGui_ImplOpenGL3_Init())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
l_Initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void OnScreenDisplayShutdown(void)
|
||||
{
|
||||
if (!l_Initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
|
||||
l_Message = "";
|
||||
l_Initialized = false;
|
||||
l_RenderingPaused = false;
|
||||
}
|
||||
|
||||
void OnScreenDisplayLoadSettings(void)
|
||||
{
|
||||
l_Enabled = CoreSettingsGetBoolValue(SettingsID::GUI_OnScreenDisplayEnabled);
|
||||
l_MessagePosition = CoreSettingsGetIntValue(SettingsID::GUI_OnScreenDisplayLocation);
|
||||
l_MessagePaddingX = CoreSettingsGetIntValue(SettingsID::GUI_OnScreenDisplayPaddingX);
|
||||
l_MessagePaddingY = CoreSettingsGetIntValue(SettingsID::GUI_OnScreenDisplayPaddingY);
|
||||
l_MessageOpacity = CoreSettingsGetFloatValue(SettingsID::GUI_OnScreenDisplayOpacity);
|
||||
l_MessageDuration = CoreSettingsGetIntValue(SettingsID::GUI_OnScreenDisplayDuration);
|
||||
}
|
||||
|
||||
bool OnScreenDisplaySetDisplaySize(int width, int height)
|
||||
{
|
||||
if (!l_Initialized)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.IniFilename = nullptr;
|
||||
io.DisplaySize = ImVec2((float)width, (float)height);
|
||||
return true;
|
||||
}
|
||||
|
||||
void OnScreenDisplaySetMessage(std::string message)
|
||||
{
|
||||
if (!l_Initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
l_Message = message;
|
||||
l_MessageTime = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
void OnScreenDisplayRender(void)
|
||||
{
|
||||
if (!l_Initialized || !l_Enabled || l_RenderingPaused || l_Message.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto currentTime = std::chrono::high_resolution_clock::now();
|
||||
const int secondsPassed = std::chrono::duration_cast<std::chrono::seconds>(currentTime - l_MessageTime).count();
|
||||
if (secondsPassed >= l_MessageDuration)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
ImGui::SetNextWindowBgAlpha(l_MessageOpacity);
|
||||
|
||||
// right bottom = ImGui::SetNextWindowPos(ImVec2(io.DisplaySize.x - 20.0f, io.DisplaySize.y - 20.0f), ImGuiCond_Always, ImVec2(1.0f, 1.0f));
|
||||
// right top = ImGui::SetNextWindowPos(ImVec2(io.DisplaySize.x - 20.0f, 20.0f), ImGuiCond_Always, ImVec2(1.0f, 0));
|
||||
// left bottom = ImGui::SetNextWindowPos(ImVec2(20.0f, io.DisplaySize.y - 20.0f), ImGuiCond_Always, ImVec2(0.0f, 1.0f));
|
||||
// left top = ImGui::SetNextWindowPos(ImVec2(20.0f, 20.0f), ImGuiCond_Always, ImVec2(0.0f, 0.0f));
|
||||
switch (l_MessagePosition)
|
||||
{
|
||||
default:
|
||||
case 0: // left bottom
|
||||
ImGui::SetNextWindowPos(ImVec2(l_MessagePaddingX, io.DisplaySize.y - l_MessagePaddingY), ImGuiCond_Always, ImVec2(0.0f, 1.0f));
|
||||
break;
|
||||
case 1: // left top
|
||||
ImGui::SetNextWindowPos(ImVec2(l_MessagePaddingX, l_MessagePaddingY), ImGuiCond_Always, ImVec2(0.0f, 0.0f));
|
||||
break;
|
||||
case 2: // right top
|
||||
ImGui::SetNextWindowPos(ImVec2(io.DisplaySize.x - l_MessagePaddingX, l_MessagePaddingY), ImGuiCond_Always, ImVec2(1.0f, 0));
|
||||
break;
|
||||
case 3: // right bottom
|
||||
ImGui::SetNextWindowPos(ImVec2(io.DisplaySize.x - l_MessagePaddingX, io.DisplaySize.y - l_MessagePaddingY), ImGuiCond_Always, ImVec2(1.0f, 1.0f));
|
||||
break;
|
||||
}
|
||||
|
||||
ImGui::Begin("Message", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoFocusOnAppearing);
|
||||
ImGui::Text("%s", l_Message.c_str());
|
||||
ImGui::End();
|
||||
|
||||
ImGui::Render();
|
||||
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
}
|
||||
|
||||
void OnScreenDisplayPause(void)
|
||||
{
|
||||
l_RenderingPaused = true;
|
||||
}
|
||||
|
||||
void OnScreenDisplayResume(void)
|
||||
{
|
||||
l_RenderingPaused = false;
|
||||
}
|
||||