mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-13 04:51:38 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de4e56ecf2 | ||
|
|
dc2f6da813 | ||
|
|
06921aebed | ||
|
|
083914aa5c | ||
|
|
80b06c37fe | ||
|
|
d7002641c3 | ||
|
|
4d367a2b59 | ||
|
|
1250ecbc46 | ||
|
|
da15bc2802 | ||
|
|
91bf7b553a | ||
|
|
55b77cb663 | ||
|
|
78e7d23c14 | ||
|
|
b6ae697fad | ||
|
|
75f94950bf | ||
|
|
8b6fe0a934 | ||
|
|
f63c11ebaf | ||
|
|
08c1c05f82 | ||
|
|
6d73de8fa7 | ||
|
|
03ce03eb81 | ||
|
|
af0e49df57 | ||
|
|
d89ab49d32 | ||
|
|
b2ef25e20f | ||
|
|
6fcf291df4 | ||
|
|
60fab1c4a1 | ||
|
|
0f85bbf32d | ||
|
|
59b79e2191 | ||
|
|
eaf2f618b5 | ||
|
|
f3ce04be0c | ||
|
|
b98b9471d9 | ||
|
|
9b00a932d5 | ||
|
|
2f88edb93f | ||
|
|
8fc14354e9 | ||
|
|
d7983f2827 | ||
|
|
8e962e495c | ||
|
|
1aa542be2d | ||
|
|
fb444f9d91 | ||
|
|
d95bc2dd54 | ||
|
|
1e31a8d5fd | ||
|
|
2feca8994d | ||
|
|
28247ba65d | ||
|
|
05b7ac47c3 | ||
|
|
780fdf721e | ||
|
|
47c850c7fd | ||
|
|
c6892d03c3 | ||
|
|
82c69995f0 | ||
|
|
4d2adee166 | ||
|
|
a18e29cc8a | ||
|
|
065fb5acee | ||
|
|
27a6210f96 | ||
|
|
c6e83d23f5 | ||
|
|
72513e29b2 | ||
|
|
6d67c16507 | ||
|
|
910a652b3c | ||
|
|
ea77410037 | ||
|
|
96ddb301b6 | ||
|
|
fc653fad90 | ||
|
|
db74d7c81b | ||
|
|
675fe72ef0 | ||
|
|
e70b3d49b7 | ||
|
|
a404689894 | ||
|
|
646b0f7209 | ||
|
|
5253e4be23 | ||
|
|
4d2178fafc | ||
|
|
492a83e6d5 | ||
|
|
b08314cf60 | ||
|
|
28145abde9 | ||
|
|
6f90b24f72 | ||
|
|
fe943a65e8 | ||
|
|
3a25e1fbc3 | ||
|
|
f3bda542d6 | ||
|
|
32b8560858 | ||
|
|
86ca2006ea | ||
|
|
284fb6baa4 | ||
|
|
1490ad18a2 | ||
|
|
596bc4e686 | ||
|
|
d675276e28 | ||
|
|
c1216fd16d | ||
|
|
d9bc8fb796 | ||
|
|
b4d9d6c5fc | ||
|
|
b094c58081 | ||
|
|
53bd6908e1 | ||
|
|
0ecaa609f9 | ||
|
|
715c054de5 | ||
|
|
c67806dbd7 | ||
|
|
3b08d63b23 | ||
|
|
8a8c2da042 | ||
|
|
249241d7cd | ||
|
|
de22e06862 | ||
|
|
4f62612044 | ||
|
|
9ed51bc528 | ||
|
|
b1f52a25ea | ||
|
|
32594e6643 | ||
|
|
1726d8acb9 | ||
|
|
9bcee062ff | ||
|
|
df553bb7f5 | ||
|
|
56f2e2f7b8 | ||
|
|
01cb10d8b3 | ||
|
|
4d112da58d | ||
|
|
c11a798467 | ||
|
|
460f5e745d | ||
|
|
97c5c269a7 | ||
|
|
4a53c6771c | ||
|
|
35312feab6 | ||
|
|
621f51394b | ||
|
|
4896832612 | ||
|
|
3e4201ed30 | ||
|
|
4c87c307cc | ||
|
|
e8e8404741 | ||
|
|
f6dc1c6d93 | ||
|
|
b74b2f9b87 | ||
|
|
0f2cb88ec6 | ||
|
|
69cf8b1795 | ||
|
|
cfd408db86 | ||
|
|
3b1d75d9cf | ||
|
|
ba0d42ceb3 | ||
|
|
6faf2fe5b9 | ||
|
|
ab65e31e09 | ||
|
|
79e4106cc8 | ||
|
|
e215f56490 | ||
|
|
2c65382d50 | ||
|
|
8739264485 | ||
|
|
e4d62a3b9f | ||
|
|
e137f2c9dc | ||
|
|
8f82bcaad9 | ||
|
|
bc756e31d9 | ||
|
|
977a7d3eb6 | ||
|
|
b697f556e5 | ||
|
|
f0d9add1b2 | ||
|
|
7768c296b9 | ||
|
|
569704fb37 | ||
|
|
ee4486068d | ||
|
|
3d08aa8e32 | ||
|
|
9b0cb0fc92 | ||
|
|
6dec52fda7 | ||
|
|
023f427856 | ||
|
|
bc02f8319e | ||
|
|
3d54d952a5 | ||
|
|
7df18ec59d | ||
|
|
644e7dce7c | ||
|
|
3ae7d19cd5 | ||
|
|
78b25b20dd | ||
|
|
5a3cd8671e | ||
|
|
d2ab5d05ee | ||
|
|
8373ef1a61 | ||
|
|
b2527b43e8 | ||
|
|
bbfcecfab0 | ||
|
|
03edf4f6a9 | ||
|
|
b0928cd0f8 | ||
|
|
f0d8af0d34 | ||
|
|
80e7e1983a | ||
|
|
e76da47b31 | ||
|
|
932c9b9926 | ||
|
|
c816cd2b61 | ||
|
|
a7b7472c34 | ||
|
|
1fe70da946 | ||
|
|
5bacf1e9be | ||
|
|
5d3654ddbb | ||
|
|
0e22b1ec73 | ||
|
|
b8242601ec | ||
|
|
538b58aa7a | ||
|
|
600a3365ea | ||
|
|
19a1dbbc85 | ||
|
|
9ec7cecb93 | ||
|
|
8093f4595c | ||
|
|
a576ee49db | ||
|
|
e71e895ad6 | ||
|
|
c97b40e900 | ||
|
|
ab8e8359a6 | ||
|
|
5d3ebd0ece | ||
|
|
504a6b9670 | ||
|
|
df1f7bffb9 | ||
|
|
2febc16e2e | ||
|
|
5bc2d42cdc | ||
|
|
412c79862c | ||
|
|
e541dd5ab1 | ||
|
|
763fcd8267 | ||
|
|
d43b99cbac | ||
|
|
37a91e1be5 | ||
|
|
42e03cae83 | ||
|
|
6f7332c8fa | ||
|
|
80cf91417c | ||
|
|
b45a192416 | ||
|
|
4bb2c14293 | ||
|
|
592f69ec69 | ||
|
|
a106dedeb6 | ||
|
|
cdc5e33953 | ||
|
|
f8c3c76e18 | ||
|
|
e301fffae3 | ||
|
|
6d5d2e722d | ||
|
|
c092fa33e1 | ||
|
|
900675ad23 | ||
|
|
a953b27614 | ||
|
|
6aa89b3723 | ||
|
|
17ccfe4004 | ||
|
|
cce04bab70 | ||
|
|
ffbbdbd256 | ||
|
|
84052ee7e1 | ||
|
|
e1352060b5 | ||
|
|
a431760829 | ||
|
|
5bd7433ac4 | ||
|
|
60eaf14afc | ||
|
|
0afdb2a0cf | ||
|
|
86cced8eab | ||
|
|
dbe0252db4 | ||
|
|
4132176ece | ||
|
|
cd74221d33 | ||
|
|
d9a4dc52c0 | ||
|
|
8511152738 | ||
|
|
84f558db0b | ||
|
|
fa213a724f | ||
|
|
dfcbde86f7 | ||
|
|
2ecb253ed4 | ||
|
|
3a67162ae2 | ||
|
|
5f2ed6c85a | ||
|
|
86b47b4859 | ||
|
|
17362e3692 | ||
|
|
6ee3c14779 | ||
|
|
a612e7f7cc | ||
|
|
6ae1270e5c | ||
|
|
2c289a6212 | ||
|
|
38e74ee3cf | ||
|
|
4e24fb3d01 | ||
|
|
948ad76b14 | ||
|
|
245101b1a1 | ||
|
|
e2848ad0d5 | ||
|
|
688e00a25b | ||
|
|
49d3a947b8 | ||
|
|
5f6fc20360 |
No files matched your search
@@ -0,0 +1,44 @@
|
||||
name: CI PSP
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
repository_dispatch:
|
||||
types: [run_build]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: git.libretro.com:5050/libretro-infrastructure/libretro-build-psp:latest
|
||||
options: --user root
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Compile bootstrap
|
||||
run: |
|
||||
cd bootstrap/psp1/kernel_functions_prx
|
||||
make -j$(getconf _NPROCESSORS_ONLN) clean
|
||||
make -j$(getconf _NPROCESSORS_ONLN)
|
||||
|
||||
- name: Compile Salamander
|
||||
run: |
|
||||
make -f Makefile.psp1.salamander -j$(getconf _NPROCESSORS_ONLN) clean
|
||||
make -f Makefile.psp1.salamander -j$(getconf _NPROCESSORS_ONLN)
|
||||
|
||||
- name: Compile RA
|
||||
run: |
|
||||
make -f Makefile.psp1 -j$(getconf _NPROCESSORS_ONLN) clean
|
||||
make -f Makefile.psp1 -j$(getconf _NPROCESSORS_ONLN) HAVE_STATIC_DUMMY=1
|
||||
|
||||
- name: Get short SHA
|
||||
id: slug
|
||||
run: echo "::set-output name=sha8::$(echo ${GITHUB_SHA} | cut -c1-8)"
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: RA-PSP-dummy-${{ steps.slug.outputs.sha8 }}
|
||||
path: |
|
||||
kernel_functions.prx
|
||||
EBOOT.PBP
|
||||
@@ -363,6 +363,29 @@ build-retroarch-osx-opengl-x64:
|
||||
XCPROJECT_NAME: RetroArch
|
||||
XCCONFIG: OpenGL_GitLabCI.xcconfig
|
||||
|
||||
build-retroarch-osx-ppc:
|
||||
stage: build
|
||||
artifacts:
|
||||
paths:
|
||||
- ${XCPROJECT_NAME}.zip
|
||||
- retroarch-repo/
|
||||
expire_in: 10 min
|
||||
dependencies: []
|
||||
variables:
|
||||
XCPROJECT_NAME: RetroArch
|
||||
XCCONFIG: OpenGL_GitLabCI.xcconfig
|
||||
tags:
|
||||
- macosx-legacy
|
||||
script:
|
||||
- xcodebuild -target RetroArch -configuration Release -project pkg/apple/RetroArch_PPC.xcodeproj ONLY_ACTIVE_ARCH=NO
|
||||
- pushd pkg/apple/build/Release/
|
||||
- ditto -c -k --sequesterRsrc --keepParent RetroArch.app ${XCPROJECT_NAME}.zip
|
||||
- popd
|
||||
- mv pkg/apple/build/Release/RetroArch.zip ${XCPROJECT_NAME}.zip
|
||||
- mkdir .retroarch-repo
|
||||
- "cp -r ./* .retroarch-repo"
|
||||
- "mv .retroarch-repo/ retroarch-repo/"
|
||||
|
||||
build-retroarch-ios-arm64:
|
||||
extends: .build-retroarch-macos-xcode
|
||||
variables:
|
||||
@@ -795,6 +818,23 @@ build-static-retroarch-dummy-psp:
|
||||
- "cp -f pkg/psp1/ICON0.PNG ${MEDIA_PATH}/${CI_PROJECT_NAME}/pkg"
|
||||
- "cp -f pkg/psp1/PIC1.PNG ${MEDIA_PATH}/${CI_PROJECT_NAME}/pkg"
|
||||
|
||||
build-static-retroarch-djgpp:
|
||||
image: $CI_SERVER_HOST:5050/libretro-infrastructure/libretro-djgpp-build-container:latest
|
||||
stage: prepare-for-static-cores
|
||||
before_script:
|
||||
- export NUMPROC=$(($(nproc)/3))
|
||||
artifacts:
|
||||
paths:
|
||||
- retroarch-precompiled/
|
||||
expire_in: 10 min
|
||||
dependencies: []
|
||||
script:
|
||||
# Allow failure since we don't have a core
|
||||
- "make -f Makefile.dos -j$NUMPROC ||:"
|
||||
- "mkdir .retroarch-precompiled"
|
||||
- "cp -r ./* .retroarch-precompiled/"
|
||||
- "mv .retroarch-precompiled/ retroarch-precompiled/"
|
||||
|
||||
build-static-retroarch-ctr:
|
||||
image: $CI_SERVER_HOST:5050/libretro-infrastructure/libretro-build-devkitpro:latest
|
||||
stage: prepare-for-static-cores
|
||||
|
||||
+117
-17
@@ -1,12 +1,112 @@
|
||||
# Future
|
||||
|
||||
# 1.10.2
|
||||
- 3DS: Add a menu toggle for switching between old and new 3DS speeds, located in the 'Power Management' menu. Enabled by default, hidden on old 3DS devices.
|
||||
- AUDIO/MIXER: Free audio voices properly
|
||||
- CHEEVOS: Update to rcheevos 10.3.3
|
||||
- CHEEVOS: Support for Arduboy
|
||||
- CHEEVOS: Fix tab sequences in rich presence being turned into t character
|
||||
- CHEEVOS: Fix overflow when parsing float value that has more than 9 digits after the decimal
|
||||
- CHEEVOS: Fix memory mapping when disconnect mask breaks a region into multiple blocks
|
||||
- CORES: Enable manual selection of which cores are displayed in the 'Standalone Cores' menu
|
||||
- DATABASE/EXPLORE: Added more categories to the Explore menu
|
||||
- INPUT: Fix analog stick not working with 'Unified Menu Controls'
|
||||
- INPUT/MAPPING: Add 'Manage Remap Files' submenu + automatically save input remaps when closing content
|
||||
- INPUT/MAPPING: Add 'Reset Input Mapping' option to 'Manage Remap Files' menu
|
||||
- INPUT/MAPPING: Fix keyboard device remap nulling
|
||||
- IOS/IOS13+: Support a toolbar that allows toggling of onscreen keyboard and touch mouse
|
||||
- LIBRETRO: RETRO_ENVIRONMENT_SHUTDOWN fix - ensure core is properly unloaded when RETRO_ENVIRONMENT_SHUTDOWN is called
|
||||
- LIBRETRO: RETRO_ENVIRONMENT_SHUTDOWN fix - ensure menu stack is properly flushed when RETRO_ENVIRONMENT_SHUTDOWN is called
|
||||
- LINUX/MALI FBDEV: Fix segfault switching video threaded from quickmenu
|
||||
- LOCALIZATION: Add Czech language support
|
||||
- MMAP: Handle disconnect bits on both sides of len
|
||||
- MIYOO: Improve CPU architecture and model name identification for Miyoo
|
||||
- MENU/SETTINGS: Remove 'Advanced Settings' flag from 'Settings > Core' menu
|
||||
- MENU/MATERIALUI: Add 'Gray Dark + Light' themes
|
||||
- MENU/RGUI: Add 6x10 extended ASCII and Latin Extended A and B fonts. These will enable most Latin alphabets to be displayed in RGUI.
|
||||
- MENU/RGUI: Add 'Gray Dark + Light' themes
|
||||
- MENU/XMB: Add title margin adjustment
|
||||
- MENU/XMB: Vertical fade corrections
|
||||
- MENU/OZONE: The size of the thumbnail bar can now be changed though a new option (Settings->User interface->Appearance) up to double its normal size.
|
||||
- MENU/OZONE: Add 'Gray Dark + Light' themes
|
||||
- MENU/OZONE: Add thumbnail scale option
|
||||
- HOTKEYS: Added hotkey for toggling sync to exact content framerate
|
||||
- HOTKEYS: Prevent log spam when using rewind hotkey with cores that don't support rewind, if rewind functionality itself is disabled
|
||||
- HOTKEYS: Add hotkey for toggling sync to exact content framerate
|
||||
- PS3/PSL1GHT: Add RSX graphics support
|
||||
- PS3/PSL1GHT: Add libco support
|
||||
- PS3/PSL1GHT: Add experimental PSMove support
|
||||
- RS90: Optimise layout of sdl_rs90_video
|
||||
- STEAM: Use native OSK (Onscreen Keyboard) instead of built-in RetroArch version
|
||||
- STEAM: New built-in core DLC downloader
|
||||
- STEAM: Swap OK/Cancel buttons by default
|
||||
- VIDEO/HDR: Removed redundant copy of buffer in HDR mode if the shader has already a HDR format i.e. R10G10B10A2 (updated Vulkan/D3D11/D3D12 drivers)
|
||||
- VIDEO/HDR: Fixed crash when using stock shader and HDR and previous optimisation
|
||||
- WAYLAND: Dynamically load libdecor at runtime
|
||||
- WAYLAND: Fix splash screen when using xdg_toplevel
|
||||
- WAYLAND: SHM anti-collision for the splash screen
|
||||
- WAYLAND: Skip splash screen if window is not ready
|
||||
- WII: Fix find_connection_entry(): needs unsigned int
|
||||
Otherwise the USB gamepad cannot be found, if VID/PID has leading zero. This issue happened with Retrode gamepad adapter
|
||||
- WII: Rework Retrode gamepad implementation to support multi_pad interface
|
||||
- WII: Fix - Unplugging and re-plugging now works again
|
||||
- WII: vWii- Only gamepad 1 is supported, because multi_pad is currently only relevant in the Wii U implementation
|
||||
- WIIU: Implemented the multi_pad interface according to input/connect/connect_wiiugca.c
|
||||
- WIIU: Add Optimize for Gamepad option
|
||||
- WIIU: Fix USB gamepad support
|
||||
|
||||
# 1.10.1
|
||||
- ANDROID: Add `HAVE_LANGEXTRA` back to makefile
|
||||
- ANDROID: Include adaptive launcher icons
|
||||
- ANDROID: Populate external storage devices inside the file browser on Android 11+ devices
|
||||
- CHEEVOS: add mastery placard
|
||||
- CHEEVOS: more description message for missing RetroAchievements credentials
|
||||
- CHEEVOS: prevent occasional infinite wait loading multi-disc game from secondary disc
|
||||
- CHEEVOS: stop load process if unable to retrieve achievement data
|
||||
- CHEEVOS: support for identifying Dreamcast CHDs
|
||||
- CHEEVOS: Updated to 10.3.2
|
||||
- CONTENTLESS CORES: Disable per-game and per-content-directory remaps when running contentless cores
|
||||
- CONTENTLESS CORES: Disable per-game and per-content-directory shader presets
|
||||
- CONTENTLESS CORES: Enable config overrides
|
||||
- CONTENTLESS CORES: Enable runtime logging
|
||||
- CORE INFORMATION: Show core version
|
||||
- CORE UPDATER: Add 'Core System Files Downloader'
|
||||
- CORES/SETTINGS: Add 'Standalone Cores' menu
|
||||
- D3D10/D3D11: Add Vsync swap interval
|
||||
- EMSCRIPTEN: Enable 7zip
|
||||
- GBA: New GBA filter Upscale_240x160-320x240
|
||||
- GONG: Removed now that it's a standalone core
|
||||
- HISTORY/FAVORITES: Fix default core on 'Add to Favorites'
|
||||
- INPUT/WAYLAND: Allow toggling mouse grabs
|
||||
- INPUT/WAYLAND: Release keys and mouse buttons on lost focus
|
||||
- iOS: Support for custom keyboard and touch mouse support, among other iOS 13 features
|
||||
- LIBRETRO: Fix crash when cores using RETRO_ENVIRONMENT_SET_AUDIO_CALLBACK return false from retro_load_game()
|
||||
- LIBRETRO: Add optional frame skipping when fast-forwarding
|
||||
- LOCALIZATION: Fetch translations from Crowdin
|
||||
- LOCALIZATION: OSD fonts for Chinese, Korean, Arabic and Persian
|
||||
- LOCALIZATION: Translation fixes for various languages
|
||||
- MENU/RGUI: Add dynamic theme
|
||||
- MIYOO: Enable NEAREST audio resampler
|
||||
- MIYOO: Support battery level
|
||||
- NETPLAY: Disable savestates on stateless mode
|
||||
- NGC/Wii: Several improvements
|
||||
- OSX: Enable compilation on PPC
|
||||
- OZONE/XMB: Improve efficiency/accuracy of History/Favorites icon rendering
|
||||
- RECORDING/FFMPEG: Fix building against FFmpeg 5.0
|
||||
- RETROARCH INFORMATION: No longer report on enabled Python support, which was removed in RetroArch 1.7.8
|
||||
- SAVESTATES: Disable save states based on save state support level defined in core info files
|
||||
- UNIX/WINDOWS: Allow setting the default libretro_directory via environment variable
|
||||
- WAYLAND/VULKAN: Fix Wayland Vulkan not reacting to initial resize
|
||||
- WINDOWS/WINRAW: Enable mouse overlay control
|
||||
- Xbox/UWP: Fix CHD not working with Cheevos / Add 7zip support.
|
||||
|
||||
# 1.10.0
|
||||
- 3DS: Add Jaxe, A5200 and WASM4 cores
|
||||
- 3DS: Fix rotation
|
||||
- ARCHIVE: Allow loading files from archive subdirectory
|
||||
- AUDIO: Remove frame limit from audio batch callback. Before, if a core used the audio batch callback, there would be a hidden cap of 1024 on the number of audio frames that could be sent. If a core exceeded this value, any excess samples were silently discarded. While this is sufficient for 'normal' samplerates/framerates, it means that e.g. a core using the batch callback to send 44100 Hz audio at 30 fps with would have entirely broken sound. This has been fixed by removing the audio batch frame limit.
|
||||
- AUDIO/RESAMPLER/NEON: Implement sinc kaiser NEON function
|
||||
- CHEEVOS: Reset hardcore default to enabled; show message when trying to load state in hardcore
|
||||
- CHEEVOS: Reset hardcore default to enabled; show message when trying to load state in hardcore
|
||||
- CHEEVOS: Fix memory map conversion
|
||||
- CHEEVOS: Check netplay status when unlocking achievements
|
||||
- CHEEVOS: Support for hashing buffered NDS ROMs
|
||||
@@ -29,7 +129,7 @@
|
||||
- LOGGING: Log font rendering backend only once
|
||||
- HOTKEYS: Added a hotkey toggle for the on-screen technical statistics.
|
||||
- HOTKEYS: Add delay + acceleration to volume hotkeys
|
||||
- MENU: Add option for showing notifications only in menu
|
||||
- MENU: Add option for showing notifications only in menu
|
||||
- MENU/RGUI: Add Finnish to supported languages
|
||||
- MENU/XMB: Optional vertical list item fade
|
||||
- MENU/XMB/OZONE: Category + History/Favorites icons
|
||||
@@ -78,7 +178,7 @@
|
||||
# 1.9.14
|
||||
- ANDROID/PLAYSTORE: Implement MANAGE_EXTERNAL_STORAGE permission
|
||||
- ANDROID/PLAYSTORE: Bump up SDK level to 30 to comply with Play Store policies
|
||||
- AUDIO/MIXER: Increase sample buffer padding
|
||||
- AUDIO/MIXER: Increase sample buffer padding
|
||||
- CHEEVOS: Disallow achievements when spectating netplay
|
||||
- CHEEVOS: Fix need-to-activate achievement logic for non-hardcore
|
||||
- CHEEVOS: Don't queue rewind re-init if already on main thread
|
||||
@@ -87,17 +187,17 @@
|
||||
- CHEEVOS: Validate hashes for secondary discs in multi-disc games
|
||||
- CHEEVOS: Ensure placard is initialized on main thread when game has no achievements
|
||||
- CHEEVOS: Audit achievement settings defaults and visibility
|
||||
- CHEEVOS: Show error message when no password provided
|
||||
- CHEEVOS: Show error message when no password provided
|
||||
- CHEEVOS: Use widget for game loaded achievement progress
|
||||
- CONFIG: Honor config_save_on_exit when Reboot/Shutdown is called
|
||||
- DISK CONTROL: Focus on current content entry in Disk Control append/insert
|
||||
- FRAMEDELAY: Auto Frame Delay Improvements - swap interval handling, D3DX handling, and delay target resets also on core restart. It should now work with high refresh rates and also with Direct3D 10/11/12 drivers
|
||||
- INPUT/GYRO/ACCELEROMETER/ANDROID: Re-enable Gyroscope & Accelerometer when RetroArch resumes or regains focus
|
||||
- INPUT/HID: Fix gamepad disconnect on unrecognized HID device
|
||||
- INPUT/HID: Fix gamepad disconnect on unrecognized HID device
|
||||
- LAKKA: Patch to fix keyboard typing
|
||||
- LAKKA: CD-ROM eject menu item
|
||||
- LAKKA/BLUETOOTH: Add option to remove pairing
|
||||
- LAKKA/SWITCH: Disable rumble gain
|
||||
- LAKKA/BLUETOOTH: Add option to remove pairing
|
||||
- LAKKA/SWITCH: Disable rumble gain
|
||||
- LAKKA/SWITCH: Disable cpu scaling, uses its own CPU governor
|
||||
- LOGGING: Logging cleanups. A bunch of unifications and reformattings (capitalizations, dots, quotes, prefixes etc). Also added a few missing things, such as Run-Ahead error logging and LED interface init logging when it is enabled.
|
||||
- NETPLAY: Networking - should not print country for a local lobby
|
||||
@@ -108,7 +208,7 @@
|
||||
- NETWORK: Make HTTP header parsing case insensitive
|
||||
- NETWORK/UPNP: Fixed memory leaks
|
||||
- NETWORK/UPNP: Added a task_queue_wait to prevent executing two nat tasks at once, so it's also thread safe now
|
||||
- NETWORK/UPNP: Switch to a permanent lease time, but request it to be removed when we do netplay_free. Switch to a permanent lease time, but request it to be removed when we do netplay_free.
|
||||
- NETWORK/UPNP: Switch to a permanent lease time, but request it to be removed when we do netplay_free. Switch to a permanent lease time, but request it to be removed when we do netplay_free.
|
||||
- NETWORK/UPNP: Only use a single interface for UPnP, return on the first one found instead of iterating over all of them and opening them one by one
|
||||
- OVERLAYS: Revert changes
|
||||
- RETROFW: Add OSS audio
|
||||
@@ -395,7 +495,7 @@ Not that SR works on them but to fix RA compile issues
|
||||
# 1.9.5
|
||||
- ALSATHREAD: Make alsathread default for all ALSA devices with threads
|
||||
- ARCHIVE: Fix loading of archived content with file names containing '#' characters
|
||||
- CHEEVOS: Upgrade to rcheevos 10.1
|
||||
- CHEEVOS: Upgrade to rcheevos 10.1
|
||||
- CHEEVOS: Challenge indicators
|
||||
- CHEEVOS: Group achievements by category in quick menu
|
||||
- CHEEVOS: Relabel 'Start Active' with 'Encore Mode'
|
||||
@@ -410,9 +510,9 @@ Not that SR works on them but to fix RA compile issues
|
||||
- INPUT: Allow the 8 analog stick directions to be used as keys for core keyboard mappings
|
||||
- LIBRETRO: Add API extension for setting 'need_fullpath' based on content file extension and to request persistent frontend content data buffers
|
||||
- MENU/SEARCH: Add enhanced search functionality to the 'Manage Cores' menu
|
||||
- OPENDINGUX: Fix black screens when triggering gfx driver initialisation via menu actions
|
||||
- OPENDINGUX: Fix black screens when triggering gfx driver initialisation via menu actions
|
||||
- UNIX: Get better battery stats on sysfs nodes
|
||||
- VIDEO: Extend Frame Delay range to 19 to accommodate PAL land too
|
||||
- VIDEO: Extend Frame Delay range to 19 to accommodate PAL land too
|
||||
- WIFI/LAKKA: Add nmcli to wifi drivers
|
||||
- WIFI/LAKKA: Add wifi configuration menu
|
||||
- X11: fix fullscreen when swapping monitors/resolution
|
||||
@@ -445,7 +545,7 @@ Not that SR works on them but to fix RA compile issues
|
||||
- CORE INFO/FILE IO: Core Info cache; significant file I/O performance improvements on systems with slow disk file I/O
|
||||
- CORE INFO/FILE IO: Enable core info cache by default on all 'console' platforms
|
||||
- FREEBSD: FreeBSD build fix
|
||||
- LAKKA: Support for tweaking CPU governors/scaling policies
|
||||
- LAKKA: Support for tweaking CPU governors/scaling policies
|
||||
- LAKKA: This adds managed policies and settings to store them and reload them at startup
|
||||
- LIBRETRO API: Add API extension for cores to override frontend fast-forward state
|
||||
- MENU/RGUI: Fix saving of config files/overrides when 'Lock Menu Aspect Ratio' is enabled
|
||||
@@ -486,7 +586,7 @@ Not that SR works on them but to fix RA compile issues
|
||||
- MENU: Add optional menu screensaver
|
||||
- MENU: Add search filter support to cheats and overlays file browser menus
|
||||
- MENU/FILEBROWSER: Enhanced 'Load Content' file browser search functionality
|
||||
- MENU/INPUT: Block accidental diagonals in menu navigation
|
||||
- MENU/INPUT: Block accidental diagonals in menu navigation
|
||||
- MENU/RGUI: Add option to disable menu transparency
|
||||
- MENU/RGUI: Fix display of 'Video > Scaling' menu when 'Lock Menu Aspect Ratio' is enabled
|
||||
- MENU/MATERIALUI: Add icon to 'Turbo Fire' menu entry
|
||||
@@ -605,7 +705,7 @@ Having remaps for many different cores makes finding the active core files cumbe
|
||||
- MAC/UNIVERSAL: Add CoreAudio3 audio driver for Metal Universal build
|
||||
- MAC/IOS: Only extract assets once on first install
|
||||
- MENU: Add 'L2 + R2' menu toggle gamepad combo
|
||||
- MENU: Menu text improvements; clarifications, consistency, text mistakes,
|
||||
- MENU: Menu text improvements; clarifications, consistency, text mistakes,
|
||||
- MENU: Tweak menu scroll initial hold delays
|
||||
- MENU: Restrict menu acceleration to navigation buttons
|
||||
- MENU: Add 'Menu Driver' setting to 'User Interface'
|
||||
@@ -822,7 +922,7 @@ save state limit (global). Instead of wrapping around the slot counter it will s
|
||||
- FILE PATH: Various file path handling optimisations
|
||||
- FONT: Fix Arabic, Chinese and Korean font rendering
|
||||
- INPUT MAPPING/REMAPPING: Restore broken 'reset to default' functionality with RetroPad 'start' button
|
||||
- INPUT MAPPING/REMAPPING: Fix 'reset to default' action for analog sticks and undefined core inputs
|
||||
- INPUT MAPPING/REMAPPING: Fix 'reset to default' action for analog sticks and undefined core inputs
|
||||
- LOCALIZATION: Update Arabic translation
|
||||
- LOCALIZATION: Update Chinese (Simplified) translation
|
||||
- LOCALIZATION: Update Chinese (Traditional) translation
|
||||
@@ -1084,7 +1184,7 @@ appear to be required in order for mouse input to work on macOS
|
||||
- VITA: Re-add Online Updater
|
||||
- VULKAN: Fix font driver 'vulkan_get_message_width()' function
|
||||
- VIDEO FILTERS: Only use threads when the number exceeds 1. Fixes race conditions with some CPU filters on Vita
|
||||
- WINDOWS: Add ANGLE support for x64, separate binary (for now?)
|
||||
- WINDOWS: Add ANGLE support for x64, separate binary (for now?)
|
||||
|
||||
# 1.8.2
|
||||
- BUG/CRASH/GLSLANG: Fix glslang crashing error - managed to reproduce an issue which has been plaguing
|
||||
@@ -1261,7 +1361,7 @@ Vulkan, so use the old onContentRectChanged callback to get notified when size c
|
||||
# (1.7.9)
|
||||
- AI SERVICE: Image mode is now much faster, it now saves the image in-memory in PNG format then passes it along to the translation service
|
||||
- BUGFIX: Touch input - When using an overlay to toggle the quick menu on touchscreen devices, we no longer get 'phantom' menu input - i.e. the old bug of hitting the toggle and instantly resuming content (or performing a save state) is fixed
|
||||
- BUGFIX: Networking - RetroArch crashed when pressing left while Relay Server Location entry was selected
|
||||
- BUGFIX: Networking - RetroArch crashed when pressing left while Relay Server Location entry was selected
|
||||
- BUGFIX: Networking - fix memory leak that could happen at exit after a network
|
||||
operation had run
|
||||
- CHEEVOS: Improve handling of line endings when calculating CD hashes for retroachievements
|
||||
|
||||
+17
-11
@@ -339,6 +339,7 @@ endif
|
||||
OBJ += \
|
||||
gfx/drivers_font_renderer/bitmapfont.o \
|
||||
gfx/drivers_font_renderer/bitmapfont_10x10.o \
|
||||
gfx/drivers_font_renderer/bitmapfont_6x10.o \
|
||||
tasks/task_autodetect.o \
|
||||
input/input_autodetect_builtin.o \
|
||||
input/input_keymaps.o \
|
||||
@@ -472,7 +473,8 @@ ifeq ($(HAVE_LANGEXTRA), 1)
|
||||
intl/msg_hash_fi.o \
|
||||
intl/msg_hash_id.o \
|
||||
intl/msg_hash_sv.o \
|
||||
intl/msg_hash_uk.o
|
||||
intl/msg_hash_uk.o \
|
||||
intl/msg_hash_cs.o
|
||||
endif
|
||||
|
||||
ifneq ($(HAVE_GETOPT_LONG), 1)
|
||||
@@ -1088,7 +1090,8 @@ ifeq ($(HAVE_MENU_COMMON), 1)
|
||||
menu/cbs/menu_cbs_label.o \
|
||||
menu/cbs/menu_cbs_sublabel.o \
|
||||
menu/cbs/menu_cbs_title.o \
|
||||
menu/menu_displaylist.o
|
||||
menu/menu_displaylist.o \
|
||||
menu/menu_contentless_cores.o
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_GFX_WIDGETS), 1)
|
||||
@@ -1260,6 +1263,7 @@ ifeq ($(HAVE_WAYLAND), 1)
|
||||
OBJ += gfx/drivers_context/wayland_ctx.o \
|
||||
input/common/wayland_common.o \
|
||||
input/drivers/wayland_input.o \
|
||||
gfx/common/wayland_common.o \
|
||||
gfx/common/wayland/xdg-shell.o \
|
||||
gfx/common/wayland/idle-inhibit-unstable-v1.o \
|
||||
gfx/common/wayland/xdg-decoration-unstable-v1.o
|
||||
@@ -1272,8 +1276,7 @@ ifeq ($(HAVE_WAYLAND), 1)
|
||||
LIBS += $(WAYLAND_LIBS) $(WAYLAND_CURSOR_LIBS)
|
||||
|
||||
ifeq ($(HAVE_LIBDECOR), 1)
|
||||
DEFINES += -DHAVE_LIBDECOR
|
||||
LIBS += $(LIBDECOR_LIBS)
|
||||
DEFINES += -DHAVE_LIBDECOR_H
|
||||
DEF_FLAGS += $(LIBDECOR_CFLAGS)
|
||||
endif
|
||||
|
||||
@@ -2073,12 +2076,6 @@ OBJ += $(LIBRETRO_COMM_DIR)/formats/bmp/rbmp_encode.o \
|
||||
$(LIBRETRO_COMM_DIR)/formats/image_transfer.o \
|
||||
$(LIBRETRO_COMM_DIR)/formats/m3u/m3u_file.o
|
||||
|
||||
# Gong embedded core
|
||||
ifeq ($(HAVE_GONG),1)
|
||||
DEFINES += -DHAVE_GONG
|
||||
OBJ += cores/libretro-gong/gong.o
|
||||
endif
|
||||
|
||||
# Video4Linux 2
|
||||
|
||||
ifeq ($(HAVE_V4L2),1)
|
||||
@@ -2175,6 +2172,7 @@ ifeq ($(HAVE_NETWORKING), 1)
|
||||
deps/rcheevos/src/rcheevos/runtime_progress.o \
|
||||
deps/rcheevos/src/rcheevos/trigger.o \
|
||||
deps/rcheevos/src/rcheevos/value.o \
|
||||
deps/rcheevos/src/rhash/cdreader.o \
|
||||
deps/rcheevos/src/rhash/hash.o \
|
||||
deps/rcheevos/src/rapi/rc_api_common.o \
|
||||
deps/rcheevos/src/rapi/rc_api_runtime.o \
|
||||
@@ -2394,7 +2392,8 @@ ifeq ($(HAVE_STATIC_VIDEO_FILTERS), 1)
|
||||
gfx/video_filters/dot_matrix_4x.o \
|
||||
gfx/video_filters/upscale_1_5x.o \
|
||||
gfx/video_filters/upscale_256x_320x240.o \
|
||||
gfx/video_filters/picoscale_256x_320x240.o
|
||||
gfx/video_filters/picoscale_256x_320x240.o \
|
||||
gfx/video_filters/upscale_240x160_320x240.o
|
||||
endif
|
||||
|
||||
ifeq ($(WANT_IOSUHAX), 1)
|
||||
@@ -2459,6 +2458,13 @@ ifeq ($(ANDROID), 1)
|
||||
OBJ += play_feature_delivery/play_feature_delivery.o
|
||||
endif
|
||||
|
||||
### Steam integration using mist
|
||||
ifeq ($(HAVE_MIST), 1)
|
||||
INCLUDE_DIRS += -I$(MIST_PATH)/include
|
||||
LDFLAGS += -L$(MIST_PATH) -lmist
|
||||
OBJ += steam/steam.o tasks/task_steam.o
|
||||
endif
|
||||
|
||||
##################################
|
||||
### Classic Platform specifics ###
|
||||
###############WIP################
|
||||
|
||||
+217
@@ -0,0 +1,217 @@
|
||||
include version.all
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
TOPDIR ?= $(CURDIR)
|
||||
|
||||
|
||||
DEBUG ?= 0
|
||||
WHOLE_ARCHIVE_LINK = 0
|
||||
GRIFFIN_BUILD = 0
|
||||
|
||||
OBJ :=
|
||||
|
||||
DEFINES := -DHAVE_DJGPP=1 -U__linux__ -U__linux -DRARCH_INTERNAL -DHAVE_STB_VORBIS
|
||||
CC := i586-pc-msdosdjgpp-gcc
|
||||
CXX := i586-pc-msdosdjgpp-g++
|
||||
TARGET := retrodos.exe
|
||||
|
||||
RARCH_CONSOLE = 0
|
||||
HAVE_SHADERPIPELINE = 1
|
||||
HAVE_SPIRV_CROSS = 0
|
||||
HAVE_PATCH = 1
|
||||
HAVE_LUA = 0
|
||||
HAVE_CONFIGFILE = 1
|
||||
HAVE_GFX_WIDGETS = 1
|
||||
HAVE_LANGEXTRA = 1
|
||||
HAVE_DR_MP3 = 1
|
||||
HAVE_TRANSLATE = 0
|
||||
HAVE_SCREENSHOTS = 1
|
||||
HAVE_REWIND = 1
|
||||
HAVE_AUDIOMIXER = 1
|
||||
HAVE_CC_RESAMPLER = 1
|
||||
HAVE_MENU_COMMON = 1
|
||||
HAVE_RTGA = 1
|
||||
HAVE_RPNG = 1
|
||||
HAVE_RJPEG = 1
|
||||
HAVE_RBMP = 1
|
||||
HAVE_7ZIP = 1
|
||||
HAVE_ZLIB = 1
|
||||
HAVE_IMAGEVIEWER = 1
|
||||
HAVE_BUILTINZLIB = 1
|
||||
HAVE_LIBRETRODB = 1
|
||||
HAVE_DSP_FILTER = 1
|
||||
HAVE_VIDEO_FILTER = 1
|
||||
HAVE_STATIC_VIDEO_FILTERS = 1
|
||||
HAVE_STATIC_AUDIO_FILTERS = 1
|
||||
HAVE_FILTERS_BUILTIN = 1
|
||||
HAVE_MENU = 1
|
||||
HAVE_CONFIGFILE = 1
|
||||
HAVE_PATCH = 1
|
||||
HAVE_CHEATS = 1
|
||||
HAVE_RUNAHEAD = 1
|
||||
HAVE_NETWORKING = 0
|
||||
HAVE_NETPLAYDISCOVERY = 1
|
||||
HAVE_OVERLAY := 1
|
||||
HAVE_STB_FONT = 1
|
||||
HAVE_COMMAND := 1
|
||||
HAVE_STDIN_CMD := 1
|
||||
HAVE_CMD := 1
|
||||
HAVE_CHEEVOS = 0
|
||||
HAVE_CHD = 0 # disabled due to static libretro-common and libchdr conflicts between different cores
|
||||
HAVE_STB_VORBIS = 1
|
||||
HAVE_IBXM = 1
|
||||
HAVE_CORE_INFO_CACHE = 1
|
||||
|
||||
HAVE_RGUI = 1
|
||||
HAVE_MATERIALUI = 0
|
||||
HAVE_XMB = 0
|
||||
HAVE_OZONE = 0
|
||||
|
||||
|
||||
OBJ := \
|
||||
frontend/drivers/platform_dos.o \
|
||||
gfx/drivers/vga_gfx.o gfx/drivers_font/vga_font.o \
|
||||
input/drivers/dos_input.o input/drivers_joypad/dos_joypad.o
|
||||
|
||||
include Makefile.common
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# TARGET is the name of the output
|
||||
# BUILD is the directory where object files & intermediate files will be placed
|
||||
# SOURCES is a list of directories containing source code
|
||||
# DATA is a list of directories containing data files
|
||||
# INCLUDES is a list of directories containing header files
|
||||
# EXEFS_SRC is the optional input directory containing data copied into exefs, if anything this normally should only contain "main.npdm".
|
||||
# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional)
|
||||
#
|
||||
# NO_ICON: if set to anything, do not use icon.
|
||||
# NO_NACP: if set to anything, no .nacp file is generated.
|
||||
# APP_TITLE is the name of the app stored in the .nacp file (Optional)
|
||||
# APP_AUTHOR is the author of the app stored in the .nacp file (Optional)
|
||||
# APP_VERSION is the version of the app stored in the .nacp file (Optional)
|
||||
# APP_TITLEID is the titleID of the app stored in the .nacp file (Optional)
|
||||
# ICON is the filename of the icon (.jpg), relative to the project folder.
|
||||
# If not set, it attempts to use one of the following (in this order):
|
||||
# - <Project name>.jpg
|
||||
# - icon.jpg
|
||||
# - <libnx folder>/default_icon.jpg
|
||||
#---------------------------------------------------------------------------------
|
||||
BUILD := build
|
||||
SOURCES := $(CURDIR)/source
|
||||
DATA := data
|
||||
INCLUDES := include
|
||||
EXEFS_SRC := exefs_src
|
||||
|
||||
APP_TITLE := RetroArch
|
||||
APP_VERSION := $(RARCH_VERSION)
|
||||
APP_AUTHOR := libretro Team
|
||||
APP_ICON := pkg/libnx/retroarch.jpg
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
ARCH :=
|
||||
|
||||
CFLAGS := -g -Wall -O3 -fcommon -ffast-math -ffunction-sections \
|
||||
$(ARCH) $(DEFINES) $(INCLUDE_DIRS)
|
||||
|
||||
CFLAGS += $(INCLUDE)
|
||||
|
||||
ifeq ($(HAVE_FILTERS_BUILTIN), 1)
|
||||
CFLAGS += -DHAVE_FILTERS_BUILTIN
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(HAVE_STATIC_DUMMY)),1)
|
||||
CFLAGS += -DHAVE_STATIC_DUMMY=1
|
||||
endif
|
||||
|
||||
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
|
||||
|
||||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = $(ARCH) -Wl,--allow-multiple-definition -Wl,-Map,$(notdir $*.map)
|
||||
|
||||
# add things from Makefile.common
|
||||
CFLAGS += $(DEF_FLAGS)
|
||||
|
||||
LIBS := -lstdc++ -lm
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#---------------------------------------------------------------------------------
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OUTPUT := $(TARGET)
|
||||
export TOPDIR := $(CURDIR)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||
|
||||
export DEPSDIR := $(CURDIR)/
|
||||
|
||||
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||
|
||||
export OFILES := $(OBJ)
|
||||
|
||||
ifeq ($(strip $(HAVE_STATIC_DUMMY)),)
|
||||
OFILES += libretro_libnx.a
|
||||
endif
|
||||
|
||||
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
|
||||
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC)
|
||||
|
||||
ifneq ($(APP_TITLEID),)
|
||||
export NACPFLAGS += --titleid=$(APP_TITLEID)
|
||||
endif
|
||||
|
||||
ifneq ($(ROMFS),)
|
||||
export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
|
||||
endif
|
||||
|
||||
DEPENDS_TMP := $(OFILES:.o=.d)
|
||||
DEPENDS := $(filter-out libretro_libnx.a,$(DEPENDS_TMP))
|
||||
|
||||
.PHONY: clean all
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
all : $(OUTPUT)
|
||||
|
||||
$(OUTPUT): $(OBJ)
|
||||
$(CXX) -o $@ $(LDFLAGS) $(LIBDIRS) $(OBJ) $(PLATEXTRA) -L. -lretro_dos $(LIBS)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) -c -o $@ $(CFLAGS) $<
|
||||
|
||||
%.o: %.cpp
|
||||
$(CXX) -c -o $@ $(CFLAGS) $<
|
||||
|
||||
clean:
|
||||
rm -f $(DEPENDS) $(OBJ) $(OUTPUT)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# you need a rule like this for each extension you use as binary data
|
||||
#---------------------------------------------------------------------------------
|
||||
%.bin.o %_bin.h : %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
||||
@@ -43,6 +43,7 @@ HAVE_CONFIGFILE = 1
|
||||
HAVE_CHEATS = 1
|
||||
HAVE_IBXM = 1
|
||||
HAVE_CORE_INFO_CACHE = 1
|
||||
HAVE_7ZIP = 1
|
||||
|
||||
ASYNC ?= 0
|
||||
ifeq ($(LIBRETRO), mupen64plus)
|
||||
|
||||
+10
-46
@@ -66,7 +66,6 @@ SHADER_CG_DIR := media/shaders_cg
|
||||
OVERLAY_DIR := media/overlays
|
||||
SHADER_GLSL_DIR := media/shaders_glsl
|
||||
|
||||
PYTHON2 := python2$(EXE_EXT)
|
||||
PYTHON3 := python3$(EXE_EXT)
|
||||
GIT := git$(EXE_EXT)
|
||||
|
||||
@@ -122,14 +121,20 @@ else ifeq ($(libogc_platform), 1)
|
||||
CXX = $(DEVKITPPC)/bin/powerpc-eabi-g++$(EXE_EXT)
|
||||
LD = $(DEVKITPPC)/bin/powerpc-eabi-ld$(EXE_EXT)
|
||||
ELF2DOL = $(DEVKITPPC)/bin/elf2dol$(EXE_EXT)
|
||||
# Check whether ELF2DOL executable exists
|
||||
# (path has changed in more recent toolchains)
|
||||
ifeq ("$(wildcard $(ELF2DOL))","")
|
||||
ELF2DOL = $(DEVKITPRO)/tools/bin/elf2dol$(EXE_EXT)
|
||||
endif
|
||||
EXT_TARGET := $(TARGET_NAME)_$(platform).dol
|
||||
EXT_INTER_TARGET := $(TARGET_NAME)_$(platform).elf
|
||||
|
||||
INCLUDE += -I. -I$(DEVKITPRO)/libogc/include -Iwii/libogc/include
|
||||
INCLUDE += -I.
|
||||
|
||||
ifeq ($(EXTERNAL_LIBOGC), 1)
|
||||
CFLAGS += -DEXTERNAL_LIBOGC
|
||||
CXXFLAGS += -DEXTERNAL_LIBOGC
|
||||
INCLUDE += -I$(DEVKITPRO)/libogc/include
|
||||
|
||||
ifeq ($(platform), ngc)
|
||||
LIBDIRS += -L$(DEVKITPRO)/libogc/lib/cube
|
||||
@@ -140,6 +145,7 @@ else ifeq ($(libogc_platform), 1)
|
||||
else
|
||||
CFLAGS += -DINTERNAL_LIBOGC
|
||||
CXXFLAGS += -DINTERNAL_LIBOGC
|
||||
INCLUDE += -Iwii/libogc/include
|
||||
|
||||
ifeq ($(platform), ngc)
|
||||
LIBDIRS += -Lwii/libogc/libs/cube
|
||||
@@ -935,7 +941,7 @@ else ifeq (qnx,$(platform))
|
||||
LDFLAGS += -g
|
||||
endif
|
||||
|
||||
PLATCFLAGS += -DHAVE_SHADERPIPELINE -DHAVE_OPENGL -DHAVE_OPENGLES -DHAVE_OPENGLES2 -DHAVE_OZONE -DHAVE_CC_RESAMPLER -DHAVE_CHEEVOS -DRC_DISABLE_LUA -DHAVE_FBO -DHAVE_GL_SYNC -DHAVE_GLSLANG -DHAVE_BUILTINGLSLANG -DHAVE_IMAGEVIEWER -DHAVE_LANGEXTRA -DHAVE_RUNAHEAD -DHAVE_GFX_WIDGETS -DHAVE_CONFIGFILE -DHAVE_PATCH -DHAVE_SPIRV_CROSS -DHAVE_STB_FONT -DHAVE_ONLINE_UPDATER -DHAVE_UPDATE_ASSETS -DHAVE_UPDATE_CORES -DHAVE_XMB -DRARCH_INTERNAL -DWANT_GLSLANG -DHAVE_XCB -DHAVE_EGL -DHAVE_BB10 -DHAVE_GLSL -DHAVE_AL -DHAVE_BSV_MOVIE -DRARCH_MOBILE
|
||||
PLATCFLAGS += -DHAVE_SHADERPIPELINE -DHAVE_OPENGL -DHAVE_OPENGLES -DHAVE_OPENGLES2 -DHAVE_OZONE -DHAVE_CC_RESAMPLER -DHAVE_CHEEVOS -DRC_DISABLE_LUA -DHAVE_GL_SYNC -DHAVE_GLSLANG -DHAVE_BUILTINGLSLANG -DHAVE_IMAGEVIEWER -DHAVE_LANGEXTRA -DHAVE_RUNAHEAD -DHAVE_GFX_WIDGETS -DHAVE_CONFIGFILE -DHAVE_PATCH -DHAVE_SPIRV_CROSS -DHAVE_STB_FONT -DHAVE_ONLINE_UPDATER -DHAVE_UPDATE_ASSETS -DHAVE_UPDATE_CORES -DHAVE_XMB -DRARCH_INTERNAL -DWANT_GLSLANG -DHAVE_XCB -DHAVE_EGL -DHAVE_BB10 -DHAVE_GLSL -DHAVE_AL -DHAVE_BSV_MOVIE -DRARCH_MOBILE
|
||||
EXT_TARGET := $(TARGET_NAME)
|
||||
EXT_INTER_TARGET := $(TARGET_NAME)
|
||||
INCLUDE += -Ilibretro-common/include -Igfx/include -Ideps -Ideps/stb -Ideps/rcheevos/include -Ideps/SPIRV-Cross -Ideps/glslang -I.
|
||||
@@ -973,53 +979,11 @@ else ifneq (,$(findstring unix,$(platform)))
|
||||
HAVE_CHEATS := 1
|
||||
HAVE_CORE_INFO_CACHE := 1
|
||||
|
||||
PLATCFLAGS += -D__MMX__ -D__SSE__ -DHAVE_OPENGL -DHAVE_OPENGL1 -DHAVE_GLSL -DHAVE_SHADERPIPELINE -DHAVE_OZONE -DHAVE_CC_RESAMPLER -DHAVE_CHEEVOS -DRC_DISABLE_LUA -DHAVE_FBO -DHAVE_GL_SYNC -DHAVE_SLANG -DHAVE_GLSLANG -DHAVE_BUILTINGLSLANG -DHAVE_IMAGEVIEWER -DHAVE_LANGEXTRA -DHAVE_RUNAHEAD -DHAVE_GFX_WIDGETS -DHAVE_CONFIGFILE -DHAVE_PATCH -DHAVE_SPIRV_CROSS -DHAVE_STB_FONT -DHAVE_ONLINE_UPDATER -DHAVE_UPDATE_ASSETS -DHAVE_UPDATE_CORES -DHAVE_VULKAN -DHAVE_XMB -DRARCH_INTERNAL -DWANT_GLSLANG -DHAVE_X11 -DHAVE_XCB -DHAVE_UDEV -DHAVE_BSV_MOVIE -DHAVE_PULSE
|
||||
PLATCFLAGS += -D__MMX__ -D__SSE__ -DHAVE_OPENGL -DHAVE_OPENGL1 -DHAVE_GLSL -DHAVE_SHADERPIPELINE -DHAVE_OZONE -DHAVE_CC_RESAMPLER -DHAVE_CHEEVOS -DRC_DISABLE_LUA -DHAVE_GL_SYNC -DHAVE_SLANG -DHAVE_GLSLANG -DHAVE_BUILTINGLSLANG -DHAVE_IMAGEVIEWER -DHAVE_LANGEXTRA -DHAVE_RUNAHEAD -DHAVE_GFX_WIDGETS -DHAVE_CONFIGFILE -DHAVE_PATCH -DHAVE_SPIRV_CROSS -DHAVE_STB_FONT -DHAVE_ONLINE_UPDATER -DHAVE_UPDATE_ASSETS -DHAVE_UPDATE_CORES -DHAVE_VULKAN -DHAVE_XMB -DRARCH_INTERNAL -DWANT_GLSLANG -DHAVE_X11 -DHAVE_XCB -DHAVE_UDEV -DHAVE_BSV_MOVIE -DHAVE_PULSE
|
||||
EXT_TARGET := $(TARGET_NAME)
|
||||
EXT_INTER_TARGET := $(TARGET_NAME)
|
||||
INCLUDE += -Ilibretro-common/include -Igfx/include -Ideps -Ideps/stb -Ideps/rcheevos/include -Ideps/SPIRV-Cross -Ideps/glslang -I.
|
||||
LIBS += -ldl -lm -lpthread -lGL -ludev -lpulse -lX11 -lX11-xcb -lXxf86vm
|
||||
else ifeq (dos,$(platform))
|
||||
HAVE_RPNG := 1
|
||||
HAVE_RJPEG := 1
|
||||
HAVE_RBMP := 1
|
||||
HAVE_RTGA := 1
|
||||
HAVE_ZLIB := 1
|
||||
HAVE_7ZIP := 1
|
||||
HAVE_NETWORKING := 0
|
||||
HAVE_NETWORK_CMD := 0
|
||||
HAVE_NETPLAYDISCOVERY := 0
|
||||
HAVE_OVERLAY := 1
|
||||
HAVE_VIDEO_LAYOUT := 0
|
||||
HAVE_MATERIALUI := 0
|
||||
HAVE_XMB := 0
|
||||
HAVE_STB_FONT := 1
|
||||
HAVE_THREADS := 0
|
||||
HAVE_LIBRETRODB := 1
|
||||
HAVE_COMMAND := 1
|
||||
HAVE_STDIN_CMD := 1
|
||||
HAVE_CMD := 1
|
||||
HAVE_DYLIB := 0
|
||||
HAVE_DYNAMIC := 0
|
||||
HAVE_GRIFFIN_CPP := 0
|
||||
WANT_GLSLANG := 0
|
||||
HAVE_CONFIGFILE := 1
|
||||
HAVE_PATCH := 0
|
||||
HAVE_CHEATS := 0
|
||||
HAVE_CORE_INFO_CACHE := 1
|
||||
CC=i586-pc-msdosdjgpp-gcc
|
||||
CXX=i586-pc-msdosdjgpp-g++
|
||||
|
||||
ifeq ($(DEBUG), 1)
|
||||
LDFLAGS += -g
|
||||
endif
|
||||
|
||||
PLATCFLAGS += -DHAVE_SHADERPIPELINE -DHAVE_CC_RESAMPLER -DRC_DISABLE_LUA -DHAVE_FBO -DHAVE_IMAGEVIEWER -DHAVE_LANGEXTRA -DHAVE_GFX_WIDGETS -DHAVE_CONFIGFILE -DHAVE_PATCH -DHAVE_SPIRV_CROSS -DHAVE_STB_FONT -DRARCH_INTERNAL -DHAVE_XCB
|
||||
TARGET_NAME := retrodos
|
||||
EXT_TARGET := $(TARGET_NAME).exe
|
||||
EXT_INTER_TARGET := $(TARGET_NAME).exe
|
||||
INCLUDE += -Ilibretro-common/include -Igfx/include -Ideps -Ideps/stb -Ideps/SPIRV-Cross -Ideps/glslang -I.
|
||||
LIBS += -lm
|
||||
LIBDIRS += -L.
|
||||
endif
|
||||
|
||||
ifneq (,$(findstring msvc,$(platform)))
|
||||
|
||||
+1
-1
@@ -111,7 +111,7 @@ HAVE_CHEEVOS = 0
|
||||
HAVE_LIBSHAKE = 0
|
||||
HAVE_CORE_INFO_CACHE = 1
|
||||
#HAVE_TINYALSA = 1
|
||||
#HAVE_NEAREST_RESAMPLER = 1
|
||||
HAVE_NEAREST_RESAMPLER = 1
|
||||
|
||||
OS = Linux
|
||||
TARGET = retroarch
|
||||
|
||||
+20
-125
@@ -63,16 +63,18 @@ ifeq ("$(wildcard $(ELF2DOL))","")
|
||||
ELF2DOL = $(DEVKITPRO)/tools/bin/elf2dol$(EXE_EXT)
|
||||
endif
|
||||
|
||||
INCLUDE += -I. -I$(DEVKITPRO)/libogc/include -Iwii/libogc/include
|
||||
INCLUDE += -I.
|
||||
|
||||
ifeq ($(EXTERNAL_LIBOGC), 1)
|
||||
CFLAGS += -DEXTERNAL_LIBOGC
|
||||
CXXFLAGS += -DEXTERNAL_LIBOGC
|
||||
LIBDIRS += -L$(DEVKITPRO)/libogc/lib/cube
|
||||
INCLUDE += -I$(DEVKITPRO)/libogc/include
|
||||
else
|
||||
CFLAGS += -DINTERNAL_LIBOGC
|
||||
CXXFLAGS += -DINTERNAL_LIBOGC
|
||||
LIBDIRS += -Lwii/libogc/libs/cube
|
||||
INCLUDE += -Iwii/libogc/include
|
||||
endif
|
||||
|
||||
ifeq ($(GX_PTHREAD_LEGACY), 1)
|
||||
@@ -127,18 +129,31 @@ HAVE_RWAV := 1
|
||||
RARCH_CONSOLE = 1
|
||||
HAVE_CHEATS := 1
|
||||
HAVE_CORE_INFO_CACHE := 1
|
||||
HAVE_MENU_COMMON := 1
|
||||
HAVE_MENU := 1
|
||||
HAVE_BUILTINZLIB := 1
|
||||
HAVE_STATIC_AUDIO_FILTERS := 1
|
||||
HAVE_STATIC_VIDEO_FILTERS := 1
|
||||
HAVE_XMB := 0
|
||||
HAVE_OZONE := 0
|
||||
HAVE_RGUI := 1
|
||||
HAVE_MATERIALUI := 0
|
||||
|
||||
CFLAGS += -Wall -std=gnu99 $(MACHDEP) $(PLATCFLAGS) $(INCLUDE)
|
||||
INCLUDE += -I./libretro-common/include \
|
||||
-Ideps \
|
||||
-Ideps/stb
|
||||
CFLAGS += -Wall -std=gnu99 $(MACHDEP) $(PLATCFLAGS) $(INCLUDE)
|
||||
|
||||
OBJ = griffin/griffin.o $(PLATOBJS)
|
||||
OBJ = $(PLATOBJS) audio/drivers/gx_audio.o gfx/drivers/gx_gfx.o input/drivers/gx_input.o frontend/drivers/platform_gx.o input/drivers_joypad/gx_joypad.o
|
||||
|
||||
ifeq ($(HAVE_GRIFFIN_CPP), 1)
|
||||
OBJ += griffin/griffin_cpp.o
|
||||
ifeq ($(EXTERNAL_LIBOGC), 0)
|
||||
OBJ += wii/libogc/libfat/libfat.o wii/libogc/libfat/fatfile.o wii/libogc/libfat/file_allocation_table.o wii/libogc/libfat/cache.o wii/libogc/libfat/fatdir.o wii/libogc/libfat/partition.o wii/libogc/libfat/directory.o wii/libogc/libfat/filetime.o wii/libogc/libfat/disc.o
|
||||
endif
|
||||
|
||||
include Makefile.common
|
||||
|
||||
CFLAGS += $(DEFINES)
|
||||
|
||||
ifeq ($(WANT_GLSLANG), 1)
|
||||
OBJ += griffin/griffin_glslang.o
|
||||
endif
|
||||
@@ -156,18 +171,10 @@ ifeq ($(HAVE_WIIUSB_HID), 1)
|
||||
CFLAGS += -DHAVE_WIIUSB_HID -DHAVE_HID
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_LIBRETRODB), 1)
|
||||
CFLAGS += -DHAVE_LIBRETRODB
|
||||
endif
|
||||
|
||||
ifeq ($(BIG_STACK),1)
|
||||
CFLAGS += -DBIG_STACK
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_RJPEG), 1)
|
||||
CFLAGS += -DHAVE_RJPEG
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_FILE_LOGGER), 1)
|
||||
CFLAGS += -DHAVE_FILE_LOGGER
|
||||
endif
|
||||
@@ -181,66 +188,10 @@ ifeq ($(HAVE_ZLIB), 1)
|
||||
CFLAGS += -I./libretro-common/include/compat/zlib
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_RPNG), 1)
|
||||
CFLAGS += -DHAVE_RPNG
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_RBMP), 1)
|
||||
CFLAGS += -DHAVE_RBMP
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_RTGA), 1)
|
||||
CFLAGS += -DHAVE_RTGA
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_IMAGEVIEWER), 1)
|
||||
CFLAGS += -DHAVE_IMAGEVIEWER
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_BSV_MOVIE), 1)
|
||||
CFLAGS += -DHAVE_BSV_MOVIE
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_RUNAHEAD), 1)
|
||||
CFLAGS += -DHAVE_RUNAHEAD
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_7ZIP), 1)
|
||||
CFLAGS += -DHAVE_7ZIP -D_7ZIP_ST
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_SCREENSHOTS), 1)
|
||||
CFLAGS += -DHAVE_SCREENSHOTS
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_REWIND), 1)
|
||||
CFLAGS += -DHAVE_REWIND
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_AUDIOMIXER), 1)
|
||||
CFLAGS += -DHAVE_AUDIOMIXER
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_RWAV), 1)
|
||||
CFLAGS += -DHAVE_RWAV
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_OVERLAY), 1)
|
||||
CFLAGS += -DHAVE_OVERLAY
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_VIDEO_LAYOUT), 1)
|
||||
CFLAGS += -DHAVE_VIDEO_LAYOUT
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_NETWORKING), 1)
|
||||
CFLAGS += -DHAVE_NETWORKING
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_NETPLAYDISCOVERY), 1)
|
||||
CFLAGS += -DHAVE_NETPLAYDISCOVERY
|
||||
endif
|
||||
|
||||
ifeq ($(RARCH_CONSOLE), 1)
|
||||
CFLAGS += -DRARCH_CONSOLE
|
||||
endif
|
||||
@@ -251,50 +202,10 @@ endif
|
||||
|
||||
CFLAGS += -std=gnu99 -DHAVE_RGUI -DHAVE_MENU -DHAVE_GRIFFIN=1 -Wno-char-subscripts -DRARCH_INTERNAL
|
||||
|
||||
ifeq ($(HAVE_MATERIALUI), 1)
|
||||
CFLAGS += -DHAVE_MATERIALUI
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_XMB), 1)
|
||||
CFLAGS += -DHAVE_XMB
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_STB_FONT), 1)
|
||||
CFLAGS += -DHAVE_STB_FONT
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_LANGEXTRA), 1)
|
||||
CFLAGS += -DHAVE_LANGEXTRA
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_DSP_FILTER), 1)
|
||||
CFLAGS += -DHAVE_DSP_FILTER
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_VIDEO_FILTER), 1)
|
||||
CFLAGS += -DHAVE_VIDEO_FILTER
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_FILTERS_BUILTIN), 1)
|
||||
CFLAGS += -DHAVE_FILTERS_BUILTIN
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_THREADS), 1)
|
||||
CFLAGS += -DHAVE_THREADS
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_CONFIGFILE), 1)
|
||||
CFLAGS += -DHAVE_CONFIGFILE
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_PATCH), 1)
|
||||
CFLAGS += -DHAVE_PATCH
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_CHEATS), 1)
|
||||
CFLAGS += -DHAVE_CHEATS
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_RSOUND), 1)
|
||||
CFLAGS += -DHAVE_RSOUND
|
||||
endif
|
||||
@@ -303,30 +214,14 @@ ifeq ($(HAVE_GETOPT_LONG), 1)
|
||||
CFLAGS += -DHAVE_GETOPT_LONG=1
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_DYLIB), 1)
|
||||
CFLAGS += -DHAVE_DYLIB
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_NETWORK_CMD), 1)
|
||||
CFLAGS += -DHAVE_NETWORK_CMD
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_COMMAND), 1)
|
||||
CFLAGS += -DHAVE_COMMAND
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_STDIN_CMD), 1)
|
||||
CFLAGS += -DHAVE_STDIN_CMD
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_DYNAMIC), 1)
|
||||
CFLAGS += -DHAVE_DYNAMIC
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_CORE_INFO_CACHE), 1)
|
||||
CFLAGS += -DHAVE_CORE_INFO_CACHE
|
||||
endif
|
||||
|
||||
ifeq ($(DEBUG), 1)
|
||||
CFLAGS += -O0 -g -DDEBUG
|
||||
else
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ pnd: retroarch.pnd
|
||||
install: all $(BINDIR)/retroarch-joyconfig $(BINDIR)/retroarch-zip $(PNDDIR)/readme.html
|
||||
|
||||
retroarch:
|
||||
./configure --prefix=$PND_BASEDIR/$PRJ --disable-ffmpeg --disable-cg --disable-python --disable-libpng --disable-pulse --disable-jack --enable-gles --enable-xml
|
||||
./configure --prefix=$PND_BASEDIR/$PRJ --disable-ffmpeg --disable-cg --disable-pulse --disable-jack --enable-opengles
|
||||
make -f Makefile
|
||||
|
||||
$(BINDIR)/retroarch: retroarch
|
||||
|
||||
+5
-2
@@ -31,11 +31,11 @@ endif
|
||||
|
||||
ifeq ($(SCREEN_DEBUG), 1)
|
||||
LIBS += -ldebug
|
||||
RARCH_DEFINES += -DSCREEN_DEBUG
|
||||
DEFINES += -DSCREEN_DEBUG
|
||||
endif
|
||||
|
||||
ifeq ($(BUILD_FOR_PCSX2), 1)
|
||||
RARCH_DEFINES += -DBUILD_FOR_PCSX2
|
||||
DEFINES += -DBUILD_FOR_PCSX2
|
||||
endif
|
||||
|
||||
DEFINES += -DRARCH_INTERNAL -DRARCH_CONSOLE
|
||||
@@ -57,6 +57,9 @@ else
|
||||
HAVE_PATCH = 1
|
||||
HAVE_CHEATS = 1
|
||||
HAVE_RGUI = 1
|
||||
HAVE_MATERIALUI = 0
|
||||
HAVE_XMB = 0
|
||||
HAVE_OZONE = 0
|
||||
HAVE_ZLIB = 1
|
||||
HAVE_NO_BUILTINZLIB = 1
|
||||
HAVE_7ZIP = 1
|
||||
|
||||
+9
-2
@@ -33,7 +33,7 @@ LIBDIRS += -L.
|
||||
MACHDEP := -D__PSL1GHT__ -D__PS3__ -mcpu=cell
|
||||
CFLAGS += -Wall $(MACHDEP) $(INCLUDE)
|
||||
LDFLAGS := $(MACHDEP)
|
||||
LIBS := -lretro_psl1ght -laudio -lrsx -lgcm_sys -lnet -lio -lsysutil -lsysmodule -lm -ljpgdec -lpngdec -llv2 -lnet -lnetctl -lsysfs
|
||||
LIBS := -lretro_psl1ght -laudio -lrsx -lgcm_sys -lnet -lio -lsysutil -lsysmodule -lm -ljpgdec -lpngdec -llv2 -lnet -lnetctl -lsysfs -lfreetype -lcamera -lgem -lspurs
|
||||
|
||||
# system platform
|
||||
system_platform = unix
|
||||
@@ -53,7 +53,12 @@ else
|
||||
GIT = git.exe
|
||||
endif
|
||||
|
||||
OBJ = griffin/griffin.o
|
||||
SHADER_OBJS = gfx/drivers/rsx_shaders/vpshader_basic.vpo.o \
|
||||
gfx/drivers/rsx_shaders/fpshader_basic.fpo.o
|
||||
|
||||
LIBCO_OBJ = libretro-common/libco/ps3.o
|
||||
|
||||
OBJ = $(SHADER_OBJS) $(LIBCO_OBJ) griffin/griffin.o
|
||||
|
||||
ifeq ($(HAVE_LOGGER), 1)
|
||||
CFLAGS += -DHAVE_LOGGER
|
||||
@@ -92,7 +97,9 @@ SHARED_FLAGS += -DHAVE_MENU \
|
||||
-DHAVE_SCREENSHOTS \
|
||||
-DHAVE_REWIND \
|
||||
-DHAVE_MULTIMAN \
|
||||
-DHAVE_MEMINFO \
|
||||
-DHAVE_RGUI \
|
||||
-DHAVE_FREETYPE \
|
||||
-DHAVE_CORE_INFO_CACHE
|
||||
CFLAGS += -std=gnu99 $(SHARED_FLAGS)
|
||||
CXXFLAGS += $(SHARED_FLAGS)
|
||||
|
||||
+15
-12
@@ -4,8 +4,8 @@ DEBUG ?= 0
|
||||
HAVE_KERNEL_PRX ?= 1
|
||||
HAVE_THREADS ?= 1
|
||||
BIG_STACK ?= 0
|
||||
WHOLE_ARCHIVE_LINK ?= 0
|
||||
LOAD_WITHOUT_CORE_INFO ?= 0
|
||||
HAVE_STATIC_DUMMY ?= 0
|
||||
|
||||
TARGET = retroarchpsp
|
||||
|
||||
@@ -15,13 +15,8 @@ else
|
||||
OPTIMIZE_LV := -O3
|
||||
endif
|
||||
|
||||
ifeq ($(WHOLE_ARCHIVE_LINK), 1)
|
||||
WHOLE_START := -Wl,--whole-archive
|
||||
WHOLE_END := -Wl,--no-whole-archive
|
||||
endif
|
||||
|
||||
INCDIR = deps deps/stb deps/7zip libretro-common/include libretro-common/include/compat/zlib
|
||||
CFLAGS = $(OPTIMIZE_LV) -G0 -std=gnu99 -ffast-math -fsingle-precision-constant
|
||||
CFLAGS = $(OPTIMIZE_LV) -ffast-math -fsingle-precision-constant
|
||||
ASFLAGS = $(CFLAGS)
|
||||
|
||||
RARCH_DEFINES = -DPSP \
|
||||
@@ -51,14 +46,21 @@ RARCH_DEFINES = -DPSP \
|
||||
-DHAVE_CORE_INFO_CACHE
|
||||
|
||||
LIBDIR =
|
||||
LDFLAGS =
|
||||
LIBS = $(WHOLE_START) -lretro_psp1 $(WHOLE_END) -lstdc++ -lpspgu -lpspgum -lm -lpspaudio -lpspfpu -lpsppower -lpsprtc -lpthread-psp
|
||||
LDFLAGS = -L$(shell psp-config --psp-prefix)
|
||||
|
||||
ifeq ($(HAVE_STATIC_DUMMY),1)
|
||||
RARCH_DEFINES += -DHAVE_STATIC_DUMMY
|
||||
else
|
||||
LIBS += -lretro_psp1
|
||||
endif
|
||||
LIBS += -lstdc++ -lpspgu -lpspgum -lpspaudio -lpspfpu -lpsppower -lpsprtc -lpthread
|
||||
|
||||
ifeq ($(HAVE_THREADS), 1)
|
||||
RARCH_DEFINES += -DHAVE_THREADS
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_KERNEL_PRX), 1)
|
||||
OBJS += bootstrap/psp1/kernel_functions.o
|
||||
CFLAGS += -DHAVE_KERNEL_PRX
|
||||
endif
|
||||
|
||||
@@ -75,9 +77,7 @@ CFLAGS += $(RARCH_DEFINES)
|
||||
EXTRA_TARGETS = EBOOT.PBP
|
||||
PSP_EBOOT_TITLE = RetroArch PSP1
|
||||
|
||||
PSP_OBJECTS = griffin/griffin.o bootstrap/psp1/kernel_functions.o
|
||||
|
||||
OBJS = $(PSP_OBJECTS)
|
||||
OBJS += griffin/griffin.o
|
||||
|
||||
PSPSDK=$(shell psp-config --pspsdk-path)
|
||||
include $(PSPSDK)/lib/build.mak
|
||||
@@ -96,3 +96,6 @@ pspsh-run:
|
||||
debug: clean all pspsh-debug
|
||||
|
||||
run: clean all pspsh-run
|
||||
|
||||
sim:
|
||||
/Applications/PPSSPPSDL.app/Contents/MacOS/PPSSPPSDL $(shell pwd)/EBOOT.PBP
|
||||
@@ -1,5 +1,6 @@
|
||||
BUILD_PRX = 0
|
||||
PSP_LARGE_MEMORY = 1
|
||||
HAVE_KERNEL_PRX = 1
|
||||
DEBUG = 0
|
||||
|
||||
TARGET = retroarchpsp_salamander
|
||||
@@ -7,18 +8,18 @@ TARGET = retroarchpsp_salamander
|
||||
ifeq ($(DEBUG), 1)
|
||||
OPTIMIZE_LV := -O0 -g
|
||||
else
|
||||
OPTIMIZE_LV := -O2
|
||||
OPTIMIZE_LV := -O3
|
||||
endif
|
||||
|
||||
INCDIR = $(PSPPATH)/include libretro-common/include
|
||||
CFLAGS = $(OPTIMIZE_LV) -G0 -std=gnu99 -ffast-math
|
||||
CFLAGS = $(OPTIMIZE_LV) -ffast-math -fsingle-precision-constant
|
||||
ASFLAGS = $(CFLAGS)
|
||||
|
||||
RARCH_DEFINES = -DPSP -DIS_SALAMANDER -DRARCH_CONSOLE
|
||||
|
||||
LIBDIR =
|
||||
LDFLAGS =
|
||||
LIBS = -lstdc++ -lm -lpsppower
|
||||
LIBS = -lstdc++ -lpsppower
|
||||
|
||||
CFLAGS += $(RARCH_DEFINES)
|
||||
|
||||
@@ -45,8 +46,12 @@ OBJS = frontend/frontend_salamander.o \
|
||||
libretro-common/vfs/vfs_implementation.o \
|
||||
libretro-common/hash/lrc_hash.o \
|
||||
libretro-common/time/rtime.o \
|
||||
verbosity.o \
|
||||
bootstrap/psp1/kernel_functions.o
|
||||
verbosity.o
|
||||
|
||||
ifeq ($(HAVE_KERNEL_PRX), 1)
|
||||
OBJS += bootstrap/psp1/kernel_functions.o
|
||||
CFLAGS += -DHAVE_KERNEL_PRX
|
||||
endif
|
||||
|
||||
PSPSDK=$(shell psp-config --pspsdk-path)
|
||||
include $(PSPSDK)/lib/build.mak
|
||||
+33
-127
@@ -6,6 +6,7 @@ HAVE_FILE_LOGGER = 0
|
||||
HAVE_CC_RESAMPLER = 1
|
||||
WHOLE_ARCHIVE_LINK = 0
|
||||
BIG_STACK = 1
|
||||
HAVE_NETWORKING = 1
|
||||
PC_DEVELOPMENT_IP_ADDRESS = 255.255.255.255
|
||||
PC_DEVELOPMENT_UDP_PORT = 3490
|
||||
RARCH_CONSOLE = 0
|
||||
@@ -63,16 +64,18 @@ ifeq ("$(wildcard $(ELF2DOL))","")
|
||||
ELF2DOL = $(DEVKITPRO)/tools/bin/elf2dol$(EXE_EXT)
|
||||
endif
|
||||
|
||||
INCLUDE += -I. -I$(DEVKITPRO)/libogc/include -Iwii/libogc/include
|
||||
INCLUDE += -I.
|
||||
|
||||
ifeq ($(EXTERNAL_LIBOGC), 1)
|
||||
CFLAGS += -DEXTERNAL_LIBOGC
|
||||
CXXFLAGS += -DEXTERNAL_LIBOGC
|
||||
LIBDIRS += -L$(DEVKITPRO)/libogc/lib/wii
|
||||
INCLUDE += -I$(DEVKITPRO)/libogc/include
|
||||
else
|
||||
CFLAGS += -DINTERNAL_LIBOGC
|
||||
CXXFLAGS += -DINTERNAL_LIBOGC
|
||||
LIBDIRS += -Lwii/libogc/libs/wii
|
||||
INCLUDE += -Iwii/libogc/include
|
||||
endif
|
||||
|
||||
ifeq ($(GX_PTHREAD_LEGACY), 1)
|
||||
@@ -100,6 +103,10 @@ LIBS += -lwiiuse -lbte
|
||||
ifeq ($(USBGECKO), 1)
|
||||
LIBS += -ldb
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_LOGGER), 1)
|
||||
LIBS += -lbba
|
||||
endif
|
||||
|
||||
LIBS += -logc
|
||||
|
||||
@@ -127,12 +134,25 @@ HAVE_REWIND := 1
|
||||
HAVE_AUDIOMIXER := 1
|
||||
HAVE_RWAV := 1
|
||||
RARCH_CONSOLE = 1
|
||||
HAVE_CHEATS := 1
|
||||
HAVE_CORE_INFO_CACHE := 1
|
||||
HAVE_MENU_COMMON := 1
|
||||
HAVE_MENU := 1
|
||||
HAVE_BUILTINZLIB := 1
|
||||
HAVE_STATIC_AUDIO_FILTERS := 1
|
||||
HAVE_STATIC_VIDEO_FILTERS := 1
|
||||
|
||||
#HAVE_LANGEXTRA := 1
|
||||
HAVE_WIIUSB_HID := 1
|
||||
HAVE_HID := 1
|
||||
HAVE_RARCH_EXEC := 1
|
||||
HAVE_CORE_INFO_CACHE := 1
|
||||
HAVE_RSOUND := 0
|
||||
HAVE_XMB := 0
|
||||
HAVE_OZONE := 0
|
||||
HAVE_RGUI := 1
|
||||
HAVE_MATERIALUI := 0
|
||||
|
||||
CFLAGS += -DHAVE_SOCKET_LEGACY
|
||||
|
||||
APP_BOOTER_DIR = wii/app_booter
|
||||
PLATOBJS := $(APP_BOOTER_DIR)/app_booter.binobj
|
||||
@@ -141,19 +161,23 @@ ifeq ($(USBGECKO), 1)
|
||||
CFLAGS += -DUSBGECKO
|
||||
endif
|
||||
|
||||
HAVE_CHEATS := 1
|
||||
|
||||
CFLAGS += -Wall -std=gnu99 $(MACHDEP) $(PLATCFLAGS) $(INCLUDE)
|
||||
INCLUDE += -I./libretro-common/include \
|
||||
-Ideps \
|
||||
-Ideps/stb
|
||||
CFLAGS += -Wall -std=gnu99 $(MACHDEP) $(PLATCFLAGS) $(INCLUDE)
|
||||
|
||||
OBJ = griffin/griffin.o $(PLATOBJS)
|
||||
OBJ = $(PLATOBJS) audio/drivers/gx_audio.o gfx/drivers/gx_gfx.o input/drivers/gx_input.o frontend/drivers/platform_gx.o input/drivers_joypad/gx_joypad.o memory/wii/mem2_manager.o gfx/drivers/gx_gfx_vi_encoder.o frontend/drivers/platform_wii.o input/drivers_hid/wiiusb_hid.o
|
||||
|
||||
ifeq ($(HAVE_GRIFFIN_CPP), 1)
|
||||
OBJ += griffin/griffin_cpp.o
|
||||
ifeq ($(EXTERNAL_LIBOGC), 0)
|
||||
OBJ += wii/libogc/libfat/libfat.o wii/libogc/libfat/fatfile.o wii/libogc/libfat/file_allocation_table.o wii/libogc/libfat/cache.o wii/libogc/libfat/fatdir.o wii/libogc/libfat/partition.o wii/libogc/libfat/directory.o wii/libogc/libfat/filetime.o wii/libogc/libfat/disc.o
|
||||
endif
|
||||
|
||||
include Makefile.common
|
||||
|
||||
CFLAGS += $(DEFINES) -DPC_DEVELOPMENT_IP_ADDRESS=\"$(PC_DEVELOPMENT_IP_ADDRESS)\" \
|
||||
-DPC_DEVELOPMENT_UDP_PORT=$(PC_DEVELOPMENT_UDP_PORT) \
|
||||
|
||||
|
||||
ifeq ($(WANT_GLSLANG), 1)
|
||||
OBJ += griffin/griffin_glslang.o
|
||||
endif
|
||||
@@ -163,28 +187,18 @@ ifeq ($(HAVE_LOGGER), 1)
|
||||
CFLAGS += -DPC_DEVELOPMENT_IP_ADDRESS=\"$(PC_DEVELOPMENT_IP_ADDRESS)\" -DPC_DEVELOPMENT_UDP_PORT=$(PC_DEVELOPMENT_UDP_PORT)
|
||||
endif
|
||||
|
||||
CFLAGS += -DHAVE_SOCKET_LEGACY
|
||||
|
||||
ifeq ($(HAVE_KERNEL_PRX), 1)
|
||||
CFLAGS += -DHAVE_KERNEL_PRX
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_WIIUSB_HID), 1)
|
||||
CFLAGS += -DHAVE_WIIUSB_HID -DHAVE_HID
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_LIBRETRODB), 1)
|
||||
CFLAGS += -DHAVE_LIBRETRODB
|
||||
CFLAGS += -DHAVE_WIIUSB_HID
|
||||
endif
|
||||
|
||||
ifeq ($(BIG_STACK),1)
|
||||
CFLAGS += -DBIG_STACK
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_RJPEG), 1)
|
||||
CFLAGS += -DHAVE_RJPEG
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_FILE_LOGGER), 1)
|
||||
CFLAGS += -DHAVE_FILE_LOGGER
|
||||
endif
|
||||
@@ -198,66 +212,10 @@ ifeq ($(HAVE_ZLIB), 1)
|
||||
CFLAGS += -I./libretro-common/include/compat/zlib
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_RPNG), 1)
|
||||
CFLAGS += -DHAVE_RPNG
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_RBMP), 1)
|
||||
CFLAGS += -DHAVE_RBMP
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_RTGA), 1)
|
||||
CFLAGS += -DHAVE_RTGA
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_IMAGEVIEWER), 1)
|
||||
CFLAGS += -DHAVE_IMAGEVIEWER
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_BSV_MOVIE), 1)
|
||||
CFLAGS += -DHAVE_BSV_MOVIE
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_RUNAHEAD), 1)
|
||||
CFLAGS += -DHAVE_RUNAHEAD
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_7ZIP), 1)
|
||||
CFLAGS += -DHAVE_7ZIP -D_7ZIP_ST
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_SCREENSHOTS), 1)
|
||||
CFLAGS += -DHAVE_SCREENSHOTS
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_REWIND), 1)
|
||||
CFLAGS += -DHAVE_REWIND
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_AUDIOMIXER), 1)
|
||||
CFLAGS += -DHAVE_AUDIOMIXER
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_RWAV), 1)
|
||||
CFLAGS += -DHAVE_RWAV
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_OVERLAY), 1)
|
||||
CFLAGS += -DHAVE_OVERLAY
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_VIDEO_LAYOUT), 1)
|
||||
CFLAGS += -DHAVE_VIDEO_LAYOUT
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_NETWORKING), 1)
|
||||
CFLAGS += -DHAVE_NETWORKING
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_NETPLAYDISCOVERY), 1)
|
||||
CFLAGS += -DHAVE_NETPLAYDISCOVERY
|
||||
endif
|
||||
|
||||
ifeq ($(RARCH_CONSOLE), 1)
|
||||
CFLAGS += -DRARCH_CONSOLE
|
||||
endif
|
||||
@@ -268,50 +226,10 @@ endif
|
||||
|
||||
CFLAGS += -std=gnu99 -DHAVE_RGUI -DHAVE_MENU -DHAVE_GRIFFIN=1 -Wno-char-subscripts -DRARCH_INTERNAL
|
||||
|
||||
ifeq ($(HAVE_MATERIALUI), 1)
|
||||
CFLAGS += -DHAVE_MATERIALUI
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_XMB), 1)
|
||||
CFLAGS += -DHAVE_XMB
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_STB_FONT), 1)
|
||||
CFLAGS += -DHAVE_STB_FONT
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_LANGEXTRA), 1)
|
||||
CFLAGS += -DHAVE_LANGEXTRA
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_DSP_FILTER), 1)
|
||||
CFLAGS += -DHAVE_DSP_FILTER
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_VIDEO_FILTER), 1)
|
||||
CFLAGS += -DHAVE_VIDEO_FILTER
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_FILTERS_BUILTIN), 1)
|
||||
CFLAGS += -DHAVE_FILTERS_BUILTIN
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_THREADS), 1)
|
||||
CFLAGS += -DHAVE_THREADS
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_CONFIGFILE), 1)
|
||||
CFLAGS += -DHAVE_CONFIGFILE
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_PATCH), 1)
|
||||
CFLAGS += -DHAVE_PATCH
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_CHEATS), 1)
|
||||
CFLAGS += -DHAVE_CHEATS
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_RSOUND), 1)
|
||||
CFLAGS += -DHAVE_RSOUND
|
||||
endif
|
||||
@@ -320,22 +238,10 @@ ifeq ($(HAVE_GETOPT_LONG), 1)
|
||||
CFLAGS += -DHAVE_GETOPT_LONG=1
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_DYLIB), 1)
|
||||
CFLAGS += -DHAVE_DYLIB
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_NETWORK_CMD), 1)
|
||||
CFLAGS += -DHAVE_NETWORK_CMD
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_COMMAND), 1)
|
||||
CFLAGS += -DHAVE_COMMAND
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_STDIN_CMD), 1)
|
||||
CFLAGS += -DHAVE_STDIN_CMD
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_DYNAMIC), 1)
|
||||
CFLAGS += -DHAVE_DYNAMIC
|
||||
endif
|
||||
|
||||
@@ -25,7 +25,6 @@ HAVE_MATERIALUI = 1
|
||||
HAVE_DSP_FILTER = 1
|
||||
HAVE_VIDEO_FILTER = 1
|
||||
HAVE_7ZIP = 1
|
||||
HAVE_PYTHON = 0
|
||||
DYNAMIC = 1
|
||||
HAVE_CORE_INFO_CACHE = 1
|
||||
|
||||
@@ -35,7 +34,6 @@ HAVE_WINMM = 1
|
||||
HAVE_SDL := 0
|
||||
HAVE_SDL2 := 0
|
||||
HAVE_RSOUND := 0
|
||||
HAVE_PYTHON := 0
|
||||
|
||||
HAVE_STB_FONT := 1
|
||||
HAVE_FREETYPE := 1
|
||||
@@ -84,11 +82,6 @@ RSOUND_CFLAGS := -DHAVE_RSOUND
|
||||
RSOUND_LIBS := -lrsound
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_PYTHON), 1)
|
||||
PYTHON_LIBS := -lpython32
|
||||
PYTHON_CFLAGS := -DHAVE_PYTHON -Ipython
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_FFMPEG), 1)
|
||||
AVCODEC_LIBS := -lavcodec
|
||||
AVUTIL_LIBS := -lavutil
|
||||
|
||||
@@ -76,11 +76,11 @@ In the end you should have this on your system:
|
||||
gl3.h
|
||||
gl3platform.h
|
||||
|
||||
To enable mali_fbdev you must configure RetroArch with --enable-gles and --enable-mali_fbdev.
|
||||
To enable mali_fbdev you must configure RetroArch with --enable-opengles and --enable-mali_fbdev.
|
||||
|
||||
This is an example of what you would use on a CubieBoard2 for a lightweight RetroArch:
|
||||
|
||||
./configure --enable-gles --enable-mali_fbdev --disable-x11 --disable-sdl2 --enable-floathard --disable-ffmpeg --disable-netplay --enable-udev --disable-sdl --disable-pulse --disable-oss --disable-freetype --disable-7zip
|
||||
./configure --enable-opengles --enable-mali_fbdev --disable-x11 --disable-sdl2 --enable-floathard --disable-ffmpeg --disable-netplay --enable-udev --disable-sdl --disable-pulse --disable-oss --disable-freetype --disable-7zip
|
||||
|
||||
NOTE: A TTY hack is used to auto-clean the console on exit, and the fbdev ioctls are used to retrieve
|
||||
current video mode. Both things work good, but they are not exactly ideal solutions.
|
||||
|
||||
@@ -33,12 +33,13 @@
|
||||
|
||||
#include "../audio_driver.h"
|
||||
#include "../../verbosity.h"
|
||||
#include "defines/cocoa_defines.h"
|
||||
|
||||
typedef struct coreaudio
|
||||
{
|
||||
slock_t *lock;
|
||||
scond_t *cond;
|
||||
#if (defined(__MACH__) && (defined(__ppc__) || defined(__ppc64__)))
|
||||
#if !HAS_MACOSX_10_12
|
||||
ComponentInstance dev;
|
||||
#else
|
||||
AudioComponentInstance dev;
|
||||
@@ -64,7 +65,7 @@ static void coreaudio_free(void *data)
|
||||
if (dev->dev_alive)
|
||||
{
|
||||
AudioOutputUnitStop(dev->dev);
|
||||
#if (defined(__MACH__) && (defined(__ppc__) || defined(__ppc64__)))
|
||||
#if !HAS_MACOSX_10_12
|
||||
CloseComponent(dev->dev);
|
||||
#else
|
||||
AudioComponentInstanceDispose(dev->dev);
|
||||
@@ -139,7 +140,7 @@ static void choose_output_device(coreaudio_t *dev, const char* device)
|
||||
UInt32 size = 0;
|
||||
|
||||
propaddr.mSelector = kAudioHardwarePropertyDevices;
|
||||
#if MAC_OS_X_VERSION_10_12
|
||||
#if HAS_MACOSX_10_12
|
||||
propaddr.mScope = kAudioObjectPropertyScopeOutput;
|
||||
#else
|
||||
propaddr.mScope = kAudioObjectPropertyScopeGlobal;
|
||||
@@ -157,7 +158,7 @@ static void choose_output_device(coreaudio_t *dev, const char* device)
|
||||
&propaddr, 0, 0, &size, devices) != noErr)
|
||||
goto done;
|
||||
|
||||
#if MAC_OS_X_VERSION_10_12
|
||||
#if HAS_MACOSX_10_12
|
||||
#else
|
||||
propaddr.mScope = kAudioDevicePropertyScopeOutput;
|
||||
#endif
|
||||
@@ -192,7 +193,7 @@ static void *coreaudio_init(const char *device,
|
||||
size_t fifo_size;
|
||||
UInt32 i_size;
|
||||
AudioStreamBasicDescription real_desc;
|
||||
#if (defined(__MACH__) && (defined(__ppc__) || defined(__ppc64__)))
|
||||
#if !HAS_MACOSX_10_12
|
||||
Component comp;
|
||||
#else
|
||||
AudioComponent comp;
|
||||
@@ -204,7 +205,7 @@ static void *coreaudio_init(const char *device,
|
||||
AudioStreamBasicDescription stream_desc = {0};
|
||||
bool component_unavailable = false;
|
||||
static bool session_initialized = false;
|
||||
#if (defined(__MACH__) && (defined(__ppc__) || defined(__ppc64__)))
|
||||
#if !HAS_MACOSX_10_12
|
||||
ComponentDescription desc = {0};
|
||||
#else
|
||||
AudioComponentDescription desc = {0};
|
||||
@@ -238,7 +239,7 @@ static void *coreaudio_init(const char *device,
|
||||
#endif
|
||||
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
|
||||
#if (defined(__MACH__) && (defined(__ppc__) || defined(__ppc64__)))
|
||||
#if !HAS_MACOSX_10_12
|
||||
comp = FindNextComponent(NULL, &desc);
|
||||
#else
|
||||
comp = AudioComponentFindNext(NULL, &desc);
|
||||
@@ -246,7 +247,7 @@ static void *coreaudio_init(const char *device,
|
||||
if (!comp)
|
||||
goto error;
|
||||
|
||||
#if (defined(__MACH__) && (defined(__ppc__) || defined(__ppc64__)))
|
||||
#if !HAS_MACOSX_10_12
|
||||
component_unavailable = (OpenAComponent(comp, &dev->dev) != noErr);
|
||||
#else
|
||||
component_unavailable = (AudioComponentInstanceNew(comp, &dev->dev) != noErr);
|
||||
|
||||
+14
-9
@@ -136,8 +136,8 @@ static void dsound_thread(void *data)
|
||||
static DWORD CALLBACK dsound_thread(PVOID data)
|
||||
#endif
|
||||
{
|
||||
DWORD write_ptr;
|
||||
dsound_t *ds = (dsound_t*)data;
|
||||
DWORD write_ptr = 0;
|
||||
dsound_t *ds = (dsound_t*)data;
|
||||
|
||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
|
||||
|
||||
@@ -315,15 +315,20 @@ static BOOL CALLBACK enumerate_cb(LPGUID guid,
|
||||
|
||||
if (guid)
|
||||
{
|
||||
unsigned i;
|
||||
LPGUID guid_copy = (LPGUID)malloc(sizeof(GUID) * 1);
|
||||
guid_copy->Data1 = guid->Data1;
|
||||
guid_copy->Data2 = guid->Data2;
|
||||
guid_copy->Data3 = guid->Data3;
|
||||
for (i = 0; i < 8; i++)
|
||||
guid_copy->Data4[i] = guid->Data4[i];
|
||||
|
||||
list->elems[list->size-1].userdata = guid_copy;
|
||||
if (guid_copy)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
guid_copy->Data1 = guid->Data1;
|
||||
guid_copy->Data2 = guid->Data2;
|
||||
guid_copy->Data3 = guid->Data3;
|
||||
for (i = 0; i < 8; i++)
|
||||
guid_copy->Data4[i] = guid->Data4[i];
|
||||
|
||||
list->elems[list->size - 1].userdata = guid_copy;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
+33
-25
@@ -54,7 +54,7 @@ typedef struct psp_audio
|
||||
|
||||
SceUID thread;
|
||||
|
||||
int rate;
|
||||
int port;
|
||||
|
||||
volatile uint16_t read_pos;
|
||||
volatile uint16_t write_pos;
|
||||
@@ -67,22 +67,27 @@ typedef struct psp_audio
|
||||
#define AUDIO_BUFFER_SIZE (1u<<13u)
|
||||
#define AUDIO_BUFFER_SIZE_MASK (AUDIO_BUFFER_SIZE-1)
|
||||
|
||||
/* Return port used */
|
||||
static int configureAudio(unsigned rate) {
|
||||
int port;
|
||||
#if defined(VITA)
|
||||
port = sceAudioOutOpenPort(
|
||||
SCE_AUDIO_OUT_PORT_TYPE_MAIN, AUDIO_OUT_COUNT,
|
||||
rate, SCE_AUDIO_OUT_MODE_STEREO);
|
||||
#elif defined(ORBIS)
|
||||
port = sceAudioOutOpen(0xff,
|
||||
SCE_AUDIO_OUT_PORT_TYPE_MAIN, 0, AUDIO_OUT_COUNT,
|
||||
rate, SCE_AUDIO_OUT_MODE_STEREO);
|
||||
#else
|
||||
port = sceAudioSRCChReserve(AUDIO_OUT_COUNT, rate, 2);
|
||||
#endif
|
||||
return port;
|
||||
}
|
||||
|
||||
static void audioMainLoop(void *data)
|
||||
{
|
||||
psp_audio_t* psp = (psp_audio_t*)data;
|
||||
|
||||
#if defined(VITA)
|
||||
int port = sceAudioOutOpenPort(
|
||||
SCE_AUDIO_OUT_PORT_TYPE_MAIN, AUDIO_OUT_COUNT,
|
||||
psp->rate, SCE_AUDIO_OUT_MODE_STEREO);
|
||||
#elif defined(ORBIS)
|
||||
int port = sceAudioOutOpen(0xff,
|
||||
SCE_AUDIO_OUT_PORT_TYPE_MAIN, 0, AUDIO_OUT_COUNT,
|
||||
psp->rate, SCE_AUDIO_OUT_MODE_STEREO);
|
||||
#else
|
||||
sceAudioSRCChReserve(AUDIO_OUT_COUNT, psp->rate, 2);
|
||||
#endif
|
||||
|
||||
while (psp->running)
|
||||
{
|
||||
bool cond = false;
|
||||
@@ -107,7 +112,7 @@ static void audioMainLoop(void *data)
|
||||
slock_unlock(psp->cond_lock);
|
||||
|
||||
#if defined(VITA) || defined(ORBIS)
|
||||
sceAudioOutOutput(port,
|
||||
sceAudioOutOutput(psp->port,
|
||||
cond ? (psp->zeroBuffer)
|
||||
: (psp->buffer + read_pos_2));
|
||||
#else
|
||||
@@ -116,14 +121,6 @@ static void audioMainLoop(void *data)
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(VITA)
|
||||
sceAudioOutReleasePort(port);
|
||||
#elif defined(ORBIS)
|
||||
sceAudioOutClose(port);
|
||||
#else
|
||||
sceAudioSRCChRelease();
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -137,8 +134,9 @@ static void *psp_audio_init(const char *device,
|
||||
if (!psp)
|
||||
return NULL;
|
||||
|
||||
(void)device;
|
||||
(void)latency;
|
||||
int port = configureAudio(rate);
|
||||
if (port < 0)
|
||||
return NULL;
|
||||
|
||||
#ifdef ORBIS
|
||||
psp->buffer = (uint32_t*)
|
||||
@@ -161,7 +159,7 @@ static void *psp_audio_init(const char *device,
|
||||
|
||||
psp->read_pos = 0;
|
||||
psp->write_pos = 0;
|
||||
psp->rate = rate;
|
||||
psp->port = port;
|
||||
|
||||
psp->fifo_lock = slock_new();
|
||||
psp->cond_lock = slock_new();
|
||||
@@ -197,7 +195,17 @@ static void psp_audio_free(void *data)
|
||||
free(psp->buffer);
|
||||
psp->worker_thread = NULL;
|
||||
free(psp->zeroBuffer);
|
||||
|
||||
#if defined(VITA)
|
||||
sceAudioOutReleasePort(psp->port);
|
||||
#elif defined(ORBIS)
|
||||
sceAudioOutClose(psp->port);
|
||||
#else
|
||||
sceAudioSRCChRelease();
|
||||
#endif
|
||||
|
||||
free(psp);
|
||||
|
||||
}
|
||||
|
||||
static ssize_t psp_audio_write(void *data, const void *buf, size_t size)
|
||||
|
||||
@@ -9,7 +9,6 @@ PSP_EXPORT_VAR_HASH(module_info)
|
||||
PSP_EXPORT_END
|
||||
|
||||
PSP_EXPORT_START(kernel_functions, 0, 0x4001)
|
||||
PSP_EXPORT_FUNC(read_system_buttons)
|
||||
PSP_EXPORT_FUNC(exitspawn_kernel)
|
||||
PSP_EXPORT_END
|
||||
|
||||
|
||||
@@ -1,49 +1,17 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <pspdebug.h>
|
||||
#include <pspsdk.h>
|
||||
#include <pspctrl.h>
|
||||
#include <psploadexec_kernel.h>
|
||||
#include <pspthreadman_kernel.h>
|
||||
|
||||
PSP_MODULE_INFO("kernel_functions", PSP_MODULE_KERNEL, 0, 0);
|
||||
PSP_MAIN_THREAD_ATTR(0);
|
||||
|
||||
static volatile int thread_active;
|
||||
static unsigned int buttons;
|
||||
static SceUID main_thread_id;
|
||||
|
||||
static int main_thread(SceSize args, void *argp)
|
||||
{
|
||||
SceCtrlData paddata;
|
||||
|
||||
thread_active = 1;
|
||||
|
||||
while (thread_active)
|
||||
{
|
||||
sceCtrlPeekBufferPositive(&paddata, 1);
|
||||
buttons = paddata.Buttons;
|
||||
sceKernelDelayThread(1000000/60);
|
||||
}
|
||||
|
||||
sceKernelExitThread(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int read_system_buttons(void)
|
||||
{
|
||||
return buttons;
|
||||
}
|
||||
|
||||
void exitspawn_kernel(const char *fileName, SceSize args, void *argp)
|
||||
{
|
||||
int k1;
|
||||
struct SceKernelLoadExecVSHParam game_param;
|
||||
|
||||
thread_active = 0;
|
||||
|
||||
memset(&game_param,0,sizeof(game_param));
|
||||
|
||||
game_param.size = sizeof(game_param);
|
||||
@@ -57,34 +25,16 @@ void exitspawn_kernel(const char *fileName, SceSize args, void *argp)
|
||||
game_param.unk5 = 0x10000;
|
||||
|
||||
k1 = pspSdkSetK1(0);
|
||||
#if 0
|
||||
sceKernelSuspendAllUserThreads();
|
||||
#endif
|
||||
sceKernelLoadExecVSHMs2(fileName, &game_param);
|
||||
pspSdkSetK1(k1);
|
||||
}
|
||||
|
||||
int module_start(SceSize args, void *argp)
|
||||
{
|
||||
(void)args;
|
||||
(void)argp;
|
||||
|
||||
buttons = 0;
|
||||
thread_active = 0;
|
||||
main_thread_id = sceKernelCreateThread("main Thread", main_thread, 0x11, 0x200, 0, NULL);
|
||||
|
||||
if (main_thread >= 0)
|
||||
sceKernelStartThread(main_thread_id, 0, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int module_stop(void)
|
||||
{
|
||||
if (main_thread_id >= 0)
|
||||
{
|
||||
thread_active = 0;
|
||||
sceKernelWaitThreadEnd(main_thread_id, NULL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -43,7 +43,6 @@ void _init_vita_heap(void) {
|
||||
|
||||
if (_newlib_vm_memblock < 0){
|
||||
//sceClibPrintf("sceKernelAllocMemBlockForVM failed\n");
|
||||
goto failure;
|
||||
}
|
||||
}else{
|
||||
_newlib_vm_memblock = 0;
|
||||
@@ -99,8 +98,8 @@ void _free_vita_heap(void) {
|
||||
// Free the heap memblock to avoid memory leakage.
|
||||
sceKernelFreeMemBlock(_newlib_heap_memblock);
|
||||
|
||||
if(_newlib_vm_memblock)
|
||||
sceKernelFreeMemBlock(_newlib_vm_memblock);
|
||||
if (_newlib_vm_memblock > 0)
|
||||
sceKernelFreeMemBlock(_newlib_vm_memblock);
|
||||
|
||||
_newlib_vm_memblock = 0;
|
||||
_newlib_heap_memblock = 0;
|
||||
|
||||
@@ -304,6 +304,8 @@ static void cheat_manager_free(void)
|
||||
free(cheat_st->cheats[i].desc);
|
||||
if (cheat_st->cheats[i].code)
|
||||
free(cheat_st->cheats[i].code);
|
||||
cheat_st->cheats[i].desc = NULL;
|
||||
cheat_st->cheats[i].code = NULL;
|
||||
}
|
||||
|
||||
free(cheat_st->cheats);
|
||||
@@ -560,6 +562,8 @@ bool cheat_manager_realloc(unsigned new_size, unsigned default_handler)
|
||||
free(cheat_st->cheats[i].code);
|
||||
if (cheat_st->cheats[i].desc)
|
||||
free(cheat_st->cheats[i].desc);
|
||||
cheat_st->cheats[i].code = NULL;
|
||||
cheat_st->cheats[i].desc = NULL;
|
||||
}
|
||||
|
||||
val = (struct item_cheat*)
|
||||
|
||||
+229
-67
@@ -65,12 +65,14 @@
|
||||
#include "../performance_counters.h"
|
||||
#include "../msg_hash.h"
|
||||
#include "../retroarch.h"
|
||||
#include "../runtime_file.h"
|
||||
#include "../core.h"
|
||||
#include "../core_option_manager.h"
|
||||
|
||||
#include "../tasks/tasks_internal.h"
|
||||
|
||||
#include "../deps/rcheevos/include/rc_runtime.h"
|
||||
#include "../deps/rcheevos/include/rc_runtime_types.h"
|
||||
#include "../deps/rcheevos/include/rc_hash.h"
|
||||
#include "../deps/rcheevos/src/rcheevos/rc_libretro.h"
|
||||
|
||||
@@ -85,6 +87,7 @@ static rcheevos_locals_t rcheevos_locals =
|
||||
#ifdef HAVE_THREADS
|
||||
CMD_EVENT_NONE, /* queued_command */
|
||||
#endif
|
||||
"", /* displayname */
|
||||
"", /* username */
|
||||
"", /* token */
|
||||
"", /* user_agent_prefix */
|
||||
@@ -662,10 +665,10 @@ bool rcheevos_unload(void)
|
||||
rcheevos_locals.hardcore_active = false;
|
||||
}
|
||||
|
||||
while (rcheevos_locals.game.hashes != NULL)
|
||||
while (rcheevos_locals.game.hashes)
|
||||
{
|
||||
rcheevos_hash_entry_t* hash_entry = rcheevos_locals.game.hashes;
|
||||
rcheevos_locals.game.hashes = hash_entry->next;
|
||||
rcheevos_locals.game.hashes = hash_entry->next;
|
||||
free(hash_entry);
|
||||
}
|
||||
|
||||
@@ -861,6 +864,9 @@ static void rcheevos_toggle_hardcore_active(rcheevos_locals_t* locals)
|
||||
runloop_msg_queue_push(msg, 0, 3 * 60, true, NULL,
|
||||
MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
||||
|
||||
/* disable slowdown */
|
||||
runloop_state_get_ptr()->slowmotion = false;
|
||||
|
||||
/* Reactivate leaderboards */
|
||||
if (locals->leaderboards_enabled)
|
||||
rcheevos_activate_leaderboards();
|
||||
@@ -922,7 +928,7 @@ static void rcheevos_toggle_hardcore_active(rcheevos_locals_t* locals)
|
||||
void rcheevos_toggle_hardcore_paused(void)
|
||||
{
|
||||
settings_t* settings = config_get_ptr();
|
||||
/* if hardcore mode is not enabled, we can't toggle it */
|
||||
/* if hardcore mode is not enabled, we can't toggle whether its active */
|
||||
if (settings->bools.cheevos_hardcore_mode_enable)
|
||||
rcheevos_toggle_hardcore_active(&rcheevos_locals);
|
||||
}
|
||||
@@ -1144,14 +1150,67 @@ bool rcheevos_get_serialized_data(void* buffer)
|
||||
|
||||
bool rcheevos_set_serialized_data(void* buffer)
|
||||
{
|
||||
if (rcheevos_locals.loaded)
|
||||
if (rcheevos_locals.loaded && buffer)
|
||||
{
|
||||
if (buffer && rc_runtime_deserialize_progress(
|
||||
&rcheevos_locals.runtime,
|
||||
(const unsigned char*)buffer, NULL) == RC_OK)
|
||||
return true;
|
||||
const int result = rc_runtime_deserialize_progress(
|
||||
&rcheevos_locals.runtime, (const unsigned char*)buffer, NULL);
|
||||
|
||||
rc_runtime_reset(&rcheevos_locals.runtime);
|
||||
#if defined(HAVE_GFX_WIDGETS)
|
||||
if (gfx_widgets_ready() && rcheevos_is_player_active())
|
||||
{
|
||||
settings_t* settings = config_get_ptr();
|
||||
|
||||
if (rcheevos_locals.leaderboard_trackers)
|
||||
{
|
||||
unsigned i;
|
||||
rc_runtime_lboard_t* lboard = rcheevos_locals.runtime.lboards;
|
||||
for (i = 0; i < rcheevos_locals.runtime.lboard_count; ++i, ++lboard)
|
||||
{
|
||||
if (!lboard->lboard)
|
||||
continue;
|
||||
|
||||
if (lboard->lboard->state == RC_LBOARD_STATE_STARTED)
|
||||
{
|
||||
rcheevos_ralboard_t* ralboard = rcheevos_find_lboard(lboard->id);
|
||||
if (ralboard != NULL)
|
||||
{
|
||||
char value[32];
|
||||
rc_runtime_format_lboard_value(value, sizeof(value), lboard->value, ralboard->format);
|
||||
gfx_widgets_set_leaderboard_display(lboard->id, value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gfx_widgets_set_leaderboard_display(lboard->id, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (settings->bools.cheevos_challenge_indicators)
|
||||
{
|
||||
unsigned i;
|
||||
rc_runtime_trigger_t* cheevo = rcheevos_locals.runtime.triggers;
|
||||
for (i = 0; i < rcheevos_locals.runtime.trigger_count; ++i, ++cheevo)
|
||||
{
|
||||
if (!cheevo->trigger)
|
||||
continue;
|
||||
|
||||
if (cheevo->trigger->state == RC_TRIGGER_STATE_PRIMED)
|
||||
{
|
||||
rcheevos_racheevo_t* racheevo = rcheevos_find_cheevo(cheevo->id);
|
||||
if (racheevo != NULL)
|
||||
gfx_widgets_set_challenge_display(racheevo->id, racheevo->badge);
|
||||
}
|
||||
else
|
||||
{
|
||||
gfx_widgets_set_challenge_display(cheevo->id, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return (result == RC_OK);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -1206,7 +1265,8 @@ static void rc_hash_handle_file_close(void* file_handle)
|
||||
CHEEVOS_FREE(file_handle);
|
||||
}
|
||||
|
||||
static void* rc_hash_handle_cd_open_track(
|
||||
#ifdef HAVE_CHD
|
||||
static void* rc_hash_handle_chd_open_track(
|
||||
const char* path, uint32_t track)
|
||||
{
|
||||
cdfs_track_t* cdfs_track;
|
||||
@@ -1218,27 +1278,11 @@ static void* rc_hash_handle_cd_open_track(
|
||||
break;
|
||||
|
||||
case RC_HASH_CDTRACK_LAST:
|
||||
#ifdef HAVE_CHD
|
||||
if (string_is_equal_noncase(path_get_extension(path), "chd"))
|
||||
{
|
||||
cdfs_track = cdfs_open_track(path, CHDSTREAM_TRACK_LAST);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "Last track only supported for CHD\n");
|
||||
cdfs_track = NULL;
|
||||
cdfs_track = cdfs_open_track(path, CHDSTREAM_TRACK_LAST);
|
||||
break;
|
||||
|
||||
case RC_HASH_CDTRACK_LARGEST:
|
||||
#ifdef HAVE_CHD
|
||||
if (string_is_equal_noncase(path_get_extension(path), "chd"))
|
||||
{
|
||||
cdfs_track = cdfs_open_track(path, CHDSTREAM_TRACK_PRIMARY);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "Largest track only supported for CHD, using first data track\n");
|
||||
cdfs_track = cdfs_open_data_track(path);
|
||||
cdfs_track = cdfs_open_track(path, CHDSTREAM_TRACK_PRIMARY);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -1259,17 +1303,29 @@ static void* rc_hash_handle_cd_open_track(
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static size_t rc_hash_handle_cd_read_sector(
|
||||
static size_t rc_hash_handle_chd_read_sector(
|
||||
void* track_handle, uint32_t sector,
|
||||
void* buffer, size_t requested_bytes)
|
||||
{
|
||||
cdfs_file_t* file = (cdfs_file_t*)track_handle;
|
||||
uint32_t track_sectors = cdfs_get_num_sectors(file);
|
||||
|
||||
sector -= cdfs_get_first_sector(file);
|
||||
if (sector >= track_sectors)
|
||||
return 0;
|
||||
|
||||
cdfs_seek_sector(file, sector);
|
||||
return cdfs_read_file(file, buffer, requested_bytes);
|
||||
}
|
||||
|
||||
static void rc_hash_handle_cd_close_track(void* track_handle)
|
||||
static uint32_t rc_hash_handle_chd_first_track_sector(
|
||||
void* track_handle)
|
||||
{
|
||||
cdfs_file_t* file = (cdfs_file_t*)track_handle;
|
||||
return cdfs_get_first_sector(file);
|
||||
}
|
||||
|
||||
static void rc_hash_handle_chd_close_track(void* track_handle)
|
||||
{
|
||||
cdfs_file_t* file = (cdfs_file_t*)track_handle;
|
||||
if (file)
|
||||
@@ -1280,8 +1336,109 @@ static void rc_hash_handle_cd_close_track(void* track_handle)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void rc_hash_reset_cdreader_hooks(void);
|
||||
|
||||
static void* rc_hash_handle_cd_open_track(
|
||||
const char* path, uint32_t track)
|
||||
{
|
||||
struct rc_hash_cdreader cdreader;
|
||||
|
||||
if (string_is_equal_noncase(path_get_extension(path), "chd"))
|
||||
{
|
||||
#ifdef HAVE_CHD
|
||||
/* special handlers for CHD file */
|
||||
memset(&cdreader, 0, sizeof(cdreader));
|
||||
cdreader.open_track = rc_hash_handle_cd_open_track;
|
||||
cdreader.read_sector = rc_hash_handle_chd_read_sector;
|
||||
cdreader.close_track = rc_hash_handle_chd_close_track;
|
||||
cdreader.first_track_sector = rc_hash_handle_chd_first_track_sector;
|
||||
rc_hash_init_custom_cdreader(&cdreader);
|
||||
|
||||
return rc_hash_handle_chd_open_track(path, track);
|
||||
#else
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "Cannot generate hash from CHD without HAVE_CHD compile flag\n");
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
/* not a CHD file, use the default handlers */
|
||||
rc_hash_get_default_cdreader(&cdreader);
|
||||
rc_hash_reset_cdreader_hooks();
|
||||
return cdreader.open_track(path, track);
|
||||
}
|
||||
}
|
||||
|
||||
static void rc_hash_reset_cdreader_hooks()
|
||||
{
|
||||
struct rc_hash_cdreader cdreader;
|
||||
rc_hash_get_default_cdreader(&cdreader);
|
||||
cdreader.open_track = rc_hash_handle_cd_open_track;
|
||||
rc_hash_init_custom_cdreader(&cdreader);
|
||||
}
|
||||
|
||||
/* end hooks */
|
||||
|
||||
void rcheevos_show_mastery_placard()
|
||||
{
|
||||
const settings_t* settings = config_get_ptr();
|
||||
char title[256];
|
||||
|
||||
if (rcheevos_locals.game.mastery_placard_shown)
|
||||
return;
|
||||
|
||||
rcheevos_locals.game.mastery_placard_shown = true;
|
||||
|
||||
snprintf(title, sizeof(title),
|
||||
msg_hash_to_str(rcheevos_locals.hardcore_active ? MSG_CHEEVOS_MASTERED_GAME : MSG_CHEEVOS_COMPLETED_GAME),
|
||||
rcheevos_locals.game.title);
|
||||
title[sizeof(title) - 1] = '\0';
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "%s\n", title);
|
||||
|
||||
#if defined (HAVE_GFX_WIDGETS)
|
||||
if (gfx_widgets_ready())
|
||||
{
|
||||
const bool content_runtime_log = settings->bools.content_runtime_log;
|
||||
const bool content_runtime_log_aggr = settings->bools.content_runtime_log_aggregate;
|
||||
char msg[128];
|
||||
size_t len;
|
||||
|
||||
len = snprintf(msg, sizeof(msg), "%s", rcheevos_locals.displayname);
|
||||
|
||||
if (len < sizeof(msg) - 12 &&
|
||||
(content_runtime_log || content_runtime_log_aggr))
|
||||
{
|
||||
const char* content_path = path_get(RARCH_PATH_CONTENT);
|
||||
const char* core_path = path_get(RARCH_PATH_CORE);
|
||||
runtime_log_t* runtime_log = runtime_log_init(
|
||||
content_path, core_path,
|
||||
settings->paths.directory_runtime_log,
|
||||
settings->paths.directory_playlist,
|
||||
!content_runtime_log_aggr);
|
||||
|
||||
if (runtime_log)
|
||||
{
|
||||
const runloop_state_t* runloop_state = runloop_state_get_ptr();
|
||||
runtime_log_add_runtime_usec(runtime_log,
|
||||
runloop_state->core_runtime_usec);
|
||||
|
||||
len += snprintf(msg + len, sizeof(msg) - len, " | ");
|
||||
runtime_log_get_runtime_str(runtime_log, msg + len, sizeof(msg) - len);
|
||||
msg[sizeof(msg) - 1] = '\0';
|
||||
|
||||
free(runtime_log);
|
||||
}
|
||||
}
|
||||
|
||||
gfx_widgets_push_achievement(title, msg, rcheevos_locals.game.badge_name);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
runloop_msg_queue_push(title, 0, 3 * 60, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
||||
}
|
||||
|
||||
static void rcheevos_show_game_placard()
|
||||
{
|
||||
char msg[256];
|
||||
@@ -1476,7 +1633,7 @@ static void rcheevos_fetch_game_data(void)
|
||||
return;
|
||||
}
|
||||
|
||||
if (rcheevos_locals.game.id == 0)
|
||||
if (rcheevos_locals.game.id <= 0)
|
||||
{
|
||||
const settings_t* settings = config_get_ptr();
|
||||
if (settings->bools.cheevos_verbose_enable)
|
||||
@@ -1577,18 +1734,36 @@ static void rcheevos_identify_game_callback(void* userdata)
|
||||
static bool rcheevos_identify_game(const struct retro_game_info* info)
|
||||
{
|
||||
struct rcheevos_identify_game_data* data;
|
||||
struct rc_hash_filereader filereader;
|
||||
struct rc_hash_iterator iterator;
|
||||
size_t len;
|
||||
char hash[33];
|
||||
|
||||
#ifndef HAVE_CHD
|
||||
if (string_is_equal_noncase(path_get_extension(info->path), "chd"))
|
||||
{
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "CHD not supported without HAVE_CHD compile flag\n");
|
||||
return false;
|
||||
}
|
||||
#ifndef DEBUG
|
||||
settings_t* settings = config_get_ptr();
|
||||
#endif
|
||||
|
||||
/* provide hooks for reading files */
|
||||
memset(&filereader, 0, sizeof(filereader));
|
||||
filereader.open = rc_hash_handle_file_open;
|
||||
filereader.seek = rc_hash_handle_file_seek;
|
||||
filereader.tell = rc_hash_handle_file_tell;
|
||||
filereader.read = rc_hash_handle_file_read;
|
||||
filereader.close = rc_hash_handle_file_close;
|
||||
rc_hash_init_custom_filereader(&filereader);
|
||||
|
||||
rc_hash_init_error_message_callback(rcheevos_handle_log_message);
|
||||
|
||||
#ifndef DEBUG
|
||||
/* in DEBUG mode, always initialize the verbose message handler */
|
||||
if (settings->bools.cheevos_verbose_enable)
|
||||
#endif
|
||||
{
|
||||
rc_hash_init_verbose_message_callback(rcheevos_handle_log_message);
|
||||
}
|
||||
|
||||
rc_hash_reset_cdreader_hooks();
|
||||
|
||||
/* fetch the first hash */
|
||||
rc_hash_initialize_iterator(&iterator,
|
||||
info->path, (uint8_t*)info->data, info->size);
|
||||
if (!rc_hash_iterate(hash, &iterator))
|
||||
@@ -1606,6 +1781,7 @@ static bool rcheevos_identify_game(const struct retro_game_info* info)
|
||||
if (iterator.consoles[iterator.index] == 0)
|
||||
{
|
||||
/* no more potential matches, just try the one hash */
|
||||
rcheevos_begin_load_state(RCHEEVOS_LOAD_STATE_IDENTIFYING_GAME);
|
||||
rcheevos_client_identify_game(hash,
|
||||
rcheevos_identify_game_callback, NULL);
|
||||
return true;
|
||||
@@ -1638,6 +1814,8 @@ static bool rcheevos_identify_game(const struct retro_game_info* info)
|
||||
}
|
||||
|
||||
memcpy(&data->iterator, &iterator, sizeof(iterator));
|
||||
|
||||
rcheevos_begin_load_state(RCHEEVOS_LOAD_STATE_IDENTIFYING_GAME);
|
||||
rcheevos_client_identify_game(hash,
|
||||
rcheevos_identify_game_callback, data);
|
||||
return true;
|
||||
@@ -1653,7 +1831,7 @@ static void rcheevos_login_callback(void* userdata)
|
||||
char msg[256];
|
||||
snprintf(msg, sizeof(msg),
|
||||
"RetroAchievements: Logged in as \"%s\".",
|
||||
rcheevos_locals.username);
|
||||
rcheevos_locals.displayname);
|
||||
runloop_msg_queue_push(msg, 0, 2 * 60, false, NULL,
|
||||
MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
||||
}
|
||||
@@ -1706,8 +1884,6 @@ bool rcheevos_load_aborted(void)
|
||||
|
||||
bool rcheevos_load(const void *data)
|
||||
{
|
||||
struct rc_hash_cdreader cdreader;
|
||||
struct rc_hash_filereader filereader;
|
||||
const struct retro_game_info *info = (const struct retro_game_info*)
|
||||
data;
|
||||
settings_t *settings = config_get_ptr();
|
||||
@@ -1720,6 +1896,7 @@ bool rcheevos_load(const void *data)
|
||||
rcheevos_locals.loaded = false;
|
||||
rcheevos_locals.game.id = -1;
|
||||
rcheevos_locals.game.console_id = 0;
|
||||
rcheevos_locals.game.mastery_placard_shown = false;
|
||||
#ifdef HAVE_THREADS
|
||||
rcheevos_locals.queued_command = CMD_EVENT_NONE;
|
||||
#endif
|
||||
@@ -1734,6 +1911,16 @@ bool rcheevos_load(const void *data)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (string_is_empty(settings->arrays.cheevos_username))
|
||||
{
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "Cannot login (no username)\n");
|
||||
runloop_msg_queue_push("Missing RetroAchievements account information.", 0, 5 * 60, false, NULL,
|
||||
MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_ERROR);
|
||||
rcheevos_locals.game.id = 0;
|
||||
rcheevos_pause_hardcore();
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
if (!rcheevos_locals.load_info.request_lock)
|
||||
rcheevos_locals.load_info.request_lock = slock_new();
|
||||
@@ -1747,31 +1934,6 @@ bool rcheevos_load(const void *data)
|
||||
rcheevos_validate_config_settings();
|
||||
rcheevos_leaderboards_enabled_changed();
|
||||
|
||||
/* provide hooks for reading files */
|
||||
memset(&filereader, 0, sizeof(filereader));
|
||||
filereader.open = rc_hash_handle_file_open;
|
||||
filereader.seek = rc_hash_handle_file_seek;
|
||||
filereader.tell = rc_hash_handle_file_tell;
|
||||
filereader.read = rc_hash_handle_file_read;
|
||||
filereader.close = rc_hash_handle_file_close;
|
||||
rc_hash_init_custom_filereader(&filereader);
|
||||
|
||||
memset(&cdreader, 0, sizeof(cdreader));
|
||||
cdreader.open_track = rc_hash_handle_cd_open_track;
|
||||
cdreader.read_sector = rc_hash_handle_cd_read_sector;
|
||||
cdreader.close_track = rc_hash_handle_cd_close_track;
|
||||
rc_hash_init_custom_cdreader(&cdreader);
|
||||
|
||||
rc_hash_init_error_message_callback(rcheevos_handle_log_message);
|
||||
|
||||
#ifndef DEBUG
|
||||
/* in DEBUG mode, always initialize the verbose message handler */
|
||||
if (settings->bools.cheevos_verbose_enable)
|
||||
#endif
|
||||
{
|
||||
rc_hash_init_verbose_message_callback(rcheevos_handle_log_message);
|
||||
}
|
||||
|
||||
/* Refresh the user agent in case it's not set or has changed */
|
||||
rcheevos_client_initialize();
|
||||
rcheevos_get_user_agent(&rcheevos_locals,
|
||||
@@ -1841,7 +2003,7 @@ bool rcheevos_load(const void *data)
|
||||
{
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "Cannot login %s (no password or token)\n",
|
||||
settings->arrays.cheevos_username);
|
||||
runloop_msg_queue_push("Error logging in: No password provided", 0, 5 * 60, false, NULL,
|
||||
runloop_msg_queue_push("No password provided for RetroAchievements account", 0, 5 * 60, false, NULL,
|
||||
MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_ERROR);
|
||||
rcheevos_unload();
|
||||
return false;
|
||||
|
||||
@@ -643,7 +643,9 @@ static void rcheevos_async_login_callback(
|
||||
settings->arrays.cheevos_password[0] = '\0';
|
||||
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "%s logged in successfully\n",
|
||||
api_response.username);
|
||||
api_response.display_name);
|
||||
strlcpy(rcheevos_locals->displayname, api_response.display_name,
|
||||
sizeof(rcheevos_locals->displayname));
|
||||
strlcpy(rcheevos_locals->username, api_response.username,
|
||||
sizeof(rcheevos_locals->username));
|
||||
strlcpy(rcheevos_locals->token, api_response.api_token,
|
||||
@@ -746,7 +748,6 @@ void rcheevos_client_identify_game(const char* hash,
|
||||
request->callback = callback;
|
||||
request->callback_data = userdata;
|
||||
|
||||
rcheevos_begin_load_state(RCHEEVOS_LOAD_STATE_IDENTIFYING_GAME);
|
||||
rcheevos_async_begin_request(request, result,
|
||||
rcheevos_async_resolve_hash_callback,
|
||||
CHEEVOS_ASYNC_RESOLVE_HASH, 0,
|
||||
@@ -1098,6 +1099,10 @@ static void rcheevos_async_fetch_game_data_callback(
|
||||
"i%s", runtime_data->game_data.image_name);
|
||||
rcheevos_client_fetch_game_badge(runtime_data->game_data.image_name, runtime_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
rcheevos_unload();
|
||||
}
|
||||
}
|
||||
|
||||
void rcheevos_client_initialize_runtime(unsigned game_id,
|
||||
@@ -1742,6 +1747,9 @@ static void rcheevos_async_award_achievement_callback(
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "Achievement %u: %s\n",
|
||||
request->id, api_response.response.error_message);
|
||||
}
|
||||
|
||||
if (api_response.achievements_remaining == 0)
|
||||
rcheevos_show_mastery_placard();
|
||||
}
|
||||
|
||||
rc_api_destroy_award_achievement_response(&api_response);
|
||||
|
||||
@@ -135,6 +135,7 @@ typedef struct rcheevos_game_info_t
|
||||
char* title;
|
||||
char badge_name[16];
|
||||
char* hash;
|
||||
bool mastery_placard_shown;
|
||||
|
||||
rcheevos_hash_entry_t* hashes;
|
||||
|
||||
@@ -168,6 +169,7 @@ typedef struct rcheevos_locals_t
|
||||
enum event_command queued_command; /* action queued by background thread to be run on main thread */
|
||||
#endif
|
||||
|
||||
char displayname[32]; /* name to display in messages */
|
||||
char username[32]; /* case-corrected username */
|
||||
char token[32]; /* user's session token */
|
||||
char user_agent_prefix[128]; /* RetroArch/OS version information */
|
||||
@@ -194,6 +196,8 @@ void rcheevos_begin_load_state(enum rcheevos_load_state state);
|
||||
int rcheevos_end_load_state(void);
|
||||
bool rcheevos_load_aborted(void);
|
||||
|
||||
void rcheevos_show_mastery_placard(void);
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
#define CHEEVOS_LOCK(l) do { slock_lock(l); } while (0)
|
||||
#define CHEEVOS_UNLOCK(l) do { slock_unlock(l); } while (0)
|
||||
|
||||
+10
-7
@@ -116,9 +116,12 @@ bool rcheevos_menu_get_state(unsigned menu_offset, char *buffer, size_t len)
|
||||
if (cheevo)
|
||||
{
|
||||
if (cheevo->menu_progress)
|
||||
snprintf(buffer, len, "%s - %d%%",
|
||||
msg_hash_to_str(menuitem->state_label_idx),
|
||||
cheevo->menu_progress);
|
||||
{
|
||||
const int written = snprintf(buffer, len, "%s - ",
|
||||
msg_hash_to_str(menuitem->state_label_idx));
|
||||
if (len - written > 0)
|
||||
rc_runtime_format_achievement_measured(&rcheevos_locals->runtime, cheevo->id, buffer + written, len - written);
|
||||
}
|
||||
else
|
||||
strlcpy(buffer, msg_hash_to_str(menuitem->state_label_idx), len);
|
||||
|
||||
@@ -398,7 +401,7 @@ void rcheevos_menu_populate_hardcore_pause_submenu(void* data)
|
||||
{
|
||||
menu_entries_append_enum(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_PAUSE_CANCEL),
|
||||
msg_hash_to_str(MENU_ENUM_SUBLABEL_ACHIEVEMENT_PAUSE_CANCEL),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_ACHIEVEMENT_PAUSE_CANCEL),
|
||||
MENU_ENUM_LABEL_ACHIEVEMENT_PAUSE_CANCEL,
|
||||
MENU_SETTING_ACTION_CLOSE, 0, 0);
|
||||
menu_entries_append_enum(info->list,
|
||||
@@ -411,7 +414,7 @@ void rcheevos_menu_populate_hardcore_pause_submenu(void* data)
|
||||
{
|
||||
menu_entries_append_enum(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_RESUME_CANCEL),
|
||||
msg_hash_to_str(MENU_ENUM_SUBLABEL_ACHIEVEMENT_RESUME_CANCEL),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_ACHIEVEMENT_RESUME_CANCEL),
|
||||
MENU_ENUM_LABEL_ACHIEVEMENT_RESUME_CANCEL,
|
||||
MENU_SETTING_ACTION_CLOSE, 0, 0);
|
||||
menu_entries_append_enum(info->list,
|
||||
@@ -448,13 +451,13 @@ void rcheevos_menu_populate(void* data)
|
||||
if (rcheevos_locals->hardcore_active)
|
||||
menu_entries_append_enum(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_PAUSE),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_PAUSE_MENU),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_ACHIEVEMENT_PAUSE_MENU),
|
||||
MENU_ENUM_LABEL_ACHIEVEMENT_PAUSE_MENU,
|
||||
MENU_SETTING_ACTION_PAUSE_ACHIEVEMENTS, 0, 0);
|
||||
else
|
||||
menu_entries_append_enum(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_RESUME),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_PAUSE_MENU),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_ACHIEVEMENT_PAUSE_MENU),
|
||||
MENU_ENUM_LABEL_ACHIEVEMENT_PAUSE_MENU,
|
||||
MENU_SETTING_ACTION_RESUME_ACHIEVEMENTS, 0, 0);
|
||||
}
|
||||
|
||||
@@ -96,6 +96,8 @@ static bool command_get_arg(const char *tok,
|
||||
if (str == tok)
|
||||
{
|
||||
const char *argument = str + strlen(action_map[i].str);
|
||||
if (!argument)
|
||||
return false;
|
||||
if (*argument != ' ' && *argument != '\0')
|
||||
return false;
|
||||
|
||||
@@ -334,6 +336,14 @@ command_t* command_stdin_new(void)
|
||||
|
||||
cmd = (command_t*)calloc(1, sizeof(command_t));
|
||||
stdincmd = (command_stdin_t*)calloc(1, sizeof(command_stdin_t));
|
||||
|
||||
if (!cmd)
|
||||
return NULL;
|
||||
if (!stdincmd)
|
||||
{
|
||||
free(cmd);
|
||||
return NULL;
|
||||
}
|
||||
cmd->userptr = stdincmd;
|
||||
cmd->poll = command_stdin_poll;
|
||||
cmd->replier = stdin_command_reply;
|
||||
@@ -683,7 +693,7 @@ bool command_version(command_t *cmd, const char* arg)
|
||||
return true;
|
||||
}
|
||||
|
||||
static const rarch_memory_descriptor_t* command_memory_get_descriptor(const rarch_memory_map_t* mmap, unsigned address)
|
||||
static const rarch_memory_descriptor_t* command_memory_get_descriptor(const rarch_memory_map_t* mmap, unsigned address, size_t* offset)
|
||||
{
|
||||
const rarch_memory_descriptor_t* desc = mmap->descriptors;
|
||||
const rarch_memory_descriptor_t* end = desc + mmap->num_descriptors;
|
||||
@@ -694,15 +704,37 @@ static const rarch_memory_descriptor_t* command_memory_get_descriptor(const rarc
|
||||
{
|
||||
/* if select is 0, attempt to explicitly match the address */
|
||||
if (address >= desc->core.start && address < desc->core.start + desc->core.len)
|
||||
{
|
||||
*offset = address - desc->core.start;
|
||||
return desc;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* otherwise, attempt to match the address by matching the select bits */
|
||||
if (((desc->core.start ^ address) & desc->core.select) == 0)
|
||||
{
|
||||
/* adjust the address to the start of the descriptor */
|
||||
unsigned desc_offset = address - (unsigned)desc->core.start;
|
||||
|
||||
/* address is unsigned. we only need that much of the disconnect mask */
|
||||
unsigned mask = (unsigned)desc->core.disconnect;
|
||||
|
||||
/* this magic logic is copied from mmap_reduce. it removes any bits from
|
||||
* address that are non-zero in the disconnect field. bits above the
|
||||
* removed bits are shifted down to fill the gap. */
|
||||
while (mask)
|
||||
{
|
||||
const unsigned tmp = (mask - 1) & ~mask;
|
||||
desc_offset = (desc_offset & tmp) | ((desc_offset >> 1) & ~tmp);
|
||||
mask = (mask & (mask - 1)) >> 1;
|
||||
}
|
||||
|
||||
/* we've calculated the actual offset of the data within the descriptor */
|
||||
*offset = desc_offset;
|
||||
|
||||
/* sanity check - make sure the descriptor is large enough to hold the target address */
|
||||
if (address - desc->core.start < desc->core.len)
|
||||
if (desc_offset < desc->core.len)
|
||||
return desc;
|
||||
}
|
||||
}
|
||||
@@ -723,7 +755,8 @@ uint8_t *command_memory_get_pointer(
|
||||
strlcpy(reply_at, " -1 no memory map defined\n", len);
|
||||
else
|
||||
{
|
||||
const rarch_memory_descriptor_t* desc = command_memory_get_descriptor(&system->mmaps, address);
|
||||
size_t offset;
|
||||
const rarch_memory_descriptor_t* desc = command_memory_get_descriptor(&system->mmaps, address, &offset);
|
||||
if (!desc)
|
||||
strlcpy(reply_at, " -1 no descriptor for address\n", len);
|
||||
else if (!desc->core.ptr)
|
||||
@@ -732,7 +765,6 @@ uint8_t *command_memory_get_pointer(
|
||||
strlcpy(reply_at, " -1 descriptor data is readonly\n", len);
|
||||
else
|
||||
{
|
||||
const size_t offset = address - desc->core.start;
|
||||
*max_bytes = (desc->core.len - offset);
|
||||
return (uint8_t*)desc->core.ptr + desc->core.offset + offset;
|
||||
}
|
||||
@@ -1088,6 +1120,8 @@ bool command_event_save_auto_state(
|
||||
return false;
|
||||
if (current_core_type == CORE_TYPE_DUMMY)
|
||||
return false;
|
||||
if (!core_info_current_supports_savestate())
|
||||
return false;
|
||||
|
||||
if (string_is_empty(path_basename(path_get(RARCH_PATH_BASENAME))))
|
||||
return false;
|
||||
@@ -1148,6 +1182,9 @@ bool command_event_load_entry_state(void)
|
||||
runloop_state_t *runloop_st = runloop_state_get_ptr();
|
||||
bool ret = false;
|
||||
|
||||
if (!core_info_current_supports_savestate())
|
||||
return false;
|
||||
|
||||
#ifdef HAVE_CHEEVOS
|
||||
if (rcheevos_hardcore_active())
|
||||
return false;
|
||||
@@ -1187,6 +1224,10 @@ void command_event_load_auto_state(void)
|
||||
char savestate_name_auto[PATH_MAX_LENGTH];
|
||||
runloop_state_t *runloop_st = runloop_state_get_ptr();
|
||||
bool ret = false;
|
||||
|
||||
if (!core_info_current_supports_savestate())
|
||||
return;
|
||||
|
||||
#ifdef HAVE_CHEEVOS
|
||||
if (rcheevos_hardcore_active())
|
||||
return;
|
||||
@@ -1551,16 +1592,22 @@ bool command_event_main_state(unsigned cmd)
|
||||
char msg[128];
|
||||
char state_path[16384];
|
||||
settings_t *settings = config_get_ptr();
|
||||
bool savestates_enabled = core_info_current_supports_savestate();
|
||||
bool ret = false;
|
||||
bool push_msg = true;
|
||||
|
||||
state_path[0] = msg[0] = '\0';
|
||||
|
||||
retroarch_get_current_savestate_path(state_path, sizeof(state_path));
|
||||
if (savestates_enabled)
|
||||
{
|
||||
retroarch_get_current_savestate_path(state_path,
|
||||
sizeof(state_path));
|
||||
|
||||
core_serialize_size(&info);
|
||||
core_serialize_size(&info);
|
||||
savestates_enabled = (info.size > 0);
|
||||
}
|
||||
|
||||
if (info.size)
|
||||
if (savestates_enabled)
|
||||
{
|
||||
switch (cmd)
|
||||
{
|
||||
|
||||
@@ -74,6 +74,7 @@ enum event_command
|
||||
CMD_EVENT_RESET,
|
||||
CMD_EVENT_SET_PER_GAME_RESOLUTION,
|
||||
CMD_EVENT_SET_FRAME_LIMIT,
|
||||
CMD_EVENT_VRR_RUNLOOP_TOGGLE,
|
||||
/* Loads core. */
|
||||
CMD_EVENT_LOAD_CORE,
|
||||
CMD_EVENT_LOAD_CORE_PERSIST,
|
||||
@@ -442,6 +443,7 @@ static const struct cmd_map map[] = {
|
||||
{ "FAST_FORWARD_HOLD", RARCH_FAST_FORWARD_HOLD_KEY },
|
||||
{ "SLOWMOTION", RARCH_SLOWMOTION_KEY },
|
||||
{ "SLOWMOTION_HOLD", RARCH_SLOWMOTION_HOLD_KEY },
|
||||
{ "VRR_RUNLOOP_TOGGLE", RARCH_VRR_RUNLOOP_TOGGLE },
|
||||
{ "LOAD_STATE", RARCH_LOAD_STATE_KEY },
|
||||
{ "SAVE_STATE", RARCH_SAVE_STATE_KEY },
|
||||
{ "FULLSCREEN_TOGGLE", RARCH_FULLSCREEN_TOGGLE_KEY },
|
||||
|
||||
+22
-4
@@ -558,6 +558,7 @@
|
||||
#define DEFAULT_OZONE_TRUNCATE_PLAYLIST_NAME true
|
||||
#define DEFAULT_OZONE_SORT_AFTER_TRUNCATE_PLAYLIST_NAME true
|
||||
#define DEFAULT_OZONE_SCROLL_CONTENT_METADATA false
|
||||
#define DEFAULT_OZONE_THUMBNAIL_SCALE_FACTOR 1.0f
|
||||
#endif
|
||||
|
||||
#define DEFAULT_SETTINGS_SHOW_DRIVERS true
|
||||
@@ -666,6 +667,9 @@ static const bool menu_show_core_updater = false;
|
||||
#else
|
||||
static const bool menu_show_core_updater = true;
|
||||
#endif
|
||||
#ifdef HAVE_MIST
|
||||
static const bool menu_show_core_manager_steam = true;
|
||||
#endif
|
||||
static const bool menu_show_legacy_thumbnail_updater = false;
|
||||
static const bool menu_show_sublabels = true;
|
||||
static const bool menu_dynamic_wallpaper_enable = true;
|
||||
@@ -730,10 +734,12 @@ static const bool content_show_playlists = true;
|
||||
#if defined(HAVE_LIBRETRODB)
|
||||
#define DEFAULT_MENU_CONTENT_SHOW_EXPLORE true
|
||||
#endif
|
||||
#define DEFAULT_MENU_CONTENT_SHOW_CONTENTLESS_CORES MENU_CONTENTLESS_CORES_DISPLAY_SINGLE_PURPOSE
|
||||
|
||||
#ifdef HAVE_XMB
|
||||
#define DEFAULT_XMB_ANIMATION 0
|
||||
#define DEFAULT_XMB_VERTICAL_FADE_FACTOR 100
|
||||
#define DEFAULT_XMB_TITLE_MARGIN 5
|
||||
|
||||
static const unsigned xmb_alpha_factor = 75;
|
||||
static const unsigned menu_font_color_red = 255;
|
||||
@@ -762,7 +768,7 @@ static const float menu_footer_opacity = 1.000;
|
||||
|
||||
static const float menu_header_opacity = 1.000;
|
||||
|
||||
#if defined(HAVE_OPENGLES2) || (defined(__MACH__) && (defined(__ppc__) || defined(__ppc64__)))
|
||||
#if defined(HAVE_OPENGLES2) || (defined(__MACH__) && defined(MAC_OS_X_VERSION_MAX_ALLOWED) && (MAC_OS_X_VERSION_MAX_ALLOWED < 101200))
|
||||
#define DEFAULT_MENU_SHADER_PIPELINE 1
|
||||
#else
|
||||
#define DEFAULT_MENU_SHADER_PIPELINE 2
|
||||
@@ -850,7 +856,7 @@ static const unsigned input_backtouch_toggle = false;
|
||||
|
||||
#define DEFAULT_OVERLAY_SHOW_INPUTS_PORT 0
|
||||
|
||||
#if defined(ANDROID) || defined(_WIN32)
|
||||
#if defined(ANDROID) || defined(_WIN32) || defined(HAVE_STEAM)
|
||||
#define DEFAULT_MENU_SWAP_OK_CANCEL_BUTTONS true
|
||||
#else
|
||||
#define DEFAULT_MENU_SWAP_OK_CANCEL_BUTTONS false
|
||||
@@ -944,12 +950,20 @@ static const float message_bgcolor_opacity = 1.0f;
|
||||
#define DEFAULT_ALLOW_ROTATE true
|
||||
|
||||
#if defined(_3DS)
|
||||
/* Enable New3DS clock and L2 cache */
|
||||
static const bool new3ds_speedup_enable = true;
|
||||
/* Enable bottom LCD screen */
|
||||
static const bool video_3ds_lcd_bottom = true;
|
||||
static const bool video_3ds_lcd_bottom = true;
|
||||
/* Sets video display mode (3D, 2D, etc.) */
|
||||
static const unsigned video_3ds_display_mode = CTR_VIDEO_MODE_3D;
|
||||
#endif
|
||||
|
||||
#ifdef WIIU
|
||||
/* On Wii U, whether to optimize for the native TV resolution
|
||||
* or exactly 2x the Wii U GamePad resolution. */
|
||||
#define DEFAULT_WIIU_PREFER_DRC false
|
||||
#endif
|
||||
|
||||
/* AUDIO */
|
||||
|
||||
/* Will enable audio or not. */
|
||||
@@ -1144,7 +1158,7 @@ static const bool audio_enable_menu_bgm = false;
|
||||
#define DEFAULT_REWIND_GRANULARITY 1
|
||||
#endif
|
||||
/* Pause gameplay when gameplay loses focus. */
|
||||
#if defined(EMSCRIPTEN) || defined(WEBOS)
|
||||
#if defined(EMSCRIPTEN)
|
||||
#define DEFAULT_PAUSE_NONACTIVE false
|
||||
#else
|
||||
#define DEFAULT_PAUSE_NONACTIVE true
|
||||
@@ -1252,6 +1266,10 @@ static const bool savestate_thumbnail_enable = false;
|
||||
|
||||
/* Maximum fast forward ratio. */
|
||||
#define DEFAULT_FASTFORWARD_RATIO 0.0
|
||||
#define MAXIMUM_FASTFORWARD_RATIO 50.0
|
||||
|
||||
/* Skip frames when fast forwarding. */
|
||||
#define DEFAULT_FASTFORWARD_FRAMESKIP true
|
||||
|
||||
/* Enable runloop for variable refresh rate screens. Force x1 speed while handling fast forward too. */
|
||||
#define DEFAULT_VRR_RUNLOOP_ENABLE false
|
||||
|
||||
@@ -304,6 +304,13 @@ static const struct retro_keybind retro_keybinds_1[] = {
|
||||
RARCH_SLOWMOTION_HOLD_KEY, NO_BTN, NO_BTN, 0,
|
||||
true
|
||||
},
|
||||
{
|
||||
NULL, NULL,
|
||||
AXIS_NONE, AXIS_NONE, AXIS_NONE,
|
||||
MENU_ENUM_LABEL_VALUE_INPUT_META_VRR_RUNLOOP_TOGGLE, RETROK_UNKNOWN,
|
||||
RARCH_VRR_RUNLOOP_TOGGLE, NO_BTN, NO_BTN, 0,
|
||||
true
|
||||
},
|
||||
{
|
||||
NULL, NULL,
|
||||
AXIS_NONE, AXIS_NONE, AXIS_NONE,
|
||||
@@ -886,6 +893,13 @@ static const struct retro_keybind retro_keybinds_1[] = {
|
||||
RARCH_SLOWMOTION_HOLD_KEY, NO_BTN, NO_BTN, 0,
|
||||
true
|
||||
},
|
||||
{
|
||||
NULL, NULL,
|
||||
AXIS_NONE, AXIS_NONE, AXIS_NONE,
|
||||
MENU_ENUM_LABEL_VALUE_INPUT_META_VRR_RUNLOOP_TOGGLE, RETROK_UNKNOWN,
|
||||
RARCH_VRR_RUNLOOP_TOGGLE, NO_BTN, NO_BTN, 0,
|
||||
true
|
||||
},
|
||||
{
|
||||
NULL, NULL,
|
||||
AXIS_NONE, AXIS_NONE, AXIS_NONE,
|
||||
@@ -1468,6 +1482,13 @@ static const struct retro_keybind retro_keybinds_1[] = {
|
||||
RARCH_SLOWMOTION_HOLD_KEY, NO_BTN, NO_BTN, 0,
|
||||
true
|
||||
},
|
||||
{
|
||||
NULL, NULL,
|
||||
AXIS_NONE, AXIS_NONE, AXIS_NONE,
|
||||
MENU_ENUM_LABEL_VALUE_INPUT_META_VRR_RUNLOOP_TOGGLE, RETROK_UNKNOWN,
|
||||
RARCH_VRR_RUNLOOP_TOGGLE, NO_BTN, NO_BTN, 0,
|
||||
true
|
||||
},
|
||||
{
|
||||
NULL, NULL,
|
||||
AXIS_NONE, AXIS_NONE, AXIS_NONE,
|
||||
|
||||
@@ -302,12 +302,6 @@
|
||||
#define SUPPORTS_NETPLAY false
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
#define SUPPORTS_PYTHON true
|
||||
#else
|
||||
#define SUPPORTS_PYTHON false
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_COCOA) || defined(HAVE_COCOATOUCH) || defined(HAVE_COCOA_METAL)
|
||||
#define SUPPORTS_COCOA true
|
||||
#else
|
||||
|
||||
+237
-194
@@ -308,6 +308,7 @@ const struct input_bind_map input_config_bind_map[RARCH_BIND_LIST_END_NULL] = {
|
||||
DECLARE_META_BIND(2, hold_fast_forward, RARCH_FAST_FORWARD_HOLD_KEY, MENU_ENUM_LABEL_VALUE_INPUT_META_FAST_FORWARD_HOLD_KEY),
|
||||
DECLARE_META_BIND(1, toggle_slowmotion, RARCH_SLOWMOTION_KEY, MENU_ENUM_LABEL_VALUE_INPUT_META_SLOWMOTION_KEY),
|
||||
DECLARE_META_BIND(2, hold_slowmotion, RARCH_SLOWMOTION_HOLD_KEY, MENU_ENUM_LABEL_VALUE_INPUT_META_SLOWMOTION_HOLD_KEY),
|
||||
DECLARE_META_BIND(2, toggle_vrr_runloop, RARCH_VRR_RUNLOOP_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_VRR_RUNLOOP_TOGGLE),
|
||||
DECLARE_META_BIND(1, load_state, RARCH_LOAD_STATE_KEY, MENU_ENUM_LABEL_VALUE_INPUT_META_LOAD_STATE_KEY),
|
||||
DECLARE_META_BIND(1, save_state, RARCH_SAVE_STATE_KEY, MENU_ENUM_LABEL_VALUE_INPUT_META_SAVE_STATE_KEY),
|
||||
DECLARE_META_BIND(2, toggle_fullscreen, RARCH_FULLSCREEN_TOGGLE_KEY, MENU_ENUM_LABEL_VALUE_INPUT_META_FULLSCREEN_TOGGLE_KEY),
|
||||
@@ -1654,6 +1655,7 @@ static struct config_bool_setting *populate_settings_bool(
|
||||
SETTING_BOOL("ui_menubar_enable", &settings->bools.ui_menubar_enable, true, DEFAULT_UI_MENUBAR_ENABLE, false);
|
||||
SETTING_BOOL("suspend_screensaver_enable", &settings->bools.ui_suspend_screensaver_enable, true, true, false);
|
||||
SETTING_BOOL("rewind_enable", &settings->bools.rewind_enable, true, DEFAULT_REWIND_ENABLE, false);
|
||||
SETTING_BOOL("fastforward_frameskip", &settings->bools.fastforward_frameskip, true, DEFAULT_FASTFORWARD_FRAMESKIP, false);
|
||||
SETTING_BOOL("vrr_runloop_enable", &settings->bools.vrr_runloop_enable, true, DEFAULT_VRR_RUNLOOP_ENABLE, false);
|
||||
SETTING_BOOL("apply_cheats_after_toggle", &settings->bools.apply_cheats_after_toggle, true, DEFAULT_APPLY_CHEATS_AFTER_TOGGLE, false);
|
||||
SETTING_BOOL("apply_cheats_after_load", &settings->bools.apply_cheats_after_load, true, DEFAULT_APPLY_CHEATS_AFTER_LOAD, false);
|
||||
@@ -1861,6 +1863,9 @@ static struct config_bool_setting *populate_settings_bool(
|
||||
SETTING_BOOL("menu_show_online_updater", &settings->bools.menu_show_online_updater, true, menu_show_online_updater, false);
|
||||
SETTING_BOOL("menu_show_core_updater", &settings->bools.menu_show_core_updater, true, menu_show_core_updater, false);
|
||||
SETTING_BOOL("menu_show_legacy_thumbnail_updater", &settings->bools.menu_show_legacy_thumbnail_updater, true, menu_show_legacy_thumbnail_updater, false);
|
||||
#ifdef HAVE_MIST
|
||||
SETTING_BOOL("menu_show_core_manager_steam", &settings->bools.menu_show_core_manager_steam, true, menu_show_core_manager_steam, false);
|
||||
#endif
|
||||
SETTING_BOOL("filter_by_current_core", &settings->bools.filter_by_current_core, true, DEFAULT_FILTER_BY_CURRENT_CORE, false);
|
||||
SETTING_BOOL("rgui_show_start_screen", &settings->bools.menu_show_start_screen, false, false /* TODO */, false);
|
||||
SETTING_BOOL("menu_navigation_wraparound_enable", &settings->bools.menu_navigation_wraparound_enable, true, true, false);
|
||||
@@ -1898,7 +1903,7 @@ static struct config_bool_setting *populate_settings_bool(
|
||||
SETTING_BOOL("cheevos_test_unofficial", &settings->bools.cheevos_test_unofficial, true, false, false);
|
||||
SETTING_BOOL("cheevos_hardcore_mode_enable", &settings->bools.cheevos_hardcore_mode_enable, true, true, false);
|
||||
SETTING_BOOL("cheevos_challenge_indicators", &settings->bools.cheevos_challenge_indicators, true, true, false);
|
||||
SETTING_BOOL("cheevos_richpresence_enable", &settings->bools.cheevos_richpresence_enable, true, false, false);
|
||||
SETTING_BOOL("cheevos_richpresence_enable", &settings->bools.cheevos_richpresence_enable, true, true, false);
|
||||
SETTING_BOOL("cheevos_unlock_sound_enable", &settings->bools.cheevos_unlock_sound_enable, true, false, false);
|
||||
SETTING_BOOL("cheevos_verbose_enable", &settings->bools.cheevos_verbose_enable, true, true, false);
|
||||
SETTING_BOOL("cheevos_auto_screenshot", &settings->bools.cheevos_auto_screenshot, true, false, false);
|
||||
@@ -1976,9 +1981,14 @@ static struct config_bool_setting *populate_settings_bool(
|
||||
SETTING_BOOL("sustained_performance_mode", &settings->bools.sustained_performance_mode, true, sustained_performance_mode, false);
|
||||
|
||||
#ifdef _3DS
|
||||
SETTING_BOOL("new3ds_speedup_enable", &settings->bools.new3ds_speedup_enable, true, new3ds_speedup_enable, false);
|
||||
SETTING_BOOL("video_3ds_lcd_bottom", &settings->bools.video_3ds_lcd_bottom, true, video_3ds_lcd_bottom, false);
|
||||
#endif
|
||||
|
||||
#ifdef WIIU
|
||||
SETTING_BOOL("video_wiiu_prefer_drc", &settings->bools.video_wiiu_prefer_drc, true, DEFAULT_WIIU_PREFER_DRC, false);
|
||||
#endif
|
||||
|
||||
SETTING_BOOL("playlist_use_old_format", &settings->bools.playlist_use_old_format, true, DEFAULT_PLAYLIST_USE_OLD_FORMAT, false);
|
||||
SETTING_BOOL("playlist_compression", &settings->bools.playlist_compression, true, DEFAULT_PLAYLIST_COMPRESSION, false);
|
||||
SETTING_BOOL("content_runtime_log", &settings->bools.content_runtime_log, true, DEFAULT_CONTENT_RUNTIME_LOG, false);
|
||||
@@ -2061,6 +2071,9 @@ static struct config_float_setting *populate_settings_float(
|
||||
#if defined(HAVE_MATERIALUI) || defined(HAVE_XMB) || defined(HAVE_OZONE)
|
||||
SETTING_FLOAT("menu_screensaver_animation_speed", &settings->floats.menu_screensaver_animation_speed, true, DEFAULT_MENU_SCREENSAVER_ANIMATION_SPEED, false);
|
||||
#endif
|
||||
#ifdef HAVE_OZONE
|
||||
SETTING_FLOAT("ozone_thumbnail_scale_factor", &settings->floats.ozone_thumbnail_scale_factor, true, DEFAULT_OZONE_THUMBNAIL_SCALE_FACTOR, false);
|
||||
#endif
|
||||
#endif
|
||||
SETTING_FLOAT("video_message_pos_x", &settings->floats.video_msg_pos_x, true, message_pos_offset_x, false);
|
||||
SETTING_FLOAT("video_message_pos_y", &settings->floats.video_msg_pos_y, true, message_pos_offset_y, false);
|
||||
@@ -2161,6 +2174,7 @@ static struct config_uint_setting *populate_settings_uint(
|
||||
SETTING_UINT("menu_ticker_type", &settings->uints.menu_ticker_type, true, DEFAULT_MENU_TICKER_TYPE, false);
|
||||
SETTING_UINT("menu_scroll_delay", &settings->uints.menu_scroll_delay, true, DEFAULT_MENU_SCROLL_DELAY, false);
|
||||
SETTING_UINT("content_show_add_entry", &settings->uints.menu_content_show_add_entry, true, DEFAULT_MENU_CONTENT_SHOW_ADD_ENTRY, false);
|
||||
SETTING_UINT("content_show_contentless_cores", &settings->uints.menu_content_show_contentless_cores, true, DEFAULT_MENU_CONTENT_SHOW_CONTENTLESS_CORES, false);
|
||||
SETTING_UINT("menu_screensaver_timeout", &settings->uints.menu_screensaver_timeout, true, DEFAULT_MENU_SCREENSAVER_TIMEOUT, false);
|
||||
#if defined(HAVE_MATERIALUI) || defined(HAVE_XMB) || defined(HAVE_OZONE)
|
||||
SETTING_UINT("menu_screensaver_animation", &settings->uints.menu_screensaver_animation, true, DEFAULT_MENU_SCREENSAVER_ANIMATION, false);
|
||||
@@ -2197,6 +2211,7 @@ static struct config_uint_setting *populate_settings_uint(
|
||||
SETTING_UINT("menu_font_color_blue", &settings->uints.menu_font_color_blue, true, menu_font_color_blue, false);
|
||||
SETTING_UINT("menu_xmb_thumbnail_scale_factor", &settings->uints.menu_xmb_thumbnail_scale_factor, true, xmb_thumbnail_scale_factor, false);
|
||||
SETTING_UINT("menu_xmb_vertical_fade_factor",&settings->uints.menu_xmb_vertical_fade_factor, true, DEFAULT_XMB_VERTICAL_FADE_FACTOR, false);
|
||||
SETTING_UINT("menu_xmb_title_margin", &settings->uints.menu_xmb_title_margin, true, DEFAULT_XMB_TITLE_MARGIN, false);
|
||||
#endif
|
||||
SETTING_UINT("materialui_menu_color_theme", &settings->uints.menu_materialui_color_theme, true, DEFAULT_MATERIALUI_THEME, false);
|
||||
SETTING_UINT("materialui_menu_transition_animation", &settings->uints.menu_materialui_transition_animation, true, DEFAULT_MATERIALUI_TRANSITION_ANIM, false);
|
||||
@@ -2620,7 +2635,7 @@ void config_set_defaults(void *data)
|
||||
#endif
|
||||
|
||||
input_config_reset();
|
||||
input_remapping_deinit();
|
||||
input_remapping_deinit(false);
|
||||
input_remapping_set_defaults(false);
|
||||
|
||||
/* Verify that binds are in proper order. */
|
||||
@@ -3408,9 +3423,6 @@ static bool config_load_file(global_t *global,
|
||||
|
||||
snprintf(buf, sizeof(buf), "input_player%u_mouse_index", i + 1);
|
||||
CONFIG_GET_INT_BASE(conf, settings, uints.input_mouse_index[i], buf);
|
||||
|
||||
snprintf(buf, sizeof(buf), "input_libretro_device_p%u", i + 1);
|
||||
CONFIG_GET_INT_BASE(conf, settings, uints.input_libretro_device[i], buf);
|
||||
}
|
||||
|
||||
/* LED map for use by the led driver */
|
||||
@@ -3821,34 +3833,45 @@ bool config_load_override(void *data)
|
||||
const char *core_name = system ?
|
||||
system->info.library_name : NULL;
|
||||
const char *rarch_path_basename = path_get(RARCH_PATH_BASENAME);
|
||||
const char *game_name = path_basename(rarch_path_basename);
|
||||
const char *game_name = NULL;
|
||||
settings_t *settings = config_st;
|
||||
bool has_content = !string_is_empty(rarch_path_basename);
|
||||
|
||||
if (!string_is_empty(rarch_path_basename))
|
||||
fill_pathname_parent_dir_name(content_dir_name,
|
||||
rarch_path_basename, sizeof(content_dir_name));
|
||||
core_path[0] = '\0';
|
||||
game_path[0] = '\0';
|
||||
content_path[0] = '\0';
|
||||
content_dir_name[0] = '\0';
|
||||
config_directory[0] = '\0';
|
||||
|
||||
if (string_is_empty(core_name) || string_is_empty(game_name))
|
||||
/* Cannot load an override if we have no core */
|
||||
if (string_is_empty(core_name))
|
||||
return false;
|
||||
|
||||
config_directory[0] = core_path[0] = game_path[0] = '\0';
|
||||
|
||||
fill_pathname_application_special(config_directory, sizeof(config_directory),
|
||||
/* Get base config directory */
|
||||
fill_pathname_application_special(config_directory,
|
||||
sizeof(config_directory),
|
||||
APPLICATION_SPECIAL_DIRECTORY_CONFIG);
|
||||
|
||||
/* Concatenate strings into full paths for core_path, game_path,
|
||||
* content_path */
|
||||
fill_pathname_join_special_ext(game_path,
|
||||
config_directory, core_name,
|
||||
game_name,
|
||||
".cfg",
|
||||
sizeof(game_path));
|
||||
/* Concatenate strings into full paths for core_path,
|
||||
* game_path, content_path */
|
||||
if (has_content)
|
||||
{
|
||||
fill_pathname_parent_dir_name(content_dir_name,
|
||||
rarch_path_basename, sizeof(content_dir_name));
|
||||
game_name = path_basename(rarch_path_basename);
|
||||
|
||||
fill_pathname_join_special_ext(content_path,
|
||||
config_directory, core_name,
|
||||
content_dir_name,
|
||||
".cfg",
|
||||
sizeof(content_path));
|
||||
fill_pathname_join_special_ext(game_path,
|
||||
config_directory, core_name,
|
||||
game_name,
|
||||
".cfg",
|
||||
sizeof(game_path));
|
||||
|
||||
fill_pathname_join_special_ext(content_path,
|
||||
config_directory, core_name,
|
||||
content_dir_name,
|
||||
".cfg",
|
||||
sizeof(content_path));
|
||||
}
|
||||
|
||||
fill_pathname_join_special_ext(core_path,
|
||||
config_directory, core_name,
|
||||
@@ -3871,69 +3894,72 @@ bool config_load_override(void *data)
|
||||
RARCH_LOG("[Overrides]: No core-specific overrides found at \"%s\".\n",
|
||||
core_path);
|
||||
|
||||
/* per-content-dir overrides */
|
||||
/* Create a new config file from content_path */
|
||||
if (config_file_exists(content_path))
|
||||
if (has_content)
|
||||
{
|
||||
char temp_path[PATH_MAX_LENGTH];
|
||||
/* per-content-dir overrides */
|
||||
/* Create a new config file from content_path */
|
||||
if (config_file_exists(content_path))
|
||||
{
|
||||
char temp_path[PATH_MAX_LENGTH];
|
||||
|
||||
RARCH_LOG("[Overrides]: Content dir-specific overrides found at \"%s\".\n",
|
||||
RARCH_LOG("[Overrides]: Content dir-specific overrides found at \"%s\".\n",
|
||||
content_path);
|
||||
|
||||
if (should_append)
|
||||
{
|
||||
RARCH_LOG("[Overrides]: Content dir-specific overrides stacking on top of previous overrides.\n");
|
||||
snprintf(temp_path, sizeof(temp_path),
|
||||
"%s|%s",
|
||||
path_get(RARCH_PATH_CONFIG_APPEND),
|
||||
content_path
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
temp_path[0] = '\0';
|
||||
strlcpy(temp_path, content_path, sizeof(temp_path));
|
||||
}
|
||||
|
||||
path_set(RARCH_PATH_CONFIG_APPEND, temp_path);
|
||||
|
||||
should_append = true;
|
||||
}
|
||||
else
|
||||
RARCH_LOG("[Overrides]: No content-dir-specific overrides found at \"%s\".\n",
|
||||
content_path);
|
||||
|
||||
if (should_append)
|
||||
/* per-game overrides */
|
||||
/* Create a new config file from game_path */
|
||||
if (config_file_exists(game_path))
|
||||
{
|
||||
RARCH_LOG("[Overrides]: Content dir-specific overrides stacking on top of previous overrides.\n");
|
||||
snprintf(temp_path, sizeof(temp_path),
|
||||
"%s|%s",
|
||||
path_get(RARCH_PATH_CONFIG_APPEND),
|
||||
content_path
|
||||
);
|
||||
char temp_path[PATH_MAX_LENGTH];
|
||||
|
||||
RARCH_LOG("[Overrides]: Game-specific overrides found at \"%s\".\n",
|
||||
game_path);
|
||||
|
||||
if (should_append)
|
||||
{
|
||||
RARCH_LOG("[Overrides]: Game-specific overrides stacking on top of previous overrides.\n");
|
||||
snprintf(temp_path, sizeof(temp_path),
|
||||
"%s|%s",
|
||||
path_get(RARCH_PATH_CONFIG_APPEND),
|
||||
game_path
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
temp_path[0] = '\0';
|
||||
strlcpy(temp_path, game_path, sizeof(temp_path));
|
||||
}
|
||||
|
||||
path_set(RARCH_PATH_CONFIG_APPEND, temp_path);
|
||||
|
||||
should_append = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
temp_path[0] = '\0';
|
||||
strlcpy(temp_path, content_path, sizeof(temp_path));
|
||||
}
|
||||
|
||||
path_set(RARCH_PATH_CONFIG_APPEND, temp_path);
|
||||
|
||||
should_append = true;
|
||||
RARCH_LOG("[Overrides]: No game-specific overrides found at \"%s\".\n",
|
||||
game_path);
|
||||
}
|
||||
else
|
||||
RARCH_LOG("[Overrides]: No content-dir-specific overrides found at \"%s\".\n",
|
||||
content_path);
|
||||
|
||||
/* per-game overrides */
|
||||
/* Create a new config file from game_path */
|
||||
if (config_file_exists(game_path))
|
||||
{
|
||||
char temp_path[PATH_MAX_LENGTH];
|
||||
|
||||
RARCH_LOG("[Overrides]: Game-specific overrides found at \"%s\".\n",
|
||||
game_path);
|
||||
|
||||
if (should_append)
|
||||
{
|
||||
RARCH_LOG("[Overrides]: Game-specific overrides stacking on top of previous overrides.\n");
|
||||
snprintf(temp_path, sizeof(temp_path),
|
||||
"%s|%s",
|
||||
path_get(RARCH_PATH_CONFIG_APPEND),
|
||||
game_path
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
temp_path[0] = '\0';
|
||||
strlcpy(temp_path, game_path, sizeof(temp_path));
|
||||
}
|
||||
|
||||
path_set(RARCH_PATH_CONFIG_APPEND, temp_path);
|
||||
|
||||
should_append = true;
|
||||
}
|
||||
else
|
||||
RARCH_LOG("[Overrides]: No game-specific overrides found at \"%s\".\n",
|
||||
game_path);
|
||||
|
||||
if (!should_append)
|
||||
return false;
|
||||
@@ -4008,9 +4034,7 @@ bool config_unload_override(void)
|
||||
bool config_load_remap(const char *directory_input_remapping,
|
||||
void *data)
|
||||
{
|
||||
char content_dir_name[PATH_MAX_LENGTH] = { 0 };
|
||||
/* path to the directory containing retroarch.cfg (prefix) */
|
||||
char remap_directory[PATH_MAX_LENGTH];
|
||||
char content_dir_name[PATH_MAX_LENGTH];
|
||||
/* final path for core-specific configuration (prefix+suffix) */
|
||||
char core_path[PATH_MAX_LENGTH];
|
||||
/* final path for game-specific configuration (prefix+suffix) */
|
||||
@@ -4021,52 +4045,56 @@ bool config_load_remap(const char *directory_input_remapping,
|
||||
rarch_system_info_t *system = (rarch_system_info_t*)data;
|
||||
const char *core_name = system ? system->info.library_name : NULL;
|
||||
const char *rarch_path_basename = path_get(RARCH_PATH_BASENAME);
|
||||
const char *game_name = path_basename(rarch_path_basename);
|
||||
const char *game_name = NULL;
|
||||
bool has_content = !string_is_empty(rarch_path_basename);
|
||||
enum msg_hash_enums msg_remap_loaded = MSG_GAME_REMAP_FILE_LOADED;
|
||||
settings_t *settings = config_st;
|
||||
bool notification_show_remap_load = settings->bools.notification_show_remap_load;
|
||||
|
||||
if (string_is_empty(core_name))
|
||||
content_dir_name[0] = '\0';
|
||||
core_path[0] = '\0';
|
||||
game_path[0] = '\0';
|
||||
content_path[0] = '\0';
|
||||
|
||||
/* > Cannot load remaps if we have no core
|
||||
* > Cannot load remaps if remap directory is unset */
|
||||
if (string_is_empty(core_name) ||
|
||||
string_is_empty(directory_input_remapping))
|
||||
return false;
|
||||
|
||||
/* Remap directory: remap_directory.
|
||||
* Try remap directory setting, no fallbacks defined */
|
||||
if (string_is_empty(directory_input_remapping))
|
||||
return false;
|
||||
RARCH_LOG("[Remaps]: Remap directory: \"%s\".\n", directory_input_remapping);
|
||||
|
||||
if (!string_is_empty(rarch_path_basename))
|
||||
/* Concatenate strings into full paths for core_path,
|
||||
* game_path, content_path */
|
||||
if (has_content)
|
||||
{
|
||||
fill_pathname_parent_dir_name(content_dir_name,
|
||||
rarch_path_basename, sizeof(content_dir_name));
|
||||
game_name = path_basename(rarch_path_basename);
|
||||
|
||||
remap_directory[0] = core_path[0] = game_path[0] = '\0';
|
||||
fill_pathname_join_special_ext(game_path,
|
||||
directory_input_remapping, core_name,
|
||||
game_name,
|
||||
FILE_PATH_REMAP_EXTENSION,
|
||||
sizeof(game_path));
|
||||
|
||||
strlcpy(remap_directory,
|
||||
directory_input_remapping, sizeof(remap_directory));
|
||||
RARCH_LOG("[Remaps]: Remap directory: \"%s\".\n", remap_directory);
|
||||
fill_pathname_join_special_ext(content_path,
|
||||
directory_input_remapping, core_name,
|
||||
content_dir_name,
|
||||
FILE_PATH_REMAP_EXTENSION,
|
||||
sizeof(content_path));
|
||||
}
|
||||
|
||||
/* Concatenate strings into full paths for core_path, game_path */
|
||||
fill_pathname_join_special_ext(core_path,
|
||||
remap_directory, core_name,
|
||||
directory_input_remapping, core_name,
|
||||
core_name,
|
||||
FILE_PATH_REMAP_EXTENSION,
|
||||
sizeof(core_path));
|
||||
|
||||
fill_pathname_join_special_ext(content_path,
|
||||
remap_directory, core_name,
|
||||
content_dir_name,
|
||||
FILE_PATH_REMAP_EXTENSION,
|
||||
sizeof(content_path));
|
||||
|
||||
fill_pathname_join_special_ext(game_path,
|
||||
remap_directory, core_name,
|
||||
game_name,
|
||||
FILE_PATH_REMAP_EXTENSION,
|
||||
sizeof(game_path));
|
||||
|
||||
input_remapping_set_defaults(false);
|
||||
|
||||
/* If a game remap file exists, load it. */
|
||||
if ((new_conf = config_file_new_from_path_to_string(game_path)))
|
||||
if (has_content && (new_conf = config_file_new_from_path_to_string(game_path)))
|
||||
{
|
||||
bool ret = input_remapping_load_file(new_conf, game_path);
|
||||
config_file_free(new_conf);
|
||||
@@ -4082,7 +4110,7 @@ bool config_load_remap(const char *directory_input_remapping,
|
||||
}
|
||||
|
||||
/* If a content-dir remap file exists, load it. */
|
||||
if ((new_conf = config_file_new_from_path_to_string(content_path)))
|
||||
if (has_content && (new_conf = config_file_new_from_path_to_string(content_path)))
|
||||
{
|
||||
bool ret = input_remapping_load_file(new_conf, content_path);
|
||||
config_file_free(new_conf);
|
||||
@@ -4674,8 +4702,6 @@ bool config_save_file(const char *path)
|
||||
config_set_int(conf, cfg, settings->uints.input_device[i]);
|
||||
snprintf(cfg, sizeof(cfg), "input_player%u_joypad_index", i + 1);
|
||||
config_set_int(conf, cfg, settings->uints.input_joypad_index[i]);
|
||||
snprintf(cfg, sizeof(cfg), "input_libretro_device_p%u", i + 1);
|
||||
config_set_int(conf, cfg, input_config_get_device(i));
|
||||
snprintf(cfg, sizeof(cfg), "input_player%u_analog_dpad_mode", i + 1);
|
||||
config_set_int(conf, cfg, settings->uints.input_analog_dpad_mode[i]);
|
||||
snprintf(cfg, sizeof(cfg), "input_player%u_mouse_index", i + 1);
|
||||
@@ -4787,6 +4813,7 @@ bool config_save_overrides(enum override_type type, void *data)
|
||||
char core_path[PATH_MAX_LENGTH];
|
||||
char game_path[PATH_MAX_LENGTH];
|
||||
char content_path[PATH_MAX_LENGTH];
|
||||
char content_dir_name[PATH_MAX_LENGTH];
|
||||
settings_t *overrides = config_st;
|
||||
int bool_settings_size = sizeof(settings->bools) / sizeof(settings->bools.placeholder);
|
||||
int float_settings_size = sizeof(settings->floats) / sizeof(settings->floats.placeholder);
|
||||
@@ -4798,50 +4825,38 @@ bool config_save_overrides(enum override_type type, void *data)
|
||||
rarch_system_info_t *system = (rarch_system_info_t*)data;
|
||||
const char *core_name = system ? system->info.library_name : NULL;
|
||||
const char *rarch_path_basename = path_get(RARCH_PATH_BASENAME);
|
||||
const char *game_name = path_basename(rarch_path_basename);
|
||||
char content_dir_name[PATH_MAX_LENGTH];
|
||||
const char *game_name = NULL;
|
||||
bool has_content = !string_is_empty(rarch_path_basename);
|
||||
|
||||
if (!string_is_empty(rarch_path_basename))
|
||||
fill_pathname_parent_dir_name(content_dir_name, rarch_path_basename, sizeof(content_dir_name));
|
||||
config_directory[0] = '\0';
|
||||
override_directory[0] = '\0';
|
||||
core_path[0] = '\0';
|
||||
game_path[0] = '\0';
|
||||
content_path[0] = '\0';
|
||||
content_dir_name[0] = '\0';
|
||||
|
||||
if (string_is_empty(core_name) || string_is_empty(game_name))
|
||||
/* > Cannot save an override if we have no core
|
||||
* > Cannot save a per-game or per-content-directory
|
||||
* override if we have no content */
|
||||
if (string_is_empty(core_name) ||
|
||||
(!has_content && (type != OVERRIDE_CORE)))
|
||||
return false;
|
||||
|
||||
settings = (settings_t*)calloc(1, sizeof(settings_t));
|
||||
settings = (settings_t*)calloc(1, sizeof(settings_t));
|
||||
conf = config_file_new_alloc();
|
||||
|
||||
config_directory[0] = override_directory[0] = core_path[0] =
|
||||
game_path[0] = '\0';
|
||||
|
||||
fill_pathname_application_special(config_directory, sizeof(config_directory),
|
||||
/* Get base config directory */
|
||||
fill_pathname_application_special(config_directory,
|
||||
sizeof(config_directory),
|
||||
APPLICATION_SPECIAL_DIRECTORY_CONFIG);
|
||||
|
||||
fill_pathname_join(override_directory, config_directory, core_name,
|
||||
fill_pathname_join(override_directory,
|
||||
config_directory, core_name,
|
||||
sizeof(override_directory));
|
||||
|
||||
/* Ensure base config directory exists */
|
||||
if (!path_is_directory(override_directory))
|
||||
path_mkdir(override_directory);
|
||||
|
||||
/* Concatenate strings into full paths for core_path, game_path */
|
||||
fill_pathname_join_special_ext(game_path,
|
||||
config_directory, core_name,
|
||||
game_name,
|
||||
FILE_PATH_CONFIG_EXTENSION,
|
||||
sizeof(game_path));
|
||||
|
||||
fill_pathname_join_special_ext(content_path,
|
||||
config_directory, core_name,
|
||||
content_dir_name,
|
||||
FILE_PATH_CONFIG_EXTENSION,
|
||||
sizeof(content_path));
|
||||
|
||||
fill_pathname_join_special_ext(core_path,
|
||||
config_directory, core_name,
|
||||
core_name,
|
||||
FILE_PATH_CONFIG_EXTENSION,
|
||||
sizeof(core_path));
|
||||
|
||||
if (!conf)
|
||||
conf = config_file_new_alloc();
|
||||
path_mkdir(override_directory);
|
||||
|
||||
/* Load the original config file in memory */
|
||||
config_load_file(global_get_ptr(),
|
||||
@@ -4967,14 +4982,32 @@ bool config_save_overrides(enum override_type type, void *data)
|
||||
switch (type)
|
||||
{
|
||||
case OVERRIDE_CORE:
|
||||
fill_pathname_join_special_ext(core_path,
|
||||
config_directory, core_name,
|
||||
core_name,
|
||||
FILE_PATH_CONFIG_EXTENSION,
|
||||
sizeof(core_path));
|
||||
RARCH_LOG ("[Overrides]: Path \"%s\".\n", core_path);
|
||||
ret = config_file_write(conf, core_path, true);
|
||||
break;
|
||||
case OVERRIDE_GAME:
|
||||
game_name = path_basename(rarch_path_basename);
|
||||
fill_pathname_join_special_ext(game_path,
|
||||
config_directory, core_name,
|
||||
game_name,
|
||||
FILE_PATH_CONFIG_EXTENSION,
|
||||
sizeof(game_path));
|
||||
RARCH_LOG ("[Overrides]: Path \"%s\".\n", game_path);
|
||||
ret = config_file_write(conf, game_path, true);
|
||||
break;
|
||||
case OVERRIDE_CONTENT_DIR:
|
||||
fill_pathname_parent_dir_name(content_dir_name,
|
||||
rarch_path_basename, sizeof(content_dir_name));
|
||||
fill_pathname_join_special_ext(content_path,
|
||||
config_directory, core_name,
|
||||
content_dir_name,
|
||||
FILE_PATH_CONFIG_EXTENSION,
|
||||
sizeof(content_path));
|
||||
RARCH_LOG ("[Overrides]: Path \"%s\".\n", content_path);
|
||||
ret = config_file_write(conf, content_path, true);
|
||||
break;
|
||||
@@ -5070,7 +5103,7 @@ bool input_remapping_load_file(void *data, const char *path)
|
||||
|
||||
if (!string_is_empty(runloop_st->name.remapfile))
|
||||
{
|
||||
input_remapping_deinit();
|
||||
input_remapping_deinit(false);
|
||||
input_remapping_set_defaults(false);
|
||||
}
|
||||
runloop_st->name.remapfile = strdup(path);
|
||||
@@ -5131,13 +5164,7 @@ bool input_remapping_load_file(void *data, const char *path)
|
||||
key_ident[0] = '\0';
|
||||
|
||||
fill_pathname_join_delim(stk_ident, s3,
|
||||
key_string, '$', sizeof(stk_ident));
|
||||
|
||||
snprintf(stk_ident,
|
||||
sizeof(stk_ident),
|
||||
"%s_%s",
|
||||
s3,
|
||||
key_string);
|
||||
key_string, '_', sizeof(stk_ident));
|
||||
|
||||
if (config_get_int(conf, stk_ident, &stk_remap))
|
||||
{
|
||||
@@ -5181,7 +5208,7 @@ bool input_remapping_load_file(void *data, const char *path)
|
||||
|
||||
/**
|
||||
* input_remapping_save_file:
|
||||
* @path : Path to remapping file (relative path).
|
||||
* @path : Path to remapping file.
|
||||
*
|
||||
* Saves remapping values to file.
|
||||
*
|
||||
@@ -5191,23 +5218,33 @@ bool input_remapping_save_file(const char *path)
|
||||
{
|
||||
bool ret;
|
||||
unsigned i, j;
|
||||
char remap_file[PATH_MAX_LENGTH];
|
||||
char remap_file_dir[PATH_MAX_LENGTH];
|
||||
char key_strings[RARCH_FIRST_CUSTOM_BIND + 8][8] = {
|
||||
"b", "y", "select", "start",
|
||||
"up", "down", "left", "right",
|
||||
"a", "x", "l", "r", "l2", "r2",
|
||||
"l3", "r3", "l_x+", "l_x-", "l_y+", "l_y-", "r_x+", "r_x-", "r_y+", "r_y-" };
|
||||
config_file_t *conf = NULL;
|
||||
settings_t *settings = config_st;
|
||||
unsigned max_users = settings->uints.input_max_users;
|
||||
const char *dir_input_remapping = settings->paths.directory_input_remapping;
|
||||
const char *remap_file = path;
|
||||
config_file_t *conf = NULL;
|
||||
runloop_state_t *runloop_st = runloop_state_get_ptr();
|
||||
settings_t *settings = config_st;
|
||||
unsigned max_users = settings->uints.input_max_users;
|
||||
|
||||
remap_file[0] = '\0';
|
||||
remap_file_dir[0] = '\0';
|
||||
|
||||
fill_pathname_join_concat(remap_file, dir_input_remapping, path,
|
||||
FILE_PATH_REMAP_EXTENSION,
|
||||
sizeof(remap_file));
|
||||
if (string_is_empty(remap_file))
|
||||
return false;
|
||||
|
||||
/* Create output directory, if required */
|
||||
strlcpy(remap_file_dir, remap_file, sizeof(remap_file_dir));
|
||||
path_parent_dir(remap_file_dir);
|
||||
|
||||
if (!string_is_empty(remap_file_dir) &&
|
||||
!path_is_directory(remap_file_dir) &&
|
||||
!path_mkdir(remap_file_dir))
|
||||
return false;
|
||||
|
||||
/* Attempt to load file */
|
||||
if (!(conf = config_file_new_from_path_to_string(remap_file)))
|
||||
{
|
||||
if (!(conf = config_file_new_alloc()))
|
||||
@@ -5253,14 +5290,20 @@ bool input_remapping_save_file(const char *path)
|
||||
for (j = 0; j < RARCH_FIRST_CUSTOM_BIND; j++)
|
||||
{
|
||||
char btn_ident[128];
|
||||
char key_ident[128];
|
||||
const char *key_string = key_strings[j];
|
||||
unsigned remap_id = settings->uints.input_remap_ids[i][j];
|
||||
unsigned keymap_id = settings->uints.input_keymapper_ids[i][j];
|
||||
const char *key_string = key_strings[j];
|
||||
|
||||
btn_ident[0] = '\0';
|
||||
key_ident[0] = '\0';
|
||||
|
||||
fill_pathname_join_delim(btn_ident, s1,
|
||||
key_string, '_', sizeof(btn_ident));
|
||||
fill_pathname_join_delim(key_ident, s2,
|
||||
key_string, '_', sizeof(key_ident));
|
||||
|
||||
/* only save values that have been modified */
|
||||
/* Only save modified button values */
|
||||
if (remap_id == j)
|
||||
config_unset(conf, btn_ident);
|
||||
else
|
||||
@@ -5272,48 +5315,48 @@ bool input_remapping_save_file(const char *path)
|
||||
settings->uints.input_remap_ids[i][j]);
|
||||
}
|
||||
|
||||
if (keymap_id != RETROK_UNKNOWN)
|
||||
{
|
||||
char key_ident[128];
|
||||
key_ident[0] = '\0';
|
||||
fill_pathname_join_delim(key_ident, s2,
|
||||
key_string, '_', sizeof(key_ident));
|
||||
/* Only save non-empty keymapper values */
|
||||
if (keymap_id == RETROK_UNKNOWN)
|
||||
config_unset(conf, key_ident);
|
||||
else
|
||||
config_set_int(conf, key_ident,
|
||||
settings->uints.input_keymapper_ids[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
for (j = RARCH_FIRST_CUSTOM_BIND; j < (RARCH_FIRST_CUSTOM_BIND + 8); j++)
|
||||
{
|
||||
char stk_ident[128];
|
||||
char key_ident[128];
|
||||
const char *key_string = key_strings[j];
|
||||
unsigned remap_id = settings->uints.input_remap_ids[i][j];
|
||||
unsigned keymap_id = settings->uints.input_keymapper_ids[i][j];
|
||||
const char *key_string = key_strings[j];
|
||||
|
||||
stk_ident[0] = '\0';
|
||||
key_ident[0] = '\0';
|
||||
|
||||
fill_pathname_join_delim(stk_ident, s3,
|
||||
key_string, '_', sizeof(stk_ident));
|
||||
fill_pathname_join_delim(key_ident, s2,
|
||||
key_string, '_', sizeof(key_ident));
|
||||
|
||||
/* Only save modified button values */
|
||||
if (remap_id == j)
|
||||
config_unset(conf, stk_ident);
|
||||
else
|
||||
{
|
||||
if (remap_id == RARCH_UNMAPPED)
|
||||
config_set_int(conf, stk_ident,
|
||||
-1);
|
||||
config_set_int(conf, stk_ident, -1);
|
||||
else
|
||||
config_set_int(conf, stk_ident,
|
||||
settings->uints.input_remap_ids[i][j]);
|
||||
}
|
||||
|
||||
if (keymap_id != RETROK_UNKNOWN)
|
||||
{
|
||||
char key_ident[128];
|
||||
key_ident[0] = '\0';
|
||||
fill_pathname_join_delim(key_ident, s2,
|
||||
key_string, '_', sizeof(key_ident));
|
||||
/* Only save non-empty keymapper values */
|
||||
if (keymap_id == RETROK_UNKNOWN)
|
||||
config_unset(conf, key_ident);
|
||||
else
|
||||
config_set_int(conf, key_ident,
|
||||
settings->uints.input_keymapper_ids[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(s1, sizeof(s1), "input_libretro_device_p%u", i + 1);
|
||||
@@ -5329,18 +5372,18 @@ bool input_remapping_save_file(const char *path)
|
||||
ret = config_file_write(conf, remap_file, true);
|
||||
config_file_free(conf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
/* Cache remap file path
|
||||
* > Must guard against the case where
|
||||
* runloop_st->name.remapfile itself
|
||||
* is passed to this function... */
|
||||
if (runloop_st->name.remapfile != remap_file)
|
||||
{
|
||||
if (runloop_st->name.remapfile)
|
||||
free(runloop_st->name.remapfile);
|
||||
runloop_st->name.remapfile = strdup(remap_file);
|
||||
}
|
||||
|
||||
bool input_remapping_remove_file(const char *path,
|
||||
const char *dir_input_remapping)
|
||||
{
|
||||
char remap_file[PATH_MAX_LENGTH];
|
||||
remap_file[0] = '\0';
|
||||
fill_pathname_join_concat(remap_file, dir_input_remapping, path,
|
||||
FILE_PATH_REMAP_EXTENSION,
|
||||
sizeof(remap_file));
|
||||
return filestream_delete(remap_file) == 0 ? true : false;
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
+12
-2
@@ -156,8 +156,7 @@ typedef struct settings
|
||||
unsigned input_joypad_index[MAX_USERS];
|
||||
unsigned input_device[MAX_USERS];
|
||||
unsigned input_mouse_index[MAX_USERS];
|
||||
/* Set by autoconfiguration in joypad_autoconfig_dir.
|
||||
* Does not override main binds. */
|
||||
|
||||
unsigned input_libretro_device[MAX_USERS];
|
||||
unsigned input_analog_dpad_mode[MAX_USERS];
|
||||
|
||||
@@ -279,6 +278,7 @@ typedef struct settings
|
||||
unsigned menu_xmb_color_theme;
|
||||
unsigned menu_xmb_thumbnail_scale_factor;
|
||||
unsigned menu_xmb_vertical_fade_factor;
|
||||
unsigned menu_xmb_title_margin;
|
||||
unsigned menu_materialui_color_theme;
|
||||
unsigned menu_materialui_transition_animation;
|
||||
unsigned menu_materialui_thumbnail_view_portrait;
|
||||
@@ -295,6 +295,7 @@ typedef struct settings
|
||||
unsigned menu_ticker_type;
|
||||
unsigned menu_scroll_delay;
|
||||
unsigned menu_content_show_add_entry;
|
||||
unsigned menu_content_show_contentless_cores;
|
||||
unsigned menu_screensaver_timeout;
|
||||
unsigned menu_screensaver_animation;
|
||||
|
||||
@@ -368,6 +369,7 @@ typedef struct settings
|
||||
float menu_ticker_speed;
|
||||
float menu_rgui_particle_effect_speed;
|
||||
float menu_screensaver_animation_speed;
|
||||
float ozone_thumbnail_scale_factor;
|
||||
|
||||
float audio_max_timing_skew;
|
||||
float audio_volume; /* dB scale. */
|
||||
@@ -564,6 +566,7 @@ typedef struct settings
|
||||
bool video_memory_show;
|
||||
bool video_msg_bgcolor_enable;
|
||||
bool video_3ds_lcd_bottom;
|
||||
bool video_wiiu_prefer_drc;
|
||||
bool video_notch_write_over_enable;
|
||||
#ifdef HAVE_VIDEO_LAYOUT
|
||||
bool video_layout_enable;
|
||||
@@ -657,6 +660,9 @@ typedef struct settings
|
||||
bool menu_horizontal_animation;
|
||||
bool menu_scroll_fast;
|
||||
bool menu_show_online_updater;
|
||||
#ifdef HAVE_MIST
|
||||
bool menu_show_core_manager_steam;
|
||||
#endif
|
||||
bool menu_show_core_updater;
|
||||
bool menu_show_load_core;
|
||||
bool menu_show_load_content;
|
||||
@@ -834,6 +840,7 @@ typedef struct settings
|
||||
bool history_list_enable;
|
||||
bool playlist_entry_rename;
|
||||
bool rewind_enable;
|
||||
bool fastforward_frameskip;
|
||||
bool vrr_runloop_enable;
|
||||
bool apply_cheats_after_toggle;
|
||||
bool apply_cheats_after_load;
|
||||
@@ -918,6 +925,9 @@ typedef struct settings
|
||||
bool ai_service_pause;
|
||||
|
||||
bool gamemode_enable;
|
||||
#ifdef _3DS
|
||||
bool new3ds_speedup_enable;
|
||||
#endif
|
||||
} bools;
|
||||
|
||||
} settings_t;
|
||||
|
||||
@@ -27,10 +27,6 @@
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
#ifdef HAVE_REWIND
|
||||
bool core_set_rewind_callbacks(void);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_NETWORKING
|
||||
bool core_set_netplay_callbacks(void);
|
||||
|
||||
|
||||
+423
-69
@@ -48,6 +48,7 @@
|
||||
/* Core Info Cache START */
|
||||
/*************************/
|
||||
|
||||
#define CORE_INFO_CACHE_VERSION "1.2"
|
||||
#define CORE_INFO_CACHE_DEFAULT_CAPACITY 8
|
||||
|
||||
/* TODO/FIXME: Apparently rzip compression is an issue on UWP */
|
||||
@@ -60,6 +61,7 @@ typedef struct
|
||||
core_info_t *items;
|
||||
size_t length;
|
||||
size_t capacity;
|
||||
char *version;
|
||||
bool refresh;
|
||||
} core_info_cache_list_t;
|
||||
|
||||
@@ -103,7 +105,9 @@ static bool CCJSONObjectMemberHandler(void *context,
|
||||
{
|
||||
CCJSONContext *pCtx = (CCJSONContext *)context;
|
||||
|
||||
if ((pCtx->object_depth == 2) && (pCtx->array_depth == 1) && length)
|
||||
if ((pCtx->object_depth == 2) &&
|
||||
(pCtx->array_depth == 1) &&
|
||||
length)
|
||||
{
|
||||
pCtx->current_string_val = NULL;
|
||||
pCtx->current_string_list_val = NULL;
|
||||
@@ -199,26 +203,31 @@ static bool CCJSONObjectMemberHandler(void *context,
|
||||
}
|
||||
else if (string_is_equal(pValue, "supports_no_game"))
|
||||
pCtx->current_entry_bool_val = &pCtx->core_info->supports_no_game;
|
||||
else if (string_is_equal(pValue, "single_purpose"))
|
||||
pCtx->current_entry_bool_val = &pCtx->core_info->single_purpose;
|
||||
else if (string_is_equal(pValue, "savestate_support_level"))
|
||||
pCtx->current_entry_uint_val = &pCtx->core_info->savestate_support_level;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if ((pCtx->object_depth == 3)
|
||||
&& (pCtx->array_depth == 1) && length)
|
||||
else if ((pCtx->object_depth == 3) &&
|
||||
(pCtx->array_depth == 1) &&
|
||||
length)
|
||||
{
|
||||
pCtx->current_string_val = NULL;
|
||||
pCtx->current_entry_uint_val = NULL;
|
||||
|
||||
if (pCtx->to_core_file_id)
|
||||
{
|
||||
if (string_is_equal(pValue, "str"))
|
||||
if (string_is_equal(pValue, "str"))
|
||||
pCtx->current_string_val = &pCtx->core_info->core_file_id.str;
|
||||
else if (string_is_equal(pValue, "hash"))
|
||||
pCtx->current_entry_uint_val = &pCtx->core_info->core_file_id.hash;
|
||||
}
|
||||
}
|
||||
else if ((pCtx->object_depth == 3)
|
||||
&& (pCtx->array_depth == 2)
|
||||
&& length)
|
||||
else if ((pCtx->object_depth == 3) &&
|
||||
(pCtx->array_depth == 2) &&
|
||||
length)
|
||||
{
|
||||
pCtx->current_string_val = NULL;
|
||||
pCtx->current_entry_bool_val = NULL;
|
||||
@@ -227,7 +236,7 @@ static bool CCJSONObjectMemberHandler(void *context,
|
||||
{
|
||||
size_t firmware_idx = pCtx->core_info->firmware_count - 1;
|
||||
|
||||
if (string_is_equal(pValue, "path"))
|
||||
if (string_is_equal(pValue, "path"))
|
||||
pCtx->current_string_val = &pCtx->core_info->firmware[firmware_idx].path;
|
||||
else if (string_is_equal(pValue, "desc"))
|
||||
pCtx->current_string_val = &pCtx->core_info->firmware[firmware_idx].desc;
|
||||
@@ -235,6 +244,15 @@ static bool CCJSONObjectMemberHandler(void *context,
|
||||
pCtx->current_entry_bool_val = &pCtx->core_info->firmware[firmware_idx].optional;
|
||||
}
|
||||
}
|
||||
else if ((pCtx->object_depth == 1) &&
|
||||
(pCtx->array_depth == 0) &&
|
||||
length)
|
||||
{
|
||||
pCtx->current_string_val = NULL;
|
||||
|
||||
if (string_is_equal(pValue, "version"))
|
||||
pCtx->current_string_val = &pCtx->core_info_cache_list->version;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -319,6 +337,11 @@ static bool CCJSONStartObjectHandler(void *context)
|
||||
pCtx->core_info = (core_info_t*)calloc(1, sizeof(core_info_t));
|
||||
if (!pCtx->core_info)
|
||||
return false;
|
||||
|
||||
/* Assume all cores have 'full' savestate support
|
||||
* by default */
|
||||
pCtx->core_info->savestate_support_level =
|
||||
CORE_INFO_SAVESTATE_DETERMINISTIC;
|
||||
}
|
||||
else if ((pCtx->object_depth == 3) && (pCtx->array_depth == 2))
|
||||
{
|
||||
@@ -448,11 +471,14 @@ static void core_info_copy(core_info_t *src, core_info_t *dst)
|
||||
? strdup(src->core_file_id.str) : NULL;
|
||||
dst->core_file_id.hash = src->core_file_id.hash;
|
||||
|
||||
dst->savestate_support_level = src->savestate_support_level;
|
||||
dst->has_info = src->has_info;
|
||||
dst->supports_no_game = src->supports_no_game;
|
||||
dst->single_purpose = src->single_purpose;
|
||||
dst->database_match_archive_member = src->database_match_archive_member;
|
||||
dst->is_experimental = src->is_experimental;
|
||||
dst->is_locked = src->is_locked;
|
||||
dst->is_standalone_exempt = src->is_standalone_exempt;
|
||||
dst->is_installed = src->is_installed;
|
||||
}
|
||||
|
||||
@@ -542,11 +568,14 @@ static void core_info_transfer(core_info_t *src, core_info_t *dst)
|
||||
src->core_file_id.str = NULL;
|
||||
dst->core_file_id.hash = src->core_file_id.hash;
|
||||
|
||||
dst->savestate_support_level = src->savestate_support_level;
|
||||
dst->has_info = src->has_info;
|
||||
dst->supports_no_game = src->supports_no_game;
|
||||
dst->single_purpose = src->single_purpose;
|
||||
dst->database_match_archive_member = src->database_match_archive_member;
|
||||
dst->is_experimental = src->is_experimental;
|
||||
dst->is_locked = src->is_locked;
|
||||
dst->is_standalone_exempt = src->is_standalone_exempt;
|
||||
dst->is_installed = src->is_installed;
|
||||
}
|
||||
|
||||
@@ -565,6 +594,10 @@ static void core_info_cache_list_free(
|
||||
}
|
||||
|
||||
free(core_info_cache_list->items);
|
||||
|
||||
if (core_info_cache_list->version)
|
||||
free(core_info_cache_list->version);
|
||||
|
||||
free(core_info_cache_list);
|
||||
}
|
||||
|
||||
@@ -657,6 +690,7 @@ static core_info_cache_list_t *core_info_cache_list_new(void)
|
||||
|
||||
core_info_cache_list->capacity = CORE_INFO_CACHE_DEFAULT_CAPACITY;
|
||||
core_info_cache_list->refresh = false;
|
||||
core_info_cache_list->version = NULL;
|
||||
|
||||
return core_info_cache_list;
|
||||
}
|
||||
@@ -758,6 +792,24 @@ static core_info_cache_list_t *core_info_cache_read(const char *info_dir)
|
||||
free(context.core_info);
|
||||
}
|
||||
|
||||
if (!core_info_cache_list)
|
||||
goto end;
|
||||
|
||||
/* If info cache file has the wrong version
|
||||
* number, discard it */
|
||||
if (string_is_empty(core_info_cache_list->version) ||
|
||||
!string_is_equal(core_info_cache_list->version,
|
||||
CORE_INFO_CACHE_VERSION))
|
||||
{
|
||||
RARCH_WARN("[Core Info] Core info cache has invalid version"
|
||||
" - forcing refresh (required v%s, found v%s)\n",
|
||||
CORE_INFO_CACHE_VERSION,
|
||||
core_info_cache_list->version);
|
||||
|
||||
core_info_cache_list_free(context.core_info_cache_list);
|
||||
core_info_cache_list = core_info_cache_list_new();
|
||||
}
|
||||
|
||||
end:
|
||||
intfstream_close(file);
|
||||
free(file);
|
||||
@@ -822,7 +874,7 @@ static bool core_info_cache_write(core_info_cache_list_t *list, const char *info
|
||||
rjsonwriter_add_string(writer, "version");
|
||||
rjsonwriter_add_colon(writer);
|
||||
rjsonwriter_add_space(writer);
|
||||
rjsonwriter_add_string(writer, "1.0");
|
||||
rjsonwriter_add_string(writer, CORE_INFO_CACHE_VERSION);
|
||||
rjsonwriter_add_comma(writer);
|
||||
rjsonwriter_add_newline(writer);
|
||||
rjsonwriter_add_spaces(writer, 2);
|
||||
@@ -1051,6 +1103,14 @@ static bool core_info_cache_write(core_info_cache_list_t *list, const char *info
|
||||
rjsonwriter_add_comma(writer);
|
||||
rjsonwriter_add_newline(writer);
|
||||
|
||||
rjsonwriter_add_spaces(writer, 6);
|
||||
rjsonwriter_add_string(writer, "savestate_support_level");
|
||||
rjsonwriter_add_colon(writer);
|
||||
rjsonwriter_add_space(writer);
|
||||
rjsonwriter_add_unsigned(writer, info->savestate_support_level);
|
||||
rjsonwriter_add_comma(writer);
|
||||
rjsonwriter_add_newline(writer);
|
||||
|
||||
rjsonwriter_add_spaces(writer, 6);
|
||||
rjsonwriter_add_string(writer, "has_info");
|
||||
rjsonwriter_add_colon(writer);
|
||||
@@ -1067,6 +1127,14 @@ static bool core_info_cache_write(core_info_cache_list_t *list, const char *info
|
||||
rjsonwriter_add_comma(writer);
|
||||
rjsonwriter_add_newline(writer);
|
||||
|
||||
rjsonwriter_add_spaces(writer, 6);
|
||||
rjsonwriter_add_string(writer, "single_purpose");
|
||||
rjsonwriter_add_colon(writer);
|
||||
rjsonwriter_add_space(writer);
|
||||
rjsonwriter_add_bool(writer, info->single_purpose);
|
||||
rjsonwriter_add_comma(writer);
|
||||
rjsonwriter_add_newline(writer);
|
||||
|
||||
rjsonwriter_add_spaces(writer, 6);
|
||||
rjsonwriter_add_string(writer, "database_match_archive_member");
|
||||
rjsonwriter_add_colon(writer);
|
||||
@@ -1216,19 +1284,20 @@ typedef struct
|
||||
{
|
||||
const char *filename;
|
||||
uint32_t hash;
|
||||
} core_lock_file_path_t;
|
||||
} core_aux_file_path_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
core_lock_file_path_t *list;
|
||||
core_aux_file_path_t *list;
|
||||
size_t size;
|
||||
} core_lock_file_path_list_t;
|
||||
} core_aux_file_path_list_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct string_list *dir_list;
|
||||
core_file_path_list_t *core_list;
|
||||
core_lock_file_path_list_t *lock_list;
|
||||
core_aux_file_path_list_t *lock_list;
|
||||
core_aux_file_path_list_t *standalone_exempt_list;
|
||||
} core_path_list_t;
|
||||
|
||||
static uint32_t core_info_hash_string(const char *str)
|
||||
@@ -1259,6 +1328,13 @@ static void core_info_path_list_free(core_path_list_t *path_list)
|
||||
free(path_list->lock_list);
|
||||
}
|
||||
|
||||
if (path_list->standalone_exempt_list)
|
||||
{
|
||||
if (path_list->standalone_exempt_list->list)
|
||||
free(path_list->standalone_exempt_list->list);
|
||||
free(path_list->standalone_exempt_list);
|
||||
}
|
||||
|
||||
if (path_list->dir_list)
|
||||
string_list_free(path_list->dir_list);
|
||||
|
||||
@@ -1286,22 +1362,30 @@ static core_path_list_t *core_info_path_list_new(const char *core_dir,
|
||||
goto error;
|
||||
|
||||
/* Allocate list containers */
|
||||
path_list->dir_list = string_list_new();
|
||||
path_list->core_list = (core_file_path_list_t*)calloc(1,
|
||||
sizeof(*path_list->core_list));
|
||||
path_list->lock_list = (core_lock_file_path_list_t*)calloc(1,
|
||||
sizeof(*path_list->lock_list));
|
||||
path_list->dir_list = string_list_new();
|
||||
path_list->core_list = (core_file_path_list_t*)
|
||||
calloc(1, sizeof(*path_list->core_list));
|
||||
path_list->lock_list = (core_aux_file_path_list_t*)
|
||||
calloc(1, sizeof(*path_list->lock_list));
|
||||
path_list->standalone_exempt_list = (core_aux_file_path_list_t*)
|
||||
calloc(1, sizeof(*path_list->standalone_exempt_list));
|
||||
|
||||
if ( !path_list->dir_list
|
||||
|| !path_list->core_list
|
||||
|| !path_list->lock_list)
|
||||
|| !path_list->lock_list
|
||||
|| !path_list->standalone_exempt_list)
|
||||
goto error;
|
||||
|
||||
/* Get list of file extensions to include
|
||||
* (core + lock file) */
|
||||
fill_pathname_join_delim(exts,
|
||||
core_exts, FILE_PATH_LOCK_EXTENSION_NO_DOT,
|
||||
'|', sizeof(exts));
|
||||
* > core + lock */
|
||||
strlcpy(exts, core_exts, sizeof(exts));
|
||||
strlcat(exts, "|" FILE_PATH_LOCK_EXTENSION_NO_DOT,
|
||||
sizeof(exts));
|
||||
#if defined(HAVE_DYNAMIC)
|
||||
/* > 'standalone exempt' */
|
||||
strlcat(exts, "|" FILE_PATH_STANDALONE_EXEMPT_EXTENSION_NO_DOT,
|
||||
sizeof(exts));
|
||||
#endif
|
||||
|
||||
/* Fetch core directory listing */
|
||||
dir_list_ok = dir_list_append(path_list->dir_list,
|
||||
@@ -1330,15 +1414,19 @@ static core_path_list_t *core_info_path_list_new(const char *core_dir,
|
||||
#endif
|
||||
|
||||
/* Allocate sub lists */
|
||||
path_list->core_list->list = (core_file_path_t*)
|
||||
path_list->core_list->list = (core_file_path_t*)
|
||||
malloc(path_list->dir_list->size *
|
||||
sizeof(*path_list->core_list->list));
|
||||
path_list->lock_list->list = (core_lock_file_path_t*)
|
||||
path_list->lock_list->list = (core_aux_file_path_t*)
|
||||
malloc(path_list->dir_list->size *
|
||||
sizeof(*path_list->lock_list->list));
|
||||
path_list->standalone_exempt_list->list = (core_aux_file_path_t*)
|
||||
malloc(path_list->dir_list->size *
|
||||
sizeof(*path_list->standalone_exempt_list->list));
|
||||
|
||||
if (!path_list->core_list->list ||
|
||||
!path_list->lock_list->list)
|
||||
!path_list->lock_list->list ||
|
||||
!path_list->standalone_exempt_list->list)
|
||||
goto error;
|
||||
|
||||
/* Parse directory listing */
|
||||
@@ -1353,7 +1441,8 @@ static core_path_list_t *core_info_path_list_new(const char *core_dir,
|
||||
|| !(file_ext = path_get_extension(filename)))
|
||||
continue;
|
||||
|
||||
/* Check whether this is a core or lock file */
|
||||
/* Check whether this is a core, lock or
|
||||
* 'standalone exempt' file */
|
||||
if (string_list_find_elem(core_ext_list, file_ext))
|
||||
{
|
||||
path_list->core_list->list[
|
||||
@@ -1370,6 +1459,16 @@ static core_path_list_t *core_info_path_list_new(const char *core_dir,
|
||||
path_list->lock_list->size].hash = core_info_hash_string(filename);
|
||||
path_list->lock_list->size++;
|
||||
}
|
||||
#if defined(HAVE_DYNAMIC)
|
||||
else if (string_is_equal(file_ext, FILE_PATH_STANDALONE_EXEMPT_EXTENSION_NO_DOT))
|
||||
{
|
||||
path_list->standalone_exempt_list->list[
|
||||
path_list->standalone_exempt_list->size].filename = filename;
|
||||
path_list->standalone_exempt_list->list[
|
||||
path_list->standalone_exempt_list->size].hash = core_info_hash_string(filename);
|
||||
path_list->standalone_exempt_list->size++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
string_list_free(core_ext_list);
|
||||
@@ -1382,13 +1481,15 @@ error:
|
||||
}
|
||||
|
||||
static bool core_info_path_is_locked(
|
||||
core_lock_file_path_list_t *lock_list,
|
||||
core_aux_file_path_list_t *lock_list,
|
||||
const char *core_file_name)
|
||||
{
|
||||
size_t i;
|
||||
uint32_t hash;
|
||||
char lock_filename[256];
|
||||
|
||||
lock_filename[0] = '\0';
|
||||
|
||||
if (lock_list->size < 1)
|
||||
return false;
|
||||
|
||||
@@ -1399,7 +1500,7 @@ static bool core_info_path_is_locked(
|
||||
|
||||
for (i = 0; i < lock_list->size; i++)
|
||||
{
|
||||
core_lock_file_path_t *lock_file = &lock_list->list[i];
|
||||
core_aux_file_path_t *lock_file = &lock_list->list[i];
|
||||
|
||||
if ((lock_file->hash == hash) &&
|
||||
string_is_equal(lock_file->filename, lock_filename))
|
||||
@@ -1409,6 +1510,37 @@ static bool core_info_path_is_locked(
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool core_info_path_is_standalone_exempt(
|
||||
core_aux_file_path_list_t *exempt_list,
|
||||
const char *core_file_name)
|
||||
{
|
||||
size_t i;
|
||||
uint32_t hash;
|
||||
char exempt_filename[256];
|
||||
|
||||
exempt_filename[0] = '\0';
|
||||
|
||||
if (exempt_list->size < 1)
|
||||
return false;
|
||||
|
||||
snprintf(exempt_filename, sizeof(exempt_filename),
|
||||
"%s" FILE_PATH_STANDALONE_EXEMPT_EXTENSION,
|
||||
core_file_name);
|
||||
|
||||
hash = core_info_hash_string(exempt_filename);
|
||||
|
||||
for (i = 0; i < exempt_list->size; i++)
|
||||
{
|
||||
core_aux_file_path_t *exempt_file = &exempt_list->list[i];
|
||||
|
||||
if ((exempt_file->hash == hash) &&
|
||||
string_is_equal(exempt_file->filename, exempt_filename))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool core_info_get_file_id(const char *core_filename,
|
||||
char *core_file_id, size_t len)
|
||||
{
|
||||
@@ -1695,6 +1827,10 @@ static void core_info_parse_config_file(
|
||||
&tmp_bool))
|
||||
info->supports_no_game = tmp_bool;
|
||||
|
||||
if (config_get_bool(conf, "single_purpose",
|
||||
&tmp_bool))
|
||||
info->single_purpose = tmp_bool;
|
||||
|
||||
if (config_get_bool(conf, "database_match_archive_member",
|
||||
&tmp_bool))
|
||||
info->database_match_archive_member = tmp_bool;
|
||||
@@ -1703,6 +1839,40 @@ static void core_info_parse_config_file(
|
||||
&tmp_bool))
|
||||
info->is_experimental = tmp_bool;
|
||||
|
||||
|
||||
/* Savestate support level is slightly more complex,
|
||||
* since it is a value derived from two configuration
|
||||
* parameters */
|
||||
|
||||
/* > Assume all cores have 'full' savestate support
|
||||
* by default */
|
||||
info->savestate_support_level =
|
||||
CORE_INFO_SAVESTATE_DETERMINISTIC;
|
||||
|
||||
/* > Check whether savestate functionality is defined
|
||||
* in the info file */
|
||||
if (config_get_bool(conf, "savestate", &tmp_bool))
|
||||
{
|
||||
if (tmp_bool)
|
||||
{
|
||||
/* Check if savestate features are defined */
|
||||
entry = config_get_entry(conf, "savestate_features");
|
||||
|
||||
if (entry && !string_is_empty(entry->value))
|
||||
{
|
||||
if (string_is_equal(entry->value, "basic"))
|
||||
info->savestate_support_level =
|
||||
CORE_INFO_SAVESTATE_BASIC;
|
||||
else if (string_is_equal(entry->value, "serialized"))
|
||||
info->savestate_support_level =
|
||||
CORE_INFO_SAVESTATE_SERIALIZED;
|
||||
}
|
||||
}
|
||||
else
|
||||
info->savestate_support_level =
|
||||
CORE_INFO_SAVESTATE_DISABLED;
|
||||
}
|
||||
|
||||
core_info_resolve_firmware(info, conf);
|
||||
|
||||
info->has_info = true;
|
||||
@@ -1780,8 +1950,12 @@ static void core_info_free(core_info_t* info)
|
||||
|
||||
for (i = 0; i < info->firmware_count; i++)
|
||||
{
|
||||
free(info->firmware[i].path);
|
||||
free(info->firmware[i].desc);
|
||||
if (info->firmware[i].path)
|
||||
free(info->firmware[i].path);
|
||||
if (info->firmware[i].desc)
|
||||
free(info->firmware[i].desc);
|
||||
info->firmware[i].path = NULL;
|
||||
info->firmware[i].desc = NULL;
|
||||
}
|
||||
free(info->firmware);
|
||||
|
||||
@@ -1879,23 +2053,35 @@ static core_info_list_t *core_info_list_new(const char *path,
|
||||
if (info_cache)
|
||||
{
|
||||
core_info_copy(info_cache, info);
|
||||
|
||||
/* Core path is 'dynamic', and cannot
|
||||
* be cached (i.e. core directory may
|
||||
* change between runs) */
|
||||
if (info->path)
|
||||
free(info->path);
|
||||
info->path = strdup(base_path);
|
||||
info->path = strdup(base_path);
|
||||
|
||||
/* Core lock status is 'dynamic', and
|
||||
* cannot be cached */
|
||||
info->is_locked = core_info_path_is_locked(
|
||||
path_list->lock_list, core_filename);
|
||||
|
||||
|
||||
/* Core 'standalone exempt' status is 'dynamic',
|
||||
* and cannot be cached
|
||||
* > It is also dependent upon whether the core
|
||||
* supports contentless operation */
|
||||
info->is_standalone_exempt = info->supports_no_game &&
|
||||
core_info_path_is_standalone_exempt(
|
||||
path_list->standalone_exempt_list,
|
||||
core_filename);
|
||||
|
||||
/* 'info_count' is normally incremented inside
|
||||
* core_info_parse_config_file(). If core entry
|
||||
* is cached, must instead increment the value
|
||||
* here */
|
||||
if (info->has_info)
|
||||
core_info_list->info_count++;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -1924,7 +2110,13 @@ static core_info_list_t *core_info_list_new(const char *path,
|
||||
if (!info->display_name)
|
||||
info->display_name = strdup(core_filename);
|
||||
|
||||
info->is_installed = true;
|
||||
/* Get core 'standalone exempt' status */
|
||||
info->is_standalone_exempt = info->supports_no_game &&
|
||||
core_info_path_is_standalone_exempt(
|
||||
path_list->standalone_exempt_list,
|
||||
core_filename);
|
||||
|
||||
info->is_installed = true;
|
||||
|
||||
/* If info cache is enabled and we reach this
|
||||
* point, current core is uncached
|
||||
@@ -2093,10 +2285,14 @@ bool core_info_init_current_core(void)
|
||||
return false;
|
||||
current->has_info = false;
|
||||
current->supports_no_game = false;
|
||||
current->single_purpose = false;
|
||||
current->database_match_archive_member = false;
|
||||
current->is_experimental = false;
|
||||
current->is_locked = false;
|
||||
current->is_standalone_exempt = false;
|
||||
current->is_installed = false;
|
||||
current->firmware_count = 0;
|
||||
current->savestate_support_level = CORE_INFO_SAVESTATE_DETERMINISTIC;
|
||||
current->path = NULL;
|
||||
current->display_name = NULL;
|
||||
current->display_version = NULL;
|
||||
@@ -2409,7 +2605,12 @@ bool core_info_list_get_display_name(
|
||||
core_info_list_t *core_info_list,
|
||||
const char *core_path, char *s, size_t len)
|
||||
{
|
||||
core_info_t *info = core_info_find_internal(
|
||||
core_info_t *info;
|
||||
|
||||
if (!core_info_list)
|
||||
return false;
|
||||
|
||||
info = core_info_find_internal(
|
||||
core_info_list, core_path);
|
||||
|
||||
if (s &&
|
||||
@@ -2916,6 +3117,99 @@ bool core_info_hw_api_supported(core_info_t *info)
|
||||
#endif
|
||||
}
|
||||
|
||||
bool core_info_current_supports_savestate(void)
|
||||
{
|
||||
core_info_state_t *p_coreinfo = &core_info_st;
|
||||
|
||||
/* If no core is currently loaded, assume
|
||||
* by default that all savestate functionality
|
||||
* is supported */
|
||||
if (!p_coreinfo->current)
|
||||
return true;
|
||||
|
||||
return p_coreinfo->current->savestate_support_level >=
|
||||
CORE_INFO_SAVESTATE_BASIC;
|
||||
}
|
||||
|
||||
bool core_info_current_supports_rewind(void)
|
||||
{
|
||||
core_info_state_t *p_coreinfo = &core_info_st;
|
||||
|
||||
/* If no core is currently loaded, assume
|
||||
* by default that all savestate functionality
|
||||
* is supported */
|
||||
if (!p_coreinfo->current)
|
||||
return true;
|
||||
|
||||
return p_coreinfo->current->savestate_support_level >=
|
||||
CORE_INFO_SAVESTATE_SERIALIZED;
|
||||
}
|
||||
|
||||
bool core_info_current_supports_netplay(void)
|
||||
{
|
||||
core_info_state_t *p_coreinfo = &core_info_st;
|
||||
|
||||
/* If no core is currently loaded, assume
|
||||
* by default that all savestate functionality
|
||||
* is supported */
|
||||
if (!p_coreinfo->current)
|
||||
return true;
|
||||
|
||||
return p_coreinfo->current->savestate_support_level >=
|
||||
CORE_INFO_SAVESTATE_DETERMINISTIC;
|
||||
}
|
||||
|
||||
bool core_info_current_supports_runahead(void)
|
||||
{
|
||||
core_info_state_t *p_coreinfo = &core_info_st;
|
||||
|
||||
/* If no core is currently loaded, assume
|
||||
* by default that all savestate functionality
|
||||
* is supported */
|
||||
if (!p_coreinfo->current)
|
||||
return true;
|
||||
|
||||
return p_coreinfo->current->savestate_support_level >=
|
||||
CORE_INFO_SAVESTATE_DETERMINISTIC;
|
||||
}
|
||||
|
||||
static bool core_info_update_core_aux_file(const char *path, bool create)
|
||||
{
|
||||
bool aux_file_exists = false;
|
||||
|
||||
if (string_is_empty(path))
|
||||
return false;
|
||||
|
||||
/* Check whether aux file exists */
|
||||
aux_file_exists = path_is_valid(path);
|
||||
|
||||
/* Create or delete aux file, as required */
|
||||
if (create && !aux_file_exists)
|
||||
{
|
||||
RFILE *aux_file = filestream_open(path,
|
||||
RETRO_VFS_FILE_ACCESS_WRITE,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
|
||||
if (!aux_file)
|
||||
return false;
|
||||
|
||||
/* We have to write something - just output
|
||||
* a single character */
|
||||
if (filestream_putc(aux_file, 0) != 0)
|
||||
{
|
||||
filestream_close(aux_file);
|
||||
return false;
|
||||
}
|
||||
|
||||
filestream_close(aux_file);
|
||||
}
|
||||
else if (!create && aux_file_exists)
|
||||
if (filestream_delete(path) != 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Sets 'locked' status of specified core
|
||||
* > Returns true if successful
|
||||
* > Like all functions that access the cached
|
||||
@@ -2923,9 +3217,10 @@ bool core_info_hw_api_supported(core_info_t *info)
|
||||
bool core_info_set_core_lock(const char *core_path, bool lock)
|
||||
{
|
||||
core_info_t *core_info = NULL;
|
||||
bool lock_file_exists = false;
|
||||
char lock_file_path[PATH_MAX_LENGTH];
|
||||
|
||||
lock_file_path[0] = '\0';
|
||||
|
||||
#if defined(ANDROID)
|
||||
/* Play Store builds do not support
|
||||
* core locking */
|
||||
@@ -2933,47 +3228,19 @@ bool core_info_set_core_lock(const char *core_path, bool lock)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
if (string_is_empty(core_path))
|
||||
return false;
|
||||
|
||||
/* Search for specified core */
|
||||
if (!core_info_find(core_path, &core_info))
|
||||
return false;
|
||||
|
||||
if (string_is_empty(core_info->path))
|
||||
if (string_is_empty(core_path) ||
|
||||
!core_info_find(core_path, &core_info) ||
|
||||
string_is_empty(core_info->path))
|
||||
return false;
|
||||
|
||||
/* Get lock file path */
|
||||
snprintf(lock_file_path, sizeof(lock_file_path),
|
||||
"%s" FILE_PATH_LOCK_EXTENSION, core_info->path);
|
||||
|
||||
/* Check whether lock file exists */
|
||||
lock_file_exists = path_is_valid(lock_file_path);
|
||||
|
||||
/* Create or delete lock file, as required */
|
||||
if (lock && !lock_file_exists)
|
||||
{
|
||||
RFILE *lock_file = filestream_open(
|
||||
lock_file_path,
|
||||
RETRO_VFS_FILE_ACCESS_WRITE,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
|
||||
if (!lock_file)
|
||||
return false;
|
||||
|
||||
/* We have to write something - just output
|
||||
* a single character */
|
||||
if (filestream_putc(lock_file, 0) != 0)
|
||||
{
|
||||
filestream_close(lock_file);
|
||||
return false;
|
||||
}
|
||||
|
||||
filestream_close(lock_file);
|
||||
}
|
||||
else if (!lock && lock_file_exists)
|
||||
if (filestream_delete(lock_file_path) != 0)
|
||||
return false;
|
||||
if (!core_info_update_core_aux_file(lock_file_path, lock))
|
||||
return false;
|
||||
|
||||
/* File operations were successful - update
|
||||
* core info entry */
|
||||
@@ -2998,6 +3265,8 @@ bool core_info_get_core_lock(const char *core_path, bool validate_path)
|
||||
bool is_locked = false;
|
||||
char lock_file_path[PATH_MAX_LENGTH];
|
||||
|
||||
lock_file_path[0] = '\0';
|
||||
|
||||
#if defined(ANDROID)
|
||||
/* Play Store builds do not support
|
||||
* core locking */
|
||||
@@ -3038,3 +3307,88 @@ bool core_info_get_core_lock(const char *core_path, bool validate_path)
|
||||
|
||||
return is_locked;
|
||||
}
|
||||
|
||||
/* Sets 'standalone exempt' status of specified core
|
||||
* > A 'standalone exempt' core will not be shown
|
||||
* in the contentless cores menu when display type
|
||||
* is set to 'custom'
|
||||
* > Returns true if successful
|
||||
* > Returns false if core does not support
|
||||
* contentless operation
|
||||
* > *Not* thread safe */
|
||||
bool core_info_set_core_standalone_exempt(const char *core_path, bool exempt)
|
||||
{
|
||||
#if defined(HAVE_DYNAMIC)
|
||||
core_info_t *core_info = NULL;
|
||||
char exempt_file_path[PATH_MAX_LENGTH];
|
||||
|
||||
exempt_file_path[0] = '\0';
|
||||
|
||||
/* Search for specified core */
|
||||
if (string_is_empty(core_path) ||
|
||||
!core_info_find(core_path, &core_info) ||
|
||||
string_is_empty(core_info->path) ||
|
||||
!core_info->supports_no_game)
|
||||
return false;
|
||||
|
||||
/* Get 'standalone exempt' file path */
|
||||
snprintf(exempt_file_path, sizeof(exempt_file_path),
|
||||
"%s" FILE_PATH_STANDALONE_EXEMPT_EXTENSION,
|
||||
core_info->path);
|
||||
|
||||
/* Create or delete 'standalone exempt' file, as required */
|
||||
if (!core_info_update_core_aux_file(exempt_file_path, exempt))
|
||||
return false;
|
||||
|
||||
/* File operations were successful - update
|
||||
* core info entry */
|
||||
core_info->is_standalone_exempt = exempt;
|
||||
|
||||
return true;
|
||||
#else
|
||||
/* Static platforms do not support the contentless
|
||||
* cores menu */
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Fetches 'standalone exempt' status of specified core
|
||||
* > Returns true if core should be excluded from
|
||||
* the contentless cores menu when display type is
|
||||
* set to 'custom'
|
||||
* > *Not* thread safe */
|
||||
bool core_info_get_core_standalone_exempt(const char *core_path)
|
||||
{
|
||||
#if defined(HAVE_DYNAMIC)
|
||||
core_info_t *core_info = NULL;
|
||||
bool is_exempt = false;
|
||||
char exempt_file_path[PATH_MAX_LENGTH];
|
||||
|
||||
exempt_file_path[0] = '\0';
|
||||
|
||||
/* Search for specified core */
|
||||
if (string_is_empty(core_path) ||
|
||||
!core_info_find(core_path, &core_info) ||
|
||||
string_is_empty(core_info->path) ||
|
||||
!core_info->supports_no_game)
|
||||
return false;
|
||||
|
||||
/* Get 'standalone exempt' file path */
|
||||
snprintf(exempt_file_path, sizeof(exempt_file_path),
|
||||
"%s" FILE_PATH_STANDALONE_EXEMPT_EXTENSION,
|
||||
core_info->path);
|
||||
|
||||
/* Check whether 'standalone exempt' file exists */
|
||||
is_exempt = path_is_valid(exempt_file_path);
|
||||
|
||||
/* Ensure that core info 'is_standalone_exempt'
|
||||
* field is up to date */
|
||||
core_info->is_standalone_exempt = is_exempt;
|
||||
|
||||
return is_exempt;
|
||||
#else
|
||||
/* Static platforms do not support the contentless
|
||||
* cores menu */
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
+46
@@ -25,6 +25,23 @@
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
/* Defines the levels of savestate support
|
||||
* that may be offered by a core:
|
||||
* - serialized: rewind
|
||||
* - deterministic: netplay/runahead
|
||||
* Thus:
|
||||
* (level < CORE_INFO_SAVESTATE_BASIC)
|
||||
* -> no savestate support
|
||||
* (level < CORE_INFO_SAVESTATE_SERIALIZED)
|
||||
* -> no rewind/netplay/runahead
|
||||
* (level < CORE_INFO_SAVESTATE_DETERMINISTIC)
|
||||
* -> no netplay/runahead
|
||||
*/
|
||||
#define CORE_INFO_SAVESTATE_DISABLED 0
|
||||
#define CORE_INFO_SAVESTATE_BASIC 1
|
||||
#define CORE_INFO_SAVESTATE_SERIALIZED 2
|
||||
#define CORE_INFO_SAVESTATE_DETERMINISTIC 3
|
||||
|
||||
enum core_info_list_qsort_type
|
||||
{
|
||||
CORE_INFO_LIST_SORT_PATH = 0,
|
||||
@@ -84,11 +101,14 @@ typedef struct
|
||||
core_info_firmware_t *firmware;
|
||||
core_file_id_t core_file_id; /* ptr alignment */
|
||||
size_t firmware_count;
|
||||
uint32_t savestate_support_level;
|
||||
bool has_info;
|
||||
bool supports_no_game;
|
||||
bool single_purpose;
|
||||
bool database_match_archive_member;
|
||||
bool is_experimental;
|
||||
bool is_locked;
|
||||
bool is_standalone_exempt;
|
||||
bool is_installed;
|
||||
} core_info_t;
|
||||
|
||||
@@ -184,6 +204,16 @@ bool core_info_list_get_info(core_info_list_t *core_info_list,
|
||||
|
||||
bool core_info_hw_api_supported(core_info_t *info);
|
||||
|
||||
/* Convenience wrapper functions used to interpret
|
||||
* the 'savestate_support_level' parameter of
|
||||
* the currently loaded core. If no core is
|
||||
* loaded, will return 'true' (since full
|
||||
* savestate functionality is assumed by default) */
|
||||
bool core_info_current_supports_savestate(void);
|
||||
bool core_info_current_supports_rewind(void);
|
||||
bool core_info_current_supports_netplay(void);
|
||||
bool core_info_current_supports_runahead(void);
|
||||
|
||||
/* Sets 'locked' status of specified core
|
||||
* > Returns true if successful
|
||||
* > Like all functions that access the cached
|
||||
@@ -200,6 +230,22 @@ bool core_info_set_core_lock(const char *core_path, bool lock);
|
||||
* must be checked externally */
|
||||
bool core_info_get_core_lock(const char *core_path, bool validate_path);
|
||||
|
||||
/* Sets 'standalone exempt' status of specified core
|
||||
* > A 'standalone exempt' core will not be shown
|
||||
* in the contentless cores menu when display type
|
||||
* is set to 'custom'
|
||||
* > Returns true if successful
|
||||
* > Returns false if core does not support
|
||||
* contentless operation
|
||||
* > *Not* thread safe */
|
||||
bool core_info_set_core_standalone_exempt(const char *core_path, bool exempt);
|
||||
/* Fetches 'standalone exempt' status of specified core
|
||||
* > Returns true if core should be excluded from
|
||||
* the contentless cores menu when display type is
|
||||
* set to 'custom'
|
||||
* > *Not* thread safe */
|
||||
bool core_info_get_core_standalone_exempt(const char *core_path);
|
||||
|
||||
bool core_info_core_file_id_is_equal(const char *core_path_a, const char *core_path_b);
|
||||
|
||||
/* When called, generates a temporary file
|
||||
|
||||
@@ -737,7 +737,7 @@ static bool core_option_manager_parse_variable(
|
||||
value, NULL);
|
||||
|
||||
/* Set value hash */
|
||||
*value_hash = core_option_manager_hash_string(value);
|
||||
*value_hash = core_option_manager_hash_string(value);
|
||||
option->vals->elems[i].userdata = (void*)value_hash;
|
||||
|
||||
/* Redundant safely check... */
|
||||
|
||||
@@ -360,62 +360,6 @@ size_t libretro_videoprocessor_retro_get_memory_size(unsigned id);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_GONG
|
||||
/* Internal gong core. */
|
||||
|
||||
void libretro_gong_retro_init(void);
|
||||
|
||||
void libretro_gong_retro_deinit(void);
|
||||
|
||||
unsigned libretro_gong_retro_api_version(void);
|
||||
|
||||
void libretro_gong_retro_get_system_info(struct retro_system_info *info);
|
||||
|
||||
void libretro_gong_retro_get_system_av_info(struct retro_system_av_info *info);
|
||||
|
||||
void libretro_gong_retro_set_environment(retro_environment_t cb);
|
||||
|
||||
void libretro_gong_retro_set_video_refresh(retro_video_refresh_t cb);
|
||||
|
||||
void libretro_gong_retro_set_audio_sample(retro_audio_sample_t cb);
|
||||
|
||||
void libretro_gong_retro_set_audio_sample_batch(retro_audio_sample_batch_t cb);
|
||||
|
||||
void libretro_gong_retro_set_input_poll(retro_input_poll_t cb);
|
||||
|
||||
void libretro_gong_retro_set_input_state(retro_input_state_t cb);
|
||||
|
||||
void libretro_gong_retro_set_controller_port_device(unsigned port, unsigned device);
|
||||
|
||||
void libretro_gong_retro_reset(void);
|
||||
|
||||
void libretro_gong_retro_run(void);
|
||||
|
||||
size_t libretro_gong_retro_serialize_size(void);
|
||||
|
||||
bool libretro_gong_retro_serialize(void *data, size_t size);
|
||||
|
||||
bool libretro_gong_retro_unserialize(const void *data, size_t size);
|
||||
|
||||
void libretro_gong_retro_cheat_reset(void);
|
||||
|
||||
void libretro_gong_retro_cheat_set(unsigned index, bool enabled, const char *code);
|
||||
|
||||
bool libretro_gong_retro_load_game(const struct retro_game_info *game);
|
||||
|
||||
bool libretro_gong_retro_load_game_special(unsigned game_type,
|
||||
const struct retro_game_info *info, size_t num_info);
|
||||
|
||||
void libretro_gong_retro_unload_game(void);
|
||||
|
||||
unsigned libretro_gong_retro_get_region(void);
|
||||
|
||||
void *libretro_gong_retro_get_memory_data(unsigned id);
|
||||
|
||||
size_t libretro_gong_retro_get_memory_size(unsigned id);
|
||||
|
||||
#endif
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
||||
@@ -96,11 +96,8 @@ static unsigned sw_sws_threads;
|
||||
static video_buffer_t *video_buffer;
|
||||
static tpool_t *tpool;
|
||||
|
||||
/* If libavutil is at least version 55 or higher,
|
||||
* and if libavcodec is at least version 57.80.100 or higher,
|
||||
* enable hardware acceleration */
|
||||
#define ENABLE_HW_ACCEL ((LIBAVUTIL_VERSION_MAJOR > 55) && ENABLE_HW_ACCEL_CHECK2())
|
||||
#define ENABLE_HW_ACCEL_CHECK2() ((LIBAVCODEC_VERSION_MAJOR == 57 && LIBAVCODEC_VERSION_MINOR >= 80 && LIBAVCODEC_VERSION_MICRO >= 100) || (LIBAVCODEC_VERSION_MAJOR > 57))
|
||||
#define FFMPEG3 ((LIBAVUTIL_VERSION_INT < (56, 6, 100)) || \
|
||||
(LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 10, 100)))
|
||||
|
||||
#if ENABLE_HW_ACCEL
|
||||
static enum AVHWDeviceType hw_decoder;
|
||||
@@ -240,7 +237,9 @@ void CORE_PREFIX(retro_init)(void)
|
||||
{
|
||||
reset_triggered = false;
|
||||
|
||||
#if FFMPEG3
|
||||
av_register_all();
|
||||
#endif
|
||||
|
||||
if (CORE_PREFIX(environ_cb)(RETRO_ENVIRONMENT_GET_INPUT_BITMASKS, NULL))
|
||||
libretro_supports_bitmasks = true;
|
||||
@@ -462,10 +461,12 @@ static void check_variables(bool firststart)
|
||||
hw_decoder = AV_HWDEVICE_TYPE_DRM;
|
||||
else if (string_is_equal(hw_var.value, "dxva2"))
|
||||
hw_decoder = AV_HWDEVICE_TYPE_DXVA2;
|
||||
#if !FFMPEG3
|
||||
else if (string_is_equal(hw_var.value, "mediacodec"))
|
||||
hw_decoder = AV_HWDEVICE_TYPE_MEDIACODEC;
|
||||
else if (string_is_equal(hw_var.value, "opencl"))
|
||||
hw_decoder = AV_HWDEVICE_TYPE_OPENCL;
|
||||
#endif
|
||||
else if (string_is_equal(hw_var.value, "qsv"))
|
||||
hw_decoder = AV_HWDEVICE_TYPE_QSV;
|
||||
else if (string_is_equal(hw_var.value, "vaapi"))
|
||||
@@ -988,8 +989,9 @@ static enum AVPixelFormat init_hw_decoder(struct AVCodecContext *ctx,
|
||||
{
|
||||
int ret = 0;
|
||||
enum AVPixelFormat decoder_pix_fmt = AV_PIX_FMT_NONE;
|
||||
struct AVCodec *codec = avcodec_find_decoder(fctx->streams[video_stream_index]->codec->codec_id);
|
||||
const AVCodec *codec = avcodec_find_decoder(fctx->streams[video_stream_index]->codecpar->codec_id);
|
||||
|
||||
#if !FFMPEG3
|
||||
for (int i = 0;; i++)
|
||||
{
|
||||
const AVCodecHWConfig *config = avcodec_get_hw_config(codec, i);
|
||||
@@ -1002,12 +1004,15 @@ static enum AVPixelFormat init_hw_decoder(struct AVCodecContext *ctx,
|
||||
if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX &&
|
||||
config->device_type == type)
|
||||
{
|
||||
enum AVPixelFormat device_pix_fmt = config->pix_fmt;
|
||||
#else
|
||||
enum AVPixelFormat device_pix_fmt =
|
||||
pix_fmts ? ctx->get_format(ctx, pix_fmts) : decoder_pix_fmt;
|
||||
#endif
|
||||
log_cb(RETRO_LOG_INFO, "[FFMPEG] Selected HW decoder %s.\n",
|
||||
av_hwdevice_get_type_name(type));
|
||||
log_cb(RETRO_LOG_INFO, "[FFMPEG] Selected HW pixel format %s.\n",
|
||||
av_get_pix_fmt_name(config->pix_fmt));
|
||||
|
||||
enum AVPixelFormat device_pix_fmt = config->pix_fmt;
|
||||
av_get_pix_fmt_name(device_pix_fmt));
|
||||
|
||||
if (pix_fmts != NULL)
|
||||
{
|
||||
@@ -1019,16 +1024,17 @@ static enum AVPixelFormat init_hw_decoder(struct AVCodecContext *ctx,
|
||||
goto exit;
|
||||
}
|
||||
log_cb(RETRO_LOG_ERROR, "[FFMPEG] Codec %s does not support device pixel format %s.\n",
|
||||
codec->name, av_get_pix_fmt_name(config->pix_fmt));
|
||||
codec->name, av_get_pix_fmt_name(device_pix_fmt));
|
||||
}
|
||||
else
|
||||
{
|
||||
decoder_pix_fmt = device_pix_fmt;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
#if !FFMPEG3
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
exit:
|
||||
if (decoder_pix_fmt != AV_PIX_FMT_NONE)
|
||||
@@ -1070,7 +1076,6 @@ static enum AVPixelFormat auto_hw_decoder(AVCodecContext *ctx,
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static enum AVPixelFormat select_decoder(AVCodecContext *ctx,
|
||||
const enum AVPixelFormat *pix_fmts)
|
||||
{
|
||||
@@ -1098,7 +1103,7 @@ static enum AVPixelFormat select_decoder(AVCodecContext *ctx,
|
||||
ctx->thread_count = sw_decoder_threads;
|
||||
log_cb(RETRO_LOG_INFO, "[FFMPEG] Configured software decoding threads: %d\n", sw_decoder_threads);
|
||||
|
||||
format = fctx->streams[video_stream_index]->codec->pix_fmt;
|
||||
format = fctx->streams[video_stream_index]->codecpar->format;
|
||||
|
||||
#if ENABLE_HW_ACCEL
|
||||
hw_decoding_enabled = false;
|
||||
@@ -1132,14 +1137,15 @@ static bool open_codec(AVCodecContext **ctx, enum AVMediaType type, unsigned ind
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
AVCodec *codec = avcodec_find_decoder(fctx->streams[index]->codec->codec_id);
|
||||
const AVCodec *codec = avcodec_find_decoder(fctx->streams[index]->codecpar->codec_id);
|
||||
if (!codec)
|
||||
{
|
||||
log_cb(RETRO_LOG_ERROR, "[FFMPEG] Couldn't find suitable decoder\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
*ctx = fctx->streams[index]->codec;
|
||||
*ctx = avcodec_alloc_context3(codec);
|
||||
avcodec_parameters_to_context((*ctx), fctx->streams[index]->codecpar);
|
||||
|
||||
if (type == AVMEDIA_TYPE_VIDEO)
|
||||
{
|
||||
@@ -1230,7 +1236,7 @@ static bool open_codecs(void)
|
||||
|
||||
for (i = 0; i < fctx->nb_streams; i++)
|
||||
{
|
||||
enum AVMediaType type = fctx->streams[i]->codec->codec_type;
|
||||
enum AVMediaType type = fctx->streams[i]->codecpar->codec_type;
|
||||
switch (type)
|
||||
{
|
||||
case AVMEDIA_TYPE_AUDIO:
|
||||
@@ -1245,7 +1251,7 @@ static bool open_codecs(void)
|
||||
|
||||
case AVMEDIA_TYPE_VIDEO:
|
||||
if (!vctx
|
||||
&& !codec_is_image(fctx->streams[i]->codec->codec_id))
|
||||
&& !codec_is_image(fctx->streams[i]->codecpar->codec_id))
|
||||
{
|
||||
if (!open_codec(&vctx, type, i))
|
||||
return false;
|
||||
@@ -1255,7 +1261,7 @@ static bool open_codecs(void)
|
||||
case AVMEDIA_TYPE_SUBTITLE:
|
||||
#ifdef HAVE_SSA
|
||||
if (subtitle_streams_num < MAX_STREAMS
|
||||
&& codec_id_is_ass(fctx->streams[i]->codec->codec_id))
|
||||
&& codec_id_is_ass(fctx->streams[i]->codecpar->codec_id))
|
||||
{
|
||||
int size;
|
||||
AVCodecContext **s = &sctx[subtitle_streams_num];
|
||||
@@ -1280,9 +1286,9 @@ static bool open_codecs(void)
|
||||
|
||||
case AVMEDIA_TYPE_ATTACHMENT:
|
||||
{
|
||||
AVCodecContext *ctx = fctx->streams[i]->codec;
|
||||
if (codec_id_is_ttf(ctx->codec_id))
|
||||
append_attachment(ctx->extradata, ctx->extradata_size);
|
||||
AVCodecParameters *params = fctx->streams[i]->codecpar;
|
||||
if (codec_id_is_ttf(params->codec_id))
|
||||
append_attachment(params->extradata, params->extradata_size);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1464,12 +1470,12 @@ static void sws_worker_thread(void *arg)
|
||||
|
||||
ctx->sws = sws_getCachedContext(ctx->sws,
|
||||
media.width, media.height, (enum AVPixelFormat)tmp_frame->format,
|
||||
media.width, media.height, PIX_FMT_RGB32,
|
||||
media.width, media.height, AV_PIX_FMT_RGB32,
|
||||
SWS_POINT, NULL, NULL, NULL);
|
||||
|
||||
set_colorspace(ctx->sws, media.width, media.height,
|
||||
av_frame_get_colorspace(tmp_frame),
|
||||
av_frame_get_color_range(tmp_frame));
|
||||
tmp_frame->colorspace,
|
||||
tmp_frame->color_range);
|
||||
|
||||
if ((ret = sws_scale(ctx->sws, (const uint8_t *const*)tmp_frame->data,
|
||||
tmp_frame->linesize, 0, media.height,
|
||||
@@ -1620,7 +1626,7 @@ static int16_t *decode_audio(AVCodecContext *ctx, AVPacket *pkt,
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
required_buffer = frame->nb_samples * sizeof(int16_t) * 2;
|
||||
if (required_buffer > *buffer_cap)
|
||||
{
|
||||
@@ -1637,7 +1643,7 @@ static int16_t *decode_audio(AVCodecContext *ctx, AVPacket *pkt,
|
||||
pts = frame->best_effort_timestamp;
|
||||
slock_lock(fifo_lock);
|
||||
|
||||
while (!decode_thread_dead &&
|
||||
while (!decode_thread_dead &&
|
||||
FIFO_WRITE_AVAIL(audio_decode_fifo) < required_buffer)
|
||||
{
|
||||
if (!main_sleeping)
|
||||
@@ -1740,7 +1746,7 @@ static void decode_thread(void *data)
|
||||
|
||||
if (video_stream_index >= 0)
|
||||
{
|
||||
frame_size = avpicture_get_size(PIX_FMT_RGB32, media.width, media.height);
|
||||
frame_size = av_image_get_buffer_size(AV_PIX_FMT_RGB32, media.width, media.height, 1);
|
||||
video_buffer = video_buffer_create(4, frame_size, media.width, media.height);
|
||||
tpool = tpool_create(sw_sws_threads);
|
||||
log_cb(RETRO_LOG_INFO, "[FFMPEG] Configured worker threads: %d\n", sw_sws_threads);
|
||||
@@ -1813,7 +1819,7 @@ static void decode_thread(void *data)
|
||||
if (!packet_buffer_empty(video_packet_buffer))
|
||||
next_video_end = video_timebase * packet_buffer_peek_end_pts(video_packet_buffer);
|
||||
|
||||
/*
|
||||
/*
|
||||
* Decode audio packet if:
|
||||
* 1. it's the start of file or it's audio only media
|
||||
* 2. there is a video packet for in the buffer
|
||||
@@ -1835,7 +1841,7 @@ static void decode_thread(void *data)
|
||||
av_packet_unref(pkt);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* Decode video packet if:
|
||||
* 1. we already decoded an audio packet
|
||||
* 2. there is no audio stream to play
|
||||
@@ -1865,7 +1871,7 @@ static void decode_thread(void *data)
|
||||
av_packet_free(&pkt);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// Read the next frame and stage it in case of audio or video frame.
|
||||
if (av_read_frame(fctx, pkt) < 0)
|
||||
eof = true;
|
||||
@@ -1878,7 +1884,7 @@ static void decode_thread(void *data)
|
||||
/**
|
||||
* Decode subtitle packets right away, since SSA/ASS can operate this way.
|
||||
* If we ever support other subtitles, we need to handle this with a
|
||||
* buffer too
|
||||
* buffer too
|
||||
**/
|
||||
AVSubtitle sub;
|
||||
int finished = 0;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavutil/imgutils.h>
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -69,17 +70,18 @@ video_buffer_t *video_buffer_create(
|
||||
b->buffer[i].pts = 0;
|
||||
b->buffer[i].sws = sws_alloc_context();
|
||||
b->buffer[i].source = av_frame_alloc();
|
||||
#if LIBAVUTIL_VERSION_MAJOR > 55
|
||||
#if ENABLE_HW_ACCEL
|
||||
b->buffer[i].hw_source = av_frame_alloc();
|
||||
#endif
|
||||
b->buffer[i].target = av_frame_alloc();
|
||||
|
||||
avpicture_alloc((AVPicture*)b->buffer[i].target,
|
||||
PIX_FMT_RGB32, width, height);
|
||||
AVFrame* frame = b->buffer[i].target;
|
||||
av_image_alloc(frame->data, frame->linesize,
|
||||
width, height, AV_PIX_FMT_RGB32, 1);
|
||||
|
||||
if (!b->buffer[i].sws ||
|
||||
!b->buffer[i].source ||
|
||||
#if LIBAVUTIL_VERSION_MAJOR > 55
|
||||
#if ENABLE_HW_ACCEL
|
||||
!b->buffer[i].hw_source ||
|
||||
#endif
|
||||
!b->buffer[i].target)
|
||||
@@ -106,11 +108,11 @@ void video_buffer_destroy(video_buffer_t *video_buffer)
|
||||
{
|
||||
for (i = 0; i < video_buffer->capacity; i++)
|
||||
{
|
||||
#if LIBAVUTIL_VERSION_MAJOR > 55
|
||||
#if ENABLE_HW_ACCEL
|
||||
av_frame_free(&video_buffer->buffer[i].hw_source);
|
||||
#endif
|
||||
av_frame_free(&video_buffer->buffer[i].source);
|
||||
avpicture_free((AVPicture*)video_buffer->buffer[i].target);
|
||||
av_freep((AVFrame*)video_buffer->buffer[i].target);
|
||||
av_frame_free(&video_buffer->buffer[i].target);
|
||||
sws_freeContext(video_buffer->buffer[i].sws);
|
||||
}
|
||||
|
||||
@@ -31,22 +31,24 @@ extern "C" {
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
#ifndef PIX_FMT_RGB32
|
||||
#define PIX_FMT_RGB32 AV_PIX_FMT_RGB32
|
||||
#endif
|
||||
/* If libavutil is at least version 55,
|
||||
* and if libavcodec is at least version 57.80.100,
|
||||
* enable hardware acceleration */
|
||||
#define ENABLE_HW_ACCEL ((LIBAVUTIL_VERSION_MAJOR >= 55) && \
|
||||
(LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 80, 100)))
|
||||
|
||||
/**
|
||||
* video_decoder_context
|
||||
*
|
||||
*
|
||||
* Context object for the sws worker threads.
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct video_decoder_context
|
||||
{
|
||||
int64_t pts;
|
||||
struct SwsContext *sws;
|
||||
AVFrame *source;
|
||||
#if LIBAVUTIL_VERSION_MAJOR > 55
|
||||
#if ENABLE_HW_ACCEL
|
||||
AVFrame *hw_source;
|
||||
#endif
|
||||
AVFrame *target;
|
||||
@@ -60,15 +62,15 @@ typedef struct video_decoder_context video_decoder_context_t;
|
||||
|
||||
/**
|
||||
* video_buffer
|
||||
*
|
||||
* The video buffer is a ring buffer, that can be used as a
|
||||
*
|
||||
* The video buffer is a ring buffer, that can be used as a
|
||||
* buffer for many workers while keeping the order.
|
||||
*
|
||||
*
|
||||
* It is thread safe in a sensem that it is designed to work
|
||||
* with one work coordinator, that allocates work slots for
|
||||
* workers threads to work on and later collect the work
|
||||
* product in the same order, as the slots were allocated.
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct video_buffer;
|
||||
typedef struct video_buffer video_buffer_t;
|
||||
@@ -81,38 +83,38 @@ typedef struct video_buffer video_buffer_t;
|
||||
* @height : Height of the target frame.
|
||||
*
|
||||
* Create a video buffer.
|
||||
*
|
||||
*
|
||||
* Returns: A video buffer.
|
||||
*/
|
||||
video_buffer_t *video_buffer_create(size_t capacity, int frame_size, int width, int height);
|
||||
|
||||
/**
|
||||
/**
|
||||
* video_buffer_destroy:
|
||||
* @video_buffer : video buffer.
|
||||
*
|
||||
*
|
||||
* Destroys a video buffer.
|
||||
*
|
||||
*
|
||||
* Does also free the buffer allocated with video_buffer_create().
|
||||
* User has to shut down any external worker threads that may have
|
||||
* a reference to this video buffer.
|
||||
*
|
||||
*
|
||||
**/
|
||||
void video_buffer_destroy(video_buffer_t *video_buffer);
|
||||
|
||||
/**
|
||||
/**
|
||||
* video_buffer_clear:
|
||||
* @video_buffer : video buffer.
|
||||
*
|
||||
*
|
||||
* Clears a video buffer.
|
||||
*
|
||||
*
|
||||
**/
|
||||
void video_buffer_clear(video_buffer_t *video_buffer);
|
||||
|
||||
/**
|
||||
/**
|
||||
* video_buffer_get_open_slot:
|
||||
* @video_buffer : video buffer.
|
||||
* @context : sws context.
|
||||
*
|
||||
*
|
||||
* Returns the next open context inside the ring buffer
|
||||
* and it's index. The status of the slot will be marked as
|
||||
* 'in progress' until slot is marked as finished with
|
||||
@@ -121,21 +123,21 @@ void video_buffer_clear(video_buffer_t *video_buffer);
|
||||
**/
|
||||
void video_buffer_get_open_slot(video_buffer_t *video_buffer, video_decoder_context_t **context);
|
||||
|
||||
/**
|
||||
/**
|
||||
* video_buffer_return_open_slot:
|
||||
* @video_buffer : video buffer.
|
||||
* @context : sws context.
|
||||
*
|
||||
*
|
||||
* Marks the given sws context that is "in progress" as "open" again.
|
||||
*
|
||||
**/
|
||||
void video_buffer_return_open_slot(video_buffer_t *video_buffer, video_decoder_context_t *context);
|
||||
|
||||
/**
|
||||
/**
|
||||
* video_buffer_open_slot:
|
||||
* @video_buffer : video buffer.
|
||||
* @context : sws context.
|
||||
*
|
||||
*
|
||||
* Sets the status of the given context from "finished" to "open".
|
||||
* The slot is then available for producers to claim again with video_buffer_get_open_slot().
|
||||
**/
|
||||
@@ -145,7 +147,7 @@ void video_buffer_open_slot(video_buffer_t *video_buffer, video_decoder_context_
|
||||
* video_buffer_get_finished_slot:
|
||||
* @video_buffer : video buffer.
|
||||
* @context : sws context.
|
||||
*
|
||||
*
|
||||
* Returns a reference for the next context inside
|
||||
* the ring buffer. User needs to use video_buffer_open_slot()
|
||||
* to open the slot in the ringbuffer for the next
|
||||
@@ -158,7 +160,7 @@ void video_buffer_get_finished_slot(video_buffer_t *video_buffer, video_decoder_
|
||||
* video_buffer_finish_slot:
|
||||
* @video_buffer : video buffer.
|
||||
* @context : sws context.
|
||||
*
|
||||
*
|
||||
* Sets the status of the given context from "in progress" to "finished".
|
||||
* This is normally done by a producer. User can then retrieve the finished work
|
||||
* context by calling video_buffer_get_finished_slot().
|
||||
@@ -168,9 +170,9 @@ void video_buffer_finish_slot(video_buffer_t *video_buffer, video_decoder_contex
|
||||
/**
|
||||
* video_buffer_wait_for_open_slot:
|
||||
* @video_buffer : video buffer.
|
||||
*
|
||||
*
|
||||
* Blocks until open slot is available.
|
||||
*
|
||||
*
|
||||
* Returns true if the buffer has a open slot available.
|
||||
*/
|
||||
bool video_buffer_wait_for_open_slot(video_buffer_t *video_buffer);
|
||||
@@ -180,7 +182,7 @@ bool video_buffer_wait_for_open_slot(video_buffer_t *video_buffer);
|
||||
* @video_buffer : video buffer.
|
||||
*
|
||||
* Blocks until finished slot is available.
|
||||
*
|
||||
*
|
||||
* Returns true if the buffers next slot is finished and a
|
||||
* context available.
|
||||
*/
|
||||
@@ -190,7 +192,7 @@ bool video_buffer_wait_for_finished_slot(video_buffer_t *video_buffer);
|
||||
* bool video_buffer_has_open_slot(video_buffer_t *video_buffer)
|
||||
:
|
||||
* @video_buffer : video buffer.
|
||||
*
|
||||
*
|
||||
* Returns true if the buffer has a open slot available.
|
||||
*/
|
||||
bool video_buffer_has_open_slot(video_buffer_t *video_buffer);
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
gong.so: gong.c
|
||||
gcc \
|
||||
-std=c89 \
|
||||
-pedantic \
|
||||
-Wall \
|
||||
-Wno-unused-variable \
|
||||
-g \
|
||||
gong.c \
|
||||
-I../../libretro-common/include/ \
|
||||
-shared \
|
||||
-fPIC \
|
||||
-Wl,--no-undefined \
|
||||
-lm \
|
||||
-o gong.so
|
||||
|
||||
clean:
|
||||
rm -f gong.so
|
||||
@@ -1,939 +0,0 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2017 - Daniel De Matteis
|
||||
* Copyright (C) 2016-2019 - Brad Parker
|
||||
*
|
||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Libretro port by Brad Parker,
|
||||
Original source code by Dan Zaidan: https://danzaidan.itch.io/
|
||||
Original license:
|
||||
"You can do whatever you want with the code, but I am providing it as is without any warranty whatsoever."
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <libretro.h>
|
||||
#include <retro_math.h>
|
||||
#include <retro_inline.h>
|
||||
#include <retro_endianness.h>
|
||||
|
||||
#ifdef RARCH_INTERNAL
|
||||
#include "internal_cores.h"
|
||||
#define GONG_CORE_PREFIX(s) libretro_gong_##s
|
||||
#else
|
||||
#define GONG_CORE_PREFIX(s) s
|
||||
#endif
|
||||
|
||||
#define WIDTH 356
|
||||
#define HEIGHT 200
|
||||
#define MAX_PLAYERS 2
|
||||
#define STATE_SIZE 4096 /* can be anything as long as it's large enough to hold everything */
|
||||
|
||||
static retro_log_printf_t GONG_CORE_PREFIX(log_cb);
|
||||
static retro_video_refresh_t GONG_CORE_PREFIX(video_cb);
|
||||
static retro_input_poll_t GONG_CORE_PREFIX(input_poll_cb);
|
||||
static retro_input_state_t GONG_CORE_PREFIX(input_state_cb);
|
||||
static retro_audio_sample_t GONG_CORE_PREFIX(audio_cb);
|
||||
static retro_audio_sample_batch_t GONG_CORE_PREFIX(audio_batch_cb);
|
||||
static retro_environment_t GONG_CORE_PREFIX(environ_cb);
|
||||
|
||||
static const char *GONG_CORE_PREFIX(valid_extensions) = "gong";
|
||||
|
||||
static unsigned char *video_buf = NULL;
|
||||
|
||||
enum
|
||||
{
|
||||
B_MOVE_UP,
|
||||
B_MOVE_DOWN,
|
||||
B_SPEED_UP,
|
||||
B_COUNT /* This should always be in the bottom */
|
||||
};
|
||||
|
||||
/* any changes here must be handled in serialization code too */
|
||||
typedef struct
|
||||
{
|
||||
union { float f; unsigned u; } py;
|
||||
union { float f; unsigned u; } dpy;
|
||||
} Player;
|
||||
|
||||
/* any changes here must be handled in serialization code too */
|
||||
typedef struct
|
||||
{
|
||||
int half_transition_count;
|
||||
bool ended_down;
|
||||
} Game_Button_State;
|
||||
|
||||
/* any changes here must be handled in serialization code too */
|
||||
typedef struct
|
||||
{
|
||||
Game_Button_State buttons[B_COUNT];
|
||||
float last_dt; /* not in savestate */
|
||||
} Game_Input;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16_t input;
|
||||
uint16_t not_input;
|
||||
uint16_t realinput;
|
||||
int16_t analogYLeft;
|
||||
int16_t analogYRight;
|
||||
} retro_inputs;
|
||||
|
||||
/* any changes here must be handled in serialization code too */
|
||||
typedef struct
|
||||
{
|
||||
unsigned version;
|
||||
unsigned player1_score;
|
||||
unsigned player2_score;
|
||||
union { float f; unsigned u; } player2_speed;
|
||||
union { float f; unsigned u; } ball_px;
|
||||
union { float f; unsigned u; } ball_py;
|
||||
union { float f; unsigned u; } ball_dpx;
|
||||
union { float f; unsigned u; } ball_dpy;
|
||||
union { float f; unsigned u; } ball_speed;
|
||||
union { float f; unsigned u; } current_play_points;
|
||||
float refresh; /* not in savestate */
|
||||
bool is_initialized;
|
||||
bool player2_human;
|
||||
uint16_t previnput[MAX_PLAYERS];
|
||||
Game_Input g_input[MAX_PLAYERS];
|
||||
Player player[MAX_PLAYERS];
|
||||
} State;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* Pixels are always 32-bit wide, memory order XX BB GG RR */
|
||||
int width;
|
||||
int height;
|
||||
int pitch;
|
||||
void *memory;
|
||||
} Game_Offscreen_Buffer;
|
||||
|
||||
static State *g_state = NULL;
|
||||
static Game_Offscreen_Buffer game_buffer = {0};
|
||||
static void game_update_and_render(Game_Input *input, Game_Offscreen_Buffer *draw_buffer);
|
||||
|
||||
static const struct retro_controller_description pads1[] = {
|
||||
{ "Joypad", RETRO_DEVICE_JOYPAD },
|
||||
{ NULL, 0 },
|
||||
};
|
||||
|
||||
static const struct retro_controller_description pads2[] = {
|
||||
{ "Joypad", RETRO_DEVICE_JOYPAD },
|
||||
{ NULL, 0 },
|
||||
};
|
||||
|
||||
static const struct retro_controller_info ports[] = {
|
||||
{ pads1, 1 },
|
||||
{ pads2, 1 },
|
||||
{ 0 },
|
||||
};
|
||||
|
||||
struct retro_input_descriptor desc[] = {
|
||||
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" },
|
||||
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" },
|
||||
{ 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Analog Y" },
|
||||
{ 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Analog Y" },
|
||||
|
||||
{ 0 },
|
||||
};
|
||||
|
||||
static void check_variables(void)
|
||||
{
|
||||
struct retro_variable var = {0};
|
||||
|
||||
var.key = "gong_refresh";
|
||||
if (GONG_CORE_PREFIX(environ_cb)(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
||||
{
|
||||
int i;
|
||||
g_state->refresh = atoi(var.value);
|
||||
|
||||
for (i = 0; i < (int)(sizeof(g_state->g_input) / sizeof(g_state->g_input[0])); i++)
|
||||
g_state->g_input[i].last_dt = 1.0f / g_state->refresh;
|
||||
}
|
||||
|
||||
var.key = "gong_player2";
|
||||
if (GONG_CORE_PREFIX(environ_cb)(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
||||
{
|
||||
if (!strncmp(var.value, "CPU", 3))
|
||||
g_state->player2_human = false;
|
||||
else if (!strncmp(var.value, "Human", 5))
|
||||
g_state->player2_human = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void save_state(void *data, size_t size)
|
||||
{
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
unsigned char *buf = (unsigned char*)data;
|
||||
unsigned version = swap_if_little32(g_state->version);
|
||||
unsigned player1_score = swap_if_little32(g_state->player1_score);
|
||||
unsigned player2_score = swap_if_little32(g_state->player2_score);
|
||||
unsigned player2_speed = swap_if_little32(g_state->player2_speed.u);
|
||||
unsigned ball_px = swap_if_little32(g_state->ball_px.u);
|
||||
unsigned ball_py = swap_if_little32(g_state->ball_py.u);
|
||||
unsigned ball_dpx = swap_if_little32(g_state->ball_dpx.u);
|
||||
unsigned ball_dpy = swap_if_little32(g_state->ball_dpy.u);
|
||||
unsigned ball_speed = swap_if_little32(g_state->ball_speed.u);
|
||||
unsigned current_play_points = swap_if_little32(g_state->current_play_points.u);
|
||||
unsigned is_initialized = g_state->is_initialized;
|
||||
unsigned player2_human = g_state->player2_human;
|
||||
|
||||
(void)size;
|
||||
|
||||
memcpy(buf, &version, sizeof(unsigned));
|
||||
buf += sizeof(unsigned);
|
||||
|
||||
memcpy(buf, &player1_score, sizeof(unsigned));
|
||||
buf += sizeof(unsigned);
|
||||
|
||||
memcpy(buf, &player2_score, sizeof(unsigned));
|
||||
buf += sizeof(unsigned);
|
||||
|
||||
memcpy(buf, &player2_speed, sizeof(unsigned));
|
||||
buf += sizeof(unsigned);
|
||||
|
||||
memcpy(buf, &ball_px, sizeof(unsigned));
|
||||
buf += sizeof(unsigned);
|
||||
|
||||
memcpy(buf, &ball_py, sizeof(unsigned));
|
||||
buf += sizeof(unsigned);
|
||||
|
||||
memcpy(buf, &ball_dpx, sizeof(unsigned));
|
||||
buf += sizeof(unsigned);
|
||||
|
||||
memcpy(buf, &ball_dpy, sizeof(unsigned));
|
||||
buf += sizeof(unsigned);
|
||||
|
||||
memcpy(buf, &ball_speed, sizeof(unsigned));
|
||||
buf += sizeof(unsigned);
|
||||
|
||||
memcpy(buf, ¤t_play_points, sizeof(unsigned));
|
||||
buf += sizeof(unsigned);
|
||||
|
||||
memcpy(buf, &is_initialized, sizeof(unsigned));
|
||||
buf += sizeof(unsigned);
|
||||
|
||||
memcpy(buf, &player2_human, sizeof(unsigned));
|
||||
buf += sizeof(unsigned);
|
||||
|
||||
/* previnput */
|
||||
for (i = 0; i < MAX_PLAYERS; i++)
|
||||
{
|
||||
uint16_t previnput = swap_if_little16(g_state->previnput[i]);
|
||||
memcpy(buf, &previnput, sizeof(uint16_t));
|
||||
buf += sizeof(uint16_t);
|
||||
}
|
||||
|
||||
/* g_input */
|
||||
for (i = 0; i < MAX_PLAYERS; i++)
|
||||
{
|
||||
for (j = 0; j < B_COUNT; j++)
|
||||
{
|
||||
int half_transition_count = swap_if_little32(g_state->g_input[i].buttons[j].half_transition_count);
|
||||
unsigned ended_down = g_state->g_input[i].buttons[j].ended_down;
|
||||
|
||||
memcpy(buf, &half_transition_count, sizeof(int));
|
||||
buf += sizeof(int);
|
||||
|
||||
memcpy(buf, &ended_down, sizeof(unsigned));
|
||||
buf += sizeof(unsigned);
|
||||
}
|
||||
}
|
||||
|
||||
/* player */
|
||||
for (i = 0; i < MAX_PLAYERS; i++)
|
||||
{
|
||||
unsigned py = swap_if_little32((unsigned)g_state->player[i].py.u);
|
||||
|
||||
memcpy(buf, &py, sizeof(unsigned));
|
||||
buf += sizeof(uint16_t);
|
||||
}
|
||||
}
|
||||
|
||||
static void load_state(const void *data, size_t size)
|
||||
{
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
const unsigned char *buf = (const unsigned char*)data;
|
||||
|
||||
(void)size;
|
||||
|
||||
memset(g_state, 0, sizeof(*g_state));
|
||||
|
||||
g_state->version = swap_if_little32(*(unsigned*)buf);
|
||||
buf += sizeof(unsigned);
|
||||
|
||||
g_state->player1_score = swap_if_little32(*(unsigned*)buf);
|
||||
buf += sizeof(unsigned);
|
||||
|
||||
g_state->player2_score = swap_if_little32(*(unsigned*)buf);
|
||||
buf += sizeof(unsigned);
|
||||
|
||||
g_state->player2_speed.u = swap_if_little32(*(unsigned*)buf);
|
||||
buf += sizeof(unsigned);
|
||||
|
||||
g_state->ball_px.u = swap_if_little32(*(unsigned*)buf);
|
||||
buf += sizeof(unsigned);
|
||||
|
||||
g_state->ball_py.u = swap_if_little32(*(unsigned*)buf);
|
||||
buf += sizeof(unsigned);
|
||||
|
||||
g_state->ball_dpx.u = swap_if_little32(*(unsigned*)buf);
|
||||
buf += sizeof(unsigned);
|
||||
|
||||
g_state->ball_dpy.u = swap_if_little32(*(unsigned*)buf);
|
||||
buf += sizeof(unsigned);
|
||||
|
||||
g_state->ball_speed.u = swap_if_little32(*(unsigned*)buf);
|
||||
buf += sizeof(unsigned);
|
||||
|
||||
g_state->current_play_points.u = swap_if_little32(*(unsigned*)buf);
|
||||
buf += sizeof(unsigned);
|
||||
|
||||
g_state->is_initialized = (bool)swap_if_little32(*(unsigned*)buf);
|
||||
buf += sizeof(unsigned);
|
||||
|
||||
g_state->player2_human = (bool)swap_if_little32(*(unsigned*)buf);
|
||||
buf += sizeof(unsigned);
|
||||
|
||||
/* previnput */
|
||||
for (i = 0; i < MAX_PLAYERS; i++)
|
||||
{
|
||||
uint16_t previnput = swap_if_little16(*(uint16_t*)buf);
|
||||
g_state->previnput[i] = previnput;
|
||||
buf += sizeof(uint16_t);
|
||||
}
|
||||
|
||||
/* g_input */
|
||||
for (i = 0; i < MAX_PLAYERS; i++)
|
||||
{
|
||||
for (j = 0; j < B_COUNT; j++)
|
||||
{
|
||||
int half_transition_count;
|
||||
bool ended_down;
|
||||
|
||||
half_transition_count = (int)swap_if_little32(*(unsigned*)buf);
|
||||
|
||||
g_state->g_input[i].buttons[j].half_transition_count = half_transition_count;
|
||||
buf += sizeof(int);
|
||||
|
||||
ended_down = (bool)swap_if_little32(*(unsigned*)buf);
|
||||
|
||||
g_state->g_input[i].buttons[j].ended_down = ended_down;
|
||||
buf += sizeof(unsigned);
|
||||
}
|
||||
}
|
||||
|
||||
/* player */
|
||||
for (i = 0; i < MAX_PLAYERS; i++)
|
||||
{
|
||||
g_state->player[i].py.u = swap_if_little32(*(unsigned*)buf);
|
||||
buf += sizeof(unsigned);
|
||||
|
||||
g_state->player[i].dpy.u = swap_if_little32(*(unsigned*)buf);
|
||||
buf += sizeof(unsigned);
|
||||
}
|
||||
|
||||
check_variables();
|
||||
}
|
||||
|
||||
static INLINE bool is_down(Game_Button_State state)
|
||||
{
|
||||
return state.ended_down;
|
||||
}
|
||||
|
||||
void GONG_CORE_PREFIX(retro_get_system_info)(struct retro_system_info *info)
|
||||
{
|
||||
info->library_name = "gong";
|
||||
info->library_version = "v1.0";
|
||||
info->need_fullpath = false;
|
||||
info->block_extract = false;
|
||||
info->valid_extensions = GONG_CORE_PREFIX(valid_extensions);
|
||||
}
|
||||
|
||||
void GONG_CORE_PREFIX(retro_get_system_av_info)(struct retro_system_av_info *info)
|
||||
{
|
||||
info->geometry.base_width = WIDTH;
|
||||
info->geometry.base_height = HEIGHT;
|
||||
info->geometry.max_width = WIDTH;
|
||||
info->geometry.max_height = HEIGHT;
|
||||
info->geometry.aspect_ratio = 16.0f / 9.0f;
|
||||
info->timing.fps = g_state->refresh;
|
||||
info->timing.sample_rate = 44100.0;
|
||||
}
|
||||
|
||||
void GONG_CORE_PREFIX(retro_init)(void)
|
||||
{
|
||||
struct retro_log_callback log;
|
||||
|
||||
g_state = (State*)calloc(1, sizeof(*g_state));
|
||||
|
||||
if (GONG_CORE_PREFIX(environ_cb)(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log))
|
||||
GONG_CORE_PREFIX(log_cb) = log.log;
|
||||
else
|
||||
GONG_CORE_PREFIX(log_cb) = NULL;
|
||||
|
||||
video_buf = (unsigned char*)calloc(1, WIDTH * HEIGHT * sizeof(unsigned));
|
||||
|
||||
game_buffer.width = WIDTH;
|
||||
game_buffer.height = HEIGHT;
|
||||
game_buffer.pitch = WIDTH * sizeof(unsigned);
|
||||
game_buffer.memory = video_buf;
|
||||
}
|
||||
|
||||
void GONG_CORE_PREFIX(retro_deinit)(void)
|
||||
{
|
||||
if (video_buf)
|
||||
free(video_buf);
|
||||
|
||||
video_buf = NULL;
|
||||
game_buffer.memory = NULL;
|
||||
|
||||
if (g_state)
|
||||
free(g_state);
|
||||
}
|
||||
|
||||
void GONG_CORE_PREFIX(retro_set_environment)(retro_environment_t cb)
|
||||
{
|
||||
bool no_content = true;
|
||||
|
||||
static const struct retro_variable vars[] = {
|
||||
{ "gong_player2", "Player 2; CPU|Human" },
|
||||
{ "gong_refresh", "Video Refresh Rate (restart); 60|70|72|75|100|119|120|140|144" },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
GONG_CORE_PREFIX(environ_cb) = cb;
|
||||
|
||||
cb(RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME, &no_content);
|
||||
cb(RETRO_ENVIRONMENT_SET_VARIABLES, (void*)vars);
|
||||
cb(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports);
|
||||
}
|
||||
|
||||
void GONG_CORE_PREFIX(retro_set_video_refresh)(retro_video_refresh_t cb)
|
||||
{
|
||||
GONG_CORE_PREFIX(video_cb) = cb;
|
||||
}
|
||||
|
||||
void GONG_CORE_PREFIX(retro_set_audio_sample)(retro_audio_sample_t cb)
|
||||
{
|
||||
GONG_CORE_PREFIX(audio_cb) = cb;
|
||||
}
|
||||
|
||||
void GONG_CORE_PREFIX(retro_set_audio_sample_batch)(retro_audio_sample_batch_t cb)
|
||||
{
|
||||
GONG_CORE_PREFIX(audio_batch_cb) = cb;
|
||||
}
|
||||
|
||||
void GONG_CORE_PREFIX(retro_set_input_poll)(retro_input_poll_t cb)
|
||||
{
|
||||
GONG_CORE_PREFIX(input_poll_cb) = cb;
|
||||
}
|
||||
|
||||
void GONG_CORE_PREFIX(retro_set_input_state)(retro_input_state_t cb)
|
||||
{
|
||||
GONG_CORE_PREFIX(input_state_cb) = cb;
|
||||
}
|
||||
|
||||
void GONG_CORE_PREFIX(retro_set_controller_port_device)(unsigned a, unsigned b)
|
||||
{
|
||||
}
|
||||
|
||||
void GONG_CORE_PREFIX(retro_reset)(void)
|
||||
{
|
||||
memset(g_state, 0, sizeof(*g_state));
|
||||
check_variables();
|
||||
}
|
||||
|
||||
size_t GONG_CORE_PREFIX(retro_serialize_size)(void)
|
||||
{
|
||||
return STATE_SIZE;
|
||||
}
|
||||
|
||||
bool GONG_CORE_PREFIX(retro_serialize)(void *data, size_t size)
|
||||
{
|
||||
if (size != STATE_SIZE)
|
||||
return false;
|
||||
|
||||
save_state(data, size);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GONG_CORE_PREFIX(retro_unserialize)(const void *data, size_t size)
|
||||
{
|
||||
if (size != STATE_SIZE)
|
||||
return false;
|
||||
|
||||
load_state(data, size);
|
||||
return true;
|
||||
}
|
||||
|
||||
void GONG_CORE_PREFIX(retro_cheat_reset)(void)
|
||||
{
|
||||
}
|
||||
|
||||
void GONG_CORE_PREFIX(retro_cheat_set)(unsigned a, bool b, const char * c)
|
||||
{
|
||||
}
|
||||
|
||||
bool GONG_CORE_PREFIX(retro_load_game)(const struct retro_game_info *info)
|
||||
{
|
||||
enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_XRGB8888;
|
||||
|
||||
check_variables();
|
||||
|
||||
if (!GONG_CORE_PREFIX(environ_cb)(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt))
|
||||
{
|
||||
if (GONG_CORE_PREFIX(log_cb))
|
||||
GONG_CORE_PREFIX(log_cb)(RETRO_LOG_INFO, "XRGB8888 is not supported.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GONG_CORE_PREFIX(retro_load_game_special)(unsigned a, const struct retro_game_info *b, size_t c)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void GONG_CORE_PREFIX(retro_unload_game)(void)
|
||||
{
|
||||
}
|
||||
|
||||
unsigned GONG_CORE_PREFIX(retro_get_region)(void)
|
||||
{
|
||||
return RETRO_REGION_NTSC;
|
||||
}
|
||||
|
||||
void* GONG_CORE_PREFIX(retro_get_memory_data)(unsigned id)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t GONG_CORE_PREFIX(retro_get_memory_size)(unsigned id)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void process_joypad(Game_Button_State *new_state, bool is_down)
|
||||
{
|
||||
if (new_state->ended_down != is_down)
|
||||
{
|
||||
new_state->ended_down = is_down;
|
||||
new_state->half_transition_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_key_up_or_down(int16_t input, int16_t not_input, int key)
|
||||
{
|
||||
if (input & (1 << key) || not_input & (1 << key))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void GONG_CORE_PREFIX(retro_run)(void)
|
||||
{
|
||||
int i = 0;
|
||||
int port = 0;
|
||||
bool updated = false;
|
||||
retro_inputs inputs[MAX_PLAYERS] = {{0}};
|
||||
|
||||
if (GONG_CORE_PREFIX(environ_cb)(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated)
|
||||
check_variables();
|
||||
|
||||
GONG_CORE_PREFIX(input_poll_cb)();
|
||||
|
||||
for (port = 0; port < MAX_PLAYERS; port++)
|
||||
{
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
if (GONG_CORE_PREFIX(input_state_cb)(port, RETRO_DEVICE_JOYPAD, 0, i))
|
||||
{
|
||||
inputs[port].realinput |= 1 << i;
|
||||
}
|
||||
}
|
||||
|
||||
inputs[port].analogYLeft = GONG_CORE_PREFIX(input_state_cb)(port, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y) / 5000.0f;
|
||||
inputs[port].analogYRight = GONG_CORE_PREFIX(input_state_cb)(port, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y) / 5000.0f;
|
||||
|
||||
if (inputs[port].analogYLeft > 0)
|
||||
inputs[port].realinput |= (1 << RETRO_DEVICE_ID_JOYPAD_DOWN);
|
||||
else if (inputs[port].analogYRight > 0)
|
||||
inputs[port].realinput |= (1 << RETRO_DEVICE_ID_JOYPAD_DOWN);
|
||||
|
||||
if (inputs[port].analogYLeft < 0)
|
||||
inputs[port].realinput |= (1 << RETRO_DEVICE_ID_JOYPAD_UP);
|
||||
else if (inputs[port].analogYRight < 0)
|
||||
inputs[port].realinput |= (1 << RETRO_DEVICE_ID_JOYPAD_UP);
|
||||
|
||||
inputs[port].input = inputs[port].realinput & ~g_state->previnput[port];
|
||||
inputs[port].not_input = g_state->previnput[port] & ~inputs[port].realinput;
|
||||
|
||||
if (is_key_up_or_down(inputs[port].input, inputs[port].not_input, RETRO_DEVICE_ID_JOYPAD_UP))
|
||||
process_joypad(&g_state->g_input[port].buttons[B_MOVE_UP], inputs[port].realinput & (1 << RETRO_DEVICE_ID_JOYPAD_UP));
|
||||
else if (is_key_up_or_down(inputs[port].input, inputs[port].not_input, RETRO_DEVICE_ID_JOYPAD_DOWN))
|
||||
process_joypad(&g_state->g_input[port].buttons[B_MOVE_DOWN], inputs[port].realinput & (1 << RETRO_DEVICE_ID_JOYPAD_DOWN));
|
||||
else if (is_key_up_or_down(inputs[port].input, inputs[port].not_input, RETRO_DEVICE_ID_JOYPAD_DOWN))
|
||||
process_joypad(&g_state->g_input[port].buttons[B_MOVE_DOWN], inputs[port].realinput & (1 << RETRO_DEVICE_ID_JOYPAD_DOWN));
|
||||
|
||||
if (is_key_up_or_down(inputs[port].input, inputs[port].not_input, RETRO_DEVICE_ID_JOYPAD_A))
|
||||
process_joypad(&g_state->g_input[port].buttons[B_SPEED_UP], inputs[port].realinput & (1 << RETRO_DEVICE_ID_JOYPAD_A));
|
||||
else if (is_key_up_or_down(inputs[port].input, inputs[port].not_input, RETRO_DEVICE_ID_JOYPAD_B))
|
||||
process_joypad(&g_state->g_input[port].buttons[B_SPEED_UP], inputs[port].realinput & (1 << RETRO_DEVICE_ID_JOYPAD_B));
|
||||
else if (is_key_up_or_down(inputs[port].input, inputs[port].not_input, RETRO_DEVICE_ID_JOYPAD_X))
|
||||
process_joypad(&g_state->g_input[port].buttons[B_SPEED_UP], inputs[port].realinput & (1 << RETRO_DEVICE_ID_JOYPAD_X));
|
||||
else if (is_key_up_or_down(inputs[port].input, inputs[port].not_input, RETRO_DEVICE_ID_JOYPAD_Y))
|
||||
process_joypad(&g_state->g_input[port].buttons[B_SPEED_UP], inputs[port].realinput & (1 << RETRO_DEVICE_ID_JOYPAD_Y));
|
||||
|
||||
g_state->previnput[port] = inputs[port].realinput;
|
||||
}
|
||||
|
||||
game_update_and_render(g_state->g_input, &game_buffer);
|
||||
|
||||
GONG_CORE_PREFIX(video_cb)(video_buf, WIDTH, HEIGHT, WIDTH * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
unsigned GONG_CORE_PREFIX(retro_api_version)(void)
|
||||
{
|
||||
return RETRO_API_VERSION;
|
||||
}
|
||||
|
||||
static void draw_rect_in_pixels(Game_Offscreen_Buffer *buffer, unsigned color, int min_x, int min_y, int max_x, int max_y)
|
||||
{
|
||||
int y;
|
||||
|
||||
min_x = MAX(min_x, 0);
|
||||
min_y = MAX(min_y, 0);
|
||||
max_x = MIN(max_x, buffer->width);
|
||||
max_y = MIN(max_y, buffer->height);
|
||||
|
||||
for (y = min_y; y < max_y; y++)
|
||||
{
|
||||
int x;
|
||||
|
||||
for (x = min_x; x < max_x; x++)
|
||||
{
|
||||
unsigned *pixel = (unsigned*)((unsigned char*)buffer->memory + ((buffer->width * (buffer->height - y - 1) + x) * sizeof(unsigned)));
|
||||
|
||||
*pixel++ = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void clear(Game_Offscreen_Buffer *buffer, unsigned color)
|
||||
{
|
||||
draw_rect_in_pixels(buffer, color, 0, 0, buffer->width, buffer->height);
|
||||
}
|
||||
|
||||
static void draw_rect(Game_Offscreen_Buffer *buffer, unsigned color, float x, float y, float half_size_x, float half_size_y)
|
||||
{
|
||||
/* @Hardcoded to always keep the playing field area on screen, no matter the aspect ratio */
|
||||
float scale = .01f;
|
||||
float relative_axis = (float)buffer->height;
|
||||
int min_x, min_y, max_x, max_y;
|
||||
|
||||
if ((float)buffer->width / (float)buffer->height < 1.77f)
|
||||
{
|
||||
relative_axis = (float)buffer->width;
|
||||
scale = .0056f;
|
||||
}
|
||||
|
||||
half_size_x *= relative_axis * scale;
|
||||
half_size_y *= relative_axis * scale;
|
||||
x *= relative_axis * scale;
|
||||
y *= relative_axis * scale;
|
||||
|
||||
x = x + buffer->width / 2;
|
||||
y = y + buffer->height / 2;
|
||||
|
||||
min_x = (unsigned)(x - half_size_x);
|
||||
min_y = (unsigned)(y - half_size_y);
|
||||
max_x = (unsigned)(x + half_size_x);
|
||||
max_y = (unsigned)(y + half_size_y);
|
||||
|
||||
draw_rect_in_pixels(buffer, color, min_x, min_y, max_x, max_y);
|
||||
}
|
||||
|
||||
static void draw_number(Game_Offscreen_Buffer *buffer, unsigned number, unsigned color, float x, float y)
|
||||
{
|
||||
float at_x = x;
|
||||
|
||||
do {
|
||||
unsigned alg = number % 10;
|
||||
|
||||
number /= 10;
|
||||
|
||||
switch (alg)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
draw_rect(buffer, color, at_x - 2.f, y, .5f, 4.f);
|
||||
draw_rect(buffer, color, at_x + 2.f, y, .5f, 4.f);
|
||||
draw_rect(buffer, color, at_x, y + 4.f, 2.5f, .5f);
|
||||
draw_rect(buffer, color, at_x, y - 4.f, 2.5f, .5f);
|
||||
break;
|
||||
}
|
||||
|
||||
case 1:
|
||||
{
|
||||
draw_rect(buffer, color, at_x + 2.f, y, .5f, 4.5f);
|
||||
break;
|
||||
}
|
||||
|
||||
case 2:
|
||||
{
|
||||
draw_rect(buffer, color, at_x - 2.f, y - 2.f, .5f, 2.f);
|
||||
draw_rect(buffer, color, at_x + 2.f, y + 2.f, .5f, 2.f);
|
||||
draw_rect(buffer, color, at_x, y + 4.f, 2.5f, .5f);
|
||||
draw_rect(buffer, color, at_x, y, 2.5f, .5f);
|
||||
draw_rect(buffer, color, at_x, y - 4.f, 2.5f, .5f);
|
||||
break;
|
||||
}
|
||||
|
||||
case 3:
|
||||
{
|
||||
draw_rect(buffer, color, at_x + 2.f, y, .5f, 4.f);
|
||||
draw_rect(buffer, color, at_x, y + 4.f, 2.5f, .5f);
|
||||
draw_rect(buffer, color, at_x, y, 2.5f, .5f);
|
||||
draw_rect(buffer, color, at_x, y - 4.f, 2.5f, .5f);
|
||||
break;
|
||||
};
|
||||
|
||||
case 4:
|
||||
{
|
||||
draw_rect(buffer, color, at_x, y, 2.5f, .5f);
|
||||
draw_rect(buffer, color, at_x + 2.f, y, .5f, 4.5f);
|
||||
draw_rect(buffer, color, at_x - 2.f, y+2.5f, .5f, 2.f);
|
||||
break;
|
||||
};
|
||||
|
||||
case 5:
|
||||
{
|
||||
draw_rect(buffer, color, at_x + 2.f, y-2.f, .5f, 2.f);
|
||||
draw_rect(buffer, color, at_x - 2.f, y+2.f, .5f, 2.f);
|
||||
draw_rect(buffer, color, at_x, y + 4.f, 2.5f, .5f);
|
||||
draw_rect(buffer, color, at_x, y, 2.5f, .5f);
|
||||
draw_rect(buffer, color, at_x, y - 4.f, 2.5f, .5f);
|
||||
break;
|
||||
};
|
||||
|
||||
case 6:
|
||||
{
|
||||
draw_rect(buffer, color, at_x + 2.f, y-2.f, .5f, 2.f);
|
||||
draw_rect(buffer, color, at_x - 2.f, y, .5f, 4.f);
|
||||
draw_rect(buffer, color, at_x, y + 4.f, 2.5f, .5f);
|
||||
draw_rect(buffer, color, at_x, y, 2.5f, .5f);
|
||||
draw_rect(buffer, color, at_x, y - 4.f, 2.5f, .5f);
|
||||
break;
|
||||
};
|
||||
|
||||
case 7:
|
||||
{
|
||||
draw_rect(buffer, color, at_x + 2.f, y, .5f, 4.5f);
|
||||
draw_rect(buffer, color, at_x, y + 4.f, 2.5f, .5f);
|
||||
break;
|
||||
};
|
||||
|
||||
case 8:
|
||||
{
|
||||
draw_rect(buffer, color, at_x - 2.f, y, .5f, 4.f);
|
||||
draw_rect(buffer, color, at_x + 2.f, y, .5f, 4.f);
|
||||
draw_rect(buffer, color, at_x, y + 4.f, 2.5f, .5f);
|
||||
draw_rect(buffer, color, at_x, y - 4.f, 2.5f, .5f);
|
||||
draw_rect(buffer, color, at_x, y, 2.5f, .5f);
|
||||
break;
|
||||
};
|
||||
|
||||
case 9:
|
||||
{
|
||||
draw_rect(buffer, color, at_x - 2.f, y + 2.f, .5f, 2.f);
|
||||
draw_rect(buffer, color, at_x + 2.f, y, .5f, 4.f);
|
||||
draw_rect(buffer, color, at_x, y + 4.f, 2.5f, .5f);
|
||||
draw_rect(buffer, color, at_x, y, 2.5f, .5f);
|
||||
draw_rect(buffer, color, at_x, y - 4.f, 2.5f, .5f);
|
||||
break;
|
||||
};
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
at_x -= 7.f;
|
||||
} while(number > 0);
|
||||
}
|
||||
|
||||
static void game_update_and_render(Game_Input *input, Game_Offscreen_Buffer *draw_buffer)
|
||||
{
|
||||
const float initial_ball_speed = 80.f;
|
||||
float playing_field_x = 85.f;
|
||||
float playing_field_y = 48.f;
|
||||
float player_size_x = 2.5f;
|
||||
float player_size_y = 10.f;
|
||||
int i;
|
||||
|
||||
if (!g_state->is_initialized)
|
||||
{
|
||||
g_state->is_initialized = 1;
|
||||
g_state->ball_px.f = 0;
|
||||
g_state->ball_py.f = 0;
|
||||
g_state->ball_dpx.f = initial_ball_speed;
|
||||
g_state->ball_dpy.f = 0;
|
||||
g_state->current_play_points.f = 10.f;
|
||||
g_state->player2_speed.f = 80.f;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_PLAYERS; i++)
|
||||
{
|
||||
float speed = 80.f;
|
||||
|
||||
if (i == 1 && !g_state->player2_human)
|
||||
break;
|
||||
|
||||
g_state->player[i].dpy.f = 0.f;
|
||||
|
||||
if (is_down(input[i].buttons[B_SPEED_UP]))
|
||||
speed = 150.f;
|
||||
|
||||
if (is_down(input[i].buttons[B_MOVE_UP]))
|
||||
{
|
||||
if (g_state->player[i].py.f < playing_field_y - player_size_y)
|
||||
{
|
||||
g_state->player[i].dpy.f = speed;
|
||||
}
|
||||
|
||||
if (g_state->player[i].py.f < -playing_field_y + player_size_y)
|
||||
{
|
||||
g_state->player[i].py.f = -playing_field_y + player_size_y;
|
||||
g_state->player[i].dpy.f = 0.f;
|
||||
}
|
||||
}
|
||||
if (is_down(input[i].buttons[B_MOVE_DOWN]))
|
||||
{
|
||||
if (g_state->player[i].py.f > -playing_field_y + player_size_y)
|
||||
{
|
||||
g_state->player[i].dpy.f = -speed;
|
||||
}
|
||||
|
||||
if (g_state->player[i].py.f < -playing_field_y + player_size_y)
|
||||
{
|
||||
g_state->player[i].py.f = -playing_field_y + player_size_y;
|
||||
g_state->player[i].dpy.f = 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
g_state->player[i].py.f += g_state->player[i].dpy.f * input->last_dt;
|
||||
}
|
||||
|
||||
if (!g_state->player2_human)
|
||||
{
|
||||
g_state->player[1].dpy.f = (g_state->ball_py.f - g_state->player[1].py.f) * 100.f;
|
||||
g_state->player[1].dpy.f = MIN(g_state->player[1].dpy.f, g_state->player2_speed.f);
|
||||
g_state->player[1].dpy.f = MAX(g_state->player[1].dpy.f, -g_state->player2_speed.f);
|
||||
g_state->player[1].py.f += g_state->player[1].dpy.f * input->last_dt;
|
||||
|
||||
if (g_state->player[1].py.f < -playing_field_y + player_size_y)
|
||||
{
|
||||
g_state->player[1].py.f = -playing_field_y + player_size_y;
|
||||
g_state->player[1].dpy.f = 0.f;
|
||||
}
|
||||
|
||||
if (g_state->player[1].py.f > playing_field_y - player_size_y)
|
||||
{
|
||||
g_state->player[1].py.f = playing_field_y - player_size_y;
|
||||
g_state->player[1].dpy.f = 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
g_state->ball_px.f += g_state->ball_dpx.f * input->last_dt;
|
||||
|
||||
if (g_state->ball_dpx.f > 0)
|
||||
{
|
||||
g_state->ball_dpx.f += 10.f * input->last_dt;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_state->ball_dpx.f -= 10.f * input->last_dt;
|
||||
}
|
||||
|
||||
g_state->ball_py.f += g_state->ball_dpy.f * input->last_dt;
|
||||
|
||||
if (g_state->ball_py.f > playing_field_y - 1.f)
|
||||
{
|
||||
g_state->ball_py.f = playing_field_y - 1.f;
|
||||
g_state->ball_dpy.f *= -1.f;
|
||||
}
|
||||
else if (g_state->ball_py.f < -playing_field_y + 1)
|
||||
{
|
||||
g_state->ball_py.f = -playing_field_y + 1.f;
|
||||
g_state->ball_dpy.f *= -1;
|
||||
}
|
||||
|
||||
if (g_state->ball_px.f > 80.f - 2.5f - 1.f) /* @Hardcoded */
|
||||
{
|
||||
if ((g_state->ball_py.f >= (g_state->player[1].py.f - 10.f)) && (g_state->ball_py.f <= (g_state->player[1].py.f + 10.f)))
|
||||
{
|
||||
g_state->ball_dpx.f *= -1.f;
|
||||
g_state->ball_px.f = 80.f - 2.5f - 1.f; /* @Hardcoded */
|
||||
g_state->ball_dpy.f = (g_state->ball_py.f - g_state->player[1].py.f) + g_state->player[1].dpy.f;
|
||||
++g_state->current_play_points.f;
|
||||
}
|
||||
else if (g_state->ball_px.f >= playing_field_x - 1)
|
||||
{
|
||||
g_state->ball_px.f = 0;
|
||||
g_state->ball_py.f = 0;
|
||||
g_state->ball_dpy.f = 0;
|
||||
g_state->ball_dpx.f = -initial_ball_speed;
|
||||
g_state->player2_score += (unsigned)g_state->current_play_points.f;
|
||||
g_state->current_play_points.f = 10.f;
|
||||
}
|
||||
}
|
||||
else if (g_state->ball_px.f < -80 + 2.5f + 1.f) /* @Hardcoded */
|
||||
{
|
||||
if ((g_state->ball_py.f >= (g_state->player[0].py.f - 10.f)) && (g_state->ball_py.f <= (g_state->player[0].py.f + 10.f)))
|
||||
{
|
||||
g_state->ball_dpx.f *= -1.f;
|
||||
g_state->ball_px.f = -80 + 2.5f + 1.f; /* @Hardcoded */
|
||||
g_state->ball_dpy.f = (g_state->ball_py.f - g_state->player[0].py.f) + g_state->player[0].dpy.f;
|
||||
++g_state->current_play_points.f;
|
||||
}
|
||||
else if (g_state->ball_px.f <= -playing_field_x + 1)
|
||||
{
|
||||
g_state->ball_px.f = 0;
|
||||
g_state->ball_py.f = 0;
|
||||
g_state->ball_dpy.f = 0;
|
||||
g_state->ball_dpx.f = initial_ball_speed;
|
||||
g_state->player1_score += (unsigned)g_state->current_play_points.f;
|
||||
g_state->current_play_points.f = 10.f;
|
||||
|
||||
if (!g_state->player2_human)
|
||||
g_state->player2_speed.f += g_state->current_play_points.f * 0.01f;
|
||||
}
|
||||
}
|
||||
|
||||
clear(draw_buffer, 0x021077);
|
||||
draw_rect(draw_buffer, 0x000530, 0.f, 0.f, playing_field_x, playing_field_y);
|
||||
|
||||
draw_rect(draw_buffer, 0x00ffff, -80.f, g_state->player[0].py.f, player_size_x, player_size_y);
|
||||
draw_rect(draw_buffer, 0x00ffff, 80.f, g_state->player[1].py.f, player_size_x, player_size_y);
|
||||
|
||||
draw_rect(draw_buffer, 0xffff00, g_state->ball_px.f, g_state->ball_py.f, 1.f, 1.f);
|
||||
|
||||
draw_number(draw_buffer, (unsigned)g_state->current_play_points.f, 0xaaaaaa, 0.f, 38.f);
|
||||
draw_number(draw_buffer, g_state->player1_score, 0xff6611, 20.f, 38.f);
|
||||
draw_number(draw_buffer, g_state->player2_score, 0xff6611, -20.f, 38.f);
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
#include "../internal_cores.h"
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
global: retro_*;
|
||||
local: *;
|
||||
};
|
||||
+171
-1
@@ -209,6 +209,76 @@ static int database_cursor_iterate(libretrodb_cursor_t *cur,
|
||||
if (!string_is_empty(val_string))
|
||||
db_info->genre = strdup(val_string);
|
||||
}
|
||||
else if (string_is_equal(str, "category"))
|
||||
{
|
||||
if (!string_is_empty(val_string))
|
||||
db_info->category = strdup(val_string);
|
||||
}
|
||||
else if (string_is_equal(str, "language"))
|
||||
{
|
||||
if (!string_is_empty(val_string))
|
||||
db_info->language = strdup(val_string);
|
||||
}
|
||||
else if (string_is_equal(str, "region"))
|
||||
{
|
||||
if (!string_is_empty(val_string))
|
||||
db_info->region = strdup(val_string);
|
||||
}
|
||||
else if (string_is_equal(str, "score"))
|
||||
{
|
||||
if (!string_is_empty(val_string))
|
||||
db_info->score = strdup(val_string);
|
||||
}
|
||||
else if (string_is_equal(str, "media"))
|
||||
{
|
||||
if (!string_is_empty(val_string))
|
||||
db_info->media = strdup(val_string);
|
||||
}
|
||||
else if (string_is_equal(str, "controls"))
|
||||
{
|
||||
if (!string_is_empty(val_string))
|
||||
db_info->controls = strdup(val_string);
|
||||
}
|
||||
else if (string_is_equal(str, "artstyle"))
|
||||
{
|
||||
if (!string_is_empty(val_string))
|
||||
db_info->artstyle = strdup(val_string);
|
||||
}
|
||||
else if (string_is_equal(str, "gameplay"))
|
||||
{
|
||||
if (!string_is_empty(val_string))
|
||||
db_info->gameplay = strdup(val_string);
|
||||
}
|
||||
else if (string_is_equal(str, "narrative"))
|
||||
{
|
||||
if (!string_is_empty(val_string))
|
||||
db_info->narrative = strdup(val_string);
|
||||
}
|
||||
else if (string_is_equal(str, "pacing"))
|
||||
{
|
||||
if (!string_is_empty(val_string))
|
||||
db_info->pacing = strdup(val_string);
|
||||
}
|
||||
else if (string_is_equal(str, "perspective"))
|
||||
{
|
||||
if (!string_is_empty(val_string))
|
||||
db_info->perspective = strdup(val_string);
|
||||
}
|
||||
else if (string_is_equal(str, "setting"))
|
||||
{
|
||||
if (!string_is_empty(val_string))
|
||||
db_info->setting = strdup(val_string);
|
||||
}
|
||||
else if (string_is_equal(str, "visual"))
|
||||
{
|
||||
if (!string_is_empty(val_string))
|
||||
db_info->visual = strdup(val_string);
|
||||
}
|
||||
else if (string_is_equal(str, "vehicular"))
|
||||
{
|
||||
if (!string_is_empty(val_string))
|
||||
db_info->vehicular = strdup(val_string);
|
||||
}
|
||||
else if (string_is_equal(str, "origin"))
|
||||
{
|
||||
if (!string_is_empty(val_string))
|
||||
@@ -274,6 +344,12 @@ static int database_cursor_iterate(libretrodb_cursor_t *cur,
|
||||
db_info->releaseyear = (unsigned)val->val.uint_;
|
||||
else if (string_is_equal(str, "rumble"))
|
||||
db_info->rumble_supported = (int)val->val.uint_;
|
||||
else if (string_is_equal(str, "achievements"))
|
||||
db_info->achievements = (int)val->val.uint_;
|
||||
else if (string_is_equal(str, "console_exclusive"))
|
||||
db_info->console_exclusive = (int)val->val.uint_;
|
||||
else if (string_is_equal(str, "platform_exclusive"))
|
||||
db_info->platform_exclusive = (int)val->val.uint_;
|
||||
else if (string_is_equal(str, "coop"))
|
||||
db_info->coop_supported = (int)val->val.uint_;
|
||||
else if (string_is_equal(str, "analog"))
|
||||
@@ -504,6 +580,34 @@ database_info_list_t *database_info_list_new(
|
||||
free(db_info.franchise);
|
||||
if (db_info.genre)
|
||||
free(db_info.genre);
|
||||
if (db_info.category)
|
||||
free(db_info.category);
|
||||
if (db_info.language)
|
||||
free(db_info.language);
|
||||
if (db_info.region)
|
||||
free(db_info.region);
|
||||
if (db_info.score)
|
||||
free(db_info.score);
|
||||
if (db_info.media)
|
||||
free(db_info.media);
|
||||
if (db_info.controls)
|
||||
free(db_info.controls);
|
||||
if (db_info.artstyle)
|
||||
free(db_info.artstyle);
|
||||
if (db_info.gameplay)
|
||||
free(db_info.gameplay);
|
||||
if (db_info.narrative)
|
||||
free(db_info.narrative);
|
||||
if (db_info.pacing)
|
||||
free(db_info.pacing);
|
||||
if (db_info.perspective)
|
||||
free(db_info.perspective);
|
||||
if (db_info.setting)
|
||||
free(db_info.setting);
|
||||
if (db_info.visual)
|
||||
free(db_info.visual);
|
||||
if (db_info.vehicular)
|
||||
free(db_info.vehicular);
|
||||
if (db_info.name)
|
||||
free(db_info.name);
|
||||
if (db_info.origin)
|
||||
@@ -520,6 +624,26 @@ database_info_list_t *database_info_list_new(
|
||||
free(db_info.md5);
|
||||
if (db_info.sha1)
|
||||
free(db_info.sha1);
|
||||
|
||||
db_info.name = NULL;
|
||||
db_info.rom_name = NULL;
|
||||
db_info.serial = NULL;
|
||||
db_info.genre = NULL;
|
||||
db_info.description = NULL;
|
||||
db_info.publisher = NULL;
|
||||
db_info.developer = NULL;
|
||||
db_info.origin = NULL;
|
||||
db_info.franchise = NULL;
|
||||
db_info.edge_magazine_review = NULL;
|
||||
db_info.cero_rating = NULL;
|
||||
db_info.pegi_rating = NULL;
|
||||
db_info.enhancement_hw = NULL;
|
||||
db_info.elspa_rating = NULL;
|
||||
db_info.esrb_rating = NULL;
|
||||
db_info.bbfc_rating = NULL;
|
||||
db_info.sha1 = NULL;
|
||||
db_info.md5 = NULL;
|
||||
|
||||
database_info_list_free(database_info_list);
|
||||
free(database_info);
|
||||
free(database_info_list);
|
||||
@@ -570,13 +694,40 @@ void database_info_list_free(database_info_list_t *database_info_list)
|
||||
free(info->serial);
|
||||
if (info->genre)
|
||||
free(info->genre);
|
||||
if (info->category)
|
||||
free(info->category);
|
||||
if (info->language)
|
||||
free(info->language);
|
||||
if (info->region)
|
||||
free(info->region);
|
||||
if (info->score)
|
||||
free(info->score);
|
||||
if (info->media)
|
||||
free(info->media);
|
||||
if (info->controls)
|
||||
free(info->controls);
|
||||
if (info->artstyle)
|
||||
free(info->artstyle);
|
||||
if (info->gameplay)
|
||||
free(info->gameplay);
|
||||
if (info->narrative)
|
||||
free(info->narrative);
|
||||
if (info->pacing)
|
||||
free(info->pacing);
|
||||
if (info->perspective)
|
||||
free(info->perspective);
|
||||
if (info->setting)
|
||||
free(info->setting);
|
||||
if (info->visual)
|
||||
free(info->visual);
|
||||
if (info->vehicular)
|
||||
free(info->vehicular);
|
||||
if (info->description)
|
||||
free(info->description);
|
||||
if (info->publisher)
|
||||
free(info->publisher);
|
||||
if (info->developer)
|
||||
string_list_free(info->developer);
|
||||
info->developer = NULL;
|
||||
if (info->origin)
|
||||
free(info->origin);
|
||||
if (info->franchise)
|
||||
@@ -600,6 +751,25 @@ void database_info_list_free(database_info_list_t *database_info_list)
|
||||
free(info->sha1);
|
||||
if (info->md5)
|
||||
free(info->md5);
|
||||
|
||||
info->name = NULL;
|
||||
info->rom_name = NULL;
|
||||
info->serial = NULL;
|
||||
info->genre = NULL;
|
||||
info->description = NULL;
|
||||
info->publisher = NULL;
|
||||
info->developer = NULL;
|
||||
info->origin = NULL;
|
||||
info->franchise = NULL;
|
||||
info->edge_magazine_review = NULL;
|
||||
info->cero_rating = NULL;
|
||||
info->pegi_rating = NULL;
|
||||
info->enhancement_hw = NULL;
|
||||
info->elspa_rating = NULL;
|
||||
info->esrb_rating = NULL;
|
||||
info->bbfc_rating = NULL;
|
||||
info->sha1 = NULL;
|
||||
info->md5 = NULL;
|
||||
}
|
||||
|
||||
free(database_info_list->list);
|
||||
|
||||
@@ -85,6 +85,23 @@ typedef struct
|
||||
char *serial;
|
||||
char *description;
|
||||
char *genre;
|
||||
int achievements;
|
||||
char *category;
|
||||
char *language;
|
||||
char *region;
|
||||
int console_exclusive;
|
||||
int platform_exclusive;
|
||||
char *score;
|
||||
char *media;
|
||||
char *controls;
|
||||
char *artstyle;
|
||||
char *gameplay;
|
||||
char *narrative;
|
||||
char *pacing;
|
||||
char *perspective;
|
||||
char *setting;
|
||||
char *visual;
|
||||
char *vehicular;
|
||||
char *publisher;
|
||||
struct string_list *developer;
|
||||
char *origin;
|
||||
|
||||
Vendored
+2
-3
@@ -212,9 +212,8 @@ WRes File_Seek(CSzFile *p, int64_t *pos, ESzSeek origin)
|
||||
WRes File_GetLength(CSzFile *p, uint64_t *length)
|
||||
{
|
||||
#ifdef USE_WINDOWS_FILE
|
||||
|
||||
DWORD sizeHigh;
|
||||
DWORD sizeLow = GetFileSize(p->handle, &sizeHigh);
|
||||
DWORD sizeHigh = 0;
|
||||
DWORD sizeLow = GetFileSize(p->handle, &sizeHigh);
|
||||
if (sizeLow == 0xFFFFFFFF)
|
||||
{
|
||||
DWORD res = GetLastError();
|
||||
|
||||
+7
@@ -88,6 +88,10 @@ typedef struct rc_api_leaderboard_definition_t {
|
||||
const char* description;
|
||||
/* The definition of the leaderboard to be passed to rc_runtime_activate_lboard */
|
||||
const char* definition;
|
||||
/* Non-zero if lower values are better for this leaderboard */
|
||||
int lower_is_better;
|
||||
/* Non-zero if the leaderboard should not be displayed in a list of leaderboards */
|
||||
int hidden;
|
||||
}
|
||||
rc_api_leaderboard_definition_t;
|
||||
|
||||
@@ -210,6 +214,9 @@ typedef struct rc_api_award_achievement_response_t {
|
||||
unsigned awarded_achievement_id;
|
||||
/* The updated player score */
|
||||
unsigned new_player_score;
|
||||
/* The number of achievements the user has not yet unlocked for this game
|
||||
* (in hardcore/non-hardcore per hardcore flag in request) */
|
||||
unsigned achievements_remaining;
|
||||
|
||||
/* Common server-provided response information */
|
||||
rc_api_response_t response;
|
||||
|
||||
Vendored
+2
@@ -35,6 +35,8 @@ typedef struct rc_api_login_response_t {
|
||||
unsigned score;
|
||||
/* The number of unread messages waiting for the player on the web site */
|
||||
unsigned num_unread_messages;
|
||||
/* The preferred name to display for the player */
|
||||
const char* display_name;
|
||||
|
||||
/* Common server-provided response information */
|
||||
rc_api_response_t response;
|
||||
|
||||
Vendored
+5
@@ -76,6 +76,11 @@ enum {
|
||||
RC_CONSOLE_SHARPX1 = 64,
|
||||
RC_CONSOLE_TIC80 = 65,
|
||||
RC_CONSOLE_THOMSONTO8 = 66,
|
||||
RC_CONSOLE_PC6000 = 67,
|
||||
RC_CONSOLE_PICO = 68,
|
||||
RC_CONSOLE_MEGADUCK = 69,
|
||||
RC_CONSOLE_ZEEBO = 70,
|
||||
RC_CONSOLE_ARDUBOY = 71,
|
||||
|
||||
RC_CONSOLE_HUBS = 100,
|
||||
RC_CONSOLE_EVENTS = 101
|
||||
|
||||
Vendored
+5
-4
@@ -101,7 +101,7 @@ extern "C" {
|
||||
*/
|
||||
typedef void* (*rc_hash_cdreader_open_track_handler)(const char* path, uint32_t track);
|
||||
|
||||
/* attempts to read the specified number of bytes from the file starting at the read pointer.
|
||||
/* attempts to read the specified number of bytes from the file starting at the specified absolute sector.
|
||||
* returns the number of bytes actually read.
|
||||
*/
|
||||
typedef size_t (*rc_hash_cdreader_read_sector_handler)(void* track_handle, uint32_t sector, void* buffer, size_t requested_bytes);
|
||||
@@ -109,17 +109,18 @@ extern "C" {
|
||||
/* closes the track handle */
|
||||
typedef void (*rc_hash_cdreader_close_track_handler)(void* track_handle);
|
||||
|
||||
/* convert absolute sector to track sector */
|
||||
typedef uint32_t(*rc_hash_cdreader_absolute_sector_to_track_sector)(void* track_handle, uint32_t sector);
|
||||
/* gets the absolute sector index for the first sector of a track */
|
||||
typedef uint32_t(*rc_hash_cdreader_first_track_sector_handler)(void* track_handle);
|
||||
|
||||
struct rc_hash_cdreader
|
||||
{
|
||||
rc_hash_cdreader_open_track_handler open_track;
|
||||
rc_hash_cdreader_read_sector_handler read_sector;
|
||||
rc_hash_cdreader_close_track_handler close_track;
|
||||
rc_hash_cdreader_absolute_sector_to_track_sector absolute_sector_to_track_sector;
|
||||
rc_hash_cdreader_first_track_sector_handler first_track_sector;
|
||||
};
|
||||
|
||||
void rc_hash_get_default_cdreader(struct rc_hash_cdreader* cdreader);
|
||||
void rc_hash_init_default_cdreader(void);
|
||||
void rc_hash_init_custom_cdreader(struct rc_hash_cdreader* reader);
|
||||
|
||||
|
||||
Vendored
+2
@@ -58,6 +58,7 @@ typedef struct rc_runtime_lboard_t {
|
||||
void* buffer;
|
||||
rc_memref_t* invalid_memref;
|
||||
unsigned char md5[16];
|
||||
int serialized_size;
|
||||
char owns_memrefs;
|
||||
}
|
||||
rc_runtime_lboard_t;
|
||||
@@ -66,6 +67,7 @@ typedef struct rc_runtime_richpresence_t {
|
||||
rc_richpresence_t* richpresence;
|
||||
void* buffer;
|
||||
struct rc_runtime_richpresence_t* previous;
|
||||
unsigned char md5[16];
|
||||
char owns_memrefs;
|
||||
}
|
||||
rc_runtime_richpresence_t;
|
||||
|
||||
+14
-3
@@ -54,6 +54,8 @@ enum {
|
||||
RC_MEMSIZE_16_BITS_BE,
|
||||
RC_MEMSIZE_24_BITS_BE,
|
||||
RC_MEMSIZE_32_BITS_BE,
|
||||
RC_MEMSIZE_FLOAT,
|
||||
RC_MEMSIZE_MBF32,
|
||||
RC_MEMSIZE_VARIABLE
|
||||
};
|
||||
|
||||
@@ -67,6 +69,8 @@ typedef struct rc_memref_value_t {
|
||||
char size;
|
||||
/* True if the value changed this frame. */
|
||||
char changed;
|
||||
/* The value type of the value (for variables) */
|
||||
char type;
|
||||
/* True if the reference will be used in indirection.
|
||||
* NOTE: This is actually a property of the rc_memref_t, but we put it here to save space */
|
||||
char is_indirect;
|
||||
@@ -123,7 +127,7 @@ typedef struct rc_operand_t {
|
||||
}
|
||||
rc_operand_t;
|
||||
|
||||
int rc_operand_is_memref(rc_operand_t* operand);
|
||||
int rc_operand_is_memref(const rc_operand_t* operand);
|
||||
|
||||
/*****************************************************************************\
|
||||
| Conditions |
|
||||
@@ -280,7 +284,7 @@ struct rc_value_t {
|
||||
/* The list of conditions to evaluate. */
|
||||
rc_condset_t* conditions;
|
||||
|
||||
/* The memory references required by the value. */
|
||||
/* The memory references required by the variable. */
|
||||
rc_memref_t* memrefs;
|
||||
|
||||
/* The name of the variable. */
|
||||
@@ -337,7 +341,13 @@ enum {
|
||||
RC_FORMAT_SCORE,
|
||||
RC_FORMAT_VALUE,
|
||||
RC_FORMAT_MINUTES,
|
||||
RC_FORMAT_SECONDS_AS_MINUTES
|
||||
RC_FORMAT_SECONDS_AS_MINUTES,
|
||||
RC_FORMAT_FLOAT1,
|
||||
RC_FORMAT_FLOAT2,
|
||||
RC_FORMAT_FLOAT3,
|
||||
RC_FORMAT_FLOAT4,
|
||||
RC_FORMAT_FLOAT5,
|
||||
RC_FORMAT_FLOAT6
|
||||
};
|
||||
|
||||
int rc_parse_format(const char* format_str);
|
||||
@@ -398,6 +408,7 @@ rc_richpresence_t* rc_parse_richpresence(void* buffer, const char* script, lua_S
|
||||
int rc_evaluate_richpresence(rc_richpresence_t* richpresence, char* buffer, unsigned buffersize, rc_peek_t peek, void* peek_ud, lua_State* L);
|
||||
void rc_update_richpresence(rc_richpresence_t* richpresence, rc_peek_t peek, void* peek_ud, lua_State* L);
|
||||
int rc_get_richpresence_display_string(rc_richpresence_t* richpresence, char* buffer, unsigned buffersize, rc_peek_t peek, void* peek_ud, lua_State* L);
|
||||
void rc_reset_richpresence(rc_richpresence_t* self);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
+9
-10
@@ -4,8 +4,6 @@
|
||||
|
||||
#include "../rcheevos/rc_compat.h"
|
||||
|
||||
#include "../rhash/md5.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -520,6 +518,13 @@ int rc_json_get_string(const char** out, rc_api_buffer_t* buffer, const rc_json_
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*src == 't') {
|
||||
/* tab */
|
||||
++src;
|
||||
*dst++ = '\t';
|
||||
continue;
|
||||
}
|
||||
|
||||
/* just an escaped character, fallthrough to normal copy */
|
||||
}
|
||||
|
||||
@@ -631,7 +636,7 @@ int rc_json_get_unum(unsigned* out, const rc_json_field_t* field, const char* fi
|
||||
return 1;
|
||||
}
|
||||
|
||||
void rc_json_get_optional_unum(unsigned* out, const rc_json_field_t* field, const char* field_name, int default_value) {
|
||||
void rc_json_get_optional_unum(unsigned* out, const rc_json_field_t* field, const char* field_name, unsigned default_value) {
|
||||
if (!rc_json_get_unum(out, field, field_name))
|
||||
*out = default_value;
|
||||
}
|
||||
@@ -813,13 +818,7 @@ void rc_api_destroy_request(rc_api_request_t* request) {
|
||||
rc_buf_destroy(&request->buffer);
|
||||
}
|
||||
|
||||
void rc_api_generate_checksum(char checksum[33], const char* data) {
|
||||
md5_state_t md5;
|
||||
md5_byte_t digest[16];
|
||||
|
||||
md5_init(&md5);
|
||||
md5_append(&md5, (unsigned char*)data, (int)strlen(data));
|
||||
md5_finish(&md5, digest);
|
||||
void rc_api_format_md5(char checksum[33], const unsigned char digest[16]) {
|
||||
snprintf(checksum, 33, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
||||
digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7],
|
||||
digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15]
|
||||
|
||||
+2
-2
@@ -46,7 +46,7 @@ int rc_json_get_bool(int* out, const rc_json_field_t* field, const char* field_n
|
||||
int rc_json_get_datetime(time_t* out, const rc_json_field_t* field, const char* field_name);
|
||||
void rc_json_get_optional_string(const char** out, rc_api_response_t* response, const rc_json_field_t* field, const char* field_name, const char* default_value);
|
||||
void rc_json_get_optional_num(int* out, const rc_json_field_t* field, const char* field_name, int default_value);
|
||||
void rc_json_get_optional_unum(unsigned* out, const rc_json_field_t* field, const char* field_name, int default_value);
|
||||
void rc_json_get_optional_unum(unsigned* out, const rc_json_field_t* field, const char* field_name, unsigned default_value);
|
||||
void rc_json_get_optional_bool(int* out, const rc_json_field_t* field, const char* field_name, int default_value);
|
||||
int rc_json_get_required_string(const char** out, rc_api_response_t* response, const rc_json_field_t* field, const char* field_name);
|
||||
int rc_json_get_required_num(int* out, rc_api_response_t* response, const rc_json_field_t* field, const char* field_name);
|
||||
@@ -72,7 +72,7 @@ void rc_url_builder_append_str_param(rc_api_url_builder_t* builder, const char*
|
||||
|
||||
void rc_api_url_build_dorequest_url(rc_api_request_t* request);
|
||||
int rc_api_url_build_dorequest(rc_api_url_builder_t* builder, const char* api, const char* username, const char* api_token);
|
||||
void rc_api_generate_checksum(char checksum[33], const char* data);
|
||||
void rc_api_format_md5(char checksum[33], const unsigned char digest[16]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
+35
-14
@@ -4,6 +4,7 @@
|
||||
#include "rc_runtime.h"
|
||||
#include "rc_runtime_types.h"
|
||||
#include "../rcheevos/rc_compat.h"
|
||||
#include "../rhash/md5.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
@@ -119,14 +120,16 @@ int rc_api_process_fetch_game_data_response(rc_api_fetch_game_data_response_t* r
|
||||
{"Title"},
|
||||
{"Description"},
|
||||
{"Mem"},
|
||||
{"Format"}
|
||||
{"Format"},
|
||||
{"LowerIsBetter"},
|
||||
{"Hidden"}
|
||||
};
|
||||
|
||||
memset(response, 0, sizeof(*response));
|
||||
rc_buf_init(&response->response.buffer);
|
||||
|
||||
result = rc_json_parse_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
if (result != RC_OK)
|
||||
if (result != RC_OK || !response->response.succeeded)
|
||||
return result;
|
||||
|
||||
if (!rc_json_get_required_object(patchdata_fields, sizeof(patchdata_fields) / sizeof(patchdata_fields[0]), &response->response, &fields[2], "PatchData"))
|
||||
@@ -237,6 +240,8 @@ int rc_api_process_fetch_game_data_response(rc_api_fetch_game_data_response_t* r
|
||||
return RC_MISSING_VALUE;
|
||||
if (!rc_json_get_required_string(&leaderboard->definition, &response->response, &leaderboard_fields[3], "Mem"))
|
||||
return RC_MISSING_VALUE;
|
||||
rc_json_get_optional_bool(&leaderboard->lower_is_better, &leaderboard_fields[5], "LowerIsBetter", 0);
|
||||
rc_json_get_optional_bool(&leaderboard->hidden, &leaderboard_fields[6], "Hidden", 0);
|
||||
|
||||
if (!leaderboard_fields[4].value_end)
|
||||
return RC_MISSING_VALUE;
|
||||
@@ -304,8 +309,9 @@ void rc_api_destroy_ping_response(rc_api_ping_response_t* response) {
|
||||
|
||||
int rc_api_init_award_achievement_request(rc_api_request_t* request, const rc_api_award_achievement_request_t* api_params) {
|
||||
rc_api_url_builder_t builder;
|
||||
char signature[128];
|
||||
char checksum[33];
|
||||
char buffer[33];
|
||||
md5_state_t md5;
|
||||
md5_byte_t digest[16];
|
||||
|
||||
rc_api_url_build_dorequest_url(request);
|
||||
|
||||
@@ -320,9 +326,15 @@ int rc_api_init_award_achievement_request(rc_api_request_t* request, const rc_ap
|
||||
rc_url_builder_append_str_param(&builder, "m", api_params->game_hash);
|
||||
|
||||
/* Evaluate the signature. */
|
||||
snprintf(signature, sizeof(signature), "%u%s%u", api_params->achievement_id, api_params->username, api_params->hardcore ? 1 : 0);
|
||||
rc_api_generate_checksum(checksum, signature);
|
||||
rc_url_builder_append_str_param(&builder, "v", checksum);
|
||||
md5_init(&md5);
|
||||
snprintf(buffer, sizeof(buffer), "%u", api_params->achievement_id);
|
||||
md5_append(&md5, (md5_byte_t*)buffer, (int)strlen(buffer));
|
||||
md5_append(&md5, (md5_byte_t*)api_params->username, (int)strlen(api_params->username));
|
||||
snprintf(buffer, sizeof(buffer), "%d", api_params->hardcore ? 1 : 0);
|
||||
md5_append(&md5, (md5_byte_t*)buffer, (int)strlen(buffer));
|
||||
md5_finish(&md5, digest);
|
||||
rc_api_format_md5(buffer, digest);
|
||||
rc_url_builder_append_str_param(&builder, "v", buffer);
|
||||
|
||||
request->post_data = rc_url_builder_finalize(&builder);
|
||||
}
|
||||
@@ -336,7 +348,8 @@ int rc_api_process_award_achievement_response(rc_api_award_achievement_response_
|
||||
{"Success"},
|
||||
{"Error"},
|
||||
{"Score"},
|
||||
{"AchievementID"}
|
||||
{"AchievementID"},
|
||||
{"AchievementsRemaining"}
|
||||
};
|
||||
|
||||
memset(response, 0, sizeof(*response));
|
||||
@@ -361,6 +374,7 @@ int rc_api_process_award_achievement_response(rc_api_award_achievement_response_
|
||||
|
||||
rc_json_get_optional_unum(&response->new_player_score, &fields[2], "Score", 0);
|
||||
rc_json_get_optional_unum(&response->awarded_achievement_id, &fields[3], "AchievementID", 0);
|
||||
rc_json_get_optional_unum(&response->achievements_remaining, &fields[4], "AchievementsRemaining", (unsigned)-1);
|
||||
|
||||
return RC_OK;
|
||||
}
|
||||
@@ -373,8 +387,9 @@ void rc_api_destroy_award_achievement_response(rc_api_award_achievement_response
|
||||
|
||||
int rc_api_init_submit_lboard_entry_request(rc_api_request_t* request, const rc_api_submit_lboard_entry_request_t* api_params) {
|
||||
rc_api_url_builder_t builder;
|
||||
char signature[128];
|
||||
char checksum[33];
|
||||
char buffer[33];
|
||||
md5_state_t md5;
|
||||
md5_byte_t digest[16];
|
||||
|
||||
rc_api_url_build_dorequest_url(request);
|
||||
|
||||
@@ -390,9 +405,15 @@ int rc_api_init_submit_lboard_entry_request(rc_api_request_t* request, const rc_
|
||||
rc_url_builder_append_str_param(&builder, "m", api_params->game_hash);
|
||||
|
||||
/* Evaluate the signature. */
|
||||
snprintf(signature, sizeof(signature), "%u%s%d", api_params->leaderboard_id, api_params->username, api_params->score);
|
||||
rc_api_generate_checksum(checksum, signature);
|
||||
rc_url_builder_append_str_param(&builder, "v", checksum);
|
||||
md5_init(&md5);
|
||||
snprintf(buffer, sizeof(buffer), "%u", api_params->leaderboard_id);
|
||||
md5_append(&md5, (md5_byte_t*)buffer, (int)strlen(buffer));
|
||||
md5_append(&md5, (md5_byte_t*)api_params->username, (int)strlen(api_params->username));
|
||||
snprintf(buffer, sizeof(buffer), "%d", api_params->score);
|
||||
md5_append(&md5, (md5_byte_t*)buffer, (int)strlen(buffer));
|
||||
md5_finish(&md5, digest);
|
||||
rc_api_format_md5(buffer, digest);
|
||||
rc_url_builder_append_str_param(&builder, "v", buffer);
|
||||
|
||||
request->post_data = rc_url_builder_finalize(&builder);
|
||||
}
|
||||
@@ -456,7 +477,7 @@ int rc_api_process_submit_lboard_entry_response(rc_api_submit_lboard_entry_respo
|
||||
rc_buf_init(&response->response.buffer);
|
||||
|
||||
result = rc_json_parse_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
if (result != RC_OK)
|
||||
if (result != RC_OK || !response->response.succeeded)
|
||||
return result;
|
||||
|
||||
if (!rc_json_get_required_object(response_fields, sizeof(response_fields) / sizeof(response_fields[0]), &response->response, &fields[2], "Response"))
|
||||
|
||||
Vendored
+5
-2
@@ -37,7 +37,8 @@ int rc_api_process_login_response(rc_api_login_response_t* response, const char*
|
||||
{"User"},
|
||||
{"Token"},
|
||||
{"Score"},
|
||||
{"Messages"}
|
||||
{"Messages"},
|
||||
{"DisplayName"}
|
||||
};
|
||||
|
||||
memset(response, 0, sizeof(*response));
|
||||
@@ -55,6 +56,8 @@ int rc_api_process_login_response(rc_api_login_response_t* response, const char*
|
||||
rc_json_get_optional_unum(&response->score, &fields[4], "Score", 0);
|
||||
rc_json_get_optional_unum(&response->num_unread_messages, &fields[5], "Messages", 0);
|
||||
|
||||
rc_json_get_optional_string(&response->display_name, &response->response, &fields[6], "DisplayName", response->username);
|
||||
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
@@ -138,7 +141,7 @@ int rc_api_process_fetch_user_unlocks_response(rc_api_fetch_user_unlocks_respons
|
||||
rc_buf_init(&response->response.buffer);
|
||||
|
||||
result = rc_json_parse_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
if (result != RC_OK)
|
||||
if (result != RC_OK || !response->response.succeeded)
|
||||
return result;
|
||||
|
||||
result = rc_json_get_required_unum_array(&response->achievement_ids, &response->num_achievement_ids, &response->response, &fields[2], "UserUnlocks");
|
||||
|
||||
+34
-54
@@ -70,6 +70,7 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse
|
||||
self = RC_ALLOC(rc_condition_t, parse);
|
||||
self->current_hits = 0;
|
||||
self->is_true = 0;
|
||||
self->pause = 0;
|
||||
|
||||
if (*aux != 0 && aux[1] == ':') {
|
||||
switch (*aux) {
|
||||
@@ -106,11 +107,6 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (self->operand1.type == RC_OPERAND_FP) {
|
||||
parse->offset = can_modify ? RC_INVALID_FP_OPERAND : RC_INVALID_COMPARISON;
|
||||
return 0;
|
||||
}
|
||||
|
||||
result = rc_parse_operator(&aux);
|
||||
if (result < 0) {
|
||||
parse->offset = result;
|
||||
@@ -175,11 +171,6 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse
|
||||
self->operand2.value.num = 0;
|
||||
}
|
||||
|
||||
if (!can_modify && self->operand2.type == RC_OPERAND_FP) {
|
||||
parse->offset = RC_INVALID_COMPARISON;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (*aux == '(') {
|
||||
char* end;
|
||||
self->required_hits = (unsigned)strtoul(++aux, &end, 10);
|
||||
@@ -222,63 +213,52 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse
|
||||
return self;
|
||||
}
|
||||
|
||||
int rc_test_condition(rc_condition_t* self, rc_eval_state_t* eval_state) {
|
||||
unsigned value1 = rc_evaluate_operand(&self->operand1, eval_state) + eval_state->add_value;
|
||||
unsigned value2 = rc_evaluate_operand(&self->operand2, eval_state);
|
||||
int rc_condition_is_combining(const rc_condition_t* self) {
|
||||
switch (self->type) {
|
||||
case RC_CONDITION_STANDARD:
|
||||
case RC_CONDITION_PAUSE_IF:
|
||||
case RC_CONDITION_RESET_IF:
|
||||
case RC_CONDITION_MEASURED_IF:
|
||||
case RC_CONDITION_TRIGGER:
|
||||
case RC_CONDITION_MEASURED:
|
||||
return 0;
|
||||
|
||||
switch (self->oper) {
|
||||
case RC_OPERATOR_EQ: return value1 == value2;
|
||||
case RC_OPERATOR_NE: return value1 != value2;
|
||||
case RC_OPERATOR_LT: return value1 < value2;
|
||||
case RC_OPERATOR_LE: return value1 <= value2;
|
||||
case RC_OPERATOR_GT: return value1 > value2;
|
||||
case RC_OPERATOR_GE: return value1 >= value2;
|
||||
case RC_OPERATOR_NONE: return 1;
|
||||
default: return 1;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int rc_evaluate_condition_value(rc_condition_t* self, rc_eval_state_t* eval_state) {
|
||||
unsigned value = rc_evaluate_operand(&self->operand1, eval_state);
|
||||
int rc_test_condition(rc_condition_t* self, rc_eval_state_t* eval_state) {
|
||||
rc_typed_value_t value1, value2;
|
||||
|
||||
rc_evaluate_operand(&value1, &self->operand1, eval_state);
|
||||
if (eval_state->add_value.type != RC_VALUE_TYPE_NONE)
|
||||
rc_typed_value_add(&value1, &eval_state->add_value);
|
||||
|
||||
rc_evaluate_operand(&value2, &self->operand2, eval_state);
|
||||
|
||||
return rc_typed_value_compare(&value1, &value2, self->oper);
|
||||
}
|
||||
|
||||
void rc_evaluate_condition_value(rc_typed_value_t* value, rc_condition_t* self, rc_eval_state_t* eval_state) {
|
||||
rc_typed_value_t amount;
|
||||
|
||||
rc_evaluate_operand(value, &self->operand1, eval_state);
|
||||
rc_evaluate_operand(&amount, &self->operand2, eval_state);
|
||||
|
||||
switch (self->oper) {
|
||||
case RC_OPERATOR_MULT:
|
||||
if (self->operand2.type == RC_OPERAND_FP) {
|
||||
value = (int)((double)value * self->operand2.value.dbl);
|
||||
}
|
||||
else {
|
||||
/* the c standard for unsigned multiplication is well defined as non-overflowing truncation
|
||||
* to the type's size. this allows negative multiplication through twos-complements. i.e.
|
||||
* 1 * -1 (0xFFFFFFFF) = 0xFFFFFFFF = -1
|
||||
* 3 * -2 (0xFFFFFFFE) = 0x2FFFFFFFA & 0xFFFFFFFF = 0xFFFFFFFA = -6
|
||||
* 10 * -5 (0xFFFFFFFB) = 0x9FFFFFFCE & 0xFFFFFFFF = 0xFFFFFFCE = -50
|
||||
*/
|
||||
value *= rc_evaluate_operand(&self->operand2, eval_state);
|
||||
}
|
||||
rc_typed_value_multiply(value, &amount);
|
||||
break;
|
||||
|
||||
case RC_OPERATOR_DIV:
|
||||
if (self->operand2.type == RC_OPERAND_FP)
|
||||
{
|
||||
if (self->operand2.value.dbl == 0.0)
|
||||
value = 0;
|
||||
else
|
||||
value = (int)((double)value / self->operand2.value.dbl);
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned value2 = rc_evaluate_operand(&self->operand2, eval_state);
|
||||
if (value2 == 0)
|
||||
value = 0;
|
||||
else
|
||||
value /= value2;
|
||||
}
|
||||
rc_typed_value_divide(value, &amount);
|
||||
break;
|
||||
|
||||
case RC_OPERATOR_AND:
|
||||
value &= rc_evaluate_operand(&self->operand2, eval_state);
|
||||
rc_typed_value_convert(value, RC_VALUE_TYPE_UNSIGNED);
|
||||
rc_typed_value_convert(&amount, RC_VALUE_TYPE_UNSIGNED);
|
||||
value->value.u32 &= amount.value.u32;
|
||||
break;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
Vendored
+80
-47
@@ -1,36 +1,32 @@
|
||||
#include "rc_internal.h"
|
||||
|
||||
static void rc_update_condition_pause(rc_condition_t* condition, int* in_pause) {
|
||||
if (condition->next != 0) {
|
||||
rc_update_condition_pause(condition->next, in_pause);
|
||||
}
|
||||
#include <string.h> /* memcpy */
|
||||
|
||||
switch (condition->type) {
|
||||
case RC_CONDITION_PAUSE_IF:
|
||||
*in_pause = condition->pause = 1;
|
||||
break;
|
||||
static void rc_update_condition_pause(rc_condition_t* condition) {
|
||||
rc_condition_t* subclause = condition;
|
||||
|
||||
case RC_CONDITION_ADD_SOURCE:
|
||||
case RC_CONDITION_SUB_SOURCE:
|
||||
case RC_CONDITION_ADD_HITS:
|
||||
case RC_CONDITION_SUB_HITS:
|
||||
case RC_CONDITION_AND_NEXT:
|
||||
case RC_CONDITION_OR_NEXT:
|
||||
case RC_CONDITION_ADD_ADDRESS:
|
||||
case RC_CONDITION_RESET_NEXT_IF:
|
||||
condition->pause = (char)*in_pause;
|
||||
break;
|
||||
while (condition) {
|
||||
if (condition->type == RC_CONDITION_PAUSE_IF) {
|
||||
while (subclause != condition) {
|
||||
subclause->pause = 1;
|
||||
subclause = subclause->next;
|
||||
}
|
||||
condition->pause = 1;
|
||||
}
|
||||
else {
|
||||
condition->pause = 0;
|
||||
}
|
||||
|
||||
default:
|
||||
*in_pause = condition->pause = 0;
|
||||
break;
|
||||
if (!rc_condition_is_combining(condition))
|
||||
subclause = condition->next;
|
||||
|
||||
condition = condition->next;
|
||||
}
|
||||
}
|
||||
|
||||
rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse, int is_value) {
|
||||
rc_condset_t* self;
|
||||
rc_condition_t** next;
|
||||
int in_pause;
|
||||
int in_add_address;
|
||||
unsigned measured_target = 0;
|
||||
|
||||
@@ -85,8 +81,20 @@ rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse, in
|
||||
}
|
||||
else if (is_value) {
|
||||
measured_target = (unsigned)-1;
|
||||
if ((*next)->oper != RC_OPERATOR_NONE)
|
||||
(*next)->required_hits = measured_target;
|
||||
switch ((*next)->oper)
|
||||
{
|
||||
case RC_OPERATOR_AND:
|
||||
case RC_OPERATOR_DIV:
|
||||
case RC_OPERATOR_MULT:
|
||||
case RC_OPERATOR_NONE:
|
||||
/* measuring value. leave required_hits at 0 */
|
||||
break;
|
||||
|
||||
default:
|
||||
/* comparison operator, measuring hits. set required_hits to MAX_INT */
|
||||
(*next)->required_hits = measured_target;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if ((*next)->required_hits != 0) {
|
||||
measured_target = (*next)->required_hits;
|
||||
@@ -94,6 +102,9 @@ rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse, in
|
||||
else if ((*next)->operand2.type == RC_OPERAND_CONST) {
|
||||
measured_target = (*next)->operand2.value.num;
|
||||
}
|
||||
else if ((*next)->operand2.type == RC_OPERAND_FP) {
|
||||
measured_target = (unsigned)(*next)->operand2.value.dbl;
|
||||
}
|
||||
else {
|
||||
parse->offset = RC_INVALID_MEASURED_TARGET;
|
||||
return 0;
|
||||
@@ -132,10 +143,8 @@ rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse, in
|
||||
|
||||
*next = 0;
|
||||
|
||||
if (parse->buffer != 0) {
|
||||
in_pause = 0;
|
||||
rc_update_condition_pause(self->conditions, &in_pause);
|
||||
}
|
||||
if (parse->buffer != 0)
|
||||
rc_update_condition_pause(self->conditions);
|
||||
|
||||
return self;
|
||||
}
|
||||
@@ -146,7 +155,10 @@ static void rc_condset_update_indirect_memrefs(rc_condition_t* condition, int pr
|
||||
continue;
|
||||
|
||||
if (condition->type == RC_CONDITION_ADD_ADDRESS) {
|
||||
eval_state->add_address = rc_evaluate_condition_value(condition, eval_state);
|
||||
rc_typed_value_t value;
|
||||
rc_evaluate_condition_value(&value, condition, eval_state);
|
||||
rc_typed_value_convert(&value, RC_VALUE_TYPE_UNSIGNED);
|
||||
eval_state->add_address = value.value.u32;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -163,20 +175,25 @@ static void rc_condset_update_indirect_memrefs(rc_condition_t* condition, int pr
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int rc_test_condset_internal(rc_condset_t* self, int processing_pause, rc_eval_state_t* eval_state) {
|
||||
rc_condition_t* condition;
|
||||
int set_valid, cond_valid, and_next, or_next, reset_next;
|
||||
unsigned measured_value = 0;
|
||||
unsigned total_hits = 0;
|
||||
int can_measure = 1, measured_from_hits = 0;
|
||||
rc_typed_value_t value;
|
||||
int set_valid, cond_valid, and_next, or_next, reset_next, measured_from_hits, can_measure;
|
||||
rc_typed_value_t measured_value;
|
||||
unsigned total_hits;
|
||||
|
||||
measured_value.type = RC_VALUE_TYPE_NONE;
|
||||
measured_from_hits = 0;
|
||||
can_measure = 1;
|
||||
total_hits = 0;
|
||||
|
||||
eval_state->primed = 1;
|
||||
set_valid = 1;
|
||||
and_next = 1;
|
||||
or_next = 0;
|
||||
reset_next = 0;
|
||||
eval_state->add_value = eval_state->add_hits = eval_state->add_address = 0;
|
||||
eval_state->add_value.type = RC_VALUE_TYPE_NONE;
|
||||
eval_state->add_hits = eval_state->add_address = 0;
|
||||
|
||||
for (condition = self->conditions; condition != 0; condition = condition->next) {
|
||||
if (condition->pause != processing_pause)
|
||||
@@ -185,23 +202,30 @@ static int rc_test_condset_internal(rc_condset_t* self, int processing_pause, rc
|
||||
/* STEP 1: process modifier conditions */
|
||||
switch (condition->type) {
|
||||
case RC_CONDITION_ADD_SOURCE:
|
||||
eval_state->add_value += rc_evaluate_condition_value(condition, eval_state);
|
||||
rc_evaluate_condition_value(&value, condition, eval_state);
|
||||
rc_typed_value_add(&eval_state->add_value, &value);
|
||||
eval_state->add_address = 0;
|
||||
continue;
|
||||
|
||||
case RC_CONDITION_SUB_SOURCE:
|
||||
eval_state->add_value -= rc_evaluate_condition_value(condition, eval_state);
|
||||
rc_evaluate_condition_value(&value, condition, eval_state);
|
||||
rc_typed_value_convert(&value, RC_VALUE_TYPE_SIGNED);
|
||||
value.value.i32 = -value.value.i32;
|
||||
rc_typed_value_add(&eval_state->add_value, &value);
|
||||
eval_state->add_address = 0;
|
||||
continue;
|
||||
|
||||
case RC_CONDITION_ADD_ADDRESS:
|
||||
eval_state->add_address = rc_evaluate_condition_value(condition, eval_state);
|
||||
rc_evaluate_condition_value(&value, condition, eval_state);
|
||||
rc_typed_value_convert(&value, RC_VALUE_TYPE_UNSIGNED);
|
||||
eval_state->add_address = value.value.u32;
|
||||
continue;
|
||||
|
||||
case RC_CONDITION_MEASURED:
|
||||
if (condition->required_hits == 0) {
|
||||
if (condition->required_hits == 0 && can_measure) {
|
||||
/* Measured condition without a hit target measures the value of the left operand */
|
||||
measured_value = rc_evaluate_condition_value(condition, eval_state) + eval_state->add_value;
|
||||
rc_evaluate_condition_value(&measured_value, condition, eval_state);
|
||||
rc_typed_value_add(&measured_value, &eval_state->add_value);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -211,7 +235,7 @@ static int rc_test_condset_internal(rc_condset_t* self, int processing_pause, rc
|
||||
|
||||
/* STEP 2: evaluate the current condition */
|
||||
condition->is_true = (char)rc_test_condition(condition, eval_state);
|
||||
eval_state->add_value = 0;
|
||||
eval_state->add_value.type = RC_VALUE_TYPE_NONE;
|
||||
eval_state->add_address = 0;
|
||||
|
||||
/* apply logic flags and reset them for the next condition */
|
||||
@@ -345,13 +369,19 @@ static int rc_test_condset_internal(rc_condset_t* self, int processing_pause, rc
|
||||
if (condition->required_hits != 0) {
|
||||
/* if there's a hit target, capture the current hits for recording Measured value later */
|
||||
measured_from_hits = 1;
|
||||
measured_value = total_hits;
|
||||
if (can_measure) {
|
||||
measured_value.value.u32 = total_hits;
|
||||
measured_value.type = RC_VALUE_TYPE_UNSIGNED;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case RC_CONDITION_MEASURED_IF:
|
||||
if (!cond_valid)
|
||||
if (!cond_valid) {
|
||||
measured_value.value.u32 = 0;
|
||||
measured_value.type = RC_VALUE_TYPE_UNSIGNED;
|
||||
can_measure = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case RC_CONDITION_TRIGGER:
|
||||
@@ -368,10 +398,13 @@ static int rc_test_condset_internal(rc_condset_t* self, int processing_pause, rc
|
||||
set_valid &= cond_valid;
|
||||
}
|
||||
|
||||
/* if not suppressed, update the measured value */
|
||||
if (measured_value > eval_state->measured_value && can_measure) {
|
||||
eval_state->measured_value = measured_value;
|
||||
eval_state->measured_from_hits = (char)measured_from_hits;
|
||||
if (measured_value.type != RC_VALUE_TYPE_NONE) {
|
||||
/* if no previous Measured value was captured, or the new one is greater, keep the new one */
|
||||
if (eval_state->measured_value.type == RC_VALUE_TYPE_NONE ||
|
||||
rc_typed_value_compare(&measured_value, &eval_state->measured_value, RC_OPERATOR_GT)) {
|
||||
memcpy(&eval_state->measured_value, &measured_value, sizeof(measured_value));
|
||||
eval_state->measured_from_hits = (char)measured_from_hits;
|
||||
}
|
||||
}
|
||||
|
||||
return set_valid;
|
||||
|
||||
+72
@@ -21,6 +21,9 @@ const char* rc_console_name(int console_id)
|
||||
case RC_CONSOLE_ARCADE:
|
||||
return "Arcade";
|
||||
|
||||
case RC_CONSOLE_ARDUBOY:
|
||||
return "Arduboy";
|
||||
|
||||
case RC_CONSOLE_ATARI_2600:
|
||||
return "Atari 2600";
|
||||
|
||||
@@ -96,6 +99,9 @@ const char* rc_console_name(int console_id)
|
||||
case RC_CONSOLE_MEGA_DRIVE:
|
||||
return "Sega Genesis";
|
||||
|
||||
case RC_CONSOLE_MEGADUCK:
|
||||
return "Mega Duck";
|
||||
|
||||
case RC_CONSOLE_MS_DOS:
|
||||
return "MS-DOS";
|
||||
|
||||
@@ -126,6 +132,9 @@ const char* rc_console_name(int console_id)
|
||||
case RC_CONSOLE_ORIC:
|
||||
return "Oric";
|
||||
|
||||
case RC_CONSOLE_PC6000:
|
||||
return "PC-6000";
|
||||
|
||||
case RC_CONSOLE_PC8800:
|
||||
return "PC-8000/8800";
|
||||
|
||||
@@ -155,6 +164,9 @@ const char* rc_console_name(int console_id)
|
||||
|
||||
case RC_CONSOLE_SEGA_CD:
|
||||
return "Sega CD";
|
||||
|
||||
case RC_CONSOLE_PICO:
|
||||
return "Sega Pico";
|
||||
|
||||
case RC_CONSOLE_SATURN:
|
||||
return "Sega Saturn";
|
||||
@@ -204,6 +216,9 @@ const char* rc_console_name(int console_id)
|
||||
case RC_CONSOLE_XBOX:
|
||||
return "XBOX";
|
||||
|
||||
case RC_CONSOLE_ZEEBO:
|
||||
return "Zeebo";
|
||||
|
||||
case RC_CONSOLE_ZX81:
|
||||
return "ZX-81";
|
||||
|
||||
@@ -227,6 +242,20 @@ static const rc_memory_region_t _rc_memory_regions_3do[] = {
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_3do = { _rc_memory_regions_3do, 1 };
|
||||
|
||||
/* ===== Amstrad CPC ===== */
|
||||
/* http://www.cpcalive.com/docs/amstrad_cpc_6128_memory_map.html */
|
||||
/* https://www.cpcwiki.eu/index.php/File:AWMG_page151.jpg */
|
||||
/* The original CPC only had 64KB of memory, but the newer model has 128KB (expandable to 576KB) */
|
||||
/* https://www.grimware.org/doku.php/documentations/devices/gatearraydo=export_xhtml#mmr */
|
||||
static const rc_memory_region_t _rc_memory_regions_amstrad_pc[] = {
|
||||
{ 0x000000U, 0x00003FU, 0x000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Firmware" },
|
||||
{ 0x000040U, 0x00B0FFU, 0x000040U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" },
|
||||
{ 0x00B100U, 0x00BFFFU, 0x00B100U, RC_MEMORY_TYPE_SYSTEM_RAM, "Stack and Firmware Data" },
|
||||
{ 0x00C000U, 0x00FFFFU, 0x00C000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Screen Memory" },
|
||||
{ 0x010000U, 0x08FFFFU, 0x010000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Extended RAM" },
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_amstrad_pc = { _rc_memory_regions_amstrad_pc, 5 };
|
||||
|
||||
/* ===== Apple II ===== */
|
||||
static const rc_memory_region_t _rc_memory_regions_appleii[] = {
|
||||
{ 0x000000U, 0x00FFFFU, 0x000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Main RAM" },
|
||||
@@ -234,6 +263,21 @@ static const rc_memory_region_t _rc_memory_regions_appleii[] = {
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_appleii = { _rc_memory_regions_appleii, 2 };
|
||||
|
||||
/* ===== Arduboy ===== */
|
||||
/* https://scienceprog.com/avr-microcontroller-memory-map/ (Atmega32) */
|
||||
static const rc_memory_region_t _rc_memory_regions_arduboy[] = {
|
||||
{ 0x000000U, 0x0000FFU, 0x00000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Registers" },
|
||||
/* https://www.dailydot.com/debug/arduboy-kickstarter/ 2.5KB of RAM */
|
||||
/* https://github.com/buserror/simavr/blob/1d227277b3d0039f9faef9ea62880ca3051b14f8/simavr/cores/avr/iom32u4.h#L1444-L1445 */
|
||||
{ 0x000100U, 0x000AFFU, 0x00000100U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" },
|
||||
/* 1KB of EEPROM https://github.com/libretro/arduous/blob/93e1a6289b42ef48de1fcfb96443981725955ad0/src/arduous/arduous.cpp#L453-L455
|
||||
* https://github.com/buserror/simavr/blob/1d227277b3d0039f9faef9ea62880ca3051b14f8/simavr/cores/avr/iom32u4.h#L1450 */
|
||||
/* EEPROM has it's own addressing scheme starting at $0000. I've chosen to virtualize the address
|
||||
* at $80000000 to avoid a conflict */
|
||||
{ 0x000B00U, 0x000EFFU, 0x80000000U, RC_MEMORY_TYPE_SAVE_RAM, "EEPROM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_arduboy = { _rc_memory_regions_arduboy, 3 };
|
||||
|
||||
/* ===== Atari 2600 ===== */
|
||||
static const rc_memory_region_t _rc_memory_regions_atari2600[] = {
|
||||
{ 0x000000U, 0x00007FU, 0x000080U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
|
||||
@@ -280,6 +324,13 @@ static const rc_memory_region_t _rc_memory_regions_colecovision[] = {
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_colecovision = { _rc_memory_regions_colecovision, 1 };
|
||||
|
||||
/* ===== Dreamcast ===== */
|
||||
/* http://archiv.sega-dc.de/munkeechuff/hardware/Memory.html */
|
||||
static const rc_memory_region_t _rc_memory_regions_dreamcast[] = {
|
||||
{ 0x00000000U, 0x00FFFFFFU, 0x0C000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_dreamcast = { _rc_memory_regions_dreamcast, 1 };
|
||||
|
||||
/* ===== GameBoy / GameBoy Color ===== */
|
||||
static const rc_memory_region_t _rc_memory_regions_gameboy[] = {
|
||||
{ 0x000000U, 0x0000FFU, 0x000000U, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "Interrupt vector" },
|
||||
@@ -506,6 +557,14 @@ static const rc_memory_region_t _rc_memory_regions_playstation2[] = {
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_playstation2 = { _rc_memory_regions_playstation2, 2 };
|
||||
|
||||
/* ===== PlayStation Portable ===== */
|
||||
/* https://github.com/uofw/upspd/wiki/Memory-map */
|
||||
static const rc_memory_region_t _rc_memory_regions_psp[] = {
|
||||
{ 0x00000000U, 0x007FFFFFU, 0x08000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Kernel RAM" },
|
||||
{ 0x00800000U, 0x01FFFFFFU, 0x08800000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" },
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_psp = { _rc_memory_regions_psp, 2 };
|
||||
|
||||
/* ===== Pokemon Mini ===== */
|
||||
/* https://www.pokemon-mini.net/documentation/memory-map/ */
|
||||
static const rc_memory_region_t _rc_memory_regions_pokemini[] = {
|
||||
@@ -638,9 +697,15 @@ const rc_memory_regions_t* rc_console_memory_regions(int console_id)
|
||||
case RC_CONSOLE_3DO:
|
||||
return &rc_memory_regions_3do;
|
||||
|
||||
case RC_CONSOLE_AMSTRAD_PC:
|
||||
return &rc_memory_regions_amstrad_pc;
|
||||
|
||||
case RC_CONSOLE_APPLE_II:
|
||||
return &rc_memory_regions_appleii;
|
||||
|
||||
case RC_CONSOLE_ARDUBOY:
|
||||
return &rc_memory_regions_arduboy;
|
||||
|
||||
case RC_CONSOLE_ATARI_2600:
|
||||
return &rc_memory_regions_atari2600;
|
||||
|
||||
@@ -656,6 +721,10 @@ const rc_memory_regions_t* rc_console_memory_regions(int console_id)
|
||||
case RC_CONSOLE_COLECOVISION:
|
||||
return &rc_memory_regions_colecovision;
|
||||
|
||||
case RC_CONSOLE_DREAMCAST:
|
||||
return &rc_memory_regions_dreamcast;
|
||||
|
||||
case RC_CONSOLE_MEGADUCK:
|
||||
case RC_CONSOLE_GAMEBOY:
|
||||
return &rc_memory_regions_gameboy;
|
||||
|
||||
@@ -716,6 +785,9 @@ const rc_memory_regions_t* rc_console_memory_regions(int console_id)
|
||||
case RC_CONSOLE_PLAYSTATION_2:
|
||||
return &rc_memory_regions_playstation2;
|
||||
|
||||
case RC_CONSOLE_PSP:
|
||||
return &rc_memory_regions_psp;
|
||||
|
||||
case RC_CONSOLE_POKEMON_MINI:
|
||||
return &rc_memory_regions_pokemini;
|
||||
|
||||
|
||||
Vendored
+65
-14
@@ -11,6 +11,9 @@ int rc_parse_format(const char* format_str) {
|
||||
if (!strcmp(format_str, "RAMES")) {
|
||||
return RC_FORMAT_FRAMES;
|
||||
}
|
||||
if (!strncmp(format_str, "LOAT", 4) && format_str[4] >= '1' && format_str[4] <= '6' && format_str[5] == '\0') {
|
||||
return RC_FORMAT_FLOAT1 + (format_str[4] - '1');
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -18,7 +21,7 @@ int rc_parse_format(const char* format_str) {
|
||||
if (!strcmp(format_str, "IME")) {
|
||||
return RC_FORMAT_FRAMES;
|
||||
}
|
||||
else if (!strcmp(format_str, "IMESECS")) {
|
||||
if (!strcmp(format_str, "IMESECS")) {
|
||||
return RC_FORMAT_SECONDS;
|
||||
}
|
||||
|
||||
@@ -116,40 +119,88 @@ static int rc_format_value_centiseconds(char* buffer, int size, unsigned centise
|
||||
return chars;
|
||||
}
|
||||
|
||||
int rc_format_value(char* buffer, int size, int value, int format) {
|
||||
int rc_format_typed_value(char* buffer, int size, const rc_typed_value_t* value, int format) {
|
||||
int chars;
|
||||
rc_typed_value_t converted_value;
|
||||
|
||||
memcpy(&converted_value, value, sizeof(converted_value));
|
||||
|
||||
switch (format) {
|
||||
case RC_FORMAT_FRAMES:
|
||||
/* 60 frames per second = 100 centiseconds / 60 frames; multiply frames by 100 / 60 */
|
||||
chars = rc_format_value_centiseconds(buffer, size, value * 10 / 6);
|
||||
default:
|
||||
case RC_FORMAT_VALUE:
|
||||
rc_typed_value_convert(&converted_value, RC_VALUE_TYPE_SIGNED);
|
||||
chars = snprintf(buffer, size, "%d", converted_value.value.i32);
|
||||
break;
|
||||
|
||||
case RC_FORMAT_SECONDS:
|
||||
chars = rc_format_value_seconds(buffer, size, value);
|
||||
case RC_FORMAT_FRAMES:
|
||||
/* 60 frames per second = 100 centiseconds / 60 frames; multiply frames by 100 / 60 */
|
||||
rc_typed_value_convert(&converted_value, RC_VALUE_TYPE_UNSIGNED);
|
||||
chars = rc_format_value_centiseconds(buffer, size, converted_value.value.u32 * 10 / 6);
|
||||
break;
|
||||
|
||||
case RC_FORMAT_CENTISECS:
|
||||
chars = rc_format_value_centiseconds(buffer, size, value);
|
||||
rc_typed_value_convert(&converted_value, RC_VALUE_TYPE_UNSIGNED);
|
||||
chars = rc_format_value_centiseconds(buffer, size, converted_value.value.u32);
|
||||
break;
|
||||
|
||||
case RC_FORMAT_SECONDS:
|
||||
rc_typed_value_convert(&converted_value, RC_VALUE_TYPE_UNSIGNED);
|
||||
chars = rc_format_value_seconds(buffer, size, converted_value.value.u32);
|
||||
break;
|
||||
|
||||
case RC_FORMAT_SECONDS_AS_MINUTES:
|
||||
chars = rc_format_value_minutes(buffer, size, value / 60);
|
||||
rc_typed_value_convert(&converted_value, RC_VALUE_TYPE_UNSIGNED);
|
||||
chars = rc_format_value_minutes(buffer, size, converted_value.value.u32 / 60);
|
||||
break;
|
||||
|
||||
case RC_FORMAT_MINUTES:
|
||||
chars = rc_format_value_minutes(buffer, size, value);
|
||||
rc_typed_value_convert(&converted_value, RC_VALUE_TYPE_UNSIGNED);
|
||||
chars = rc_format_value_minutes(buffer, size, converted_value.value.u32);
|
||||
break;
|
||||
|
||||
case RC_FORMAT_SCORE:
|
||||
chars = snprintf(buffer, size, "%06d", value);
|
||||
rc_typed_value_convert(&converted_value, RC_VALUE_TYPE_SIGNED);
|
||||
chars = snprintf(buffer, size, "%06d", converted_value.value.i32);
|
||||
break;
|
||||
|
||||
default:
|
||||
case RC_FORMAT_VALUE:
|
||||
chars = snprintf(buffer, size, "%d", value);
|
||||
case RC_FORMAT_FLOAT1:
|
||||
rc_typed_value_convert(&converted_value, RC_VALUE_TYPE_FLOAT);
|
||||
chars = snprintf(buffer, size, "%.1f", converted_value.value.f32);
|
||||
break;
|
||||
|
||||
case RC_FORMAT_FLOAT2:
|
||||
rc_typed_value_convert(&converted_value, RC_VALUE_TYPE_FLOAT);
|
||||
chars = snprintf(buffer, size, "%.2f", converted_value.value.f32);
|
||||
break;
|
||||
|
||||
case RC_FORMAT_FLOAT3:
|
||||
rc_typed_value_convert(&converted_value, RC_VALUE_TYPE_FLOAT);
|
||||
chars = snprintf(buffer, size, "%.3f", converted_value.value.f32);
|
||||
break;
|
||||
|
||||
case RC_FORMAT_FLOAT4:
|
||||
rc_typed_value_convert(&converted_value, RC_VALUE_TYPE_FLOAT);
|
||||
chars = snprintf(buffer, size, "%.4f", converted_value.value.f32);
|
||||
break;
|
||||
|
||||
case RC_FORMAT_FLOAT5:
|
||||
rc_typed_value_convert(&converted_value, RC_VALUE_TYPE_FLOAT);
|
||||
chars = snprintf(buffer, size, "%.5f", converted_value.value.f32);
|
||||
break;
|
||||
|
||||
case RC_FORMAT_FLOAT6:
|
||||
rc_typed_value_convert(&converted_value, RC_VALUE_TYPE_FLOAT);
|
||||
chars = snprintf(buffer, size, "%.6f", converted_value.value.f32);
|
||||
break;
|
||||
}
|
||||
|
||||
return chars;
|
||||
}
|
||||
|
||||
int rc_format_value(char* buffer, int size, int value, int format) {
|
||||
rc_typed_value_t typed_value;
|
||||
|
||||
typed_value.value.i32 = value;
|
||||
typed_value.type = RC_VALUE_TYPE_SIGNED;
|
||||
return rc_format_typed_value(buffer, size, &typed_value, format);
|
||||
}
|
||||
Vendored
+38
-16
@@ -25,10 +25,12 @@ void rc_parse_lboard_internal(rc_lboard_t* self, const char* memaddr, rc_parse_s
|
||||
return;
|
||||
}
|
||||
|
||||
found |= RC_LBOARD_START;
|
||||
memaddr += 4;
|
||||
rc_parse_trigger_internal(&self->start, &memaddr, parse);
|
||||
self->start.memrefs = 0;
|
||||
if (*memaddr && *memaddr != ':') {
|
||||
found |= RC_LBOARD_START;
|
||||
rc_parse_trigger_internal(&self->start, &memaddr, parse);
|
||||
self->start.memrefs = 0;
|
||||
}
|
||||
}
|
||||
else if ((memaddr[0] == 'c' || memaddr[0] == 'C') &&
|
||||
(memaddr[1] == 'a' || memaddr[1] == 'A') &&
|
||||
@@ -38,10 +40,12 @@ void rc_parse_lboard_internal(rc_lboard_t* self, const char* memaddr, rc_parse_s
|
||||
return;
|
||||
}
|
||||
|
||||
found |= RC_LBOARD_CANCEL;
|
||||
memaddr += 4;
|
||||
rc_parse_trigger_internal(&self->cancel, &memaddr, parse);
|
||||
self->cancel.memrefs = 0;
|
||||
if (*memaddr && *memaddr != ':') {
|
||||
found |= RC_LBOARD_CANCEL;
|
||||
rc_parse_trigger_internal(&self->cancel, &memaddr, parse);
|
||||
self->cancel.memrefs = 0;
|
||||
}
|
||||
}
|
||||
else if ((memaddr[0] == 's' || memaddr[0] == 'S') &&
|
||||
(memaddr[1] == 'u' || memaddr[1] == 'U') &&
|
||||
@@ -51,10 +55,12 @@ void rc_parse_lboard_internal(rc_lboard_t* self, const char* memaddr, rc_parse_s
|
||||
return;
|
||||
}
|
||||
|
||||
found |= RC_LBOARD_SUBMIT;
|
||||
memaddr += 4;
|
||||
rc_parse_trigger_internal(&self->submit, &memaddr, parse);
|
||||
self->submit.memrefs = 0;
|
||||
if (*memaddr && *memaddr != ':') {
|
||||
found |= RC_LBOARD_SUBMIT;
|
||||
rc_parse_trigger_internal(&self->submit, &memaddr, parse);
|
||||
self->submit.memrefs = 0;
|
||||
}
|
||||
}
|
||||
else if ((memaddr[0] == 'v' || memaddr[0] == 'V') &&
|
||||
(memaddr[1] == 'a' || memaddr[1] == 'A') &&
|
||||
@@ -64,10 +70,12 @@ void rc_parse_lboard_internal(rc_lboard_t* self, const char* memaddr, rc_parse_s
|
||||
return;
|
||||
}
|
||||
|
||||
found |= RC_LBOARD_VALUE;
|
||||
memaddr += 4;
|
||||
rc_parse_value_internal(&self->value, &memaddr, parse);
|
||||
self->value.memrefs = 0;
|
||||
if (*memaddr && *memaddr != ':') {
|
||||
found |= RC_LBOARD_VALUE;
|
||||
rc_parse_value_internal(&self->value, &memaddr, parse);
|
||||
self->value.memrefs = 0;
|
||||
}
|
||||
}
|
||||
else if ((memaddr[0] == 'p' || memaddr[0] == 'P') &&
|
||||
(memaddr[1] == 'r' || memaddr[1] == 'R') &&
|
||||
@@ -77,12 +85,14 @@ void rc_parse_lboard_internal(rc_lboard_t* self, const char* memaddr, rc_parse_s
|
||||
return;
|
||||
}
|
||||
|
||||
found |= RC_LBOARD_PROGRESS;
|
||||
memaddr += 4;
|
||||
if (*memaddr && *memaddr != ':') {
|
||||
found |= RC_LBOARD_PROGRESS;
|
||||
|
||||
self->progress = RC_ALLOC(rc_value_t, parse);
|
||||
rc_parse_value_internal(self->progress, &memaddr, parse);
|
||||
self->progress->memrefs = 0;
|
||||
self->progress = RC_ALLOC(rc_value_t, parse);
|
||||
rc_parse_value_internal(self->progress, &memaddr, parse);
|
||||
self->progress->memrefs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* encountered an error parsing one of the parts */
|
||||
@@ -239,6 +249,18 @@ int rc_evaluate_lboard(rc_lboard_t* self, int* value, rc_peek_t peek, void* peek
|
||||
return self->state;
|
||||
}
|
||||
|
||||
int rc_lboard_state_active(int state) {
|
||||
switch (state)
|
||||
{
|
||||
case RC_LBOARD_STATE_DISABLED:
|
||||
case RC_LBOARD_STATE_INACTIVE:
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
void rc_reset_lboard(rc_lboard_t* self) {
|
||||
self->state = RC_LBOARD_STATE_WAITING;
|
||||
|
||||
|
||||
Vendored
+218
-86
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <stdlib.h> /* malloc/realloc */
|
||||
#include <string.h> /* memcpy */
|
||||
#include <math.h> /* INFINITY/NAN */
|
||||
|
||||
#define MEMREF_PLACEHOLDER_ADDRESS 0xFFFFFFFF
|
||||
|
||||
@@ -45,50 +46,62 @@ int rc_parse_memref(const char** memaddr, char* size, unsigned* address) {
|
||||
char* end;
|
||||
unsigned long value;
|
||||
|
||||
if (*aux++ != '0')
|
||||
return RC_INVALID_MEMORY_OPERAND;
|
||||
|
||||
if (*aux != 'x' && *aux != 'X')
|
||||
return RC_INVALID_MEMORY_OPERAND;
|
||||
aux++;
|
||||
|
||||
switch (*aux++) {
|
||||
/* ordered by estimated frequency in case compiler doesn't build a jump table */
|
||||
case 'h': case 'H': *size = RC_MEMSIZE_8_BITS; break;
|
||||
case ' ': *size = RC_MEMSIZE_16_BITS; break;
|
||||
case 'x': case 'X': *size = RC_MEMSIZE_32_BITS; break;
|
||||
|
||||
case 'm': case 'M': *size = RC_MEMSIZE_BIT_0; break;
|
||||
case 'n': case 'N': *size = RC_MEMSIZE_BIT_1; break;
|
||||
case 'o': case 'O': *size = RC_MEMSIZE_BIT_2; break;
|
||||
case 'p': case 'P': *size = RC_MEMSIZE_BIT_3; break;
|
||||
case 'q': case 'Q': *size = RC_MEMSIZE_BIT_4; break;
|
||||
case 'r': case 'R': *size = RC_MEMSIZE_BIT_5; break;
|
||||
case 's': case 'S': *size = RC_MEMSIZE_BIT_6; break;
|
||||
case 't': case 'T': *size = RC_MEMSIZE_BIT_7; break;
|
||||
case 'l': case 'L': *size = RC_MEMSIZE_LOW; break;
|
||||
case 'u': case 'U': *size = RC_MEMSIZE_HIGH; break;
|
||||
case 'k': case 'K': *size = RC_MEMSIZE_BITCOUNT; break;
|
||||
case 'w': case 'W': *size = RC_MEMSIZE_24_BITS; break;
|
||||
case 'g': case 'G': *size = RC_MEMSIZE_32_BITS_BE; break;
|
||||
case 'i': case 'I': *size = RC_MEMSIZE_16_BITS_BE; break;
|
||||
case 'j': case 'J': *size = RC_MEMSIZE_24_BITS_BE; break;
|
||||
|
||||
/* case 'v': case 'V': */
|
||||
/* case 'y': case 'Y': 64 bit? */
|
||||
/* case 'z': case 'Z': 128 bit? */
|
||||
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
|
||||
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
|
||||
/* legacy support - addresses without a size prefix are assumed to be 16-bit */
|
||||
aux--;
|
||||
*size = RC_MEMSIZE_16_BITS;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (aux[0] == '0') {
|
||||
if (aux[1] != 'x' && aux[1] != 'X')
|
||||
return RC_INVALID_MEMORY_OPERAND;
|
||||
|
||||
aux += 2;
|
||||
switch (*aux++) {
|
||||
/* ordered by estimated frequency in case compiler doesn't build a jump table */
|
||||
case 'h': case 'H': *size = RC_MEMSIZE_8_BITS; break;
|
||||
case ' ': *size = RC_MEMSIZE_16_BITS; break;
|
||||
case 'x': case 'X': *size = RC_MEMSIZE_32_BITS; break;
|
||||
|
||||
case 'm': case 'M': *size = RC_MEMSIZE_BIT_0; break;
|
||||
case 'n': case 'N': *size = RC_MEMSIZE_BIT_1; break;
|
||||
case 'o': case 'O': *size = RC_MEMSIZE_BIT_2; break;
|
||||
case 'p': case 'P': *size = RC_MEMSIZE_BIT_3; break;
|
||||
case 'q': case 'Q': *size = RC_MEMSIZE_BIT_4; break;
|
||||
case 'r': case 'R': *size = RC_MEMSIZE_BIT_5; break;
|
||||
case 's': case 'S': *size = RC_MEMSIZE_BIT_6; break;
|
||||
case 't': case 'T': *size = RC_MEMSIZE_BIT_7; break;
|
||||
case 'l': case 'L': *size = RC_MEMSIZE_LOW; break;
|
||||
case 'u': case 'U': *size = RC_MEMSIZE_HIGH; break;
|
||||
case 'k': case 'K': *size = RC_MEMSIZE_BITCOUNT; break;
|
||||
case 'w': case 'W': *size = RC_MEMSIZE_24_BITS; break;
|
||||
case 'g': case 'G': *size = RC_MEMSIZE_32_BITS_BE; break;
|
||||
case 'i': case 'I': *size = RC_MEMSIZE_16_BITS_BE; break;
|
||||
case 'j': case 'J': *size = RC_MEMSIZE_24_BITS_BE; break;
|
||||
|
||||
/* case 'v': case 'V': */
|
||||
/* case 'y': case 'Y': 64 bit? */
|
||||
/* case 'z': case 'Z': 128 bit? */
|
||||
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
|
||||
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
|
||||
/* legacy support - addresses without a size prefix are assumed to be 16-bit */
|
||||
aux--;
|
||||
*size = RC_MEMSIZE_16_BITS;
|
||||
break;
|
||||
|
||||
default:
|
||||
return RC_INVALID_MEMORY_OPERAND;
|
||||
}
|
||||
}
|
||||
else if (aux[0] == 'f' || aux[0] == 'F') {
|
||||
++aux;
|
||||
switch (*aux++) {
|
||||
case 'f': case 'F': *size = RC_MEMSIZE_FLOAT; break;
|
||||
case 'm': case 'M': *size = RC_MEMSIZE_MBF32; break;
|
||||
|
||||
default:
|
||||
return RC_INVALID_FP_OPERAND;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return RC_INVALID_MEMORY_OPERAND;
|
||||
}
|
||||
|
||||
value = strtoul(aux, &end, 16);
|
||||
@@ -104,94 +117,212 @@ int rc_parse_memref(const char** memaddr, char* size, unsigned* address) {
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
static float rc_build_float(unsigned mantissa_bits, int exponent, int sign) {
|
||||
/* 32-bit float has a 23-bit mantissa and 8-bit exponent */
|
||||
const unsigned mantissa = mantissa_bits | 0x800000;
|
||||
double dbl = ((double)mantissa) / ((double)0x800000);
|
||||
|
||||
if (exponent > 127) {
|
||||
/* exponent above 127 is a special number */
|
||||
if (mantissa_bits == 0) {
|
||||
/* infinity */
|
||||
#ifdef INFINITY /* INFINITY and NAN #defines require C99 */
|
||||
dbl = INFINITY;
|
||||
#else
|
||||
dbl = -log(0.0);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
/* NaN */
|
||||
#ifdef NAN
|
||||
dbl = NAN;
|
||||
#else
|
||||
dbl = -sqrt(-1);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else if (exponent > 0) {
|
||||
/* exponent from 1 to 127 is a number greater than 1 */
|
||||
while (exponent > 30) {
|
||||
dbl *= (double)(1 << 30);
|
||||
exponent -= 30;
|
||||
}
|
||||
dbl *= (double)((long long)1 << exponent);
|
||||
}
|
||||
else if (exponent < 0) {
|
||||
/* exponent from -1 to -127 is a number less than 1 */
|
||||
exponent = -exponent;
|
||||
while (exponent > 30) {
|
||||
dbl /= (double)(1 << 30);
|
||||
exponent -= 30;
|
||||
}
|
||||
dbl /= (double)((long long)1 << exponent);
|
||||
}
|
||||
else {
|
||||
/* exponent of 0 requires no adjustment */
|
||||
}
|
||||
|
||||
return (sign) ? (float)-dbl : (float)dbl;
|
||||
}
|
||||
|
||||
static void rc_transform_memref_float(rc_typed_value_t* value) {
|
||||
/* decodes an IEEE 754 float */
|
||||
const unsigned mantissa = (value->value.u32 & 0x7FFFFF);
|
||||
const int exponent = (int)((value->value.u32 >> 23) & 0xFF) - 127;
|
||||
const int sign = (value->value.u32 & 0x80000000);
|
||||
|
||||
if (mantissa == 0 && exponent == -127)
|
||||
value->value.f32 = (sign) ? -0.0f : 0.0f;
|
||||
else
|
||||
value->value.f32 = rc_build_float(mantissa, exponent, sign);
|
||||
|
||||
value->type = RC_VALUE_TYPE_FLOAT;
|
||||
}
|
||||
|
||||
static void rc_transform_memref_mbf32(rc_typed_value_t* value) {
|
||||
/* decodes a Microsoft Binary Format float */
|
||||
/* NOTE: 32-bit MBF is stored in memory as big endian (at least for Apple II) */
|
||||
const unsigned mantissa = ((value->value.u32 & 0xFF000000) >> 24) |
|
||||
((value->value.u32 & 0x00FF0000) >> 8) |
|
||||
((value->value.u32 & 0x00007F00) << 8);
|
||||
const int exponent = (int)(value->value.u32 & 0xFF) - 129;
|
||||
const int sign = (value->value.u32 & 0x00008000);
|
||||
|
||||
if (mantissa == 0 && exponent == -129)
|
||||
value->value.f32 = (sign) ? -0.0f : 0.0f;
|
||||
else
|
||||
value->value.f32 = rc_build_float(mantissa, exponent, sign);
|
||||
|
||||
value->type = RC_VALUE_TYPE_FLOAT;
|
||||
}
|
||||
|
||||
static const unsigned char rc_bits_set[16] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 };
|
||||
|
||||
unsigned rc_transform_memref_value(unsigned value, char size) {
|
||||
void rc_transform_memref_value(rc_typed_value_t* value, char size) {
|
||||
/* ASSERT: value->type == RC_VALUE_TYPE_UNSIGNED */
|
||||
switch (size)
|
||||
{
|
||||
case RC_MEMSIZE_8_BITS:
|
||||
value = (value & 0x000000ff);
|
||||
value->value.u32 = (value->value.u32 & 0x000000ff);
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_16_BITS:
|
||||
value = (value & 0x0000ffff);
|
||||
value->value.u32 = (value->value.u32 & 0x0000ffff);
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_24_BITS:
|
||||
value = (value & 0x00ffffff);
|
||||
value->value.u32 = (value->value.u32 & 0x00ffffff);
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_32_BITS:
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_0:
|
||||
value = (value >> 0) & 1;
|
||||
value->value.u32 = (value->value.u32 >> 0) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_1:
|
||||
value = (value >> 1) & 1;
|
||||
value->value.u32 = (value->value.u32 >> 1) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_2:
|
||||
value = (value >> 2) & 1;
|
||||
value->value.u32 = (value->value.u32 >> 2) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_3:
|
||||
value = (value >> 3) & 1;
|
||||
value->value.u32 = (value->value.u32 >> 3) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_4:
|
||||
value = (value >> 4) & 1;
|
||||
value->value.u32 = (value->value.u32 >> 4) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_5:
|
||||
value = (value >> 5) & 1;
|
||||
value->value.u32 = (value->value.u32 >> 5) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_6:
|
||||
value = (value >> 6) & 1;
|
||||
value->value.u32 = (value->value.u32 >> 6) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_7:
|
||||
value = (value >> 7) & 1;
|
||||
value->value.u32 = (value->value.u32 >> 7) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_LOW:
|
||||
value = value & 0x0f;
|
||||
value->value.u32 = value->value.u32 & 0x0f;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_HIGH:
|
||||
value = (value >> 4) & 0x0f;
|
||||
value->value.u32 = (value->value.u32 >> 4) & 0x0f;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BITCOUNT:
|
||||
value = rc_bits_set[(value & 0x0F)]
|
||||
+ rc_bits_set[((value >> 4) & 0x0F)];
|
||||
value->value.u32 = rc_bits_set[(value->value.u32 & 0x0F)]
|
||||
+ rc_bits_set[((value->value.u32 >> 4) & 0x0F)];
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_16_BITS_BE:
|
||||
value = ((value & 0xFF00) >> 8) |
|
||||
((value & 0x00FF) << 8);
|
||||
value->value.u32 = ((value->value.u32 & 0xFF00) >> 8) |
|
||||
((value->value.u32 & 0x00FF) << 8);
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_24_BITS_BE:
|
||||
value = ((value & 0xFF0000) >> 16) |
|
||||
(value & 0x00FF00) |
|
||||
((value & 0x0000FF) << 16);
|
||||
value->value.u32 = ((value->value.u32 & 0xFF0000) >> 16) |
|
||||
(value->value.u32 & 0x00FF00) |
|
||||
((value->value.u32 & 0x0000FF) << 16);
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_32_BITS_BE:
|
||||
value = ((value & 0xFF000000) >> 24) |
|
||||
((value & 0x00FF0000) >> 8) |
|
||||
((value & 0x0000FF00) << 8) |
|
||||
((value & 0x000000FF) << 24);
|
||||
value->value.u32 = ((value->value.u32 & 0xFF000000) >> 24) |
|
||||
((value->value.u32 & 0x00FF0000) >> 8) |
|
||||
((value->value.u32 & 0x0000FF00) << 8) |
|
||||
((value->value.u32 & 0x000000FF) << 24);
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_FLOAT:
|
||||
rc_transform_memref_float(value);
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_MBF32:
|
||||
rc_transform_memref_mbf32(value);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
static const unsigned rc_memref_masks[] = {
|
||||
0x000000ff, /* RC_MEMSIZE_8_BITS */
|
||||
0x0000ffff, /* RC_MEMSIZE_16_BITS */
|
||||
0x00ffffff, /* RC_MEMSIZE_24_BITS */
|
||||
0xffffffff, /* RC_MEMSIZE_32_BITS */
|
||||
0x0000000f, /* RC_MEMSIZE_LOW */
|
||||
0x000000f0, /* RC_MEMSIZE_HIGH */
|
||||
0x00000001, /* RC_MEMSIZE_BIT_0 */
|
||||
0x00000002, /* RC_MEMSIZE_BIT_1 */
|
||||
0x00000004, /* RC_MEMSIZE_BIT_2 */
|
||||
0x00000008, /* RC_MEMSIZE_BIT_3 */
|
||||
0x00000010, /* RC_MEMSIZE_BIT_4 */
|
||||
0x00000020, /* RC_MEMSIZE_BIT_5 */
|
||||
0x00000040, /* RC_MEMSIZE_BIT_6 */
|
||||
0x00000080, /* RC_MEMSIZE_BIT_7 */
|
||||
0x000000ff, /* RC_MEMSIZE_BITCOUNT */
|
||||
0x0000ffff, /* RC_MEMSIZE_16_BITS_BE */
|
||||
0x00ffffff, /* RC_MEMSIZE_24_BITS_BE */
|
||||
0xffffffff, /* RC_MEMSIZE_32_BITS_BE */
|
||||
0xffffffff, /* RC_MEMSIZE_FLOAT */
|
||||
0xffffffff, /* RC_MEMSIZE_MBF32 */
|
||||
0xffffffff /* RC_MEMSIZE_VARIABLE */
|
||||
};
|
||||
|
||||
unsigned rc_memref_mask(char size) {
|
||||
const size_t index = (size_t)size;
|
||||
if (index >= sizeof(rc_memref_masks) / sizeof(rc_memref_masks[0]))
|
||||
return 0xffffffff;
|
||||
|
||||
return rc_memref_masks[index];
|
||||
}
|
||||
|
||||
/* all sizes less than 8-bits (1 byte) are mapped to 8-bits. 24-bit is mapped to 32-bit
|
||||
@@ -216,6 +347,8 @@ static const char rc_memref_shared_sizes[] = {
|
||||
RC_MEMSIZE_16_BITS, /* RC_MEMSIZE_16_BITS_BE */
|
||||
RC_MEMSIZE_32_BITS, /* RC_MEMSIZE_24_BITS_BE */
|
||||
RC_MEMSIZE_32_BITS, /* RC_MEMSIZE_32_BITS_BE */
|
||||
RC_MEMSIZE_32_BITS, /* RC_MEMSIZE_FLOAT */
|
||||
RC_MEMSIZE_32_BITS, /* RC_MEMSIZE_MBF32 */
|
||||
RC_MEMSIZE_32_BITS /* RC_MEMSIZE_VARIABLE */
|
||||
};
|
||||
|
||||
@@ -228,35 +361,34 @@ char rc_memref_shared_size(char size) {
|
||||
}
|
||||
|
||||
static unsigned rc_peek_value(unsigned address, char size, rc_peek_t peek, void* ud) {
|
||||
unsigned value;
|
||||
char shared_size;
|
||||
|
||||
if (!peek)
|
||||
return 0;
|
||||
|
||||
shared_size = rc_memref_shared_size(size);
|
||||
switch (shared_size)
|
||||
switch (size)
|
||||
{
|
||||
case RC_MEMSIZE_8_BITS:
|
||||
value = peek(address, 1, ud);
|
||||
break;
|
||||
return peek(address, 1, ud);
|
||||
|
||||
case RC_MEMSIZE_16_BITS:
|
||||
value = peek(address, 2, ud);
|
||||
break;
|
||||
return peek(address, 2, ud);
|
||||
|
||||
case RC_MEMSIZE_32_BITS:
|
||||
value = peek(address, 4, ud);
|
||||
break;
|
||||
return peek(address, 4, ud);
|
||||
|
||||
default:
|
||||
return 0;
|
||||
{
|
||||
unsigned value;
|
||||
const size_t index = (size_t)size;
|
||||
if (index >= sizeof(rc_memref_shared_sizes) / sizeof(rc_memref_shared_sizes[0]))
|
||||
return 0;
|
||||
|
||||
/* fetch the larger value and mask off the bits associated to the specified size
|
||||
* for correct deduction of prior value. non-prior memrefs should already be using
|
||||
* shared size memrefs to minimize the total number of memory reads required. */
|
||||
value = rc_peek_value(address, rc_memref_shared_sizes[index], peek, ud);
|
||||
return value & rc_memref_masks[index];
|
||||
}
|
||||
}
|
||||
|
||||
if (shared_size != size)
|
||||
value = rc_transform_memref_value(value, size);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void rc_update_memref_value(rc_memref_value_t* memref, unsigned new_value) {
|
||||
|
||||
Vendored
+120
-74
@@ -103,6 +103,16 @@ static int rc_parse_operand_memory(rc_operand_t* self, const char** memaddr, rc_
|
||||
return ret;
|
||||
|
||||
size = rc_memref_shared_size(self->size);
|
||||
if (size != self->size && self->type == RC_OPERAND_PRIOR) {
|
||||
/* if the shared size differs from the requested size and it's a prior operation, we
|
||||
* have to check to make sure both sizes use the same mask, or the prior value may be
|
||||
* updated when bits outside the mask are modified, which would make it look like the
|
||||
* current value once the mask is applied. if the mask differs, create a new
|
||||
* non-shared record for tracking the prior data. */
|
||||
if (rc_memref_mask(size) != rc_memref_mask(self->size))
|
||||
size = self->size;
|
||||
}
|
||||
|
||||
self->value.memref = rc_alloc_memref(parse, address, size, (char)is_indirect);
|
||||
if (parse->offset < 0)
|
||||
return parse->offset;
|
||||
@@ -142,6 +152,14 @@ int rc_parse_operand(rc_operand_t* self, const char** memaddr, int is_indirect,
|
||||
break;
|
||||
|
||||
case 'f': case 'F': /* floating point constant */
|
||||
if (isalpha((unsigned char)aux[1])) {
|
||||
ret = rc_parse_operand_memory(self, &aux, parse, is_indirect);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
break;
|
||||
}
|
||||
allow_decimal = 1;
|
||||
/* fall through */
|
||||
case 'v': case 'V': /* signed integer constant */
|
||||
@@ -169,13 +187,16 @@ int rc_parse_operand(rc_operand_t* self, const char** memaddr, int is_indirect,
|
||||
return RC_INVALID_FP_OPERAND;
|
||||
|
||||
do {
|
||||
fraction *= 10;
|
||||
fraction += (*aux - '0');
|
||||
shift *= 10;
|
||||
/* only keep as many digits as will fit in a 32-bit value to prevent overflow.
|
||||
* float only has around 7 digits of precision anyway. */
|
||||
if (shift < 1000000000) {
|
||||
fraction *= 10;
|
||||
fraction += (*aux - '0');
|
||||
shift *= 10;
|
||||
}
|
||||
++aux;
|
||||
} while (*aux >= '0' && *aux <= '9');
|
||||
|
||||
/* if fractional part is 0, convert to an integer constant */
|
||||
if (fraction != 0) {
|
||||
/* non-zero fractional part, convert to double and merge in integer portion */
|
||||
const double dbl_fraction = ((double)fraction) / ((double)shift);
|
||||
@@ -183,28 +204,34 @@ int rc_parse_operand(rc_operand_t* self, const char** memaddr, int is_indirect,
|
||||
self->value.dbl = ((double)(-((long)value))) - dbl_fraction;
|
||||
else
|
||||
self->value.dbl = (double)value + dbl_fraction;
|
||||
|
||||
self->type = RC_OPERAND_FP;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
else {
|
||||
/* fractional part is 0, just convert the integer portion */
|
||||
if (negative)
|
||||
self->value.dbl = (double)(-((long)value));
|
||||
else
|
||||
self->value.dbl = (double)value;
|
||||
}
|
||||
|
||||
self->type = RC_OPERAND_FP;
|
||||
}
|
||||
else {
|
||||
/* not a floating point value, make sure something was read and advance the read pointer */
|
||||
if (end == aux)
|
||||
return allow_decimal ? RC_INVALID_FP_OPERAND : RC_INVALID_CONST_OPERAND;
|
||||
|
||||
aux = end;
|
||||
|
||||
if (value > 0x7fffffffU)
|
||||
value = 0x7fffffffU;
|
||||
|
||||
self->type = RC_OPERAND_CONST;
|
||||
|
||||
if (negative)
|
||||
self->value.num = (unsigned)(-((long)value));
|
||||
else
|
||||
self->value.num = (unsigned)value;
|
||||
}
|
||||
|
||||
if (value > 0x7fffffffU)
|
||||
value = 0x7fffffffU;
|
||||
|
||||
self->type = RC_OPERAND_CONST;
|
||||
|
||||
if (negative)
|
||||
self->value.num = (unsigned)(-((long)value));
|
||||
else
|
||||
self->value.num = (unsigned)value;
|
||||
|
||||
break;
|
||||
|
||||
case '0':
|
||||
@@ -269,7 +296,18 @@ static int rc_luapeek(lua_State* L) {
|
||||
|
||||
#endif /* RC_DISABLE_LUA */
|
||||
|
||||
int rc_operand_is_memref(rc_operand_t* self) {
|
||||
int rc_operand_is_float_memref(const rc_operand_t* self) {
|
||||
switch (self->size) {
|
||||
case RC_MEMSIZE_FLOAT:
|
||||
case RC_MEMSIZE_MBF32:
|
||||
return 1;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int rc_operand_is_memref(const rc_operand_t* self) {
|
||||
switch (self->type) {
|
||||
case RC_OPERAND_CONST:
|
||||
case RC_OPERAND_FP:
|
||||
@@ -281,60 +319,7 @@ int rc_operand_is_memref(rc_operand_t* self) {
|
||||
}
|
||||
}
|
||||
|
||||
unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
|
||||
#ifndef RC_DISABLE_LUA
|
||||
rc_luapeek_t luapeek;
|
||||
#endif /* RC_DISABLE_LUA */
|
||||
|
||||
unsigned value;
|
||||
|
||||
/* step 1: read memory */
|
||||
switch (self->type) {
|
||||
case RC_OPERAND_CONST:
|
||||
return self->value.num;
|
||||
|
||||
case RC_OPERAND_FP:
|
||||
/* This is handled by rc_evaluate_condition_value. */
|
||||
return 0;
|
||||
|
||||
case RC_OPERAND_LUA:
|
||||
value = 0;
|
||||
|
||||
#ifndef RC_DISABLE_LUA
|
||||
if (eval_state->L != 0) {
|
||||
lua_rawgeti(eval_state->L, LUA_REGISTRYINDEX, self->value.luafunc);
|
||||
lua_pushcfunction(eval_state->L, rc_luapeek);
|
||||
|
||||
luapeek.peek = eval_state->peek;
|
||||
luapeek.ud = eval_state->peek_userdata;
|
||||
|
||||
lua_pushlightuserdata(eval_state->L, &luapeek);
|
||||
|
||||
if (lua_pcall(eval_state->L, 2, 1, 0) == LUA_OK) {
|
||||
if (lua_isboolean(eval_state->L, -1)) {
|
||||
value = lua_toboolean(eval_state->L, -1);
|
||||
}
|
||||
else {
|
||||
value = (unsigned)lua_tonumber(eval_state->L, -1);
|
||||
}
|
||||
}
|
||||
|
||||
lua_pop(eval_state->L, 1);
|
||||
}
|
||||
|
||||
#endif /* RC_DISABLE_LUA */
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
value = rc_get_memref_value(self->value.memref, self->type, eval_state);
|
||||
break;
|
||||
}
|
||||
|
||||
/* step 2: mask off appropriate bits */
|
||||
value = rc_transform_memref_value(value, self->size);
|
||||
|
||||
/* step 3: apply logic */
|
||||
unsigned rc_transform_operand_value(unsigned value, const rc_operand_t* self) {
|
||||
switch (self->type)
|
||||
{
|
||||
case RC_OPERAND_BCD:
|
||||
@@ -421,3 +406,64 @@ unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void rc_evaluate_operand(rc_typed_value_t* result, rc_operand_t* self, rc_eval_state_t* eval_state) {
|
||||
#ifndef RC_DISABLE_LUA
|
||||
rc_luapeek_t luapeek;
|
||||
#endif /* RC_DISABLE_LUA */
|
||||
|
||||
/* step 1: read memory */
|
||||
switch (self->type) {
|
||||
case RC_OPERAND_CONST:
|
||||
result->type = RC_VALUE_TYPE_UNSIGNED;
|
||||
result->value.u32 = self->value.num;
|
||||
return;
|
||||
|
||||
case RC_OPERAND_FP:
|
||||
result->type = RC_VALUE_TYPE_FLOAT;
|
||||
result->value.f32 = (float)self->value.dbl;
|
||||
return;
|
||||
|
||||
case RC_OPERAND_LUA:
|
||||
result->type = RC_VALUE_TYPE_UNSIGNED;
|
||||
result->value.u32 = 0;
|
||||
|
||||
#ifndef RC_DISABLE_LUA
|
||||
if (eval_state->L != 0) {
|
||||
lua_rawgeti(eval_state->L, LUA_REGISTRYINDEX, self->value.luafunc);
|
||||
lua_pushcfunction(eval_state->L, rc_luapeek);
|
||||
|
||||
luapeek.peek = eval_state->peek;
|
||||
luapeek.ud = eval_state->peek_userdata;
|
||||
|
||||
lua_pushlightuserdata(eval_state->L, &luapeek);
|
||||
|
||||
if (lua_pcall(eval_state->L, 2, 1, 0) == LUA_OK) {
|
||||
if (lua_isboolean(eval_state->L, -1)) {
|
||||
result->value.u32 = (unsigned)lua_toboolean(eval_state->L, -1);
|
||||
}
|
||||
else {
|
||||
result->value.u32 = (unsigned)lua_tonumber(eval_state->L, -1);
|
||||
}
|
||||
}
|
||||
|
||||
lua_pop(eval_state->L, 1);
|
||||
}
|
||||
|
||||
#endif /* RC_DISABLE_LUA */
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
result->type = RC_VALUE_TYPE_UNSIGNED;
|
||||
result->value.u32 = rc_get_memref_value(self->value.memref, self->type, eval_state);
|
||||
break;
|
||||
}
|
||||
|
||||
/* step 2: convert read memory to desired format */
|
||||
rc_transform_memref_value(result, self->size);
|
||||
|
||||
/* step 3: apply logic (BCD/invert) */
|
||||
if (result->type == RC_VALUE_TYPE_UNSIGNED)
|
||||
result->value.u32 = rc_transform_operand_value(result->value.u32, self);
|
||||
}
|
||||
+37
-5
@@ -65,8 +65,26 @@ typedef struct {
|
||||
}
|
||||
rc_scratch_t;
|
||||
|
||||
enum {
|
||||
RC_VALUE_TYPE_NONE,
|
||||
RC_VALUE_TYPE_UNSIGNED,
|
||||
RC_VALUE_TYPE_SIGNED,
|
||||
RC_VALUE_TYPE_FLOAT
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
unsigned add_value; /* AddSource/SubSource */
|
||||
union {
|
||||
unsigned u32;
|
||||
int i32;
|
||||
float f32;
|
||||
} value;
|
||||
|
||||
char type;
|
||||
}
|
||||
rc_typed_value_t;
|
||||
|
||||
typedef struct {
|
||||
rc_typed_value_t add_value;/* AddSource/SubSource */
|
||||
int add_hits; /* AddHits */
|
||||
unsigned add_address; /* AddAddress */
|
||||
|
||||
@@ -74,7 +92,7 @@ typedef struct {
|
||||
void* peek_userdata;
|
||||
lua_State* L;
|
||||
|
||||
unsigned measured_value; /* Measured */
|
||||
rc_typed_value_t measured_value; /* Measured */
|
||||
char was_reset; /* ResetIf triggered */
|
||||
char has_hits; /* one of more hit counts is non-zero */
|
||||
char primed; /* true if all non-Trigger conditions are true */
|
||||
@@ -118,7 +136,8 @@ void rc_update_memref_values(rc_memref_t* memref, rc_peek_t peek, void* ud);
|
||||
void rc_update_memref_value(rc_memref_value_t* memref, unsigned value);
|
||||
unsigned rc_get_memref_value(rc_memref_t* memref, int operand_type, rc_eval_state_t* eval_state);
|
||||
char rc_memref_shared_size(char size);
|
||||
unsigned rc_transform_memref_value(unsigned value, char size);
|
||||
unsigned rc_memref_mask(char size);
|
||||
void rc_transform_memref_value(rc_typed_value_t* value, char size);
|
||||
|
||||
void rc_parse_trigger_internal(rc_trigger_t* self, const char** memaddr, rc_parse_state_t* parse);
|
||||
int rc_trigger_state_active(int state);
|
||||
@@ -129,17 +148,30 @@ void rc_reset_condset(rc_condset_t* self);
|
||||
|
||||
rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse, int is_indirect);
|
||||
int rc_test_condition(rc_condition_t* self, rc_eval_state_t* eval_state);
|
||||
int rc_evaluate_condition_value(rc_condition_t* self, rc_eval_state_t* eval_state);
|
||||
void rc_evaluate_condition_value(rc_typed_value_t* value, rc_condition_t* self, rc_eval_state_t* eval_state);
|
||||
int rc_condition_is_combining(const rc_condition_t* self);
|
||||
|
||||
int rc_parse_operand(rc_operand_t* self, const char** memaddr, int is_indirect, rc_parse_state_t* parse);
|
||||
unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state);
|
||||
void rc_evaluate_operand(rc_typed_value_t* value, rc_operand_t* self, rc_eval_state_t* eval_state);
|
||||
int rc_operand_is_float_memref(const rc_operand_t* self);
|
||||
|
||||
void rc_parse_value_internal(rc_value_t* self, const char** memaddr, rc_parse_state_t* parse);
|
||||
int rc_evaluate_value_typed(rc_value_t* self, rc_typed_value_t* value, rc_peek_t peek, void* ud, lua_State* L);
|
||||
void rc_reset_value(rc_value_t* self);
|
||||
rc_value_t* rc_alloc_helper_variable(const char* memaddr, int memaddr_len, rc_parse_state_t* parse);
|
||||
void rc_update_variables(rc_value_t* variable, rc_peek_t peek, void* ud, lua_State* L);
|
||||
|
||||
void rc_typed_value_convert(rc_typed_value_t* value, char new_type);
|
||||
void rc_typed_value_add(rc_typed_value_t* value, const rc_typed_value_t* amount);
|
||||
void rc_typed_value_multiply(rc_typed_value_t* value, const rc_typed_value_t* amount);
|
||||
void rc_typed_value_divide(rc_typed_value_t* value, const rc_typed_value_t* amount);
|
||||
int rc_typed_value_compare(const rc_typed_value_t* value1, const rc_typed_value_t* value2, char oper);
|
||||
void rc_typed_value_from_memref_value(rc_typed_value_t* value, const rc_memref_value_t* memref);
|
||||
|
||||
int rc_format_typed_value(char* buffer, int size, const rc_typed_value_t* value, int format);
|
||||
|
||||
void rc_parse_lboard_internal(rc_lboard_t* self, const char* memaddr, rc_parse_state_t* parse);
|
||||
int rc_lboard_state_active(int state);
|
||||
|
||||
void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script, rc_parse_state_t* parse);
|
||||
|
||||
|
||||
+56
-9
@@ -31,11 +31,21 @@ static const rc_disallowed_setting_t _rc_disallowed_bsnes_settings[] = {
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const rc_disallowed_setting_t _rc_disallowed_cap32_settings[] = {
|
||||
{ "cap32_autorun", "disabled" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const rc_disallowed_setting_t _rc_disallowed_dolphin_settings[] = {
|
||||
{ "dolphin_cheats_enabled", "enabled" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const rc_disallowed_setting_t _rc_disallowed_duckstation_settings[] = {
|
||||
{ "duckstation_CDROM.LoadImagePatches", "true" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const rc_disallowed_setting_t _rc_disallowed_ecwolf_settings[] = {
|
||||
{ "ecwolf-invulnerability", "enabled" },
|
||||
{ NULL, NULL }
|
||||
@@ -44,6 +54,8 @@ static const rc_disallowed_setting_t _rc_disallowed_ecwolf_settings[] = {
|
||||
static const rc_disallowed_setting_t _rc_disallowed_fbneo_settings[] = {
|
||||
{ "fbneo-allow-patched-romsets", "enabled" },
|
||||
{ "fbneo-cheat-*", "!,Disabled,0 - Disabled" },
|
||||
{ "fbneo-dipswitch-*", "Universe BIOS*" },
|
||||
{ "fbneo-neogeo-mode", "UNIBIOS" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
@@ -95,6 +107,9 @@ static const rc_disallowed_setting_t _rc_disallowed_smsplus_settings[] = {
|
||||
};
|
||||
|
||||
static const rc_disallowed_setting_t _rc_disallowed_snes9x_settings[] = {
|
||||
{ "snes9x_gfx_clip", "disabled" },
|
||||
{ "snes9x_gfx_transp", "disabled" },
|
||||
{ "snes9x_layer_*", "disabled" },
|
||||
{ "snes9x_region", "pal" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
@@ -106,7 +121,9 @@ static const rc_disallowed_setting_t _rc_disallowed_virtual_jaguar_settings[] =
|
||||
|
||||
static const rc_disallowed_core_settings_t rc_disallowed_core_settings[] = {
|
||||
{ "bsnes-mercury", _rc_disallowed_bsnes_settings },
|
||||
{ "cap32", _rc_disallowed_cap32_settings },
|
||||
{ "dolphin-emu", _rc_disallowed_dolphin_settings },
|
||||
{ "DuckStation", _rc_disallowed_duckstation_settings },
|
||||
{ "ecwolf", _rc_disallowed_ecwolf_settings },
|
||||
{ "FCEUmm", _rc_disallowed_fceumm_settings },
|
||||
{ "FinalBurn Neo", _rc_disallowed_fbneo_settings },
|
||||
@@ -123,10 +140,11 @@ static const rc_disallowed_core_settings_t rc_disallowed_core_settings[] = {
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static int rc_libretro_string_equal_nocase(const char* test, const char* value) {
|
||||
while (*test) {
|
||||
if (tolower(*test++) != tolower(*value++))
|
||||
return 0;
|
||||
static int rc_libretro_string_equal_nocase_wildcard(const char* test, const char* value) {
|
||||
char c1, c2;
|
||||
while ((c1 = *test++)) {
|
||||
if (tolower(c1) != tolower(c2 = *value++))
|
||||
return (c2 == '*');
|
||||
}
|
||||
|
||||
return (*value == '\0');
|
||||
@@ -151,7 +169,7 @@ static int rc_libretro_match_value(const char* val, const char* match) {
|
||||
char buffer[128];
|
||||
memcpy(buffer, ptr, size);
|
||||
buffer[size] = '\0';
|
||||
if (rc_libretro_string_equal_nocase(buffer, val))
|
||||
if (rc_libretro_string_equal_nocase_wildcard(buffer, val))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -165,7 +183,7 @@ static int rc_libretro_match_value(const char* val, const char* match) {
|
||||
return !rc_libretro_match_value(val, &match[1]);
|
||||
|
||||
/* just a single value, attempt to match it */
|
||||
return rc_libretro_string_equal_nocase(val, match);
|
||||
return rc_libretro_string_equal_nocase_wildcard(val, match);
|
||||
}
|
||||
|
||||
int rc_libretro_is_setting_allowed(const rc_disallowed_setting_t* disallowed_settings, const char* setting, const char* value) {
|
||||
@@ -325,11 +343,24 @@ static const struct retro_memory_descriptor* rc_libretro_memory_get_descriptor(c
|
||||
/* otherwise, attempt to match the address by matching the select bits */
|
||||
/* address is in the block if (addr & select) == (start & select) */
|
||||
if (((desc->start ^ real_address) & desc->select) == 0) {
|
||||
/* calculate the offset within the descriptor, removing any disconnected bits */
|
||||
*offset = (real_address & ~desc->disconnect) - desc->start;
|
||||
/* get the relative offset of the address from the start of the memory block */
|
||||
unsigned reduced_address = real_address - (unsigned)desc->start;
|
||||
|
||||
/* remove any bits from the reduced_address that correspond to the bits in the disconnect
|
||||
* mask and collapse the remaining bits. this code was copied from the mmap_reduce function
|
||||
* in RetroArch. i'm not exactly sure how it works, but it does. */
|
||||
unsigned disconnect_mask = (unsigned)desc->disconnect;
|
||||
while (disconnect_mask) {
|
||||
const unsigned tmp = (disconnect_mask - 1) & ~disconnect_mask;
|
||||
reduced_address = (reduced_address & tmp) | ((reduced_address >> 1) & ~tmp);
|
||||
disconnect_mask = (disconnect_mask & (disconnect_mask - 1)) >> 1;
|
||||
}
|
||||
|
||||
/* calculate the offset within the descriptor */
|
||||
*offset = reduced_address;
|
||||
|
||||
/* sanity check - make sure the descriptor is large enough to hold the target address */
|
||||
if (*offset < desc->len)
|
||||
if (reduced_address < desc->len)
|
||||
return desc;
|
||||
}
|
||||
}
|
||||
@@ -352,6 +383,7 @@ static void rc_libretro_memory_init_from_memory_map(rc_libretro_memory_regions_t
|
||||
const rc_memory_region_t* console_region = &console_regions->region[i];
|
||||
size_t console_region_size = console_region->end_address - console_region->start_address + 1;
|
||||
unsigned real_address = console_region->real_address;
|
||||
unsigned disconnect_size = 0;
|
||||
|
||||
while (console_region_size > 0) {
|
||||
const struct retro_memory_descriptor* desc = rc_libretro_memory_get_descriptor(mmap, real_address, &offset);
|
||||
@@ -362,6 +394,14 @@ static void rc_libretro_memory_init_from_memory_map(rc_libretro_memory_regions_t
|
||||
rc_libretro_verbose(description);
|
||||
}
|
||||
|
||||
if (disconnect_size && console_region_size > disconnect_size) {
|
||||
rc_libretro_memory_register_region(regions, console_region->type, NULL, disconnect_size, "null filler");
|
||||
console_region_size -= disconnect_size;
|
||||
real_address += disconnect_size;
|
||||
disconnect_size = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
rc_libretro_memory_register_region(regions, console_region->type, NULL, console_region_size, "null filler");
|
||||
break;
|
||||
}
|
||||
@@ -378,6 +418,13 @@ static void rc_libretro_memory_init_from_memory_map(rc_libretro_memory_regions_t
|
||||
}
|
||||
|
||||
desc_size = desc->len - offset;
|
||||
if (desc->disconnect && desc_size > desc->disconnect) {
|
||||
/* if we need to extract a disconnect bit, the largest block we can read is up to
|
||||
* the next time that bit flips */
|
||||
/* https://stackoverflow.com/questions/12247186/find-the-lowest-set-bit */
|
||||
disconnect_size = (desc->disconnect & -((int)desc->disconnect));
|
||||
desc_size = disconnect_size - (real_address & (disconnect_size - 1));
|
||||
}
|
||||
|
||||
if (console_region_size > desc_size) {
|
||||
if (desc_size == 0) {
|
||||
|
||||
+168
-50
@@ -8,7 +8,9 @@
|
||||
enum {
|
||||
RC_FORMAT_STRING = 101,
|
||||
RC_FORMAT_LOOKUP = 102,
|
||||
RC_FORMAT_UNKNOWN_MACRO = 103
|
||||
RC_FORMAT_UNKNOWN_MACRO = 103,
|
||||
RC_FORMAT_ASCIICHAR = 104,
|
||||
RC_FORMAT_UNICODECHAR = 105
|
||||
};
|
||||
|
||||
static rc_memref_value_t* rc_alloc_helper_variable_memref_value(const char* memaddr, int memaddr_len, rc_parse_state_t* parse) {
|
||||
@@ -73,6 +75,12 @@ static const char* rc_parse_line(const char* line, const char** end, rc_parse_st
|
||||
return nextline;
|
||||
}
|
||||
|
||||
typedef struct rc_richpresence_builtin_macro_t {
|
||||
const char* name;
|
||||
size_t name_len;
|
||||
unsigned short display_type;
|
||||
} rc_richpresence_builtin_macro_t;
|
||||
|
||||
static rc_richpresence_display_t* rc_parse_richpresence_display_internal(const char* line, const char* endline, rc_parse_state_t* parse, rc_richpresence_lookup_t* first_lookup) {
|
||||
rc_richpresence_display_t* self;
|
||||
rc_richpresence_display_part_t* part;
|
||||
@@ -132,6 +140,8 @@ static rc_richpresence_display_t* rc_parse_richpresence_display_internal(const c
|
||||
|
||||
if (*ptr == '@') {
|
||||
/* handle macro part */
|
||||
size_t macro_len;
|
||||
|
||||
line = ++ptr;
|
||||
while (ptr < endline && *ptr != '(')
|
||||
++ptr;
|
||||
@@ -141,58 +151,81 @@ static rc_richpresence_display_t* rc_parse_richpresence_display_internal(const c
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ptr > line) {
|
||||
/* find the lookup and hook it up */
|
||||
lookup = first_lookup;
|
||||
while (lookup) {
|
||||
if (strncmp(lookup->name, line, ptr - line) == 0 && lookup->name[ptr - line] == '\0') {
|
||||
part = RC_ALLOC(rc_richpresence_display_part_t, parse);
|
||||
*next = part;
|
||||
next = &part->next;
|
||||
macro_len = ptr - line;
|
||||
|
||||
part->text = lookup->name;
|
||||
part->lookup = lookup;
|
||||
part->display_type = lookup->format;
|
||||
part = RC_ALLOC(rc_richpresence_display_part_t, parse);
|
||||
memset(part, 0, sizeof(rc_richpresence_display_part_t));
|
||||
*next = part;
|
||||
next = &part->next;
|
||||
|
||||
in = line;
|
||||
line = ++ptr;
|
||||
while (ptr < endline && *ptr != ')')
|
||||
++ptr;
|
||||
if (*ptr == ')') {
|
||||
part->value = rc_alloc_helper_variable_memref_value(line, (int)(ptr-line), parse);
|
||||
if (parse->offset < 0)
|
||||
return 0;
|
||||
++ptr;
|
||||
}
|
||||
else {
|
||||
/* non-terminated macro, dump the macro and the remaining portion of the line */
|
||||
--in; /* already skipped over @ */
|
||||
part->display_type = RC_FORMAT_STRING;
|
||||
part->text = rc_alloc_str(parse, in, (int)(ptr - in));
|
||||
}
|
||||
part->display_type = RC_FORMAT_UNKNOWN_MACRO;
|
||||
|
||||
/* find the lookup and hook it up */
|
||||
lookup = first_lookup;
|
||||
while (lookup) {
|
||||
if (strncmp(lookup->name, line, macro_len) == 0 && lookup->name[macro_len] == '\0') {
|
||||
part->text = lookup->name;
|
||||
part->lookup = lookup;
|
||||
part->display_type = lookup->format;
|
||||
break;
|
||||
}
|
||||
|
||||
lookup = lookup->next;
|
||||
}
|
||||
|
||||
if (!lookup) {
|
||||
static const rc_richpresence_builtin_macro_t builtin_macros[] = {
|
||||
{"Number", 6, RC_FORMAT_VALUE},
|
||||
{"Score", 5, RC_FORMAT_SCORE},
|
||||
{"Centiseconds", 12, RC_FORMAT_CENTISECS},
|
||||
{"Seconds", 7, RC_FORMAT_SECONDS},
|
||||
{"Minutes", 7, RC_FORMAT_MINUTES},
|
||||
{"SecondsAsMinutes", 16, RC_FORMAT_SECONDS_AS_MINUTES},
|
||||
{"ASCIIChar", 9, RC_FORMAT_ASCIICHAR},
|
||||
{"UnicodeChar", 11, RC_FORMAT_UNICODECHAR},
|
||||
{"Float1", 6, RC_FORMAT_FLOAT1},
|
||||
{"Float2", 6, RC_FORMAT_FLOAT2},
|
||||
{"Float3", 6, RC_FORMAT_FLOAT3},
|
||||
{"Float4", 6, RC_FORMAT_FLOAT4},
|
||||
{"Float5", 6, RC_FORMAT_FLOAT5},
|
||||
{"Float6", 6, RC_FORMAT_FLOAT6},
|
||||
};
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < sizeof(builtin_macros) / sizeof(builtin_macros[0]); ++i) {
|
||||
if (macro_len == builtin_macros[i].name_len &&
|
||||
memcmp(builtin_macros[i].name, line, builtin_macros[i].name_len) == 0) {
|
||||
part->text = builtin_macros[i].name;
|
||||
part->lookup = NULL;
|
||||
part->display_type = builtin_macros[i].display_type;
|
||||
break;
|
||||
}
|
||||
|
||||
lookup = lookup->next;
|
||||
}
|
||||
}
|
||||
|
||||
if (!lookup) {
|
||||
part = RC_ALLOC(rc_richpresence_display_part_t, parse);
|
||||
memset(part, 0, sizeof(rc_richpresence_display_part_t));
|
||||
*next = part;
|
||||
next = &part->next;
|
||||
/* find the closing parenthesis */
|
||||
in = line;
|
||||
line = ++ptr;
|
||||
while (ptr < endline && *ptr != ')')
|
||||
++ptr;
|
||||
|
||||
/* find the closing parenthesis */
|
||||
while (ptr < endline && *ptr != ')')
|
||||
++ptr;
|
||||
if (*ptr == ')')
|
||||
++ptr;
|
||||
if (*ptr != ')') {
|
||||
/* non-terminated macro, dump the macro and the remaining portion of the line */
|
||||
--in; /* already skipped over @ */
|
||||
part->display_type = RC_FORMAT_STRING;
|
||||
part->text = rc_alloc_str(parse, in, (int)(ptr - in));
|
||||
}
|
||||
else if (part->display_type != RC_FORMAT_UNKNOWN_MACRO) {
|
||||
part->value = rc_alloc_helper_variable_memref_value(line, (int)(ptr - line), parse);
|
||||
if (parse->offset < 0)
|
||||
return 0;
|
||||
|
||||
/* assert: the allocated string is going to be smaller than the memory used for the parameter of the macro */
|
||||
part->display_type = RC_FORMAT_UNKNOWN_MACRO;
|
||||
part->text = rc_alloc_str(parse, line, (int)(ptr - line));
|
||||
}
|
||||
++ptr;
|
||||
}
|
||||
else {
|
||||
/* assert: the allocated string is going to be smaller than the memory used for the parameter of the macro */
|
||||
++ptr;
|
||||
part->text = rc_alloc_str(parse, in, (int)(ptr - in));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -623,11 +656,11 @@ void rc_update_richpresence(rc_richpresence_t* richpresence, rc_peek_t peek, voi
|
||||
static int rc_evaluate_richpresence_display(rc_richpresence_display_part_t* part, char* buffer, unsigned buffersize)
|
||||
{
|
||||
rc_richpresence_lookup_item_t* item;
|
||||
rc_typed_value_t value;
|
||||
char tmp[256];
|
||||
char* ptr = buffer;
|
||||
const char* text;
|
||||
size_t chars;
|
||||
unsigned value;
|
||||
|
||||
*ptr = '\0';
|
||||
while (part) {
|
||||
@@ -638,14 +671,16 @@ static int rc_evaluate_richpresence_display(rc_richpresence_display_part_t* part
|
||||
break;
|
||||
|
||||
case RC_FORMAT_LOOKUP:
|
||||
value = part->value->value;
|
||||
rc_typed_value_from_memref_value(&value, part->value);
|
||||
rc_typed_value_convert(&value, RC_VALUE_TYPE_UNSIGNED);
|
||||
|
||||
text = part->lookup->default_label;
|
||||
item = part->lookup->root;
|
||||
while (item) {
|
||||
if (value > item->last) {
|
||||
if (value.value.u32 > item->last) {
|
||||
item = item->right;
|
||||
}
|
||||
else if (value < item->first) {
|
||||
else if (value.value.u32 < item->first) {
|
||||
item = item->left;
|
||||
}
|
||||
else {
|
||||
@@ -657,14 +692,86 @@ static int rc_evaluate_richpresence_display(rc_richpresence_display_part_t* part
|
||||
chars = strlen(text);
|
||||
break;
|
||||
|
||||
case RC_FORMAT_ASCIICHAR:
|
||||
chars = 0;
|
||||
text = tmp;
|
||||
value.type = RC_VALUE_TYPE_UNSIGNED;
|
||||
|
||||
do {
|
||||
value.value.u32 = part->value->value;
|
||||
if (value.value.u32 == 0) {
|
||||
/* null terminator - skip over remaining character macros */
|
||||
while (part->next && part->next->display_type == RC_FORMAT_ASCIICHAR)
|
||||
part = part->next;
|
||||
break;
|
||||
}
|
||||
|
||||
if (value.value.u32 < 32 || value.value.u32 >= 127)
|
||||
value.value.u32 = '?';
|
||||
|
||||
tmp[chars++] = (char)value.value.u32;
|
||||
if (chars == sizeof(tmp) || !part->next || part->next->display_type != RC_FORMAT_ASCIICHAR)
|
||||
break;
|
||||
|
||||
part = part->next;
|
||||
} while (1);
|
||||
|
||||
tmp[chars] = '\0';
|
||||
break;
|
||||
|
||||
case RC_FORMAT_UNICODECHAR:
|
||||
chars = 0;
|
||||
text = tmp;
|
||||
value.type = RC_VALUE_TYPE_UNSIGNED;
|
||||
|
||||
do {
|
||||
value.value.u32 = part->value->value;
|
||||
if (value.value.u32 == 0) {
|
||||
/* null terminator - skip over remaining character macros */
|
||||
while (part->next && part->next->display_type == RC_FORMAT_UNICODECHAR)
|
||||
part = part->next;
|
||||
break;
|
||||
}
|
||||
|
||||
if (value.value.u32 < 32 || value.value.u32 > 65535)
|
||||
value.value.u32 = 0xFFFD; /* unicode replacement char */
|
||||
|
||||
if (value.value.u32 < 0x80) {
|
||||
tmp[chars++] = (char)value.value.u32;
|
||||
}
|
||||
else if (value.value.u32 < 0x0800) {
|
||||
tmp[chars + 1] = (char)(0x80 | (value.value.u32 & 0x3F)); value.value.u32 >>= 6;
|
||||
tmp[chars] = (char)(0xC0 | (value.value.u32 & 0x1F));
|
||||
chars += 2;
|
||||
}
|
||||
else {
|
||||
/* surrogate pair not supported, convert to replacement char */
|
||||
if (value.value.u32 >= 0xD800 && value.value.u32 < 0xE000)
|
||||
value.value.u32 = 0xFFFD;
|
||||
|
||||
tmp[chars + 2] = (char)(0x80 | (value.value.u32 & 0x3F)); value.value.u32 >>= 6;
|
||||
tmp[chars + 1] = (char)(0x80 | (value.value.u32 & 0x3F)); value.value.u32 >>= 6;
|
||||
tmp[chars] = (char)(0xE0 | (value.value.u32 & 0x1F));
|
||||
chars += 3;
|
||||
}
|
||||
|
||||
if (chars >= sizeof(tmp) - 3 || !part->next || part->next->display_type != RC_FORMAT_UNICODECHAR)
|
||||
break;
|
||||
|
||||
part = part->next;
|
||||
} while (1);
|
||||
|
||||
tmp[chars] = '\0';
|
||||
break;
|
||||
|
||||
case RC_FORMAT_UNKNOWN_MACRO:
|
||||
chars = snprintf(tmp, sizeof(tmp), "[Unknown macro]%s", part->text);
|
||||
text = tmp;
|
||||
break;
|
||||
|
||||
default:
|
||||
value = part->value->value;
|
||||
chars = rc_format_value(tmp, sizeof(tmp), value, part->display_type);
|
||||
rc_typed_value_from_memref_value(&value, part->value);
|
||||
chars = rc_format_typed_value(tmp, sizeof(tmp), &value, part->display_type);
|
||||
text = tmp;
|
||||
break;
|
||||
}
|
||||
@@ -715,3 +822,14 @@ int rc_evaluate_richpresence(rc_richpresence_t* richpresence, char* buffer, unsi
|
||||
rc_update_richpresence(richpresence, peek, peek_ud, L);
|
||||
return rc_get_richpresence_display_string(richpresence, buffer, buffersize, peek, peek_ud, L);
|
||||
}
|
||||
|
||||
void rc_reset_richpresence(rc_richpresence_t* self) {
|
||||
rc_richpresence_display_t* display;
|
||||
rc_value_t* variable;
|
||||
|
||||
for (display = self->first_display; display; display = display->next)
|
||||
rc_reset_trigger(&display->trigger);
|
||||
|
||||
for (variable = self->variables; variable; variable = variable->next)
|
||||
rc_reset_value(variable);
|
||||
}
|
||||
Vendored
+49
-14
@@ -300,6 +300,7 @@ int rc_runtime_activate_lboard(rc_runtime_t* self, unsigned id, const char* mema
|
||||
unsigned char md5[16];
|
||||
rc_lboard_t* lboard;
|
||||
rc_parse_state_t parse;
|
||||
rc_runtime_lboard_t* runtime_lboard;
|
||||
int size;
|
||||
unsigned i;
|
||||
|
||||
@@ -378,14 +379,15 @@ int rc_runtime_activate_lboard(rc_runtime_t* self, unsigned id, const char* mema
|
||||
}
|
||||
|
||||
/* assign the new lboard */
|
||||
self->lboards[self->lboard_count].id = id;
|
||||
self->lboards[self->lboard_count].value = 0;
|
||||
self->lboards[self->lboard_count].lboard = lboard;
|
||||
self->lboards[self->lboard_count].buffer = lboard_buffer;
|
||||
self->lboards[self->lboard_count].invalid_memref = NULL;
|
||||
memcpy(self->lboards[self->lboard_count].md5, md5, 16);
|
||||
self->lboards[self->lboard_count].owns_memrefs = rc_runtime_allocated_memrefs(self);
|
||||
++self->lboard_count;
|
||||
runtime_lboard = &self->lboards[self->lboard_count++];
|
||||
runtime_lboard->id = id;
|
||||
runtime_lboard->value = 0;
|
||||
runtime_lboard->lboard = lboard;
|
||||
runtime_lboard->buffer = lboard_buffer;
|
||||
runtime_lboard->invalid_memref = NULL;
|
||||
memcpy(runtime_lboard->md5, md5, 16);
|
||||
runtime_lboard->serialized_size = 0;
|
||||
runtime_lboard->owns_memrefs = rc_runtime_allocated_memrefs(self);
|
||||
|
||||
/* reset it, and return it */
|
||||
lboard->memrefs = NULL;
|
||||
@@ -413,17 +415,52 @@ int rc_runtime_format_lboard_value(char* buffer, int size, int value, int format
|
||||
int rc_runtime_activate_richpresence(rc_runtime_t* self, const char* script, lua_State* L, int funcs_idx) {
|
||||
rc_richpresence_t* richpresence;
|
||||
rc_runtime_richpresence_t* previous;
|
||||
rc_richpresence_display_t* display;
|
||||
rc_runtime_richpresence_t** previous_ptr;
|
||||
rc_parse_state_t parse;
|
||||
unsigned char md5[16];
|
||||
int size;
|
||||
|
||||
if (script == NULL)
|
||||
return RC_MISSING_DISPLAY_STRING;
|
||||
|
||||
rc_runtime_checksum(script, md5);
|
||||
|
||||
/* look for existing match */
|
||||
previous_ptr = NULL;
|
||||
previous = self->richpresence;
|
||||
while (previous) {
|
||||
if (previous && memcmp(self->richpresence->md5, md5, 16) == 0) {
|
||||
/* unchanged. reset all of the conditions */
|
||||
rc_reset_richpresence(self->richpresence->richpresence);
|
||||
|
||||
/* move to front of linked list*/
|
||||
if (previous_ptr) {
|
||||
*previous_ptr = previous->previous;
|
||||
if (!self->richpresence->owns_memrefs) {
|
||||
free(self->richpresence->buffer);
|
||||
previous->previous = self->richpresence->previous;
|
||||
}
|
||||
else {
|
||||
previous->previous = self->richpresence;
|
||||
}
|
||||
|
||||
self->richpresence = previous;
|
||||
}
|
||||
|
||||
/* return success*/
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
previous_ptr = &previous->previous;
|
||||
previous = previous->previous;
|
||||
}
|
||||
|
||||
/* no existing match found, parse script */
|
||||
size = rc_richpresence_size(script);
|
||||
if (size < 0)
|
||||
return size;
|
||||
|
||||
/* if the previous script doesn't have any memrefs, free it */
|
||||
previous = self->richpresence;
|
||||
if (previous) {
|
||||
if (!previous->owns_memrefs) {
|
||||
@@ -432,12 +469,14 @@ int rc_runtime_activate_richpresence(rc_runtime_t* self, const char* script, lua
|
||||
}
|
||||
}
|
||||
|
||||
/* allocate and process the new script */
|
||||
self->richpresence = (rc_runtime_richpresence_t*)malloc(sizeof(rc_runtime_richpresence_t));
|
||||
if (!self->richpresence)
|
||||
return RC_OUT_OF_MEMORY;
|
||||
|
||||
self->richpresence->previous = previous;
|
||||
self->richpresence->owns_memrefs = 0;
|
||||
memcpy(self->richpresence->md5, md5, sizeof(md5));
|
||||
self->richpresence->buffer = malloc(size);
|
||||
|
||||
if (!self->richpresence->buffer)
|
||||
@@ -469,11 +508,7 @@ int rc_runtime_activate_richpresence(rc_runtime_t* self, const char* script, lua
|
||||
}
|
||||
else {
|
||||
/* reset all of the conditions */
|
||||
display = richpresence->first_display;
|
||||
while (display != NULL) {
|
||||
rc_reset_trigger(&display->trigger);
|
||||
display = display->next;
|
||||
}
|
||||
rc_reset_richpresence(richpresence);
|
||||
}
|
||||
|
||||
return RC_OK;
|
||||
|
||||
+365
-7
@@ -3,14 +3,18 @@
|
||||
|
||||
#include "../rhash/md5.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define RC_RUNTIME_MARKER 0x0A504152 /* RAP\n */
|
||||
#define RC_RUNTIME_MARKER 0x0A504152 /* RAP\n */
|
||||
|
||||
#define RC_RUNTIME_CHUNK_MEMREFS 0x4645524D /* MREF */
|
||||
#define RC_RUNTIME_CHUNK_ACHIEVEMENT 0x56484341 /* ACHV */
|
||||
#define RC_RUNTIME_CHUNK_MEMREFS 0x4645524D /* MREF */
|
||||
#define RC_RUNTIME_CHUNK_VARIABLES 0x53524156 /* VARS */
|
||||
#define RC_RUNTIME_CHUNK_ACHIEVEMENT 0x56484341 /* ACHV */
|
||||
#define RC_RUNTIME_CHUNK_LEADERBOARD 0x4452424C /* LBRD */
|
||||
#define RC_RUNTIME_CHUNK_RICHPRESENCE 0x48434952 /* RICH */
|
||||
|
||||
#define RC_RUNTIME_CHUNK_DONE 0x454E4F44 /* DONE */
|
||||
#define RC_RUNTIME_CHUNK_DONE 0x454E4F44 /* DONE */
|
||||
|
||||
typedef struct rc_runtime_progress_t {
|
||||
rc_runtime_t* runtime;
|
||||
@@ -27,6 +31,8 @@ typedef struct rc_runtime_progress_t {
|
||||
|
||||
#define RC_MEMREF_FLAG_CHANGED_THIS_FRAME 0x00010000
|
||||
|
||||
#define RC_VAR_FLAG_HAS_COND_DATA 0x01000000
|
||||
|
||||
#define RC_COND_FLAG_IS_TRUE 0x00000001
|
||||
#define RC_COND_FLAG_OPERAND1_IS_INDIRECT_MEMREF 0x00010000
|
||||
#define RC_COND_FLAG_OPERAND1_MEMREF_CHANGED_THIS_FRAME 0x00020000
|
||||
@@ -75,6 +81,17 @@ static int rc_runtime_progress_match_md5(rc_runtime_progress_t* progress, unsign
|
||||
return result;
|
||||
}
|
||||
|
||||
static unsigned rc_runtime_progress_djb2(const char* input)
|
||||
{
|
||||
unsigned result = 5381;
|
||||
char c;
|
||||
|
||||
while ((c = *input++) != '\0')
|
||||
result = ((result << 5) + result) + c; /* result = result * 33 + c */
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void rc_runtime_progress_start_chunk(rc_runtime_progress_t* progress, unsigned chunk_id)
|
||||
{
|
||||
rc_runtime_progress_write_uint(progress, chunk_id);
|
||||
@@ -280,7 +297,158 @@ static int rc_runtime_progress_read_condset(rc_runtime_progress_t* progress, rc_
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
static int rc_runtime_progress_write_trigger(rc_runtime_progress_t* progress, rc_trigger_t* trigger)
|
||||
static unsigned rc_runtime_progress_should_serialize_variable_condset(const rc_condset_t* conditions)
|
||||
{
|
||||
const rc_condition_t* condition;
|
||||
|
||||
/* predetermined presence of pause flag or indirect memrefs - must serialize */
|
||||
if (conditions->has_pause || conditions->has_indirect_memrefs)
|
||||
return RC_VAR_FLAG_HAS_COND_DATA;
|
||||
|
||||
/* if any conditions has required hits, must serialize */
|
||||
/* ASSERT: Measured with comparison and no explicit target will set hit target to 0xFFFFFFFF */
|
||||
for (condition = conditions->conditions; condition; condition = condition->next) {
|
||||
if (condition->required_hits > 0)
|
||||
return RC_VAR_FLAG_HAS_COND_DATA;
|
||||
}
|
||||
|
||||
/* can safely be reset without affecting behavior */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rc_runtime_progress_write_variable(rc_runtime_progress_t* progress, const rc_value_t* variable)
|
||||
{
|
||||
unsigned flags;
|
||||
|
||||
flags = rc_runtime_progress_should_serialize_variable_condset(variable->conditions);
|
||||
if (variable->value.changed)
|
||||
flags |= RC_MEMREF_FLAG_CHANGED_THIS_FRAME;
|
||||
|
||||
rc_runtime_progress_write_uint(progress, flags);
|
||||
rc_runtime_progress_write_uint(progress, variable->value.value);
|
||||
rc_runtime_progress_write_uint(progress, variable->value.prior);
|
||||
|
||||
if (flags & RC_VAR_FLAG_HAS_COND_DATA) {
|
||||
int result = rc_runtime_progress_write_condset(progress, variable->conditions);
|
||||
if (result != RC_OK)
|
||||
return result;
|
||||
}
|
||||
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
static int rc_runtime_progress_write_variables(rc_runtime_progress_t* progress)
|
||||
{
|
||||
unsigned count = 0;
|
||||
const rc_value_t* variable;
|
||||
|
||||
for (variable = progress->runtime->variables; variable; variable = variable->next)
|
||||
++count;
|
||||
if (count == 0)
|
||||
return RC_OK;
|
||||
|
||||
rc_runtime_progress_start_chunk(progress, RC_RUNTIME_CHUNK_VARIABLES);
|
||||
rc_runtime_progress_write_uint(progress, count);
|
||||
|
||||
for (variable = progress->runtime->variables; variable; variable = variable->next)
|
||||
{
|
||||
unsigned djb2 = rc_runtime_progress_djb2(variable->name);
|
||||
rc_runtime_progress_write_uint(progress, djb2);
|
||||
|
||||
rc_runtime_progress_write_variable(progress, variable);
|
||||
}
|
||||
|
||||
rc_runtime_progress_end_chunk(progress);
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
static int rc_runtime_progress_read_variable(rc_runtime_progress_t* progress, rc_value_t* variable)
|
||||
{
|
||||
unsigned flags = rc_runtime_progress_read_uint(progress);
|
||||
variable->value.changed = (flags & RC_MEMREF_FLAG_CHANGED_THIS_FRAME) ? 1 : 0;
|
||||
variable->value.value = rc_runtime_progress_read_uint(progress);
|
||||
variable->value.prior = rc_runtime_progress_read_uint(progress);
|
||||
|
||||
if (flags & RC_VAR_FLAG_HAS_COND_DATA) {
|
||||
int result = rc_runtime_progress_read_condset(progress, variable->conditions);
|
||||
if (result != RC_OK)
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
rc_reset_condset(variable->conditions);
|
||||
}
|
||||
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
static int rc_runtime_progress_read_variables(rc_runtime_progress_t* progress)
|
||||
{
|
||||
struct rc_pending_value_t
|
||||
{
|
||||
rc_value_t* variable;
|
||||
unsigned djb2;
|
||||
};
|
||||
struct rc_pending_value_t local_pending_variables[32];
|
||||
struct rc_pending_value_t* pending_variables;
|
||||
rc_value_t* variable;
|
||||
unsigned count, serialized_count;
|
||||
int result;
|
||||
unsigned i;
|
||||
|
||||
serialized_count = rc_runtime_progress_read_uint(progress);
|
||||
if (serialized_count == 0)
|
||||
return RC_OK;
|
||||
|
||||
count = 0;
|
||||
for (variable = progress->runtime->variables; variable; variable = variable->next)
|
||||
++count;
|
||||
|
||||
if (count == 0)
|
||||
return RC_OK;
|
||||
|
||||
if (count <= sizeof(local_pending_variables) / sizeof(local_pending_variables[0])) {
|
||||
pending_variables = local_pending_variables;
|
||||
}
|
||||
else {
|
||||
pending_variables = (struct rc_pending_value_t*)malloc(count * sizeof(struct rc_pending_value_t));
|
||||
if (pending_variables == NULL)
|
||||
return RC_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
count = 0;
|
||||
for (variable = progress->runtime->variables; variable; variable = variable->next) {
|
||||
pending_variables[count].variable = variable;
|
||||
pending_variables[count].djb2 = rc_runtime_progress_djb2(variable->name);
|
||||
++count;
|
||||
}
|
||||
|
||||
result = RC_OK;
|
||||
for (; serialized_count > 0 && result == RC_OK; --serialized_count) {
|
||||
unsigned djb2 = rc_runtime_progress_read_uint(progress);
|
||||
for (i = 0; i < count; ++i) {
|
||||
if (pending_variables[i].djb2 == djb2) {
|
||||
variable = pending_variables[i].variable;
|
||||
result = rc_runtime_progress_read_variable(progress, variable);
|
||||
if (result == RC_OK) {
|
||||
if (i < count - 1)
|
||||
memcpy(&pending_variables[i], &pending_variables[count - 1], sizeof(struct rc_pending_value_t));
|
||||
count--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (count > 0)
|
||||
rc_reset_value(pending_variables[--count].variable);
|
||||
|
||||
if (pending_variables != local_pending_variables)
|
||||
free(pending_variables);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int rc_runtime_progress_write_trigger(rc_runtime_progress_t* progress, const rc_trigger_t* trigger)
|
||||
{
|
||||
rc_condset_t* condset;
|
||||
int result;
|
||||
@@ -394,6 +562,153 @@ static int rc_runtime_progress_read_achievement(rc_runtime_progress_t* progress)
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
static int rc_runtime_progress_write_leaderboards(rc_runtime_progress_t* progress)
|
||||
{
|
||||
unsigned i;
|
||||
unsigned flags;
|
||||
int offset = 0;
|
||||
int result;
|
||||
|
||||
for (i = 0; i < progress->runtime->lboard_count; ++i) {
|
||||
rc_runtime_lboard_t* runtime_lboard = &progress->runtime->lboards[i];
|
||||
if (!runtime_lboard->lboard)
|
||||
continue;
|
||||
|
||||
/* don't store state for inactive leaderboards */
|
||||
if (!rc_lboard_state_active(runtime_lboard->lboard->state))
|
||||
continue;
|
||||
|
||||
if (!progress->buffer) {
|
||||
if (runtime_lboard->serialized_size) {
|
||||
progress->offset += runtime_lboard->serialized_size;
|
||||
continue;
|
||||
}
|
||||
|
||||
offset = progress->offset;
|
||||
}
|
||||
|
||||
rc_runtime_progress_start_chunk(progress, RC_RUNTIME_CHUNK_LEADERBOARD);
|
||||
rc_runtime_progress_write_uint(progress, runtime_lboard->id);
|
||||
rc_runtime_progress_write_md5(progress, runtime_lboard->md5);
|
||||
|
||||
flags = runtime_lboard->lboard->state;
|
||||
rc_runtime_progress_write_uint(progress, flags);
|
||||
|
||||
result = rc_runtime_progress_write_trigger(progress, &runtime_lboard->lboard->start);
|
||||
if (result != RC_OK)
|
||||
return result;
|
||||
|
||||
result = rc_runtime_progress_write_trigger(progress, &runtime_lboard->lboard->submit);
|
||||
if (result != RC_OK)
|
||||
return result;
|
||||
|
||||
result = rc_runtime_progress_write_trigger(progress, &runtime_lboard->lboard->cancel);
|
||||
if (result != RC_OK)
|
||||
return result;
|
||||
|
||||
result = rc_runtime_progress_write_variable(progress, &runtime_lboard->lboard->value);
|
||||
if (result != RC_OK)
|
||||
return result;
|
||||
|
||||
rc_runtime_progress_end_chunk(progress);
|
||||
|
||||
if (!progress->buffer)
|
||||
runtime_lboard->serialized_size = progress->offset - offset;
|
||||
}
|
||||
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
static int rc_runtime_progress_read_leaderboard(rc_runtime_progress_t* progress)
|
||||
{
|
||||
unsigned id = rc_runtime_progress_read_uint(progress);
|
||||
unsigned i;
|
||||
int result;
|
||||
|
||||
for (i = 0; i < progress->runtime->lboard_count; ++i) {
|
||||
rc_runtime_lboard_t* runtime_lboard = &progress->runtime->lboards[i];
|
||||
if (runtime_lboard->id == id && runtime_lboard->lboard != NULL) {
|
||||
/* ignore triggered and waiting achievements */
|
||||
if (runtime_lboard->lboard->state == RC_TRIGGER_STATE_UNUPDATED) {
|
||||
/* only update state if definition hasn't changed (md5 matches) */
|
||||
if (rc_runtime_progress_match_md5(progress, runtime_lboard->md5)) {
|
||||
unsigned flags = rc_runtime_progress_read_uint(progress);
|
||||
|
||||
result = rc_runtime_progress_read_trigger(progress, &runtime_lboard->lboard->start);
|
||||
if (result != RC_OK)
|
||||
return result;
|
||||
|
||||
result = rc_runtime_progress_read_trigger(progress, &runtime_lboard->lboard->submit);
|
||||
if (result != RC_OK)
|
||||
return result;
|
||||
|
||||
result = rc_runtime_progress_read_trigger(progress, &runtime_lboard->lboard->cancel);
|
||||
if (result != RC_OK)
|
||||
return result;
|
||||
|
||||
result = rc_runtime_progress_read_variable(progress, &runtime_lboard->lboard->value);
|
||||
if (result != RC_OK)
|
||||
return result;
|
||||
|
||||
runtime_lboard->lboard->state = (char)(flags & 0x7F);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
static int rc_runtime_progress_write_rich_presence(rc_runtime_progress_t* progress)
|
||||
{
|
||||
const rc_richpresence_display_t* display;
|
||||
int result;
|
||||
|
||||
if (!progress->runtime->richpresence || !progress->runtime->richpresence->richpresence)
|
||||
return RC_OK;
|
||||
|
||||
/* if there are no conditional display strings, there's nothing to capture */
|
||||
display = progress->runtime->richpresence->richpresence->first_display;
|
||||
if (!display->next)
|
||||
return RC_OK;
|
||||
|
||||
rc_runtime_progress_start_chunk(progress, RC_RUNTIME_CHUNK_RICHPRESENCE);
|
||||
rc_runtime_progress_write_md5(progress, progress->runtime->richpresence->md5);
|
||||
|
||||
for (; display->next; display = display->next) {
|
||||
result = rc_runtime_progress_write_trigger(progress, &display->trigger);
|
||||
if (result != RC_OK)
|
||||
return result;
|
||||
}
|
||||
|
||||
rc_runtime_progress_end_chunk(progress);
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
static int rc_runtime_progress_read_rich_presence(rc_runtime_progress_t* progress)
|
||||
{
|
||||
rc_richpresence_display_t* display;
|
||||
int result;
|
||||
|
||||
if (!progress->runtime->richpresence || !progress->runtime->richpresence->richpresence)
|
||||
return RC_OK;
|
||||
|
||||
if (!rc_runtime_progress_match_md5(progress, progress->runtime->richpresence->md5)) {
|
||||
rc_reset_richpresence(progress->runtime->richpresence->richpresence);
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
display = progress->runtime->richpresence->richpresence->first_display;
|
||||
for (; display->next; display = display->next) {
|
||||
result = rc_runtime_progress_read_trigger(progress, &display->trigger);
|
||||
if (result != RC_OK)
|
||||
return result;
|
||||
}
|
||||
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
static int rc_runtime_progress_serialize_internal(rc_runtime_progress_t* progress)
|
||||
{
|
||||
md5_state_t state;
|
||||
@@ -405,9 +720,18 @@ static int rc_runtime_progress_serialize_internal(rc_runtime_progress_t* progres
|
||||
if ((result = rc_runtime_progress_write_memrefs(progress)) != RC_OK)
|
||||
return result;
|
||||
|
||||
if ((result = rc_runtime_progress_write_variables(progress)) != RC_OK)
|
||||
return result;
|
||||
|
||||
if ((result = rc_runtime_progress_write_achievements(progress)) != RC_OK)
|
||||
return result;
|
||||
|
||||
if ((result = rc_runtime_progress_write_leaderboards(progress)) != RC_OK)
|
||||
return result;
|
||||
|
||||
if ((result = rc_runtime_progress_write_rich_presence(progress)) != RC_OK)
|
||||
return result;
|
||||
|
||||
rc_runtime_progress_write_uint(progress, RC_RUNTIME_CHUNK_DONE);
|
||||
rc_runtime_progress_write_uint(progress, 16);
|
||||
|
||||
@@ -455,6 +779,7 @@ int rc_runtime_deserialize_progress(rc_runtime_t* runtime, const unsigned char*
|
||||
unsigned chunk_size;
|
||||
unsigned next_chunk_offset;
|
||||
unsigned i;
|
||||
int seen_rich_presence = 0;
|
||||
int result = RC_OK;
|
||||
|
||||
rc_runtime_progress_init(&progress, runtime, L);
|
||||
@@ -469,8 +794,7 @@ int rc_runtime_deserialize_progress(rc_runtime_t* runtime, const unsigned char*
|
||||
rc_runtime_trigger_t* runtime_trigger = &runtime->triggers[i];
|
||||
if (runtime_trigger->trigger) {
|
||||
/* don't update state for inactive or triggered achievements */
|
||||
if (rc_trigger_state_active(runtime_trigger->trigger->state))
|
||||
{
|
||||
if (rc_trigger_state_active(runtime_trigger->trigger->state)) {
|
||||
/* mark active achievements as unupdated. anything that's still unupdated
|
||||
* after deserializing the progress will be reset to waiting */
|
||||
runtime_trigger->trigger->state = RC_TRIGGER_STATE_UNUPDATED;
|
||||
@@ -478,6 +802,18 @@ int rc_runtime_deserialize_progress(rc_runtime_t* runtime, const unsigned char*
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < runtime->lboard_count; ++i) {
|
||||
rc_runtime_lboard_t* runtime_lboard = &runtime->lboards[i];
|
||||
if (runtime_lboard->lboard) {
|
||||
/* don't update state for inactive or triggered achievements */
|
||||
if (rc_lboard_state_active(runtime_lboard->lboard->state)) {
|
||||
/* mark active achievements as unupdated. anything that's still unupdated
|
||||
* after deserializing the progress will be reset to waiting */
|
||||
runtime_lboard->lboard->state = RC_TRIGGER_STATE_UNUPDATED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
chunk_id = rc_runtime_progress_read_uint(&progress);
|
||||
chunk_size = rc_runtime_progress_read_uint(&progress);
|
||||
@@ -489,10 +825,23 @@ int rc_runtime_deserialize_progress(rc_runtime_t* runtime, const unsigned char*
|
||||
result = rc_runtime_progress_read_memrefs(&progress);
|
||||
break;
|
||||
|
||||
case RC_RUNTIME_CHUNK_VARIABLES:
|
||||
result = rc_runtime_progress_read_variables(&progress);
|
||||
break;
|
||||
|
||||
case RC_RUNTIME_CHUNK_ACHIEVEMENT:
|
||||
result = rc_runtime_progress_read_achievement(&progress);
|
||||
break;
|
||||
|
||||
case RC_RUNTIME_CHUNK_LEADERBOARD:
|
||||
result = rc_runtime_progress_read_leaderboard(&progress);
|
||||
break;
|
||||
|
||||
case RC_RUNTIME_CHUNK_RICHPRESENCE:
|
||||
seen_rich_presence = 1;
|
||||
result = rc_runtime_progress_read_rich_presence(&progress);
|
||||
break;
|
||||
|
||||
case RC_RUNTIME_CHUNK_DONE:
|
||||
md5_init(&state);
|
||||
md5_append(&state, progress.buffer, progress.offset);
|
||||
@@ -519,6 +868,15 @@ int rc_runtime_deserialize_progress(rc_runtime_t* runtime, const unsigned char*
|
||||
if (trigger && trigger->state == RC_TRIGGER_STATE_UNUPDATED)
|
||||
rc_reset_trigger(trigger);
|
||||
}
|
||||
|
||||
for (i = 0; i < runtime->lboard_count; ++i) {
|
||||
rc_lboard_t* lboard = runtime->lboards[i].lboard;
|
||||
if (lboard && lboard->state == RC_TRIGGER_STATE_UNUPDATED)
|
||||
rc_reset_lboard(lboard);
|
||||
}
|
||||
|
||||
if (!seen_rich_presence && runtime->richpresence && runtime->richpresence->richpresence)
|
||||
rc_reset_richpresence(runtime->richpresence->richpresence);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
Vendored
+4
-2
@@ -179,8 +179,10 @@ int rc_evaluate_trigger(rc_trigger_t* self, rc_peek_t peek, void* ud, lua_State*
|
||||
}
|
||||
|
||||
/* if paused, the measured value may not be captured, keep the old value */
|
||||
if (!is_paused)
|
||||
self->measured_value = eval_state.measured_value;
|
||||
if (!is_paused) {
|
||||
rc_typed_value_convert(&eval_state.measured_value, RC_VALUE_TYPE_UNSIGNED);
|
||||
self->measured_value = eval_state.measured_value.value.u32;
|
||||
}
|
||||
|
||||
/* if the state is WAITING and the trigger is ready to fire, ignore it and reset the hit counts */
|
||||
/* otherwise, if the state is WAITING, proceed to activating the trigger */
|
||||
|
||||
Vendored
+418
-50
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <string.h> /* memset */
|
||||
#include <ctype.h> /* isdigit */
|
||||
#include <float.h> /* FLT_EPSILON */
|
||||
|
||||
static void rc_parse_cond_value(rc_value_t* self, const char** memaddr, rc_parse_state_t* parse) {
|
||||
rc_condset_t** next_clause;
|
||||
@@ -43,28 +44,26 @@ void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_parse_stat
|
||||
char buffer[64] = "A:";
|
||||
const char* buffer_ptr;
|
||||
char* ptr;
|
||||
int end_of_clause;
|
||||
|
||||
/* convert legacy format into condset */
|
||||
self->conditions = RC_ALLOC(rc_condset_t, parse);
|
||||
self->conditions->has_pause = 0;
|
||||
self->conditions->is_paused = 0;
|
||||
memset(self->conditions, 0, sizeof(rc_condset_t));
|
||||
|
||||
next = &self->conditions->conditions;
|
||||
next_clause = &self->conditions->next;
|
||||
|
||||
for (;;) {
|
||||
for (;; ++(*memaddr)) {
|
||||
buffer[0] = 'A'; /* reset to AddSource */
|
||||
ptr = &buffer[2];
|
||||
end_of_clause = 0;
|
||||
|
||||
do {
|
||||
/* extract the next clause */
|
||||
for (;; ++(*memaddr)) {
|
||||
switch (**memaddr) {
|
||||
case '_': /* add next */
|
||||
case '$': /* maximum of */
|
||||
case '\0': /* end of string */
|
||||
case ':': /* end of leaderboard clause */
|
||||
case ')': /* end of rich presence macro */
|
||||
end_of_clause = 1;
|
||||
*ptr = '\0';
|
||||
break;
|
||||
|
||||
@@ -72,24 +71,31 @@ void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_parse_stat
|
||||
*ptr++ = '*';
|
||||
|
||||
buffer_ptr = *memaddr + 1;
|
||||
if (*buffer_ptr == '-' || *buffer_ptr == '+')
|
||||
++buffer_ptr; /* ignore sign */
|
||||
if (*buffer_ptr == '-') {
|
||||
buffer[0] = 'B'; /* change to SubSource */
|
||||
++(*memaddr); /* don't copy sign */
|
||||
++buffer_ptr; /* ignore sign when doing floating point check */
|
||||
}
|
||||
else if (*buffer_ptr == '+') {
|
||||
++buffer_ptr; /* ignore sign when doing floating point check */
|
||||
}
|
||||
|
||||
/* if it looks like a floating point number, add the 'f' prefix */
|
||||
while (isdigit((unsigned char)*buffer_ptr))
|
||||
++buffer_ptr;
|
||||
if (*buffer_ptr == '.')
|
||||
*ptr++ = 'f';
|
||||
break;
|
||||
continue;
|
||||
|
||||
default:
|
||||
*ptr++ = **memaddr;
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
++(*memaddr);
|
||||
} while (!end_of_clause);
|
||||
break;
|
||||
}
|
||||
|
||||
/* process the clause */
|
||||
buffer_ptr = buffer;
|
||||
cond = rc_parse_condition(&buffer_ptr, parse, 0);
|
||||
if (parse->offset < 0)
|
||||
@@ -113,31 +119,40 @@ void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_parse_stat
|
||||
return;
|
||||
}
|
||||
|
||||
cond->pause = 0;
|
||||
*next = cond;
|
||||
|
||||
switch ((*memaddr)[-1]) {
|
||||
case '_': /* add next */
|
||||
next = &cond->next;
|
||||
break;
|
||||
|
||||
case '$': /* max of */
|
||||
cond->type = RC_CONDITION_MEASURED;
|
||||
cond->next = 0;
|
||||
*next_clause = RC_ALLOC(rc_condset_t, parse);
|
||||
(*next_clause)->has_pause = 0;
|
||||
(*next_clause)->is_paused = 0;
|
||||
next = &(*next_clause)->conditions;
|
||||
next_clause = &(*next_clause)->next;
|
||||
break;
|
||||
|
||||
default: /* end of valid string */
|
||||
--(*memaddr); /* undo the increment we performed when copying the string */
|
||||
cond->type = RC_CONDITION_MEASURED;
|
||||
cond->next = 0;
|
||||
*next_clause = 0;
|
||||
return;
|
||||
if (**memaddr == '_') {
|
||||
/* add next */
|
||||
next = &cond->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cond->type == RC_CONDITION_SUB_SOURCE) {
|
||||
/* cannot change SubSource to Measured. add a dummy condition */
|
||||
next = &cond->next;
|
||||
buffer_ptr = "A:0";
|
||||
cond = rc_parse_condition(&buffer_ptr, parse, 0);
|
||||
*next = cond;
|
||||
}
|
||||
|
||||
/* convert final AddSource condition to Measured */
|
||||
cond->type = RC_CONDITION_MEASURED;
|
||||
cond->next = 0;
|
||||
|
||||
if (**memaddr != '$') {
|
||||
/* end of valid string */
|
||||
*next_clause = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* max of ($), start a new clause */
|
||||
*next_clause = RC_ALLOC(rc_condset_t, parse);
|
||||
|
||||
if (parse->buffer) /* don't clear in sizing mode or pointer will break */
|
||||
memset(*next_clause, 0, sizeof(rc_condset_t));
|
||||
|
||||
next = &(*next_clause)->conditions;
|
||||
next_clause = &(*next_clause)->next;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,14 +203,16 @@ rc_value_t* rc_parse_value(void* buffer, const char* memaddr, lua_State* L, int
|
||||
return (parse.offset >= 0) ? self : NULL;
|
||||
}
|
||||
|
||||
int rc_evaluate_value(rc_value_t* self, rc_peek_t peek, void* ud, lua_State* L) {
|
||||
int rc_evaluate_value_typed(rc_value_t* self, rc_typed_value_t* value, rc_peek_t peek, void* ud, lua_State* L) {
|
||||
rc_eval_state_t eval_state;
|
||||
rc_condset_t* condset;
|
||||
int result = 0;
|
||||
int paused = 1;
|
||||
int valid = 0;
|
||||
|
||||
rc_update_memref_values(self->memrefs, peek, ud);
|
||||
|
||||
value->value.i32 = 0;
|
||||
value->type = RC_VALUE_TYPE_SIGNED;
|
||||
|
||||
for (condset = self->conditions; condset != NULL; condset = condset->next) {
|
||||
memset(&eval_state, 0, sizeof(eval_state));
|
||||
eval_state.peek = peek;
|
||||
@@ -214,34 +231,46 @@ int rc_evaluate_value(rc_value_t* self, rc_peek_t peek, void* ud, lua_State* L)
|
||||
rc_reset_condset(condset);
|
||||
|
||||
/* if the measured value came from a hit count, reset it too */
|
||||
if (eval_state.measured_from_hits)
|
||||
eval_state.measured_value = 0;
|
||||
if (eval_state.measured_from_hits) {
|
||||
eval_state.measured_value.value.u32 = 0;
|
||||
eval_state.measured_value.type = RC_VALUE_TYPE_UNSIGNED;
|
||||
}
|
||||
}
|
||||
|
||||
if (paused) {
|
||||
if (!valid) {
|
||||
/* capture the first valid measurement */
|
||||
result = (int)eval_state.measured_value;
|
||||
paused = 0;
|
||||
memcpy(value, &eval_state.measured_value, sizeof(*value));
|
||||
valid = 1;
|
||||
}
|
||||
else {
|
||||
/* multiple condsets are currently only used for the MAX_OF operation.
|
||||
* only keep the condset's value if it's higher than the current highest value.
|
||||
*/
|
||||
if ((int)eval_state.measured_value > result)
|
||||
result = (int)eval_state.measured_value;
|
||||
if (rc_typed_value_compare(&eval_state.measured_value, value, RC_OPERATOR_GT))
|
||||
memcpy(value, &eval_state.measured_value, sizeof(*value));
|
||||
}
|
||||
}
|
||||
|
||||
if (!paused) {
|
||||
return valid;
|
||||
}
|
||||
|
||||
int rc_evaluate_value(rc_value_t* self, rc_peek_t peek, void* ud, lua_State* L) {
|
||||
rc_typed_value_t result;
|
||||
int valid = rc_evaluate_value_typed(self, &result, peek, ud, L);
|
||||
|
||||
if (valid) {
|
||||
/* if not paused, store the value so that it's available when paused. */
|
||||
rc_update_memref_value(&self->value, result);
|
||||
rc_typed_value_convert(&result, RC_VALUE_TYPE_UNSIGNED);
|
||||
rc_update_memref_value(&self->value, result.value.u32);
|
||||
}
|
||||
else {
|
||||
/* when paused, the Measured value will not be captured, use the last captured value. */
|
||||
result = self->value.value;
|
||||
result.value.u32 = self->value.value;
|
||||
result.type = RC_VALUE_TYPE_UNSIGNED;
|
||||
}
|
||||
|
||||
return result;
|
||||
rc_typed_value_convert(&result, RC_VALUE_TYPE_SIGNED);
|
||||
return result.value.i32;
|
||||
}
|
||||
|
||||
void rc_reset_value(rc_value_t* self) {
|
||||
@@ -309,8 +338,347 @@ rc_value_t* rc_alloc_helper_variable(const char* memaddr, int memaddr_len, rc_pa
|
||||
}
|
||||
|
||||
void rc_update_variables(rc_value_t* variable, rc_peek_t peek, void* ud, lua_State* L) {
|
||||
rc_typed_value_t result;
|
||||
|
||||
while (variable) {
|
||||
rc_evaluate_value(variable, peek, ud, L);
|
||||
if (rc_evaluate_value_typed(variable, &result, peek, ud, L)) {
|
||||
/* store the raw bytes and type to be restored by rc_typed_value_from_memref_value */
|
||||
rc_update_memref_value(&variable->value, result.value.u32);
|
||||
variable->value.type = result.type;
|
||||
}
|
||||
|
||||
variable = variable->next;
|
||||
}
|
||||
}
|
||||
|
||||
void rc_typed_value_from_memref_value(rc_typed_value_t* value, const rc_memref_value_t* memref) {
|
||||
value->value.u32 = memref->value;
|
||||
|
||||
if (memref->size == RC_MEMSIZE_VARIABLE) {
|
||||
/* a variable can be any of the supported types, but the raw data was copied into u32 */
|
||||
value->type = memref->type;
|
||||
}
|
||||
else {
|
||||
/* not a variable, only u32 is supported */
|
||||
value->type = RC_VALUE_TYPE_UNSIGNED;
|
||||
}
|
||||
}
|
||||
|
||||
void rc_typed_value_convert(rc_typed_value_t* value, char new_type) {
|
||||
switch (new_type) {
|
||||
case RC_VALUE_TYPE_UNSIGNED:
|
||||
switch (value->type) {
|
||||
case RC_VALUE_TYPE_UNSIGNED:
|
||||
return;
|
||||
case RC_VALUE_TYPE_SIGNED:
|
||||
value->value.u32 = (unsigned)value->value.i32;
|
||||
break;
|
||||
case RC_VALUE_TYPE_FLOAT:
|
||||
value->value.u32 = (unsigned)value->value.f32;
|
||||
break;
|
||||
default:
|
||||
value->value.u32 = 0;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case RC_VALUE_TYPE_SIGNED:
|
||||
switch (value->type) {
|
||||
case RC_VALUE_TYPE_SIGNED:
|
||||
return;
|
||||
case RC_VALUE_TYPE_UNSIGNED:
|
||||
value->value.i32 = (int)value->value.u32;
|
||||
break;
|
||||
case RC_VALUE_TYPE_FLOAT:
|
||||
value->value.i32 = (int)value->value.f32;
|
||||
break;
|
||||
default:
|
||||
value->value.i32 = 0;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case RC_VALUE_TYPE_FLOAT:
|
||||
switch (value->type) {
|
||||
case RC_VALUE_TYPE_FLOAT:
|
||||
return;
|
||||
case RC_VALUE_TYPE_UNSIGNED:
|
||||
value->value.f32 = (float)value->value.u32;
|
||||
break;
|
||||
case RC_VALUE_TYPE_SIGNED:
|
||||
value->value.f32 = (float)value->value.i32;
|
||||
break;
|
||||
default:
|
||||
value->value.f32 = 0.0;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
value->type = new_type;
|
||||
}
|
||||
|
||||
static rc_typed_value_t* rc_typed_value_convert_into(rc_typed_value_t* dest, const rc_typed_value_t* source, char new_type) {
|
||||
memcpy(dest, source, sizeof(rc_typed_value_t));
|
||||
rc_typed_value_convert(dest, new_type);
|
||||
return dest;
|
||||
}
|
||||
|
||||
void rc_typed_value_add(rc_typed_value_t* value, const rc_typed_value_t* amount) {
|
||||
rc_typed_value_t converted;
|
||||
|
||||
if (amount->type != value->type && value->type != RC_VALUE_TYPE_NONE)
|
||||
amount = rc_typed_value_convert_into(&converted, amount, value->type);
|
||||
|
||||
switch (value->type)
|
||||
{
|
||||
case RC_VALUE_TYPE_UNSIGNED:
|
||||
value->value.u32 += amount->value.u32;
|
||||
break;
|
||||
|
||||
case RC_VALUE_TYPE_SIGNED:
|
||||
value->value.i32 += amount->value.i32;
|
||||
break;
|
||||
|
||||
case RC_VALUE_TYPE_FLOAT:
|
||||
value->value.f32 += amount->value.f32;
|
||||
break;
|
||||
|
||||
case RC_VALUE_TYPE_NONE:
|
||||
memcpy(value, amount, sizeof(rc_typed_value_t));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void rc_typed_value_multiply(rc_typed_value_t* value, const rc_typed_value_t* amount) {
|
||||
rc_typed_value_t converted;
|
||||
|
||||
switch (value->type)
|
||||
{
|
||||
case RC_VALUE_TYPE_UNSIGNED:
|
||||
switch (amount->type)
|
||||
{
|
||||
case RC_VALUE_TYPE_UNSIGNED:
|
||||
/* the c standard for unsigned multiplication is well defined as non-overflowing truncation
|
||||
* to the type's size. this allows negative multiplication through twos-complements. i.e.
|
||||
* 1 * -1 (0xFFFFFFFF) = 0xFFFFFFFF = -1
|
||||
* 3 * -2 (0xFFFFFFFE) = 0x2FFFFFFFA & 0xFFFFFFFF = 0xFFFFFFFA = -6
|
||||
* 10 * -5 (0xFFFFFFFB) = 0x9FFFFFFCE & 0xFFFFFFFF = 0xFFFFFFCE = -50
|
||||
*/
|
||||
value->value.u32 *= amount->value.u32;
|
||||
break;
|
||||
|
||||
case RC_VALUE_TYPE_SIGNED:
|
||||
value->value.u32 *= (unsigned)amount->value.i32;
|
||||
break;
|
||||
|
||||
case RC_VALUE_TYPE_FLOAT:
|
||||
rc_typed_value_convert(value, RC_VALUE_TYPE_FLOAT);
|
||||
value->value.f32 *= amount->value.f32;
|
||||
break;
|
||||
|
||||
default:
|
||||
value->type = RC_VALUE_TYPE_NONE;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case RC_VALUE_TYPE_SIGNED:
|
||||
switch (amount->type)
|
||||
{
|
||||
case RC_VALUE_TYPE_SIGNED:
|
||||
value->value.i32 *= amount->value.i32;
|
||||
break;
|
||||
|
||||
case RC_VALUE_TYPE_UNSIGNED:
|
||||
value->value.i32 *= (int)amount->value.u32;
|
||||
break;
|
||||
|
||||
case RC_VALUE_TYPE_FLOAT:
|
||||
rc_typed_value_convert(value, RC_VALUE_TYPE_FLOAT);
|
||||
value->value.f32 *= amount->value.f32;
|
||||
break;
|
||||
|
||||
default:
|
||||
value->type = RC_VALUE_TYPE_NONE;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case RC_VALUE_TYPE_FLOAT:
|
||||
if (amount->type == RC_VALUE_TYPE_NONE) {
|
||||
value->type = RC_VALUE_TYPE_NONE;
|
||||
}
|
||||
else {
|
||||
amount = rc_typed_value_convert_into(&converted, amount, RC_VALUE_TYPE_FLOAT);
|
||||
value->value.f32 *= amount->value.f32;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
value->type = RC_VALUE_TYPE_NONE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void rc_typed_value_divide(rc_typed_value_t* value, const rc_typed_value_t* amount) {
|
||||
rc_typed_value_t converted;
|
||||
|
||||
switch (amount->type)
|
||||
{
|
||||
case RC_VALUE_TYPE_UNSIGNED:
|
||||
if (amount->value.u32 == 0) { /* divide by zero */
|
||||
value->type = RC_VALUE_TYPE_NONE;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (value->type) {
|
||||
case RC_VALUE_TYPE_UNSIGNED: /* integer math */
|
||||
value->value.u32 /= amount->value.u32;
|
||||
return;
|
||||
case RC_VALUE_TYPE_SIGNED: /* integer math */
|
||||
value->value.i32 /= (int)amount->value.u32;
|
||||
return;
|
||||
case RC_VALUE_TYPE_FLOAT:
|
||||
amount = rc_typed_value_convert_into(&converted, amount, RC_VALUE_TYPE_FLOAT);
|
||||
break;
|
||||
default:
|
||||
value->type = RC_VALUE_TYPE_NONE;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case RC_VALUE_TYPE_SIGNED:
|
||||
if (amount->value.i32 == 0) { /* divide by zero */
|
||||
value->type = RC_VALUE_TYPE_NONE;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (value->type) {
|
||||
case RC_VALUE_TYPE_SIGNED: /* integer math */
|
||||
value->value.i32 /= amount->value.i32;
|
||||
return;
|
||||
case RC_VALUE_TYPE_UNSIGNED: /* integer math */
|
||||
value->value.u32 /= (unsigned)amount->value.i32;
|
||||
return;
|
||||
case RC_VALUE_TYPE_FLOAT:
|
||||
amount = rc_typed_value_convert_into(&converted, amount, RC_VALUE_TYPE_FLOAT);
|
||||
break;
|
||||
default:
|
||||
value->type = RC_VALUE_TYPE_NONE;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case RC_VALUE_TYPE_FLOAT:
|
||||
break;
|
||||
|
||||
default:
|
||||
value->type = RC_VALUE_TYPE_NONE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (amount->value.f32 == 0.0) { /* divide by zero */
|
||||
value->type = RC_VALUE_TYPE_NONE;
|
||||
return;
|
||||
}
|
||||
|
||||
rc_typed_value_convert(value, RC_VALUE_TYPE_FLOAT);
|
||||
value->value.f32 /= amount->value.f32;
|
||||
}
|
||||
|
||||
static int rc_typed_value_compare_floats(float f1, float f2, char oper) {
|
||||
if (f1 == f2) {
|
||||
/* exactly equal */
|
||||
}
|
||||
else {
|
||||
/* attempt to match 7 significant digits (24-bit mantissa supports just over 7 significant decimal digits) */
|
||||
/* https://stackoverflow.com/questions/17333/what-is-the-most-effective-way-for-float-and-double-comparison */
|
||||
const float abs1 = (f1 < 0) ? -f1 : f1;
|
||||
const float abs2 = (f2 < 0) ? -f2 : f2;
|
||||
const float threshold = ((abs1 < abs2) ? abs1 : abs2) * FLT_EPSILON;
|
||||
const float diff = f1 - f2;
|
||||
const float abs_diff = (diff < 0) ? -diff : diff;
|
||||
|
||||
if (abs_diff <= threshold) {
|
||||
/* approximately equal */
|
||||
}
|
||||
else if (diff > threshold) {
|
||||
/* greater */
|
||||
switch (oper) {
|
||||
case RC_OPERATOR_NE:
|
||||
case RC_OPERATOR_GT:
|
||||
case RC_OPERATOR_GE:
|
||||
return 1;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* lesser */
|
||||
switch (oper) {
|
||||
case RC_OPERATOR_NE:
|
||||
case RC_OPERATOR_LT:
|
||||
case RC_OPERATOR_LE:
|
||||
return 1;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* exactly or approximately equal */
|
||||
switch (oper) {
|
||||
case RC_OPERATOR_EQ:
|
||||
case RC_OPERATOR_GE:
|
||||
case RC_OPERATOR_LE:
|
||||
return 1;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int rc_typed_value_compare(const rc_typed_value_t* value1, const rc_typed_value_t* value2, char oper) {
|
||||
rc_typed_value_t converted_value2;
|
||||
if (value2->type != value1->type)
|
||||
value2 = rc_typed_value_convert_into(&converted_value2, value2, value1->type);
|
||||
|
||||
switch (value1->type) {
|
||||
case RC_VALUE_TYPE_UNSIGNED:
|
||||
switch (oper) {
|
||||
case RC_OPERATOR_EQ: return value1->value.u32 == value2->value.u32;
|
||||
case RC_OPERATOR_NE: return value1->value.u32 != value2->value.u32;
|
||||
case RC_OPERATOR_LT: return value1->value.u32 < value2->value.u32;
|
||||
case RC_OPERATOR_LE: return value1->value.u32 <= value2->value.u32;
|
||||
case RC_OPERATOR_GT: return value1->value.u32 > value2->value.u32;
|
||||
case RC_OPERATOR_GE: return value1->value.u32 >= value2->value.u32;
|
||||
default: return 1;
|
||||
}
|
||||
|
||||
case RC_VALUE_TYPE_SIGNED:
|
||||
switch (oper) {
|
||||
case RC_OPERATOR_EQ: return value1->value.i32 == value2->value.i32;
|
||||
case RC_OPERATOR_NE: return value1->value.i32 != value2->value.i32;
|
||||
case RC_OPERATOR_LT: return value1->value.i32 < value2->value.i32;
|
||||
case RC_OPERATOR_LE: return value1->value.i32 <= value2->value.i32;
|
||||
case RC_OPERATOR_GT: return value1->value.i32 > value2->value.i32;
|
||||
case RC_OPERATOR_GE: return value1->value.i32 >= value2->value.i32;
|
||||
default: return 1;
|
||||
}
|
||||
|
||||
case RC_VALUE_TYPE_FLOAT:
|
||||
return rc_typed_value_compare_floats(value1->value.f32, value2->value.f32, oper);
|
||||
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
Vendored
+852
@@ -0,0 +1,852 @@
|
||||
#include "rc_hash.h"
|
||||
|
||||
#include "../rcheevos/rc_compat.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* internal helper functions in hash.c */
|
||||
extern void* rc_file_open(const char* path);
|
||||
extern void rc_file_seek(void* file_handle, int64_t offset, int origin);
|
||||
extern int64_t rc_file_tell(void* file_handle);
|
||||
extern size_t rc_file_read(void* file_handle, void* buffer, int requested_bytes);
|
||||
extern void rc_file_close(void* file_handle);
|
||||
extern int rc_hash_error(const char* message);
|
||||
extern const char* rc_path_get_filename(const char* path);
|
||||
extern int rc_path_compare_extension(const char* path, const char* ext);
|
||||
extern rc_hash_message_callback verbose_message_callback;
|
||||
|
||||
struct cdrom_t
|
||||
{
|
||||
void* file_handle; /* the file handle for reading the track data */
|
||||
int sector_size; /* the size of each sector in the track data */
|
||||
int sector_header_size; /* the offset to the raw data within a sector block */
|
||||
int64_t file_track_offset;/* the offset of the track data within the file */
|
||||
int track_first_sector; /* the first absolute sector associated to the track (includes pregap) */
|
||||
int track_pregap_sectors; /* the number of pregap sectors */
|
||||
#ifndef NDEBUG
|
||||
uint32_t track_id; /* the index of the track */
|
||||
#endif
|
||||
};
|
||||
|
||||
static int cdreader_get_sector(unsigned char header[16])
|
||||
{
|
||||
int minutes = (header[12] >> 4) * 10 + (header[12] & 0x0F);
|
||||
int seconds = (header[13] >> 4) * 10 + (header[13] & 0x0F);
|
||||
int frames = (header[14] >> 4) * 10 + (header[14] & 0x0F);
|
||||
|
||||
/* convert the MSF value to a sector index, and subtract 150 (2 seconds) per:
|
||||
* For data and mixed mode media (those conforming to ISO/IEC 10149), logical block address
|
||||
* zero shall be assigned to the block at MSF address 00/02/00 */
|
||||
return ((minutes * 60) + seconds) * 75 + frames - 150;
|
||||
}
|
||||
|
||||
static void cdreader_determine_sector_size(struct cdrom_t* cdrom)
|
||||
{
|
||||
/* Attempt to determine the sector and header sizes. The CUE file may be lying.
|
||||
* Look for the sync pattern using each of the supported sector sizes.
|
||||
* Then check for the presence of "CD001", which is gauranteed to be in either the
|
||||
* boot record or primary volume descriptor, one of which is always at sector 16.
|
||||
*/
|
||||
const unsigned char sync_pattern[] = {
|
||||
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00
|
||||
};
|
||||
|
||||
unsigned char header[32];
|
||||
const int64_t toc_sector = 16 + cdrom->track_pregap_sectors;
|
||||
|
||||
cdrom->sector_size = 0;
|
||||
cdrom->sector_header_size = 0;
|
||||
|
||||
rc_file_seek(cdrom->file_handle, toc_sector * 2352 + cdrom->file_track_offset, SEEK_SET);
|
||||
if (rc_file_read(cdrom->file_handle, header, sizeof(header)) < sizeof(header))
|
||||
return;
|
||||
|
||||
if (memcmp(header, sync_pattern, 12) == 0)
|
||||
{
|
||||
cdrom->sector_size = 2352;
|
||||
|
||||
if (memcmp(&header[25], "CD001", 5) == 0)
|
||||
cdrom->sector_header_size = 24;
|
||||
else
|
||||
cdrom->sector_header_size = 16;
|
||||
|
||||
cdrom->track_first_sector = cdreader_get_sector(header) - (int)toc_sector;
|
||||
}
|
||||
else
|
||||
{
|
||||
rc_file_seek(cdrom->file_handle, toc_sector * 2336 + cdrom->file_track_offset, SEEK_SET);
|
||||
rc_file_read(cdrom->file_handle, header, sizeof(header));
|
||||
|
||||
if (memcmp(header, sync_pattern, 12) == 0)
|
||||
{
|
||||
cdrom->sector_size = 2336;
|
||||
|
||||
if (memcmp(&header[25], "CD001", 5) == 0)
|
||||
cdrom->sector_header_size = 24;
|
||||
else
|
||||
cdrom->sector_header_size = 16;
|
||||
|
||||
cdrom->track_first_sector = cdreader_get_sector(header) - (int)toc_sector;
|
||||
}
|
||||
else
|
||||
{
|
||||
rc_file_seek(cdrom->file_handle, toc_sector * 2048 + cdrom->file_track_offset, SEEK_SET);
|
||||
rc_file_read(cdrom->file_handle, header, sizeof(header));
|
||||
|
||||
if (memcmp(&header[1], "CD001", 5) == 0)
|
||||
{
|
||||
cdrom->sector_size = 2048;
|
||||
cdrom->sector_header_size = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void* cdreader_open_bin_track(const char* path, uint32_t track)
|
||||
{
|
||||
void* file_handle;
|
||||
struct cdrom_t* cdrom;
|
||||
|
||||
if (track > 1)
|
||||
{
|
||||
if (verbose_message_callback)
|
||||
verbose_message_callback("Cannot locate secondary tracks without a cue sheet");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
file_handle = rc_file_open(path);
|
||||
if (!file_handle)
|
||||
return NULL;
|
||||
|
||||
cdrom = (struct cdrom_t*)calloc(1, sizeof(*cdrom));
|
||||
cdrom->file_handle = file_handle;
|
||||
#ifndef NDEBUG
|
||||
cdrom->track_id = track;
|
||||
#endif
|
||||
|
||||
cdreader_determine_sector_size(cdrom);
|
||||
|
||||
if (cdrom->sector_size == 0)
|
||||
{
|
||||
int64_t size;
|
||||
|
||||
rc_file_seek(cdrom->file_handle, 0, SEEK_END);
|
||||
size = rc_file_tell(cdrom->file_handle);
|
||||
|
||||
if ((size % 2352) == 0)
|
||||
{
|
||||
/* raw tracks use all 2352 bytes and have a 24 byte header */
|
||||
cdrom->sector_size = 2352;
|
||||
cdrom->sector_header_size = 24;
|
||||
}
|
||||
else if ((size % 2048) == 0)
|
||||
{
|
||||
/* cooked tracks eliminate all header/footer data */
|
||||
cdrom->sector_size = 2048;
|
||||
cdrom->sector_header_size = 0;
|
||||
}
|
||||
else if ((size % 2336) == 0)
|
||||
{
|
||||
/* MODE 2 format without 16-byte sync data */
|
||||
cdrom->sector_size = 2336;
|
||||
cdrom->sector_header_size = 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
free(cdrom);
|
||||
|
||||
if (verbose_message_callback)
|
||||
verbose_message_callback("Could not determine sector size");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return cdrom;
|
||||
}
|
||||
|
||||
static int cdreader_open_bin(struct cdrom_t* cdrom, const char* path, const char* mode)
|
||||
{
|
||||
cdrom->file_handle = rc_file_open(path);
|
||||
if (!cdrom->file_handle)
|
||||
return 0;
|
||||
|
||||
/* determine sector size */
|
||||
cdreader_determine_sector_size(cdrom);
|
||||
|
||||
/* could not determine, which means we'll probably have more issues later
|
||||
* but use the CUE provided information anyway
|
||||
*/
|
||||
if (cdrom->sector_size == 0)
|
||||
{
|
||||
/* All of these modes have 2048 byte payloads. In MODE1/2352 and MODE2/2352
|
||||
* modes, the mode can actually be specified per sector to change the payload
|
||||
* size, but that reduces the ability to recover from errors when the disc
|
||||
* is damaged, so it's seldomly used, and when it is, it's mostly for audio
|
||||
* or video data where a blip or two probably won't be noticed by the user.
|
||||
* So, while we techincally support all of the following modes, we only do
|
||||
* so with 2048 byte payloads.
|
||||
* http://totalsonicmastering.com/cuesheetsyntax.htm
|
||||
* MODE1/2048 ? CDROM Mode1 Data (cooked) [no header, no footer]
|
||||
* MODE1/2352 ? CDROM Mode1 Data (raw) [16 byte header, 288 byte footer]
|
||||
* MODE2/2336 ? CDROM-XA Mode2 Data [8 byte header, 280 byte footer]
|
||||
* MODE2/2352 ? CDROM-XA Mode2 Data [24 byte header, 280 byte footer]
|
||||
*/
|
||||
if (memcmp(mode, "MODE2/2352", 10) == 0)
|
||||
{
|
||||
cdrom->sector_size = 2352;
|
||||
cdrom->sector_header_size = 24;
|
||||
}
|
||||
else if (memcmp(mode, "MODE1/2048", 10) == 0)
|
||||
{
|
||||
cdrom->sector_size = 2048;
|
||||
cdrom->sector_header_size = 0;
|
||||
}
|
||||
else if (memcmp(mode, "MODE2/2336", 10) == 0)
|
||||
{
|
||||
cdrom->sector_size = 2336;
|
||||
cdrom->sector_header_size = 8;
|
||||
}
|
||||
else if (memcmp(mode, "MODE1/2352", 10) == 0)
|
||||
{
|
||||
cdrom->sector_size = 2352;
|
||||
cdrom->sector_header_size = 16;
|
||||
}
|
||||
else if (memcmp(mode, "AUDIO", 5) == 0)
|
||||
{
|
||||
cdrom->sector_size = 2352;
|
||||
cdrom->sector_header_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return (cdrom->sector_size != 0);
|
||||
}
|
||||
|
||||
static char* cdreader_get_bin_path(const char* cue_path, const char* bin_name)
|
||||
{
|
||||
const char* filename = rc_path_get_filename(cue_path);
|
||||
const size_t bin_name_len = strlen(bin_name);
|
||||
const size_t cue_path_len = filename - cue_path;
|
||||
const size_t needed = cue_path_len + bin_name_len + 1;
|
||||
|
||||
char* bin_filename = (char*)malloc(needed);
|
||||
if (!bin_filename)
|
||||
{
|
||||
char buffer[64];
|
||||
snprintf(buffer, sizeof(buffer), "Failed to allocate %u bytes", (unsigned)needed);
|
||||
rc_hash_error((const char*)buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(bin_filename, cue_path, cue_path_len);
|
||||
memcpy(bin_filename + cue_path_len, bin_name, bin_name_len + 1);
|
||||
}
|
||||
|
||||
return bin_filename;
|
||||
}
|
||||
|
||||
static int64_t cdreader_get_bin_size(const char* cue_path, const char* bin_name)
|
||||
{
|
||||
int64_t size = 0;
|
||||
char* bin_filename = cdreader_get_bin_path(cue_path, bin_name);
|
||||
if (bin_filename)
|
||||
{
|
||||
/* disable verbose messaging while getting file size */
|
||||
rc_hash_message_callback old_verbose_message_callback = verbose_message_callback;
|
||||
void* file_handle;
|
||||
verbose_message_callback = NULL;
|
||||
|
||||
file_handle = rc_file_open(bin_filename);
|
||||
if (file_handle)
|
||||
{
|
||||
rc_file_seek(file_handle, 0, SEEK_END);
|
||||
size = rc_file_tell(file_handle);
|
||||
rc_file_close(file_handle);
|
||||
}
|
||||
|
||||
verbose_message_callback = old_verbose_message_callback;
|
||||
free(bin_filename);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static void* cdreader_open_cue_track(const char* path, uint32_t track)
|
||||
{
|
||||
void* cue_handle;
|
||||
int64_t cue_offset = 0;
|
||||
char buffer[1024];
|
||||
char* bin_filename = NULL;
|
||||
char *ptr, *ptr2, *end;
|
||||
int done = 0;
|
||||
size_t num_read = 0;
|
||||
struct cdrom_t* cdrom = NULL;
|
||||
|
||||
struct track_t
|
||||
{
|
||||
uint32_t id;
|
||||
int sector_size;
|
||||
int sector_count;
|
||||
int first_sector;
|
||||
int pregap_sectors;
|
||||
int is_data;
|
||||
int file_track_offset;
|
||||
int file_first_sector;
|
||||
char mode[16];
|
||||
char filename[256];
|
||||
} current_track, previous_track, largest_track;
|
||||
|
||||
cue_handle = rc_file_open(path);
|
||||
if (!cue_handle)
|
||||
return NULL;
|
||||
|
||||
memset(¤t_track, 0, sizeof(current_track));
|
||||
memset(&previous_track, 0, sizeof(previous_track));
|
||||
memset(&largest_track, 0, sizeof(largest_track));
|
||||
|
||||
do
|
||||
{
|
||||
num_read = rc_file_read(cue_handle, buffer, sizeof(buffer) - 1);
|
||||
if (num_read == 0)
|
||||
break;
|
||||
|
||||
buffer[num_read] = 0;
|
||||
if (num_read == sizeof(buffer) - 1)
|
||||
end = buffer + sizeof(buffer) * 3 / 4;
|
||||
else
|
||||
end = buffer + num_read;
|
||||
|
||||
for (ptr = buffer; ptr < end; ++ptr)
|
||||
{
|
||||
while (*ptr == ' ')
|
||||
++ptr;
|
||||
|
||||
if (strncasecmp(ptr, "INDEX ", 6) == 0)
|
||||
{
|
||||
int m = 0, s = 0, f = 0;
|
||||
int index;
|
||||
int sector_offset;
|
||||
|
||||
ptr += 6;
|
||||
index = atoi(ptr);
|
||||
|
||||
while (*ptr != ' ' && *ptr != '\n')
|
||||
++ptr;
|
||||
while (*ptr == ' ')
|
||||
++ptr;
|
||||
|
||||
/* convert mm:ss:ff to sector count */
|
||||
sscanf(ptr, "%d:%d:%d", &m, &s, &f);
|
||||
sector_offset = ((m * 60) + s) * 75 + f;
|
||||
|
||||
if (current_track.first_sector == -1)
|
||||
{
|
||||
current_track.first_sector = sector_offset;
|
||||
if (strcmp(current_track.filename, previous_track.filename) == 0)
|
||||
{
|
||||
previous_track.sector_count = current_track.first_sector - previous_track.first_sector;
|
||||
current_track.file_track_offset += previous_track.sector_count * previous_track.sector_size;
|
||||
}
|
||||
|
||||
/* if looking for the largest data track, determine previous track size */
|
||||
if (track == RC_HASH_CDTRACK_LARGEST && previous_track.sector_count > largest_track.sector_count &&
|
||||
previous_track.is_data)
|
||||
{
|
||||
memcpy(&largest_track, &previous_track, sizeof(largest_track));
|
||||
}
|
||||
}
|
||||
|
||||
if (index == 1)
|
||||
{
|
||||
current_track.pregap_sectors = (sector_offset - current_track.first_sector);
|
||||
|
||||
if (verbose_message_callback)
|
||||
{
|
||||
char message[128];
|
||||
char* scan = current_track.mode;
|
||||
while (*scan && !isspace((unsigned char)*scan))
|
||||
++scan;
|
||||
*scan = '\0';
|
||||
|
||||
/* it's undesirable to truncate offset to 32-bits, but %lld isn't defined in c89. */
|
||||
snprintf(message, sizeof(message), "Found %s track %d (first sector %d, sector size %d, %d pregap sectors)",
|
||||
current_track.mode, current_track.id, current_track.first_sector, current_track.sector_size, current_track.pregap_sectors);
|
||||
verbose_message_callback(message);
|
||||
}
|
||||
|
||||
if (current_track.id == track)
|
||||
{
|
||||
done = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (track == RC_HASH_CDTRACK_FIRST_DATA && current_track.is_data)
|
||||
{
|
||||
track = current_track.id;
|
||||
done = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (strncasecmp(ptr, "TRACK ", 6) == 0)
|
||||
{
|
||||
if (current_track.sector_size)
|
||||
memcpy(&previous_track, ¤t_track, sizeof(current_track));
|
||||
|
||||
ptr += 6;
|
||||
current_track.id = atoi(ptr);
|
||||
|
||||
current_track.pregap_sectors = -1;
|
||||
current_track.first_sector = -1;
|
||||
|
||||
while (*ptr != ' ')
|
||||
++ptr;
|
||||
while (*ptr == ' ')
|
||||
++ptr;
|
||||
memcpy(current_track.mode, ptr, sizeof(current_track.mode));
|
||||
current_track.is_data = (memcmp(current_track.mode, "MODE", 4) == 0);
|
||||
|
||||
if (current_track.is_data)
|
||||
{
|
||||
current_track.sector_size = atoi(ptr + 6);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* assume AUDIO */
|
||||
current_track.sector_size = 2352;
|
||||
}
|
||||
}
|
||||
else if (strncasecmp(ptr, "FILE ", 5) == 0)
|
||||
{
|
||||
if (current_track.sector_size)
|
||||
{
|
||||
memcpy(&previous_track, ¤t_track, sizeof(previous_track));
|
||||
|
||||
if (previous_track.sector_count == 0)
|
||||
{
|
||||
const uint32_t file_sector_count = (uint32_t)cdreader_get_bin_size(path, previous_track.filename) / previous_track.sector_size;
|
||||
previous_track.sector_count = file_sector_count - previous_track.first_sector;
|
||||
}
|
||||
|
||||
/* if looking for the largest data track, check to see if this one is larger */
|
||||
if (track == RC_HASH_CDTRACK_LARGEST && previous_track.is_data &&
|
||||
previous_track.sector_count > largest_track.sector_count)
|
||||
{
|
||||
memcpy(&largest_track, &previous_track, sizeof(largest_track));
|
||||
}
|
||||
}
|
||||
|
||||
memset(¤t_track, 0, sizeof(current_track));
|
||||
|
||||
current_track.file_first_sector = previous_track.file_first_sector +
|
||||
previous_track.first_sector + previous_track.sector_count;
|
||||
|
||||
ptr += 5;
|
||||
ptr2 = ptr;
|
||||
if (*ptr == '"')
|
||||
{
|
||||
++ptr;
|
||||
do
|
||||
{
|
||||
++ptr2;
|
||||
} while (*ptr2 && *ptr2 != '\n' && *ptr2 != '"');
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
++ptr2;
|
||||
} while (*ptr2 && *ptr2 != '\n' && *ptr2 != ' ');
|
||||
}
|
||||
|
||||
if (ptr2 - ptr < (int)sizeof(current_track.filename))
|
||||
memcpy(current_track.filename, ptr, ptr2 - ptr);
|
||||
}
|
||||
|
||||
while (*ptr && *ptr != '\n')
|
||||
++ptr;
|
||||
}
|
||||
|
||||
if (done)
|
||||
break;
|
||||
|
||||
cue_offset += (ptr - buffer);
|
||||
rc_file_seek(cue_handle, cue_offset, SEEK_SET);
|
||||
|
||||
} while (1);
|
||||
|
||||
rc_file_close(cue_handle);
|
||||
|
||||
if (track == RC_HASH_CDTRACK_LARGEST)
|
||||
{
|
||||
if (current_track.sector_size && current_track.is_data)
|
||||
{
|
||||
const uint32_t file_sector_count = (uint32_t)cdreader_get_bin_size(path, current_track.filename) / current_track.sector_size;
|
||||
current_track.sector_count = file_sector_count - current_track.first_sector;
|
||||
|
||||
if (largest_track.sector_count > current_track.sector_count)
|
||||
memcpy(¤t_track, &largest_track, sizeof(current_track));
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(¤t_track, &largest_track, sizeof(current_track));
|
||||
}
|
||||
|
||||
track = current_track.id;
|
||||
}
|
||||
else if (track == RC_HASH_CDTRACK_LAST && !done)
|
||||
{
|
||||
track = current_track.id;
|
||||
}
|
||||
|
||||
if (current_track.id == track)
|
||||
{
|
||||
cdrom = (struct cdrom_t*)calloc(1, sizeof(*cdrom));
|
||||
if (!cdrom)
|
||||
{
|
||||
snprintf((char*)buffer, sizeof(buffer), "Failed to allocate %u bytes", (unsigned)sizeof(*cdrom));
|
||||
rc_hash_error((const char*)buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cdrom->file_track_offset = current_track.file_track_offset;
|
||||
cdrom->track_pregap_sectors = current_track.pregap_sectors;
|
||||
cdrom->track_first_sector = current_track.file_first_sector + current_track.first_sector;
|
||||
#ifndef NDEBUG
|
||||
cdrom->track_id = current_track.id;
|
||||
#endif
|
||||
|
||||
/* verify existance of bin file */
|
||||
bin_filename = cdreader_get_bin_path(path, current_track.filename);
|
||||
if (bin_filename)
|
||||
{
|
||||
if (cdreader_open_bin(cdrom, bin_filename, current_track.mode))
|
||||
{
|
||||
if (verbose_message_callback)
|
||||
{
|
||||
if (cdrom->track_pregap_sectors)
|
||||
snprintf((char*)buffer, sizeof(buffer), "Opened track %d (sector size %d, %d pregap sectors)",
|
||||
track, cdrom->sector_size, cdrom->track_pregap_sectors);
|
||||
else
|
||||
snprintf((char*)buffer, sizeof(buffer), "Opened track %d (sector size %d)", track, cdrom->sector_size);
|
||||
|
||||
verbose_message_callback((const char*)buffer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cdrom->file_handle)
|
||||
{
|
||||
rc_file_close(cdrom->file_handle);
|
||||
snprintf((char*)buffer, sizeof(buffer), "Could not determine sector size for %s track", current_track.mode);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf((char*)buffer, sizeof(buffer), "Could not open %s", bin_filename);
|
||||
}
|
||||
|
||||
rc_hash_error((const char*)buffer);
|
||||
|
||||
free(cdrom);
|
||||
cdrom = NULL;
|
||||
}
|
||||
|
||||
free(bin_filename);
|
||||
}
|
||||
}
|
||||
|
||||
return cdrom;
|
||||
}
|
||||
|
||||
static void* cdreader_open_gdi_track(const char* path, uint32_t track)
|
||||
{
|
||||
void* file_handle;
|
||||
char buffer[1024];
|
||||
char mode[16] = "MODE1/";
|
||||
char sector_size[16];
|
||||
char file[256];
|
||||
int64_t track_size;
|
||||
int track_type;
|
||||
char* bin_path = "";
|
||||
uint32_t current_track = 0;
|
||||
char* ptr, *ptr2, *end;
|
||||
int lba = 0;
|
||||
|
||||
uint32_t largest_track = 0;
|
||||
int64_t largest_track_size = 0;
|
||||
char largest_track_file[256];
|
||||
char largest_track_sector_size[16];
|
||||
int largest_track_lba = 0;
|
||||
|
||||
int found = 0;
|
||||
size_t num_read = 0;
|
||||
int64_t file_offset = 0;
|
||||
struct cdrom_t* cdrom = NULL;
|
||||
|
||||
file_handle = rc_file_open(path);
|
||||
if (!file_handle)
|
||||
return NULL;
|
||||
|
||||
file[0] = '\0';
|
||||
do
|
||||
{
|
||||
num_read = rc_file_read(file_handle, buffer, sizeof(buffer) - 1);
|
||||
if (num_read == 0)
|
||||
break;
|
||||
|
||||
buffer[num_read] = 0;
|
||||
if (num_read == sizeof(buffer) - 1)
|
||||
end = buffer + sizeof(buffer) * 3 / 4;
|
||||
else
|
||||
end = buffer + num_read;
|
||||
|
||||
ptr = buffer;
|
||||
|
||||
/* the first line contains the number of tracks, so we can get the last track index from it */
|
||||
if (track == RC_HASH_CDTRACK_LAST)
|
||||
track = atoi(ptr);
|
||||
|
||||
/* first line contains the number of tracks and will be skipped */
|
||||
while (ptr < end)
|
||||
{
|
||||
/* skip until next newline */
|
||||
while (*ptr != '\n' && ptr < end)
|
||||
++ptr;
|
||||
|
||||
/* skip newlines */
|
||||
while ((*ptr == '\n' || *ptr == '\r') && ptr < end)
|
||||
++ptr;
|
||||
|
||||
/* line format: [trackid] [lba] [type] [sectorsize] [file] [?] */
|
||||
while (isspace((unsigned char)*ptr))
|
||||
++ptr;
|
||||
|
||||
current_track = (uint32_t)atoi(ptr);
|
||||
if (track && current_track != track && track != RC_HASH_CDTRACK_FIRST_DATA)
|
||||
continue;
|
||||
|
||||
while (isdigit((unsigned char)*ptr))
|
||||
++ptr;
|
||||
++ptr;
|
||||
|
||||
while (isspace((unsigned char)*ptr))
|
||||
++ptr;
|
||||
|
||||
lba = atoi(ptr);
|
||||
while (isdigit((unsigned char)*ptr))
|
||||
++ptr;
|
||||
++ptr;
|
||||
|
||||
while (isspace((unsigned char)*ptr))
|
||||
++ptr;
|
||||
|
||||
track_type = atoi(ptr);
|
||||
while (isdigit((unsigned char)*ptr))
|
||||
++ptr;
|
||||
++ptr;
|
||||
|
||||
while (isspace((unsigned char)*ptr))
|
||||
++ptr;
|
||||
|
||||
ptr2 = sector_size;
|
||||
while (isdigit((unsigned char)*ptr))
|
||||
*ptr2++ = *ptr++;
|
||||
*ptr2 = '\0';
|
||||
++ptr;
|
||||
|
||||
while (isspace((unsigned char)*ptr))
|
||||
++ptr;
|
||||
|
||||
ptr2 = file;
|
||||
if (*ptr == '\"')
|
||||
{
|
||||
++ptr;
|
||||
while (*ptr != '\"')
|
||||
*ptr2++ = *ptr++;
|
||||
++ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (*ptr != ' ')
|
||||
*ptr2++ = *ptr++;
|
||||
}
|
||||
*ptr2 = '\0';
|
||||
|
||||
if (track == current_track)
|
||||
{
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
else if (track == RC_HASH_CDTRACK_FIRST_DATA && track_type == 4)
|
||||
{
|
||||
track = current_track;
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
else if (track == RC_HASH_CDTRACK_LARGEST && track_type == 4)
|
||||
{
|
||||
track_size = cdreader_get_bin_size(path, file);
|
||||
if (track_size > largest_track_size)
|
||||
{
|
||||
largest_track_size = track_size;
|
||||
largest_track = current_track;
|
||||
largest_track_lba = lba;
|
||||
strcpy(largest_track_file, file);
|
||||
strcpy(largest_track_sector_size, sector_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
break;
|
||||
|
||||
file_offset += (ptr - buffer);
|
||||
rc_file_seek(file_handle, file_offset, SEEK_SET);
|
||||
|
||||
} while (1);
|
||||
|
||||
rc_file_close(file_handle);
|
||||
|
||||
cdrom = (struct cdrom_t*)calloc(1, sizeof(*cdrom));
|
||||
if (!cdrom)
|
||||
{
|
||||
snprintf((char*)buffer, sizeof(buffer), "Failed to allocate %u bytes", (unsigned)sizeof(*cdrom));
|
||||
rc_hash_error((const char*)buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* if we were tracking the largest track, make it the current track.
|
||||
* otherwise, current_track will be the requested track, or last track. */
|
||||
if (largest_track != 0 && largest_track != current_track)
|
||||
{
|
||||
current_track = largest_track;
|
||||
strcpy(file, largest_track_file);
|
||||
strcpy(sector_size, largest_track_sector_size);
|
||||
lba = largest_track_lba;
|
||||
}
|
||||
|
||||
/* open the bin file for the track - construct mode parameter from sector_size */
|
||||
ptr = &mode[6];
|
||||
ptr2 = sector_size;
|
||||
while (*ptr2 && *ptr2 != '\"')
|
||||
*ptr++ = *ptr2++;
|
||||
*ptr = '\0';
|
||||
|
||||
bin_path = cdreader_get_bin_path(path, file);
|
||||
if (cdreader_open_bin(cdrom, bin_path, mode))
|
||||
{
|
||||
cdrom->track_pregap_sectors = 0;
|
||||
cdrom->track_first_sector = lba;
|
||||
#ifndef NDEBUG
|
||||
cdrom->track_id = current_track;
|
||||
#endif
|
||||
|
||||
if (verbose_message_callback)
|
||||
{
|
||||
snprintf((char*)buffer, sizeof(buffer), "Opened track %d (sector size %d)", current_track, cdrom->sector_size);
|
||||
verbose_message_callback((const char*)buffer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf((char*)buffer, sizeof(buffer), "Could not open %s", bin_path);
|
||||
rc_hash_error((const char*)buffer);
|
||||
|
||||
free(cdrom);
|
||||
cdrom = NULL;
|
||||
}
|
||||
|
||||
free(bin_path);
|
||||
|
||||
return cdrom;
|
||||
}
|
||||
|
||||
static void* cdreader_open_track(const char* path, uint32_t track)
|
||||
{
|
||||
/* backwards compatibility - 0 used to mean largest */
|
||||
if (track == 0)
|
||||
track = RC_HASH_CDTRACK_LARGEST;
|
||||
|
||||
if (rc_path_compare_extension(path, "cue"))
|
||||
return cdreader_open_cue_track(path, track);
|
||||
if (rc_path_compare_extension(path, "gdi"))
|
||||
return cdreader_open_gdi_track(path, track);
|
||||
|
||||
return cdreader_open_bin_track(path, track);
|
||||
}
|
||||
|
||||
static size_t cdreader_read_sector(void* track_handle, uint32_t sector, void* buffer, size_t requested_bytes)
|
||||
{
|
||||
int64_t sector_start;
|
||||
size_t num_read, total_read = 0;
|
||||
uint8_t* buffer_ptr = (uint8_t*)buffer;
|
||||
|
||||
struct cdrom_t* cdrom = (struct cdrom_t*)track_handle;
|
||||
if (!cdrom)
|
||||
return 0;
|
||||
|
||||
if (sector < (uint32_t)cdrom->track_first_sector)
|
||||
return 0;
|
||||
|
||||
sector_start = (int64_t)(sector - cdrom->track_first_sector) * cdrom->sector_size +
|
||||
cdrom->sector_header_size + cdrom->file_track_offset;
|
||||
|
||||
while (requested_bytes > 2048)
|
||||
{
|
||||
rc_file_seek(cdrom->file_handle, sector_start, SEEK_SET);
|
||||
num_read = rc_file_read(cdrom->file_handle, buffer_ptr, 2048);
|
||||
total_read += num_read;
|
||||
|
||||
if (num_read < 2048)
|
||||
return total_read;
|
||||
|
||||
buffer_ptr += 2048;
|
||||
sector_start += cdrom->sector_size;
|
||||
requested_bytes -= 2048;
|
||||
}
|
||||
|
||||
rc_file_seek(cdrom->file_handle, sector_start, SEEK_SET);
|
||||
num_read = rc_file_read(cdrom->file_handle, buffer_ptr, (int)requested_bytes);
|
||||
total_read += num_read;
|
||||
|
||||
return total_read;
|
||||
}
|
||||
|
||||
static void cdreader_close_track(void* track_handle)
|
||||
{
|
||||
struct cdrom_t* cdrom = (struct cdrom_t*)track_handle;
|
||||
if (cdrom)
|
||||
{
|
||||
if (cdrom->file_handle)
|
||||
rc_file_close(cdrom->file_handle);
|
||||
|
||||
free(track_handle);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t cdreader_first_track_sector(void* track_handle)
|
||||
{
|
||||
struct cdrom_t* cdrom = (struct cdrom_t*)track_handle;
|
||||
if (cdrom)
|
||||
return cdrom->track_first_sector + cdrom->track_pregap_sectors;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rc_hash_get_default_cdreader(struct rc_hash_cdreader* cdreader)
|
||||
{
|
||||
cdreader->open_track = cdreader_open_track;
|
||||
cdreader->read_sector = cdreader_read_sector;
|
||||
cdreader->close_track = cdreader_close_track;
|
||||
cdreader->first_track_sector = cdreader_first_track_sector;
|
||||
}
|
||||
|
||||
void rc_hash_init_default_cdreader()
|
||||
{
|
||||
struct rc_hash_cdreader cdreader;
|
||||
rc_hash_get_default_cdreader(&cdreader);
|
||||
rc_hash_init_custom_cdreader(&cdreader);
|
||||
}
|
||||
Vendored
+131
-62
@@ -83,6 +83,12 @@ static void filereader_close(void* file_handle)
|
||||
fclose((FILE*)file_handle);
|
||||
}
|
||||
|
||||
/* for unit tests - normally would call rc_hash_init_custom_filereader(NULL) */
|
||||
void rc_hash_reset_filereader(void)
|
||||
{
|
||||
filereader = NULL;
|
||||
}
|
||||
|
||||
void rc_hash_init_custom_filereader(struct rc_hash_filereader* reader)
|
||||
{
|
||||
/* initialize with defaults first */
|
||||
@@ -193,13 +199,13 @@ static size_t rc_cd_read_sector(void* track_handle, uint32_t sector, void* buffe
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t rc_cd_absolute_sector_to_track_sector(void* track_handle, uint32_t sector)
|
||||
static uint32_t rc_cd_first_track_sector(void* track_handle)
|
||||
{
|
||||
if (cdreader && cdreader->absolute_sector_to_track_sector)
|
||||
return cdreader->absolute_sector_to_track_sector(track_handle, sector);
|
||||
if (cdreader && cdreader->first_track_sector)
|
||||
return cdreader->first_track_sector(track_handle);
|
||||
|
||||
rc_hash_error("no hook registered for cdreader_absolute_sector_to_track_sector");
|
||||
return sector;
|
||||
rc_hash_error("no hook registered for cdreader_first_track_sector");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rc_cd_close_track(void* track_handle)
|
||||
@@ -242,7 +248,7 @@ static uint32_t rc_cd_find_file_sector(void* track_handle, const char* path, uns
|
||||
else
|
||||
{
|
||||
/* find the cd information */
|
||||
if (!rc_cd_read_sector(track_handle, 16, buffer, 256))
|
||||
if (!rc_cd_read_sector(track_handle, rc_cd_first_track_sector(track_handle) + 16, buffer, 256))
|
||||
return 0;
|
||||
|
||||
/* the directory_record starts at 156, the sector containing the table of contents is 2 bytes into that.
|
||||
@@ -252,7 +258,6 @@ static uint32_t rc_cd_find_file_sector(void* track_handle, const char* path, uns
|
||||
}
|
||||
|
||||
/* fetch and process the directory record */
|
||||
sector = rc_cd_absolute_sector_to_track_sector(track_handle, sector);
|
||||
if (!rc_cd_read_sector(track_handle, sector, buffer, sizeof(buffer)))
|
||||
return 0;
|
||||
|
||||
@@ -405,7 +410,7 @@ static int rc_hash_cd_file(md5_state_t* md5, void* track_handle, uint32_t sector
|
||||
if (name)
|
||||
snprintf(message, sizeof(message), "Hashing %s title (%u bytes) and contents (%u bytes) ", name, (unsigned)strlen(name), size);
|
||||
else
|
||||
snprintf(message, sizeof(message), "Hashing %s contents (%u bytes)", description, size);
|
||||
snprintf(message, sizeof(message), "Hashing %s contents (%u bytes @ sector %u)", description, size, sector);
|
||||
|
||||
verbose_message_callback(message);
|
||||
}
|
||||
@@ -648,6 +653,37 @@ static int rc_hash_arcade(char hash[33], const char* path)
|
||||
return rc_hash_buffer(hash, (uint8_t*)filename, filename_length);
|
||||
}
|
||||
|
||||
static int rc_hash_text(char hash[33], const uint8_t* buffer, size_t buffer_size)
|
||||
{
|
||||
md5_state_t md5;
|
||||
const uint8_t* scan = buffer;
|
||||
const uint8_t* stop = buffer + buffer_size;
|
||||
|
||||
md5_init(&md5);
|
||||
|
||||
do {
|
||||
/* find end of line */
|
||||
while (scan < stop && *scan != '\r' && *scan != '\n')
|
||||
++scan;
|
||||
|
||||
md5_append(&md5, buffer, (int)(scan - buffer));
|
||||
|
||||
/* include a normalized line ending */
|
||||
/* NOTE: this causes a line ending to be hashed at the end of the file, even if one was not present */
|
||||
md5_append(&md5, (const uint8_t*)"\n", 1);
|
||||
|
||||
/* skip newline */
|
||||
if (scan < stop && *scan == '\r')
|
||||
++scan;
|
||||
if (scan < stop && *scan == '\n')
|
||||
++scan;
|
||||
|
||||
buffer = scan;
|
||||
} while (scan < stop);
|
||||
|
||||
return rc_hash_finalize(&md5, hash);
|
||||
}
|
||||
|
||||
static int rc_hash_lynx(char hash[33], const uint8_t* buffer, size_t buffer_size)
|
||||
{
|
||||
/* if the file contains a header, ignore it */
|
||||
@@ -947,7 +983,7 @@ static int rc_hash_pce_track(char hash[33], void* track_handle)
|
||||
* the string "PC Engine CD-ROM SYSTEM" should exist at 32 bytes into the sector
|
||||
* http://shu.sheldows.com/shu/download/pcedocs/pce_cdrom.html
|
||||
*/
|
||||
if (rc_cd_read_sector(track_handle, 1, buffer, 128) < 128)
|
||||
if (rc_cd_read_sector(track_handle, rc_cd_first_track_sector(track_handle) + 1, buffer, 128) < 128)
|
||||
{
|
||||
return rc_hash_error("Not a PC Engine CD");
|
||||
}
|
||||
@@ -980,6 +1016,7 @@ static int rc_hash_pce_track(char hash[33], void* track_handle)
|
||||
verbose_message_callback(message);
|
||||
}
|
||||
|
||||
sector += rc_cd_first_track_sector(track_handle);
|
||||
while (num_sectors > 0)
|
||||
{
|
||||
rc_cd_read_sector(track_handle, sector, buffer, sizeof(buffer));
|
||||
@@ -1043,7 +1080,8 @@ static int rc_hash_pcfx_cd(char hash[33], const char* path)
|
||||
return rc_hash_error("Could not open track");
|
||||
|
||||
/* PC-FX CD will have a header marker in sector 0 */
|
||||
rc_cd_read_sector(track_handle, 0, buffer, 32);
|
||||
sector = rc_cd_first_track_sector(track_handle);
|
||||
rc_cd_read_sector(track_handle, sector, buffer, 32);
|
||||
if (memcmp("PC-FX:Hu_CD-ROM", &buffer[0], 15) != 0)
|
||||
{
|
||||
rc_cd_close_track(track_handle);
|
||||
@@ -1053,7 +1091,8 @@ static int rc_hash_pcfx_cd(char hash[33], const char* path)
|
||||
if (!track_handle)
|
||||
return rc_hash_error("Could not open track");
|
||||
|
||||
rc_cd_read_sector(track_handle, 0, buffer, 32);
|
||||
sector = rc_cd_first_track_sector(track_handle);
|
||||
rc_cd_read_sector(track_handle, sector, buffer, 32);
|
||||
}
|
||||
|
||||
if (memcmp("PC-FX:Hu_CD-ROM", &buffer[0], 15) == 0)
|
||||
@@ -1061,7 +1100,7 @@ static int rc_hash_pcfx_cd(char hash[33], const char* path)
|
||||
/* PC-FX boot header fills the first two sectors of the disc
|
||||
* https://bitbucket.org/trap15/pcfxtools/src/master/pcfx-cdlink.c
|
||||
* the important stuff is the first 128 bytes of the second sector (title being the first 32) */
|
||||
rc_cd_read_sector(track_handle, 1, buffer, 128);
|
||||
rc_cd_read_sector(track_handle, sector + 1, buffer, 128);
|
||||
|
||||
md5_init(&md5);
|
||||
md5_append(&md5, buffer, 128);
|
||||
@@ -1087,6 +1126,7 @@ static int rc_hash_pcfx_cd(char hash[33], const char* path)
|
||||
verbose_message_callback(message);
|
||||
}
|
||||
|
||||
sector += rc_cd_first_track_sector(track_handle);
|
||||
while (num_sectors > 0)
|
||||
{
|
||||
rc_cd_read_sector(track_handle, sector, buffer, sizeof(buffer));
|
||||
@@ -1099,7 +1139,7 @@ static int rc_hash_pcfx_cd(char hash[33], const char* path)
|
||||
else
|
||||
{
|
||||
int result = 0;
|
||||
rc_cd_read_sector(track_handle, 1, buffer, 128);
|
||||
rc_cd_read_sector(track_handle, sector + 1, buffer, 128);
|
||||
|
||||
/* some PC-FX CDs still identify as PCE CDs */
|
||||
if (memcmp("PC Engine CD-ROM SYSTEM", &buffer[32], 23) == 0)
|
||||
@@ -1119,30 +1159,41 @@ static int rc_hash_pcfx_cd(char hash[33], const char* path)
|
||||
|
||||
static int rc_hash_dreamcast(char hash[33], const char* path)
|
||||
{
|
||||
uint8_t buffer[256];
|
||||
uint8_t buffer[256] = "";
|
||||
void* track_handle;
|
||||
void* last_track_handle;
|
||||
char exe_file[32] = "";
|
||||
unsigned size;
|
||||
uint32_t sector;
|
||||
uint32_t track_sector;
|
||||
int result = 0;
|
||||
md5_state_t md5;
|
||||
int i = 0;
|
||||
|
||||
/* track 03 is the data track that contains the TOC and IP.BIN */
|
||||
track_handle = rc_cd_open_track(path, 3);
|
||||
if (!track_handle)
|
||||
return rc_hash_error("Could not open track");
|
||||
|
||||
/* first 256 bytes from first sector should have IP.BIN structure that stores game meta information
|
||||
* https://mc.pp.se/dc/ip.bin.html */
|
||||
rc_cd_read_sector(track_handle, 0, buffer, sizeof(buffer));
|
||||
if (track_handle)
|
||||
{
|
||||
/* first 256 bytes from first sector should have IP.BIN structure that stores game meta information
|
||||
* https://mc.pp.se/dc/ip.bin.html */
|
||||
rc_cd_read_sector(track_handle, rc_cd_first_track_sector(track_handle), buffer, sizeof(buffer));
|
||||
}
|
||||
|
||||
if (memcmp(&buffer[0], "SEGA SEGAKATANA ", 16) != 0)
|
||||
{
|
||||
rc_cd_close_track(track_handle);
|
||||
return rc_hash_error("Not a Dreamcast CD");
|
||||
if (track_handle)
|
||||
rc_cd_close_track(track_handle);
|
||||
|
||||
/* not a gd-rom dreamcast file. check for mil-cd by looking for the marker in the first data track */
|
||||
track_handle = rc_cd_open_track(path, RC_HASH_CDTRACK_FIRST_DATA);
|
||||
if (!track_handle)
|
||||
return rc_hash_error("Could not open track");
|
||||
|
||||
rc_cd_read_sector(track_handle, rc_cd_first_track_sector(track_handle), buffer, sizeof(buffer));
|
||||
if (memcmp(&buffer[0], "SEGA SEGAKATANA ", 16) != 0)
|
||||
{
|
||||
/* did not find marker on track 3 or first data track */
|
||||
rc_cd_close_track(track_handle);
|
||||
return rc_hash_error("Not a Dreamcast CD");
|
||||
}
|
||||
}
|
||||
|
||||
/* start the hash with the game meta information */
|
||||
@@ -1179,30 +1230,26 @@ static int rc_hash_dreamcast(char hash[33], const char* path)
|
||||
exe_file[i] = '\0';
|
||||
|
||||
sector = rc_cd_find_file_sector(track_handle, exe_file, &size);
|
||||
|
||||
rc_cd_close_track(track_handle);
|
||||
|
||||
if (sector == 0)
|
||||
return rc_hash_error("Could not locate boot executable");
|
||||
|
||||
/* last track contains the boot executable */
|
||||
last_track_handle = rc_cd_open_track(path, RC_HASH_CDTRACK_LAST);
|
||||
track_sector = rc_cd_absolute_sector_to_track_sector(last_track_handle, sector);
|
||||
|
||||
if ((int32_t)track_sector < 0)
|
||||
{
|
||||
/* boot executable is not in the last track; try the primary data track.
|
||||
* There's only a handful of games that do this: Q*bert was the first identified. */
|
||||
rc_cd_close_track(last_track_handle);
|
||||
|
||||
rc_hash_verbose("Boot executable not found in last track, trying primary track");
|
||||
last_track_handle = rc_cd_open_track(path, 3);
|
||||
track_sector = rc_cd_absolute_sector_to_track_sector(last_track_handle, sector);
|
||||
rc_cd_close_track(track_handle);
|
||||
return rc_hash_error("Could not locate boot executable");
|
||||
}
|
||||
|
||||
result = rc_hash_cd_file(&md5, last_track_handle, track_sector, NULL, size, "boot executable");
|
||||
if (rc_cd_read_sector(track_handle, sector, buffer, 1))
|
||||
{
|
||||
/* the boot executable is in the primary data track */
|
||||
}
|
||||
else
|
||||
{
|
||||
rc_cd_close_track(track_handle);
|
||||
|
||||
rc_cd_close_track(last_track_handle);
|
||||
/* the boot executable is normally in the last track */
|
||||
track_handle = rc_cd_open_track(path, RC_HASH_CDTRACK_LAST);
|
||||
}
|
||||
|
||||
result = rc_hash_cd_file(&md5, track_handle, sector, NULL, size, "boot executable");
|
||||
rc_cd_close_track(track_handle);
|
||||
|
||||
rc_hash_finalize(&md5, hash);
|
||||
return result;
|
||||
@@ -1525,23 +1572,24 @@ void rc_file_close_buffered_file(void* file_handle)
|
||||
|
||||
static int rc_hash_file_from_buffer(char hash[33], int console_id, const uint8_t* buffer, size_t buffer_size)
|
||||
{
|
||||
struct rc_hash_filereader filereader_funcs_old;
|
||||
struct rc_hash_filereader buffered_filereader_funcs;
|
||||
struct rc_hash_filereader* old_filereader = filereader;
|
||||
int result;
|
||||
|
||||
memcpy(&filereader_funcs_old, &filereader_funcs, sizeof(filereader_funcs));
|
||||
|
||||
filereader_funcs.open = rc_file_open_buffered_file;
|
||||
filereader_funcs.close = rc_file_close_buffered_file;
|
||||
filereader_funcs.read = rc_file_read_buffered_file;
|
||||
filereader_funcs.seek = rc_file_seek_buffered_file;
|
||||
filereader_funcs.tell = rc_file_tell_buffered_file;
|
||||
memset(&buffered_filereader_funcs, 0, sizeof(buffered_filereader_funcs));
|
||||
buffered_filereader_funcs.open = rc_file_open_buffered_file;
|
||||
buffered_filereader_funcs.close = rc_file_close_buffered_file;
|
||||
buffered_filereader_funcs.read = rc_file_read_buffered_file;
|
||||
buffered_filereader_funcs.seek = rc_file_seek_buffered_file;
|
||||
buffered_filereader_funcs.tell = rc_file_tell_buffered_file;
|
||||
filereader = &buffered_filereader_funcs;
|
||||
|
||||
rc_buffered_file.data = rc_buffered_file.read_ptr = buffer;
|
||||
rc_buffered_file.data_size = buffer_size;
|
||||
|
||||
result = rc_hash_generate_from_file(hash, console_id, "[buffered file]");
|
||||
|
||||
memcpy(&filereader_funcs, &filereader_funcs_old, sizeof(filereader_funcs));
|
||||
filereader = old_filereader;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1556,6 +1604,7 @@ int rc_hash_generate_from_buffer(char hash[33], int console_id, const uint8_t* b
|
||||
return rc_hash_error(message);
|
||||
}
|
||||
|
||||
case RC_CONSOLE_AMSTRAD_PC:
|
||||
case RC_CONSOLE_APPLE_II:
|
||||
case RC_CONSOLE_ATARI_2600:
|
||||
case RC_CONSOLE_ATARI_JAGUAR:
|
||||
@@ -1568,6 +1617,7 @@ int rc_hash_generate_from_buffer(char hash[33], int console_id, const uint8_t* b
|
||||
case RC_CONSOLE_MAGNAVOX_ODYSSEY2:
|
||||
case RC_CONSOLE_MASTER_SYSTEM:
|
||||
case RC_CONSOLE_MEGA_DRIVE:
|
||||
case RC_CONSOLE_MEGADUCK:
|
||||
case RC_CONSOLE_MSX:
|
||||
case RC_CONSOLE_NEOGEO_POCKET:
|
||||
case RC_CONSOLE_ORIC:
|
||||
@@ -1582,6 +1632,10 @@ int rc_hash_generate_from_buffer(char hash[33], int console_id, const uint8_t* b
|
||||
case RC_CONSOLE_WONDERSWAN:
|
||||
return rc_hash_buffer(hash, buffer, buffer_size);
|
||||
|
||||
case RC_CONSOLE_ARDUBOY:
|
||||
/* https://en.wikipedia.org/wiki/Intel_HEX */
|
||||
return rc_hash_text(hash, buffer, buffer_size);
|
||||
|
||||
case RC_CONSOLE_ATARI_7800:
|
||||
return rc_hash_7800(hash, buffer, buffer_size);
|
||||
|
||||
@@ -1841,7 +1895,6 @@ int rc_hash_generate_from_file(char hash[33], int console_id, const char* path)
|
||||
return rc_hash_error(buffer);
|
||||
}
|
||||
|
||||
case RC_CONSOLE_APPLE_II:
|
||||
case RC_CONSOLE_ATARI_2600:
|
||||
case RC_CONSOLE_ATARI_JAGUAR:
|
||||
case RC_CONSOLE_COLECOVISION:
|
||||
@@ -1853,6 +1906,7 @@ int rc_hash_generate_from_file(char hash[33], int console_id, const char* path)
|
||||
case RC_CONSOLE_MAGNAVOX_ODYSSEY2:
|
||||
case RC_CONSOLE_MASTER_SYSTEM:
|
||||
case RC_CONSOLE_MEGA_DRIVE:
|
||||
case RC_CONSOLE_MEGADUCK:
|
||||
case RC_CONSOLE_NEOGEO_POCKET:
|
||||
case RC_CONSOLE_ORIC:
|
||||
case RC_CONSOLE_POKEMON_MINI:
|
||||
@@ -1866,6 +1920,8 @@ int rc_hash_generate_from_file(char hash[33], int console_id, const char* path)
|
||||
/* generic whole-file hash - don't buffer */
|
||||
return rc_hash_whole_file(hash, path);
|
||||
|
||||
case RC_CONSOLE_AMSTRAD_PC:
|
||||
case RC_CONSOLE_APPLE_II:
|
||||
case RC_CONSOLE_MSX:
|
||||
case RC_CONSOLE_PC8800:
|
||||
/* generic whole-file hash with m3u support - don't buffer */
|
||||
@@ -1874,6 +1930,7 @@ int rc_hash_generate_from_file(char hash[33], int console_id, const char* path)
|
||||
|
||||
return rc_hash_whole_file(hash, path);
|
||||
|
||||
case RC_CONSOLE_ARDUBOY:
|
||||
case RC_CONSOLE_ATARI_7800:
|
||||
case RC_CONSOLE_ATARI_LYNX:
|
||||
case RC_CONSOLE_NINTENDO:
|
||||
@@ -1985,6 +2042,9 @@ static void rc_hash_initialize_dsk_iterator(struct rc_hash_iterator* iterator, c
|
||||
{
|
||||
/* FAT-12 5.25" DD (512 byte sectors, 9 sectors per track, 40 tracks per side */
|
||||
iterator->consoles[0] = RC_CONSOLE_MSX;
|
||||
|
||||
/* AMSDOS 3" - 40 tracks */
|
||||
iterator->consoles[1] = RC_CONSOLE_AMSTRAD_PC;
|
||||
}
|
||||
else if (size == 256 * 16 * 35) /* 140KB */
|
||||
{
|
||||
@@ -2001,6 +2061,7 @@ static void rc_hash_initialize_dsk_iterator(struct rc_hash_iterator* iterator, c
|
||||
|
||||
/* check MSX first, as Apple II isn't supported by RetroArch, and RAppleWin won't use the iterator */
|
||||
rc_hash_iterator_append_console(iterator, RC_CONSOLE_MSX);
|
||||
rc_hash_iterator_append_console(iterator, RC_CONSOLE_AMSTRAD_PC);
|
||||
rc_hash_iterator_append_console(iterator, RC_CONSOLE_APPLE_II);
|
||||
}
|
||||
|
||||
@@ -2071,7 +2132,7 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
|
||||
}
|
||||
}
|
||||
|
||||
/* bin is associated with MegaDrive, Sega32X, Atari 2600, and Watara Supervision.
|
||||
/* bin is associated with MegaDrive, Sega32X, Atari 2600, Watara Supervision, and MegaDuck.
|
||||
* Since they all use the same hashing algorithm, only specify one of them */
|
||||
iterator->consoles[0] = RC_CONSOLE_MEGA_DRIVE;
|
||||
}
|
||||
@@ -2086,10 +2147,11 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
|
||||
{
|
||||
iterator->consoles[0] = RC_CONSOLE_PLAYSTATION;
|
||||
iterator->consoles[1] = RC_CONSOLE_PLAYSTATION_2;
|
||||
iterator->consoles[2] = RC_CONSOLE_PC_ENGINE;
|
||||
iterator->consoles[3] = RC_CONSOLE_3DO;
|
||||
iterator->consoles[4] = RC_CONSOLE_PCFX;
|
||||
iterator->consoles[5] = RC_CONSOLE_SEGA_CD; /* ASSERT: handles both Sega CD and Saturn */
|
||||
iterator->consoles[2] = RC_CONSOLE_DREAMCAST;
|
||||
iterator->consoles[3] = RC_CONSOLE_SEGA_CD; /* ASSERT: handles both Sega CD and Saturn */
|
||||
iterator->consoles[4] = RC_CONSOLE_PC_ENGINE;
|
||||
iterator->consoles[5] = RC_CONSOLE_3DO;
|
||||
iterator->consoles[6] = RC_CONSOLE_PCFX;
|
||||
need_path = 1;
|
||||
}
|
||||
else if (rc_path_compare_extension(ext, "chd"))
|
||||
@@ -2097,10 +2159,10 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
|
||||
iterator->consoles[0] = RC_CONSOLE_PLAYSTATION;
|
||||
iterator->consoles[1] = RC_CONSOLE_PLAYSTATION_2;
|
||||
iterator->consoles[2] = RC_CONSOLE_DREAMCAST;
|
||||
iterator->consoles[3] = RC_CONSOLE_PC_ENGINE;
|
||||
iterator->consoles[4] = RC_CONSOLE_3DO;
|
||||
iterator->consoles[5] = RC_CONSOLE_PCFX;
|
||||
iterator->consoles[6] = RC_CONSOLE_SEGA_CD; /* ASSERT: handles both Sega CD and Saturn */
|
||||
iterator->consoles[3] = RC_CONSOLE_SEGA_CD; /* ASSERT: handles both Sega CD and Saturn */
|
||||
iterator->consoles[4] = RC_CONSOLE_PC_ENGINE;
|
||||
iterator->consoles[5] = RC_CONSOLE_3DO;
|
||||
iterator->consoles[6] = RC_CONSOLE_PCFX;
|
||||
need_path = 1;
|
||||
}
|
||||
else if (rc_path_compare_extension(ext, "col"))
|
||||
@@ -2163,6 +2225,13 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
|
||||
}
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
if (rc_path_compare_extension(ext, "hex"))
|
||||
{
|
||||
iterator->consoles[0] = RC_CONSOLE_ARDUBOY;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
if (rc_path_compare_extension(ext, "iso"))
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2017 - Daniel De Matteis
|
||||
* Copyright (C) 2019-2020 - James Leaver
|
||||
* Copyright (C) 2022-2022 - Jahed Ahmed
|
||||
*
|
||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
@@ -49,6 +50,9 @@
|
||||
#define DINGUX_SCALING_SHARPNESS_ENVAR "SDL_VIDEO_KMSDRM_SCALING_SHARPNESS"
|
||||
#define DINGUX_VIDEO_REFRESHRATE_ENVAR "SDL_VIDEO_REFRESHRATE"
|
||||
|
||||
/* Miyoo defines */
|
||||
#define MIYOO_BATTERY_VOLTAGE_NOW_FILE "/sys/class/power_supply/miyoo-battery/voltage_now"
|
||||
|
||||
/* Enables/disables downscaling when using
|
||||
* the IPU hardware scaler */
|
||||
bool dingux_ipu_set_downscaling_enable(bool enable)
|
||||
@@ -307,6 +311,24 @@ int dingux_get_battery_level(void)
|
||||
return -1;
|
||||
|
||||
return (int)(((voltage_now - voltage_min) * 100) / (voltage_max - voltage_min));
|
||||
#elif defined(MIYOO)
|
||||
/* miyoo-battery only provides "voltage_now". Results are based on
|
||||
* value distribution while running a game at max load. */
|
||||
int voltage_now = dingux_read_battery_sys_file(MIYOO_BATTERY_VOLTAGE_NOW_FILE);
|
||||
if (voltage_now < 0) return -1; // voltage_now not available
|
||||
if (voltage_now > 4300) return 100; // 4320
|
||||
if (voltage_now > 4200) return 90; // 4230
|
||||
if (voltage_now > 4100) return 80; // 4140
|
||||
if (voltage_now > 4000) return 70; // 4050
|
||||
if (voltage_now > 3900) return 60; // 3960
|
||||
if (voltage_now > 3800) return 50; // 3870
|
||||
if (voltage_now > 3700) return 40; // 3780
|
||||
if (voltage_now > 3600) return 30; // 3690
|
||||
if (voltage_now > 3550) return 20; // 3600
|
||||
if (voltage_now > 3500) return 10; // 3510
|
||||
if (voltage_now > 3400) return 5; // 3420
|
||||
if (voltage_now > 3300) return 1; // 3330
|
||||
return 0; // 3240
|
||||
#else
|
||||
return dingux_read_battery_sys_file(DINGUX_BATTERY_CAPACITY_FILE);
|
||||
#endif
|
||||
|
||||
@@ -627,6 +627,7 @@ void drivers_init(
|
||||
#ifdef HAVE_LIBRETRODB
|
||||
menu_explore_context_init();
|
||||
#endif
|
||||
menu_contentless_cores_context_init();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -694,6 +695,7 @@ void driver_uninit(int flags)
|
||||
#ifdef HAVE_LIBRETRODB
|
||||
menu_explore_context_deinit();
|
||||
#endif
|
||||
menu_contentless_cores_context_deinit();
|
||||
|
||||
menu_driver_ctl(RARCH_MENU_CTL_DEINIT, NULL);
|
||||
}
|
||||
@@ -808,18 +810,19 @@ void retroarch_deinit_drivers(struct retro_callbacks *cbs)
|
||||
audio_state_get_ptr()->active = false;
|
||||
audio_state_get_ptr()->current_audio = NULL;
|
||||
|
||||
/* Input */
|
||||
input_st->keyboard_linefeed_enable = false;
|
||||
input_st->block_hotkey = false;
|
||||
input_st->block_libretro_input = false;
|
||||
|
||||
if (input_st)
|
||||
input_st->nonblocking_flag = false;
|
||||
|
||||
memset(&input_st->turbo_btns, 0, sizeof(turbo_buttons_t));
|
||||
memset(&input_st->analog_requested, 0,
|
||||
if (input_st)
|
||||
{
|
||||
/* Input */
|
||||
input_st->keyboard_linefeed_enable = false;
|
||||
input_st->block_hotkey = false;
|
||||
input_st->block_libretro_input = false;
|
||||
input_st->nonblocking_flag = false;
|
||||
|
||||
memset(&input_st->turbo_btns, 0, sizeof(turbo_buttons_t));
|
||||
memset(&input_st->analog_requested, 0,
|
||||
sizeof(input_st->analog_requested));
|
||||
input_st->current_driver = NULL;
|
||||
input_st->current_driver = NULL;
|
||||
}
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
menu_driver_destroy(
|
||||
|
||||
@@ -107,6 +107,8 @@ RETRO_BEGIN_DECLS
|
||||
#define FILE_PATH_CORE_BACKUP_EXTENSION_NO_DOT "lcbk"
|
||||
#define FILE_PATH_LOCK_EXTENSION ".lck"
|
||||
#define FILE_PATH_LOCK_EXTENSION_NO_DOT "lck"
|
||||
#define FILE_PATH_STANDALONE_EXEMPT_EXTENSION ".lsae"
|
||||
#define FILE_PATH_STANDALONE_EXEMPT_EXTENSION_NO_DOT "lsae"
|
||||
#define FILE_PATH_BACKUP_EXTENSION ".bak"
|
||||
#if defined(RARCH_MOBILE)
|
||||
#define FILE_PATH_DEFAULT_OVERLAY "gamepads/neo-retropad/neo-retropad.cfg"
|
||||
|
||||
@@ -114,7 +114,7 @@ typedef enum
|
||||
CFAllDomainsMask = 0x0ffff /* All domains: all of the above and future items */
|
||||
} CFDomainMask;
|
||||
|
||||
#if (defined(OSX) && !(defined(__ppc__) || defined(__ppc64__)))
|
||||
#if (defined(OSX) && (MAC_OS_X_VERSION_MAX_ALLOWED >= 101200))
|
||||
static int speak_pid = 0;
|
||||
#endif
|
||||
|
||||
@@ -128,14 +128,19 @@ static void CFSearchPathForDirectoriesInDomains(
|
||||
#else
|
||||
NSSearchPathDirectory dir = NSDocumentDirectory;
|
||||
#endif
|
||||
CFStringRef array_val;
|
||||
#if __has_feature(objc_arc)
|
||||
CFStringRef array_val = (__bridge CFStringRef)[
|
||||
array_val = (__bridge CFStringRef)[
|
||||
NSSearchPathForDirectoriesInDomains(dir,
|
||||
NSUserDomainMask, YES) firstObject];
|
||||
#else
|
||||
CFStringRef array_val = (CFStringRef)[
|
||||
NSSearchPathForDirectoriesInDomains(dir,
|
||||
NSUserDomainMask, YES) firstObject];
|
||||
NSArray *arr = NSSearchPathForDirectoriesInDomains(dir,
|
||||
NSUserDomainMask, YES);
|
||||
if ([arr count] == 0) {
|
||||
array_val = nil;
|
||||
} else{
|
||||
array_val = (CFStringRef)[arr objectAtIndex:0];
|
||||
}
|
||||
#endif
|
||||
if (array_val)
|
||||
CFStringGetCString(array_val, s, len, kCFStringEncodingUTF8);
|
||||
@@ -805,7 +810,7 @@ static uint64_t frontend_darwin_get_total_mem(void)
|
||||
|
||||
static uint64_t frontend_darwin_get_free_mem(void)
|
||||
{
|
||||
#if (defined(OSX) && !(defined(__ppc__) || defined(__ppc64__)))
|
||||
#if (defined(OSX) && (MAC_OS_X_VERSION_MAX_ALLOWED >= 101200))
|
||||
vm_size_t page_size;
|
||||
vm_statistics64_data_t vm_stats;
|
||||
mach_port_t mach_port = mach_host_self();
|
||||
@@ -831,7 +836,7 @@ static const char* frontend_darwin_get_cpu_model_name(void)
|
||||
return darwin_cpu_model_name;
|
||||
}
|
||||
|
||||
#if (defined(OSX) && !(defined(__ppc__) || defined(__ppc64__)))
|
||||
#if (defined(OSX) && (MAC_OS_X_VERSION_MAX_ALLOWED >= 101200))
|
||||
static char* accessibility_mac_language_code(const char* language)
|
||||
{
|
||||
if (string_is_equal(language,"en"))
|
||||
@@ -995,13 +1000,9 @@ frontend_ctx_driver_t frontend_ctx_darwin = {
|
||||
NULL, /* watch_path_for_changes */
|
||||
NULL, /* check_for_path_changes */
|
||||
NULL, /* set_sustained_performance_mode */
|
||||
#if (defined(OSX) && !(defined(__ppc__) || defined(__ppc64__)))
|
||||
frontend_darwin_get_cpu_model_name, /* get_cpu_model_name */
|
||||
#else
|
||||
NULL, /* get_cpu_model_name */
|
||||
#endif
|
||||
frontend_darwin_get_cpu_model_name, /* get_cpu_model_name */
|
||||
NULL, /* get_user_language */
|
||||
#if (defined(OSX) && !(defined(__ppc__) || defined(__ppc64__)))
|
||||
#if (defined(OSX) && (MAC_OS_X_VERSION_MAX_ALLOWED >= 101200))
|
||||
is_narrator_running_macos, /* is_narrator_running */
|
||||
accessibility_speak_macos, /* accessibility_speak */
|
||||
#else
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "../frontend_driver.h"
|
||||
#include "../../defaults.h"
|
||||
#include "../../paths.h"
|
||||
#include "../command.h"
|
||||
|
||||
static enum frontend_fork dos_fork_mode = FRONTEND_FORK_NONE;
|
||||
|
||||
@@ -145,21 +146,21 @@ static void frontend_dos_exitspawn(char *s, size_t len, char *args)
|
||||
frontend_dos_exec(s, should_load_content);
|
||||
}
|
||||
|
||||
static bool frontend_unix_set_fork(enum frontend_fork fork_mode)
|
||||
static bool frontend_dos_set_fork(enum frontend_fork fork_mode)
|
||||
{
|
||||
switch (fork_mode)
|
||||
{
|
||||
case FRONTEND_FORK_CORE:
|
||||
RARCH_LOG("FRONTEND_FORK_CORE\n");
|
||||
unix_fork_mode = fork_mode;
|
||||
dos_fork_mode = fork_mode;
|
||||
break;
|
||||
case FRONTEND_FORK_CORE_WITH_ARGS:
|
||||
RARCH_LOG("FRONTEND_FORK_CORE_WITH_ARGS\n");
|
||||
unix_fork_mode = fork_mode;
|
||||
dos_fork_mode = fork_mode;
|
||||
break;
|
||||
case FRONTEND_FORK_RESTART:
|
||||
RARCH_LOG("FRONTEND_FORK_RESTART\n");
|
||||
unix_fork_mode = FRONTEND_FORK_CORE;
|
||||
dos_fork_mode = FRONTEND_FORK_CORE;
|
||||
|
||||
{
|
||||
char executable_path[PATH_MAX_LENGTH] = {0};
|
||||
|
||||
@@ -29,6 +29,11 @@
|
||||
#include "../../config.h"
|
||||
#endif
|
||||
|
||||
#include <retroarch_types.h>
|
||||
#include <msg_hash.h>
|
||||
#include <command.h>
|
||||
#include <verbosity.h>
|
||||
|
||||
#if defined(HW_RVL) && !defined(IS_SALAMANDER)
|
||||
#include <rthreads/rthreads.h>
|
||||
#include "../../memory/wii/mem2_manager.h"
|
||||
@@ -162,6 +167,9 @@ static void frontend_gx_get_env(
|
||||
{
|
||||
char *last_slash = NULL;
|
||||
char *device_end = NULL;
|
||||
#if defined(HAVE_LOGGER) && !defined(IS_SALAMANDER)
|
||||
logger_init();
|
||||
#endif
|
||||
#ifndef IS_SALAMANDER
|
||||
|
||||
/* This situation can happen on some loaders so we really need some
|
||||
@@ -241,7 +249,7 @@ static void frontend_gx_get_env(
|
||||
"cheats", sizeof(g_defaults.dirs[DEFAULT_DIR_CHEATS]));
|
||||
|
||||
/* User paths */
|
||||
fill_pathname_join(g_defaults.path_config, g_defaults.dirs[DEFAULT_DIR_PORT],
|
||||
fill_pathname_join(g_defaults.path_config, g_defaults.dirs[DEFAULT_DIR_CORE],
|
||||
"retroarch.cfg", sizeof(g_defaults.path_config));
|
||||
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SYSTEM], g_defaults.dirs[DEFAULT_DIR_PORT],
|
||||
"system", sizeof(g_defaults.dirs[DEFAULT_DIR_SYSTEM]));
|
||||
|
||||
@@ -73,6 +73,18 @@ SYS_PROCESS_PARAM(1001, 0x200000)
|
||||
static bool multiman_detected = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MEMINFO
|
||||
typedef struct {
|
||||
uint32_t total;
|
||||
uint32_t avail;
|
||||
} sys_memory_info_t;
|
||||
#ifdef __PSL1GHT__
|
||||
#define sys_memory_get_user_memory_size(x) lv2syscall1(352, x)
|
||||
#else
|
||||
#define sys_memory_get_user_memory_size(x) system_call_1(352, x)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef IS_SALAMANDER
|
||||
static enum frontend_fork ps3_fork_mode = FRONTEND_FORK_NONE;
|
||||
|
||||
@@ -365,6 +377,11 @@ static void frontend_ps3_init(void *data)
|
||||
cellSysmoduleLoadModule(CELL_SYSMODULE_SYSUTIL_NP);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIGHTGUN
|
||||
cellSysmoduleLoadModule(SYSMODULE_GEM);
|
||||
cellSysmoduleLoadModule(SYSMODULE_CAMERA);
|
||||
#endif
|
||||
|
||||
#ifndef __PSL1GHT__
|
||||
sys_net_initialize_network();
|
||||
sceNpInit(NP_POOL_SIZE, np_pool);
|
||||
@@ -674,6 +691,22 @@ static void frontend_ps3_process_args(int *argc, char *argv[])
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAVE_MEMINFO
|
||||
static size_t frontend_ps3_get_mem_total(void)
|
||||
{
|
||||
sys_memory_info_t mem_info;
|
||||
sys_memory_get_user_memory_size(&mem_info);
|
||||
return mem_info.total;
|
||||
}
|
||||
|
||||
static size_t frontend_ps3_get_mem_used(void)
|
||||
{
|
||||
sys_memory_info_t mem_info;
|
||||
sys_memory_get_user_memory_size(&mem_info);
|
||||
return mem_info.avail;
|
||||
}
|
||||
#endif
|
||||
|
||||
frontend_ctx_driver_t frontend_ctx_ps3 = {
|
||||
frontend_ps3_get_env,
|
||||
frontend_ps3_init,
|
||||
@@ -694,8 +727,13 @@ frontend_ctx_driver_t frontend_ctx_ps3 = {
|
||||
frontend_ps3_get_arch, /* get_architecture */
|
||||
NULL, /* get_powerstate */
|
||||
frontend_ps3_parse_drive_list,/* parse_drive_list */
|
||||
#ifdef HAVE_MEMINFO
|
||||
frontend_ps3_get_mem_total,
|
||||
frontend_ps3_get_mem_used,
|
||||
#else
|
||||
NULL, /* get_total_mem */
|
||||
NULL, /* get_free_mem */
|
||||
#endif
|
||||
NULL, /* install_signal_handler */
|
||||
NULL, /* get_sighandler_state */
|
||||
NULL, /* set_sighandler_state */
|
||||
|
||||
@@ -60,11 +60,9 @@
|
||||
#include "../../paths.h"
|
||||
#include "../../verbosity.h"
|
||||
|
||||
#if defined(HAVE_KERNEL_PRX) || defined(IS_SALAMANDER)
|
||||
#ifndef VITA
|
||||
#if defined(PSP) && defined(HAVE_KERNEL_PRX)
|
||||
#include "../../bootstrap/psp1/kernel_functions.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_VITAGLES)
|
||||
#include "../../deps/Pigs-In-A-Blanket/include/pib.h"
|
||||
@@ -73,10 +71,6 @@
|
||||
#ifndef VITA
|
||||
PSP_MODULE_INFO("RetroArch", 0, 1, 1);
|
||||
PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER|THREAD_ATTR_VFPU);
|
||||
#ifdef BIG_STACK
|
||||
PSP_MAIN_THREAD_STACK_SIZE_KB(4*1024);
|
||||
#endif
|
||||
PSP_HEAP_SIZE_MAX();
|
||||
#endif
|
||||
|
||||
#ifdef SCE_LIBC_SIZE
|
||||
@@ -241,7 +235,7 @@ static void frontend_psp_shutdown(bool unused)
|
||||
//sceKernelExitProcess(0);
|
||||
return;
|
||||
#else
|
||||
sceKernelExitGame();
|
||||
exit(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -309,16 +303,13 @@ static void frontend_psp_init(void *data)
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_KERNEL_PRX) || defined(IS_SALAMANDER)
|
||||
#ifndef VITA
|
||||
#if defined(PSP) && defined(HAVE_KERNEL_PRX)
|
||||
pspSdkLoadStartModule("kernel_functions.prx", PSP_MEMORY_PARTITION_KERNEL);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
static void frontend_psp_exec(const char *path, bool should_load_game)
|
||||
{
|
||||
#if defined(HAVE_KERNEL_PRX) || defined(IS_SALAMANDER) || defined(VITA)
|
||||
#ifdef IS_SALAMANDER
|
||||
char boot_params[1024];
|
||||
char core_name[256];
|
||||
@@ -326,7 +317,7 @@ static void frontend_psp_exec(const char *path, bool should_load_game)
|
||||
char argp[512] = {0};
|
||||
SceSize args = 0;
|
||||
|
||||
#if !defined(VITA)
|
||||
#if defined(PSP)
|
||||
strlcpy(argp, eboot_path, sizeof(argp));
|
||||
args = strlen(argp) + 1;
|
||||
#endif
|
||||
@@ -392,10 +383,9 @@ static void frontend_psp_exec(const char *path, bool should_load_game)
|
||||
int ret = sceAppMgrLoadExec(path, args == 0 ? NULL : (char * const*)((const char*[]){argp, 0}), NULL);
|
||||
RARCH_LOG("Attempt to load executable: [%d].\n", ret);
|
||||
}
|
||||
#else
|
||||
#elif defined(PSP) && defined(HAVE_KERNEL_PRX)
|
||||
exitspawn_kernel(path, args, argp);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef IS_SALAMANDER
|
||||
|
||||
@@ -1274,12 +1274,7 @@ static enum frontend_architecture frontend_unix_get_arch(void)
|
||||
string_is_equal(val, "armv7b")
|
||||
)
|
||||
return FRONTEND_ARCH_ARMV7;
|
||||
else if (
|
||||
string_is_equal(val, "armv6l") ||
|
||||
string_is_equal(val, "armv6b") ||
|
||||
string_is_equal(val, "armv5tel") ||
|
||||
string_is_equal(val, "arm")
|
||||
)
|
||||
else if (string_starts_with(val, "arm"))
|
||||
return FRONTEND_ARCH_ARM;
|
||||
else if (string_is_equal(val, "x86_64"))
|
||||
return FRONTEND_ARCH_X86_64;
|
||||
@@ -1377,6 +1372,7 @@ static void frontend_unix_get_env(int *argc,
|
||||
char *argv[], void *data, void *params_data)
|
||||
{
|
||||
unsigned i;
|
||||
const char* libretro_directory = getenv("LIBRETRO_DIRECTORY");
|
||||
#ifdef ANDROID
|
||||
int32_t major, minor, rel;
|
||||
char device_model[PROP_VALUE_MAX] = {0};
|
||||
@@ -1798,7 +1794,7 @@ static void frontend_unix_get_env(int *argc,
|
||||
char base_path[PATH_MAX] = {0};
|
||||
#if defined(RARCH_UNIX_CWD_ENV)
|
||||
/* The entire path is zero initialized. */
|
||||
base_path[0] = '.';
|
||||
getcwd(base_path, sizeof(base_path));
|
||||
#elif defined(DINGUX)
|
||||
dingux_get_base_path(base_path, sizeof(base_path));
|
||||
#else
|
||||
@@ -1819,8 +1815,12 @@ static void frontend_unix_get_env(int *argc,
|
||||
strcpy_literal(base_path, "retroarch");
|
||||
#endif
|
||||
|
||||
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE], base_path,
|
||||
"cores", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE]));
|
||||
if (!string_is_empty(libretro_directory))
|
||||
strlcpy(g_defaults.dirs[DEFAULT_DIR_CORE], libretro_directory,
|
||||
sizeof(g_defaults.dirs[DEFAULT_DIR_CORE]));
|
||||
else
|
||||
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE], base_path,
|
||||
"cores", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE]));
|
||||
#if defined(DINGUX)
|
||||
/* On platforms that require manual core installation/
|
||||
* removal, placing core info files in the same directory
|
||||
@@ -2135,6 +2135,10 @@ static void frontend_unix_init(void *data)
|
||||
"deleteCore", "(Ljava/lang/String;)V");
|
||||
CALL_OBJ_METHOD(env, obj, android_app->activity->clazz,
|
||||
android_app->getIntent);
|
||||
GET_METHOD_ID(env, android_app->getVolumeCount, class,
|
||||
"getVolumeCount", "()I");
|
||||
GET_METHOD_ID(env, android_app->getVolumePath, class,
|
||||
"getVolumePath", "(Ljava/lang/String;)Ljava/lang/String;");
|
||||
|
||||
GET_OBJECT_CLASS(env, class, obj);
|
||||
GET_METHOD_ID(env, android_app->getStringExtra, class,
|
||||
@@ -2152,6 +2156,29 @@ static int frontend_unix_parse_drive_list(void *data, bool load_content)
|
||||
MENU_ENUM_LABEL_FILE_BROWSER_DIRECTORY;
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
JNIEnv *env = jni_thread_getenv();
|
||||
jint output = 0;
|
||||
jobject obj = NULL;
|
||||
jstring jstr = NULL;
|
||||
|
||||
int volume_count = 0;
|
||||
|
||||
if (!env || !g_android)
|
||||
return 0;
|
||||
|
||||
CALL_OBJ_METHOD(env, obj, g_android->activity->clazz,
|
||||
g_android->getIntent);
|
||||
|
||||
if (g_android->getVolumeCount)
|
||||
{
|
||||
CALL_INT_METHOD(env, output,
|
||||
g_android->activity->clazz, g_android->getVolumeCount);
|
||||
volume_count = output;
|
||||
}
|
||||
|
||||
RARCH_LOG("external volumes: %d\n", volume_count);
|
||||
|
||||
if (!string_is_empty(internal_storage_path))
|
||||
{
|
||||
if (storage_permissions == INTERNAL_STORAGE_WRITABLE)
|
||||
@@ -2198,6 +2225,37 @@ static int frontend_unix_parse_drive_list(void *data, bool load_content)
|
||||
msg_hash_to_str(MSG_APPLICATION_DIR),
|
||||
enum_idx,
|
||||
FILE_TYPE_DIRECTORY, 0, 0);
|
||||
for (unsigned i=0; i < volume_count; i++)
|
||||
{
|
||||
static char aux_path[PATH_MAX_LENGTH];
|
||||
char index[2];
|
||||
index[0] = '\0';
|
||||
|
||||
snprintf(index, sizeof(index), "%d", i);
|
||||
|
||||
CALL_OBJ_METHOD_PARAM(env, jstr, g_android->activity->clazz, g_android->getVolumePath,
|
||||
(*env)->NewStringUTF(env, index));
|
||||
|
||||
if (jstr)
|
||||
{
|
||||
const char *str = (*env)->GetStringUTFChars(env, jstr, 0);
|
||||
|
||||
aux_path[0] = '\0';
|
||||
|
||||
if (str && *str)
|
||||
strlcpy(aux_path, str,
|
||||
sizeof(aux_path));
|
||||
|
||||
(*env)->ReleaseStringUTFChars(env, jstr, str);
|
||||
if (!string_is_empty(aux_path))
|
||||
menu_entries_append_enum(list,
|
||||
aux_path,
|
||||
msg_hash_to_str(MSG_APPLICATION_DIR),
|
||||
enum_idx,
|
||||
FILE_TYPE_DIRECTORY, 0, 0);
|
||||
}
|
||||
|
||||
}
|
||||
#elif defined(WEBOS)
|
||||
if (path_is_directory("/media/internal"))
|
||||
menu_entries_append_enum(list, "/media/internal",
|
||||
|
||||
@@ -175,6 +175,9 @@ struct android_app
|
||||
jmethodID downloadCore;
|
||||
jmethodID deleteCore;
|
||||
|
||||
jmethodID getVolumeCount;
|
||||
jmethodID getVolumePath;
|
||||
|
||||
struct
|
||||
{
|
||||
unsigned width, height;
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
#include <file/file_path.h>
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <paths.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "../../config.h"
|
||||
|
||||
@@ -571,6 +571,7 @@ static void frontend_win32_env_get(int *argc, char *argv[],
|
||||
void *args, void *params_data)
|
||||
{
|
||||
const char *tmp_dir = getenv("TMP");
|
||||
const char *libretro_directory = getenv("LIBRETRO_DIRECTORY");
|
||||
if (!string_is_empty(tmp_dir))
|
||||
fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_CACHE],
|
||||
tmp_dir, sizeof(g_defaults.dirs[DEFAULT_DIR_CACHE]));
|
||||
@@ -609,8 +610,12 @@ static void frontend_win32_env_get(int *argc, char *argv[],
|
||||
fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_VIDEO_LAYOUT],
|
||||
":\\layouts", sizeof(g_defaults.dirs[DEFAULT_DIR_VIDEO_LAYOUT]));
|
||||
#endif
|
||||
fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_CORE],
|
||||
":\\cores", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE]));
|
||||
if (!string_is_empty(libretro_directory))
|
||||
strlcpy(g_defaults.dirs[DEFAULT_DIR_CORE], libretro_directory,
|
||||
sizeof(g_defaults.dirs[DEFAULT_DIR_CORE]));
|
||||
else
|
||||
fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_CORE],
|
||||
":\\cores", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE]));
|
||||
fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_CORE_INFO],
|
||||
":\\info", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_INFO]));
|
||||
fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_AUTOCONFIG],
|
||||
|
||||
@@ -251,7 +251,7 @@ bool frontend_driver_get_salamander_basename(char *s, size_t len)
|
||||
strcpy_literal(s, "eboot.bin");
|
||||
return true;
|
||||
#elif defined(PS2)
|
||||
strcpy_literal(s, "eboot.elf");
|
||||
strcpy_literal(s, "raboot.elf");
|
||||
return true;
|
||||
#elif defined(__PSL1GHT__) || defined(__PS3__)
|
||||
strcpy_literal(s, "EBOOT.BIN");
|
||||
|
||||
@@ -1152,6 +1152,7 @@ typedef struct
|
||||
D3D10_VIEWPORT viewport;
|
||||
DXGI_FORMAT format;
|
||||
float clearcolor[4];
|
||||
unsigned swap_interval;
|
||||
bool vsync;
|
||||
bool resize_chain;
|
||||
bool keep_aspect;
|
||||
|
||||
@@ -2543,6 +2543,7 @@ typedef struct
|
||||
D3D11_RECT scissor;
|
||||
DXGI_FORMAT format;
|
||||
float clearcolor[4];
|
||||
unsigned swap_interval;
|
||||
bool vsync;
|
||||
bool resize_chain;
|
||||
bool keep_aspect;
|
||||
|
||||
@@ -40,7 +40,7 @@ RETRO_BEGIN_DECLS
|
||||
#define RARCH_GL_FRAMEBUFFER GL_FRAMEBUFFER_OES
|
||||
#define RARCH_GL_FRAMEBUFFER_COMPLETE GL_FRAMEBUFFER_COMPLETE_OES
|
||||
#define RARCH_GL_COLOR_ATTACHMENT0 GL_COLOR_ATTACHMENT0_EXT
|
||||
#elif (defined(__MACH__) && (defined(__ppc__) || defined(__ppc64__)))
|
||||
#elif (defined(__MACH__) && defined(MAC_OS_X_VERSION_MAX_ALLOWED) && (MAC_OS_X_VERSION_MAX_ALLOWED < 101200))
|
||||
#define RARCH_GL_FRAMEBUFFER GL_FRAMEBUFFER_EXT
|
||||
#define RARCH_GL_FRAMEBUFFER_COMPLETE GL_FRAMEBUFFER_COMPLETE_EXT
|
||||
#define RARCH_GL_COLOR_ATTACHMENT0 GL_COLOR_ATTACHMENT0_EXT
|
||||
@@ -59,7 +59,7 @@ RETRO_BEGIN_DECLS
|
||||
#endif
|
||||
#define RARCH_GL_DEPTH_ATTACHMENT GL_DEPTH_ATTACHMENT
|
||||
#define RARCH_GL_STENCIL_ATTACHMENT GL_STENCIL_ATTACHMENT
|
||||
#elif (defined(__MACH__) && (defined(__ppc__) || defined(__ppc64__)))
|
||||
#elif (defined(__MACH__) && defined(MAC_OS_X_VERSION_MAX_ALLOWED) && (MAC_OS_X_VERSION_MAX_ALLOWED < 101200))
|
||||
#define RARCH_GL_RENDERBUFFER GL_RENDERBUFFER_EXT
|
||||
#define RARCH_GL_DEPTH24_STENCIL8 GL_DEPTH24_STENCIL8_EXT
|
||||
#define RARCH_GL_DEPTH_ATTACHMENT GL_DEPTH_ATTACHMENT_EXT
|
||||
@@ -76,7 +76,7 @@ RETRO_BEGIN_DECLS
|
||||
#define RARCH_GL_STENCIL_ATTACHMENT GL_STENCIL_ATTACHMENT
|
||||
#endif
|
||||
|
||||
#if (defined(__MACH__) && (defined(__ppc__) || defined(__ppc64__)))
|
||||
#if (defined(__MACH__) && defined(MAC_OS_X_VERSION_MAX_ALLOWED) && (MAC_OS_X_VERSION_MAX_ALLOWED < 101200))
|
||||
#define RARCH_GL_MAX_RENDERBUFFER_SIZE GL_MAX_RENDERBUFFER_SIZE_EXT
|
||||
#elif defined(HAVE_PSGL)
|
||||
#define RARCH_GL_MAX_RENDERBUFFER_SIZE GL_MAX_RENDERBUFFER_SIZE_OES
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2017 - Daniel De Matteis
|
||||
*
|
||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RSX_COMMON_H__
|
||||
#define RSX_COMMON_H__
|
||||
|
||||
#include <rsx/rsx.h>
|
||||
#include <rsx/nv40.h>
|
||||
#include <ppu-types.h>
|
||||
|
||||
#include <retro_inline.h>
|
||||
#include <string/stdstring.h>
|
||||
#include <gfx/math/matrix_4x4.h>
|
||||
|
||||
#include <defines/ps3_defines.h>
|
||||
#include "../../driver.h"
|
||||
#include "../../retroarch.h"
|
||||
#include "../video_coord_array.h"
|
||||
|
||||
#define MAX_BUFFERS 2
|
||||
#define MAX_MENU_BUFFERS 2
|
||||
|
||||
/* Shader objects */
|
||||
extern const u8 vpshader_basic_vpo_end[];
|
||||
extern const u8 vpshader_basic_vpo[];
|
||||
extern const u32 vpshader_basic_vpo_size;
|
||||
|
||||
extern const u8 fpshader_basic_fpo_end[];
|
||||
extern const u8 fpshader_basic_fpo[];
|
||||
extern const u32 fpshader_basic_fpo_size;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float x, y;
|
||||
float w, h;
|
||||
float min, max;
|
||||
float scale[4];
|
||||
float offset[4];
|
||||
} rsx_viewport_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float x, y, z;
|
||||
float nx, ny, nz;
|
||||
float u, v;
|
||||
float r, g, b, a;
|
||||
} rsx_vertex_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gcmTexture tex;
|
||||
u32 *data;
|
||||
u32 offset;
|
||||
u32 wrap_s;
|
||||
u32 wrap_t;
|
||||
u32 min_filter;
|
||||
u32 mag_filter;
|
||||
u32 width;
|
||||
u32 height;
|
||||
} rsx_texture_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int height;
|
||||
int width;
|
||||
int id;
|
||||
uint32_t *ptr;
|
||||
/* Internal stuff */
|
||||
uint32_t offset;
|
||||
} rsxBuffer;
|
||||
|
||||
typedef struct {
|
||||
video_viewport_t vp;
|
||||
rsxBuffer buffers[MAX_BUFFERS];
|
||||
#if defined(HAVE_MENU_BUFFER)
|
||||
rsxBuffer menuBuffers[MAX_MENU_BUFFERS];
|
||||
int menuBuffer;
|
||||
#endif
|
||||
int currentBuffer, nextBuffer;
|
||||
gcmContextData* context;
|
||||
u16 width;
|
||||
u16 height;
|
||||
u16 menu_width;
|
||||
u16 menu_height;
|
||||
bool menu_frame_enable;
|
||||
bool rgb32;
|
||||
bool vsync;
|
||||
u32 depth_pitch;
|
||||
u32 depth_offset;
|
||||
u32* depth_buffer;
|
||||
|
||||
#if defined(HAVE_MENU_BUFFER)
|
||||
gcmSurface surface[MAX_BUFFERS+MAX_MENU_BUFFERS];
|
||||
#else
|
||||
gcmSurface surface[MAX_BUFFERS];
|
||||
#endif
|
||||
rsx_texture_t texture;
|
||||
rsx_texture_t menu_texture;
|
||||
rsx_vertex_t *vertices;
|
||||
u32 pos_offset;
|
||||
u32 uv_offset;
|
||||
u32 col_offset;
|
||||
rsxProgramConst *proj_matrix;
|
||||
rsxProgramAttrib* pos_index;
|
||||
rsxProgramAttrib* col_index;
|
||||
rsxProgramAttrib* uv_index;
|
||||
rsxProgramAttrib* tex_unit;
|
||||
void *vp_ucode;
|
||||
void *fp_ucode;
|
||||
rsxVertexProgram *vpo;
|
||||
rsxFragmentProgram *fpo;
|
||||
u32 *fp_buffer;
|
||||
u32 fp_offset;
|
||||
math_matrix_4x4 mvp, mvp_no_rot;
|
||||
float menu_texture_alpha;
|
||||
|
||||
bool smooth;
|
||||
unsigned rotation;
|
||||
bool keep_aspect;
|
||||
bool should_resize;
|
||||
bool msg_rendering_enabled;
|
||||
|
||||
const shader_backend_t* shader;
|
||||
void* shader_data;
|
||||
void* renderchain_data;
|
||||
void* ctx_data;
|
||||
const gfx_ctx_driver_t* ctx_driver;
|
||||
bool shared_context_use;
|
||||
|
||||
video_info_t video_info;
|
||||
struct video_tex_info tex_info; /* unsigned int alignment */
|
||||
struct video_tex_info prev_info[GFX_MAX_TEXTURES]; /* unsigned alignment */
|
||||
struct video_fbo_rect fbo_rect[GFX_MAX_SHADERS]; /* unsigned alignment */
|
||||
} rsx_t;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,76 @@
|
||||
|
||||
#ifndef RA_WAYLAND_MODULE
|
||||
#define RA_WAYLAND_MODULE(modname)
|
||||
#endif
|
||||
|
||||
#ifndef RA_WAYLAND_SYM
|
||||
#define RA_WAYLAND_SYM(rc,fn,params)
|
||||
#endif
|
||||
|
||||
#ifndef RA_WAYLAND_INTERFACE
|
||||
#define RA_WAYLAND_INTERFACE(iface)
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef HAVE_LIBDECOR_H
|
||||
RA_WAYLAND_MODULE(WAYLAND_LIBDECOR)
|
||||
RA_WAYLAND_SYM(void, libdecor_unref, (struct libdecor *))
|
||||
RA_WAYLAND_SYM(struct libdecor *, libdecor_new, (struct wl_display *, struct libdecor_interface *))
|
||||
RA_WAYLAND_SYM(int, libdecor_dispatch, (struct libdecor *, int timeout))
|
||||
RA_WAYLAND_SYM(struct libdecor_frame *, libdecor_decorate, (struct libdecor *,\
|
||||
struct wl_surface *,\
|
||||
struct libdecor_frame_interface *,\
|
||||
void *))
|
||||
RA_WAYLAND_SYM(void, libdecor_frame_unref, (struct libdecor_frame *))
|
||||
RA_WAYLAND_SYM(void, libdecor_frame_set_title, (struct libdecor_frame *, const char *))
|
||||
RA_WAYLAND_SYM(void, libdecor_frame_set_app_id, (struct libdecor_frame *, const char *))
|
||||
RA_WAYLAND_SYM(void, libdecor_frame_set_max_content_size, (struct libdecor_frame *frame,\
|
||||
int content_width,\
|
||||
int content_height))
|
||||
RA_WAYLAND_SYM(void, libdecor_frame_set_min_content_size, (struct libdecor_frame *frame,\
|
||||
int content_width,\
|
||||
int content_height))
|
||||
RA_WAYLAND_SYM(void, libdecor_frame_resize, (struct libdecor_frame *,\
|
||||
struct wl_seat *,\
|
||||
uint32_t,\
|
||||
enum libdecor_resize_edge))
|
||||
RA_WAYLAND_SYM(void, libdecor_frame_move, (struct libdecor_frame *,\
|
||||
struct wl_seat *,\
|
||||
uint32_t))
|
||||
RA_WAYLAND_SYM(void, libdecor_frame_commit, (struct libdecor_frame *,\
|
||||
struct libdecor_state *,\
|
||||
struct libdecor_configuration *))
|
||||
RA_WAYLAND_SYM(void, libdecor_frame_set_minimized, (struct libdecor_frame *))
|
||||
RA_WAYLAND_SYM(void, libdecor_frame_set_maximized, (struct libdecor_frame *))
|
||||
RA_WAYLAND_SYM(void, libdecor_frame_unset_maximized, (struct libdecor_frame *))
|
||||
RA_WAYLAND_SYM(void, libdecor_frame_set_fullscreen, (struct libdecor_frame *, struct wl_output *))
|
||||
RA_WAYLAND_SYM(void, libdecor_frame_unset_fullscreen, (struct libdecor_frame *))
|
||||
RA_WAYLAND_SYM(void, libdecor_frame_set_capabilities, (struct libdecor_frame *, \
|
||||
enum libdecor_capabilities))
|
||||
RA_WAYLAND_SYM(void, libdecor_frame_unset_capabilities, (struct libdecor_frame *, \
|
||||
enum libdecor_capabilities))
|
||||
RA_WAYLAND_SYM(bool, libdecor_frame_has_capability, (struct libdecor_frame *, \
|
||||
enum libdecor_capabilities))
|
||||
RA_WAYLAND_SYM(void, libdecor_frame_set_visibility, (struct libdecor_frame *, bool))
|
||||
RA_WAYLAND_SYM(bool, libdecor_frame_is_visible, (struct libdecor_frame *))
|
||||
RA_WAYLAND_SYM(bool, libdecor_frame_is_floating, (struct libdecor_frame *))
|
||||
RA_WAYLAND_SYM(void, libdecor_frame_set_parent, (struct libdecor_frame *,\
|
||||
struct libdecor_frame *))
|
||||
RA_WAYLAND_SYM(struct xdg_surface *, libdecor_frame_get_xdg_surface, (struct libdecor_frame *))
|
||||
RA_WAYLAND_SYM(struct xdg_toplevel *, libdecor_frame_get_xdg_toplevel, (struct libdecor_frame *))
|
||||
RA_WAYLAND_SYM(void, libdecor_frame_map, (struct libdecor_frame *))
|
||||
RA_WAYLAND_SYM(struct libdecor_state *, libdecor_state_new, (int, int))
|
||||
RA_WAYLAND_SYM(void, libdecor_state_free, (struct libdecor_state *))
|
||||
RA_WAYLAND_SYM(bool, libdecor_configuration_get_content_size, (struct libdecor_configuration *,\
|
||||
struct libdecor_frame *,\
|
||||
int *,\
|
||||
int *))
|
||||
RA_WAYLAND_SYM(bool, libdecor_configuration_get_window_state, (struct libdecor_configuration *,\
|
||||
enum libdecor_window_state *))
|
||||
#endif
|
||||
|
||||
#undef RA_WAYLAND_MODULE
|
||||
#undef RA_WAYLAND_SYM
|
||||
#undef RA_WAYLAND_INTERFACE
|
||||
|
||||
@@ -0,0 +1,782 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2011-2020 - Daniel De Matteis
|
||||
*
|
||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/mman.h>
|
||||
#include <poll.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "wayland_common.h"
|
||||
#include "../../frontend/frontend_driver.h"
|
||||
|
||||
#define SPLASH_SHM_NAME "retroarch-wayland-vk-splash"
|
||||
|
||||
#ifdef HAVE_LIBDECOR_H
|
||||
#include <libdecor.h>
|
||||
#endif
|
||||
|
||||
#define DEFAULT_WINDOWED_WIDTH 640
|
||||
#define DEFAULT_WINDOWED_HEIGHT 480
|
||||
|
||||
void xdg_toplevel_handle_configure_common(gfx_ctx_wayland_data_t *wl,
|
||||
void *toplevel,
|
||||
int32_t width, int32_t height, struct wl_array *states)
|
||||
{
|
||||
const uint32_t *state;
|
||||
|
||||
wl->fullscreen = false;
|
||||
wl->maximized = false;
|
||||
|
||||
WL_ARRAY_FOR_EACH(state, states, const uint32_t*)
|
||||
{
|
||||
switch (*state)
|
||||
{
|
||||
case XDG_TOPLEVEL_STATE_FULLSCREEN:
|
||||
wl->fullscreen = true;
|
||||
break;
|
||||
case XDG_TOPLEVEL_STATE_MAXIMIZED:
|
||||
wl->maximized = true;
|
||||
break;
|
||||
case XDG_TOPLEVEL_STATE_RESIZING:
|
||||
wl->resize = true;
|
||||
break;
|
||||
case XDG_TOPLEVEL_STATE_ACTIVATED:
|
||||
wl->activated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
wl->width = width > 0 ? width : DEFAULT_WINDOWED_WIDTH;
|
||||
wl->height = height > 0 ? height : DEFAULT_WINDOWED_HEIGHT;
|
||||
}
|
||||
|
||||
void xdg_toplevel_handle_close(void *data,
|
||||
struct xdg_toplevel *xdg_toplevel)
|
||||
{
|
||||
gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
|
||||
command_event(CMD_EVENT_QUIT, NULL);
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBDECOR_H
|
||||
void libdecor_frame_handle_configure_common(struct libdecor_frame *frame,
|
||||
struct libdecor_configuration *configuration,
|
||||
gfx_ctx_wayland_data_t *wl)
|
||||
{
|
||||
int width, height;
|
||||
struct libdecor_state *state = NULL;
|
||||
static const enum
|
||||
libdecor_window_state tiled_states = (
|
||||
LIBDECOR_WINDOW_STATE_TILED_LEFT
|
||||
| LIBDECOR_WINDOW_STATE_TILED_RIGHT
|
||||
| LIBDECOR_WINDOW_STATE_TILED_TOP
|
||||
| LIBDECOR_WINDOW_STATE_TILED_BOTTOM
|
||||
);
|
||||
enum libdecor_window_state window_state;
|
||||
bool focused = false;
|
||||
bool tiled = false;
|
||||
|
||||
wl->fullscreen = false;
|
||||
wl->maximized = false;
|
||||
|
||||
if (wl->libdecor_configuration_get_window_state(
|
||||
configuration, &window_state))
|
||||
{
|
||||
wl->fullscreen = (window_state & LIBDECOR_WINDOW_STATE_FULLSCREEN) != 0;
|
||||
wl->maximized = (window_state & LIBDECOR_WINDOW_STATE_MAXIMIZED) != 0;
|
||||
focused = (window_state & LIBDECOR_WINDOW_STATE_ACTIVE) != 0;
|
||||
tiled = (window_state & tiled_states) != 0;
|
||||
}
|
||||
|
||||
if (!wl->libdecor_configuration_get_content_size(configuration, frame,
|
||||
&width, &height))
|
||||
{
|
||||
width = wl->floating_width;
|
||||
height = wl->floating_height;
|
||||
}
|
||||
|
||||
if ( width > 0
|
||||
&& height > 0)
|
||||
{
|
||||
wl->width = width;
|
||||
wl->height = height;
|
||||
}
|
||||
|
||||
state = wl->libdecor_state_new(wl->width, wl->height);
|
||||
wl->libdecor_frame_commit(frame, state, configuration);
|
||||
wl->libdecor_state_free(state);
|
||||
|
||||
if (wl->libdecor_frame_is_floating(frame)) {
|
||||
wl->floating_width = width;
|
||||
wl->floating_height = height;
|
||||
}
|
||||
}
|
||||
|
||||
void libdecor_frame_handle_close(struct libdecor_frame *frame,
|
||||
void *data)
|
||||
{
|
||||
gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
|
||||
command_event(CMD_EVENT_QUIT, NULL);
|
||||
}
|
||||
|
||||
void libdecor_frame_handle_commit(struct libdecor_frame *frame,
|
||||
void *data)
|
||||
{
|
||||
gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void gfx_ctx_wl_get_video_size_common(gfx_ctx_wayland_data_t *wl,
|
||||
unsigned *width, unsigned *height)
|
||||
{
|
||||
if (!wl->reported_display_size) {
|
||||
wl->reported_display_size = true;
|
||||
output_info_t *oi, *tmp;
|
||||
oi = wl->current_output;
|
||||
|
||||
// If window is not ready get any monitor
|
||||
if (!oi)
|
||||
wl_list_for_each_safe(oi, tmp, &wl->all_outputs, link)
|
||||
break;
|
||||
|
||||
*width = oi->width;
|
||||
*height = oi->height;
|
||||
} else {
|
||||
*width = wl->width * wl->buffer_scale;
|
||||
*height = wl->height * wl->buffer_scale;
|
||||
}
|
||||
}
|
||||
|
||||
void gfx_ctx_wl_destroy_resources_common(gfx_ctx_wayland_data_t *wl)
|
||||
{
|
||||
if (wl->input.dpy != NULL && wl->input.fd >= 0)
|
||||
close(wl->input.fd);
|
||||
|
||||
#ifdef HAVE_XKBCOMMON
|
||||
free_xkb();
|
||||
#endif
|
||||
|
||||
if (wl->wl_keyboard)
|
||||
wl_keyboard_destroy(wl->wl_keyboard);
|
||||
if (wl->wl_pointer)
|
||||
wl_pointer_destroy(wl->wl_pointer);
|
||||
if (wl->wl_touch)
|
||||
wl_touch_destroy(wl->wl_touch);
|
||||
|
||||
if (wl->cursor.theme)
|
||||
wl_cursor_theme_destroy(wl->cursor.theme);
|
||||
if (wl->cursor.surface)
|
||||
wl_surface_destroy(wl->cursor.surface);
|
||||
|
||||
if (wl->seat)
|
||||
wl_seat_destroy(wl->seat);
|
||||
if (wl->xdg_shell)
|
||||
xdg_wm_base_destroy(wl->xdg_shell);
|
||||
if (wl->compositor)
|
||||
wl_compositor_destroy(wl->compositor);
|
||||
if (wl->registry)
|
||||
wl_registry_destroy(wl->registry);
|
||||
if (wl->xdg_surface)
|
||||
xdg_surface_destroy(wl->xdg_surface);
|
||||
if (wl->surface)
|
||||
wl_surface_destroy(wl->surface);
|
||||
if (wl->xdg_toplevel)
|
||||
xdg_toplevel_destroy(wl->xdg_toplevel);
|
||||
if (wl->idle_inhibit_manager)
|
||||
zwp_idle_inhibit_manager_v1_destroy(wl->idle_inhibit_manager);
|
||||
if (wl->deco)
|
||||
zxdg_toplevel_decoration_v1_destroy(wl->deco);
|
||||
if (wl->deco_manager)
|
||||
zxdg_decoration_manager_v1_destroy(wl->deco_manager);
|
||||
if (wl->idle_inhibitor)
|
||||
zwp_idle_inhibitor_v1_destroy(wl->idle_inhibitor);
|
||||
|
||||
if (wl->input.dpy)
|
||||
{
|
||||
wl_display_flush(wl->input.dpy);
|
||||
wl_display_disconnect(wl->input.dpy);
|
||||
}
|
||||
|
||||
wl->xdg_shell = NULL;
|
||||
wl->compositor = NULL;
|
||||
wl->registry = NULL;
|
||||
wl->input.dpy = NULL;
|
||||
wl->xdg_surface = NULL;
|
||||
wl->surface = NULL;
|
||||
wl->xdg_toplevel = NULL;
|
||||
|
||||
wl->width = 0;
|
||||
wl->height = 0;
|
||||
}
|
||||
|
||||
void gfx_ctx_wl_update_title_common(gfx_ctx_wayland_data_t *wl)
|
||||
{
|
||||
char title[128];
|
||||
|
||||
title[0] = '\0';
|
||||
|
||||
video_driver_get_window_title(title, sizeof(title));
|
||||
|
||||
#ifdef HAVE_LIBDECOR_H
|
||||
if (wl->libdecor)
|
||||
{
|
||||
if (wl && title[0])
|
||||
wl->libdecor_frame_set_title(wl->libdecor_frame, title);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if (wl && title[0])
|
||||
{
|
||||
if (wl->deco)
|
||||
zxdg_toplevel_decoration_v1_set_mode(wl->deco,
|
||||
ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
|
||||
xdg_toplevel_set_title(wl->xdg_toplevel, title);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool gfx_ctx_wl_get_metrics_common(gfx_ctx_wayland_data_t *wl,
|
||||
enum display_metric_types type, float *value)
|
||||
{
|
||||
output_info_t *oi, *tmp;
|
||||
oi = wl->current_output;
|
||||
|
||||
if (!oi)
|
||||
wl_list_for_each_safe(oi, tmp, &wl->all_outputs, link)
|
||||
break;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case DISPLAY_METRIC_MM_WIDTH:
|
||||
*value = (float)oi->physical_width;
|
||||
break;
|
||||
|
||||
case DISPLAY_METRIC_MM_HEIGHT:
|
||||
*value = (float)oi->physical_height;
|
||||
break;
|
||||
|
||||
case DISPLAY_METRIC_DPI:
|
||||
*value = (float)oi->width * 25.4f /
|
||||
(float)oi->physical_width;
|
||||
break;
|
||||
|
||||
default:
|
||||
*value = 0.0f;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gfx_ctx_wl_init_common(void *video_driver,
|
||||
const toplevel_listener_t *toplevel_listener, gfx_ctx_wayland_data_t **wwl)
|
||||
{
|
||||
int i;
|
||||
*wwl = calloc(1, sizeof(gfx_ctx_wayland_data_t));
|
||||
gfx_ctx_wayland_data_t *wl = *wwl;
|
||||
|
||||
if (!wl)
|
||||
return false;
|
||||
|
||||
|
||||
#ifdef HAVE_LIBDECOR_H
|
||||
#ifdef HAVE_DYNAMIC
|
||||
wl->libdecor = dylib_load("libdecor-0.so");
|
||||
if (wl->libdecor)
|
||||
{
|
||||
#define RA_WAYLAND_SYM(rc,fn,params) wl->fn = (rc (*) params)dylib_proc(wl->libdecor, #fn);
|
||||
#include "wayland/libdecor_sym.h"
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
wl_list_init(&wl->all_outputs);
|
||||
|
||||
frontend_driver_destroy_signal_handler_state();
|
||||
|
||||
wl->input.dpy = wl_display_connect(NULL);
|
||||
wl->last_buffer_scale = 1;
|
||||
wl->buffer_scale = 1;
|
||||
wl->floating_width = DEFAULT_WINDOWED_WIDTH;
|
||||
wl->floating_height = DEFAULT_WINDOWED_HEIGHT;
|
||||
|
||||
if (!wl->input.dpy)
|
||||
{
|
||||
RARCH_ERR("[Wayland]: Failed to connect to Wayland server.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
frontend_driver_install_signal_handler();
|
||||
|
||||
wl->registry = wl_display_get_registry(wl->input.dpy);
|
||||
wl_registry_add_listener(wl->registry, ®istry_listener, wl);
|
||||
wl_display_roundtrip(wl->input.dpy);
|
||||
|
||||
if (!wl->compositor)
|
||||
{
|
||||
RARCH_ERR("[Wayland]: Failed to create compositor.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!wl->shm)
|
||||
{
|
||||
RARCH_ERR("[Wayland]: Failed to create shm.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!wl->xdg_shell)
|
||||
{
|
||||
RARCH_ERR("[Wayland]: Failed to create shell.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!wl->idle_inhibit_manager)
|
||||
{
|
||||
RARCH_LOG("[Wayland]: Compositor doesn't support zwp_idle_inhibit_manager_v1 protocol\n");
|
||||
}
|
||||
|
||||
if (!wl->deco_manager)
|
||||
{
|
||||
RARCH_LOG("[Wayland]: Compositor doesn't support zxdg_decoration_manager_v1 protocol\n");
|
||||
}
|
||||
|
||||
wl->surface = wl_compositor_create_surface(wl->compositor);
|
||||
|
||||
wl_surface_set_buffer_scale(wl->surface, wl->buffer_scale);
|
||||
wl_surface_add_listener(wl->surface, &wl_surface_listener, wl);
|
||||
|
||||
#ifdef HAVE_LIBDECOR_H
|
||||
if (wl->libdecor)
|
||||
{
|
||||
wl->libdecor_context = wl->libdecor_new(wl->input.dpy, &libdecor_interface);
|
||||
if (wl->libdecor_context)
|
||||
{
|
||||
wl->libdecor_frame = wl->libdecor_decorate(wl->libdecor_context, wl->surface, &toplevel_listener->libdecor_frame_interface, wl);
|
||||
if (!wl->libdecor_frame)
|
||||
{
|
||||
RARCH_ERR("[Wayland]: Failed to crate libdecor frame\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
wl->libdecor_frame_set_app_id(wl->libdecor_frame, "retroarch");
|
||||
wl->libdecor_frame_set_title(wl->libdecor_frame, "RetroArch");
|
||||
wl->libdecor_frame_map(wl->libdecor_frame);
|
||||
}
|
||||
|
||||
/* Waiting for libdecor to be configured before starting to draw */
|
||||
wl_surface_commit(wl->surface);
|
||||
wl->configured = true;
|
||||
|
||||
while (wl->configured)
|
||||
{
|
||||
if (wl->libdecor_dispatch(wl->libdecor_context, 0) < 0)
|
||||
{
|
||||
RARCH_ERR("[Wayland]: libdecor failed to dispatch\n");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
wl->xdg_surface = xdg_wm_base_get_xdg_surface(wl->xdg_shell, wl->surface);
|
||||
xdg_surface_add_listener(wl->xdg_surface, &xdg_surface_listener, wl);
|
||||
|
||||
wl->xdg_toplevel = xdg_surface_get_toplevel(wl->xdg_surface);
|
||||
xdg_toplevel_add_listener(wl->xdg_toplevel, &toplevel_listener->xdg_toplevel_listener, wl);
|
||||
|
||||
xdg_toplevel_set_app_id(wl->xdg_toplevel, "retroarch");
|
||||
xdg_toplevel_set_title(wl->xdg_toplevel, "RetroArch");
|
||||
|
||||
if (wl->deco_manager)
|
||||
wl->deco = zxdg_decoration_manager_v1_get_toplevel_decoration(
|
||||
wl->deco_manager, wl->xdg_toplevel);
|
||||
|
||||
/* Waiting for xdg_toplevel to be configured before starting to draw */
|
||||
wl_surface_commit(wl->surface);
|
||||
wl->configured = true;
|
||||
|
||||
while (wl->configured)
|
||||
wl_display_dispatch(wl->input.dpy);
|
||||
}
|
||||
|
||||
wl_display_roundtrip(wl->input.dpy);
|
||||
xdg_wm_base_add_listener(wl->xdg_shell, &xdg_shell_listener, NULL);
|
||||
|
||||
/* Bind SHM based wl_buffer to wl_surface until the vulkan surface is ready.
|
||||
* This shows the window which assigns us a display (wl_output)
|
||||
* which is usefull for HiDPI and auto selecting a display for fullscreen. */
|
||||
if (!draw_splash_screen(wl))
|
||||
RARCH_ERR("[Wayland`]: Failed to draw splash screen\n");
|
||||
|
||||
wl_display_roundtrip(wl->input.dpy);
|
||||
|
||||
wl->input.fd = wl_display_get_fd(wl->input.dpy);
|
||||
|
||||
wl->input.keyboard_focus = true;
|
||||
wl->input.mouse.focus = true;
|
||||
|
||||
wl->cursor.surface = wl_compositor_create_surface(wl->compositor);
|
||||
wl->cursor.theme = wl_cursor_theme_load(NULL, 16, wl->shm);
|
||||
wl->cursor.default_cursor = wl_cursor_theme_get_cursor(wl->cursor.theme, "left_ptr");
|
||||
|
||||
wl->num_active_touches = 0;
|
||||
|
||||
for (i = 0;i < MAX_TOUCHES;i++)
|
||||
{
|
||||
wl->active_touch_positions[i].active = false;
|
||||
wl->active_touch_positions[i].id = -1;
|
||||
wl->active_touch_positions[i].x = (unsigned) 0;
|
||||
wl->active_touch_positions[i].y = (unsigned) 0;
|
||||
}
|
||||
|
||||
flush_wayland_fd(&wl->input);
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool gfx_ctx_wl_set_video_mode_common_size(gfx_ctx_wayland_data_t *wl,
|
||||
unsigned width, unsigned height)
|
||||
{
|
||||
settings_t *settings = config_get_ptr();
|
||||
unsigned video_monitor_index = settings->uints.video_monitor_index;
|
||||
|
||||
wl->width = width ? width : DEFAULT_WINDOWED_WIDTH;
|
||||
wl->height = height ? height : DEFAULT_WINDOWED_HEIGHT;
|
||||
|
||||
wl_surface_set_buffer_scale(wl->surface, wl->buffer_scale);
|
||||
|
||||
#ifdef HAVE_LIBDECOR_H
|
||||
if (wl->libdecor)
|
||||
{
|
||||
struct libdecor_state *state = wl->libdecor_state_new(width, height);
|
||||
wl->libdecor_frame_commit(wl->libdecor_frame, state, NULL);
|
||||
wl->libdecor_state_free(state);
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
return false;
|
||||
}
|
||||
|
||||
bool gfx_ctx_wl_set_video_mode_common_fullscreen(gfx_ctx_wayland_data_t *wl,
|
||||
bool fullscreen)
|
||||
{
|
||||
settings_t *settings = config_get_ptr();
|
||||
unsigned video_monitor_index = settings->uints.video_monitor_index;
|
||||
|
||||
if (fullscreen)
|
||||
{
|
||||
struct wl_output *output = NULL;
|
||||
int output_i = 0;
|
||||
output_info_t *oi, *tmp;
|
||||
|
||||
if (video_monitor_index <= 0 && wl->current_output != NULL)
|
||||
{
|
||||
oi = wl->current_output;
|
||||
output = oi->output;
|
||||
RARCH_LOG("[Wayland]: Auto fullscreen on display \"%s\" \"%s\"\n", oi->make, oi->model);
|
||||
}
|
||||
else wl_list_for_each_safe(oi, tmp, &wl->all_outputs, link)
|
||||
{
|
||||
if (++output_i == video_monitor_index)
|
||||
{
|
||||
output = oi->output;
|
||||
RARCH_LOG("[Wayland]: Fullscreen on display %i \"%s\" \"%s\"\n", output_i, oi->make, oi->model);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (output == NULL)
|
||||
RARCH_LOG("[Wayland] Failed to specify monitor for fullscreen, letting compositor decide\n");
|
||||
|
||||
#ifdef HAVE_LIBDECOR_H
|
||||
if (wl->libdecor)
|
||||
wl->libdecor_frame_set_fullscreen(wl->libdecor_frame, output);
|
||||
else
|
||||
#endif
|
||||
{
|
||||
xdg_toplevel_set_fullscreen(wl->xdg_toplevel, output);
|
||||
}
|
||||
}
|
||||
|
||||
flush_wayland_fd(&wl->input);
|
||||
|
||||
if (fullscreen)
|
||||
{
|
||||
wl->cursor.visible = false;
|
||||
gfx_ctx_wl_show_mouse(wl, false);
|
||||
}
|
||||
else
|
||||
wl->cursor.visible = true;
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
return false;
|
||||
}
|
||||
|
||||
bool gfx_ctx_wl_suppress_screensaver(void *data, bool state)
|
||||
{
|
||||
gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
|
||||
|
||||
if (!wl->idle_inhibit_manager)
|
||||
return false;
|
||||
if (state == (!!wl->idle_inhibitor))
|
||||
return true;
|
||||
|
||||
if (state)
|
||||
{
|
||||
RARCH_LOG("[Wayland]: Enabling idle inhibitor\n");
|
||||
struct zwp_idle_inhibit_manager_v1 *mgr = wl->idle_inhibit_manager;
|
||||
wl->idle_inhibitor = zwp_idle_inhibit_manager_v1_create_inhibitor(mgr, wl->surface);
|
||||
}
|
||||
else
|
||||
{
|
||||
RARCH_LOG("[Wayland]: Disabling the idle inhibitor\n");
|
||||
zwp_idle_inhibitor_v1_destroy(wl->idle_inhibitor);
|
||||
wl->idle_inhibitor = NULL;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
float gfx_ctx_wl_get_refresh_rate(void *data)
|
||||
{
|
||||
gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
|
||||
|
||||
if (!wl || !wl->current_output)
|
||||
return false;
|
||||
|
||||
return (float) wl->current_output->refresh_rate / 1000.0f;
|
||||
}
|
||||
|
||||
bool gfx_ctx_wl_has_focus(void *data)
|
||||
{
|
||||
gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
|
||||
return wl->input.keyboard_focus;
|
||||
}
|
||||
|
||||
void gfx_ctx_wl_check_window_common(gfx_ctx_wayland_data_t *wl,
|
||||
void (*get_video_size)(void*, unsigned*, unsigned*), bool *quit,
|
||||
bool *resize, unsigned *width, unsigned *height)
|
||||
{
|
||||
/* this function works with SCALED sizes, it's used from the renderer */
|
||||
unsigned new_width, new_height;
|
||||
|
||||
flush_wayland_fd(&wl->input);
|
||||
|
||||
new_width = *width * wl->last_buffer_scale;
|
||||
new_height = *height * wl->last_buffer_scale;
|
||||
|
||||
get_video_size(wl, &new_width, &new_height);
|
||||
|
||||
if ( new_width != *width * wl->last_buffer_scale
|
||||
|| new_height != *height * wl->last_buffer_scale)
|
||||
{
|
||||
*width = new_width;
|
||||
*height = new_height;
|
||||
*resize = true;
|
||||
|
||||
wl->last_buffer_scale = wl->buffer_scale;
|
||||
}
|
||||
|
||||
*quit = (bool)frontend_driver_get_signal_handler_state();
|
||||
}
|
||||
|
||||
static void shm_buffer_handle_release(void *data,
|
||||
struct wl_buffer *wl_buffer)
|
||||
{
|
||||
shm_buffer_t *buffer = data;
|
||||
|
||||
wl_buffer_destroy(buffer->wl_buffer);
|
||||
munmap(buffer->data, buffer->data_size);
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
static void xdg_surface_handle_configure(void *data, struct xdg_surface *surface,
|
||||
uint32_t serial)
|
||||
{
|
||||
xdg_surface_ack_configure(surface, serial);
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBDECOR_H
|
||||
static void libdecor_handle_error(struct libdecor *context,
|
||||
enum libdecor_error error, const char *message)
|
||||
{
|
||||
RARCH_ERR("[Wayland]: libdecor Caught error (%d): %s\n", error, message);
|
||||
}
|
||||
#endif
|
||||
|
||||
int create_shm_file(off_t size)
|
||||
{
|
||||
int fd;
|
||||
|
||||
int ret;
|
||||
|
||||
#ifdef HAVE_MEMFD_CREATE
|
||||
fd = memfd_create(SPLASH_SHM_NAME, MFD_CLOEXEC | MFD_ALLOW_SEALING);
|
||||
|
||||
if (fd >= 0) {
|
||||
fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK);
|
||||
|
||||
do {
|
||||
ret = posix_fallocate(fd, 0, size);
|
||||
} while (ret == EINTR);
|
||||
if (ret != 0) {
|
||||
close(fd);
|
||||
errno = ret;
|
||||
fd = -1;
|
||||
}
|
||||
}
|
||||
if (fd < 0)
|
||||
#endif
|
||||
{
|
||||
for (unsigned retry_count = 0; retry_count < 100; retry_count++)
|
||||
{
|
||||
char *name;
|
||||
if (asprintf (&name, "%s-%02d", SPLASH_SHM_NAME, retry_count) < 0)
|
||||
continue;
|
||||
fd = shm_open(name, O_RDWR | O_CREAT, 0600);
|
||||
if (fd >= 0)
|
||||
{
|
||||
shm_unlink(name);
|
||||
free(name);
|
||||
ftruncate(fd, size);
|
||||
break;
|
||||
}
|
||||
free(name);
|
||||
}
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
shm_buffer_t *create_shm_buffer(gfx_ctx_wayland_data_t *wl, int width,
|
||||
int height,
|
||||
uint32_t format)
|
||||
{
|
||||
struct wl_shm_pool *pool;
|
||||
int fd, size, stride, ofd;
|
||||
void *data;
|
||||
shm_buffer_t *buffer;
|
||||
|
||||
stride = width * 4;
|
||||
size = stride * height;
|
||||
|
||||
if (size <= 0)
|
||||
return NULL;
|
||||
|
||||
fd = create_shm_file(size);
|
||||
if (fd < 0) {
|
||||
RARCH_ERR("[Wayland] [SHM]: Creating a buffer file for %d B failed: %s\n",
|
||||
size, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (data == MAP_FAILED) {
|
||||
RARCH_ERR("[Wayland] [SHM]: mmap failed: %s\n", strerror(errno));
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buffer = calloc(1, sizeof *buffer);
|
||||
|
||||
pool = wl_shm_create_pool(wl->shm, fd, size);
|
||||
buffer->wl_buffer = wl_shm_pool_create_buffer(pool, 0,
|
||||
width, height,
|
||||
stride, format);
|
||||
wl_buffer_add_listener(buffer->wl_buffer, &shm_buffer_listener, buffer);
|
||||
wl_shm_pool_destroy(pool);
|
||||
|
||||
close(fd);
|
||||
|
||||
buffer->data = data;
|
||||
buffer->data_size = size;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
void shm_buffer_paint_checkerboard(shm_buffer_t *buffer,
|
||||
int width, int height, int scale,
|
||||
size_t chk, uint32_t bg, uint32_t fg)
|
||||
{
|
||||
uint32_t *pixels = buffer->data;
|
||||
uint32_t color;
|
||||
int y, x, sx, sy;
|
||||
size_t off;
|
||||
int stride = width * scale;
|
||||
|
||||
for (y = 0; y < height; y++) {
|
||||
for (x = 0; x < width; x++) {
|
||||
color = (x & chk) ^ (y & chk) ? fg : bg;
|
||||
for (sx = 0; sx < scale; sx++) {
|
||||
for (sy = 0; sy < scale; sy++) {
|
||||
off = x * scale + sx
|
||||
+ (y * scale + sy) * stride;
|
||||
pixels[off] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool draw_splash_screen(gfx_ctx_wayland_data_t *wl)
|
||||
{
|
||||
shm_buffer_t *buffer;
|
||||
|
||||
buffer = create_shm_buffer(wl,
|
||||
wl->width * wl->buffer_scale,
|
||||
wl->height * wl->buffer_scale,
|
||||
WL_SHM_FORMAT_XRGB8888);
|
||||
|
||||
if (buffer == NULL)
|
||||
return false;
|
||||
|
||||
shm_buffer_paint_checkerboard(buffer, wl->width,
|
||||
wl->height, wl->buffer_scale,
|
||||
16, 0xffbcbcbc, 0xff8e8e8e);
|
||||
|
||||
wl_surface_attach(wl->surface, buffer->wl_buffer, 0, 0);
|
||||
wl_surface_set_buffer_scale(wl->surface, wl->buffer_scale);
|
||||
if (wl_surface_get_version(wl->surface) >= WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION)
|
||||
wl_surface_damage_buffer(wl->surface, 0, 0,
|
||||
wl->width * wl->buffer_scale,
|
||||
wl->height * wl->buffer_scale);
|
||||
wl_surface_commit(wl->surface);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
const struct wl_buffer_listener shm_buffer_listener = {
|
||||
shm_buffer_handle_release,
|
||||
};
|
||||
|
||||
#ifdef HAVE_LIBDECOR_H
|
||||
const struct libdecor_interface libdecor_interface = {
|
||||
.error = libdecor_handle_error,
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,102 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2011-2017 - Daniel De Matteis
|
||||
*
|
||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef HAVE_LIBDECOR_H
|
||||
#include <libdecor.h>
|
||||
#endif
|
||||
|
||||
#include "../../input/common/wayland_common.h"
|
||||
|
||||
typedef struct toplevel_listener {
|
||||
#ifdef HAVE_LIBDECOR_H
|
||||
struct libdecor_frame_interface libdecor_frame_interface;
|
||||
#endif
|
||||
struct xdg_toplevel_listener xdg_toplevel_listener;
|
||||
} toplevel_listener_t;
|
||||
|
||||
typedef struct shm_buffer {
|
||||
struct wl_buffer *wl_buffer;
|
||||
void *data;
|
||||
size_t data_size;
|
||||
} shm_buffer_t;
|
||||
|
||||
void xdg_toplevel_handle_configure_common(gfx_ctx_wayland_data_t *wl, void *toplevel,
|
||||
int32_t width, int32_t height, struct wl_array *states);
|
||||
|
||||
void xdg_toplevel_handle_close(void *data,
|
||||
struct xdg_toplevel *xdg_toplevel);
|
||||
|
||||
#ifdef HAVE_LIBDECOR_H
|
||||
void libdecor_frame_handle_configure_common(struct libdecor_frame *frame,
|
||||
struct libdecor_configuration *configuration, gfx_ctx_wayland_data_t *wl);
|
||||
|
||||
void libdecor_frame_handle_close(struct libdecor_frame *frame,
|
||||
void *data);
|
||||
|
||||
void libdecor_frame_handle_commit(struct libdecor_frame *frame,
|
||||
void *data);
|
||||
#endif
|
||||
|
||||
void gfx_ctx_wl_get_video_size_common(gfx_ctx_wayland_data_t *wl,
|
||||
unsigned *width, unsigned *height);
|
||||
|
||||
void gfx_ctx_wl_destroy_resources_common(gfx_ctx_wayland_data_t *wl);
|
||||
|
||||
void gfx_ctx_wl_update_title_common(gfx_ctx_wayland_data_t *wl);
|
||||
|
||||
bool gfx_ctx_wl_get_metrics_common(gfx_ctx_wayland_data_t *wl,
|
||||
enum display_metric_types type, float *value);
|
||||
|
||||
bool gfx_ctx_wl_init_common(void *video_driver,
|
||||
const toplevel_listener_t *toplevel_listener,
|
||||
gfx_ctx_wayland_data_t **wl);
|
||||
|
||||
bool gfx_ctx_wl_set_video_mode_common_size(gfx_ctx_wayland_data_t *wl,
|
||||
unsigned width, unsigned height);
|
||||
|
||||
bool gfx_ctx_wl_set_video_mode_common_fullscreen(gfx_ctx_wayland_data_t *wl,
|
||||
bool fullscreen);
|
||||
|
||||
bool gfx_ctx_wl_suppress_screensaver(void *data, bool state);
|
||||
|
||||
bool gfx_ctx_wl_set_video_mode_common(gfx_ctx_wayland_data_t *wl,
|
||||
unsigned width, unsigned height,
|
||||
bool fullscreen);
|
||||
|
||||
float gfx_ctx_wl_get_refresh_rate(void *data);
|
||||
|
||||
bool gfx_ctx_wl_has_focus(void *data);
|
||||
|
||||
void gfx_ctx_wl_check_window_common(gfx_ctx_wayland_data_t *wl,
|
||||
void (*get_video_size)(void*, unsigned*, unsigned*), bool *quit,
|
||||
bool *resize, unsigned *width, unsigned *height);
|
||||
|
||||
int create_shm_file(off_t size);
|
||||
|
||||
shm_buffer_t *create_shm_buffer(gfx_ctx_wayland_data_t *wl,
|
||||
int width, int height, uint32_t format);
|
||||
|
||||
void shm_buffer_paint_checkerboard(shm_buffer_t *buffer,
|
||||
int width, int height, int scale,
|
||||
size_t chk, uint32_t bg, uint32_t fg);
|
||||
|
||||
bool draw_splash_screen(gfx_ctx_wayland_data_t *wl);
|
||||
|
||||
#ifdef HAVE_LIBDECOR_H
|
||||
extern const struct libdecor_interface libdecor_interface;
|
||||
#endif
|
||||
|
||||
@@ -1828,12 +1828,16 @@ void win32_clip_window(bool state)
|
||||
{
|
||||
PWINDOWINFO info;
|
||||
info = (PWINDOWINFO)malloc(sizeof(*info));
|
||||
info->cbSize = sizeof(PWINDOWINFO);
|
||||
|
||||
if (GetWindowInfo(main_window.hwnd, info))
|
||||
clip_rect = info->rcClient;
|
||||
if (info)
|
||||
{
|
||||
info->cbSize = sizeof(PWINDOWINFO);
|
||||
|
||||
free(info);
|
||||
if (GetWindowInfo(main_window.hwnd, info))
|
||||
clip_rect = info->rcClient;
|
||||
|
||||
free(info);
|
||||
}
|
||||
info = NULL;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -115,6 +115,9 @@ static void win32_display_server_destroy(void *data)
|
||||
{
|
||||
dispserv_win32_t *dispserv = (dispserv_win32_t*)data;
|
||||
|
||||
if (!dispserv)
|
||||
return;
|
||||
|
||||
if (dispserv->orig_width > 0 && dispserv->orig_height > 0)
|
||||
video_display_server_set_resolution(
|
||||
dispserv->orig_width,
|
||||
@@ -131,8 +134,7 @@ static void win32_display_server_destroy(void *data)
|
||||
}
|
||||
#endif
|
||||
|
||||
if (dispserv)
|
||||
free(dispserv);
|
||||
free(dispserv);
|
||||
}
|
||||
|
||||
static bool win32_display_server_set_window_opacity(
|
||||
@@ -170,8 +172,10 @@ static bool win32_display_server_set_window_progress(
|
||||
HWND hwnd = win32_get_window();
|
||||
dispserv_win32_t *serv = (dispserv_win32_t*)data;
|
||||
|
||||
if (serv)
|
||||
serv->progress = progress;
|
||||
if (!serv)
|
||||
return false;
|
||||
|
||||
serv->progress = progress;
|
||||
|
||||
#ifdef HAS_TASKBAR_EXT
|
||||
if (!serv->taskbar_list || !win32_taskbar_is_created())
|
||||
@@ -208,8 +212,10 @@ static bool win32_display_server_set_window_decorations(void *data, bool on)
|
||||
{
|
||||
dispserv_win32_t *serv = (dispserv_win32_t*)data;
|
||||
|
||||
if (serv)
|
||||
serv->decorations = on;
|
||||
if (!serv)
|
||||
return false;
|
||||
|
||||
serv->decorations = on;
|
||||
|
||||
/* menu_setting performs a reinit instead to properly
|
||||
* apply decoration changes */
|
||||
@@ -316,6 +322,8 @@ static int resolution_list_qsort_func(
|
||||
if (!a || !b)
|
||||
return 0;
|
||||
|
||||
str_a[0] = str_b[0] = '\0';
|
||||
|
||||
snprintf(str_a, sizeof(str_a), "%04dx%04d (%d Hz)",
|
||||
a->width,
|
||||
a->height,
|
||||
@@ -333,7 +341,9 @@ static void *win32_display_server_get_resolution_list(
|
||||
void *data, unsigned *len)
|
||||
{
|
||||
DEVMODE dm = {0};
|
||||
unsigned i, j, count = 0;
|
||||
unsigned i = 0;
|
||||
unsigned j = 0;
|
||||
unsigned count = 0;
|
||||
unsigned curr_width = 0;
|
||||
unsigned curr_height = 0;
|
||||
unsigned curr_bpp = 0;
|
||||
|
||||
@@ -1033,6 +1033,7 @@ static void* ctr_init(const video_info_t* video,
|
||||
void* ctrinput = NULL;
|
||||
settings_t *settings = config_get_ptr();
|
||||
bool lcd_bottom = settings->bools.video_3ds_lcd_bottom;
|
||||
bool speedup_enable = settings->bools.new3ds_speedup_enable;
|
||||
ctr_video_t* ctr = (ctr_video_t*)linearAlloc(sizeof(ctr_video_t));
|
||||
|
||||
if (!ctr)
|
||||
@@ -1220,6 +1221,8 @@ static void* ctr_init(const video_info_t* video,
|
||||
gspSetEventCallback(GSPGPU_EVENT_VBlank0,
|
||||
(ThreadFunc)ctr_vsync_hook, ctr, false);
|
||||
|
||||
osSetSpeedupEnable(speedup_enable);
|
||||
|
||||
return ctr;
|
||||
}
|
||||
|
||||
|
||||
+34
-19
@@ -96,10 +96,14 @@ d3d10_overlay_vertex_geom(void* data, unsigned index, float x, float y, float w,
|
||||
|
||||
D3D10MapBuffer(d3d10->overlays.vbo,
|
||||
D3D10_MAP_WRITE_NO_OVERWRITE, 0, (void**)&sprites);
|
||||
sprites[index].pos.x = x;
|
||||
sprites[index].pos.y = y;
|
||||
sprites[index].pos.w = w;
|
||||
sprites[index].pos.h = h;
|
||||
|
||||
if (sprites)
|
||||
{
|
||||
sprites[index].pos.x = x;
|
||||
sprites[index].pos.y = y;
|
||||
sprites[index].pos.w = w;
|
||||
sprites[index].pos.h = h;
|
||||
}
|
||||
D3D10UnmapBuffer(d3d10->overlays.vbo);
|
||||
}
|
||||
|
||||
@@ -132,20 +136,24 @@ static void d3d10_overlay_set_alpha(void* data, unsigned index, float mod)
|
||||
D3D10MapBuffer(
|
||||
d3d10->overlays.vbo,
|
||||
D3D10_MAP_WRITE_NO_OVERWRITE, 0, (void**)&sprites);
|
||||
sprites[index].colors[0] = DXGI_COLOR_RGBA(0xFF, 0xFF, 0xFF, mod * 0xFF);
|
||||
sprites[index].colors[1] = sprites[index].colors[0];
|
||||
sprites[index].colors[2] = sprites[index].colors[0];
|
||||
sprites[index].colors[3] = sprites[index].colors[0];
|
||||
|
||||
if (sprites)
|
||||
{
|
||||
sprites[index].colors[0] = DXGI_COLOR_RGBA(0xFF, 0xFF, 0xFF, mod * 0xFF);
|
||||
sprites[index].colors[1] = sprites[index].colors[0];
|
||||
sprites[index].colors[2] = sprites[index].colors[0];
|
||||
sprites[index].colors[3] = sprites[index].colors[0];
|
||||
}
|
||||
D3D10UnmapBuffer(d3d10->overlays.vbo);
|
||||
}
|
||||
|
||||
static bool d3d10_overlay_load(void* data, const void* image_data, unsigned num_images)
|
||||
{
|
||||
D3D10_BUFFER_DESC desc;
|
||||
unsigned i;
|
||||
d3d10_sprite_t* sprites;
|
||||
d3d10_video_t* d3d10 = (d3d10_video_t*)data;
|
||||
const struct texture_image* images = (const struct texture_image*)image_data;
|
||||
unsigned i = 0;
|
||||
d3d10_sprite_t* sprites = NULL;
|
||||
d3d10_video_t* d3d10 = (d3d10_video_t*)data;
|
||||
const struct texture_image* images = (const struct texture_image*)image_data;
|
||||
|
||||
if (!d3d10)
|
||||
return false;
|
||||
@@ -268,11 +276,14 @@ static void d3d10_set_filtering(void* data, unsigned index, bool smooth, bool ct
|
||||
unsigned i;
|
||||
d3d10_video_t* d3d10 = (d3d10_video_t*)data;
|
||||
|
||||
for (i = 0; i < RARCH_WRAP_MAX; i++)
|
||||
if (smooth)
|
||||
{
|
||||
if (smooth)
|
||||
for (i = 0; i < RARCH_WRAP_MAX; i++)
|
||||
d3d10->samplers[RARCH_FILTER_UNSPEC][i] = d3d10->samplers[RARCH_FILTER_LINEAR][i];
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < RARCH_WRAP_MAX; i++)
|
||||
d3d10->samplers[RARCH_FILTER_UNSPEC][i] = d3d10->samplers[RARCH_FILTER_NEAREST][i];
|
||||
}
|
||||
}
|
||||
@@ -332,6 +343,9 @@ static void d3d10_free_shader_preset(d3d10_video_t* d3d10)
|
||||
free(d3d10->shader_preset->pass[i].source.string.vertex);
|
||||
free(d3d10->shader_preset->pass[i].source.string.fragment);
|
||||
free(d3d10->pass[i].semantics.textures);
|
||||
d3d10->shader_preset->pass[i].source.string.vertex = NULL;
|
||||
d3d10->shader_preset->pass[i].source.string.fragment = NULL;
|
||||
d3d10->pass[i].semantics.textures = NULL;
|
||||
d3d10_release_shader(&d3d10->pass[i].shader);
|
||||
d3d10_release_texture(&d3d10->pass[i].rt);
|
||||
d3d10_release_texture(&d3d10->pass[i].feedback);
|
||||
@@ -994,9 +1008,9 @@ static void *d3d10_gfx_init(const video_info_t* video,
|
||||
{
|
||||
D3D10_RASTERIZER_DESC desc = { (D3D10_FILL_MODE)0 };
|
||||
|
||||
desc.FillMode = D3D10_FILL_SOLID;
|
||||
desc.CullMode = D3D10_CULL_NONE;
|
||||
desc.ScissorEnable = TRUE;
|
||||
desc.FillMode = D3D10_FILL_SOLID;
|
||||
desc.CullMode = D3D10_CULL_NONE;
|
||||
desc.ScissorEnable = TRUE;
|
||||
|
||||
D3D10CreateRasterizerState(d3d10->device, &desc, &d3d10->state);
|
||||
}
|
||||
@@ -1560,7 +1574,7 @@ static bool d3d10_gfx_frame(
|
||||
#ifndef __WINRT__
|
||||
win32_update_title();
|
||||
#endif
|
||||
DXGIPresent(d3d10->swapChain, !!d3d10->vsync, 0);
|
||||
DXGIPresent(d3d10->swapChain, d3d10->swap_interval, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1575,6 +1589,7 @@ static void d3d10_gfx_set_nonblock_state(void* data, bool toggle,
|
||||
return;
|
||||
|
||||
d3d10->vsync = !toggle;
|
||||
d3d10->swap_interval = (!toggle) ? swap_interval : 0;
|
||||
}
|
||||
|
||||
static bool d3d10_gfx_alive(void* data)
|
||||
|
||||
Loaded 100 of 523 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user