Compare commits

...
137 Commits
Author SHA1 Message Date
twinaphex c226bd87f4 Bump version to 1.9.4 2021-05-29 09:23:37 +02:00
Autechre c0b7c34264 Update version.dtd 2021-05-29 09:22:08 +02:00
Autechre 5cf7b57064 Update version.all 2021-05-29 09:21:49 +02:00
Autechre adf6a7ff20 Merge pull request #12450 from libretro/kivutar/fixnota
Try to fix notarization issue
2021-05-29 06:05:04 +02:00
Jean-André Santoni 5a7cebf7da Last fix 2021-05-29 11:03:35 +07:00
Jean-André Santoni 4a8fdaed84 Don't duplicate bundle IDs 2021-05-29 10:54:26 +07:00
Jean-André Santoni 4441219df9 Always prefix bundle ID with com 2021-05-29 10:51:00 +07:00
Autechre 750ad9aa7b Update CHANGES.md 2021-05-28 21:27:19 +02:00
Autechre 0d7a028ce3 Merge pull request #12444 from Jamiras/info_crash 2021-05-28 08:50:21 +02:00
Jamiras 45c0dc4486 fix crash by restoring incorrectly eliminated line 2021-05-27 21:34:51 -06:00
Jamiras 2c21e3df8b (cheevos) upgrade to rcheevos 10.0 (#12442)
* update rcheevos to v10.0.0

* changes for rcheevos 10

* map virtual tracks in cd_open_track_handler

* address travis warnings
2021-05-27 21:01:00 +02:00
Autechre 138f41160f Merge pull request #12443 from libretro/vitafreetexture
Vita: wait before free texture
2021-05-27 19:53:43 +02:00
Francisco José García García 194de13afb Vita: wait before free texture 2021-05-27 18:41:44 +02:00
Autechre 2c7c8def4c Merge pull request #12435 from toshixm/refactor_word_wrap 2021-05-27 17:31:45 +02:00
Autechre ff3928224e Merge pull request #12441 from jdgleaver/overlay-show-inputs-fix 2021-05-27 17:31:02 +02:00
toshixm a39c38f267 refactor word_wrap() 2021-05-27 19:55:37 +09:00
jdgleaver 8e54b4cec9 (Show Inputs on Overlay) DISABLE_MENU=1 buildfix + Qt settings update 2021-05-26 18:15:23 +01:00
twinaphex 0dfb2b57fb use snprintf instead of multiple consecutive strlcpy/strlcats 2021-05-26 05:22:18 +02:00
twinaphex a84c9022af Cleanups 2021-05-25 21:31:39 +02:00
twinaphex 0a5d4288f5 Rollback runloop changes - will require a rethink in how we approach
this
2021-05-25 20:41:39 +02:00
Autechre 3c6ac487d0 Merge pull request #12438 from jdgleaver/overlay-show-inputs-redux
Add option to select between 'touched' elements and physical controller inputs when showing inputs on overlays
2021-05-25 19:04:17 +02:00
jdgleaver 466bd3be7f Add option to select between 'touched' elements and physical controller inputs when showing inputs on overlays 2021-05-25 18:01:52 +01:00
Autechre 5dc7b7f7c7 Merge pull request #12437 from DisasterMo/inconsistencies_fix
Fixed some inconsistencies in the RetroArch texts
2021-05-25 18:09:21 +02:00
Michael Burgardt 3e814bc4b6 Fixed some inconsistencies in the RetroArch texts 2021-05-25 17:13:55 +02:00
twinaphex f70b6fbf58 Revert "Go through p_runloop pointer"
This reverts commit 30a90adc47.
2021-05-25 05:27:06 +02:00
twinaphex 30a90adc47 Go through p_runloop pointer 2021-05-25 05:19:43 +02:00
twinaphex 621a0b61f6 Move more state to runloop_state (in preparation of removing path_clear
and path_is_empty)
2021-05-24 20:38:56 +02:00
twinaphex 244a2ad052 Simplify video_thread_get_ptr - get rid of unused p_rarch pointer
in dir_free_shader
2021-05-24 19:22:55 +02:00
Autechre bb04b0b9d3 Merge pull request #12432 from DisasterMo/crowdin_sync
Fixed a tiny bug in the Crowdin sync script
2021-05-24 18:51:41 +02:00
Autechre e616b41b62 Merge pull request #12431 from yoshisuga/ios_disable_core_info_cache
[iOS/tvOS] Disable core info caching for iOS
2021-05-24 18:42:51 +02:00
twinaphex fb8c73508b Apply diff patch taking care of issues 2021-05-24 18:40:40 +02:00
Michael Burgardt f2a73d1b14 Fixed a tiny bug in the Crowdin sync script 2021-05-24 12:43:48 +02:00
twinaphex 2b4f2525d0 move more runloop functions over to runloop.c 2021-05-24 02:57:40 +02:00
twinaphex 75175ddbce move more runloop functiosn to runloop.c 2021-05-24 02:27:55 +02:00
Yoshi Sugawara 4dcbd3e383 [iOS/tvOS] Disable core info caching for iOS 2021-05-23 08:33:14 -10:00
twinaphex b48ab663b0 Move BSV runloop code over to runloop.c 2021-05-23 02:20:01 +02:00
twinaphex 364d518726 More runloop_state changes 2021-05-23 01:54:33 +02:00
twinaphex d1c7d97b10 Split up code to runloop.c 2021-05-22 23:09:00 +02:00
Autechre f8b6443b91 Update CHANGES.md 2021-05-22 18:52:52 +02:00
Autechre 8344ed9db5 Update CHANGES.md 2021-05-22 18:52:08 +02:00
Autechre 76f3d0c1b7 Merge pull request #12429 from libretro/pstv720p
[VITA] Support for 720p on PSTV when using unlock framebuffer in Sharpscale plugin
2021-05-22 18:51:38 +02:00
Francisco José García García 52af69e6cd Vita: support for 720p in PSTV & Sharpscale plugin 2021-05-22 17:29:03 +02:00
Francisco José García García d6b1836919 Vita: arrange compile flags 2021-05-22 17:29:03 +02:00
twinaphex 7c483340fc Start creating a distinction between runloop_ and retroarch_ prefix
functions
2021-05-22 16:51:55 +02:00
twinaphex 33de17548b Fix regression caused by core info file caching - Downloads was no
longer showing up in Load Content
2021-05-22 16:25:05 +02:00
twinaphex 9cd19d4944 Silence warning - ret was never used 2021-05-22 16:05:26 +02:00
twinaphex 89e4cac3f1 Small cleanups 2021-05-22 07:33:05 +02:00
twinaphex 29e9027ec0 Cleanup find_driver_* functions - don't pass p_rarch through it 2021-05-22 05:37:14 +02:00
twinaphex 68c377cd55 Move more to runloop_state 2021-05-22 02:57:02 +02:00
twinaphex 706ab17b2c Remove p_rarch passing around where it's no longer needed 2021-05-22 02:14:32 +02:00
twinaphex fede9eee4b Get rid of unused variables 2021-05-22 01:31:29 +02:00
twinaphex d84a2ef700 Move boolean state variables from p_rarch to runloop_state 2021-05-22 01:25:59 +02:00
twinaphex 4ba92b1643 Move more runloop state over to runloop_state 2021-05-22 00:25:01 +02:00
twinaphex 57a728fd41 Rename audio_out_rate to audio_output_sample_rate 2021-05-21 22:36:46 +02:00
twinaphex bc8f98358b Cleanup in audio_driver_init_internal 2021-05-21 22:30:16 +02:00
twinaphex 3887db349e Get rid of unused variable 2021-05-21 22:26:46 +02:00
twinaphex 98448960f5 Move audio_driver_mixer_volume_gain to runloop_state struct 2021-05-21 22:25:36 +02:00
twinaphex 22a2b5ac1c Move more variables to runloop_state 2021-05-21 22:20:26 +02:00
twinaphex 6e4620cebc Prevent warning on Griffin builds 2021-05-21 21:25:43 +02:00
twinaphex 458d636861 Move more runloop state to runloop state struct 2021-05-21 21:23:06 +02:00
twinaphex 64c01e620d Rename retroarch_get_runloop_fastforward_* function 2021-05-21 20:09:50 +02:00
twinaphex 57c4802ab4 Remove unused variable 2021-05-21 20:02:22 +02:00
twinaphex 74f1bf59b9 Refactors - plenty of runloop state functions no longer pass p_rarch
around, so get rid of it
2021-05-21 19:52:09 +02:00
Autechre cacbd451dc Update CHANGES.md 2021-05-21 17:48:36 +02:00
Autechre 7174563f7d Merge pull request #12426 from jdgleaver/overlay-remap-fix
Prevent duplicate inputs when using remaps with input overlays
2021-05-21 17:46:32 +02:00
jdgleaver d5f862ee07 Prevent duplicate inputs when using remaps with input overlays 2021-05-21 16:31:11 +01:00
twinaphex f66d4ea33c Remove unused variables 2021-05-21 17:14:13 +02:00
twinaphex 0797fd957a menu_entries_get_core_title - cut down on amount of lines of code 2021-05-21 17:10:20 +02:00
twinaphex b45acf2223 MSVC buildfixes 2021-05-21 17:09:55 +02:00
twinaphex 30143f15f2 Simplify menu_entries_get_core_title 2021-05-21 16:54:43 +02:00
twinaphex 24389bbcd5 Small cleanups to task_content.c 2021-05-21 16:30:02 +02:00
twinaphex 2e50b57643 task_content_disc.c - header include cleanups 2021-05-21 14:10:44 +02:00
Autechre 5b995dec26 Update CHANGES.md 2021-05-21 14:08:55 +02:00
Autechre 5ae4524176 Merge pull request #12424 from libretro/pstvinput 2021-05-21 13:13:28 +02:00
Francisco José García García 87a7795331 [VITA] Fix input driver for PSTV 2021-05-21 10:54:02 +02:00
Autechre 44fc3666ed Merge pull request #12421 from jdgleaver/task-content-cleanup-1 2021-05-21 06:16:38 +02:00
jdgleaver 334a43a7c5 Prevent unnecessary extraction (to disk) of compressed content files + task_content.c clean-ups 2021-05-20 17:08:51 +01:00
Autechre 29475a904f Merge pull request #12419 from crystalct/master
Change default video driver name for PSL1GHT
2021-05-20 05:30:26 +02:00
U-LAPTOP-0BS9KNOH\crystal 159602d697 Change default video driver name for PSL1GHT 2021-05-19 22:50:34 +02:00
twinaphex de88edf56c Fix header include now that defines/ files are in libretro-common 2021-05-19 16:47:40 +02:00
twinaphex 4fb6bc9f33 Fix another camelcase variable 2021-05-19 16:13:05 +02:00
twinaphex b82a20ea0c We don't want camelcase notation variable names for RetroArch 2021-05-19 16:11:26 +02:00
twinaphex 8291b18771 Small cleanup to cheevos content loading part 2021-05-19 16:05:42 +02:00
twinaphex 181824eb00 (Tasks/content) Split up CDROM code into tasks/task_content_disc.c 2021-05-19 15:53:38 +02:00
twinaphex 7a2115670a Fix relative path includes 2021-05-19 15:41:40 +02:00
Autechre 951999e5ce Merge pull request #12416 from crystalct/master
PSL1GHT salamander build fix
2021-05-19 14:48:03 +02:00
U-LAPTOP-0BS9KNOH\crystal 23acacf436 PSL1GHT salamander build fix 2021-05-19 00:31:09 +02:00
twinaphex 925c459aed Use single snprintf instead of multiple strlcpy/strlcat calls in
several places
2021-05-18 23:18:09 +02:00
twinaphex 0482892a9c Get rid of return variable for snprintf 2021-05-18 23:09:45 +02:00
Autechre dd922bfc2c Merge pull request #12413 from Jamiras/cheevos_sprintf
(cheevos) replace sprintf with snprintf
2021-05-18 22:55:13 +02:00
twinaphex 35543ecd91 Move defines files over to libretro-common 2021-05-18 22:15:04 +02:00
twinaphex e08290acb0 Less strlcpy/strlcat concatenation of strings 2021-05-18 21:06:29 +02:00
twinaphex e34c84d791 Use snprintf in more situations 2021-05-18 20:19:19 +02:00
twinaphex 2e4b734159 Cleanups - replace several messy strlcpy/strlcats with one single
snprintf
2021-05-18 20:09:48 +02:00
twinaphex 3b64752f4f Simplify strlcpy/strlcat combinations to single snprintf 2021-05-18 20:01:51 +02:00
twinaphex 9829051841 win32_localize_menu - replace strcmp call with string_is_not_equal 2021-05-18 19:49:57 +02:00
twinaphex fc2a011a5c Use single snprintf call where possible instead of multiple
strlcpy/strlcat calls
2021-05-18 19:36:20 +02:00
twinaphex 0e7ff6ea4e - Avoid some repeated strlcpy/strlcat calls one after another; replace
it with a single snprintf call under the assumption it will probably
still be faster
- In some instances, the first time the string was manipulated on was
with a strlcat call instead of an strlcpy - change this
2021-05-18 19:27:54 +02:00
twinaphex e784cc1aaf Cleanup some accessibiliyt-related code - pre-initialize strings
normally, don't use weird string_is_equal check which checks if string
is empty, etc.
2021-05-18 19:02:10 +02:00
twinaphex e4a8f42ad1 win32_localize_menu - get rid of direct strcpy/strcat calls 2021-05-18 18:53:30 +02:00
Autechre a04fe66032 Merge pull request #12380 from stenzek/vulkan-software-fb
(video/vulkan) Avoid caching stale mapped GPU texture as frame data
2021-05-18 18:01:47 +02:00
Jamiras 9669cff16b fix travis error 2021-05-18 09:46:03 -06:00
Jamiras 09439332f3 implement feedback 2021-05-18 09:40:53 -06:00
Jamiras 216deda910 replace sprintf with snprintf 2021-05-18 08:31:44 -06:00
twinaphex 7bbdd6d18a Prefer using snprintf instead of insecure sprintf 2021-05-18 15:14:08 +02:00
twinaphex b037da7264 (vita/launcher) Use snprintf 2021-05-18 15:07:58 +02:00
twinaphex ef35adc9d6 platform_ps2.c - Use snprintf 2021-05-18 14:15:18 +02:00
twinaphex f6a8480f21 (platform_psp.c) Switch to strlcpys instead of sprintf 2021-05-18 13:57:23 +02:00
Autechre d3b3b40ea7 Update CHANGES.md 2021-05-18 13:43:43 +02:00
Autechre 6972cc2bc6 Merge pull request #12398 from DisasterMo/playlist_entry_enumeration
(ozone) Added simple playlist entry enumeration
2021-05-18 13:19:35 +02:00
twinaphex 74cef32c1d Cleanups 2021-05-18 00:57:46 +02:00
Michael Burgardt d50be7a0ab Added simple playlist entry enumeration to ozone; added toggle option by jdgleaver 2021-05-17 18:49:25 +02:00
twinaphex d228e5f759 Small style nits 2021-05-17 15:58:04 +02:00
Autechre 10546594de Update CHANGES.md 2021-05-17 15:54:39 +02:00
Autechre e9b5f009cc Merge pull request #12411 from jdgleaver/max-users-dropdown-fix
(XMB) Fix display of 'Maximum Users' menu entry dropdown list
2021-05-17 15:52:14 +02:00
twinaphex d6ec36c707 Enable 'Cache Core Info Files' by default for all platforms 2021-05-17 15:47:28 +02:00
jdgleaver be84700d0a (XMB) Fix display of 'Maximum Users' menu entry dropdown list 2021-05-17 14:41:57 +01:00
Autechre ad5b2d614a Merge pull request #12401 from davidgfnet/br3
Add brightness restore hook
2021-05-17 13:24:50 +02:00
Autechre 9e87040c02 Merge pull request #12406 from guoyunhe/crowdin
Fetch translations from Crowdin
2021-05-17 13:24:39 +02:00
Autechre 90abd49892 Merge pull request #12410 from Alcaro/master
rpng: Fix some memory corruption if processing broken input
2021-05-17 13:22:51 +02:00
Alcaro 654aae58d6 rpng: Fix some memory corruption if processing broken input 2021-05-17 08:43:53 +02:00
twinaphex e6adca7679 rarch_init_core_options_path - NULL termination of strings is already
done outside by caller
2021-05-17 00:56:29 +02:00
Autechre 095f6d4377 Merge pull request #12407 from crystalct/master
PSL1GHT joypad restored
2021-05-16 23:01:32 +02:00
Autechre b946b146f3 Merge pull request #12408 from LazyFunker/master
Fix typo
2021-05-16 23:00:39 +02:00
LazyFunker 4e0f8bf621 Fix typo 2021-05-16 20:44:31 +02:00
unknown de4343caeb PSL1GHT joypad restored 2021-05-16 20:04:43 +02:00
Autechre a9426925cf Update config.def.h 2021-05-16 19:27:07 +02:00
Autechre aebdc8b747 Merge pull request #12404 from mudlord/master
Look into fixing CVE-2021-28927
2021-05-16 18:43:34 +02:00
Guo Yunhe c9d4775ffc Fetch translations from Crowdin 2021-05-17 00:09:21 +08:00
mudlord 3b8bd1a6e8 There. 2021-05-16 18:28:59 +10:00
mudlord 734401440d People are never happy. 2021-05-16 18:20:52 +10:00
mudlord 9fbf3b9732 Whoops 2021-05-16 17:58:43 +10:00
mudlord 67b180d63d Look into fixing CVE-2021-28927 2021-05-16 17:55:14 +10:00
David Guillen Fandos c378665af5 Add brightness restore hook
This is out of the unix frontend driver on purpose, since settings do
not seem to be available at init() time.
2021-05-15 21:20:37 +02:00
Autechre 3187c87451 Update CHANGES.md 2021-05-15 20:07:09 +02:00
Stenzek c9aaf598bb (video/vulkan) Avoid caching stale mapped GPU texture as frame data
At best, this results in junk on screen, at worst we could crash.

It's not a perfect fix, there's still a scenario where this breaks: when
fast forwarding is enabled, and we swap from vsync-off to vsync-on when
pausing/rendering the menu, the swap chain (and textures) gets
recreated, and the frame data is completely lost.

There isn't much we can do about this without more intrusive changes,
such as preserving the textures inbetween swap chain reinits.
2021-05-11 09:49:39 -07:00
Stenzek 92e795aa28 (main) Remove null check from video_driver_set_cached_frame_ptr
The only call sites here use NULL as a parameter, and it makes
sense to clear it, since the intent is to remove a potentially
dangling pointer.
2021-05-11 09:49:39 -07:00
180 changed files with 17652 additions and 4205 deletions

No files matched your search

+35
View File
@@ -1,5 +1,40 @@
# Future
# 1.9.4
- CHEEVOS: update rcheevos to v10.0.0
- CONTENT LOADING/FILE IO: Prevent unnecessary extraction (to disk) of compressed content files when need_fullpath is false
- CORE INFO/FILE IO: Enable core info cache by default now for all platforms
- CORE INFO/REGRESSION FIX: Fix regression caused by core info file caching - Downloads was no longer showing up in Load Content
- FILE IO/COMPRESSED: Ability to load content inside ZIP files directly into RAM
- INPUT/OVERLAYS: Add option to select between 'touched' elements and physical controller inputs when showing inputs on overlays
- INPUT REMAPPING/OVERLAYS: Prevent duplicate inputs when using remaps with input overlays
- LAKKA: Add brightness restore hook
- LOCALIZATION: Fetch translations from Crowdin
- MENU/OZONE: Added simple playlist entry enumeration
- MENU/XMB: Fix display of 'Maximum Users' menu entry dropdown list
- PS3/PSL1GHT: Joypad driver works again
- PSTV: Fix Vita input driver for PSTV
- PSTV: Support for 720p on PSTV when using 'Unlock framebuffer' in Sharpscale plugin
- RPNG: Fix some memory corruption if processing broken input PNG file
- SECURITY: Fix CVE-2021-28927
# 1.9.3
- 3DS: Disable menu screensaver animations in XMB/GLUI
- COMMAND: Initialize netcmd->cmd_source_len before recvfrom()
- CONTENT LOADING/STATICALLY LINKED: Ensure 'Always Reload Core on Run Content' setting is applied when loading content via the file browser
- CONTENT LOADING/EMSCRIPTEN: Fix content loading via file browser on platforms with 'broken' core handling (i.e. emscripten)
- CORE INFO: Skip whitespace when writing compressed core info cache files
- 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: 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
- SHADERS: Fix 'Auto-Shader Delay' functionality
- UWP/D3D11: Disable mipmap generation
- UWP/XBOX: Add 'Force 4K resolution' option (Force the resolution to the fullscreen size on Xbox, if set to 0, a fixed value of 3840 x 2160 will be used)
# 1.9.2
- 3DS: Enable graphics widgets / overlays
- 3DS: Load texture images as BGR colors
+2 -1
View File
@@ -1990,7 +1990,8 @@ ifeq ($(HAVE_CDROM), 1)
DEFINES += -DHAVE_CDROM
OBJ += $(LIBRETRO_COMM_DIR)/cdrom/cdrom.o \
$(LIBRETRO_COMM_DIR)/vfs/vfs_implementation_cdrom.o \
$(LIBRETRO_COMM_DIR)/media/media_detect_cd.o
$(LIBRETRO_COMM_DIR)/media/media_detect_cd.o \
tasks/task_content_disc.o
endif
ifeq ($(HAVE_RTGA), 1)
+1 -1
View File
@@ -30,7 +30,7 @@ CORE_PATH = pkg/psl1ght/pkg/USRDIR/cores/CORE.SELF
INCLUDE += -I. -Ips3/gcmgl/include/export -Ips3/include -Ideps -Ideps/stb -Ilibretro-common/include/compat/zlib -Ilibretro-common/include $(LIBPSL1GHT_INC) -Iinclude -Idefines
LIBDIRS += -L.
MACHDEP := -D__PSL1GHT__ -mcpu=cell
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
+4 -3
View File
@@ -27,13 +27,14 @@ ELF_TARGET := retroarch_psl1ght_salamander.elf
ELF_TARGET_NONSTRIPPED := retroarch_psl1ght_salamander_nonstripped.elf
EBOOT_PATH = pkg/psl1ght/pkg/USRDIR/EBOOT.BIN
INCLUDE += -I. -Ips3/gcmgl/include/export -Ips3/include -Ideps -Ideps/stb -Ilibretro-common/include/compat/zlib -Ilibretro-common/include $(LIBPSL1GHT_INC) -Iinclude
INCLUDE += -I. -Ips3/gcmgl/include/export -Ips3/include -Ideps -Ideps/stb -Ilibretro-common/include/compat/zlib \
-Ilibretro-common/include $(LIBPSL1GHT_INC) -Iinclude -Idefines
LIBDIRS += -L.
MACHDEP := -D__PSL1GHT__ -mcpu=cell -mhard-float -fmodulo-sched -ffunction-sections -fdata-sections
MACHDEP := -D__PS3__ -D__PSL1GHT__ -mcpu=cell -mhard-float -fmodulo-sched -ffunction-sections -fdata-sections
CFLAGS += -Wall $(MACHDEP) $(INCLUDE)
LDFLAGS := $(MACHDEP)
LIBS := -lgcm_sys -lrsx -lsysutil -lio -lnet -lsysmodule -lrt -llv2 -lm
LIBS := -lgcm_sys -lrsx -lsysutil -lio -lnet -lsysmodule -lrt -llv2 -lm -lsysfs
# system platform
system_platform = unix
+5 -3
View File
@@ -113,16 +113,18 @@ LD := $(CXX)
LIBDIRS := -L.
ARCHFLAGS := -march=armv7-a -mfpu=neon -mfloat-abi=hard -DVITA -DSCE_LIBC_SIZE=$(SCE_LIBC_SIZE)
CFLAGS += $(ARCHFLAGS) -mword-relocations -fno-optimize-sibling-calls -O2
CFLAGS += $(ARCHFLAGS) -mword-relocations -fno-optimize-sibling-calls
ifeq ($(DEBUG), 1)
CFLAGS += -g
CFLAGS += -g -Og
else
CFLAGS += -O2
endif
ASFLAGS := $(CFLAGS)
LDFLAGS := -Wl,-q
CFLAGS += -Wall -ffast-math -g
CFLAGS += -Wall -ffast-math
CFLAGS += -DRARCH_INTERNAL -DHAVE_SCREENSHOTS -DRARCH_CONSOLE
CFLAGS += -DHAVE_DSP_FILTER
CFLAGS += -DHAVE_VIDEO_FILTER
+1 -1
View File
@@ -28,7 +28,7 @@
#include <retro_inline.h>
#include "../../retroarch.h"
#include "../../defines/gx_defines.h"
#include <defines/gx_defines.h>
typedef struct
{
+1 -1
View File
@@ -21,7 +21,7 @@
#include "../../retroarch.h"
#include "../../defines/ps3_defines.h"
#include <defines/ps3_defines.h>
#define AUDIO_BLOCKS 8
#define AUDIO_CHANNELS 2
+1 -1
View File
@@ -1,6 +1,6 @@
#include <errno.h>
#include <reent.h>
#include "../../defines/psp_defines.h"
#include <defines/psp_defines.h>
#include <psp2/kernel/sysmem.h>
#include <psp2/kernel/threadmgr.h>
+146 -93
View File
@@ -54,6 +54,10 @@
#include "../cheat_manager.h"
#endif
#ifdef HAVE_CHD
#include "streams/chd_stream.h"
#endif
#include "badges.h"
#include "cheevos.h"
#include "cheevos_memory.h"
@@ -76,8 +80,9 @@
#include "../network/net_http_special.h"
#include "../tasks/tasks_internal.h"
#include "../deps/rcheevos/include/rcheevos.h"
#include "../deps/rcheevos/include/rurl.h"
#include "../deps/rcheevos/include/rc_runtime.h"
#include "../deps/rcheevos/include/rc_runtime_types.h"
#include "../deps/rcheevos/include/rc_url.h"
#include "../deps/rcheevos/include/rc_hash.h"
/* Define this macro to prevent cheevos from being deactivated. */
@@ -199,73 +204,79 @@ void rcheevos_log(const char *fmt, ...)
}
#endif
static int append_no_spaces(char* buffer, char* stop, const char* text)
{
char* ptr = buffer;
while (ptr < stop && *text)
{
if (*text == ' ')
{
*ptr++ = '_';
++text;
}
else
{
*ptr++ = *text++;
}
}
*ptr = '\0';
return (ptr - buffer);
}
static void rcheevos_get_user_agent(
rcheevos_locals_t *locals,
char *buffer, size_t len)
{
struct retro_system_info *system = runloop_get_libretro_system_info();
const char* scan;
char* ptr;
/* if we haven't calculated the non-changing portion yet, do so now [retroarch version + os version] */
if (!locals->user_agent_prefix[0])
{
const frontend_ctx_driver_t *frontend = frontend_get_ptr();
int major, minor;
char tmp[64];
ptr = locals->user_agent_prefix + snprintf(locals->user_agent_prefix, sizeof(locals->user_agent_prefix), "RetroArch/%s", PACKAGE_VERSION);
if (frontend && frontend->get_os)
{
frontend->get_os(tmp, sizeof(tmp), &major, &minor);
ptr += sprintf(ptr, " (%s %d.%d)", tmp, major, minor);
(void)ptr;
snprintf(locals->user_agent_prefix, sizeof(locals->user_agent_prefix),
"RetroArch/%s (%s %d.%d)", PACKAGE_VERSION, tmp, major, minor);
}
else
{
snprintf(locals->user_agent_prefix, sizeof(locals->user_agent_prefix),
"RetroArch/%s", PACKAGE_VERSION);
}
}
ptr = buffer + snprintf(buffer, len, "%s", locals->user_agent_prefix);
/* append the non-changing portion */
ptr = buffer + strlcpy(buffer, locals->user_agent_prefix, len);
/* if a core is loaded, append its information */
if (system && !string_is_empty(system->library_name))
{
char* stop = buffer + len - 1;
const char* path = path_get(RARCH_PATH_CORE);
*ptr++ = ' ';
if (!string_is_empty(path))
{
sprintf(ptr, " %s", path_basename(path));
append_no_spaces(ptr, stop, path_basename(path));
path_remove_extension(ptr);
ptr += strlen(ptr);
}
else
{
*ptr++ = ' ';
scan = system->library_name;
while (*scan)
{
if (*scan == ' ')
{
*ptr++ = '_';
++scan;
}
else
*ptr++ = *scan++;
}
ptr += append_no_spaces(ptr, stop, system->library_name);
}
if (system->library_version)
{
*ptr++ = '/';
scan = system->library_version;
while (*scan)
{
if (*scan == ' ')
{
*ptr++ = '_';
++scan;
}
else
*ptr++ = *scan++;
}
ptr += append_no_spaces(ptr, stop, system->library_version);
}
}
@@ -362,7 +373,7 @@ static void rcheevos_log_post_url(
}
static bool rcheevos_condset_contains_memref(const rc_condset_t* condset,
const rc_memref_value_t* memref)
const rc_memref_t* memref)
{
if (condset)
{
@@ -379,7 +390,7 @@ static bool rcheevos_condset_contains_memref(const rc_condset_t* condset,
}
static bool rcheevos_trigger_contains_memref(const rc_trigger_t* trigger,
const rc_memref_value_t* memref)
const rc_memref_t* memref)
{
rc_condset_t* condset;
if (!trigger)
@@ -397,6 +408,26 @@ static bool rcheevos_trigger_contains_memref(const rc_trigger_t* trigger,
return false;
}
static void rcheevos_achievement_disabled(rcheevos_racheevo_t* cheevo, unsigned address)
{
if (!cheevo)
return;
CHEEVOS_ERR(RCHEEVOS_TAG "Achievement disabled (invalid address %06X): %s\n", address, cheevo->title);
CHEEVOS_FREE(cheevo->memaddr);
cheevo->memaddr = NULL;
}
static void rcheevos_lboard_disabled(rcheevos_ralboard_t* lboard, unsigned address)
{
if (!lboard)
return;
CHEEVOS_ERR(RCHEEVOS_TAG "Leaderboard disabled (invalid address %06X): %s\n", address, lboard->title);
CHEEVOS_FREE(lboard->mem);
lboard->mem = NULL;
}
static void rcheevos_invalidate_address(unsigned address)
{
unsigned i, count;
@@ -406,12 +437,12 @@ static void rcheevos_invalidate_address(unsigned address)
* try to evaluate it in the future.
* It's still there, so anything referencing it will
* continue to fetch 0. */
rc_memref_value_t **last_memref = &rcheevos_locals.runtime.memrefs;
rc_memref_value_t *memref = *last_memref;
rc_memref_t **last_memref = &rcheevos_locals.runtime.memrefs;
rc_memref_t *memref = *last_memref;
do
{
if (memref->memref.address == address && !memref->memref.is_indirect)
if (memref->address == address && !memref->value.is_indirect)
{
*last_memref = memref->next;
break;
@@ -449,12 +480,9 @@ static void rcheevos_invalidate_address(unsigned address)
if (trigger && rcheevos_trigger_contains_memref(trigger, memref))
{
CHEEVOS_ERR(RCHEEVOS_TAG "Achievement disabled (invalid address %06X): %s\n", address, cheevo->title);
rcheevos_achievement_disabled(cheevo, address);
rc_runtime_deactivate_achievement(&rcheevos_locals.runtime,
cheevo->id);
CHEEVOS_FREE(cheevo->memaddr);
cheevo->memaddr = NULL;
}
}
@@ -479,11 +507,8 @@ static void rcheevos_invalidate_address(unsigned address)
memref))
)
{
CHEEVOS_ERR(RCHEEVOS_TAG "Leaderboard disabled (invalid address %06X): %s\n", address, lboard->title);
rcheevos_lboard_disabled(lboard, address);
rc_runtime_deactivate_lboard(&rcheevos_locals.runtime, lboard->id);
CHEEVOS_FREE(lboard->mem);
lboard->mem = NULL;
}
}
}
@@ -558,15 +583,17 @@ static retro_time_t rcheevos_async_send_rich_presence(
rcheevos_async_io_request* request)
{
char url[256], post_data[1024];
settings_t *settings = config_get_ptr();
const char *cheevos_username = settings->arrays.cheevos_username;
bool cheevos_richpresence_enable = settings->bools.cheevos_richpresence_enable;
int ret = rc_url_ping(
url, sizeof(url), post_data, sizeof(post_data),
cheevos_username, locals->token, locals->patchdata.game_id,
cheevos_richpresence_enable
? rc_runtime_get_richpresence(&locals->runtime)
: "");
char buffer[256] = "";
const settings_t *settings = config_get_ptr();
const char *cheevos_username = settings->arrays.cheevos_username;
const bool cheevos_richpresence_enable = settings->bools.cheevos_richpresence_enable;
int ret;
if (cheevos_richpresence_enable)
rcheevos_get_richpresence(buffer, sizeof(buffer));
ret = rc_url_ping(url, sizeof(url), post_data, sizeof(post_data),
cheevos_username, locals->token, locals->patchdata.game_id, buffer);
if (ret < 0)
{
@@ -582,12 +609,8 @@ static retro_time_t rcheevos_async_send_rich_presence(
}
#ifdef HAVE_DISCORD
if (locals->runtime.richpresence_display_buffer)
{
if (settings->bools.discord_enable
&& discord_is_ready())
discord_update(DISCORD_PRESENCE_RETROACHIEVEMENTS);
}
if (settings->bools.discord_enable && discord_is_ready())
discord_update(DISCORD_PRESENCE_RETROACHIEVEMENTS);
#endif
/* Update rich presence every two minutes */
@@ -728,15 +751,16 @@ static void rcheevos_async_task_callback(
static void rcheevos_validate_memrefs(rcheevos_locals_t* locals)
{
rc_memref_value_t* memref = locals->runtime.memrefs;
rc_memref_t* memref = locals->runtime.memrefs;
while (memref)
{
if (!memref->memref.is_indirect)
if (!memref->value.is_indirect)
{
uint8_t* data = rcheevos_memory_find(&rcheevos_locals.memory,
memref->memref.address);
memref->address);
if (!data)
rcheevos_invalidate_address(memref->memref.address);
rcheevos_invalidate_address(memref->address);
}
memref = memref->next;
@@ -901,22 +925,6 @@ static int rcheevos_parse(rcheevos_locals_t *locals, const char* json)
}
}
if (res < 0 && locals->patchdata.title)
{
size_t len = strlen(locals->patchdata.title);
if (!locals->runtime.richpresence_display_buffer)
locals->runtime.richpresence_display_buffer = (char*)malloc(len + 9);
if (locals->runtime.richpresence_display_buffer)
{
memcpy(locals->runtime.richpresence_display_buffer,
"Playing ", 8);
memcpy(&locals->runtime.richpresence_display_buffer[8],
locals->patchdata.title, len + 1);
}
}
/* schedule the first rich presence call in 30 seconds */
{
rcheevos_async_io_request* request = (rcheevos_async_io_request*)
@@ -1193,9 +1201,14 @@ static void rcheevos_lboard_updated(rcheevos_ralboard_t* lboard, int value,
}
#endif
const char* rcheevos_get_richpresence(void)
int rcheevos_get_richpresence(char buffer[], int buffer_size)
{
return rc_runtime_get_richpresence(&rcheevos_locals.runtime);
int ret = rc_runtime_get_richpresence(&rcheevos_locals.runtime, buffer, buffer_size, &rcheevos_peek, NULL, NULL);
if (ret <= 0 && rcheevos_locals.patchdata.title)
ret = snprintf(buffer, buffer_size, "Playing %s", rcheevos_locals.patchdata.title);
return ret;
}
void rcheevos_reset_game(bool widgets_ready)
@@ -1846,7 +1859,7 @@ void rcheevos_validate_config_settings(void)
if (rarch_ctl(RARCH_CTL_CORE_OPTIONS_LIST_GET, &coreopts))
{
int i;
size_t i;
const char *val = NULL;
const rc_disallowed_setting_t
*disallowed_setting = core_filter->disallowed_settings;
@@ -1951,6 +1964,14 @@ static void rcheevos_runtime_event_handler(const rc_runtime_event_t* runtime_eve
rcheevos_lboard_submit(&rcheevos_locals, rcheevos_find_lboard(runtime_event->id), runtime_event->value, widgets_ready);
break;
case RC_RUNTIME_EVENT_ACHIEVEMENT_DISABLED:
rcheevos_achievement_disabled(rcheevos_find_cheevo(runtime_event->id), runtime_event->value);
break;
case RC_RUNTIME_EVENT_LBOARD_DISABLED:
rcheevos_lboard_disabled(rcheevos_find_lboard(runtime_event->id), runtime_event->value);
break;
default:
break;
}
@@ -2836,12 +2857,12 @@ static void* rc_hash_handle_file_open(const char* path)
return intfstream_open_file(path, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE);
}
static void rc_hash_handle_file_seek(void* file_handle, size_t offset, int origin)
static void rc_hash_handle_file_seek(void* file_handle, int64_t offset, int origin)
{
intfstream_seek((intfstream_t*)file_handle, offset, origin);
}
static size_t rc_hash_handle_file_tell(void* file_handle)
static int64_t rc_hash_handle_file_tell(void* file_handle)
{
return intfstream_tell((intfstream_t*)file_handle);
}
@@ -2861,21 +2882,51 @@ static void* rc_hash_handle_cd_open_track(const char* path, uint32_t track)
{
cdfs_track_t* cdfs_track;
if (track == 0)
cdfs_track = cdfs_open_data_track(path);
else
cdfs_track = cdfs_open_track(path, track);
switch (track)
{
case RC_HASH_CDTRACK_FIRST_DATA:
cdfs_track = cdfs_open_data_track(path);
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;
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);
break;
default:
cdfs_track = cdfs_open_track(path, track);
break;
}
if (cdfs_track)
{
cdfs_file_t* file = (cdfs_file_t*)malloc(sizeof(cdfs_file_t));
if (cdfs_open_file(file, cdfs_track, NULL))
return file;
return file; /* ASSERT: file owns cdfs_track now */
CHEEVOS_FREE(file);
cdfs_close_track(cdfs_track); /* ASSERT: this free()s cdfs_track */
}
cdfs_close_track(cdfs_track); /* ASSERT: this free()s cdfs_track */
return NULL;
}
@@ -2938,6 +2989,7 @@ bool rcheevos_load(const void *data)
return false;
/* 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;
@@ -2945,6 +2997,7 @@ bool rcheevos_load(const void *data)
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;
+1 -1
View File
@@ -73,7 +73,7 @@ bool rcheevos_get_support_cheevos(void);
const char* rcheevos_get_hash(void);
const char *rcheevos_get_richpresence(void);
int rcheevos_get_richpresence(char buffer[], int buffer_size);
uint8_t* rcheevos_patch_address(unsigned address);
+18 -5
View File
@@ -48,6 +48,11 @@
#include "menu/menu_screensaver.h"
#endif
/* Required for 'show inputs on overlay' setting */
#if defined(HAVE_OVERLAY)
#include "../input/input_overlay.h"
#endif
#if defined(HW_RVL)
#define MAX_GAMMA_SETTING 30
#elif defined(GEKKO)
@@ -249,11 +254,7 @@
/* Specifies whether to cache core info
* into a single (compressed) file for improved
* load times on platforms with slow IO */
#if defined(RARCH_CONSOLE)
#define DEFAULT_CORE_INFO_CACHE_ENABLE true
#else
#define DEFAULT_CORE_INFO_CACHE_ENABLE false
#endif
/* Specifies whether to 'reload' (fork and quit)
* RetroArch when launching content with the
@@ -746,7 +747,15 @@ static const unsigned input_backtouch_toggle = false;
#define DEFAULT_OVERLAY_ENABLE_AUTOPREFERRED true
#define DEFAULT_SHOW_PHYSICAL_INPUTS true
#if defined(HAVE_OVERLAY)
#if defined(RARCH_MOBILE)
#define DEFAULT_OVERLAY_SHOW_INPUTS OVERLAY_SHOW_INPUT_TOUCHED
#else
#define DEFAULT_OVERLAY_SHOW_INPUTS OVERLAY_SHOW_INPUT_PHYSICAL
#endif
#endif
#define DEFAULT_OVERLAY_SHOW_INPUTS_PORT 0
#define DEFAULT_ALL_USERS_CONTROL_MENU false
@@ -1197,6 +1206,10 @@ static const int default_content_favorites_size = 200;
#define DEFAULT_PLAYLIST_SHOW_SUBLABELS true
#endif
/* Show the indices of playlist entries in
* a menu-driver-specific fashion */
#define DEFAULT_PLAYLIST_SHOW_ENTRY_IDX true
#define DEFAULT_PLAYLIST_FUZZY_ARCHIVE_MATCH false
#define DEFAULT_PLAYLIST_PORTABLE_PATHS false
+30 -14
View File
@@ -93,6 +93,7 @@ enum video_driver_enum
VIDEO_GDI,
VIDEO_VGA,
VIDEO_FPGA,
VIDEO_RSX,
VIDEO_NULL
};
@@ -315,6 +316,8 @@ static const enum video_driver_enum VIDEO_DEFAULT_DRIVER = VIDEO_VGA;
static const enum video_driver_enum VIDEO_DEFAULT_DRIVER = VIDEO_FPGA;
#elif defined(HAVE_DYLIB) && !defined(ANDROID)
static const enum video_driver_enum VIDEO_DEFAULT_DRIVER = VIDEO_EXT;
#elif defined(__PSL1GHT__)
static const enum video_driver_enum VIDEO_DEFAULT_DRIVER = VIDEO_RSX;
#else
static const enum video_driver_enum VIDEO_DEFAULT_DRIVER = VIDEO_NULL;
#endif
@@ -465,7 +468,7 @@ static const enum joypad_driver_enum JOYPAD_DEFAULT_DRIVER = JOYPAD_WIIU;
static const enum joypad_driver_enum JOYPAD_DEFAULT_DRIVER = JOYPAD_XDK;
#elif defined(PS2)
static const enum joypad_driver_enum JOYPAD_DEFAULT_DRIVER = JOYPAD_PS2;
#elif defined(__PS3__) && !defined(__PSL1GHT__)
#elif defined(__PS3__) || defined(__PSL1GHT__)
static const enum joypad_driver_enum JOYPAD_DEFAULT_DRIVER = JOYPAD_PS3;
#elif defined(ORBIS)
static const enum joypad_driver_enum JOYPAD_DEFAULT_DRIVER = JOYPAD_PS4;
@@ -867,6 +870,8 @@ const char *config_get_default_video(void)
return "vga";
case VIDEO_FPGA:
return "fpga";
case VIDEO_RSX:
return "rsx";
case VIDEO_NULL:
break;
}
@@ -1744,7 +1749,6 @@ static struct config_bool_setting *populate_settings_bool(
#ifdef HAVE_OVERLAY
SETTING_BOOL("input_overlay_enable", &settings->bools.input_overlay_enable, true, config_overlay_enable_default(), false);
SETTING_BOOL("input_overlay_enable_autopreferred", &settings->bools.input_overlay_enable_autopreferred, true, DEFAULT_OVERLAY_ENABLE_AUTOPREFERRED, false);
SETTING_BOOL("input_overlay_show_physical_inputs", &settings->bools.input_overlay_show_physical_inputs, true, DEFAULT_SHOW_PHYSICAL_INPUTS, false);
SETTING_BOOL("input_overlay_hide_in_menu", &settings->bools.input_overlay_hide_in_menu, true, DEFAULT_OVERLAY_HIDE_IN_MENU, false);
SETTING_BOOL("input_overlay_hide_when_gamepad_connected", &settings->bools.input_overlay_hide_when_gamepad_connected, true, DEFAULT_OVERLAY_HIDE_WHEN_GAMEPAD_CONNECTED, false);
SETTING_BOOL("input_overlay_show_mouse_cursor", &settings->bools.input_overlay_show_mouse_cursor, true, DEFAULT_OVERLAY_SHOW_MOUSE_CURSOR, false);
@@ -1818,6 +1822,7 @@ static struct config_bool_setting *populate_settings_bool(
SETTING_BOOL("content_runtime_log", &settings->bools.content_runtime_log, true, DEFAULT_CONTENT_RUNTIME_LOG, false);
SETTING_BOOL("content_runtime_log_aggregate", &settings->bools.content_runtime_log_aggregate, true, DEFAULT_CONTENT_RUNTIME_LOG_AGGREGATE, false);
SETTING_BOOL("playlist_show_sublabels", &settings->bools.playlist_show_sublabels, true, DEFAULT_PLAYLIST_SHOW_SUBLABELS, false);
SETTING_BOOL("playlist_show_entry_idx", &settings->bools.playlist_show_entry_idx, true, DEFAULT_PLAYLIST_SHOW_ENTRY_IDX, false);
SETTING_BOOL("playlist_sort_alphabetical", &settings->bools.playlist_sort_alphabetical, true, DEFAULT_PLAYLIST_SORT_ALPHABETICAL, false);
SETTING_BOOL("playlist_fuzzy_archive_match", &settings->bools.playlist_fuzzy_archive_match, true, DEFAULT_PLAYLIST_FUZZY_ARCHIVE_MATCH, false);
SETTING_BOOL("playlist_portable_paths", &settings->bools.playlist_portable_paths, true, DEFAULT_PLAYLIST_PORTABLE_PATHS, false);
@@ -2031,7 +2036,7 @@ static struct config_uint_setting *populate_settings_uint(
SETTING_UINT("ozone_menu_color_theme", &settings->uints.menu_ozone_color_theme, true, DEFAULT_OZONE_COLOR_THEME, false);
#endif
#endif
SETTING_UINT("audio_out_rate", &settings->uints.audio_out_rate, true, DEFAULT_OUTPUT_RATE, false);
SETTING_UINT("audio_out_rate", &settings->uints.audio_output_sample_rate, true, DEFAULT_OUTPUT_RATE, false);
SETTING_UINT("custom_viewport_width", &settings->video_viewport_custom.width, false, 0 /* TODO */, false);
SETTING_UINT("crt_switch_resolution_super", &settings->uints.crt_switch_resolution_super, true, DEFAULT_CRT_SWITCH_RESOLUTION_SUPER, false);
SETTING_UINT("custom_viewport_height", &settings->video_viewport_custom.height, false, 0 /* TODO */, false);
@@ -2068,7 +2073,12 @@ static struct config_uint_setting *populate_settings_uint(
SETTING_UINT("bundle_assets_extract_version_current", &settings->uints.bundle_assets_extract_version_current, true, 0, false);
#endif
SETTING_UINT("bundle_assets_extract_last_version", &settings->uints.bundle_assets_extract_last_version, true, 0, false);
SETTING_UINT("input_overlay_show_physical_inputs_port", &settings->uints.input_overlay_show_physical_inputs_port, true, 0, false);
#if defined(HAVE_OVERLAY)
SETTING_UINT("input_overlay_show_inputs", &settings->uints.input_overlay_show_inputs, true, DEFAULT_OVERLAY_SHOW_INPUTS, false);
SETTING_UINT("input_overlay_show_inputs_port", &settings->uints.input_overlay_show_inputs_port, true, DEFAULT_OVERLAY_SHOW_INPUTS_PORT, false);
#endif
SETTING_UINT("video_msg_bgcolor_red", &settings->uints.video_msg_bgcolor_red, true, message_bgcolor_red, false);
SETTING_UINT("video_msg_bgcolor_green", &settings->uints.video_msg_bgcolor_green, true, message_bgcolor_green, false);
SETTING_UINT("video_msg_bgcolor_blue", &settings->uints.video_msg_bgcolor_blue, true, message_bgcolor_blue, false);
@@ -3655,20 +3665,23 @@ bool config_load_override(void *data)
{
char temp_path[PATH_MAX_LENGTH];
temp_path[0] = '\0';
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");
strlcpy(temp_path, path_get(RARCH_PATH_CONFIG_APPEND), sizeof(temp_path));
strlcat(temp_path, "|", sizeof(temp_path));
strlcat(temp_path, content_path, sizeof(temp_path));
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);
@@ -3684,20 +3697,23 @@ bool config_load_override(void *data)
{
char temp_path[PATH_MAX_LENGTH];
temp_path[0] = '\0';
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");
strlcpy(temp_path, path_get(RARCH_PATH_CONFIG_APPEND), sizeof(temp_path));
strlcat(temp_path, "|", sizeof(temp_path));
strlcat(temp_path, game_path, sizeof(temp_path));
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);
+4 -3
View File
@@ -134,7 +134,7 @@ typedef struct settings
unsigned led_map[MAX_LEDS];
unsigned audio_out_rate;
unsigned audio_output_sample_rate;
unsigned audio_block_frames;
unsigned audio_latency;
@@ -266,7 +266,8 @@ typedef struct settings
unsigned camera_width;
unsigned camera_height;
unsigned input_overlay_show_physical_inputs_port;
unsigned input_overlay_show_inputs;
unsigned input_overlay_show_inputs_port;
unsigned run_ahead_frames;
@@ -542,7 +543,6 @@ typedef struct settings
bool input_overlay_enable_autopreferred;
bool input_overlay_hide_in_menu;
bool input_overlay_hide_when_gamepad_connected;
bool input_overlay_show_physical_inputs;
bool input_overlay_show_mouse_cursor;
bool input_overlay_auto_rotate;
bool input_overlay_auto_scale;
@@ -826,6 +826,7 @@ typedef struct settings
bool playlist_sort_alphabetical;
bool playlist_show_sublabels;
bool playlist_show_entry_idx;
bool playlist_fuzzy_archive_match;
bool playlist_portable_paths;
+3 -18
View File
@@ -116,7 +116,6 @@ bool core_backup_get_backup_path(
const char *core_path, uint32_t crc, enum core_backup_mode backup_mode,
const char *dir_core_assets, char *backup_path, size_t len)
{
int n;
time_t current_time;
struct tm time_info;
const char *core_filename = NULL;
@@ -152,7 +151,7 @@ bool core_backup_get_backup_path(
rtime_localtime(&current_time, &time_info);
/* Generate backup filename */
n = snprintf(backup_filename, sizeof(backup_filename),
snprintf(backup_filename, sizeof(backup_filename),
"%s.%04u%02u%02uT%02u%02u%02u.%08x.%u%s",
core_filename,
(unsigned)time_info.tm_year + 1900,
@@ -165,10 +164,6 @@ bool core_backup_get_backup_path(
(unsigned)backup_mode,
FILE_PATH_CORE_BACKUP_EXTENSION);
if ((n < 0) || (n >= 128))
n = 0; /* Silence GCC warnings... */
(void)n;
/* Build final path */
fill_pathname_join(backup_path, backup_dir,
backup_filename, len);
@@ -712,7 +707,6 @@ bool core_backup_list_get_entry_timestamp_str(
enum core_backup_date_separator_type date_separator,
char *timestamp, size_t len)
{
int n;
const char *format_str = "";
if (!entry || (len < 20))
@@ -732,7 +726,7 @@ bool core_backup_list_get_entry_timestamp_str(
break;
}
n = snprintf(timestamp, len,
snprintf(timestamp, len,
format_str,
entry->date.year,
entry->date.month,
@@ -741,9 +735,6 @@ bool core_backup_list_get_entry_timestamp_str(
entry->date.minute,
entry->date.second);
if ((n < 0) || (n >= 32))
n = 0; /* Silence GCC warnings... */
(void)n;
return true;
}
@@ -754,15 +745,9 @@ bool core_backup_list_get_entry_crc_str(
const core_backup_list_entry_t *entry,
char *crc, size_t len)
{
int n;
if (!entry || (len < 9))
return false;
n = snprintf(crc, len, "%08x", entry->crc);
if ((n < 0) || (n >= 32))
n = 0; /* Silence GCC warnings... */
(void)n;
snprintf(crc, len, "%08x", entry->crc);
return true;
}
+24 -20
View File
@@ -1357,8 +1357,8 @@ static bool core_info_path_is_locked(core_lock_file_path_list_t *lock_list,
if (lock_list->size < 1)
return false;
strlcpy(lock_filename, core_file_name, sizeof(lock_filename));
strlcat(lock_filename, FILE_PATH_LOCK_EXTENSION, sizeof(lock_filename));
snprintf(lock_filename, sizeof(lock_filename),
"%s" FILE_PATH_LOCK_EXTENSION, core_file_name);
hash = core_info_hash_string(lock_filename);
@@ -1490,15 +1490,16 @@ static config_file_t *core_info_get_config_file(
{
char info_path[PATH_MAX_LENGTH];
info_path[0] = '\0';
if (string_is_empty(info_dir))
strlcpy(info_path, core_file_id, sizeof(info_path));
snprintf(info_path, sizeof(info_path),
"%s" ".info", core_file_id);
else
{
info_path[0] = '\0';
fill_pathname_join(info_path, info_dir, core_file_id,
sizeof(info_path));
strlcat(info_path, ".info", sizeof(info_path));
strlcat(info_path, ".info", sizeof(info_path));
}
return config_file_new_from_path_to_string(info_path);
}
@@ -1808,13 +1809,18 @@ static core_info_list_t *core_info_list_new(const char *path,
core_info_list->list = core_info;
core_info_list->count = path_list->core_list->size;
/* Read core info cache, if enabled */
#if !defined(IOS)
/* Read core info cache, if enabled
* > This functionality is hard disabled on iOS/tvOS,
* where core path changes on every install
* (invalidating any cached parameters) */
if (enable_cache)
{
core_info_cache_list = core_info_cache_read(info_dir);
if (!core_info_cache_list)
goto error;
}
#endif
for (i = 0; i < path_list->core_list->size; i++)
{
@@ -1845,6 +1851,12 @@ static core_info_list_t *core_info_list_new(const char *path,
* cannot be cached */
info->is_locked = core_info_path_is_locked(path_list->lock_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;
}
}
@@ -2818,8 +2830,6 @@ bool core_info_set_core_lock(const char *core_path, bool lock)
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 */
@@ -2838,10 +2848,8 @@ bool core_info_set_core_lock(const char *core_path, bool lock)
return false;
/* Get lock file path */
strlcpy(lock_file_path, core_info->path,
sizeof(lock_file_path));
strlcat(lock_file_path, FILE_PATH_LOCK_EXTENSION,
sizeof(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);
@@ -2894,8 +2902,6 @@ 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 */
@@ -2921,10 +2927,8 @@ bool core_info_get_core_lock(const char *core_path, bool validate_path)
return false;
/* Get lock file path */
strlcpy(lock_file_path, core_file_path,
sizeof(lock_file_path));
strlcat(lock_file_path, FILE_PATH_LOCK_EXTENSION,
sizeof(lock_file_path));
snprintf(lock_file_path, sizeof(lock_file_path),
"%s" FILE_PATH_LOCK_EXTENSION, core_file_path);
/* Check whether lock file exists */
is_locked = path_is_valid(lock_file_path);
+2
View File
@@ -8,8 +8,10 @@
#define CProb UInt16
#undef kTopValue
#define kTopValue ((UInt32)1 << 24)
#define kNumModelBits 11
#undef kBitModelTotal
#define kBitModelTotal (1 << kNumModelBits)
#define kNumMoveBits 5
+4 -1
View File
@@ -205,6 +205,7 @@ typedef struct
#define kEndPosModelIndex 14
#define kNumFullDistances (1 << (kEndPosModelIndex >> 1))
#ifndef __LZMA_DEC_H
typedef
#ifdef _LZMA_PROB32
UInt32
@@ -212,6 +213,7 @@ typedef
UInt16
#endif
CLzmaProb;
#endif
#define LZMA_PB_MAX 4
#define LZMA_LC_MAX 8
@@ -973,7 +975,8 @@ MY_NO_INLINE static void MY_FAST_CALL LenPriceEnc_UpdateTables(
printf("\n MovePos %u", num);
#endif
*/
#undef MOVE_POS
#define MOVE_POS(p, num) { \
p->additionalOffset += (num); \
p->matchFinder.Skip(p->matchFinderObj, (UInt32)(num)); }
+15 -3
View File
@@ -11,6 +11,18 @@ extern "C" {
#define RGBA8(r,g,b,a) ((((a)&0xFF)<<24) | (((b)&0xFF)<<16) | (((g)&0xFF)<<8) | (((r)&0xFF)<<0))
typedef enum
{
VITA2D_VIDEO_MODE_960x544 = 0,
VITA2D_VIDEO_MODE_1280x720
} vita2d_video_mode;
typedef struct vita2d_video_mode_data {
int width;
int height;
int stride;
} vita2d_video_mode_data;
typedef struct vita2d_clear_vertex {
float x;
float y;
@@ -58,7 +70,7 @@ typedef struct vita2d_pgf vita2d_pgf;
int vita2d_init();
int vita2d_init_advanced(unsigned int temp_pool_size);
int vita2d_init_advanced_with_msaa(unsigned int temp_pool_size, SceGxmMultisampleMode msaa);
int vita2d_init_advanced_with_msaa(unsigned int temp_pool_size, SceGxmMultisampleMode msaa, vita2d_video_mode video_mode);
void vita2d_wait_rendering_done();
int vita2d_fini();
@@ -74,8 +86,8 @@ int vita2d_common_dialog_update();
void vita2d_set_clear_color(unsigned int color);
unsigned int vita2d_get_clear_color();
void vita2d_set_clear_color(unsigned int color);
unsigned int vita2d_get_clear_color();
vita2d_video_mode_data vita2d_get_video_mode_data();
void vita2d_set_vblank_wait(int enable);
void *vita2d_get_current_fb();
SceGxmContext *vita2d_get_context();
+83 -34
View File
@@ -18,9 +18,6 @@
/* Defines */
#define DISPLAY_WIDTH 960
#define DISPLAY_HEIGHT 544
#define DISPLAY_STRIDE_IN_PIXELS 1024
#define DISPLAY_COLOR_FORMAT SCE_GXM_COLOR_FORMAT_A8B8G8R8
#define DISPLAY_PIXEL_FORMAT SCE_DISPLAY_PIXELFORMAT_A8B8G8R8
#define DISPLAY_BUFFER_COUNT 3
@@ -44,6 +41,9 @@ extern const SceGxmProgram texture_tint_f_gxp;
/* Static variables */
static vita2d_video_mode_data video_mode_data;
static vita2d_video_mode video_mode_initial;
static int pgf_module_was_loaded = 0;
static const SceGxmProgram *const clearVertexProgramGxp = &clear_v_gxp;
@@ -60,8 +60,8 @@ static float clear_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
static unsigned int clear_color_u = 0xFF000000;
static int clip_rect_x_min = 0;
static int clip_rect_y_min = 0;
static int clip_rect_x_max = DISPLAY_WIDTH;
static int clip_rect_y_max = DISPLAY_HEIGHT;
static int clip_rect_x_max = 0;
static int clip_rect_y_max = 0;
static int vblank_wait = 1;
static int drawing = 0;
static int clipping_enabled = 0;
@@ -152,6 +152,40 @@ static void patcher_host_free(void *user_data, void *mem)
free(mem);
}
vita2d_video_mode_data vita2d_get_video_mode_data(){
return video_mode_data;
}
static int vita2d_switch_video_mode(vita2d_video_mode video_mode){
if(video_mode > video_mode_initial)
return -1;
switch (video_mode)
{
case VITA2D_VIDEO_MODE_960x544:
video_mode_data.width = 960;
video_mode_data.height = 544;
video_mode_data.stride = 1280;
break;
case VITA2D_VIDEO_MODE_1280x720:
video_mode_data.width = 1280;
video_mode_data.height = 720;
video_mode_data.stride = 1280;
break;
default:
return -1;
break;
}
clip_rect_x_max = video_mode_data.width;
clip_rect_y_max = video_mode_data.height;
matrix_init_orthographic(_vita2d_ortho_matrix, 0.0f, video_mode_data.width, video_mode_data.height, 0.0f, 0.0f, 1.0f);
return 0;
}
static void display_callback(const void *callback_data)
{
SceDisplayFrameBuf framebuf;
@@ -160,11 +194,14 @@ static void display_callback(const void *callback_data)
memset(&framebuf, 0x00, sizeof(SceDisplayFrameBuf));
framebuf.size = sizeof(SceDisplayFrameBuf);
framebuf.base = display_data->address;
framebuf.pitch = DISPLAY_STRIDE_IN_PIXELS;
framebuf.pitch = video_mode_data.stride;
framebuf.pixelformat = DISPLAY_PIXEL_FORMAT;
framebuf.width = DISPLAY_WIDTH;
framebuf.height = DISPLAY_HEIGHT;
sceDisplaySetFrameBuf(&framebuf, SCE_DISPLAY_SETBUF_NEXTFRAME);
framebuf.width = video_mode_data.width;
framebuf.height = video_mode_data.height;
if(sceDisplaySetFrameBuf(&framebuf, SCE_DISPLAY_SETBUF_NEXTFRAME)<0){
if(video_mode_initial)
vita2d_switch_video_mode(VITA2D_VIDEO_MODE_960x544);
}
if (vblank_wait) {
sceDisplayWaitVblankStart();
@@ -218,7 +255,7 @@ static void _vita2d_make_fragment_programs(vita2d_fragment_programs *out,
VITA2D_DEBUG("texture_tint sceGxmShaderPatcherCreateFragmentProgram(): 0x%08X\n", err);
}
static int vita2d_init_internal(unsigned int temp_pool_size, SceGxmMultisampleMode msaa)
static int vita2d_init_internal(unsigned int temp_pool_size, SceGxmMultisampleMode msaa, vita2d_video_mode video_mode)
{
int err;
unsigned int i, x, y;
@@ -229,6 +266,14 @@ static int vita2d_init_internal(unsigned int temp_pool_size, SceGxmMultisampleMo
return 1;
}
video_mode_initial = video_mode;
err = vita2d_switch_video_mode(video_mode);
if(err<0){
VITA2D_DEBUG("vita2d_switch_video_mode(): 0x%08X\n", err);
return err;
}
SceGxmInitializeParams initializeParams;
memset(&initializeParams, 0, sizeof(SceGxmInitializeParams));
initializeParams.flags = 0;
@@ -288,8 +333,8 @@ static int vita2d_init_internal(unsigned int temp_pool_size, SceGxmMultisampleMo
SceGxmRenderTargetParams renderTargetParams;
memset(&renderTargetParams, 0, sizeof(SceGxmRenderTargetParams));
renderTargetParams.flags = 0;
renderTargetParams.width = DISPLAY_WIDTH;
renderTargetParams.height = DISPLAY_HEIGHT;
renderTargetParams.width = video_mode_data.width;
renderTargetParams.height = video_mode_data.height;
renderTargetParams.scenesPerFrame = 1;
renderTargetParams.multisampleMode = msaa;
renderTargetParams.multisampleLocations = 0;
@@ -304,15 +349,15 @@ static int vita2d_init_internal(unsigned int temp_pool_size, SceGxmMultisampleMo
// allocate memory for display
displayBufferData[i] = gpu_alloc(
SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW,
4*DISPLAY_STRIDE_IN_PIXELS*DISPLAY_HEIGHT,
4*video_mode_data.stride*video_mode_data.height,
SCE_GXM_COLOR_SURFACE_ALIGNMENT,
SCE_GXM_MEMORY_ATTRIB_READ | SCE_GXM_MEMORY_ATTRIB_WRITE,
&displayBufferUid[i]);
// memset the buffer to black
for (y = 0; y < DISPLAY_HEIGHT; y++) {
unsigned int *row = (unsigned int *)displayBufferData[i] + y*DISPLAY_STRIDE_IN_PIXELS;
for (x = 0; x < DISPLAY_WIDTH; x++) {
for (y = 0; y < video_mode_data.height; y++) {
unsigned int *row = (unsigned int *)displayBufferData[i] + y*video_mode_data.stride;
for (x = 0; x < video_mode_data.width; x++) {
row[x] = 0xff000000;
}
}
@@ -324,9 +369,9 @@ static int vita2d_init_internal(unsigned int temp_pool_size, SceGxmMultisampleMo
SCE_GXM_COLOR_SURFACE_LINEAR,
(msaa == SCE_GXM_MULTISAMPLE_NONE) ? SCE_GXM_COLOR_SURFACE_SCALE_NONE : SCE_GXM_COLOR_SURFACE_SCALE_MSAA_DOWNSCALE,
SCE_GXM_OUTPUT_REGISTER_SIZE_32BIT,
DISPLAY_WIDTH,
DISPLAY_HEIGHT,
DISPLAY_STRIDE_IN_PIXELS,
video_mode_data.width,
video_mode_data.height,
video_mode_data.stride,
displayBufferData[i]);
// create a sync object that we will associate with this buffer
@@ -334,8 +379,8 @@ static int vita2d_init_internal(unsigned int temp_pool_size, SceGxmMultisampleMo
}
// compute the memory footprint of the depth buffer
const unsigned int alignedWidth = ALIGN(DISPLAY_WIDTH, SCE_GXM_TILE_SIZEX);
const unsigned int alignedHeight = ALIGN(DISPLAY_HEIGHT, SCE_GXM_TILE_SIZEY);
const unsigned int alignedWidth = ALIGN(video_mode_data.width, SCE_GXM_TILE_SIZEX);
const unsigned int alignedHeight = ALIGN(video_mode_data.height, SCE_GXM_TILE_SIZEY);
unsigned int sampleCount = alignedWidth*alignedHeight;
unsigned int depthStrideInSamples = alignedWidth;
if (msaa == SCE_GXM_MULTISAMPLE_4X) {
@@ -713,9 +758,9 @@ static int vita2d_init_internal(unsigned int temp_pool_size, SceGxmMultisampleMo
sizeof(void *),
SCE_GXM_MEMORY_ATTRIB_READ,
&poolUid);
matrix_init_orthographic(_vita2d_ortho_matrix, 0.0f, DISPLAY_WIDTH, DISPLAY_HEIGHT, 0.0f, 0.0f, 1.0f);
matrix_init_orthographic(_vita2d_ortho_matrix, 0.0f, video_mode_data.width, video_mode_data.height, 0.0f, 0.0f, 1.0f);
backBufferIndex = 0;
frontBufferIndex = 0;
@@ -725,23 +770,27 @@ static int vita2d_init_internal(unsigned int temp_pool_size, SceGxmMultisampleMo
if (pgf_module_was_loaded != SCE_SYSMODULE_LOADED)
sceSysmoduleLoadModule(SCE_SYSMODULE_PGF);
vita2d_display_data displayData;
displayData.address = displayBufferData[backBufferIndex];
display_callback(&displayData);
vita2d_initialized = 1;
return 1;
}
int vita2d_init()
{
return vita2d_init_internal(DEFAULT_TEMP_POOL_SIZE, SCE_GXM_MULTISAMPLE_NONE);
return vita2d_init_internal(DEFAULT_TEMP_POOL_SIZE, SCE_GXM_MULTISAMPLE_NONE, VITA2D_VIDEO_MODE_960x544);
}
int vita2d_init_advanced(unsigned int temp_pool_size)
{
return vita2d_init_internal(temp_pool_size, SCE_GXM_MULTISAMPLE_NONE);
return vita2d_init_internal(temp_pool_size, SCE_GXM_MULTISAMPLE_NONE, VITA2D_VIDEO_MODE_960x544);
}
int vita2d_init_advanced_with_msaa(unsigned int temp_pool_size, SceGxmMultisampleMode msaa)
int vita2d_init_advanced_with_msaa(unsigned int temp_pool_size, SceGxmMultisampleMode msaa, vita2d_video_mode video_mode)
{
return vita2d_init_internal(temp_pool_size, msaa);
return vita2d_init_internal(temp_pool_size, msaa, video_mode);
}
void vita2d_wait_rendering_done()
@@ -780,7 +829,7 @@ int vita2d_fini()
gpu_free(depthBufferUid);
for (i = 0; i < DISPLAY_BUFFER_COUNT; i++) {
// clear the buffer then deallocate
memset(displayBufferData[i], 0, DISPLAY_HEIGHT*DISPLAY_STRIDE_IN_PIXELS*4);
memset(displayBufferData[i], 0, video_mode_data.height*video_mode_data.stride*4);
gpu_free(displayBufferUid[i]);
// destroy the sync object
@@ -933,7 +982,7 @@ int vita2d_get_clipping_enabled()
void vita2d_set_clip_rectangle(int x_min, int y_min, int x_max, int y_max)
{
vita2d_set_viewport(0,0,DISPLAY_WIDTH,DISPLAY_HEIGHT);
vita2d_set_viewport(0,0,video_mode_data.width,video_mode_data.height);
clipping_enabled = 1;
clip_rect_x_min = x_min;
clip_rect_y_min = y_min;
@@ -952,7 +1001,7 @@ void vita2d_set_clip_rectangle(int x_min, int y_min, int x_max, int y_max)
SCE_GXM_STENCIL_OP_ZERO,
0xFF,
0xFF);
vita2d_draw_rectangle(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, 0);
vita2d_draw_rectangle(0, 0, video_mode_data.width, video_mode_data.height, 0);
// set the stencil to 1 in the desired region
sceGxmSetFrontStencilFunc(
_vita2d_context,
@@ -1003,9 +1052,9 @@ int vita2d_common_dialog_update()
updateParam.renderTarget.colorFormat = DISPLAY_COLOR_FORMAT;
updateParam.renderTarget.surfaceType = SCE_GXM_COLOR_SURFACE_LINEAR;
updateParam.renderTarget.width = DISPLAY_WIDTH;
updateParam.renderTarget.height = DISPLAY_HEIGHT;
updateParam.renderTarget.strideInPixels = DISPLAY_STRIDE_IN_PIXELS;
updateParam.renderTarget.width = video_mode_data.width;
updateParam.renderTarget.height = video_mode_data.height;
updateParam.renderTarget.strideInPixels = video_mode_data.stride;
updateParam.renderTarget.colorSurfaceData = displayBufferData[backBufferIndex];
updateParam.renderTarget.depthSurfaceData = depthBufferData;
@@ -1100,7 +1149,7 @@ void vita2d_set_blend_mode_add(int enable)
}
void vita2d_set_viewport(int x, int y, int width, int height){
static float vh = DISPLAY_HEIGHT;
float vh = video_mode_data.height;
float sw = width / 2.;
float sh = height / 2.;
float x_scale = sw;
+50
View File
@@ -1,3 +1,53 @@
# v10.0.0
* add rapi sublibrary for communicating with server (eliminates need for client-side JSON parsing; client must still
provide HTTP functionality). rurl is now deprecated
* renamed 'rhash.h' to 'rc_hash.h' to eliminate conflict with system headers, renamed 'rconsoles.h' and 'rurl.h' for
consistency
* split non-runtime functions out of 'rcheevos.h' as they're not needed by most clients
* allow ranges in rich presence lookups
* add rc_richpresence_size_lines function to fetch line associated to error when processing rich presence script
* add rc_runtime_invalidate_address function to disable achievements when an unknown address is queried
* add RC_CONDITION_RESET_NEXT_IF
* add RC_CONDITION_SUB_HITS
* support MAXOF operator ($) for leaderboard values using trigger syntax
* allow RC_CONDITION_PAUSE_IF and RC_CONDITION_RESET_IF in leaderboard value expression
* changed track parameter of rc_hash_cdreader_open_track_handler to support three virtual tracks:
RC_HASH_CDTRACK_FIRST_DATA, RC_HASH_CDTRACK_LAST and RC_HASH_CDTRACK_LARGEST.
* changed offset parameter of rc_hash_filereader_seek_handler and return value of rc_hash_filereader_tell_handler
from size_t to int64_t to support files larger than 2GB when compiling in 32-bit mode.
* reset to default cd reader if NULL is passed to rc_hash_init_custom_cdreader
* add hash support for RC_CONSOLE_DREAMCAST, RC_CONSOLE_PLAYSTATION_2, RC_CONSOLE_SUPERVISION, and RC_CONSOLE_TIC80
* ignore headers when generating hashs for RC_CONSOLE_PC_ENGINE and RC_CONSOLE_ATARI_7800
* require unique identifier when hashing RC_CONSOLE_SEGA_CD and RC_CONSOLE_SATURN discs
* add expansion memory to RC_CONSOLE_SG1000 memory map
* rename RC_CONSOLE_MAGNAVOX_ODYSSEY -> RC_CONSOLE_MAGNAVOX_ODYSSEY2
* rename RC_CONSOLE_AMIGA_ST -> RC_CONSOLE_ATARI_ST
* add RC_CONSOLE_SUPERVISION, RC_CONSOLE_SHARPX1, RC_CONSOLE_TIC80, RC_CONSOLE_THOMSONTO8
* fix error identifying largest track when track has multiple bins
* fix memory corruption error when cue track has more than 6 INDEXs
* several improvements to data storage for conditions (rc_memref_t and rc_memref_value_t structures have been modified)
# v9.2.0
* fix issue identifying some PC-FX titles where the boot code is not in the first data track
* add enums and labels for RC_CONSOLE_MAGNAVOX_ODYSSEY, RC_CONSOLE_SUPER_CASSETTEVISION, RC_CONSOLE_NEO_GEO_CD,
RC_CONSOLE_FAIRCHILD_CHANNEL_F, RC_CONSOLE_FM_TOWNS, RC_CONSOLE_ZX_SPECTRUM, RC_CONSOLE_GAME_AND_WATCH,
RC_CONSOLE_NOKIA_NGAGE, RC_CONSOLE_NINTENDO_3DS
# v9.1.0
* add hash support and memory map for RC_CONSOLE_MSX
* add hash support and memory map for RC_CONSOLE_PCFX
* include parent directory when hashing non-arcade titles in arcade mode
* support absolute paths in m3u
* make cue scanning case-insensitive
* expand SRAM mapping for RC_CONSOLE_WONDERSWAN
* fix display of measured value when another group has an unmeasured hit count
* fix memory read error when hashing file with no extension
* fix possible divide by zero when using RC_CONDITION_ADD_SOURCE/RC_CONDITION_SUB_SOURCE
* fix classification of secondary RC_CONSOLE_SATURN memory region
# v9.0.0
* new size: RC_MEMSIZE_BITCOUNT
+69 -397
View File
@@ -2,17 +2,19 @@
**rcheevos** is a set of C code, or a library if you will, that tries to make it easier for emulators to process [RetroAchievements](https://retroachievements.org) data, providing support for achievements and leaderboards for their players.
Keep in mind that **rcheevos** does *not* provide HTTP network connections or JSON parsing. Clients must get data from RetroAchievements, parse the JSON payloads and pass the results down to **rcheevos** for processing. (**TODO**: document the server API and JSON schema.)
Keep in mind that **rcheevos** does *not* provide HTTP network connections. Clients must get data from RetroAchievements, and pass the response down to **rcheevos** for processing.
Not all structures defined by **rcheevos** can be created via the public API, but are exposed to allow interactions beyond just creation, destruction, and testing, such as the ones required by UI code that helps to create them.
Finally, **rcheevos** does *not* allocate or manage memory by itself. All structures that can be returned by it have a function to determine the number of bytes needed to hold the structure, and another one that actually builds the structure using a caller-provided buffer to bake it. However, calls to **rcheevos** may allocate and/or free memory as part of the Lua runtime, which is a dependency.
Finally, **rcheevos** does *not* allocate or manage memory by itself. All structures that can be returned by it have a function to determine the number of bytes needed to hold the structure, and another one that actually builds the structure using a caller-provided buffer to bake it.
## Lua
RetroAchievements has decided to support achievements written using the [Lua](https://www.lua.org) language. The current expression-based implementation was too limiting already for the Nintendo 64, and was preventing the support of other systems.
RetroAchievements is considering the use of the [Lua](https://www.lua.org) language to expand the syntax supported for creating achievements. The current expression-based implementation is often limiting on newer systems.
> **rcheevos** does *not* create or maintain a Lua state, you have to create your own state and provide it to **rcheevos** to be used when Lua-coded achievements are found.
At this point, to enable Lua support, you must compile with an additional compilation flag: `HAVE_LUA`, as neither the backend nor the UI for editing achievements are currently Lua-enabled.
> **rcheevos** does *not* create or maintain a Lua state, you have to create your own state and provide it to **rcheevos** to be used when Lua-coded achievements are found. Calls to **rcheevos** may allocate and/or free additional memory as part of the Lua runtime.
Lua functions used in trigger operands receive two parameters: `peek`, which is used to read from the emulated system's memory, and `userdata`, which must be passed to `peek`. `peek`'s signature is the same as its C counterpart:
@@ -22,7 +24,9 @@ function peek(address, num_bytes, userdata)
## API
> An understanding about how achievements are developed may be useful, you can read more about it [here](http://docs.retroachievements.org/Developer-docs/).
An understanding about how achievements are developed may be useful, you can read more about it [here](http://docs.retroachievements.org/Developer-docs/).
Most of the exposed APIs are documented [here](https://github.com/RetroAchievements/rcheevos/wiki)
### User Configuration
@@ -32,7 +36,9 @@ If your platform will benefit from a different value, define a new value for it
### Return values
The functions that compute the amount of memory that something will take return the number of bytes, or a negative value from the following enumeration:
Any function in the rcheevos library that returns a success indicator will return one of the following values.
These are in `rc_error.h`.
```c
enum {
@@ -60,7 +66,9 @@ enum {
RC_MISSING_VALUE_MEASURED = -21,
RC_MULTIPLE_MEASURED = -22,
RC_INVALID_MEASURED_TARGET = -23,
RC_INVALID_COMPARISON = -24
RC_INVALID_COMPARISON = -24,
RC_INVALID_STATE = -25,
RC_INVALID_JSON = -26
};
```
@@ -73,6 +81,8 @@ const char* rc_error_str(int ret);
This enumeration uniquely identifies each of the supported platforms in RetroAchievements.
These are in `rc_consoles.h`.
```c
enum {
RC_CONSOLE_MEGA_DRIVE = 1,
@@ -97,7 +107,7 @@ enum {
RC_CONSOLE_WII_U = 20,
RC_CONSOLE_PLAYSTATION_2 = 21,
RC_CONSOLE_XBOX = 22,
RC_CONSOLE_SKYNET = 23,
RC_CONSOLE_MAGNAVOX_ODYSSEY2 = 23,
RC_CONSOLE_POKEMON_MINI = 24,
RC_CONSOLE_ATARI_2600 = 25,
RC_CONSOLE_MS_DOS = 26,
@@ -110,7 +120,7 @@ enum {
RC_CONSOLE_SG1000 = 33,
RC_CONSOLE_VIC20 = 34,
RC_CONSOLE_AMIGA = 35,
RC_CONSOLE_AMIGA_ST = 36,
RC_CONSOLE_ATARI_ST = 36,
RC_CONSOLE_AMSTRAD_PC = 37,
RC_CONSOLE_APPLE_II = 38,
RC_CONSOLE_SATURN = 39,
@@ -129,338 +139,28 @@ enum {
RC_CONSOLE_X68K = 52,
RC_CONSOLE_WONDERSWAN = 53,
RC_CONSOLE_CASSETTEVISION = 54,
RC_CONSOLE_SUPER_CASSETTEVISION = 55
RC_CONSOLE_SUPER_CASSETTEVISION = 55,
RC_CONSOLE_NEO_GEO_CD = 56,
RC_CONSOLE_FAIRCHILD_CHANNEL_F = 57,
RC_CONSOLE_FM_TOWNS = 58,
RC_CONSOLE_ZX_SPECTRUM = 59,
RC_CONSOLE_GAME_AND_WATCH = 60,
RC_CONSOLE_NOKIA_NGAGE = 61,
RC_CONSOLE_NINTENDO_3DS = 62,
RC_CONSOLE_SUPERVISION = 63,
RC_CONSOLE_SHARPX1 = 64,
RC_CONSOLE_TIC80 = 65,
RC_CONSOLE_THOMSONTO8 = 66
};
```
### `rc_operand_t`
## Runtime support
An operand is the leaf node of RetroAchievements expressions, and can hold one of the following:
The runtime encapsulates a set of achievements, leaderboards, and rich presence for a game and manages processing them for each frame. When important things occur, events are raised for the caller via a callback.
* A constant integer or floating-point value
* A memory address of the system being emulated
* A reference to the Lua function that will be called to provide the value
These are in `rc_runtime.h`.
```c
typedef struct {
union {
/* A value read from memory. */
rc_memref_value_t* memref;
/* An integer value. */
unsigned num;
/* A floating point value. */
double dbl;
/* A reference to the Lua function that provides the value. */
int luafunc;
};
/* specifies which member of the value union is being used */
char type;
/* the actual RC_MEMSIZE of the operand - memref.size may differ */
char size;
}
rc_operand_t;
```
The `size` field, when applicable, holds one of these values:
```c
enum {
RC_MEMSIZE_8_BITS,
RC_MEMSIZE_16_BITS,
RC_MEMSIZE_24_BITS,
RC_MEMSIZE_32_BITS,
RC_MEMSIZE_LOW,
RC_MEMSIZE_HIGH,
RC_MEMSIZE_BIT_0,
RC_MEMSIZE_BIT_1,
RC_MEMSIZE_BIT_2,
RC_MEMSIZE_BIT_3,
RC_MEMSIZE_BIT_4,
RC_MEMSIZE_BIT_5,
RC_MEMSIZE_BIT_6,
RC_MEMSIZE_BIT_7,
RC_MEMSIZE_BITCOUNT
};
```
The `type` field is always valid, and holds one of these values:
```c
enum {
RC_OPERAND_ADDRESS, /* The value of a live address in RAM. */
RC_OPERAND_DELTA, /* The value last known at this address. */
RC_OPERAND_CONST, /* A 32-bit unsigned integer. */
RC_OPERAND_FP, /* A floating point value. */
RC_OPERAND_LUA, /* A Lua function that provides the value. */
RC_OPERAND_PRIOR, /* The last differing value at this address. */
RC_OPERAND_BCD, /* The BCD-decoded value of a live address in RAM */
RC_OPERAND_INVERTED /* The twos-complement value of a live address in RAM */
};
```
`RC_OPERAND_ADDRESS`, `RC_OPERAND_DELTA`, `RC_OPERAND_PRIOR`, `RC_OPERAND_BCD`, and `RC_OPERAND_INVERTED` mean that `memref` is active. `RC_OPERAND_CONST` means that `num` is active. `RC_OPERAND_FP` means that `dbl` is active. `RC_OPERAND_LUA` means `luafunc` is active.
### `rc_condition_t`
A condition compares its two operands according to the defined operator. It also keeps track of other things to make it possible to code more advanced achievements.
```c
typedef struct rc_condition_t rc_condition_t;
struct rc_condition_t {
/* The condition's operands. */
rc_operand_t operand1;
rc_operand_t operand2;
/* Required hits to fire this condition. */
unsigned required_hits;
/* Number of hits so far. */
unsigned current_hits;
/* The next condition in the chain. */
rc_condition_t* next;
/* The type of the condition. */
char type;
/* The comparison operator to use. */
char oper; /* operator is a reserved word in C++. */
/* Set if the condition needs to processed as part of the "check if paused" pass. */
char pause;
/* Whether or not the condition evaluated as true on the last check. */
char is_true;
};
```
`type` can be one of these values:
```c
enum {
RC_CONDITION_STANDARD,
RC_CONDITION_PAUSE_IF,
RC_CONDITION_RESET_IF,
RC_CONDITION_ADD_SOURCE,
RC_CONDITION_SUB_SOURCE,
RC_CONDITION_ADD_HITS,
RC_CONDITION_AND_NEXT,
RC_CONDITION_MEASURED,
RC_CONDITION_ADD_ADDRESS,
RC_CONDITION_TRIGGER,
RC_CONDITION_MEASURED_IF
};
```
`oper` is the comparison operator to be used when comparing the two operands:
```c
enum {
RC_OPERATOR_EQ,
RC_OPERATOR_LT,
RC_OPERATOR_LE,
RC_OPERATOR_GT,
RC_OPERATOR_GE,
RC_OPERATOR_NE,
RC_OPERATOR_NONE,
RC_OPERATOR_MULT,
RC_OPERATOR_DIV,
RC_OPERATOR_AND
};
```
### `rc_condset_t`
Condition sets are an ordered collection of conditions (`rc_condition_t`), which are usually and'ed together to help build complex expressions for achievements.
```c
typedef struct rc_condset_t rc_condset_t;
struct rc_condset_t {
/* The next condition set in the chain. */
rc_condset_t* next;
/* The list of conditions in this condition set. */
rc_condition_t* conditions;
/* True if any condition in the set is a pause condition. */
char has_pause;
};
```
### `rc_trigger_t`
Triggers are the basic blocks of achievements and leaderboards. In fact, achievements are just triggers with some additional information like title, description, a badge, and some state, like whether it has already been awarded or not. All the logic to test if an achievement should be awarded is encapsulated in `rc_trigger_t`.
```c
typedef struct {
/* The main condition set. */
rc_condset_t* requirement;
/* The list of sub condition sets in this test. */
rc_condset_t* alternative;
/* The memory references required by the trigger. */
rc_memref_value_t* memrefs;
}
rc_trigger_t;
```
The size in bytes of memory a trigger needs to be created is given by the `rc_trigger_size` function:
```c
int rc_trigger_size(const char* memaddr);
```
The return value is the size needed for the trigger described by the `memaddr` parameter, or a negative value with an [error code](#return-values).
Once the memory size is known, `rc_parse_trigger` can be called to actually construct a trigger in the caller-provided buffer:
```c
rc_trigger_t* rc_parse_trigger(void* buffer, const char* memaddr, lua_State* L, int funcs_ndx);
```
`buffer` is the caller-allocated buffer, which must have enough space for the trigger. `memaddr` describes the trigger, and must be the same one used to compute the trigger's size with `rc_trigger_size`. `L` must be a valid Lua state, and `funcs_ndx` must be an index to the current Lua stack which contains a table which is a map of names to functions. This map is used to look for operands which are Lua functions.
Once the trigger is created, `rc_evaluate_trigger` can be called to test whether the trigger fires or not.
```c
int rc_evaluate_trigger(rc_trigger_t* trigger, rc_peek_t peek, void* ud, lua_State* L);
```
`trigger` is the trigger to test. `peek` is a callback used to read bytes from the emulated memory. `ud` is an user-provided opaque value that is passed to `peek`. `L` is the Lua state in which context the Lua functions are looked for and called, if necessary.
`rc_peek_t`'s signature is:
```c
typedef unsigned (*rc_peek_t)(unsigned address, unsigned num_bytes, void* ud);
```
where `address` is the starting address to read from, `num_bytes` the number of bytes to read (1, 2, or 4, little-endian), and `ud` is the same value passed to `rc_test_trigger`.
> Addresses passed to `peek` do *not* map 1:1 to the emulated memory. (**TODO**: document the mapping from `peek` addresses to emulated memory for each supported system.)
The return value of `rc_evaluate_trigger` is one of the following:
```c
enum {
RC_TRIGGER_STATE_INACTIVE, /* achievement is not being processed */
RC_TRIGGER_STATE_WAITING, /* achievement cannot trigger until it has been false for at least one frame */
RC_TRIGGER_STATE_ACTIVE, /* achievement is active and may trigger */
RC_TRIGGER_STATE_PAUSED, /* achievement is currently paused and will not trigger */
RC_TRIGGER_STATE_RESET, /* achievement hit counts were reset */
RC_TRIGGER_STATE_TRIGGERED, /* achievement has triggered */
RC_TRIGGER_STATE_PRIMED /* all non-Trigger conditions are true */
};
```
Finally, `rc_reset_trigger` can be used to reset the internal state of a trigger.
```c
void rc_reset_trigger(rc_trigger_t* self);
```
### `rc_value_t`
A value is a collection of conditions that result in a single RC_CONDITION_MEASURED expression. It's used to calculate the value for a leaderboard and for lookups in rich presence.
```c
typedef struct {
/* The list of conditions to evaluate. */
rc_condset_t* conditions;
/* The memory references required by the value. */
rc_memref_value_t* memrefs;
}
rc_value_t;
```
The size in bytes needed to create a value can be computed by `rc_value_size`:
```c
int rc_value_size(const char* memaddr);
```
With the size at hand, the caller can allocate the necessary memory and pass it to `rc_parse_value` to create the value:
```c
rc_value_t* rc_parse_value(void* buffer, const char* memaddr, lua_State* L, int funcs_ndx);
```
`buffer`, `memaddr`, `L`, and `funcs_ndx` are the same as in [`rc_parse_trigger`](#rc_parse_trigger).
To compute the value, use `rc_evaluate_value`:
```c
int rc_evaluate_value(rc_value_t* value, rc_peek_t peek, void* ud, lua_State* L);
```
`value` is the value to compute the value of, and `peek`, `ud`, and `L`, are as in [`rc_test_trigger`](#rc_test_trigger).
### `rc_lboard_t`
Leaderboards track a value over time, starting when a trigger is fired. The leaderboard can be canceled depending on the value of another trigger, and submitted to the RetroAchievements server depending on a third trigger.
The value submitted comes from the `value` field. Values displayed to the player come from the `progress` field unless it's `NULL`, in which case it's the same as `value`.
```c
typedef struct {
rc_trigger_t start;
rc_trigger_t submit;
rc_trigger_t cancel;
rc_value_t value;
rc_value_t* progress;
rc_memref_value_t* memrefs;
char state;
}
rc_lboard_t;
```
Leaderboards are created and parsed just the same as triggers and values:
```c
int rc_lboard_size(const char* memaddr);
rc_lboard_t* rc_parse_lboard(void* buffer, const char* memaddr, lua_State* L, int funcs_ndx);
```
A leaderboard can be evaluated with the `rc_evaluate_lboard` function:
```c
int rc_evaluate_lboard(rc_lboard_t* lboard, int* value, rc_peek_t peek, void* peek_ud, lua_State* L);
```
The function returns an action that must be performed by the caller, and `value` contains the value to be used for that action when the function returns. The action can be one of:
```c
enum {
RC_LBOARD_STATE_INACTIVE, /* leaderboard is not being processed */
RC_LBOARD_STATE_WAITING, /* leaderboard cannot activate until the start condition has been false for at least one frame */
RC_LBOARD_STATE_ACTIVE, /* leaderboard is active and may start */
RC_LBOARD_STATE_STARTED, /* leaderboard attempt in progress */
RC_LBOARD_STATE_CANCELED, /* leaderboard attempt canceled */
RC_LBOARD_STATE_TRIGGERED /* leaderboard attempt complete, value should be submitted */
};
```
The caller must keep track of these values and do the necessary actions:
* `RC_LBOARD_ACTIVE` and `RC_LBOARD_INACTIVE`: just signal that the leaderboard didn't change its state.
* `RC_LBOARD_STARTED`: indicates that the leaderboard has been started, so the caller can i.e. show a message for the player, and start showing its value in the UI.
* `RC_LBOARD_CANCELED`: the leaderboard has been canceled, and the caller can inform the user and stop showing its value.
* `RC_LBOARD_TRIGGERED`: the leaderboard has been finished, and the value must be submitted to the RetroAchievements server; the caller can also notify the player and stop showing the value in the UI.
`rc_reset_lboard` resets the leaderboard:
```c
void rc_reset_lboard(rc_lboard_t* lboard);
```
### `rc_runtime_t`
The runtime encapsulates a set of achievements and leaderboards and manages processing them for each frame. When important things occur, events are raised for the caller via a callback.
The `rc_runtime_t` structure uses several forward-defines. If you need access to the actual contents of any of the forward-defined structures, those definitions are in `rc_runtime_types.h`
```c
typedef struct rc_runtime_t {
@@ -473,11 +173,12 @@ typedef struct rc_runtime_t {
unsigned lboard_capacity;
rc_runtime_richpresence_t* richpresence;
char* richpresence_display_buffer;
char richpresence_update_timer;
rc_memref_value_t* memrefs;
rc_memref_value_t** next_memref;
rc_value_t* variables;
rc_value_t** next_variable;
}
rc_runtime_t;
```
@@ -496,7 +197,7 @@ int rc_runtime_activate_richpresence(rc_runtime_t* runtime, const char* script,
The runtime should be called once per frame to evaluate the state of the active achievements/leaderboards:
```c
void rc_runtime_do_frame(rc_runtime_t* runtime, rc_runtime_event_handler_t event_handler, rc_peek_t peek, void* ud, lua_State* L);
void rc_runtime_do_frame(rc_runtime_t* runtime, rc_runtime_event_handler_t event_handler, rc_runtime_peek_t peek, void* ud, lua_State* L);
```
The `event_handler` is a callback function that is called for each event that occurs when processing the frame.
@@ -531,6 +232,10 @@ The `event.type` field will be one of the following:
The leaderboard value has changed.
* RC_RUNTIME_EVENT_LBOARD_TRIGGERED (id=leaderboard id, value=leaderboard value)
The leaderboard's submit condition has been met and the user should be informed that a leaderboard attempt was successful. The value should be submitted.
* RC_RUNTIME_EVENT_ACHIEVEMENT_DISABLED (id=achievement id)
The achievement has been disabled by a call to `rc_invalidate_address`.
* RC_RUNTIME_EVENT_LBOARD_DISABLED (id=leaderboard id)
The achievement has been disabled by a call to `rc_invalidate_address`.
When an achievement triggers, it should be deactivated so it won't trigger again:
```c
@@ -538,7 +243,13 @@ void rc_runtime_deactivate_achievement(rc_runtime_t* runtime, unsigned id);
```
Additionally, the unlock should be submitted to the server.
When a leaderboard triggers, it should not be deactivated in case the player wants to try again for a better score. The value should be submitted to the server.
When a leaderboard triggers, it should not be deactivated in case the player wants to try again for a better score. The value should be submitted to the server.
For `RC_RUNTIME_EVENT_LBOARD_UPDATED` and `RC_RUNTIME_EVENT_LBOARD_TRIGGERED` events, there is a helper function to call if you wish to display the leaderboard value on screen.
```c
int rc_runtime_format_lboard_value(char* buffer, int size, int value, int format);
```
`rc_runtime_do_frame` also periodically updates the rich presense string (every 60 frames). To get the current value, call
```c
@@ -552,73 +263,34 @@ void rc_runtime_reset(rc_runtime_t* runtime);
This ensures any active achievements/leaderboards are set back to their initial states and prevents unexpected triggers when the memory changes in atypical way.
## Server Communication
### Value Formatting
**rapi** builds URLs to access many RetroAchievements web services. Its purpose it to just to free the developer from having to URL-encode parameters and build correct URLs that are valid for the server.
**rcheevos** includes helper functions to parse formatting strings from RetroAchievements, and format values according to them.
**rapi** does *not* make HTTP requests.
`rc_parse_format` returns the format for the given string:
NOTE: **rapi** is a replacement for **rurl**. **rurl** has been deprecated.
```c
int rc_parse_format(const char* format_str);
```
These are in `rc_api_user.h`, `rc_api_runtime.h` and `rc_api_common.h`.
The returned value is one of:
The basic process of making an **rapi** call is to initialize a params object, call a function to convert it to a URL, send that to the server, then pass the response to a function to convert it into a response object, and handle the response values.
```c
enum {
RC_FORMAT_FRAMES,
RC_FORMAT_SECONDS,
RC_FORMAT_CENTISECS,
RC_FORMAT_SCORE,
RC_FORMAT_VALUE,
RC_FORMAT_MINUTES,
RC_FORMAT_SECONDS_AS_MINUTES
};
```
`RC_FORMAT_VALUE` is returned if `format_str` doesn't contain a valid format.
`rc_format_value` can be used to format the given value into the provided buffer:
```c
int rc_format_value(char* buffer, int size, int value, int format);
```
`buffer` receives `value` formatted according to `format`. No more than `size` characters will be written to `buffer`. 32 characters are enough to hold any valid value with any format. The returned value is the number of characters written.
# **rurl**
**rurl** builds URLs to access many RetroAchievements web services. Its purpose it to just to free the developer from having to URL-encode parameters and build correct URL that are valid for the server.
**rurl** does *not* make HTTP requests.
## API
### Return values
All functions return `0` if successful, or `-1` in case of errors. Errors are usually because the provided buffer is too small to hold the URL. If your buffer is large and you're still receiving errors, please open an issue.
An example can be found on the [rc_api_init_login_request](https://github.com/RetroAchievements/rcheevos/wiki/rc_api_init_login_request#example) page.
### Functions
All functions take a `buffer`, where the URL will be written into, and `size` with the size of the buffer. The other parameters are particular to the desired URL.
Please see the [wiki](https://github.com/RetroAchievements/rcheevos/wiki) for details on the functions exposed for **rapi**.
## Game Identification
**rhash** provides logic for generating a RetroAchievements hash for a given game. There are two ways to use the API - you can pass the filename and let rhash open and process the file, or you can pass the buffered copy of the file to rhash if you've already loaded it into memory.
These are in `rc_hash.h`.
```c
int rc_url_award_cheevo(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned cheevo_id, int hardcore);
int rc_url_submit_lboard(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned lboard_id, unsigned value, unsigned char hash[16]);
int rc_url_get_gameid(char* buffer, size_t size, unsigned char hash[16]);
int rc_url_get_patch(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned gameid);
int rc_url_get_badge_image(char* buffer, size_t size, const char* badge_name);
int rc_url_login_with_password(char* buffer, size_t size, const char* user_name, const char* password);
int rc_url_login_with_token(char* buffer, size_t size, const char* user_name, const char* login_token);
int rc_url_get_unlock_list(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned gameid, int hardcore);
int rc_url_post_playing(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned gameid);
int rc_hash_generate_from_buffer(char hash[33], int console_id, uint8_t* buffer, size_t buffer_size);
int rc_hash_generate_from_file(char hash[33], int console_id, const char* path);
```
+63
View File
@@ -0,0 +1,63 @@
#ifndef RC_API_REQUEST_H
#define RC_API_REQUEST_H
#include "rc_error.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* A block of memory for variable length data (like strings and arrays).
*/
typedef struct rc_api_buffer_t {
/* The current location where data is being written */
char* write;
/* The first byte past the end of data where writing cannot occur */
char* end;
/* The next block in the allocated memory chain */
struct rc_api_buffer_t* next;
/* The buffer containing the data. The actual size may be larger than 256 bytes for buffers allocated in
* the next chain. The 256 byte size specified is for the initial allocation within the container object. */
char data[256];
}
rc_api_buffer_t;
/**
* A constructed request to send to the retroachievements server.
*/
typedef struct rc_api_request_t {
/* The URL to send the request to (contains protocol, host, path, and query args) */
const char* url;
/* Additional query args that should be sent via a POST command. If null, GET may be used */
const char* post_data;
/* Storage for the url and post_data */
rc_api_buffer_t buffer;
}
rc_api_request_t;
/**
* Common attributes for all server responses.
*/
typedef struct rc_api_response_t {
/* Server-provided success indicator (non-zero on failure) */
int succeeded;
/* Server-provided message associated to the failure */
const char* error_message;
/* Storage for the response data */
rc_api_buffer_t buffer;
}
rc_api_response_t;
void rc_api_destroy_request(rc_api_request_t* request);
void rc_api_set_host(const char* hostname);
void rc_api_set_image_host(const char* hostname);
#ifdef __cplusplus
}
#endif
#endif /* RC_API_REQUEST_H */
+284
View File
@@ -0,0 +1,284 @@
#ifndef RC_API_RUNTIME_H
#define RC_API_RUNTIME_H
#include "rc_api_request.h"
#include <time.h>
#ifdef __cplusplus
extern "C" {
#endif
/* --- Fetch Image --- */
/**
* API parameters for a fetch image request.
* NOTE: fetch image server response is the raw image data. There is no rc_api_process_fetch_image_response function.
*/
typedef struct rc_api_fetch_image_request_t {
/* The name of the image to fetch */
const char* image_name;
/* The type of image to fetch */
int image_type;
}
rc_api_fetch_image_request_t;
#define RC_IMAGE_TYPE_GAME 1
#define RC_IMAGE_TYPE_ACHIEVEMENT 2
#define RC_IMAGE_TYPE_ACHIEVEMENT_LOCKED 3
#define RC_IMAGE_TYPE_USER 4
int rc_api_init_fetch_image_request(rc_api_request_t* request, const rc_api_fetch_image_request_t* api_params);
/* --- Resolve Hash --- */
/**
* API parameters for a resolve hash request.
*/
typedef struct rc_api_resolve_hash_request_t {
/* The username of the player */
const char* username;
/* The API token from the login request */
const char* api_token;
/* The generated hash of the game to be identified */
const char* game_hash;
}
rc_api_resolve_hash_request_t;
/**
* Response data for a resolve hash request.
*/
typedef struct rc_api_resolve_hash_response_t {
/* The unique identifier of the game, 0 if no match was found */
unsigned game_id;
/* Common server-provided response information */
rc_api_response_t response;
}
rc_api_resolve_hash_response_t;
int rc_api_init_resolve_hash_request(rc_api_request_t* request, const rc_api_resolve_hash_request_t* api_params);
int rc_api_process_resolve_hash_response(rc_api_resolve_hash_response_t* response, const char* server_response);
void rc_api_destroy_resolve_hash_response(rc_api_resolve_hash_response_t* response);
/* --- Fetch Game Data --- */
/**
* API parameters for a fetch game data request.
*/
typedef struct rc_api_fetch_game_data_request_t {
/* The username of the player */
const char* username;
/* The API token from the login request */
const char* api_token;
/* The unique identifier of the game */
unsigned game_id;
}
rc_api_fetch_game_data_request_t;
/* A leaderboard definition */
typedef struct rc_api_leaderboard_definition_t {
/* The unique identifier of the leaderboard */
unsigned id;
/* The format to pass to rc_format_value to format the leaderboard value */
int format;
/* The title of the leaderboard */
const char* title;
/* The description of the leaderboard */
const char* description;
/* The definition of the leaderboard to be passed to rc_runtime_activate_lboard */
const char* definition;
}
rc_api_leaderboard_definition_t;
/* An achievement definition */
typedef struct rc_api_achievement_definition_t {
/* The unique identifier of the achievement */
unsigned id;
/* The number of points the achievement is worth */
unsigned points;
/* The achievement category (core, unofficial) */
unsigned category;
/* The title of the achievement */
const char* title;
/* The dscription of the achievement */
const char* description;
/* The definition of the achievement to be passed to rc_runtime_activate_achievement */
const char* definition;
/* The author of the achievment */
const char* author;
/* The image name for the achievement badge */
const char* badge_name;
/* When the achievement was first uploaded to the server */
time_t created;
/* When the achievement was last modified on the server */
time_t updated;
}
rc_api_achievement_definition_t;
#define RC_ACHIEVEMENT_CATEGORY_CORE 3
#define RC_ACHIEVEMENT_CATEGORY_UNOFFICIAL 5
/**
* Response data for a fetch game data request.
*/
typedef struct rc_api_fetch_game_data_response_t {
/* The unique identifier of the game */
unsigned id;
/* The console associated to the game */
unsigned console_id;
/* The title of the game */
const char* title;
/* The image name for the game badge */
const char* image_name;
/* The rich presence script for the game to be passed to rc_runtime_activate_richpresence */
const char* rich_presence_script;
/* An array of achievements for the game */
rc_api_achievement_definition_t* achievements;
/* The number of items in the achievements array */
unsigned num_achievements;
/* An array of leaderboards for the game */
rc_api_leaderboard_definition_t* leaderboards;
/* The number of items in the leaderboards array */
unsigned num_leaderboards;
/* Common server-provided response information */
rc_api_response_t response;
}
rc_api_fetch_game_data_response_t;
int rc_api_init_fetch_game_data_request(rc_api_request_t* request, const rc_api_fetch_game_data_request_t* api_params);
int rc_api_process_fetch_game_data_response(rc_api_fetch_game_data_response_t* response, const char* server_response);
void rc_api_destroy_fetch_game_data_response(rc_api_fetch_game_data_response_t* response);
/* --- Ping --- */
/**
* API parameters for a ping request.
*/
typedef struct rc_api_ping_request_t {
/* The username of the player */
const char* username;
/* The API token from the login request */
const char* api_token;
/* The unique identifier of the game */
unsigned game_id;
/* (optional) The current rich presence evaluation for the user */
const char* rich_presence;
}
rc_api_ping_request_t;
/**
* Response data for a ping request.
*/
typedef struct rc_api_ping_response_t {
/* Common server-provided response information */
rc_api_response_t response;
}
rc_api_ping_response_t;
int rc_api_init_ping_request(rc_api_request_t* request, const rc_api_ping_request_t* api_params);
int rc_api_process_ping_response(rc_api_ping_response_t* response, const char* server_response);
void rc_api_destroy_ping_response(rc_api_ping_response_t* response);
/* --- Award Achievement --- */
/**
* API parameters for an award achievement request.
*/
typedef struct rc_api_award_achievement_request_t {
/* The username of the player */
const char* username;
/* The API token from the login request */
const char* api_token;
/* The unique identifier of the achievement */
unsigned achievement_id;
/* Non-zero if the achievement was earned in hardcore */
int hardcore;
/* The hash associated to the game being played */
const char* game_hash;
}
rc_api_award_achievement_request_t;
/**
* Response data for an award achievement request.
*/
typedef struct rc_api_award_achievement_response_t {
/* The unique identifier of the achievement that was awarded */
unsigned awarded_achievement_id;
/* The updated player score */
unsigned new_player_score;
/* Common server-provided response information */
rc_api_response_t response;
}
rc_api_award_achievement_response_t;
int rc_api_init_award_achievement_request(rc_api_request_t* request, const rc_api_award_achievement_request_t* api_params);
int rc_api_process_award_achievement_response(rc_api_award_achievement_response_t* response, const char* server_response);
void rc_api_destroy_award_achievement_response(rc_api_award_achievement_response_t* response);
/* --- Submit Leaderboard Entry --- */
/**
* API parameters for a submit lboard entry request.
*/
typedef struct rc_api_submit_lboard_entry_request_t {
/* The username of the player */
const char* username;
/* The API token from the login request */
const char* api_token;
/* The unique identifier of the leaderboard */
unsigned leaderboard_id;
/* The value being submitted */
int score;
/* The hash associated to the game being played */
const char* game_hash;
}
rc_api_submit_lboard_entry_request_t;
/* A leaderboard entry */
typedef struct rc_api_lboard_entry_t {
/* The user associated to the entry */
const char* username;
/* The rank of the entry */
unsigned rank;
/* The value of the entry */
int score;
}
rc_api_lboard_entry_t;
/**
* Response data for a submit lboard entry request.
*/
typedef struct rc_api_submit_lboard_entry_response_t {
/* The value that was submitted */
int submitted_score;
/* The player's best submitted value */
int best_score;
/* The player's new rank within the leaderboard */
unsigned new_rank;
/* The total number of entries in the leaderboard */
unsigned num_entries;
/* An array of the top entries for the leaderboard */
rc_api_lboard_entry_t* top_entries;
/* The number of items in the top_entries array */
unsigned num_top_entries;
/* Common server-provided response information */
rc_api_response_t response;
}
rc_api_submit_lboard_entry_response_t;
int rc_api_init_submit_lboard_entry_request(rc_api_request_t* request, const rc_api_submit_lboard_entry_request_t* api_params);
int rc_api_process_submit_lboard_entry_response(rc_api_submit_lboard_entry_response_t* response, const char* server_response);
void rc_api_destroy_submit_lboard_entry_response(rc_api_submit_lboard_entry_response_t* response);
#ifdef __cplusplus
}
#endif
#endif /* RC_API_RUNTIME_H */
+115
View File
@@ -0,0 +1,115 @@
#ifndef RC_API_USER_H
#define RC_API_USER_H
#include "rc_api_request.h"
#ifdef __cplusplus
extern "C" {
#endif
/* --- Login --- */
/**
* API parameters for a login request.
* If both password and api_token are provided, api_token will be ignored.
*/
typedef struct rc_api_login_request_t {
/* The username of the player being logged in */
const char* username;
/* The API token from a previous login */
const char* api_token;
/* The player's password */
const char* password;
}
rc_api_login_request_t;
/**
* Response data for a login request.
*/
typedef struct rc_api_login_response_t {
/* The case-corrected username of the player */
const char* username;
/* The API token to use for all future requests */
const char* api_token;
/* The current score of the player */
unsigned score;
/* The number of unread messages waiting for the player on the web site */
unsigned num_unread_messages;
/* Common server-provided response information */
rc_api_response_t response;
}
rc_api_login_response_t;
int rc_api_init_login_request(rc_api_request_t* request, const rc_api_login_request_t* api_params);
int rc_api_process_login_response(rc_api_login_response_t* response, const char* server_response);
void rc_api_destroy_login_response(rc_api_login_response_t* response);
/* --- Start Session --- */
/**
* API parameters for a start session request.
*/
typedef struct rc_api_start_session_request_t {
/* The username of the player */
const char* username;
/* The API token from the login request */
const char* api_token;
/* The unique identifier of the game */
unsigned game_id;
}
rc_api_start_session_request_t;
/**
* Response data for a start session request.
*/
typedef struct rc_api_start_session_response_t {
/* Common server-provided response information */
rc_api_response_t response;
}
rc_api_start_session_response_t;
int rc_api_init_start_session_request(rc_api_request_t* request, const rc_api_start_session_request_t* api_params);
int rc_api_process_start_session_response(rc_api_start_session_response_t* response, const char* server_response);
void rc_api_destroy_start_session_response(rc_api_start_session_response_t* response);
/* --- Fetch User Unlocks --- */
/**
* API parameters for a fetch user unlocks request.
*/
typedef struct rc_api_fetch_user_unlocks_request_t {
/* The username of the player */
const char* username;
/* The API token from the login request */
const char* api_token;
/* The unique identifier of the game */
unsigned game_id;
/* Non-zero to fetch hardcore unlocks, 0 to fetch non-hardcore unlocks */
int hardcore;
}
rc_api_fetch_user_unlocks_request_t;
/**
* Response data for a fetch user unlocks request.
*/
typedef struct rc_api_fetch_user_unlocks_response_t {
/* An array of achievement IDs previously unlocked by the user */
unsigned* achievement_ids;
/* The number of items in the achievement_ids array */
unsigned num_achievement_ids;
/* Common server-provided response information */
rc_api_response_t response;
}
rc_api_fetch_user_unlocks_response_t;
int rc_api_init_fetch_user_unlocks_request(rc_api_request_t* request, const rc_api_fetch_user_unlocks_request_t* api_params);
int rc_api_process_fetch_user_unlocks_response(rc_api_fetch_user_unlocks_response_t* response, const char* server_response);
void rc_api_destroy_fetch_user_unlocks_response(rc_api_fetch_user_unlocks_response_t* response);
#ifdef __cplusplus
}
#endif
#endif /* RC_API_H */
+32
View File
@@ -83,6 +83,38 @@ enum {
const char* rc_console_name(int console_id);
/*****************************************************************************\
| Memory mapping |
\*****************************************************************************/
enum {
RC_MEMORY_TYPE_SYSTEM_RAM, /* normal system memory */
RC_MEMORY_TYPE_SAVE_RAM, /* memory that persists between sessions */
RC_MEMORY_TYPE_VIDEO_RAM, /* memory reserved for graphical processing */
RC_MEMORY_TYPE_READONLY, /* memory that maps to read only data */
RC_MEMORY_TYPE_HARDWARE_CONTROLLER, /* memory for interacting with system components */
RC_MEMORY_TYPE_VIRTUAL_RAM, /* secondary address space that maps to real memory in system RAM */
RC_MEMORY_TYPE_UNUSED /* these addresses don't really exist */
};
typedef struct rc_memory_region_t {
unsigned start_address; /* first address of block as queried by RetroAchievements */
unsigned end_address; /* last address of block as queried by RetroAchievements */
unsigned real_address; /* real address for first address of block */
char type; /* RC_MEMORY_TYPE_ for block */
const char* description; /* short description of block */
}
rc_memory_region_t;
typedef struct rc_memory_regions_t {
const rc_memory_region_t* region;
unsigned num_regions;
}
rc_memory_regions_t;
const rc_memory_regions_t* rc_console_memory_regions(int console_id);
#ifdef __cplusplus
}
#endif
+48
View File
@@ -0,0 +1,48 @@
#ifndef RC_ERROR_H
#define RC_ERROR_H
#ifdef __cplusplus
extern "C" {
#endif
/*****************************************************************************\
| Return values |
\*****************************************************************************/
enum {
RC_OK = 0,
RC_INVALID_LUA_OPERAND = -1,
RC_INVALID_MEMORY_OPERAND = -2,
RC_INVALID_CONST_OPERAND = -3,
RC_INVALID_FP_OPERAND = -4,
RC_INVALID_CONDITION_TYPE = -5,
RC_INVALID_OPERATOR = -6,
RC_INVALID_REQUIRED_HITS = -7,
RC_DUPLICATED_START = -8,
RC_DUPLICATED_CANCEL = -9,
RC_DUPLICATED_SUBMIT = -10,
RC_DUPLICATED_VALUE = -11,
RC_DUPLICATED_PROGRESS = -12,
RC_MISSING_START = -13,
RC_MISSING_CANCEL = -14,
RC_MISSING_SUBMIT = -15,
RC_MISSING_VALUE = -16,
RC_INVALID_LBOARD_FIELD = -17,
RC_MISSING_DISPLAY_STRING = -18,
RC_OUT_OF_MEMORY = -19,
RC_INVALID_VALUE_FLAG = -20,
RC_MISSING_VALUE_MEASURED = -21,
RC_MULTIPLE_MEASURED = -22,
RC_INVALID_MEASURED_TARGET = -23,
RC_INVALID_COMPARISON = -24,
RC_INVALID_STATE = -25,
RC_INVALID_JSON = -26
};
const char* rc_error_str(int ret);
#ifdef __cplusplus
}
#endif
#endif /* RC_ERROR_H */
+18 -10
View File
@@ -1,5 +1,5 @@
#ifndef RHASH_H
#define RHASH_H
#ifndef RC_HASH_H
#define RC_HASH_H
#include <stddef.h>
#include <stdio.h>
@@ -66,10 +66,10 @@ extern "C" {
typedef void* (*rc_hash_filereader_open_file_handler)(const char* path_utf8);
/* moves the file pointer - standard fseek parameters */
typedef void (*rc_hash_filereader_seek_handler)(void* file_handle, size_t offset, int origin);
typedef void (*rc_hash_filereader_seek_handler)(void* file_handle, int64_t offset, int origin);
/* locates the file pointer */
typedef size_t (*rc_hash_filereader_tell_handler)(void* file_handle);
typedef int64_t (*rc_hash_filereader_tell_handler)(void* file_handle);
/* reads the specified number of bytes from the file starting at the read pointer.
* returns the number of bytes actually read.
@@ -92,7 +92,11 @@ extern "C" {
/* ===================================================== */
/* opens a track from the specified file. track 0 indicates the first data track should be opened.
#define RC_HASH_CDTRACK_FIRST_DATA ((uint32_t)-1)
#define RC_HASH_CDTRACK_LAST ((uint32_t)-2)
#define RC_HASH_CDTRACK_LARGEST ((uint32_t)-3)
/* opens a track from the specified file. track 0 indicates the largest data track should be opened.
* returns a handle to be passed to the other functions, or NULL if the track could not be opened.
*/
typedef void* (*rc_hash_cdreader_open_track_handler)(const char* path, uint32_t track);
@@ -105,14 +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);
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_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;
};
void rc_hash_init_default_cdreader(void);
void rc_hash_init_default_cdreader();
void rc_hash_init_custom_cdreader(struct rc_hash_cdreader* reader);
/* ===================================================== */
@@ -121,4 +129,4 @@ extern "C" {
}
#endif
#endif /* RHASH_H */
#endif /* RC_HASH_H */
+142
View File
@@ -0,0 +1,142 @@
#ifndef RC_RUNTIME_H
#define RC_RUNTIME_H
#ifdef __cplusplus
extern "C" {
#endif
#include "rc_error.h"
/*****************************************************************************\
| Forward Declarations (defined in rc_runtime_types.h) |
\*****************************************************************************/
#ifndef RC_RUNTIME_TYPES_H /* prevents pedantic redefinition error */
typedef struct lua_State lua_State;
typedef struct rc_trigger_t rc_trigger_t;
typedef struct rc_lboard_t rc_lboard_t;
typedef struct rc_richpresence_t rc_richpresence_t;
typedef struct rc_memref_t rc_memref_t;
typedef struct rc_value_t rc_value_t;
#endif
/*****************************************************************************\
| Callbacks |
\*****************************************************************************/
/**
* Callback used to read num_bytes bytes from memory starting at address. If
* num_bytes is greater than 1, the value is read in little-endian from
* memory.
*/
typedef unsigned (*rc_runtime_peek_t)(unsigned address, unsigned num_bytes, void* ud);
/*****************************************************************************\
| Runtime |
\*****************************************************************************/
typedef struct rc_runtime_trigger_t {
unsigned id;
rc_trigger_t* trigger;
void* buffer;
rc_memref_t* invalid_memref;
unsigned char md5[16];
int serialized_size;
char owns_memrefs;
}
rc_runtime_trigger_t;
typedef struct rc_runtime_lboard_t {
unsigned id;
int value;
rc_lboard_t* lboard;
void* buffer;
rc_memref_t* invalid_memref;
unsigned char md5[16];
char owns_memrefs;
}
rc_runtime_lboard_t;
typedef struct rc_runtime_richpresence_t {
rc_richpresence_t* richpresence;
void* buffer;
struct rc_runtime_richpresence_t* previous;
char owns_memrefs;
}
rc_runtime_richpresence_t;
typedef struct rc_runtime_t {
rc_runtime_trigger_t* triggers;
unsigned trigger_count;
unsigned trigger_capacity;
rc_runtime_lboard_t* lboards;
unsigned lboard_count;
unsigned lboard_capacity;
rc_runtime_richpresence_t* richpresence;
rc_memref_t* memrefs;
rc_memref_t** next_memref;
rc_value_t* variables;
rc_value_t** next_variable;
}
rc_runtime_t;
void rc_runtime_init(rc_runtime_t* runtime);
void rc_runtime_destroy(rc_runtime_t* runtime);
int rc_runtime_activate_achievement(rc_runtime_t* runtime, unsigned id, const char* memaddr, lua_State* L, int funcs_idx);
void rc_runtime_deactivate_achievement(rc_runtime_t* runtime, unsigned id);
rc_trigger_t* rc_runtime_get_achievement(const rc_runtime_t* runtime, unsigned id);
int rc_runtime_get_achievement_measured(const rc_runtime_t* runtime, unsigned id, unsigned* measured_value, unsigned* measured_target);
int rc_runtime_activate_lboard(rc_runtime_t* runtime, unsigned id, const char* memaddr, lua_State* L, int funcs_idx);
void rc_runtime_deactivate_lboard(rc_runtime_t* runtime, unsigned id);
rc_lboard_t* rc_runtime_get_lboard(const rc_runtime_t* runtime, unsigned id);
int rc_runtime_format_lboard_value(char* buffer, int size, int value, int format);
int rc_runtime_activate_richpresence(rc_runtime_t* runtime, const char* script, lua_State* L, int funcs_idx);
int rc_runtime_get_richpresence(const rc_runtime_t* self, char* buffer, unsigned buffersize, rc_runtime_peek_t peek, void* peek_ud, lua_State* L);
enum {
RC_RUNTIME_EVENT_ACHIEVEMENT_ACTIVATED, /* from WAITING, PAUSED, or PRIMED to ACTIVE */
RC_RUNTIME_EVENT_ACHIEVEMENT_PAUSED,
RC_RUNTIME_EVENT_ACHIEVEMENT_RESET,
RC_RUNTIME_EVENT_ACHIEVEMENT_TRIGGERED,
RC_RUNTIME_EVENT_ACHIEVEMENT_PRIMED,
RC_RUNTIME_EVENT_LBOARD_STARTED,
RC_RUNTIME_EVENT_LBOARD_CANCELED,
RC_RUNTIME_EVENT_LBOARD_UPDATED,
RC_RUNTIME_EVENT_LBOARD_TRIGGERED,
RC_RUNTIME_EVENT_ACHIEVEMENT_DISABLED,
RC_RUNTIME_EVENT_LBOARD_DISABLED
};
typedef struct rc_runtime_event_t {
unsigned id;
int value;
char type;
}
rc_runtime_event_t;
typedef void (*rc_runtime_event_handler_t)(const rc_runtime_event_t* runtime_event);
void rc_runtime_do_frame(rc_runtime_t* runtime, rc_runtime_event_handler_t event_handler, rc_runtime_peek_t peek, void* ud, lua_State* L);
void rc_runtime_reset(rc_runtime_t* runtime);
void rc_runtime_invalidate_address(rc_runtime_t* runtime, unsigned address);
int rc_runtime_progress_size(const rc_runtime_t* runtime, lua_State* L);
int rc_runtime_serialize_progress(void* buffer, const rc_runtime_t* runtime, lua_State* L);
int rc_runtime_deserialize_progress(rc_runtime_t* runtime, const unsigned char* serialized, lua_State* L);
#ifdef __cplusplus
}
#endif
#endif /* RC_RUNTIME_H */
+400
View File
@@ -0,0 +1,400 @@
#ifndef RC_RUNTIME_TYPES_H
#define RC_RUNTIME_TYPES_H
#ifdef __cplusplus
extern "C" {
#endif
#include "rc_error.h"
#ifndef RC_RUNTIME_H /* prevents pedantic redefiniton error */
typedef struct lua_State lua_State;
typedef struct rc_trigger_t rc_trigger_t;
typedef struct rc_lboard_t rc_lboard_t;
typedef struct rc_richpresence_t rc_richpresence_t;
typedef struct rc_memref_t rc_memref_t;
typedef struct rc_value_t rc_value_t;
#endif
/*****************************************************************************\
| Callbacks |
\*****************************************************************************/
/**
* Callback used to read num_bytes bytes from memory starting at address. If
* num_bytes is greater than 1, the value is read in little-endian from
* memory.
*/
typedef unsigned (*rc_peek_t)(unsigned address, unsigned num_bytes, void* ud);
/*****************************************************************************\
| Memory References |
\*****************************************************************************/
/* Sizes. */
enum {
RC_MEMSIZE_8_BITS,
RC_MEMSIZE_16_BITS,
RC_MEMSIZE_24_BITS,
RC_MEMSIZE_32_BITS,
RC_MEMSIZE_LOW,
RC_MEMSIZE_HIGH,
RC_MEMSIZE_BIT_0,
RC_MEMSIZE_BIT_1,
RC_MEMSIZE_BIT_2,
RC_MEMSIZE_BIT_3,
RC_MEMSIZE_BIT_4,
RC_MEMSIZE_BIT_5,
RC_MEMSIZE_BIT_6,
RC_MEMSIZE_BIT_7,
RC_MEMSIZE_BITCOUNT,
RC_MEMSIZE_VARIABLE
};
typedef struct rc_memref_value_t {
/* The current value of this memory reference. */
unsigned value;
/* The last differing value of this memory reference. */
unsigned prior;
/* The size of the value. */
char size;
/* True if the value changed this frame. */
char changed;
/* 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;
}
rc_memref_value_t;
struct rc_memref_t {
/* The current value at the specified memory address. */
rc_memref_value_t value;
/* The memory address of this variable. */
unsigned address;
/* The next memory reference in the chain. */
rc_memref_t* next;
};
/*****************************************************************************\
| Operands |
\*****************************************************************************/
/* types */
enum {
RC_OPERAND_ADDRESS, /* The value of a live address in RAM. */
RC_OPERAND_DELTA, /* The value last known at this address. */
RC_OPERAND_CONST, /* A 32-bit unsigned integer. */
RC_OPERAND_FP, /* A floating point value. */
RC_OPERAND_LUA, /* A Lua function that provides the value. */
RC_OPERAND_PRIOR, /* The last differing value at this address. */
RC_OPERAND_BCD, /* The BCD-decoded value of a live address in RAM. */
RC_OPERAND_INVERTED /* The twos-complement value of a live address in RAM. */
};
typedef struct rc_operand_t {
union {
/* A value read from memory. */
rc_memref_t* memref;
/* An integer value. */
unsigned num;
/* A floating point value. */
double dbl;
/* A reference to the Lua function that provides the value. */
int luafunc;
} value;
/* specifies which member of the value union is being used */
char type;
/* the actual RC_MEMSIZE of the operand - memref.size may differ */
char size;
}
rc_operand_t;
int rc_operand_is_memref(rc_operand_t* operand);
/*****************************************************************************\
| Conditions |
\*****************************************************************************/
/* types */
enum {
/* NOTE: this enum is ordered to optimize the switch statements in rc_test_condset_internal. the values may change between releases */
/* non-combining conditions (third switch) */
RC_CONDITION_STANDARD, /* this should always be 0 */
RC_CONDITION_PAUSE_IF,
RC_CONDITION_RESET_IF,
RC_CONDITION_MEASURED_IF,
RC_CONDITION_TRIGGER,
RC_CONDITION_MEASURED, /* measured also appears in the first switch, so place it at the border between them */
/* modifiers (first switch) */
RC_CONDITION_ADD_SOURCE, /* everything from this point on affects the condition after it */
RC_CONDITION_SUB_SOURCE,
RC_CONDITION_ADD_ADDRESS,
/* logic flags (second switch) */
RC_CONDITION_ADD_HITS,
RC_CONDITION_SUB_HITS,
RC_CONDITION_RESET_NEXT_IF,
RC_CONDITION_AND_NEXT,
RC_CONDITION_OR_NEXT
};
/* operators */
enum {
RC_OPERATOR_EQ,
RC_OPERATOR_LT,
RC_OPERATOR_LE,
RC_OPERATOR_GT,
RC_OPERATOR_GE,
RC_OPERATOR_NE,
RC_OPERATOR_NONE,
RC_OPERATOR_MULT,
RC_OPERATOR_DIV,
RC_OPERATOR_AND
};
typedef struct rc_condition_t rc_condition_t;
struct rc_condition_t {
/* The condition's operands. */
rc_operand_t operand1;
rc_operand_t operand2;
/* Required hits to fire this condition. */
unsigned required_hits;
/* Number of hits so far. */
unsigned current_hits;
/* The next condition in the chain. */
rc_condition_t* next;
/* The type of the condition. */
char type;
/* The comparison operator to use. */
char oper; /* operator is a reserved word in C++. */
/* Set if the condition needs to processed as part of the "check if paused" pass. */
char pause;
/* Whether or not the condition evaluated true on the last check */
char is_true;
};
/*****************************************************************************\
| Condition sets |
\*****************************************************************************/
typedef struct rc_condset_t rc_condset_t;
struct rc_condset_t {
/* The next condition set in the chain. */
rc_condset_t* next;
/* The list of conditions in this condition set. */
rc_condition_t* conditions;
/* True if any condition in the set is a pause condition. */
char has_pause;
/* True if the set is currently paused. */
char is_paused;
/* True if the set has indirect memory references. */
char has_indirect_memrefs;
};
/*****************************************************************************\
| Trigger |
\*****************************************************************************/
enum {
RC_TRIGGER_STATE_INACTIVE, /* achievement is not being processed */
RC_TRIGGER_STATE_WAITING, /* achievement cannot trigger until it has been false for at least one frame */
RC_TRIGGER_STATE_ACTIVE, /* achievement is active and may trigger */
RC_TRIGGER_STATE_PAUSED, /* achievement is currently paused and will not trigger */
RC_TRIGGER_STATE_RESET, /* achievement hit counts were reset */
RC_TRIGGER_STATE_TRIGGERED, /* achievement has triggered */
RC_TRIGGER_STATE_PRIMED, /* all non-Trigger conditions are true */
RC_TRIGGER_STATE_DISABLED /* achievement cannot be processed at this time */
};
struct rc_trigger_t {
/* The main condition set. */
rc_condset_t* requirement;
/* The list of sub condition sets in this test. */
rc_condset_t* alternative;
/* The memory references required by the trigger. */
rc_memref_t* memrefs;
/* The current state of the MEASURED condition. */
unsigned measured_value;
/* The target state of the MEASURED condition */
unsigned measured_target;
/* The current state of the trigger */
char state;
/* True if at least one condition has a non-zero hit count */
char has_hits;
/* True if at least one condition has a non-zero required hit count */
char has_required_hits;
};
int rc_trigger_size(const char* memaddr);
rc_trigger_t* rc_parse_trigger(void* buffer, const char* memaddr, lua_State* L, int funcs_ndx);
int rc_evaluate_trigger(rc_trigger_t* trigger, rc_peek_t peek, void* ud, lua_State* L);
int rc_test_trigger(rc_trigger_t* trigger, rc_peek_t peek, void* ud, lua_State* L);
void rc_reset_trigger(rc_trigger_t* self);
/*****************************************************************************\
| Values |
\*****************************************************************************/
struct rc_value_t {
/* The current value of the variable. */
rc_memref_value_t value;
/* The list of conditions to evaluate. */
rc_condset_t* conditions;
/* The memory references required by the value. */
rc_memref_t* memrefs;
/* The name of the variable. */
const char* name;
/* The next variable in the chain. */
rc_value_t* next;
};
int rc_value_size(const char* memaddr);
rc_value_t* rc_parse_value(void* buffer, const char* memaddr, lua_State* L, int funcs_ndx);
int rc_evaluate_value(rc_value_t* value, rc_peek_t peek, void* ud, lua_State* L);
/*****************************************************************************\
| Leaderboards |
\*****************************************************************************/
/* Return values for rc_evaluate_lboard. */
enum {
RC_LBOARD_STATE_INACTIVE, /* leaderboard is not being processed */
RC_LBOARD_STATE_WAITING, /* leaderboard cannot activate until the start condition has been false for at least one frame */
RC_LBOARD_STATE_ACTIVE, /* leaderboard is active and may start */
RC_LBOARD_STATE_STARTED, /* leaderboard attempt in progress */
RC_LBOARD_STATE_CANCELED, /* leaderboard attempt canceled */
RC_LBOARD_STATE_TRIGGERED, /* leaderboard attempt complete, value should be submitted */
RC_LBOARD_STATE_DISABLED /* leaderboard cannot be processed at this time */
};
struct rc_lboard_t {
rc_trigger_t start;
rc_trigger_t submit;
rc_trigger_t cancel;
rc_value_t value;
rc_value_t* progress;
rc_memref_t* memrefs;
char state;
};
int rc_lboard_size(const char* memaddr);
rc_lboard_t* rc_parse_lboard(void* buffer, const char* memaddr, lua_State* L, int funcs_ndx);
int rc_evaluate_lboard(rc_lboard_t* lboard, int* value, rc_peek_t peek, void* peek_ud, lua_State* L);
void rc_reset_lboard(rc_lboard_t* lboard);
/*****************************************************************************\
| Value formatting |
\*****************************************************************************/
/* Supported formats. */
enum {
RC_FORMAT_FRAMES,
RC_FORMAT_SECONDS,
RC_FORMAT_CENTISECS,
RC_FORMAT_SCORE,
RC_FORMAT_VALUE,
RC_FORMAT_MINUTES,
RC_FORMAT_SECONDS_AS_MINUTES
};
int rc_parse_format(const char* format_str);
int rc_format_value(char* buffer, int size, int value, int format);
/*****************************************************************************\
| Rich Presence |
\*****************************************************************************/
typedef struct rc_richpresence_lookup_item_t rc_richpresence_lookup_item_t;
struct rc_richpresence_lookup_item_t {
unsigned first;
unsigned last;
rc_richpresence_lookup_item_t* left;
rc_richpresence_lookup_item_t* right;
const char* label;
};
typedef struct rc_richpresence_lookup_t rc_richpresence_lookup_t;
struct rc_richpresence_lookup_t {
rc_richpresence_lookup_item_t* root;
rc_richpresence_lookup_t* next;
const char* name;
const char* default_label;
unsigned short format;
};
typedef struct rc_richpresence_display_part_t rc_richpresence_display_part_t;
struct rc_richpresence_display_part_t {
rc_richpresence_display_part_t* next;
const char* text;
rc_richpresence_lookup_t* lookup;
rc_memref_value_t *value;
unsigned short display_type;
};
typedef struct rc_richpresence_display_t rc_richpresence_display_t;
struct rc_richpresence_display_t {
rc_trigger_t trigger;
rc_richpresence_display_t* next;
rc_richpresence_display_part_t* display;
};
struct rc_richpresence_t {
rc_richpresence_display_t* first_display;
rc_richpresence_lookup_t* first_lookup;
rc_memref_t* memrefs;
rc_value_t* variables;
};
int rc_richpresence_size(const char* script);
int rc_richpresence_size_lines(const char* script, int* lines_read);
rc_richpresence_t* rc_parse_richpresence(void* buffer, const char* script, lua_State* L, int funcs_ndx);
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);
#ifdef __cplusplus
}
#endif
#endif /* RC_RUNTIME_TYPES_H */
@@ -1,5 +1,5 @@
#ifndef RURL_H
#define RURL_H
#ifndef RC_URL_H
#define RC_URL_H
#include <stddef.h>
@@ -32,4 +32,4 @@ int rc_url_ping(char* url_buffer, size_t url_buffer_size, char* post_buffer, siz
}
#endif
#endif /* RURL_H */
#endif /* RC_URL_H */
+3 -508
View File
@@ -1,513 +1,8 @@
#ifndef RCHEEVOS_H
#define RCHEEVOS_H
#ifdef __cplusplus
extern "C" {
#endif
typedef struct lua_State lua_State;
/*****************************************************************************\
| Return values |
\*****************************************************************************/
enum {
RC_OK = 0,
RC_INVALID_LUA_OPERAND = -1,
RC_INVALID_MEMORY_OPERAND = -2,
RC_INVALID_CONST_OPERAND = -3,
RC_INVALID_FP_OPERAND = -4,
RC_INVALID_CONDITION_TYPE = -5,
RC_INVALID_OPERATOR = -6,
RC_INVALID_REQUIRED_HITS = -7,
RC_DUPLICATED_START = -8,
RC_DUPLICATED_CANCEL = -9,
RC_DUPLICATED_SUBMIT = -10,
RC_DUPLICATED_VALUE = -11,
RC_DUPLICATED_PROGRESS = -12,
RC_MISSING_START = -13,
RC_MISSING_CANCEL = -14,
RC_MISSING_SUBMIT = -15,
RC_MISSING_VALUE = -16,
RC_INVALID_LBOARD_FIELD = -17,
RC_MISSING_DISPLAY_STRING = -18,
RC_OUT_OF_MEMORY = -19,
RC_INVALID_VALUE_FLAG = -20,
RC_MISSING_VALUE_MEASURED = -21,
RC_MULTIPLE_MEASURED = -22,
RC_INVALID_MEASURED_TARGET = -23,
RC_INVALID_COMPARISON = -24,
RC_INVALID_STATE = -25
};
const char* rc_error_str(int ret);
/*****************************************************************************\
| Callbacks |
\*****************************************************************************/
/**
* Callback used to read num_bytes bytes from memory starting at address. If
* num_bytes is greater than 1, the value is read in little-endian from
* memory.
*/
typedef unsigned (*rc_peek_t)(unsigned address, unsigned num_bytes, void* ud);
/*****************************************************************************\
| Memory References |
\*****************************************************************************/
/* Sizes. */
enum {
RC_MEMSIZE_8_BITS,
RC_MEMSIZE_16_BITS,
RC_MEMSIZE_24_BITS,
RC_MEMSIZE_32_BITS,
RC_MEMSIZE_LOW,
RC_MEMSIZE_HIGH,
RC_MEMSIZE_BIT_0,
RC_MEMSIZE_BIT_1,
RC_MEMSIZE_BIT_2,
RC_MEMSIZE_BIT_3,
RC_MEMSIZE_BIT_4,
RC_MEMSIZE_BIT_5,
RC_MEMSIZE_BIT_6,
RC_MEMSIZE_BIT_7,
RC_MEMSIZE_BITCOUNT
};
typedef struct {
/* The memory address of this variable. */
unsigned address;
/* The size of the variable. */
char size;
/* True if the reference will be used in indirection */
char is_indirect;
} rc_memref_t;
typedef struct rc_memref_value_t rc_memref_value_t;
struct rc_memref_value_t {
/* The value of this memory reference. */
unsigned value;
/* The previous value of this memory reference. */
unsigned previous;
/* The last differing value of this memory reference. */
unsigned prior;
/* The referenced memory */
rc_memref_t memref;
/* The next memory reference in the chain */
rc_memref_value_t* next;
};
/*****************************************************************************\
| Operands |
\*****************************************************************************/
/* types */
enum {
RC_OPERAND_ADDRESS, /* The value of a live address in RAM. */
RC_OPERAND_DELTA, /* The value last known at this address. */
RC_OPERAND_CONST, /* A 32-bit unsigned integer. */
RC_OPERAND_FP, /* A floating point value. */
RC_OPERAND_LUA, /* A Lua function that provides the value. */
RC_OPERAND_PRIOR, /* The last differing value at this address. */
RC_OPERAND_BCD, /* The BCD-decoded value of a live address in RAM */
RC_OPERAND_INVERTED /* The twos-complement value of a live address in RAM */
};
typedef struct {
union {
/* A value read from memory. */
rc_memref_value_t* memref;
/* An integer value. */
unsigned num;
/* A floating point value. */
double dbl;
/* A reference to the Lua function that provides the value. */
int luafunc;
} value;
/* specifies which member of the value union is being used */
char type;
/* the actual RC_MEMSIZE of the operand - memref.size may differ */
char size;
}
rc_operand_t;
/*****************************************************************************\
| Conditions |
\*****************************************************************************/
/* types */
enum {
RC_CONDITION_STANDARD,
RC_CONDITION_PAUSE_IF,
RC_CONDITION_RESET_IF,
RC_CONDITION_ADD_SOURCE,
RC_CONDITION_SUB_SOURCE,
RC_CONDITION_ADD_HITS,
RC_CONDITION_AND_NEXT,
RC_CONDITION_MEASURED,
RC_CONDITION_ADD_ADDRESS,
RC_CONDITION_OR_NEXT,
RC_CONDITION_TRIGGER,
RC_CONDITION_MEASURED_IF
};
/* operators */
enum {
RC_OPERATOR_EQ,
RC_OPERATOR_LT,
RC_OPERATOR_LE,
RC_OPERATOR_GT,
RC_OPERATOR_GE,
RC_OPERATOR_NE,
RC_OPERATOR_NONE,
RC_OPERATOR_MULT,
RC_OPERATOR_DIV,
RC_OPERATOR_AND
};
typedef struct rc_condition_t rc_condition_t;
struct rc_condition_t {
/* The condition's operands. */
rc_operand_t operand1;
rc_operand_t operand2;
/* Required hits to fire this condition. */
unsigned required_hits;
/* Number of hits so far. */
unsigned current_hits;
/* The next condition in the chain. */
rc_condition_t* next;
/* The type of the condition. */
char type;
/* The comparison operator to use. */
char oper; /* operator is a reserved word in C++. */
/* Set if the condition needs to processed as part of the "check if paused" pass. */
char pause;
/* Whether or not the condition evaluated true on the last check */
char is_true;
};
/*****************************************************************************\
| Condition sets |
\*****************************************************************************/
typedef struct rc_condset_t rc_condset_t;
struct rc_condset_t {
/* The next condition set in the chain. */
rc_condset_t* next;
/* The list of conditions in this condition set. */
rc_condition_t* conditions;
/* True if any condition in the set is a pause condition. */
char has_pause;
/* True if the set is currently paused. */
char is_paused;
};
/*****************************************************************************\
| Trigger |
\*****************************************************************************/
enum {
RC_TRIGGER_STATE_INACTIVE, /* achievement is not being processed */
RC_TRIGGER_STATE_WAITING, /* achievement cannot trigger until it has been false for at least one frame */
RC_TRIGGER_STATE_ACTIVE, /* achievement is active and may trigger */
RC_TRIGGER_STATE_PAUSED, /* achievement is currently paused and will not trigger */
RC_TRIGGER_STATE_RESET, /* achievement hit counts were reset */
RC_TRIGGER_STATE_TRIGGERED, /* achievement has triggered */
RC_TRIGGER_STATE_PRIMED /* all non-Trigger conditions are true */
};
typedef struct {
/* The main condition set. */
rc_condset_t* requirement;
/* The list of sub condition sets in this test. */
rc_condset_t* alternative;
/* The memory references required by the trigger. */
rc_memref_value_t* memrefs;
/* The current state of the MEASURED condition. */
unsigned measured_value;
/* The target state of the MEASURED condition */
unsigned measured_target;
/* The current state of the trigger */
char state;
/* True if at least one condition has a non-zero hit count */
char has_hits;
}
rc_trigger_t;
int rc_trigger_size(const char* memaddr);
rc_trigger_t* rc_parse_trigger(void* buffer, const char* memaddr, lua_State* L, int funcs_ndx);
int rc_evaluate_trigger(rc_trigger_t* trigger, rc_peek_t peek, void* ud, lua_State* L);
int rc_test_trigger(rc_trigger_t* trigger, rc_peek_t peek, void* ud, lua_State* L);
void rc_reset_trigger(rc_trigger_t* self);
/*****************************************************************************\
| Values |
\*****************************************************************************/
typedef struct {
/* The list of conditions to evaluate. */
rc_condset_t* conditions;
/* The memory references required by the value. */
rc_memref_value_t* memrefs;
}
rc_value_t;
int rc_value_size(const char* memaddr);
rc_value_t* rc_parse_value(void* buffer, const char* memaddr, lua_State* L, int funcs_ndx);
int rc_evaluate_value(rc_value_t* value, rc_peek_t peek, void* ud, lua_State* L);
/*****************************************************************************\
| Leaderboards |
\*****************************************************************************/
/* Return values for rc_evaluate_lboard. */
enum {
RC_LBOARD_STATE_INACTIVE, /* leaderboard is not being processed */
RC_LBOARD_STATE_WAITING, /* leaderboard cannot activate until the start condition has been false for at least one frame */
RC_LBOARD_STATE_ACTIVE, /* leaderboard is active and may start */
RC_LBOARD_STATE_STARTED, /* leaderboard attempt in progress */
RC_LBOARD_STATE_CANCELED, /* leaderboard attempt canceled */
RC_LBOARD_STATE_TRIGGERED /* leaderboard attempt complete, value should be submitted */
};
typedef struct {
rc_trigger_t start;
rc_trigger_t submit;
rc_trigger_t cancel;
rc_value_t value;
rc_value_t* progress;
rc_memref_value_t* memrefs;
char state;
}
rc_lboard_t;
int rc_lboard_size(const char* memaddr);
rc_lboard_t* rc_parse_lboard(void* buffer, const char* memaddr, lua_State* L, int funcs_ndx);
int rc_evaluate_lboard(rc_lboard_t* lboard, int* value, rc_peek_t peek, void* peek_ud, lua_State* L);
void rc_reset_lboard(rc_lboard_t* lboard);
/*****************************************************************************\
| Value formatting |
\*****************************************************************************/
/* Supported formats. */
enum {
RC_FORMAT_FRAMES,
RC_FORMAT_SECONDS,
RC_FORMAT_CENTISECS,
RC_FORMAT_SCORE,
RC_FORMAT_VALUE,
RC_FORMAT_MINUTES,
RC_FORMAT_SECONDS_AS_MINUTES
};
int rc_parse_format(const char* format_str);
int rc_format_value(char* buffer, int size, int value, int format);
/*****************************************************************************\
| Rich Presence |
\*****************************************************************************/
typedef struct rc_richpresence_lookup_item_t rc_richpresence_lookup_item_t;
struct rc_richpresence_lookup_item_t {
unsigned value;
rc_richpresence_lookup_item_t* next_item;
const char* label;
};
typedef struct rc_richpresence_lookup_t rc_richpresence_lookup_t;
struct rc_richpresence_lookup_t {
rc_richpresence_lookup_item_t* first_item;
rc_richpresence_lookup_t* next;
const char* name;
unsigned short format;
};
typedef struct rc_richpresence_display_part_t rc_richpresence_display_part_t;
struct rc_richpresence_display_part_t {
rc_richpresence_display_part_t* next;
const char* text;
rc_richpresence_lookup_item_t* first_lookup_item;
rc_value_t value;
unsigned short display_type;
};
typedef struct rc_richpresence_display_t rc_richpresence_display_t;
struct rc_richpresence_display_t {
rc_trigger_t trigger;
rc_richpresence_display_t* next;
rc_richpresence_display_part_t* display;
};
typedef struct {
rc_richpresence_display_t* first_display;
rc_richpresence_lookup_t* first_lookup;
rc_memref_value_t* memrefs;
}
rc_richpresence_t;
int rc_richpresence_size(const char* script);
rc_richpresence_t* rc_parse_richpresence(void* buffer, const char* script, lua_State* L, int funcs_ndx);
int rc_evaluate_richpresence(rc_richpresence_t* richpresence, char* buffer, unsigned buffersize, rc_peek_t peek, void* peek_ud, lua_State* L);
/*****************************************************************************\
| Runtime |
\*****************************************************************************/
typedef struct rc_runtime_trigger_t {
unsigned id;
rc_trigger_t* trigger;
void* buffer;
unsigned char md5[16];
int serialized_size;
char owns_memrefs;
}
rc_runtime_trigger_t;
typedef struct rc_runtime_lboard_t {
unsigned id;
int value;
rc_lboard_t* lboard;
void* buffer;
unsigned char md5[16];
char owns_memrefs;
}
rc_runtime_lboard_t;
typedef struct rc_runtime_richpresence_t {
rc_richpresence_t* richpresence;
void* buffer;
struct rc_runtime_richpresence_t* previous;
char owns_memrefs;
}
rc_runtime_richpresence_t;
typedef struct rc_runtime_t {
rc_runtime_trigger_t* triggers;
unsigned trigger_count;
unsigned trigger_capacity;
rc_runtime_lboard_t* lboards;
unsigned lboard_count;
unsigned lboard_capacity;
rc_runtime_richpresence_t* richpresence;
char* richpresence_display_buffer;
char richpresence_update_timer;
rc_memref_value_t* memrefs;
rc_memref_value_t** next_memref;
}
rc_runtime_t;
void rc_runtime_init(rc_runtime_t* runtime);
void rc_runtime_destroy(rc_runtime_t* runtime);
int rc_runtime_activate_achievement(rc_runtime_t* runtime, unsigned id, const char* memaddr, lua_State* L, int funcs_idx);
void rc_runtime_deactivate_achievement(rc_runtime_t* runtime, unsigned id);
rc_trigger_t* rc_runtime_get_achievement(const rc_runtime_t* runtime, unsigned id);
int rc_runtime_activate_lboard(rc_runtime_t* runtime, unsigned id, const char* memaddr, lua_State* L, int funcs_idx);
void rc_runtime_deactivate_lboard(rc_runtime_t* runtime, unsigned id);
rc_lboard_t* rc_runtime_get_lboard(const rc_runtime_t* runtime, unsigned id);
int rc_runtime_activate_richpresence(rc_runtime_t* runtime, const char* script, lua_State* L, int funcs_idx);
const char* rc_runtime_get_richpresence(const rc_runtime_t* runtime);
enum {
RC_RUNTIME_EVENT_ACHIEVEMENT_ACTIVATED, /* from WAITING, PAUSED, or PRIMED to ACTIVE */
RC_RUNTIME_EVENT_ACHIEVEMENT_PAUSED,
RC_RUNTIME_EVENT_ACHIEVEMENT_RESET,
RC_RUNTIME_EVENT_ACHIEVEMENT_TRIGGERED,
RC_RUNTIME_EVENT_ACHIEVEMENT_PRIMED,
RC_RUNTIME_EVENT_LBOARD_STARTED,
RC_RUNTIME_EVENT_LBOARD_CANCELED,
RC_RUNTIME_EVENT_LBOARD_UPDATED,
RC_RUNTIME_EVENT_LBOARD_TRIGGERED
};
typedef struct rc_runtime_event_t {
unsigned id;
int value;
char type;
}
rc_runtime_event_t;
typedef void (*rc_runtime_event_handler_t)(const rc_runtime_event_t* runtime_event);
void rc_runtime_do_frame(rc_runtime_t* runtime, rc_runtime_event_handler_t event_handler, rc_peek_t peek, void* ud, lua_State* L);
void rc_runtime_reset(rc_runtime_t* runtime);
int rc_runtime_progress_size(const rc_runtime_t* runtime, lua_State* L);
int rc_runtime_serialize_progress(void* buffer, const rc_runtime_t* runtime, lua_State* L);
int rc_runtime_deserialize_progress(rc_runtime_t* runtime, const unsigned char* serialized, lua_State* L);
/*****************************************************************************\
| Memory mapping |
\*****************************************************************************/
enum {
RC_MEMORY_TYPE_SYSTEM_RAM, /* normal system memory */
RC_MEMORY_TYPE_SAVE_RAM, /* memory that persists between sessions */
RC_MEMORY_TYPE_VIDEO_RAM, /* memory reserved for graphical processing */
RC_MEMORY_TYPE_READONLY, /* memory that maps to read only data */
RC_MEMORY_TYPE_HARDWARE_CONTROLLER, /* memory for interacting with system components */
RC_MEMORY_TYPE_VIRTUAL_RAM, /* secondary address space that maps to real memory in system RAM */
RC_MEMORY_TYPE_UNUSED /* these addresses don't really exist */
};
typedef struct rc_memory_region_t {
unsigned start_address; /* first address of block as queried by RetroAchievements */
unsigned end_address; /* last address of block as queried by RetroAchievements */
unsigned real_address; /* real address for first address of block */
char type; /* RC_MEMORY_TYPE_ for block */
const char* description; /* short description of block */
}
rc_memory_region_t;
typedef struct rc_memory_regions_t {
const rc_memory_region_t* region;
unsigned num_regions;
}
rc_memory_regions_t;
const rc_memory_regions_t* rc_console_memory_regions(int console_id);
#ifdef __cplusplus
}
#endif
#include "rc_runtime.h"
#include "rc_runtime_types.h"
#include "rc_consoles.h"
#endif /* RCHEEVOS_H */
File diff suppressed because it is too large. Load diff
+70
View File
@@ -0,0 +1,70 @@
#ifndef RC_API_COMMON_H
#define RC_API_COMMON_H
#include "rc_api_request.h"
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct rc_api_url_builder_t {
char* write;
char* start;
char* end;
rc_api_buffer_t* buffer;
int result;
}
rc_api_url_builder_t;
void rc_url_builder_init(rc_api_url_builder_t* builder, rc_api_buffer_t* buffer, size_t estimated_size);
void rc_url_builder_append(rc_api_url_builder_t* builder, const char* data, size_t len);
const char* rc_url_builder_finalize(rc_api_url_builder_t* builder);
typedef struct rc_json_field_t {
const char* name;
const char* value_start;
const char* value_end;
unsigned array_size;
}
rc_json_field_t;
int rc_json_parse_response(rc_api_response_t* response, const char* json, rc_json_field_t* fields, size_t field_count);
int rc_json_get_string(const char** out, rc_api_buffer_t* buffer, const rc_json_field_t* field, const char* field_name);
int rc_json_get_num(int* out, const rc_json_field_t* field, const char* field_name);
int rc_json_get_unum(unsigned* out, const rc_json_field_t* field, const char* field_name);
int rc_json_get_bool(int* 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_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);
int rc_json_get_required_unum(unsigned* out, rc_api_response_t* response, const rc_json_field_t* field, const char* field_name);
int rc_json_get_required_bool(int* out, rc_api_response_t* response, const rc_json_field_t* field, const char* field_name);
int rc_json_get_required_object(rc_json_field_t* fields, size_t field_count, rc_api_response_t* response, rc_json_field_t* field, const char* field_name);
int rc_json_get_required_unum_array(unsigned** entries, unsigned* num_entries, rc_api_response_t* response, const rc_json_field_t* field, const char* field_name);
int rc_json_get_required_array(unsigned* num_entries, rc_json_field_t* iterator, rc_api_response_t* response, const rc_json_field_t* field, const char* field_name);
int rc_json_get_array_entry_object(rc_json_field_t* fields, size_t field_count, rc_json_field_t* iterator);
void rc_buf_init(rc_api_buffer_t* buffer);
void rc_buf_destroy(rc_api_buffer_t* buffer);
char* rc_buf_reserve(rc_api_buffer_t* buffer, size_t amount);
void rc_buf_consume(rc_api_buffer_t* buffer, const char* start, char* end);
void* rc_buf_alloc(rc_api_buffer_t* buffer, size_t amount);
void rc_url_builder_append_encoded_str(rc_api_url_builder_t* builder, const char* str);
void rc_url_builder_append_num_param(rc_api_url_builder_t* builder, const char* param, int value);
void rc_url_builder_append_unum_param(rc_api_url_builder_t* builder, const char* param, unsigned value);
void rc_url_builder_append_str_param(rc_api_url_builder_t* builder, const char* param, const char* value);
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);
#ifdef __cplusplus
}
#endif
#endif /* RC_API_COMMON_H */
+506
View File
@@ -0,0 +1,506 @@
#include "rc_api_runtime.h"
#include "rc_api_common.h"
#include "rc_runtime.h"
#include "rc_runtime_types.h"
#include "../rcheevos/rc_compat.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/* --- Resolve Hash --- */
int rc_api_init_resolve_hash_request(rc_api_request_t* request, const rc_api_resolve_hash_request_t* api_params) {
rc_api_url_builder_t builder;
rc_api_url_build_dorequest_url(request);
if (!api_params->game_hash || !*api_params->game_hash)
return RC_INVALID_STATE;
rc_url_builder_init(&builder, &request->buffer, 48);
if (rc_api_url_build_dorequest(&builder, "gameid", api_params->username, api_params->api_token)) {
rc_url_builder_append_str_param(&builder, "m", api_params->game_hash);
request->post_data = rc_url_builder_finalize(&builder);
}
return builder.result;
}
int rc_api_process_resolve_hash_response(rc_api_resolve_hash_response_t* response, const char* server_response) {
int result;
rc_json_field_t fields[] = {
{"Success"},
{"Error"},
{"GameID"},
};
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)
return result;
rc_json_get_required_unum(&response->game_id, &response->response, &fields[2], "GameID");
return RC_OK;
}
void rc_api_destroy_resolve_hash_response(rc_api_resolve_hash_response_t* response) {
rc_buf_destroy(&response->response.buffer);
}
/* --- Fetch Game Data --- */
int rc_api_init_fetch_game_data_request(rc_api_request_t* request, const rc_api_fetch_game_data_request_t* api_params) {
rc_api_url_builder_t builder;
rc_api_url_build_dorequest_url(request);
if (api_params->game_id == 0)
return RC_INVALID_STATE;
rc_url_builder_init(&builder, &request->buffer, 48);
if (rc_api_url_build_dorequest(&builder, "patch", api_params->username, api_params->api_token)) {
rc_url_builder_append_unum_param(&builder, "g", api_params->game_id);
request->post_data = rc_url_builder_finalize(&builder);
}
return builder.result;
}
int rc_api_process_fetch_game_data_response(rc_api_fetch_game_data_response_t* response, const char* server_response) {
rc_api_achievement_definition_t* achievement;
rc_api_leaderboard_definition_t* leaderboard;
rc_json_field_t iterator;
const char* str;
const char* last_author = "";
size_t last_author_len = 0;
size_t len;
unsigned timet;
int result;
char format[16];
rc_json_field_t fields[] = {
{"Success"},
{"Error"},
{"PatchData"} /* nested object */
};
rc_json_field_t patchdata_fields[] = {
{"ID"},
{"Title"},
{"ConsoleID"},
{"ImageIcon"},
{"RichPresencePatch"},
{"Achievements"}, /* array */
{"Leaderboards"} /* array */
/* unused fields
{"ForumTopicID"},
{"Flags"},
* unused fields */
};
rc_json_field_t achievement_fields[] = {
{"ID"},
{"Title"},
{"Description"},
{"Flags"},
{"Points"},
{"MemAddr"},
{"Author"},
{"BadgeName"},
{"Created"},
{"Modified"}
};
rc_json_field_t leaderboard_fields[] = {
{"ID"},
{"Title"},
{"Description"},
{"Mem"},
{"Format"}
};
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)
return result;
if (!rc_json_get_required_object(patchdata_fields, sizeof(patchdata_fields) / sizeof(patchdata_fields[0]), &response->response, &fields[2], "Response"))
return RC_MISSING_VALUE;
if (!rc_json_get_required_unum(&response->id, &response->response, &patchdata_fields[0], "ID"))
return RC_MISSING_VALUE;
if (!rc_json_get_required_string(&response->title, &response->response, &patchdata_fields[1], "Title"))
return RC_MISSING_VALUE;
if (!rc_json_get_required_unum(&response->console_id, &response->response, &patchdata_fields[2], "ConsoleID"))
return RC_MISSING_VALUE;
/* ImageIcon will be '/Images/0123456.png' - only return the '0123456' */
if (patchdata_fields[3].value_end) {
str = patchdata_fields[3].value_end - 5;
if (memcmp(str, ".png\"", 5) == 0) {
patchdata_fields[3].value_end -= 5;
while (str > patchdata_fields[3].value_start && str[-1] != '/')
--str;
patchdata_fields[3].value_start = str;
}
}
rc_json_get_optional_string(&response->image_name, &response->response, &patchdata_fields[3], "ImageIcon", "");
/* estimate the amount of space necessary to store the rich presence script, achievements, and leaderboards.
determine how much space each takes as a string in the JSON, then subtract out the non-data (field names, punctuation)
and add space for the structures. */
len = patchdata_fields[4].value_end - patchdata_fields[4].value_start; /* rich presence */
len += (patchdata_fields[5].value_end - patchdata_fields[5].value_start) - /* achievements */
patchdata_fields[5].array_size * (130 - sizeof(rc_api_achievement_definition_t));
len += (patchdata_fields[6].value_end - patchdata_fields[6].value_start) - /* leaderboards */
patchdata_fields[6].array_size * (60 - sizeof(rc_api_leaderboard_definition_t));
rc_buf_reserve(&response->response.buffer, len);
/* end estimation */
rc_json_get_optional_string(&response->rich_presence_script, &response->response, &patchdata_fields[4], "RichPresencePatch", "");
if (!response->rich_presence_script)
response->rich_presence_script = "";
if (!rc_json_get_required_array(&response->num_achievements, &iterator, &response->response, &patchdata_fields[5], "Achievements"))
return RC_MISSING_VALUE;
if (response->num_achievements) {
response->achievements = (rc_api_achievement_definition_t*)rc_buf_alloc(&response->response.buffer, response->num_achievements * sizeof(rc_api_achievement_definition_t));
if (!response->achievements)
return RC_OUT_OF_MEMORY;
achievement = response->achievements;
while (rc_json_get_array_entry_object(achievement_fields, sizeof(achievement_fields) / sizeof(achievement_fields[0]), &iterator)) {
if (!rc_json_get_required_unum(&achievement->id, &response->response, &achievement_fields[0], "ID"))
return RC_MISSING_VALUE;
if (!rc_json_get_required_string(&achievement->title, &response->response, &achievement_fields[1], "Title"))
return RC_MISSING_VALUE;
if (!rc_json_get_required_string(&achievement->description, &response->response, &achievement_fields[2], "Description"))
return RC_MISSING_VALUE;
if (!rc_json_get_required_unum(&achievement->category, &response->response, &achievement_fields[3], "Flags"))
return RC_MISSING_VALUE;
if (!rc_json_get_required_unum(&achievement->points, &response->response, &achievement_fields[4], "Points"))
return RC_MISSING_VALUE;
if (!rc_json_get_required_string(&achievement->definition, &response->response, &achievement_fields[5], "MemAddr"))
return RC_MISSING_VALUE;
if (!rc_json_get_required_string(&achievement->badge_name, &response->response, &achievement_fields[7], "BadgeName"))
return RC_MISSING_VALUE;
len = achievement_fields[7].value_end - achievement_fields[7].value_start;
if (len == last_author_len && memcmp(achievement_fields[7].value_start, last_author, len) == 0) {
achievement->author = last_author;
}
else {
if (!rc_json_get_required_string(&achievement->author, &response->response, &achievement_fields[6], "Author"))
return RC_MISSING_VALUE;
last_author = achievement->author;
last_author_len = len;
}
if (!rc_json_get_required_unum(&timet, &response->response, &achievement_fields[8], "Created"))
return RC_MISSING_VALUE;
achievement->created = (time_t)timet;
if (!rc_json_get_required_unum(&timet, &response->response, &achievement_fields[9], "Modified"))
return RC_MISSING_VALUE;
achievement->updated = (time_t)timet;
++achievement;
}
}
if (!rc_json_get_required_array(&response->num_leaderboards, &iterator, &response->response, &patchdata_fields[6], "Leaderboards"))
return RC_MISSING_VALUE;
if (response->num_leaderboards) {
response->leaderboards = (rc_api_leaderboard_definition_t*)rc_buf_alloc(&response->response.buffer, response->num_leaderboards * sizeof(rc_api_leaderboard_definition_t));
if (!response->leaderboards)
return RC_OUT_OF_MEMORY;
leaderboard = response->leaderboards;
while (rc_json_get_array_entry_object(leaderboard_fields, sizeof(leaderboard_fields) / sizeof(leaderboard_fields[0]), &iterator)) {
if (!rc_json_get_required_unum(&leaderboard->id, &response->response, &leaderboard_fields[0], "ID"))
return RC_MISSING_VALUE;
if (!rc_json_get_required_string(&leaderboard->title, &response->response, &leaderboard_fields[1], "Title"))
return RC_MISSING_VALUE;
if (!rc_json_get_required_string(&leaderboard->description, &response->response, &leaderboard_fields[2], "Description"))
return RC_MISSING_VALUE;
if (!rc_json_get_required_string(&leaderboard->definition, &response->response, &leaderboard_fields[3], "Mem"))
return RC_MISSING_VALUE;
if (!leaderboard_fields[4].value_end)
return RC_MISSING_VALUE;
len = leaderboard_fields[4].value_end - leaderboard_fields[4].value_start - 2;
if (len < sizeof(format) - 1) {
memcpy(format, leaderboard_fields[4].value_start + 1, len);
format[len] = '\0';
leaderboard->format = rc_parse_format(format);
}
else {
leaderboard->format = RC_FORMAT_VALUE;
}
++leaderboard;
}
}
return RC_OK;
}
void rc_api_destroy_fetch_game_data_response(rc_api_fetch_game_data_response_t* response) {
rc_buf_destroy(&response->response.buffer);
}
/* --- Ping --- */
int rc_api_init_ping_request(rc_api_request_t* request, const rc_api_ping_request_t* api_params) {
rc_api_url_builder_t builder;
rc_api_url_build_dorequest_url(request);
if (api_params->game_id == 0)
return RC_INVALID_STATE;
rc_url_builder_init(&builder, &request->buffer, 48);
if (rc_api_url_build_dorequest(&builder, "ping", api_params->username, api_params->api_token)) {
rc_url_builder_append_unum_param(&builder, "g", api_params->game_id);
if (api_params->rich_presence && *api_params->rich_presence)
rc_url_builder_append_str_param(&builder, "m", api_params->rich_presence);
request->post_data = rc_url_builder_finalize(&builder);
}
return builder.result;
}
int rc_api_process_ping_response(rc_api_ping_response_t* response, const char* server_response) {
rc_json_field_t fields[] = {
{"Success"},
{"Error"}
};
memset(response, 0, sizeof(*response));
rc_buf_init(&response->response.buffer);
return rc_json_parse_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
}
void rc_api_destroy_ping_response(rc_api_ping_response_t* response) {
rc_buf_destroy(&response->response.buffer);
}
/* --- Award Achievement --- */
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];
rc_api_url_build_dorequest_url(request);
if (api_params->achievement_id == 0)
return RC_INVALID_STATE;
rc_url_builder_init(&builder, &request->buffer, 96);
if (rc_api_url_build_dorequest(&builder, "awardachievement", api_params->username, api_params->api_token)) {
rc_url_builder_append_unum_param(&builder, "a", api_params->achievement_id);
rc_url_builder_append_unum_param(&builder, "h", api_params->hardcore ? 1 : 0);
if (api_params->game_hash && *api_params->game_hash)
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);
request->post_data = rc_url_builder_finalize(&builder);
}
return builder.result;
}
int rc_api_process_award_achievement_response(rc_api_award_achievement_response_t* response, const char* server_response) {
int result;
rc_json_field_t fields[] = {
{"Success"},
{"Error"},
{"Score"},
{"AchievementID"}
};
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)
return result;
if (!response->response.succeeded) {
if (response->response.error_message &&
memcmp(response->response.error_message, "User already has", 16) == 0) {
/* not really an error, the achievement is unlocked, just not by the current call.
* hardcore: User already has hardcore and regular achievements awarded.
* non-hardcore: User already has this achievement awarded.
*/
response->response.succeeded = 1;
} else {
return result;
}
}
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);
return RC_OK;
}
void rc_api_destroy_award_achievement_response(rc_api_award_achievement_response_t* response) {
rc_buf_destroy(&response->response.buffer);
}
/* --- Submit Leaderboard Entry --- */
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];
rc_api_url_build_dorequest_url(request);
if (api_params->leaderboard_id == 0)
return RC_INVALID_STATE;
rc_url_builder_init(&builder, &request->buffer, 96);
if (rc_api_url_build_dorequest(&builder, "submitlbentry", api_params->username, api_params->api_token)) {
rc_url_builder_append_unum_param(&builder, "i", api_params->leaderboard_id);
rc_url_builder_append_num_param(&builder, "s", api_params->score);
if (api_params->game_hash && *api_params->game_hash)
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);
request->post_data = rc_url_builder_finalize(&builder);
}
return builder.result;
}
int rc_api_process_submit_lboard_entry_response(rc_api_submit_lboard_entry_response_t* response, const char* server_response) {
rc_api_lboard_entry_t* entry;
rc_json_field_t iterator;
const char* str;
int result;
rc_json_field_t fields[] = {
{"Success"},
{"Error"},
{"Response"} /* nested object */
};
rc_json_field_t response_fields[] = {
{"Score"},
{"BestScore"},
{"RankInfo"}, /* nested object */
{"TopEntries"} /* array */
/* unused fields
{"LBData"}, / * array * /
{"ScoreFormatted"},
{"TopEntriesFriends"}, / * array * /
* unused fields */
};
/* unused fields
rc_json_field_t lbdata_fields[] = {
{"Format"},
{"LeaderboardID"},
{"GameID"},
{"Title"},
{"LowerIsBetter"}
};
* unused fields */
rc_json_field_t entry_fields[] = {
{"User"},
{"Rank"},
{"Score"}
/* unused fields
{ "DateSumitted" },
* unused fields */
};
rc_json_field_t rank_info_fields[] = {
{"Rank"},
{"NumEntries"}
/* unused fields
{"LowerIsBetter"},
{"UserRank"},
* unused fields */
};
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)
return result;
if (!rc_json_get_required_object(response_fields, sizeof(response_fields) / sizeof(response_fields[0]), &response->response, &fields[2], "Response"))
return RC_MISSING_VALUE;
if (!rc_json_get_required_num(&response->submitted_score, &response->response, &response_fields[0], "Score"))
return RC_MISSING_VALUE;
if (!rc_json_get_required_num(&response->best_score, &response->response, &response_fields[1], "BestScore"))
return RC_MISSING_VALUE;
if (!rc_json_get_required_object(rank_info_fields, sizeof(rank_info_fields) / sizeof(rank_info_fields[0]), &response->response, &response_fields[2], "RankInfo"))
return RC_MISSING_VALUE;
if (!rc_json_get_required_unum(&response->new_rank, &response->response, &rank_info_fields[0], "Rank"))
return RC_MISSING_VALUE;
if (!rc_json_get_required_string(&str, &response->response, &rank_info_fields[1], "NumEntries"))
return RC_MISSING_VALUE;
response->num_entries = (unsigned)atoi(str);
if (!rc_json_get_required_array(&response->num_top_entries, &iterator, &response->response, &response_fields[3], "TopEntries"))
return RC_MISSING_VALUE;
if (response->num_top_entries) {
response->top_entries = (rc_api_lboard_entry_t*)rc_buf_alloc(&response->response.buffer, response->num_top_entries * sizeof(rc_api_lboard_entry_t));
if (!response->top_entries)
return RC_OUT_OF_MEMORY;
entry = response->top_entries;
while (rc_json_get_array_entry_object(entry_fields, sizeof(entry_fields) / sizeof(entry_fields[0]), &iterator)) {
if (!rc_json_get_required_string(&entry->username, &response->response, &entry_fields[0], "User"))
return RC_MISSING_VALUE;
if (!rc_json_get_required_unum(&entry->rank, &response->response, &entry_fields[1], "Rank"))
return RC_MISSING_VALUE;
if (!rc_json_get_required_num(&entry->score, &response->response, &entry_fields[2], "Score"))
return RC_MISSING_VALUE;
++entry;
}
}
return RC_OK;
}
void rc_api_destroy_submit_lboard_entry_response(rc_api_submit_lboard_entry_response_t* response) {
rc_buf_destroy(&response->response.buffer);
}
+150
View File
@@ -0,0 +1,150 @@
#include "rc_api_user.h"
#include "rc_api_common.h"
#include <string.h>
/* --- Login --- */
int rc_api_init_login_request(rc_api_request_t* request, const rc_api_login_request_t* api_params) {
rc_api_url_builder_t builder;
rc_api_url_build_dorequest_url(request);
if (!api_params->username || !*api_params->username)
return RC_INVALID_STATE;
rc_url_builder_init(&builder, &request->buffer, 48);
rc_url_builder_append_str_param(&builder, "r", "login");
rc_url_builder_append_str_param(&builder, "u", api_params->username);
if (api_params->password && api_params->password[0])
rc_url_builder_append_str_param(&builder, "p", api_params->password);
else if (api_params->api_token && api_params->api_token[0])
rc_url_builder_append_str_param(&builder, "t", api_params->api_token);
else
return RC_INVALID_STATE;
request->post_data = rc_url_builder_finalize(&builder);
return builder.result;
}
int rc_api_process_login_response(rc_api_login_response_t* response, const char* server_response) {
int result;
rc_json_field_t fields[] = {
{"Success"},
{"Error"},
{"User"},
{"Token"},
{"Score"},
{"Messages"}
};
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 || !response->response.succeeded)
return result;
if (!rc_json_get_required_string(&response->username, &response->response, &fields[2], "User"))
return RC_MISSING_VALUE;
if (!rc_json_get_required_string(&response->api_token, &response->response, &fields[3], "Token"))
return RC_MISSING_VALUE;
rc_json_get_optional_unum(&response->score, &fields[4], "Score", 0);
rc_json_get_optional_unum(&response->num_unread_messages, &fields[5], "Messages", 0);
return RC_OK;
}
void rc_api_destroy_login_response(rc_api_login_response_t* response) {
rc_buf_destroy(&response->response.buffer);
}
/* --- Start Session --- */
int rc_api_init_start_session_request(rc_api_request_t* request, const rc_api_start_session_request_t* api_params) {
rc_api_url_builder_t builder;
rc_api_url_build_dorequest_url(request);
if (api_params->game_id == 0)
return RC_INVALID_STATE;
rc_url_builder_init(&builder, &request->buffer, 48);
if (rc_api_url_build_dorequest(&builder, "postactivity", api_params->username, api_params->api_token)) {
/* activity type enum (only 3 is used )
* 1 = earned achievement - handled by awardachievement
* 2 = logged in - handled by login
* 3 = started playing
* 4 = uploaded achievement - handled by uploadachievement
* 5 = modified achievement - handled by uploadachievement
*/
rc_url_builder_append_unum_param(&builder, "a", 3);
rc_url_builder_append_unum_param(&builder, "m", api_params->game_id);
request->post_data = rc_url_builder_finalize(&builder);
}
return builder.result;
}
int rc_api_process_start_session_response(rc_api_start_session_response_t* response, const char* server_response) {
rc_json_field_t fields[] = {
{"Success"},
{"Error"}
};
memset(response, 0, sizeof(*response));
rc_buf_init(&response->response.buffer);
return rc_json_parse_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
}
void rc_api_destroy_start_session_response(rc_api_start_session_response_t* response) {
rc_buf_destroy(&response->response.buffer);
}
/* --- Fetch User Unlocks --- */
int rc_api_init_fetch_user_unlocks_request(rc_api_request_t* request, const rc_api_fetch_user_unlocks_request_t* api_params) {
rc_api_url_builder_t builder;
rc_api_url_build_dorequest_url(request);
rc_url_builder_init(&builder, &request->buffer, 48);
if (rc_api_url_build_dorequest(&builder, "unlocks", api_params->username, api_params->api_token)) {
rc_url_builder_append_unum_param(&builder, "g", api_params->game_id);
rc_url_builder_append_unum_param(&builder, "h", api_params->hardcore ? 1 : 0);
request->post_data = rc_url_builder_finalize(&builder);
}
return builder.result;
}
int rc_api_process_fetch_user_unlocks_response(rc_api_fetch_user_unlocks_response_t* response, const char* server_response) {
int result;
rc_json_field_t fields[] = {
{"Success"},
{"Error"},
{"UserUnlocks"}
/* unused fields
{ "GameID" },
{ "HardcoreMode" }
* unused fields */
};
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)
return result;
result = rc_json_get_required_unum_array(&response->achievement_ids, &response->num_achievement_ids, &response->response, &fields[2], "UserUnlocks");
return result;
}
void rc_api_destroy_fetch_user_unlocks_response(rc_api_fetch_user_unlocks_response_t* response) {
rc_buf_destroy(&response->response.buffer);
}
+120 -14
View File
@@ -1,21 +1,88 @@
#include "internal.h"
#include "rc_internal.h"
#include <stdlib.h>
#include <string.h>
void* rc_alloc(void* pointer, int* offset, int size, int alignment, rc_scratch_t* scratch) {
void* rc_alloc_scratch(void* pointer, int* offset, int size, int alignment, rc_scratch_t* scratch, int scratch_object_pointer_offset)
{
rc_scratch_buffer_t* buffer;
/* if we have a real buffer, then allocate the data there */
if (pointer)
return rc_alloc(pointer, offset, size, alignment, NULL, scratch_object_pointer_offset);
/* update how much space will be required in the real buffer */
{
const int aligned_offset = (*offset + alignment - 1) & ~(alignment - 1);
*offset += (aligned_offset - *offset);
*offset += size;
}
/* find a scratch buffer to hold the temporary data */
buffer = &scratch->buffer;
do {
const int aligned_buffer_offset = (buffer->offset + alignment - 1) & ~(alignment - 1);
const int remaining = sizeof(buffer->buffer) - aligned_buffer_offset;
if (remaining >= size) {
/* claim the required space from an existing buffer */
return rc_alloc(buffer->buffer, &buffer->offset, size, alignment, NULL, -1);
}
if (!buffer->next)
break;
buffer = buffer->next;
} while (1);
/* not enough space in any existing buffer, allocate more */
if (size > (int)sizeof(buffer->buffer)) {
/* caller is asking for more than we can fit in a standard rc_scratch_buffer_t.
* leverage the fact that the buffer is the last field and extend its size.
* this chunk will be exactly large enough to hold the needed data, and since offset
* will exceed sizeof(buffer->buffer), it will never be eligible to hold anything else.
*/
const int needed = sizeof(rc_scratch_buffer_t) - sizeof(buffer->buffer) + size;
buffer->next = (rc_scratch_buffer_t*)malloc(needed);
}
else {
buffer->next = (rc_scratch_buffer_t*)malloc(sizeof(rc_scratch_buffer_t));
}
if (!buffer->next) {
*offset = RC_OUT_OF_MEMORY;
return NULL;
}
buffer = buffer->next;
buffer->offset = 0;
buffer->next = NULL;
/* claim the required space from the new buffer */
return rc_alloc(buffer->buffer, &buffer->offset, size, alignment, NULL, -1);
}
void* rc_alloc(void* pointer, int* offset, int size, int alignment, rc_scratch_t* scratch, int scratch_object_pointer_offset) {
void* ptr;
*offset = (*offset + alignment - 1) & ~(alignment - 1);
if (pointer != 0) {
/* valid buffer, grab the next chunk */
ptr = (void*)((char*)pointer + *offset);
}
else if (scratch != 0) {
ptr = &scratch->obj;
else if (scratch != 0 && scratch_object_pointer_offset >= 0) {
/* only allocate one instance of each object type (indentified by scratch_object_pointer_offset) */
void** scratch_object_pointer = (void**)((char*)&scratch->objs + scratch_object_pointer_offset);
ptr = *scratch_object_pointer;
if (!ptr) {
int used;
ptr = *scratch_object_pointer = rc_alloc_scratch(NULL, &used, size, alignment, scratch, -1);
}
}
else {
ptr = 0;
/* nowhere to get memory from, return NULL */
ptr = NULL;
}
*offset += size;
@@ -23,34 +90,72 @@ void* rc_alloc(void* pointer, int* offset, int size, int alignment, rc_scratch_t
}
char* rc_alloc_str(rc_parse_state_t* parse, const char* text, int length) {
int used = 0;
char* ptr;
ptr = (char*)rc_alloc(parse->buffer, &parse->offset, length + 1, RC_ALIGNOF(char), 0);
if (ptr) {
memcpy(ptr, text, length);
ptr[length] = '\0';
rc_scratch_string_t** next = &parse->scratch.strings;
while (*next) {
int diff = strncmp(text, (*next)->value, length);
if (diff == 0) {
diff = (*next)->value[length];
if (diff == 0)
return (*next)->value;
}
if (diff < 0)
next = &(*next)->left;
else
next = &(*next)->right;
}
*next = (rc_scratch_string_t*)rc_alloc_scratch(NULL, &used, sizeof(rc_scratch_string_t), RC_ALIGNOF(rc_scratch_string_t), &parse->scratch, RC_OFFSETOF(parse->scratch.objs, __rc_scratch_string_t));
ptr = (char*)rc_alloc_scratch(parse->buffer, &parse->offset, length + 1, RC_ALIGNOF(char), &parse->scratch, -1);
if (!ptr || !*next) {
if (parse->offset >= 0)
parse->offset = RC_OUT_OF_MEMORY;
return NULL;
}
memcpy(ptr, text, length);
ptr[length] = '\0';
(*next)->left = NULL;
(*next)->right = NULL;
(*next)->value = ptr;
return ptr;
}
void rc_init_parse_state(rc_parse_state_t* parse, void* buffer, lua_State* L, int funcs_ndx)
{
/* could use memset here, but rc_parse_state_t contains a 512 byte buffer that doesn't need to be initialized */
parse->offset = 0;
parse->L = L;
parse->funcs_ndx = funcs_ndx;
parse->buffer = buffer;
parse->scratch.memref = parse->scratch.memref_buffer;
parse->scratch.memref_size = sizeof(parse->scratch.memref_buffer) / sizeof(parse->scratch.memref_buffer[0]);
parse->scratch.memref_count = 0;
parse->scratch.buffer.offset = 0;
parse->scratch.buffer.next = NULL;
parse->scratch.strings = NULL;
memset(&parse->scratch.objs, 0, sizeof(parse->scratch.objs));
parse->first_memref = 0;
parse->variables = 0;
parse->measured_target = 0;
parse->lines_read = 0;
parse->has_required_hits = 0;
}
void rc_destroy_parse_state(rc_parse_state_t* parse)
{
if (parse->scratch.memref != parse->scratch.memref_buffer)
free(parse->scratch.memref);
rc_scratch_buffer_t* buffer = parse->scratch.buffer.next;
rc_scratch_buffer_t* next;
while (buffer) {
next = buffer->next;
free(buffer);
buffer = next;
}
}
const char* rc_error_str(int ret)
@@ -82,6 +187,7 @@ const char* rc_error_str(int ret)
case RC_INVALID_MEASURED_TARGET: return "Invalid measured target";
case RC_INVALID_COMPARISON: return "Invalid comparison";
case RC_INVALID_STATE: return "Invalid state";
case RC_INVALID_JSON: return "Invalid JSON";
default: return "Unknown error";
}
+1 -1
View File
@@ -1,4 +1,4 @@
#include "compat.h"
#include "rc_compat.h"
#include <ctype.h>
#include <stdarg.h>
+95 -61
View File
@@ -1,7 +1,65 @@
#include "internal.h"
#include "rc_internal.h"
#include <stdlib.h>
char rc_parse_operator(const char** memaddr) {
const char* oper = *memaddr;
switch (*oper) {
case '=':
++(*memaddr);
(*memaddr) += (**memaddr == '=');
return RC_OPERATOR_EQ;
case '!':
if (oper[1] == '=') {
(*memaddr) += 2;
return RC_OPERATOR_NE;
}
/* fall through */
default:
return RC_INVALID_OPERATOR;
case '<':
if (oper[1] == '=') {
(*memaddr) += 2;
return RC_OPERATOR_LE;
}
++(*memaddr);
return RC_OPERATOR_LT;
case '>':
if (oper[1] == '=') {
(*memaddr) += 2;
return RC_OPERATOR_GE;
}
++(*memaddr);
return RC_OPERATOR_GT;
case '*':
++(*memaddr);
return RC_OPERATOR_MULT;
case '/':
++(*memaddr);
return RC_OPERATOR_DIV;
case '&':
++(*memaddr);
return RC_OPERATOR_AND;
case '\0':/* end of string */
case '_': /* next condition */
case 'S': /* next condset */
case ')': /* end of macro */
case '$': /* maximum of values */
/* valid condition separator, condition may not have an operator */
return RC_OPERATOR_NONE;
}
}
rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse, int is_indirect) {
rc_condition_t* self;
const char* aux;
@@ -11,6 +69,7 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse
aux = *memaddr;
self = RC_ALLOC(rc_condition_t, parse);
self->current_hits = 0;
self->is_true = 0;
if (*aux != 0 && aux[1] == ':') {
switch (*aux) {
@@ -19,12 +78,14 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse
case 'a': case 'A': self->type = RC_CONDITION_ADD_SOURCE; can_modify = 1; break;
case 'b': case 'B': self->type = RC_CONDITION_SUB_SOURCE; can_modify = 1; break;
case 'c': case 'C': self->type = RC_CONDITION_ADD_HITS; break;
case 'd': case 'D': self->type = RC_CONDITION_SUB_HITS; break;
case 'n': case 'N': self->type = RC_CONDITION_AND_NEXT; break;
case 'o': case 'O': self->type = RC_CONDITION_OR_NEXT; break;
case 'm': case 'M': self->type = RC_CONDITION_MEASURED; break;
case 'q': case 'Q': self->type = RC_CONDITION_MEASURED_IF; break;
case 'i': case 'I': self->type = RC_CONDITION_ADD_ADDRESS; can_modify = 1; break;
case 't': case 'T': self->type = RC_CONDITION_TRIGGER; break;
case 'z': case 'Z': self->type = RC_CONDITION_RESET_NEXT_IF; break;
default: parse->offset = RC_INVALID_CONDITION_TYPE; return 0;
}
@@ -46,76 +107,37 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse
return 0;
}
switch (*aux++) {
case '=':
self->oper = RC_OPERATOR_EQ;
aux += *aux == '=';
break;
case '!':
if (*aux++ != '=') {
/* fall through */
default:
parse->offset = RC_INVALID_OPERATOR;
return 0;
self->oper = rc_parse_operator(&aux);
switch (self->oper) {
case RC_OPERATOR_NONE:
/* non-modifying statements must have a second operand */
if (!can_modify) {
/* measured does not require a second operand when used in a value */
if (self->type != RC_CONDITION_MEASURED) {
parse->offset = RC_INVALID_OPERATOR;
return 0;
}
}
self->oper = RC_OPERATOR_NE;
break;
case '<':
self->oper = RC_OPERATOR_LT;
if (*aux == '=') {
self->oper = RC_OPERATOR_LE;
aux++;
}
break;
case '>':
self->oper = RC_OPERATOR_GT;
if (*aux == '=') {
self->oper = RC_OPERATOR_GE;
aux++;
}
break;
case '*':
self->oper = RC_OPERATOR_MULT;
break;
case '/':
self->oper = RC_OPERATOR_DIV;
break;
case '&':
self->oper = RC_OPERATOR_AND;
break;
case '_':
case ')':
case '\0':
self->oper = RC_OPERATOR_NONE;
/* provide dummy operand of '1' and no required hits */
self->operand2.type = RC_OPERAND_CONST;
self->operand2.value.num = 1;
self->required_hits = 0;
*memaddr = aux - 1;
*memaddr = aux;
return self;
}
switch (self->oper) {
case RC_OPERATOR_MULT:
case RC_OPERATOR_DIV:
case RC_OPERATOR_AND:
/* modifying operators are only valid on modifying statements */
if (!can_modify) {
parse->offset = RC_INVALID_OPERATOR;
return 0;
}
break;
if (can_modify)
break;
/* fallthrough */
case RC_INVALID_OPERATOR:
parse->offset = RC_INVALID_OPERATOR;
return 0;
default:
/* comparison operators are not valid on modifying statements */
@@ -145,7 +167,7 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse
if (self->oper == RC_OPERATOR_NONE) {
/* if operator is none, explicitly clear out the right side */
self->operand2.type = RC_INVALID_CONST_OPERAND;
self->operand2.type = RC_OPERAND_CONST;
self->operand2.value.num = 0;
}
@@ -163,6 +185,12 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse
return 0;
}
/* if operator is none, explicitly clear out the required hits */
if (self->oper == RC_OPERATOR_NONE)
self->required_hits = 0;
else
parse->has_required_hits = 1;
aux = end + 1;
}
else if (*aux == '.') {
@@ -174,6 +202,12 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse
return 0;
}
/* if operator is none, explicitly clear out the required hits */
if (self->oper == RC_OPERATOR_NONE)
self->required_hits = 0;
else
parse->has_required_hits = 1;
aux = end + 1;
}
else {
+109 -22
View File
@@ -1,4 +1,4 @@
#include "internal.h"
#include "rc_internal.h"
static void rc_update_condition_pause(rc_condition_t* condition, int* in_pause) {
if (condition->next != 0) {
@@ -13,9 +13,11 @@ static void rc_update_condition_pause(rc_condition_t* condition, int* in_pause)
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 = *in_pause;
break;
@@ -25,14 +27,15 @@ static void rc_update_condition_pause(rc_condition_t* condition, int* in_pause)
}
}
rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse) {
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;
self = RC_ALLOC(rc_condset_t, parse);
self->has_pause = self->is_paused = 0;
self->has_pause = self->is_paused = self->has_indirect_memrefs = 0;
next = &self->conditions;
if (**memaddr == 'S' || **memaddr == 's' || !**memaddr) {
@@ -52,12 +55,16 @@ rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse) {
if ((*next)->oper == RC_OPERATOR_NONE) {
switch ((*next)->type) {
case RC_CONDITION_ADD_ADDRESS:
case RC_CONDITION_ADD_HITS:
case RC_CONDITION_ADD_SOURCE:
case RC_CONDITION_SUB_SOURCE:
case RC_CONDITION_AND_NEXT:
case RC_CONDITION_OR_NEXT:
/* these conditions don't require a right hand size (implied *1) */
break;
case RC_CONDITION_MEASURED:
/* right hand side is not required when Measured is used in a value */
if (is_value)
break;
/* fallthrough to default */
default:
parse->offset = RC_INVALID_OPERATOR;
@@ -67,27 +74,51 @@ rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse) {
self->has_pause |= (*next)->type == RC_CONDITION_PAUSE_IF;
in_add_address = (*next)->type == RC_CONDITION_ADD_ADDRESS;
self->has_indirect_memrefs |= in_add_address;
if ((*next)->type == RC_CONDITION_MEASURED) {
unsigned measured_target = 0;
if ((*next)->required_hits == 0) {
if ((*next)->operand2.type != RC_OPERAND_CONST) {
parse->offset = RC_INVALID_MEASURED_TARGET;
return 0;
}
switch ((*next)->type) {
case RC_CONDITION_MEASURED:
if (measured_target != 0) {
/* multiple Measured flags cannot exist in the same group */
parse->offset = RC_MULTIPLE_MEASURED;
return 0;
}
else if (is_value) {
measured_target = (unsigned)-1;
if ((*next)->oper != RC_OPERATOR_NONE)
(*next)->required_hits = measured_target;
}
else if ((*next)->required_hits != 0) {
measured_target = (*next)->required_hits;
}
else if ((*next)->operand2.type == RC_OPERAND_CONST) {
measured_target = (*next)->operand2.value.num;
}
else {
measured_target = (*next)->required_hits;
parse->offset = RC_INVALID_MEASURED_TARGET;
return 0;
}
if (parse->measured_target && measured_target != parse->measured_target) {
/* multiple Measured flags in separate groups must have the same target */
parse->offset = RC_MULTIPLE_MEASURED;
return 0;
}
parse->measured_target = measured_target;
break;
case RC_CONDITION_STANDARD:
case RC_CONDITION_TRIGGER:
/* these flags are not allowed in value expressions */
if (is_value) {
parse->offset = RC_INVALID_VALUE_FLAG;
return 0;
}
break;
default:
break;
}
next = &(*next)->next;
@@ -101,7 +132,6 @@ rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse) {
*next = 0;
if (parse->buffer != 0) {
in_pause = 0;
rc_update_condition_pause(self->conditions, &in_pause);
@@ -110,9 +140,33 @@ rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse) {
return self;
}
static void rc_condset_update_indirect_memrefs(rc_condset_t* self, rc_condition_t* condition, int processing_pause, rc_eval_state_t* eval_state) {
for (; condition != 0; condition = condition->next) {
if (condition->pause != processing_pause)
continue;
if (condition->type == RC_CONDITION_ADD_ADDRESS) {
eval_state->add_address = rc_evaluate_condition_value(condition, eval_state);
continue;
}
/* call rc_get_memref_value to update the indirect memrefs. it won't do anything with non-indirect
* memrefs and avoids a second check of is_indirect. also, we ignore the response, so it doesn't
* matter what operand type we pass. assume RC_OPERAND_ADDRESS is the quickest. */
if (rc_operand_is_memref(&condition->operand1))
rc_get_memref_value(condition->operand1.value.memref, RC_OPERAND_ADDRESS, eval_state);
if (rc_operand_is_memref(&condition->operand2))
rc_get_memref_value(condition->operand2.value.memref, RC_OPERAND_ADDRESS, eval_state);
eval_state->add_address = 0;
}
}
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;
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;
@@ -121,12 +175,12 @@ static int rc_test_condset_internal(rc_condset_t* self, int processing_pause, rc
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;
for (condition = self->conditions; condition != 0; condition = condition->next) {
if (condition->pause != processing_pause) {
if (condition->pause != processing_pause)
continue;
}
/* STEP 1: process modifier conditions */
switch (condition->type) {
@@ -167,8 +221,16 @@ static int rc_test_condset_internal(rc_condset_t* self, int processing_pause, rc
and_next = 1;
or_next = 0;
/* true conditions should update hit count */
if (cond_valid) {
if (reset_next) {
/* previous ResetNextIf resets the hit count on this condition and prevents it from being true */
if (condition->current_hits)
eval_state->was_cond_reset = 1;
condition->current_hits = 0;
cond_valid = 0;
}
else if (cond_valid) {
/* true conditions should update hit count */
eval_state->has_hits = 1;
if (condition->required_hits == 0) {
@@ -194,6 +256,16 @@ static int rc_test_condset_internal(rc_condset_t* self, int processing_pause, rc
switch (condition->type) {
case RC_CONDITION_ADD_HITS:
eval_state->add_hits += condition->current_hits;
reset_next = 0; /* ResetNextIf was applied to this AddHits condition; don't apply it to future conditions */
continue;
case RC_CONDITION_SUB_HITS:
eval_state->add_hits -= condition->current_hits;
reset_next = 0; /* ResetNextIf was applied to this AddHits condition; don't apply it to future conditions */
continue;
case RC_CONDITION_RESET_NEXT_IF:
reset_next = cond_valid;
continue;
case RC_CONDITION_AND_NEXT:
@@ -208,13 +280,17 @@ static int rc_test_condset_internal(rc_condset_t* self, int processing_pause, rc
break;
}
/* reset logic flags for next condition */
reset_next = 0;
/* STEP 4: calculate total hits */
total_hits = condition->current_hits;
if (eval_state->add_hits) {
if (condition->required_hits != 0) {
/* if the condition has a target hit count, we have to recalculate cond_valid including the AddHits counter */
total_hits = condition->current_hits + eval_state->add_hits;
const int signed_hits = (int)condition->current_hits + eval_state->add_hits;
total_hits = (signed_hits >= 0) ? (unsigned)signed_hits : 0;
cond_valid = (total_hits >= condition->required_hits);
}
else {
@@ -230,6 +306,17 @@ static int rc_test_condset_internal(rc_condset_t* self, int processing_pause, rc
case RC_CONDITION_PAUSE_IF:
/* as soon as we find a PauseIf that evaluates to true, stop processing the rest of the group */
if (cond_valid) {
/* indirect memrefs are not updated as part of the rc_update_memref_values call.
* an active pause aborts processing of the remaining part of the pause subset and the entire non-pause subset.
* if the set has any indirect memrefs, manually update them now so the deltas are correct */
if (self->has_indirect_memrefs) {
/* first, update any indirect memrefs in the remaining part of the pause subset */
rc_condset_update_indirect_memrefs(self, condition->next, 1, eval_state);
/* then, update all indirect memrefs in the non-pause subset */
rc_condset_update_indirect_memrefs(self, self->conditions, 0, eval_state);
}
return 1;
}
-1
View File
@@ -1,4 +1,3 @@
#include "rcheevos.h"
#include "rc_consoles.h"
#include <ctype.h>
+2 -2
View File
@@ -1,6 +1,6 @@
#include "internal.h"
#include "rc_internal.h"
#include "compat.h"
#include "rc_compat.h"
#include <string.h>
#include <stdio.h>
+15 -6
View File
@@ -1,4 +1,4 @@
#include "internal.h"
#include "rc_internal.h"
enum {
RC_LBOARD_START = 1 << 0,
@@ -139,7 +139,9 @@ void rc_parse_lboard_internal(rc_lboard_t* self, const char* memaddr, rc_parse_s
int rc_lboard_size(const char* memaddr) {
rc_lboard_t* self;
rc_parse_state_t parse;
rc_memref_t* first_memref;
rc_init_parse_state(&parse, 0, 0, 0);
rc_init_parse_state_memrefs(&parse, &first_memref);
self = RC_ALLOC(rc_lboard_t, &parse);
rc_parse_lboard_internal(self, memaddr, &parse);
@@ -156,7 +158,7 @@ rc_lboard_t* rc_parse_lboard(void* buffer, const char* memaddr, lua_State* L, in
return 0;
rc_init_parse_state(&parse, buffer, L, funcs_ndx);
self = RC_ALLOC(rc_lboard_t, &parse);
rc_init_parse_state_memrefs(&parse, &self->memrefs);
@@ -171,7 +173,7 @@ int rc_evaluate_lboard(rc_lboard_t* self, int* value, rc_peek_t peek, void* peek
rc_update_memref_values(self->memrefs, peek, peek_ud);
if (self->state == RC_LBOARD_STATE_INACTIVE)
if (self->state == RC_LBOARD_STATE_INACTIVE || self->state == RC_LBOARD_STATE_DISABLED)
return RC_LBOARD_STATE_INACTIVE;
/* these are always tested once every frame, to ensure hit counts work properly */
@@ -203,14 +205,16 @@ int rc_evaluate_lboard(rc_lboard_t* self, int* value, rc_peek_t peek, void* peek
}
else if (self->start.requirement == 0 && self->start.alternative == 0) {
/* start condition is empty - this leaderboard is submit-only with no measured progress */
}
}
else {
/* start the leaderboard attempt */
self->state = RC_LBOARD_STATE_STARTED;
/* reset any hit counts in the value */
if (self->value.conditions)
rc_reset_condset(self->value.conditions);
if (self->progress)
rc_reset_value(self->progress);
rc_reset_value(&self->value);
}
}
break;
@@ -255,4 +259,9 @@ void rc_reset_lboard(rc_lboard_t* self) {
rc_reset_trigger(&self->start);
rc_reset_trigger(&self->submit);
rc_reset_trigger(&self->cancel);
if (self->progress)
rc_reset_value(self->progress);
rc_reset_value(&self->value);
}
+132 -151
View File
@@ -1,195 +1,163 @@
#include "internal.h"
#include "rc_internal.h"
#include <stdlib.h> /* malloc/realloc */
#include <string.h> /* memcpy */
#define MEMREF_PLACEHOLDER_ADDRESS 0xFFFFFFFF
static rc_memref_value_t* rc_alloc_memref_value_sizing_mode(rc_parse_state_t* parse, unsigned address, char size, char is_indirect) {
rc_memref_t* rc_alloc_memref(rc_parse_state_t* parse, unsigned address, char size, char is_indirect) {
rc_memref_t** next_memref;
rc_memref_t* memref;
int i;
/* indirect address always creates two new entries; don't bother tracking them */
if (is_indirect) {
RC_ALLOC(rc_memref_value_t, parse);
return RC_ALLOC(rc_memref_value_t, parse);
}
memref = NULL;
/* have to track unique address/size/bcd combinations - use scratch.memref for sizing mode */
for (i = 0; i < parse->scratch.memref_count; ++i) {
memref = &parse->scratch.memref[i];
if (memref->address == address && memref->size == size) {
return &parse->scratch.obj.memref_value;
}
}
/* no match found - resize unique tracking buffer if necessary */
if (parse->scratch.memref_count == parse->scratch.memref_size) {
if (parse->scratch.memref == parse->scratch.memref_buffer) {
parse->scratch.memref_size += 16;
memref = (rc_memref_t*)malloc(parse->scratch.memref_size * sizeof(parse->scratch.memref_buffer[0]));
if (memref) {
parse->scratch.memref = memref;
memcpy(memref, parse->scratch.memref_buffer, parse->scratch.memref_count * sizeof(parse->scratch.memref_buffer[0]));
}
else {
parse->offset = RC_OUT_OF_MEMORY;
return 0;
}
}
else {
parse->scratch.memref_size += 32;
memref = (rc_memref_t*)realloc(parse->scratch.memref, parse->scratch.memref_size * sizeof(parse->scratch.memref_buffer[0]));
if (memref) {
parse->scratch.memref = memref;
}
else {
parse->offset = RC_OUT_OF_MEMORY;
return 0;
}
}
}
/* add new unique tracking entry */
if (parse->scratch.memref) {
memref = &parse->scratch.memref[parse->scratch.memref_count++];
memref->address = address;
memref->size = size;
memref->is_indirect = is_indirect;
}
/* allocate memory but don't actually populate, as it might overwrite the self object referencing the rc_memref_value_t */
return RC_ALLOC(rc_memref_value_t, parse);
}
static rc_memref_value_t* rc_alloc_memref_value_constuct_mode(rc_parse_state_t* parse, unsigned address, char size, char is_indirect) {
rc_memref_value_t** next_memref_value;
rc_memref_value_t* memref_value;
rc_memref_value_t* indirect_memref_value;
if (!is_indirect) {
/* attempt to find an existing rc_memref_value_t */
next_memref_value = parse->first_memref;
while (*next_memref_value) {
memref_value = *next_memref_value;
if (!memref_value->memref.is_indirect && memref_value->memref.address == address &&
memref_value->memref.size == size) {
return memref_value;
}
/* attempt to find an existing memref that can be shared */
next_memref = parse->first_memref;
while (*next_memref) {
memref = *next_memref;
if (!memref->value.is_indirect && memref->address == address && memref->value.size == size)
return memref;
next_memref_value = &memref_value->next;
next_memref = &memref->next;
}
/* no match found, create a new entry */
memref = RC_ALLOC_SCRATCH(rc_memref_t, parse);
*next_memref = memref;
}
else {
/* indirect address always creates two new entries - one for the original address, and one for
the indirect dereference - just skip ahead to the end of the list */
next_memref_value = parse->first_memref;
while (*next_memref_value) {
next_memref_value = &(*next_memref_value)->next;
}
/* indirect references always create a new entry because we can't guarantee that the
* indirection amount will be the same between references. because they aren't shared,
* don't bother putting them in the chain.
*/
memref = RC_ALLOC(rc_memref_t, parse);
}
/* no match found, create a new entry */
memref_value = RC_ALLOC(rc_memref_value_t, parse);
memref_value->memref.address = address;
memref_value->memref.size = size;
memref_value->memref.is_indirect = is_indirect;
memref_value->value = 0;
memref_value->previous = 0;
memref_value->prior = 0;
memref_value->next = 0;
memset(memref, 0, sizeof(*memref));
memref->address = address;
memref->value.size = size;
memref->value.is_indirect = is_indirect;
*next_memref_value = memref_value;
return memref;
}
/* also create the indirect deference entry for indirect references */
if (is_indirect) {
indirect_memref_value = RC_ALLOC(rc_memref_value_t, parse);
indirect_memref_value->memref.address = MEMREF_PLACEHOLDER_ADDRESS;
indirect_memref_value->memref.size = size;
indirect_memref_value->memref.is_indirect = 1;
indirect_memref_value->value = 0;
indirect_memref_value->previous = 0;
indirect_memref_value->prior = 0;
indirect_memref_value->next = 0;
int rc_parse_memref(const char** memaddr, char* size, unsigned* address) {
const char* aux = *memaddr;
char* end;
unsigned long value;
memref_value->next = indirect_memref_value;
if (*aux++ != '0')
return RC_INVALID_MEMORY_OPERAND;
if (*aux != 'x' && *aux != 'X')
return RC_INVALID_MEMORY_OPERAND;
aux++;
switch (*aux++) {
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 'h': case 'H': *size = RC_MEMSIZE_8_BITS; break;
case 'w': case 'W': *size = RC_MEMSIZE_24_BITS; break;
case 'x': case 'X': *size = RC_MEMSIZE_32_BITS; break;
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':
aux--;
/* fallthrough */
case ' ':
*size = RC_MEMSIZE_16_BITS;
break;
default:
return RC_INVALID_MEMORY_OPERAND;
}
return memref_value;
value = strtoul(aux, &end, 16);
if (end == aux)
return RC_INVALID_MEMORY_OPERAND;
if (value > 0xffffffffU)
value = 0xffffffffU;
*address = (unsigned)value;
*memaddr = end;
return RC_OK;
}
rc_memref_value_t* rc_alloc_memref_value(rc_parse_state_t* parse, unsigned address, char size, char is_indirect) {
if (!parse->first_memref)
return rc_alloc_memref_value_sizing_mode(parse, address, size, is_indirect);
return rc_alloc_memref_value_constuct_mode(parse, address, size, is_indirect);
}
static unsigned rc_memref_get_value(rc_memref_t* self, rc_peek_t peek, void* ud) {
static unsigned rc_peek_value(unsigned address, char size, rc_peek_t peek, void* ud) {
unsigned value;
if (!peek)
return 0;
switch (self->size)
switch (size)
{
case RC_MEMSIZE_BIT_0:
value = (peek(self->address, 1, ud) >> 0) & 1;
value = (peek(address, 1, ud) >> 0) & 1;
break;
case RC_MEMSIZE_BIT_1:
value = (peek(self->address, 1, ud) >> 1) & 1;
value = (peek(address, 1, ud) >> 1) & 1;
break;
case RC_MEMSIZE_BIT_2:
value = (peek(self->address, 1, ud) >> 2) & 1;
value = (peek(address, 1, ud) >> 2) & 1;
break;
case RC_MEMSIZE_BIT_3:
value = (peek(self->address, 1, ud) >> 3) & 1;
value = (peek(address, 1, ud) >> 3) & 1;
break;
case RC_MEMSIZE_BIT_4:
value = (peek(self->address, 1, ud) >> 4) & 1;
value = (peek(address, 1, ud) >> 4) & 1;
break;
case RC_MEMSIZE_BIT_5:
value = (peek(self->address, 1, ud) >> 5) & 1;
value = (peek(address, 1, ud) >> 5) & 1;
break;
case RC_MEMSIZE_BIT_6:
value = (peek(self->address, 1, ud) >> 6) & 1;
value = (peek(address, 1, ud) >> 6) & 1;
break;
case RC_MEMSIZE_BIT_7:
value = (peek(self->address, 1, ud) >> 7) & 1;
value = (peek(address, 1, ud) >> 7) & 1;
break;
case RC_MEMSIZE_LOW:
value = peek(self->address, 1, ud) & 0x0f;
value = peek(address, 1, ud) & 0x0f;
break;
case RC_MEMSIZE_HIGH:
value = (peek(self->address, 1, ud) >> 4) & 0x0f;
value = (peek(address, 1, ud) >> 4) & 0x0f;
break;
case RC_MEMSIZE_8_BITS:
value = peek(self->address, 1, ud);
value = peek(address, 1, ud);
break;
case RC_MEMSIZE_16_BITS:
value = peek(self->address, 2, ud);
value = peek(address, 2, ud);
break;
case RC_MEMSIZE_24_BITS:
/* peek 4 bytes - don't expect the caller to understand 24-bit numbers */
value = peek(self->address, 4, ud) & 0x00FFFFFF;
value = peek(address, 4, ud) & 0x00FFFFFF;
break;
case RC_MEMSIZE_32_BITS:
value = peek(self->address, 4, ud);
value = peek(address, 4, ud);
break;
default:
@@ -200,45 +168,58 @@ static unsigned rc_memref_get_value(rc_memref_t* self, rc_peek_t peek, void* ud)
return value;
}
void rc_update_memref_value(rc_memref_value_t* memref, rc_peek_t peek, void* ud) {
memref->previous = memref->value;
memref->value = rc_memref_get_value(&memref->memref, peek, ud);
if (memref->value != memref->previous)
memref->prior = memref->previous;
void rc_update_memref_value(rc_memref_value_t* memref, unsigned new_value) {
if (memref->value == new_value) {
memref->changed = 0;
}
else {
memref->prior = memref->value;
memref->value = new_value;
memref->changed = 1;
}
}
void rc_update_memref_values(rc_memref_value_t* memref, rc_peek_t peek, void* ud) {
void rc_update_memref_values(rc_memref_t* memref, rc_peek_t peek, void* ud) {
while (memref) {
if (memref->memref.address != MEMREF_PLACEHOLDER_ADDRESS)
rc_update_memref_value(memref, peek, ud);
/* indirect memory references are not shared and will be updated in rc_get_memref_value */
if (!memref->value.is_indirect)
rc_update_memref_value(&memref->value, rc_peek_value(memref->address, memref->value.size, peek, ud));
memref = memref->next;
}
}
void rc_init_parse_state_memrefs(rc_parse_state_t* parse, rc_memref_value_t** memrefs) {
void rc_init_parse_state_memrefs(rc_parse_state_t* parse, rc_memref_t** memrefs) {
parse->first_memref = memrefs;
*memrefs = 0;
}
rc_memref_value_t* rc_get_indirect_memref(rc_memref_value_t* memref, rc_eval_state_t* eval_state) {
unsigned new_address;
if (eval_state->add_address == 0)
return memref;
if (!memref->memref.is_indirect)
return memref;
new_address = memref->memref.address + eval_state->add_address;
/* an extra rc_memref_value_t is allocated for offset calculations */
memref = memref->next;
/* if the adjusted address has changed, update the record */
if (memref->memref.address != new_address) {
memref->memref.address = new_address;
rc_update_memref_value(memref, eval_state->peek, eval_state->peek_userdata);
unsigned rc_get_memref_value(rc_memref_t* memref, int operand_type, rc_eval_state_t* eval_state) {
/* if this is an indirect reference, handle the indirection. */
if (memref->value.is_indirect) {
const unsigned new_address = memref->address + eval_state->add_address;
rc_update_memref_value(&memref->value, rc_peek_value(new_address, memref->value.size, eval_state->peek, eval_state->peek_userdata));
}
return memref;
return rc_get_memref_value_value(&memref->value, operand_type);
}
unsigned rc_get_memref_value_value(rc_memref_value_t* memref, int operand_type) {
switch (operand_type)
{
/* most common case explicitly first, even though it could be handled by default case.
* this helps the compiler to optimize if it turns the switch into a series of if/elses */
case RC_OPERAND_ADDRESS:
return memref->value;
case RC_OPERAND_DELTA:
if (!memref->changed) {
/* fallthrough */
default:
return memref->value;
}
/* fallthrough */
case RC_OPERAND_PRIOR:
return memref->prior;
}
}
+58 -72
View File
@@ -1,4 +1,4 @@
#include "internal.h"
#include "rc_internal.h"
#include <stdlib.h>
#include <ctype.h>
@@ -29,7 +29,7 @@ static int rc_parse_operand_lua(rc_operand_t* self, const char** memaddr, rc_par
return RC_INVALID_LUA_OPERAND;
}
if (!isalpha((unsigned char)*aux)) {
if (!isalpha(*aux)) {
return RC_INVALID_LUA_OPERAND;
}
@@ -37,7 +37,7 @@ static int rc_parse_operand_lua(rc_operand_t* self, const char** memaddr, rc_par
id = aux;
#endif
while (isalnum((unsigned char)*aux) || *aux == '_') {
while (isalnum(*aux) || *aux == '_') {
aux++;
}
@@ -68,88 +68,66 @@ static int rc_parse_operand_lua(rc_operand_t* self, const char** memaddr, rc_par
static int rc_parse_operand_memory(rc_operand_t* self, const char** memaddr, rc_parse_state_t* parse, int is_indirect) {
const char* aux = *memaddr;
char* end;
unsigned long address;
unsigned address;
char size;
int ret;
switch (*aux++) {
switch (*aux) {
case 'd': case 'D':
self->type = RC_OPERAND_DELTA;
++aux;
break;
case 'p': case 'P':
self->type = RC_OPERAND_PRIOR;
++aux;
break;
case 'b': case 'B':
self->type = RC_OPERAND_BCD;
++aux;
break;
case '~':
self->type = RC_OPERAND_INVERTED;
++aux;
break;
default:
self->type = RC_OPERAND_ADDRESS;
aux--;
break;
}
if (*aux++ != '0') {
return RC_INVALID_MEMORY_OPERAND;
}
ret = rc_parse_memref(&aux, &self->size, &address);
if (ret != RC_OK)
return ret;
if (*aux != 'x' && *aux != 'X') {
return RC_INVALID_MEMORY_OPERAND;
}
aux++;
switch (*aux++) {
case 'm': case 'M': self->size = RC_MEMSIZE_BIT_0; size = RC_MEMSIZE_8_BITS; break;
case 'n': case 'N': self->size = RC_MEMSIZE_BIT_1; size = RC_MEMSIZE_8_BITS; break;
case 'o': case 'O': self->size = RC_MEMSIZE_BIT_2; size = RC_MEMSIZE_8_BITS; break;
case 'p': case 'P': self->size = RC_MEMSIZE_BIT_3; size = RC_MEMSIZE_8_BITS; break;
case 'q': case 'Q': self->size = RC_MEMSIZE_BIT_4; size = RC_MEMSIZE_8_BITS; break;
case 'r': case 'R': self->size = RC_MEMSIZE_BIT_5; size = RC_MEMSIZE_8_BITS; break;
case 's': case 'S': self->size = RC_MEMSIZE_BIT_6; size = RC_MEMSIZE_8_BITS; break;
case 't': case 'T': self->size = RC_MEMSIZE_BIT_7; size = RC_MEMSIZE_8_BITS; break;
case 'l': case 'L': self->size = RC_MEMSIZE_LOW; size = RC_MEMSIZE_8_BITS; break;
case 'u': case 'U': self->size = RC_MEMSIZE_HIGH; size = RC_MEMSIZE_8_BITS; break;
case 'k': case 'K': self->size = RC_MEMSIZE_BITCOUNT; size = RC_MEMSIZE_8_BITS; break;
case 'h': case 'H': self->size = size = RC_MEMSIZE_8_BITS; break;
case 'w': case 'W': self->size = size = RC_MEMSIZE_24_BITS; break;
case 'x': case 'X': self->size = size = RC_MEMSIZE_32_BITS; break;
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':
aux--;
/* fallthrough */
case ' ':
self->size = size = RC_MEMSIZE_16_BITS;
switch (self->size) {
case RC_MEMSIZE_BIT_0:
case RC_MEMSIZE_BIT_1:
case RC_MEMSIZE_BIT_2:
case RC_MEMSIZE_BIT_3:
case RC_MEMSIZE_BIT_4:
case RC_MEMSIZE_BIT_5:
case RC_MEMSIZE_BIT_6:
case RC_MEMSIZE_BIT_7:
case RC_MEMSIZE_LOW:
case RC_MEMSIZE_HIGH:
case RC_MEMSIZE_BITCOUNT:
/* these can all share an 8-bit memref and just mask off the appropriate data in rc_evaluate_operand */
size = RC_MEMSIZE_8_BITS;
break;
default:
return RC_INVALID_MEMORY_OPERAND;
size = self->size;
break;
}
address = strtoul(aux, &end, 16);
if (end == aux) {
return RC_INVALID_MEMORY_OPERAND;
}
if (address > 0xffffffffU) {
address = 0xffffffffU;
}
self->value.memref = rc_alloc_memref_value(parse, address, size, is_indirect);
self->value.memref = rc_alloc_memref(parse, address, size, is_indirect);
if (parse->offset < 0)
return parse->offset;
*memaddr = end;
*memaddr = aux;
return RC_OK;
}
@@ -204,9 +182,10 @@ int rc_parse_operand(rc_operand_t* self, const char** memaddr, int is_trigger, i
break;
case 'v': case 'V': /* signed integer constant */
negative = 0;
++aux;
/* fallthrough */
case '+': case '-': /* signed integer constant */
negative = 0;
if (*aux == '-')
{
negative = 1;
@@ -238,7 +217,7 @@ int rc_parse_operand(rc_operand_t* self, const char** memaddr, int is_trigger, i
break;
case '0':
if (aux[1] == 'x' || aux[1] == 'X') {
if (aux[1] == 'x' || aux[1] == 'X') { /* hex integer constant */
/* fall through */
default:
ret = rc_parse_operand_memory(self, &aux, parse, is_indirect);
@@ -251,8 +230,7 @@ int rc_parse_operand(rc_operand_t* self, const char** memaddr, int is_trigger, i
}
/* fall through for case '0' where not '0x' */
case '+': case '-':
case '1': case '2': case '3': case '4': case '5':
case '1': case '2': case '3': case '4': case '5': /* unsigned integer constant */
case '6': case '7': case '8': case '9':
value = strtoul(aux, &end, 10);
@@ -305,6 +283,18 @@ static int rc_luapeek(lua_State* L) {
#endif /* RC_DISABLE_LUA */
int rc_operand_is_memref(rc_operand_t* self) {
switch (self->type) {
case RC_OPERAND_CONST:
case RC_OPERAND_FP:
case RC_OPERAND_LUA:
return 0;
default:
return 1;
}
}
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_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
@@ -312,7 +302,7 @@ unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
rc_luapeek_t luapeek;
#endif /* RC_DISABLE_LUA */
unsigned value = 0;
unsigned value;
/* step 1: read memory */
switch (self->type) {
@@ -324,8 +314,9 @@ unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
return 0;
case RC_OPERAND_LUA:
#ifndef RC_DISABLE_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);
@@ -351,18 +342,8 @@ unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
break;
case RC_OPERAND_ADDRESS:
case RC_OPERAND_BCD:
case RC_OPERAND_INVERTED:
value = rc_get_indirect_memref(self->value.memref, eval_state)->value;
break;
case RC_OPERAND_DELTA:
value = rc_get_indirect_memref(self->value.memref, eval_state)->previous;
break;
case RC_OPERAND_PRIOR:
value = rc_get_indirect_memref(self->value.memref, eval_state)->prior;
default:
value = rc_get_memref_value(self->value.memref, self->type, eval_state);
break;
}
@@ -413,6 +394,9 @@ unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
value = rc_bits_set[(value & 0x0F)]
+ rc_bits_set[((value >> 4) & 0x0F)];
break;
default:
break;
}
/* step 3: apply logic */
@@ -443,6 +427,7 @@ unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
break;
case RC_MEMSIZE_32_BITS:
case RC_MEMSIZE_VARIABLE:
value = ((value >> 28) & 0x0f) * 10000000
+ ((value >> 24) & 0x0f) * 1000000
+ ((value >> 20) & 0x0f) * 100000
@@ -479,6 +464,7 @@ unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
break;
case RC_MEMSIZE_32_BITS:
case RC_MEMSIZE_VARIABLE:
value ^= 0xffffffff;
break;
File renamed without changes.
@@ -1,53 +1,73 @@
#ifndef INTERNAL_H
#define INTERNAL_H
#include "rcheevos.h"
#include "rc_runtime_types.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct rc_scratch_string {
char* value;
struct rc_scratch_string* left;
struct rc_scratch_string* right;
}
rc_scratch_string_t;
#define RC_ALLOW_ALIGN(T) struct __align_ ## T { char ch; T t; };
RC_ALLOW_ALIGN(rc_condition_t)
RC_ALLOW_ALIGN(rc_condset_t)
RC_ALLOW_ALIGN(rc_lboard_t)
RC_ALLOW_ALIGN(rc_memref_value_t)
RC_ALLOW_ALIGN(rc_memref_t)
RC_ALLOW_ALIGN(rc_operand_t)
RC_ALLOW_ALIGN(rc_richpresence_t)
RC_ALLOW_ALIGN(rc_richpresence_display_t)
RC_ALLOW_ALIGN(rc_richpresence_display_part_t)
RC_ALLOW_ALIGN(rc_richpresence_lookup_t)
RC_ALLOW_ALIGN(rc_richpresence_lookup_item_t)
RC_ALLOW_ALIGN(rc_scratch_string_t)
RC_ALLOW_ALIGN(rc_trigger_t)
RC_ALLOW_ALIGN(rc_value_t)
RC_ALLOW_ALIGN(char)
#define RC_ALIGNOF(T) (sizeof(struct __align_ ## T) - sizeof(T))
#define RC_OFFSETOF(o, t) (int)((char*)&(o.t) - (char*)&(o))
#define RC_ALLOC(t, p) ((t*)rc_alloc((p)->buffer, &(p)->offset, sizeof(t), RC_ALIGNOF(t), &(p)->scratch))
#define RC_ALLOC(t, p) ((t*)rc_alloc((p)->buffer, &(p)->offset, sizeof(t), RC_ALIGNOF(t), &(p)->scratch, RC_OFFSETOF((p)->scratch.objs, __ ## t)))
#define RC_ALLOC_SCRATCH(t, p) ((t*)rc_alloc_scratch((p)->buffer, &(p)->offset, sizeof(t), RC_ALIGNOF(t), &(p)->scratch, RC_OFFSETOF((p)->scratch.objs, __ ## t)))
typedef struct rc_scratch_buffer {
struct rc_scratch_buffer* next;
int offset;
unsigned char buffer[512 - 16];
}
rc_scratch_buffer_t;
typedef struct {
rc_memref_t memref_buffer[16];
rc_memref_t *memref;
int memref_count;
int memref_size;
rc_scratch_buffer_t buffer;
rc_scratch_string_t* strings;
union
{
rc_operand_t operand;
rc_condition_t condition;
rc_condset_t condset;
rc_trigger_t trigger;
rc_lboard_t lboard;
rc_memref_value_t memref_value;
rc_richpresence_t richpresence;
rc_richpresence_display_t richpresence_display;
rc_richpresence_display_part_t richpresence_part;
rc_richpresence_lookup_t richpresence_lookup;
rc_richpresence_lookup_item_t richpresence_lookup_item;
} obj;
struct objs {
rc_condition_t* __rc_condition_t;
rc_condset_t* __rc_condset_t;
rc_lboard_t* __rc_lboard_t;
rc_memref_t* __rc_memref_t;
rc_operand_t* __rc_operand_t;
rc_richpresence_t* __rc_richpresence_t;
rc_richpresence_display_t* __rc_richpresence_display_t;
rc_richpresence_display_part_t* __rc_richpresence_display_part_t;
rc_richpresence_lookup_t* __rc_richpresence_lookup_t;
rc_richpresence_lookup_item_t* __rc_richpresence_lookup_item_t;
rc_scratch_string_t __rc_scratch_string_t;
rc_trigger_t* __rc_trigger_t;
rc_value_t* __rc_value_t;
} objs;
}
rc_scratch_t;
typedef struct {
unsigned add_value; /* AddSource/SubSource */
unsigned add_hits; /* AddHits */
int add_hits; /* AddHits */
unsigned add_address; /* AddAddress */
rc_peek_t peek;
@@ -59,6 +79,7 @@ typedef struct {
char has_hits; /* one of more hit counts is non-zero */
char primed; /* true if all non-Trigger conditions are true */
char measured_from_hits; /* true if the measured_value came from a condition's hit count */
char was_cond_reset; /* ResetNextIf triggered */
}
rc_eval_state_t;
@@ -71,27 +92,35 @@ typedef struct {
void* buffer;
rc_scratch_t scratch;
rc_memref_value_t** first_memref;
rc_memref_t** first_memref;
rc_value_t** variables;
unsigned measured_target;
int lines_read;
char has_required_hits;
}
rc_parse_state_t;
void rc_init_parse_state(rc_parse_state_t* parse, void* buffer, lua_State* L, int funcs_ndx);
void rc_init_parse_state_memrefs(rc_parse_state_t* parse, rc_memref_value_t** memrefs);
void rc_init_parse_state_memrefs(rc_parse_state_t* parse, rc_memref_t** memrefs);
void rc_init_parse_state_variables(rc_parse_state_t* parse, rc_value_t** variables);
void rc_destroy_parse_state(rc_parse_state_t* parse);
void* rc_alloc(void* pointer, int* offset, int size, int alignment, rc_scratch_t* scratch);
void* rc_alloc(void* pointer, int* offset, int size, int alignment, rc_scratch_t* scratch, int scratch_object_pointer_offset);
void* rc_alloc_scratch(void* pointer, int* offset, int size, int alignment, rc_scratch_t* scratch, int scratch_object_pointer_offset);
char* rc_alloc_str(rc_parse_state_t* parse, const char* text, int length);
rc_memref_value_t* rc_alloc_memref_value(rc_parse_state_t* parse, unsigned address, char size, char is_indirect);
void rc_update_memref_values(rc_memref_value_t* memref, rc_peek_t peek, void* ud);
void rc_update_memref_value(rc_memref_value_t* memref, rc_peek_t peek, void* ud);
rc_memref_value_t* rc_get_indirect_memref(rc_memref_value_t* memref, rc_eval_state_t* eval_state);
rc_memref_t* rc_alloc_memref(rc_parse_state_t* parse, unsigned address, char size, char is_indirect);
int rc_parse_memref(const char** memaddr, char* size, unsigned* address);
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);
unsigned rc_get_memref_value_value(rc_memref_value_t* memref, int operand_type);
void rc_parse_trigger_internal(rc_trigger_t* self, const char** memaddr, rc_parse_state_t* parse);
rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse);
rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse, int is_value);
int rc_test_condset(rc_condset_t* self, rc_eval_state_t* eval_state);
void rc_reset_condset(rc_condset_t* self);
@@ -101,11 +130,19 @@ int rc_evaluate_condition_value(rc_condition_t* self, rc_eval_state_t* eval_stat
int rc_parse_operand(rc_operand_t* self, const char** memaddr, int is_trigger, int is_indirect, rc_parse_state_t* parse);
unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state);
char rc_parse_operator(const char** memaddr);
void rc_parse_value_internal(rc_value_t* self, const char** memaddr, rc_parse_state_t* parse);
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_parse_lboard_internal(rc_lboard_t* self, const char* memaddr, rc_parse_state_t* parse);
void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script, rc_parse_state_t* parse);
#ifdef __cplusplus
}
#endif
#endif /* INTERNAL_H */
+505
View File
@@ -0,0 +1,505 @@
/* This file provides a series of functions for integrating RetroAchievements with libretro.
* These functions will be called by a libretro frontend to validate certain expected behaviors
* and simplify mapping core data to the RAIntegration DLL.
*
* Originally designed to be shared between RALibretro and RetroArch, but will simplify
* integrating with any other frontends.
*/
#include "rc_libretro.h"
#include "rc_consoles.h"
#include "rc_compat.h"
#include <ctype.h>
#include <string.h>
static rc_libretro_message_callback rc_libretro_verbose_message_callback = NULL;
/* a value that starts with a comma is a CSV.
* if it starts with an exclamation point, it's everything but the provided value.
* if it starts with an exclamntion point followed by a comma, it's everything but the CSV values.
* values are case-insensitive */
typedef struct rc_disallowed_core_settings_t
{
const char* library_name;
const rc_disallowed_setting_t* disallowed_settings;
} rc_disallowed_core_settings_t;
static const rc_disallowed_setting_t _rc_disallowed_bsnes_settings[] = {
{ "bsnes_region", "pal" },
{ 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_ecwolf_settings[] = {
{ "ecwolf-invulnerability", "enabled" },
{ NULL, NULL }
};
static const rc_disallowed_setting_t _rc_disallowed_fbneo_settings[] = {
{ "fbneo-allow-patched-romsets", "enabled" },
{ "fbneo-cheat-*", "!,Disabled,0 - Disabled" },
{ NULL, NULL }
};
static const rc_disallowed_setting_t _rc_disallowed_fceumm_settings[] = {
{ "fceumm_region", ",PAL,Dendy" },
{ NULL, NULL }
};
static const rc_disallowed_setting_t _rc_disallowed_gpgx_settings[] = {
{ "genesis_plus_gx_lock_on", ",action replay (pro),game genie" },
{ "genesis_plus_gx_region_detect", "pal" },
{ NULL, NULL }
};
static const rc_disallowed_setting_t _rc_disallowed_gpgx_wide_settings[] = {
{ "genesis_plus_gx_wide_lock_on", ",action replay (pro),game genie" },
{ "genesis_plus_gx_wide_region_detect", "pal" },
{ NULL, NULL }
};
static const rc_disallowed_setting_t _rc_disallowed_mesen_settings[] = {
{ "mesen_region", ",PAL,Dendy" },
{ NULL, NULL }
};
static const rc_disallowed_setting_t _rc_disallowed_mesen_s_settings[] = {
{ "mesen-s_region", "PAL" },
{ NULL, NULL }
};
static const rc_disallowed_setting_t _rc_disallowed_pcsx_rearmed_settings[] = {
{ "pcsx_rearmed_region", "pal" },
{ NULL, NULL }
};
static const rc_disallowed_setting_t _rc_disallowed_picodrive_settings[] = {
{ "picodrive_region", ",Europe,Japan PAL" },
{ NULL, NULL }
};
static const rc_disallowed_setting_t _rc_disallowed_ppsspp_settings[] = {
{ "ppsspp_cheats", "enabled" },
{ NULL, NULL }
};
static const rc_disallowed_setting_t _rc_disallowed_snes9x_settings[] = {
{ "snes9x_region", "pal" },
{ NULL, NULL }
};
static const rc_disallowed_setting_t _rc_disallowed_virtual_jaguar_settings[] = {
{ "virtualjaguar_pal", "enabled" },
{ NULL, NULL }
};
static const rc_disallowed_core_settings_t rc_disallowed_core_settings[] = {
{ "bsnes-mercury", _rc_disallowed_bsnes_settings },
{ "dolphin-emu", _rc_disallowed_dolphin_settings },
{ "ecwolf", _rc_disallowed_ecwolf_settings },
{ "FCEUmm", _rc_disallowed_fceumm_settings },
{ "FinalBurn Neo", _rc_disallowed_fbneo_settings },
{ "Genesis Plus GX", _rc_disallowed_gpgx_settings },
{ "Genesis Plus GX Wide", _rc_disallowed_gpgx_wide_settings },
{ "Mesen", _rc_disallowed_mesen_settings },
{ "Mesen-S", _rc_disallowed_mesen_s_settings },
{ "PPSSPP", _rc_disallowed_ppsspp_settings },
{ "PCSX-ReARMed", _rc_disallowed_pcsx_rearmed_settings },
{ "PicoDrive", _rc_disallowed_picodrive_settings },
{ "Snes9x", _rc_disallowed_snes9x_settings },
{ "Virtual Jaguar", _rc_disallowed_virtual_jaguar_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;
}
return (*value == '\0');
}
static int rc_libretro_match_value(const char* val, const char* match) {
/* if value starts with a comma, it's a CSV list of potential matches */
if (*match == ',') {
do {
const char* ptr = ++match;
int size;
while (*match && *match != ',')
++match;
size = match - ptr;
if (val[size] == '\0') {
if (memcmp(ptr, val, size) == 0) {
return 1;
}
else {
char buffer[128];
memcpy(buffer, ptr, size);
buffer[size] = '\0';
if (rc_libretro_string_equal_nocase(buffer, val))
return 1;
}
}
} while (*match == ',');
return 0;
}
/* a leading exclamation point means the provided value(s) are not forbidden (are allowed) */
if (*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);
}
int rc_libretro_is_setting_allowed(const rc_disallowed_setting_t* disallowed_settings, const char* setting, const char* value) {
const char* key;
size_t key_len;
for (; disallowed_settings->setting; ++disallowed_settings) {
key = disallowed_settings->setting;
key_len = strlen(key);
if (key[key_len - 1] == '*') {
if (memcmp(setting, key, key_len - 1) == 0) {
if (rc_libretro_match_value(value, disallowed_settings->value))
return 0;
}
}
else {
if (memcmp(setting, key, key_len + 1) == 0) {
if (rc_libretro_match_value(value, disallowed_settings->value))
return 0;
}
}
}
return 1;
}
const rc_disallowed_setting_t* rc_libretro_get_disallowed_settings(const char* library_name) {
const rc_disallowed_core_settings_t* core_filter = rc_disallowed_core_settings;
size_t library_name_length;
if (!library_name || !library_name[0])
return NULL;
library_name_length = strlen(library_name) + 1;
while (core_filter->library_name) {
if (memcmp(core_filter->library_name, library_name, library_name_length) == 0)
return core_filter->disallowed_settings;
++core_filter;
}
return NULL;
}
unsigned char* rc_libretro_memory_find(const rc_libretro_memory_regions_t* regions, unsigned address) {
unsigned i;
for (i = 0; i < regions->count; ++i) {
const size_t size = regions->size[i];
if (address < size) {
if (regions->data[i] == NULL)
break;
return &regions->data[i][address];
}
address -= size;
}
return NULL;
}
void rc_libretro_init_verbose_message_callback(rc_libretro_message_callback callback) {
rc_libretro_verbose_message_callback = callback;
}
static void rc_libretro_verbose(const char* message) {
if (rc_libretro_verbose_message_callback)
rc_libretro_verbose_message_callback(message);
}
static const char* rc_memory_type_str(int type) {
switch (type)
{
case RC_MEMORY_TYPE_SAVE_RAM:
return "SRAM";
case RC_MEMORY_TYPE_VIDEO_RAM:
return "VRAM";
case RC_MEMORY_TYPE_UNUSED:
return "UNUSED";
default:
break;
}
return "SYSTEM RAM";
}
static void rc_libretro_memory_register_region(rc_libretro_memory_regions_t* regions, int type,
unsigned char* data, size_t size, const char* description) {
if (size == 0)
return;
if (regions->count == (sizeof(regions->size) / sizeof(regions->size[0]))) {
rc_libretro_verbose("Too many memory memory regions to register");
return;
}
if (!data && regions->count > 0 && !regions->data[regions->count - 1]) {
/* extend null region */
regions->size[regions->count - 1] += size;
}
else if (data && regions->count > 0 &&
data == (regions->data[regions->count - 1] + regions->size[regions->count - 1])) {
/* extend non-null region */
regions->size[regions->count - 1] += size;
}
else {
/* create new region */
regions->data[regions->count] = data;
regions->size[regions->count] = size;
++regions->count;
}
regions->total_size += size;
if (rc_libretro_verbose_message_callback) {
char message[128];
snprintf(message, sizeof(message), "Registered 0x%04X bytes of %s at $%06X (%s)", (unsigned)size,
rc_memory_type_str(type), (unsigned)(regions->total_size - size), description);
rc_libretro_verbose_message_callback(message);
}
}
static void rc_libretro_memory_init_without_regions(rc_libretro_memory_regions_t* regions,
rc_libretro_get_core_memory_info_func get_core_memory_info) {
/* no regions specified, assume system RAM followed by save RAM */
char description[64];
rc_libretro_core_memory_info_t info;
snprintf(description, sizeof(description), "offset 0x%06x", 0);
get_core_memory_info(RETRO_MEMORY_SYSTEM_RAM, &info);
if (info.size)
rc_libretro_memory_register_region(regions, RC_MEMORY_TYPE_SYSTEM_RAM, info.data, info.size, description);
get_core_memory_info(RETRO_MEMORY_SAVE_RAM, &info);
if (info.size)
rc_libretro_memory_register_region(regions, RC_MEMORY_TYPE_SAVE_RAM, info.data, info.size, description);
}
static const struct retro_memory_descriptor* rc_libretro_memory_get_descriptor(const struct retro_memory_map* mmap, unsigned real_address, size_t* offset)
{
const struct retro_memory_descriptor* desc = mmap->descriptors;
const struct retro_memory_descriptor* end = desc + mmap->num_descriptors;
for (; desc < end; desc++) {
if (desc->select == 0) {
/* if select is 0, attempt to explcitly match the address */
if (real_address >= desc->start && real_address < desc->start + desc->len) {
*offset = real_address - desc->start;
return desc;
}
}
else {
/* 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;
/* sanity check - make sure the descriptor is large enough to hold the target address */
if (*offset < desc->len)
return desc;
}
}
}
*offset = 0;
return NULL;
}
static void rc_libretro_memory_init_from_memory_map(rc_libretro_memory_regions_t* regions, const struct retro_memory_map* mmap,
const rc_memory_regions_t* console_regions) {
char description[64];
unsigned i;
unsigned char* region_start;
unsigned char* desc_start;
size_t desc_size;
size_t offset;
for (i = 0; i < console_regions->num_regions; ++i) {
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;
while (console_region_size > 0) {
const struct retro_memory_descriptor* desc = rc_libretro_memory_get_descriptor(mmap, real_address, &offset);
if (!desc) {
if (rc_libretro_verbose_message_callback && console_region->type != RC_MEMORY_TYPE_UNUSED) {
snprintf(description, sizeof(description), "Could not map region starting at $%06X",
real_address - console_region->real_address + console_region->start_address);
rc_libretro_verbose(description);
}
rc_libretro_memory_register_region(regions, console_region->type, NULL, console_region_size, "null filler");
break;
}
snprintf(description, sizeof(description), "descriptor %u, offset 0x%06X",
(unsigned)(desc - mmap->descriptors) + 1, (int)offset);
if (desc->ptr) {
desc_start = (uint8_t*)desc->ptr + desc->offset;
region_start = desc_start + offset;
}
else {
region_start = NULL;
}
desc_size = desc->len - offset;
if (console_region_size > desc_size) {
if (desc_size == 0) {
if (rc_libretro_verbose_message_callback && console_region->type != RC_MEMORY_TYPE_UNUSED) {
snprintf(description, sizeof(description), "Could not map region starting at $%06X",
real_address - console_region->real_address + console_region->start_address);
rc_libretro_verbose(description);
}
rc_libretro_memory_register_region(regions, console_region->type, NULL, console_region_size, "null filler");
console_region_size = 0;
}
else {
rc_libretro_memory_register_region(regions, console_region->type, region_start, desc_size, description);
console_region_size -= desc_size;
real_address += desc_size;
}
}
else {
rc_libretro_memory_register_region(regions, console_region->type, region_start, console_region_size, description);
console_region_size = 0;
}
}
}
}
static unsigned rc_libretro_memory_console_region_to_ram_type(int region_type) {
switch (region_type)
{
case RC_MEMORY_TYPE_SAVE_RAM:
return RETRO_MEMORY_SAVE_RAM;
case RC_MEMORY_TYPE_VIDEO_RAM:
return RETRO_MEMORY_VIDEO_RAM;
default:
break;
}
return RETRO_MEMORY_SYSTEM_RAM;
}
static void rc_libretro_memory_init_from_unmapped_memory(rc_libretro_memory_regions_t* regions,
rc_libretro_get_core_memory_info_func get_core_memory_info, const rc_memory_regions_t* console_regions) {
char description[64];
unsigned i, j;
rc_libretro_core_memory_info_t info;
size_t offset;
for (i = 0; i < console_regions->num_regions; ++i) {
const rc_memory_region_t* console_region = &console_regions->region[i];
const size_t console_region_size = console_region->end_address - console_region->start_address + 1;
const unsigned type = rc_libretro_memory_console_region_to_ram_type(console_region->type);
unsigned base_address = 0;
for (j = 0; j <= i; ++j) {
const rc_memory_region_t* console_region2 = &console_regions->region[j];
if (rc_libretro_memory_console_region_to_ram_type(console_region2->type) == type) {
base_address = console_region2->start_address;
break;
}
}
offset = console_region->start_address - base_address;
get_core_memory_info(type, &info);
if (offset < info.size) {
info.size -= offset;
if (info.data) {
snprintf(description, sizeof(description), "offset 0x%06X", (int)offset);
info.data += offset;
}
else {
snprintf(description, sizeof(description), "null filler");
}
}
else {
if (rc_libretro_verbose_message_callback && console_region->type != RC_MEMORY_TYPE_UNUSED) {
snprintf(description, sizeof(description), "Could not map region starting at $%06X", console_region->start_address);
rc_libretro_verbose(description);
}
info.data = NULL;
info.size = 0;
}
if (console_region_size > info.size) {
/* want more than what is available, take what we can and null fill the rest */
rc_libretro_memory_register_region(regions, console_region->type, info.data, info.size, description);
rc_libretro_memory_register_region(regions, console_region->type, NULL, console_region_size - info.size, "null filler");
}
else {
/* only take as much as we need */
rc_libretro_memory_register_region(regions, console_region->type, info.data, console_region_size, description);
}
}
}
int rc_libretro_memory_init(rc_libretro_memory_regions_t* regions, const struct retro_memory_map* mmap,
rc_libretro_get_core_memory_info_func get_core_memory_info, int console_id) {
const rc_memory_regions_t* console_regions = rc_console_memory_regions(console_id);
rc_libretro_memory_regions_t new_regions;
int has_valid_region = 0;
unsigned i;
if (!regions)
return 0;
memset(&new_regions, 0, sizeof(new_regions));
if (console_regions == NULL || console_regions->num_regions == 0)
rc_libretro_memory_init_without_regions(&new_regions, get_core_memory_info);
else if (mmap && mmap->num_descriptors != 0)
rc_libretro_memory_init_from_memory_map(&new_regions, mmap, console_regions);
else
rc_libretro_memory_init_from_unmapped_memory(&new_regions, get_core_memory_info, console_regions);
/* determine if any valid regions were found */
for (i = 0; i < new_regions.count; i++) {
if (new_regions.data[i]) {
has_valid_region = 1;
break;
}
}
memcpy(regions, &new_regions, sizeof(*regions));
return has_valid_region;
}
void rc_libretro_memory_destroy(rc_libretro_memory_regions_t* regions) {
memset(regions, 0, sizeof(*regions));
}
+60
View File
@@ -0,0 +1,60 @@
#ifndef RC_LIBRETRO_H
#define RC_LIBRETRO_H
/* this file comes from the libretro repository, which is not an explicit submodule.
* the integration must set up paths appropriately to find it. */
#include <libretro.h>
#ifdef __cplusplus
extern "C" {
#endif
/*****************************************************************************\
| Disallowed Settings |
\*****************************************************************************/
typedef struct rc_disallowed_setting_t
{
const char* setting;
const char* value;
} rc_disallowed_setting_t;
const rc_disallowed_setting_t* rc_libretro_get_disallowed_settings(const char* library_name);
int rc_libretro_is_setting_allowed(const rc_disallowed_setting_t* disallowed_settings, const char* setting, const char* value);
/*****************************************************************************\
| Memory Mapping |
\*****************************************************************************/
/* specifies a function to call for verbose logging */
typedef void (*rc_libretro_message_callback)(const char*);
void rc_libretro_init_verbose_message_callback(rc_libretro_message_callback callback);
#define RC_LIBRETRO_MAX_MEMORY_REGIONS 32
typedef struct rc_libretro_memory_regions_t
{
unsigned char* data[RC_LIBRETRO_MAX_MEMORY_REGIONS];
size_t size[RC_LIBRETRO_MAX_MEMORY_REGIONS];
size_t total_size;
unsigned count;
} rc_libretro_memory_regions_t;
typedef struct rc_libretro_core_memory_info_t
{
unsigned char* data;
size_t size;
} rc_libretro_core_memory_info_t;
typedef void (*rc_libretro_get_core_memory_info_func)(unsigned id, rc_libretro_core_memory_info_t* info);
int rc_libretro_memory_init(rc_libretro_memory_regions_t* regions, const struct retro_memory_map* mmap,
rc_libretro_get_core_memory_info_func get_core_memory_info, int console_id);
void rc_libretro_memory_destroy(rc_libretro_memory_regions_t* regions);
unsigned char* rc_libretro_memory_find(const rc_libretro_memory_regions_t* regions, unsigned address);
#ifdef __cplusplus
}
#endif
#endif /* RC_LIBRETRO_H */
+419 -192
View File
@@ -1,6 +1,6 @@
#include "internal.h"
#include "rc_internal.h"
#include "compat.h"
#include "rc_compat.h"
#include <ctype.h>
@@ -11,7 +11,31 @@ enum {
RC_FORMAT_UNKNOWN_MACRO = 103
};
static const char* rc_parse_line(const char* line, const char** end) {
static rc_memref_value_t* rc_alloc_helper_variable_memref_value(const char* memaddr, int memaddr_len, rc_parse_state_t* parse) {
const char* end;
rc_value_t* variable;
unsigned address;
char size;
/* single memory reference lookups without a modifier flag can be handled without a variable */
end = memaddr;
if (rc_parse_memref(&end, &size, &address) == RC_OK) {
/* make sure the entire memaddr was consumed. if not, there's an operator and it's a comparison, not a memory reference */
if (end == &memaddr[memaddr_len]) {
/* just a memory reference, allocate it */
return &rc_alloc_memref(parse, address, size, 0)->value;
}
}
/* not a simple memory reference, need to create a variable */
variable = rc_alloc_helper_variable(memaddr, memaddr_len, parse);
if (!variable)
return NULL;
return &variable->value;
}
static const char* rc_parse_line(const char* line, const char** end, rc_parse_state_t* parse) {
const char* nextline;
const char* endline;
@@ -20,29 +44,35 @@ static const char* rc_parse_line(const char* line, const char** end) {
while (*nextline && *nextline != '\n')
++nextline;
/* find a trailing comment marker (//) */
/* if a trailing comment marker (//) exists, the line stops there */
endline = line;
while (endline < nextline && (endline[0] != '/' || endline[1] != '/' || (endline > line && endline[-1] == '\\')))
++endline;
/* remove trailing whitespace */
if (endline == nextline) {
/* trailing whitespace on a line without a comment marker may be significant, just remove the line ending */
if (endline > line && endline[-1] == '\r')
--endline;
} else {
while (endline > line && isspace((unsigned char)endline[-1]))
/* remove trailing whitespace before the comment marker */
while (endline > line && isspace((int)((unsigned char*)endline)[-1]))
--endline;
}
/* end is pointing at the first character to ignore - makes subtraction for length easier */
/* point end at the first character to ignore, it makes subtraction for length easier */
*end = endline;
/* tally the line */
++parse->lines_read;
/* skip the newline character so we're pointing at the next line */
if (*nextline == '\n')
++nextline;
return nextline;
}
static rc_richpresence_display_t* rc_parse_richpresence_display_internal(const char* line, const char* endline, rc_parse_state_t* parse, rc_richpresence_t* richpresence) {
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;
rc_richpresence_display_part_t** next;
@@ -111,78 +141,56 @@ static rc_richpresence_display_t* rc_parse_richpresence_display_internal(const c
}
if (ptr > line) {
if (!parse->buffer) {
/* just calculating size, can't confirm lookup exists */
part = RC_ALLOC(rc_richpresence_display_part_t, parse);
in = line;
line = ++ptr;
while (ptr < endline && *ptr != ')')
++ptr;
if (*ptr == ')') {
rc_parse_value_internal(&part->value, &line, parse);
if (parse->offset < 0)
return 0;
++ptr;
} else {
/* no closing parenthesis - allocate space for the invalid string */
--in; /* already skipped over @ */
rc_alloc_str(parse, line, (int)(ptr - in));
}
} else {
/* find the lookup and hook it up */
lookup = richpresence->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;
part->text = lookup->name;
part->first_lookup_item = lookup->first_item;
part->display_type = lookup->format;
in = line;
line = ++ptr;
while (ptr < endline && *ptr != ')')
++ptr;
if (*ptr == ')') {
rc_parse_value_internal(&part->value, &line, parse);
part->value.memrefs = 0;
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));
}
break;
}
lookup = lookup->next;
}
if (!lookup) {
/* 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);
memset(part, 0, sizeof(rc_richpresence_display_part_t));
*next = part;
next = &part->next;
/* find the closing parenthesis */
part->text = lookup->name;
part->lookup = lookup;
part->display_type = lookup->format;
in = line;
line = ++ptr;
while (ptr < endline && *ptr != ')')
++ptr;
if (*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));
}
/* 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));
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 */
while (ptr < endline && *ptr != ')')
++ptr;
if (*ptr == ')')
++ptr;
/* 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));
}
}
}
@@ -195,76 +203,236 @@ static rc_richpresence_display_t* rc_parse_richpresence_display_internal(const c
return self;
}
static const char* rc_parse_richpresence_lookup(rc_richpresence_lookup_t* lookup, const char* nextline, rc_parse_state_t* parse)
static int rc_richpresence_lookup_item_count(rc_richpresence_lookup_item_t* item)
{
if (item == NULL)
return 0;
return (rc_richpresence_lookup_item_count(item->left) + rc_richpresence_lookup_item_count(item->right) + 1);
}
static void rc_rebalance_richpresence_lookup_get_items(rc_richpresence_lookup_item_t* root,
rc_richpresence_lookup_item_t** items, int* index)
{
if (root->left != NULL)
rc_rebalance_richpresence_lookup_get_items(root->left, items, index);
items[*index] = root;
++(*index);
if (root->right != NULL)
rc_rebalance_richpresence_lookup_get_items(root->right, items, index);
}
static void rc_rebalance_richpresence_lookup_rebuild(rc_richpresence_lookup_item_t** root,
rc_richpresence_lookup_item_t** items, int first, int last)
{
int mid = (first + last) / 2;
rc_richpresence_lookup_item_t* item = items[mid];
*root = item;
if (mid == first)
item->left = NULL;
else
rc_rebalance_richpresence_lookup_rebuild(&item->left, items, first, mid - 1);
if (mid == last)
item->right = NULL;
else
rc_rebalance_richpresence_lookup_rebuild(&item->right, items, mid + 1, last);
}
static void rc_rebalance_richpresence_lookup(rc_richpresence_lookup_item_t** root, rc_parse_state_t* parse)
{
rc_richpresence_lookup_item_t** items;
rc_scratch_buffer_t* buffer;
const int alignment = sizeof(rc_richpresence_lookup_item_t*);
int index;
int size;
/* don't bother rebalancing one or two items */
int count = rc_richpresence_lookup_item_count(*root);
if (count < 3)
return;
/* allocate space for the flattened list - prefer scratch memory if available */
size = count * sizeof(rc_richpresence_lookup_item_t*);
buffer = &parse->scratch.buffer;
do {
const int aligned_offset = (buffer->offset + alignment - 1) & ~(alignment - 1);
const int remaining = sizeof(buffer->buffer) - aligned_offset;
if (remaining >= size) {
items = (rc_richpresence_lookup_item_t**)&buffer->buffer[aligned_offset];
break;
}
buffer = buffer->next;
if (buffer == NULL) {
/* could not find large enough block of scratch memory; allocate. if allocation fails,
* we can still use the unbalanced tree, so just bail out */
items = (rc_richpresence_lookup_item_t**)malloc(size);
if (items == NULL)
return;
break;
}
} while (1);
/* flatten the list */
index = 0;
rc_rebalance_richpresence_lookup_get_items(*root, items, &index);
/* and rebuild it as a balanced tree */
rc_rebalance_richpresence_lookup_rebuild(root, items, 0, count - 1);
if (buffer == NULL)
free(items);
}
static void rc_insert_richpresence_lookup_item(rc_richpresence_lookup_t* lookup,
unsigned first, unsigned last, const char* label, int label_len, rc_parse_state_t* parse)
{
rc_richpresence_lookup_item_t** next;
rc_richpresence_lookup_item_t* item;
char number[64];
next = &lookup->root;
while ((item = *next) != NULL) {
if (first > item->last) {
if (first == item->last + 1 &&
strncmp(label, item->label, label_len) == 0 && item->label[label_len] == '\0') {
item->last = last;
return;
}
next = &item->right;
}
else if (last < item->first) {
if (last == item->first - 1 &&
strncmp(label, item->label, label_len) == 0 && item->label[label_len] == '\0') {
item->first = first;
return;
}
next = &item->left;
}
else {
parse->offset = RC_DUPLICATED_VALUE;
return;
}
}
item = RC_ALLOC_SCRATCH(rc_richpresence_lookup_item_t, parse);
item->first = first;
item->last = last;
item->label = rc_alloc_str(parse, label, label_len);
item->left = item->right = NULL;
*next = item;
}
static const char* rc_parse_richpresence_lookup(rc_richpresence_lookup_t* lookup, const char* nextline, rc_parse_state_t* parse)
{
const char* line;
const char* endline;
const char* defaultlabel = 0;
const char* label;
char* endptr = 0;
unsigned key;
unsigned chars;
next = &lookup->first_item;
unsigned first, last;
int base;
do
{
line = nextline;
nextline = rc_parse_line(line, &endline);
nextline = rc_parse_line(line, &endline, parse);
if (endline - line < 2)
break;
chars = 0;
while (chars < (sizeof(number) - 1) && line + chars < endline && line[chars] != '=') {
number[chars] = line[chars];
++chars;
}
number[chars] = '\0';
if (line[chars] == '=') {
line += chars + 1;
if (chars == 1 && number[0] == '*') {
defaultlabel = rc_alloc_str(parse, line, (int)(endline - line));
if (endline - line < 2) {
/* ignore full line comments inside a lookup */
if (line[0] == '/' && line[1] == '/')
continue;
}
if (number[0] == '0' && number[1] == 'x')
key = strtoul(&number[2], &endptr, 16);
else
key = strtoul(&number[0], &endptr, 10);
if (*endptr && !isspace((unsigned char)*endptr)) {
parse->offset = RC_INVALID_CONST_OPERAND;
return nextline;
}
item = RC_ALLOC(rc_richpresence_lookup_item_t, parse);
item->value = key;
item->label = rc_alloc_str(parse, line, (int)(endline - line));
*next = item;
next = &item->next_item;
/* empty line indicates end of lookup */
if (lookup->root)
rc_rebalance_richpresence_lookup(&lookup->root, parse);
break;
}
} while (1);
if (!defaultlabel)
defaultlabel = rc_alloc_str(parse, "", 0);
/* "*=XXX" specifies default label if lookup does not provide a mapping for the value */
if (line[0] == '*' && line[1] == '=') {
line += 2;
lookup->default_label = rc_alloc_str(parse, line, (int)(endline - line));
continue;
}
item = RC_ALLOC(rc_richpresence_lookup_item_t, parse);
item->value = 0;
item->label = defaultlabel;
item->next_item = 0;
*next = item;
label = line;
while (label < endline && *label != '=')
++label;
if (label == endline) {
parse->offset = RC_MISSING_VALUE;
break;
}
++label;
do {
/* get the value for the mapping */
if (line[0] == '0' && line[1] == 'x') {
line += 2;
base = 16;
} else {
base = 10;
}
first = strtoul(line, &endptr, base);
/* check for a range */
if (*endptr != '-') {
/* no range, just set last to first */
last = first;
}
else {
/* range, get last value */
line = endptr + 1;
if (line[0] == '0' && line[1] == 'x') {
line += 2;
base = 16;
} else {
base = 10;
}
last = strtoul(line, &endptr, base);
}
/* ignore spaces after the number - was previously ignored as string was split on equals */
while (*endptr == ' ')
++endptr;
/* if we've found the equal sign, this is the last item */
if (*endptr == '=') {
rc_insert_richpresence_lookup_item(lookup, first, last, label, (int)(endline - label), parse);
break;
}
/* otherwise, if it's not a comma, it's an error */
if (*endptr != ',') {
parse->offset = RC_INVALID_CONST_OPERAND;
break;
}
/* insert the current item and continue scanning the next one */
rc_insert_richpresence_lookup_item(lookup, first, last, label, (int)(endline - label), parse);
line = endptr + 1;
} while (line < endline);
} while (parse->offset > 0);
return nextline;
}
void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script, rc_parse_state_t* parse) {
rc_richpresence_display_t** nextdisplay;
rc_richpresence_lookup_t** nextlookup;
rc_richpresence_lookup_t* firstlookup = NULL;
rc_richpresence_lookup_t** nextlookup = &firstlookup;
rc_richpresence_lookup_t* lookup;
rc_trigger_t* trigger;
char format[64];
@@ -274,21 +442,28 @@ void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script,
const char* endline;
const char* ptr;
int hasdisplay = 0;
int display_line = 0;
int chars;
nextlookup = &self->first_lookup;
/* special case for empty script to return 1 line read */
if (!*script) {
parse->lines_read = 1;
parse->offset = RC_MISSING_DISPLAY_STRING;
return;
}
/* first pass: process macro initializers */
line = script;
while (*line)
{
nextline = rc_parse_line(line, &endline);
while (*line) {
nextline = rc_parse_line(line, &endline, parse);
if (strncmp(line, "Lookup:", 7) == 0) {
line += 7;
lookup = RC_ALLOC(rc_richpresence_lookup_t, parse);
lookup = RC_ALLOC_SCRATCH(rc_richpresence_lookup_t, parse);
lookup->name = rc_alloc_str(parse, line, (int)(endline - line));
lookup->format = RC_FORMAT_LOOKUP;
lookup->root = NULL;
lookup->default_label = "";
*nextlookup = lookup;
nextlookup = &lookup->next;
@@ -299,14 +474,15 @@ void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script,
} else if (strncmp(line, "Format:", 7) == 0) {
line += 7;
lookup = RC_ALLOC(rc_richpresence_lookup_t, parse);
lookup = RC_ALLOC_SCRATCH(rc_richpresence_lookup_t, parse);
lookup->name = rc_alloc_str(parse, line, (int)(endline - line));
lookup->first_item = 0;
lookup->root = NULL;
lookup->default_label = "";
*nextlookup = lookup;
nextlookup = &lookup->next;
line = nextline;
nextline = rc_parse_line(line, &endline);
nextline = rc_parse_line(line, &endline, parse);
if (parse->buffer && strncmp(line, "FormatType=", 11) == 0) {
line += 11;
@@ -322,10 +498,11 @@ void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script,
}
} else if (strncmp(line, "Display:", 8) == 0) {
display = nextline;
display_line = parse->lines_read;
do {
line = nextline;
nextline = rc_parse_line(line, &endline);
nextline = rc_parse_line(line, &endline, parse);
} while (*line == '?');
}
@@ -333,12 +510,18 @@ void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script,
}
*nextlookup = 0;
self->first_lookup = firstlookup;
nextdisplay = &self->first_display;
/* second pass, process display string*/
if (display) {
/* point the parser back at the display strings */
int lines_read = parse->lines_read;
parse->lines_read = display_line;
line = display;
nextline = rc_parse_line(line, &endline);
nextline = rc_parse_line(line, &endline, parse);
while (*line == '?') {
/* conditional display: ?trigger?string */
@@ -347,7 +530,7 @@ void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script,
++ptr;
if (ptr < endline) {
*nextdisplay = rc_parse_richpresence_display_internal(ptr + 1, endline, parse, self);
*nextdisplay = rc_parse_richpresence_display_internal(ptr + 1, endline, parse, firstlookup);
if (parse->offset < 0)
return;
trigger = &((*nextdisplay)->trigger);
@@ -360,15 +543,18 @@ void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script,
}
line = nextline;
nextline = rc_parse_line(line, &endline);
nextline = rc_parse_line(line, &endline, parse);
}
/* non-conditional display: string */
*nextdisplay = rc_parse_richpresence_display_internal(line, endline, parse, self);
*nextdisplay = rc_parse_richpresence_display_internal(line, endline, parse, firstlookup);
if (*nextdisplay) {
hasdisplay = 1;
nextdisplay = &((*nextdisplay)->next);
}
/* restore the parser state */
parse->lines_read = lines_read;
}
/* finalize */
@@ -379,111 +565,152 @@ void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script,
}
}
int rc_richpresence_size(const char* script) {
int rc_richpresence_size_lines(const char* script, int* lines_read) {
rc_richpresence_t* self;
rc_parse_state_t parse;
rc_memref_t* first_memref;
rc_value_t* variables;
rc_init_parse_state(&parse, 0, 0, 0);
rc_init_parse_state_memrefs(&parse, &first_memref);
rc_init_parse_state_variables(&parse, &variables);
self = RC_ALLOC(rc_richpresence_t, &parse);
rc_parse_richpresence_internal(self, script, &parse);
if (lines_read)
*lines_read = parse.lines_read;
rc_destroy_parse_state(&parse);
return parse.offset;
}
int rc_richpresence_size(const char* script) {
return rc_richpresence_size_lines(script, NULL);
}
rc_richpresence_t* rc_parse_richpresence(void* buffer, const char* script, lua_State* L, int funcs_ndx) {
rc_richpresence_t* self;
rc_parse_state_t parse;
if (!buffer || !script)
return 0;
return NULL;
rc_init_parse_state(&parse, buffer, L, funcs_ndx);
self = RC_ALLOC(rc_richpresence_t, &parse);
rc_init_parse_state_memrefs(&parse, &self->memrefs);
rc_init_parse_state_variables(&parse, &self->variables);
rc_parse_richpresence_internal(self, script, &parse);
rc_destroy_parse_state(&parse);
return (parse.offset >= 0) ? self : 0;
return (parse.offset >= 0) ? self : NULL;
}
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) {
rc_richpresence_display_t* display;
rc_richpresence_display_part_t* part;
rc_update_memref_values(richpresence->memrefs, peek, peek_ud);
rc_update_variables(richpresence->variables, peek, peek_ud, L);
for (display = richpresence->first_display; display; display = display->next) {
if (display->trigger.has_required_hits)
rc_test_trigger(&display->trigger, peek, peek_ud, L);
}
}
static int rc_evaluate_richpresence_display(rc_richpresence_display_part_t* part, char* buffer, unsigned buffersize)
{
rc_richpresence_lookup_item_t* item;
char tmp[256];
char* ptr;
char* ptr = buffer;
const char* text;
size_t chars;
unsigned value;
rc_update_memref_values(richpresence->memrefs, peek, peek_ud);
*ptr = '\0';
while (part) {
switch (part->display_type) {
case RC_FORMAT_STRING:
text = part->text;
chars = strlen(text);
break;
ptr = buffer;
display = richpresence->first_display;
while (display) {
if (!display->next || rc_test_trigger(&display->trigger, peek, peek_ud, L)) {
part = display->display;
while (part) {
switch (part->display_type) {
case RC_FORMAT_STRING:
text = part->text;
chars = strlen(text);
break;
case RC_FORMAT_LOOKUP:
value = rc_evaluate_value(&part->value, peek, peek_ud, L);
item = part->first_lookup_item;
if (!item) {
text = "";
chars = 0;
} else {
while (item->next_item && item->value != value)
item = item->next_item;
text = item->label;
chars = strlen(text);
}
break;
case RC_FORMAT_UNKNOWN_MACRO:
chars = snprintf(tmp, sizeof(tmp), "[Unknown macro]%s", part->text);
text = tmp;
break;
default:
value = rc_evaluate_value(&part->value, peek, peek_ud, L);
chars = rc_format_value(tmp, sizeof(tmp), value, part->display_type);
text = tmp;
break;
}
if (chars > 0 && buffersize > 0) {
if ((unsigned)chars >= buffersize) {
/* prevent write past end of buffer */
memcpy(ptr, text, buffersize - 1);
ptr[buffersize - 1] = '\0';
buffersize = 0;
case RC_FORMAT_LOOKUP:
value = part->value->value;
text = part->lookup->default_label;
item = part->lookup->root;
while (item) {
if (value > item->last) {
item = item->right;
}
else if (value < item->first) {
item = item->left;
}
else {
memcpy(ptr, text, chars);
ptr[chars] = '\0';
buffersize -= (unsigned)chars;
text = item->label;
break;
}
}
ptr += chars;
part = part->next;
}
chars = strlen(text);
break;
return (int)(ptr - buffer);
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);
text = tmp;
break;
}
display = display->next;
if (chars > 0 && buffersize > 0) {
if ((unsigned)chars >= buffersize) {
/* prevent write past end of buffer */
memcpy(ptr, text, buffersize - 1);
ptr[buffersize - 1] = '\0';
buffersize = 0;
}
else {
memcpy(ptr, text, chars);
ptr[chars] = '\0';
buffersize -= (unsigned)chars;
}
}
ptr += chars;
part = part->next;
}
return (int)(ptr - buffer);
}
int rc_get_richpresence_display_string(rc_richpresence_t* richpresence, char* buffer, unsigned buffersize, rc_peek_t peek, void* peek_ud, lua_State* L) {
rc_richpresence_display_t* display;
for (display = richpresence->first_display; display; display = display->next) {
/* if we've reached the end of the condition list, process it */
if (!display->next)
return rc_evaluate_richpresence_display(display->display, buffer, buffersize);
/* triggers with required hits will be updated in rc_update_richpresence */
if (!display->trigger.has_required_hits)
rc_test_trigger(&display->trigger, peek, peek_ud, L);
/* if we've found a valid condition, process it */
if (display->trigger.state == RC_TRIGGER_STATE_TRIGGERED)
return rc_evaluate_richpresence_display(display->display, buffer, buffersize);
}
buffer[0] = '\0';
return 0;
}
int rc_evaluate_richpresence(rc_richpresence_t* richpresence, char* buffer, unsigned buffersize, rc_peek_t peek, void* peek_ud, lua_State* L) {
rc_update_richpresence(richpresence, peek, peek_ud, L);
return rc_get_richpresence_display_string(richpresence, buffer, buffersize, peek, peek_ud, L);
}
+237 -99
View File
@@ -1,4 +1,5 @@
#include "internal.h"
#include "rc_runtime.h"
#include "rc_internal.h"
#include "../rhash/md5.h"
@@ -10,6 +11,7 @@
void rc_runtime_init(rc_runtime_t* self) {
memset(self, 0, sizeof(rc_runtime_t));
self->next_memref = &self->memrefs;
self->next_variable = &self->variables;
}
void rc_runtime_destroy(rc_runtime_t* self) {
@@ -26,6 +28,9 @@ void rc_runtime_destroy(rc_runtime_t* self) {
}
if (self->lboards) {
for (i = 0; i < self->lboard_count; ++i)
free(self->lboards[i].buffer);
free(self->lboards);
self->lboards = NULL;
@@ -40,11 +45,6 @@ void rc_runtime_destroy(rc_runtime_t* self) {
self->richpresence = previous;
}
if (self->richpresence_display_buffer) {
free(self->richpresence_display_buffer);
self->richpresence_display_buffer = NULL;
}
self->next_memref = 0;
self->memrefs = 0;
}
@@ -56,6 +56,30 @@ static void rc_runtime_checksum(const char* memaddr, unsigned char* md5) {
md5_finish(&state, md5);
}
static char rc_runtime_allocated_memrefs(rc_runtime_t* self) {
char owns_memref = 0;
/* if at least one memref was allocated within the object, we can't free the buffer when the object is deactivated */
if (*self->next_memref != NULL) {
owns_memref = 1;
/* advance through the new memrefs so we're ready for the next allocation */
do {
self->next_memref = &(*self->next_memref)->next;
} while (*self->next_memref != NULL);
}
/* if at least one variable was allocated within the object, we can't free the buffer when the object is deactivated */
if (*self->next_variable != NULL) {
owns_memref = 1;
/* advance through the new variables so we're ready for the next allocation */
do {
self->next_variable = &(*self->next_variable)->next;
} while (*self->next_variable != NULL);
}
return owns_memref;
}
static void rc_runtime_deactivate_trigger_by_index(rc_runtime_t* self, unsigned index) {
if (self->triggers[index].owns_memrefs) {
/* if the trigger has one or more memrefs in its buffer, we can't free the buffer.
@@ -85,10 +109,10 @@ void rc_runtime_deactivate_achievement(rc_runtime_t* self, unsigned id) {
int rc_runtime_activate_achievement(rc_runtime_t* self, unsigned id, const char* memaddr, lua_State* L, int funcs_idx) {
void* trigger_buffer;
rc_trigger_t* trigger;
rc_runtime_trigger_t* runtime_trigger;
rc_parse_state_t parse;
unsigned char md5[16];
int size;
char owns_memref;
unsigned i;
if (memaddr == NULL)
@@ -120,7 +144,7 @@ int rc_runtime_activate_achievement(rc_runtime_t* self, unsigned id, const char*
if (self->triggers[i].id == id && memcmp(self->triggers[i].md5, md5, 16) == 0) {
/* retrieve the trigger pointer from the buffer */
size = 0;
trigger = (rc_trigger_t*)rc_alloc(self->triggers[i].buffer, &size, sizeof(rc_trigger_t), RC_ALIGNOF(rc_trigger_t), 0);
trigger = (rc_trigger_t*)rc_alloc(self->triggers[i].buffer, &size, sizeof(rc_trigger_t), RC_ALIGNOF(rc_trigger_t), NULL, -1);
self->triggers[i].trigger = trigger;
rc_reset_trigger(trigger);
@@ -150,15 +174,6 @@ int rc_runtime_activate_achievement(rc_runtime_t* self, unsigned id, const char*
return parse.offset;
}
/* if at least one memref was allocated within the trigger, we can't free the buffer when the trigger is deactivated */
owns_memref = (*self->next_memref != NULL);
if (owns_memref) {
/* advance through the new memrefs so we're ready for the next allocation */
do {
self->next_memref = &(*self->next_memref)->next;
} while (*self->next_memref != NULL);
}
/* grow the trigger buffer if necessary */
if (self->trigger_count == self->trigger_capacity) {
self->trigger_capacity += 32;
@@ -166,15 +181,23 @@ int rc_runtime_activate_achievement(rc_runtime_t* self, unsigned id, const char*
self->triggers = (rc_runtime_trigger_t*)malloc(self->trigger_capacity * sizeof(rc_runtime_trigger_t));
else
self->triggers = (rc_runtime_trigger_t*)realloc(self->triggers, self->trigger_capacity * sizeof(rc_runtime_trigger_t));
if (!self->triggers) {
free(trigger_buffer);
*self->next_memref = NULL; /* disassociate any memrefs allocated by the failed parse */
return RC_OUT_OF_MEMORY;
}
}
/* assign the new trigger */
self->triggers[self->trigger_count].id = id;
self->triggers[self->trigger_count].trigger = trigger;
self->triggers[self->trigger_count].buffer = trigger_buffer;
self->triggers[self->trigger_count].serialized_size = 0;
memcpy(self->triggers[self->trigger_count].md5, md5, 16);
self->triggers[self->trigger_count].owns_memrefs = owns_memref;
runtime_trigger = &self->triggers[self->trigger_count];
runtime_trigger->id = id;
runtime_trigger->trigger = trigger;
runtime_trigger->buffer = trigger_buffer;
runtime_trigger->invalid_memref = NULL;
memcpy(runtime_trigger->md5, md5, 16);
runtime_trigger->serialized_size = 0;
runtime_trigger->owns_memrefs = rc_runtime_allocated_memrefs(self);
++self->trigger_count;
/* reset it, and return it */
@@ -195,6 +218,35 @@ rc_trigger_t* rc_runtime_get_achievement(const rc_runtime_t* self, unsigned id)
return NULL;
}
int rc_runtime_get_achievement_measured(const rc_runtime_t* runtime, unsigned id, unsigned* measured_value, unsigned* measured_target)
{
const rc_trigger_t* trigger = rc_runtime_get_achievement(runtime, id);
if (!measured_value || !measured_target)
return 0;
if (!trigger) {
*measured_value = *measured_target = 0;
return 0;
}
switch (trigger->state)
{
case RC_TRIGGER_STATE_DISABLED:
case RC_TRIGGER_STATE_INACTIVE:
case RC_TRIGGER_STATE_TRIGGERED:
/* don't report measured information for inactive triggers */
*measured_value = *measured_target = 0;
break;
default:
*measured_value = trigger->measured_value;
*measured_target = trigger->measured_target;
break;
}
return 1;
}
static void rc_runtime_deactivate_lboard_by_index(rc_runtime_t* self, unsigned index) {
if (self->lboards[index].owns_memrefs) {
/* if the lboard has one or more memrefs in its buffer, we can't free the buffer.
@@ -227,7 +279,6 @@ int rc_runtime_activate_lboard(rc_runtime_t* self, unsigned id, const char* mema
rc_lboard_t* lboard;
rc_parse_state_t parse;
int size;
char owns_memref;
unsigned i;
if (memaddr == 0)
@@ -259,7 +310,7 @@ int rc_runtime_activate_lboard(rc_runtime_t* self, unsigned id, const char* mema
if (self->lboards[i].id == id && memcmp(self->lboards[i].md5, md5, 16) == 0) {
/* retrieve the lboard pointer from the buffer */
size = 0;
lboard = (rc_lboard_t*)rc_alloc(self->lboards[i].buffer, &size, sizeof(rc_lboard_t), RC_ALIGNOF(rc_lboard_t), 0);
lboard = (rc_lboard_t*)rc_alloc(self->lboards[i].buffer, &size, sizeof(rc_lboard_t), RC_ALIGNOF(rc_lboard_t), NULL, -1);
self->lboards[i].lboard = lboard;
rc_reset_lboard(lboard);
@@ -289,15 +340,6 @@ int rc_runtime_activate_lboard(rc_runtime_t* self, unsigned id, const char* mema
return parse.offset;
}
/* if at least one memref was allocated within the trigger, we can't free the buffer when the trigger is deactivated */
owns_memref = (*self->next_memref != NULL);
if (owns_memref) {
/* advance through the new memrefs so we're ready for the next allocation */
do {
self->next_memref = &(*self->next_memref)->next;
} while (*self->next_memref != NULL);
}
/* grow the lboard buffer if necessary */
if (self->lboard_count == self->lboard_capacity) {
self->lboard_capacity += 16;
@@ -305,6 +347,12 @@ int rc_runtime_activate_lboard(rc_runtime_t* self, unsigned id, const char* mema
self->lboards = (rc_runtime_lboard_t*)malloc(self->lboard_capacity * sizeof(rc_runtime_lboard_t));
else
self->lboards = (rc_runtime_lboard_t*)realloc(self->lboards, self->lboard_capacity * sizeof(rc_runtime_lboard_t));
if (!self->lboards) {
free(lboard_buffer);
*self->next_memref = NULL; /* disassociate any memrefs allocated by the failed parse */
return RC_OUT_OF_MEMORY;
}
}
/* assign the new lboard */
@@ -312,8 +360,9 @@ int rc_runtime_activate_lboard(rc_runtime_t* self, unsigned id, const char* mema
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 = owns_memref;
self->lboards[self->lboard_count].owns_memrefs = rc_runtime_allocated_memrefs(self);
++self->lboard_count;
/* reset it, and return it */
@@ -334,6 +383,11 @@ rc_lboard_t* rc_runtime_get_lboard(const rc_runtime_t* self, unsigned id)
return NULL;
}
int rc_runtime_format_lboard_value(char* buffer, int size, int value, int format)
{
return rc_format_value(buffer, size, value, 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;
@@ -348,13 +402,6 @@ int rc_runtime_activate_richpresence(rc_runtime_t* self, const char* script, lua
if (size < 0)
return size;
if (!self->richpresence_display_buffer) {
self->richpresence_display_buffer = (char*)malloc(RC_RICHPRESENCE_DISPLAY_BUFFER_SIZE * sizeof(char));
if (!self->richpresence_display_buffer)
return RC_OUT_OF_MEMORY;
}
self->richpresence_display_buffer[0] = '\0';
previous = self->richpresence;
if (previous) {
if (!previous->owns_memrefs) {
@@ -377,6 +424,7 @@ int rc_runtime_activate_richpresence(rc_runtime_t* self, const char* script, lua
rc_init_parse_state(&parse, self->richpresence->buffer, L, funcs_idx);
self->richpresence->richpresence = richpresence = RC_ALLOC(rc_richpresence_t, &parse);
parse.first_memref = &self->memrefs;
parse.variables = &self->variables;
rc_parse_richpresence_internal(richpresence, script, &parse);
rc_destroy_parse_state(&parse);
@@ -388,57 +436,33 @@ int rc_runtime_activate_richpresence(rc_runtime_t* self, const char* script, lua
return parse.offset;
}
/* if at least one memref was allocated within the rich presence, we can't free the buffer when the rich presence is deactivated */
self->richpresence->owns_memrefs = (*self->next_memref != NULL);
if (self->richpresence->owns_memrefs) {
/* advance through the new memrefs so we're ready for the next allocation */
do {
self->next_memref = &(*self->next_memref)->next;
} while (*self->next_memref != NULL);
}
self->richpresence->owns_memrefs = rc_runtime_allocated_memrefs(self);
richpresence->memrefs = NULL;
self->richpresence_update_timer = 0;
richpresence->variables = NULL;
if (!richpresence->first_display || !richpresence->first_display->display) {
/* non-existant rich presence, treat like static empty string */
*self->richpresence_display_buffer = '\0';
/* non-existant rich presence */
self->richpresence->richpresence = NULL;
}
else if (richpresence->first_display->next || /* has conditional display strings */
richpresence->first_display->display->next || /* has macros */
richpresence->first_display->display->value.conditions) { /* is only a macro */
/* dynamic rich presence - reset all of the conditions */
else {
/* reset all of the conditions */
display = richpresence->first_display;
while (display != NULL) {
rc_reset_trigger(&display->trigger);
display = display->next;
}
rc_evaluate_richpresence(self->richpresence->richpresence, self->richpresence_display_buffer, RC_RICHPRESENCE_DISPLAY_BUFFER_SIZE - 1, NULL, NULL, L);
}
else {
/* static rich presence - copy the static string */
const char* src = richpresence->first_display->display->text;
char* dst = self->richpresence_display_buffer;
const char* end = dst + RC_RICHPRESENCE_DISPLAY_BUFFER_SIZE - 1;
while (*src && dst < end)
*dst++ = *src++;
*dst = '\0';
/* by setting self->richpresence to null, it won't be evaluated in do_frame() */
self->richpresence = NULL;
}
return RC_OK;
}
const char* rc_runtime_get_richpresence(const rc_runtime_t* self)
{
if (self->richpresence_display_buffer)
return self->richpresence_display_buffer;
int rc_runtime_get_richpresence(const rc_runtime_t* self, char* buffer, unsigned buffersize, rc_peek_t peek, void* peek_ud, lua_State* L) {
if (self->richpresence && self->richpresence->richpresence)
return rc_get_richpresence_display_string(self->richpresence->richpresence, buffer, buffersize, peek, peek_ud, L);
return "";
*buffer = '\0';
return 0;
}
void rc_runtime_do_frame(rc_runtime_t* self, rc_runtime_event_handler_t event_handler, rc_peek_t peek, void* ud, lua_State* L) {
@@ -448,6 +472,7 @@ void rc_runtime_do_frame(rc_runtime_t* self, rc_runtime_event_handler_t event_ha
runtime_event.value = 0;
rc_update_memref_values(self->memrefs, peek, ud);
rc_update_variables(self->variables, peek, ud, L);
for (i = self->trigger_count - 1; i >= 0; --i) {
rc_trigger_t* trigger = self->triggers[i].trigger;
@@ -456,8 +481,21 @@ void rc_runtime_do_frame(rc_runtime_t* self, rc_runtime_event_handler_t event_ha
if (!trigger)
continue;
trigger_state = trigger->state;
if (self->triggers[i].invalid_memref) {
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_DISABLED;
runtime_event.id = self->triggers[i].id;
runtime_event.value = self->triggers[i].invalid_memref->address;
trigger->state = RC_TRIGGER_STATE_DISABLED;
self->triggers[i].invalid_memref = NULL;
event_handler(&runtime_event);
runtime_event.value = 0; /* achievement loop expects this to stay at 0 */
continue;
}
trigger_state = trigger->state;
switch (rc_evaluate_trigger(trigger, peek, ud, L))
{
case RC_TRIGGER_STATE_RESET:
@@ -505,6 +543,18 @@ void rc_runtime_do_frame(rc_runtime_t* self, rc_runtime_event_handler_t event_ha
if (!lboard)
continue;
if (self->lboards[i].invalid_memref) {
runtime_event.type = RC_RUNTIME_EVENT_LBOARD_DISABLED;
runtime_event.id = self->lboards[i].id;
runtime_event.value = self->lboards[i].invalid_memref->address;
lboard->state = RC_LBOARD_STATE_DISABLED;
self->lboards[i].invalid_memref = NULL;
event_handler(&runtime_event);
continue;
}
lboard_state = lboard->state;
switch (rc_evaluate_lboard(lboard, &runtime_event.value, peek, ud, L))
{
@@ -543,31 +593,12 @@ void rc_runtime_do_frame(rc_runtime_t* self, rc_runtime_event_handler_t event_ha
}
}
if (self->richpresence && self->richpresence->richpresence) {
if (self->richpresence_update_timer == 0) {
/* generate into a temporary buffer so we don't get a partially updated string if it's read while its being updated */
char buffer[RC_RICHPRESENCE_DISPLAY_BUFFER_SIZE];
int len = rc_evaluate_richpresence(self->richpresence->richpresence, buffer, RC_RICHPRESENCE_DISPLAY_BUFFER_SIZE - 1, peek, ud, L);
/* copy into the real buffer - write the 0 terminator first to ensure reads don't overflow the buffer */
if (len > 0) {
buffer[RC_RICHPRESENCE_DISPLAY_BUFFER_SIZE - 1] = '\0';
memcpy(self->richpresence_display_buffer, buffer, RC_RICHPRESENCE_DISPLAY_BUFFER_SIZE);
}
/* schedule the next update for 60 frames later - most systems use a 60 fps framerate (some use more 50 or 75)
* since we're only sending to the server every two minutes, that's only every 7200 frames while active, which
* is evenly divisible by 50, 60, and 75.
*/
self->richpresence_update_timer = 59;
}
else {
self->richpresence_update_timer--;
}
}
if (self->richpresence && self->richpresence->richpresence)
rc_update_richpresence(self->richpresence->richpresence, peek, ud, L);
}
void rc_runtime_reset(rc_runtime_t* self) {
rc_value_t* variable;
unsigned i;
for (i = 0; i < self->trigger_count; ++i) {
@@ -587,4 +618,111 @@ void rc_runtime_reset(rc_runtime_t* self) {
display = display->next;
}
}
for (variable = self->variables; variable; variable = variable->next)
rc_reset_value(variable);
}
static int rc_condset_contains_memref(const rc_condset_t* condset, const rc_memref_t* memref) {
rc_condition_t* cond;
if (!condset)
return 0;
for (cond = condset->conditions; cond; cond = cond->next) {
if (rc_operand_is_memref(&cond->operand1) && cond->operand1.value.memref == memref)
return 1;
if (rc_operand_is_memref(&cond->operand2) && cond->operand2.value.memref == memref)
return 1;
}
return 0;
}
static int rc_value_contains_memref(const rc_value_t* value, const rc_memref_t* memref) {
rc_condset_t* condset;
if (!value)
return 0;
for (condset = value->conditions; condset; condset = condset->next) {
if (rc_condset_contains_memref(condset, memref))
return 1;
}
return 0;
}
static int rc_trigger_contains_memref(const rc_trigger_t* trigger, const rc_memref_t* memref) {
rc_condset_t* condset;
if (!trigger)
return 0;
if (rc_condset_contains_memref(trigger->requirement, memref))
return 1;
for (condset = trigger->alternative; condset; condset = condset->next) {
if (rc_condset_contains_memref(condset, memref))
return 1;
}
return 0;
}
void rc_runtime_invalidate_address(rc_runtime_t* self, unsigned address) {
unsigned i;
rc_memref_t* memref;
rc_memref_t** last_memref;
if (!self->memrefs)
return;
/* remove the invalid memref from the chain so we don't try to evaluate it in the future.
* it's still there, so anything referencing it will continue to fetch 0.
*/
last_memref = &self->memrefs;
memref = *last_memref;
do {
if (memref->address == address && !memref->value.is_indirect) {
*last_memref = memref->next;
break;
}
last_memref = &memref->next;
memref = *last_memref;
} while (memref);
/* if the address is only used indirectly, don't disable anything dependent on it */
if (!memref)
return;
/* disable any achievements dependent on the address */
for (i = 0; i < self->trigger_count; ++i) {
if (!self->triggers[i].invalid_memref && rc_trigger_contains_memref(self->triggers[i].trigger, memref))
self->triggers[i].invalid_memref = memref;
}
/* disable any leaderboards dependent on the address */
for (i = 0; i < self->lboard_count; ++i) {
if (!self->lboards[i].invalid_memref) {
rc_lboard_t* lboard = self->lboards[i].lboard;
if (lboard) {
if (rc_trigger_contains_memref(&lboard->start, memref)) {
lboard->start.state = RC_TRIGGER_STATE_DISABLED;
self->lboards[i].invalid_memref = memref;
}
if (rc_trigger_contains_memref(&lboard->cancel, memref)) {
lboard->cancel.state = RC_TRIGGER_STATE_DISABLED;
self->lboards[i].invalid_memref = memref;
}
if (rc_trigger_contains_memref(&lboard->submit, memref)) {
lboard->submit.state = RC_TRIGGER_STATE_DISABLED;
self->lboards[i].invalid_memref = memref;
}
if (rc_value_contains_memref(&lboard->value, memref))
self->lboards[i].invalid_memref = memref;
}
}
}
}
+92 -30
View File
@@ -1,4 +1,5 @@
#include "internal.h"
#include "rc_runtime.h"
#include "rc_internal.h"
#include "../rhash/md5.h"
@@ -24,7 +25,13 @@ typedef struct rc_runtime_progress_t {
#define RC_TRIGGER_STATE_UNUPDATED 0x7F
#define RC_MEMREF_FLAG_PREV_IS_PRIOR 0x00010000
#define RC_MEMREF_FLAG_CHANGED_THIS_FRAME 0x00010000
#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
#define RC_COND_FLAG_OPERAND2_IS_INDIRECT_MEMREF 0x00100000
#define RC_COND_FLAG_OPERAND2_MEMREF_CHANGED_THIS_FRAME 0x00200000
static void rc_runtime_progress_write_uint(rc_runtime_progress_t* progress, unsigned value)
{
@@ -105,7 +112,7 @@ static void rc_runtime_progress_init(rc_runtime_progress_t* progress, rc_runtime
static int rc_runtime_progress_write_memrefs(rc_runtime_progress_t* progress)
{
rc_memref_value_t* memref = progress->runtime->memrefs;
rc_memref_t* memref = progress->runtime->memrefs;
unsigned int flags = 0;
rc_runtime_progress_start_chunk(progress, RC_RUNTIME_CHUNK_MEMREFS);
@@ -118,14 +125,14 @@ static int rc_runtime_progress_write_memrefs(rc_runtime_progress_t* progress)
}
else {
while (memref) {
flags = memref->memref.size;
if (memref->previous == memref->prior)
flags |= RC_MEMREF_FLAG_PREV_IS_PRIOR;
flags = memref->value.size;
if (memref->value.changed)
flags |= RC_MEMREF_FLAG_CHANGED_THIS_FRAME;
rc_runtime_progress_write_uint(progress, memref->memref.address);
rc_runtime_progress_write_uint(progress, memref->address);
rc_runtime_progress_write_uint(progress, flags);
rc_runtime_progress_write_uint(progress, memref->value);
rc_runtime_progress_write_uint(progress, memref->prior);
rc_runtime_progress_write_uint(progress, memref->value.value);
rc_runtime_progress_write_uint(progress, memref->value.prior);
memref = memref->next;
}
@@ -140,8 +147,8 @@ static int rc_runtime_progress_read_memrefs(rc_runtime_progress_t* progress)
unsigned entries;
unsigned address, flags, value, prior;
char size;
rc_memref_value_t* memref;
rc_memref_value_t* first_unmatched = progress->runtime->memrefs;
rc_memref_t* memref;
rc_memref_t* first_unmatched_memref = progress->runtime->memrefs;
/* re-read the chunk size to determine how many memrefs are present */
progress->offset -= 4;
@@ -155,15 +162,15 @@ static int rc_runtime_progress_read_memrefs(rc_runtime_progress_t* progress)
size = flags & 0xFF;
memref = first_unmatched;
memref = first_unmatched_memref;
while (memref) {
if (memref->memref.address == address && memref->memref.size == size) {
memref->value = value;
memref->previous = (flags & RC_MEMREF_FLAG_PREV_IS_PRIOR) ? prior : value;
memref->prior = prior;
if (memref->address == address && memref->value.size == size) {
memref->value.value = value;
memref->value.changed = (flags & RC_MEMREF_FLAG_CHANGED_THIS_FRAME) ? 1 : 0;
memref->value.prior = prior;
if (memref == first_unmatched)
first_unmatched = memref->next;
if (memref == first_unmatched_memref)
first_unmatched_memref = memref->next;
break;
}
@@ -177,16 +184,57 @@ static int rc_runtime_progress_read_memrefs(rc_runtime_progress_t* progress)
return RC_OK;
}
static int rc_runtime_progress_is_indirect_memref(rc_operand_t* oper)
{
switch (oper->type)
{
case RC_OPERAND_CONST:
case RC_OPERAND_FP:
case RC_OPERAND_LUA:
return 0;
default:
return oper->value.memref->value.is_indirect;
}
}
static int rc_runtime_progress_write_condset(rc_runtime_progress_t* progress, rc_condset_t* condset)
{
rc_condition_t* cond;
unsigned flags;
rc_runtime_progress_write_uint(progress, condset->is_paused);
cond = condset->conditions;
while (cond) {
flags = 0;
if (cond->is_true)
flags |= RC_COND_FLAG_IS_TRUE;
if (rc_runtime_progress_is_indirect_memref(&cond->operand1)) {
flags |= RC_COND_FLAG_OPERAND1_IS_INDIRECT_MEMREF;
if (cond->operand1.value.memref->value.changed)
flags |= RC_COND_FLAG_OPERAND1_MEMREF_CHANGED_THIS_FRAME;
}
if (rc_runtime_progress_is_indirect_memref(&cond->operand2)) {
flags |= RC_COND_FLAG_OPERAND2_IS_INDIRECT_MEMREF;
if (cond->operand2.value.memref->value.changed)
flags |= RC_COND_FLAG_OPERAND2_MEMREF_CHANGED_THIS_FRAME;
}
rc_runtime_progress_write_uint(progress, cond->current_hits);
rc_runtime_progress_write_uint(progress, cond->is_true);
rc_runtime_progress_write_uint(progress, flags);
if (flags & RC_COND_FLAG_OPERAND1_IS_INDIRECT_MEMREF) {
rc_runtime_progress_write_uint(progress, cond->operand1.value.memref->value.value);
rc_runtime_progress_write_uint(progress, cond->operand1.value.memref->value.prior);
}
if (flags & RC_COND_FLAG_OPERAND2_IS_INDIRECT_MEMREF) {
rc_runtime_progress_write_uint(progress, cond->operand2.value.memref->value.value);
rc_runtime_progress_write_uint(progress, cond->operand2.value.memref->value.prior);
}
cond = cond->next;
}
@@ -197,13 +245,28 @@ static int rc_runtime_progress_write_condset(rc_runtime_progress_t* progress, rc
static int rc_runtime_progress_read_condset(rc_runtime_progress_t* progress, rc_condset_t* condset)
{
rc_condition_t* cond;
unsigned flags;
condset->is_paused = rc_runtime_progress_read_uint(progress);
cond = condset->conditions;
while (cond) {
cond->current_hits = rc_runtime_progress_read_uint(progress);
cond->is_true = rc_runtime_progress_read_uint(progress) & 0xFF;
flags = rc_runtime_progress_read_uint(progress);
cond->is_true = (flags & RC_COND_FLAG_IS_TRUE) ? 1 : 0;
if (flags & RC_COND_FLAG_OPERAND1_IS_INDIRECT_MEMREF) {
cond->operand1.value.memref->value.value = rc_runtime_progress_read_uint(progress);
cond->operand1.value.memref->value.prior = rc_runtime_progress_read_uint(progress);
cond->operand1.value.memref->value.changed = (flags & RC_COND_FLAG_OPERAND1_MEMREF_CHANGED_THIS_FRAME) ? 1 : 0;
}
if (flags & RC_COND_FLAG_OPERAND2_IS_INDIRECT_MEMREF) {
cond->operand2.value.memref->value.value = rc_runtime_progress_read_uint(progress);
cond->operand2.value.memref->value.prior = rc_runtime_progress_read_uint(progress);
cond->operand2.value.memref->value.changed = (flags & RC_COND_FLAG_OPERAND2_MEMREF_CHANGED_THIS_FRAME) ? 1 : 0;
}
cond = cond->next;
}
@@ -226,8 +289,7 @@ static int rc_runtime_progress_write_trigger(rc_runtime_progress_t* progress, rc
}
condset = trigger->alternative;
while (condset)
{
while (condset) {
result = rc_runtime_progress_write_condset(progress, condset);
if (result != RC_OK)
return result;
@@ -253,8 +315,7 @@ static int rc_runtime_progress_read_trigger(rc_runtime_progress_t* progress, rc_
}
condset = trigger->alternative;
while (condset)
{
while (condset) {
result = rc_runtime_progress_read_condset(progress, condset);
if (result != RC_OK)
return result;
@@ -271,25 +332,25 @@ static int rc_runtime_progress_write_achievements(rc_runtime_progress_t* progres
int offset = 0;
int result;
for (i = 0; i < progress->runtime->trigger_count; ++i)
{
for (i = 0; i < progress->runtime->trigger_count; ++i) {
rc_runtime_trigger_t* runtime_trigger = &progress->runtime->triggers[i];
if (!runtime_trigger->trigger)
continue;
switch (runtime_trigger->trigger->state)
{
case RC_TRIGGER_STATE_DISABLED:
case RC_TRIGGER_STATE_INACTIVE:
case RC_TRIGGER_STATE_TRIGGERED:
/* don't store state for inactive or triggered achievements */
break;
continue;
default:
break;
}
if (!progress->buffer) {
if(runtime_trigger->serialized_size) {
if (runtime_trigger->serialized_size) {
progress->offset += runtime_trigger->serialized_size;
continue;
}
@@ -344,10 +405,10 @@ static int rc_runtime_progress_serialize_internal(rc_runtime_progress_t* progres
rc_runtime_progress_write_uint(progress, RC_RUNTIME_MARKER);
if ((result = rc_runtime_progress_write_memrefs(progress)) != RC_OK)
return result;
return result;
if ((result = rc_runtime_progress_write_achievements(progress)) != RC_OK)
return result;
return result;
rc_runtime_progress_write_uint(progress, RC_RUNTIME_CHUNK_DONE);
rc_runtime_progress_write_uint(progress, 16);
@@ -411,6 +472,7 @@ int rc_runtime_deserialize_progress(rc_runtime_t* runtime, const unsigned char*
if (runtime_trigger->trigger) {
switch (runtime_trigger->trigger->state)
{
case RC_TRIGGER_STATE_DISABLED:
case RC_TRIGGER_STATE_INACTIVE:
case RC_TRIGGER_STATE_TRIGGERED:
/* don't update state for inactive or triggered achievements */
+24 -7
View File
@@ -1,4 +1,4 @@
#include "internal.h"
#include "rc_internal.h"
#include <stddef.h>
#include <string.h> /* memset */
@@ -10,11 +10,15 @@ void rc_parse_trigger_internal(rc_trigger_t* self, const char** memaddr, rc_pars
aux = *memaddr;
next = &self->alternative;
/* reset in case multiple triggers are parsed by the same parse_state */
parse->measured_target = 0;
parse->has_required_hits = 0;
if (*aux == 's' || *aux == 'S') {
self->requirement = 0;
}
else {
self->requirement = rc_parse_condset(&aux, parse);
self->requirement = rc_parse_condset(&aux, parse, 0);
if (parse->offset < 0) {
return;
@@ -25,7 +29,7 @@ void rc_parse_trigger_internal(rc_trigger_t* self, const char** memaddr, rc_pars
while (*aux == 's' || *aux == 'S') {
aux++;
*next = rc_parse_condset(&aux, parse);
*next = rc_parse_condset(&aux, parse, 0);
if (parse->offset < 0) {
return;
@@ -41,12 +45,15 @@ void rc_parse_trigger_internal(rc_trigger_t* self, const char** memaddr, rc_pars
self->measured_target = parse->measured_target;
self->state = RC_TRIGGER_STATE_WAITING;
self->has_hits = 0;
self->has_required_hits = parse->has_required_hits;
}
int rc_trigger_size(const char* memaddr) {
rc_trigger_t* self;
rc_parse_state_t parse;
rc_memref_t* memrefs;
rc_init_parse_state(&parse, 0, 0, 0);
rc_init_parse_state_memrefs(&parse, &memrefs);
self = RC_ALLOC(rc_trigger_t, &parse);
rc_parse_trigger_internal(self, &memaddr, &parse);
@@ -60,7 +67,7 @@ rc_trigger_t* rc_parse_trigger(void* buffer, const char* memaddr, lua_State* L,
rc_parse_state_t parse;
if (!buffer || !memaddr)
return 0;
return NULL;
rc_init_parse_state(&parse, buffer, L, funcs_ndx);
@@ -70,7 +77,7 @@ rc_trigger_t* rc_parse_trigger(void* buffer, const char* memaddr, lua_State* L,
rc_parse_trigger_internal(self, &memaddr, &parse);
rc_destroy_parse_state(&parse);
return (parse.offset >= 0) ? self : 0;
return (parse.offset >= 0) ? self : NULL;
}
static void rc_reset_trigger_hitcounts(rc_trigger_t* self) {
@@ -95,13 +102,18 @@ int rc_evaluate_trigger(rc_trigger_t* self, rc_peek_t peek, void* ud, lua_State*
char is_paused;
char is_primed;
/* previously triggered, do nothing - return INACTIVE so caller doesn't report a repeated trigger */
/* previously triggered, do nothing - return INACTIVE so caller doesn't think it triggered again */
if (self->state == RC_TRIGGER_STATE_TRIGGERED)
return RC_TRIGGER_STATE_INACTIVE;
/* unsupported, do nothing - return INACTIVE */
if (self->state == RC_TRIGGER_STATE_DISABLED)
return RC_TRIGGER_STATE_INACTIVE;
/* update the memory references */
rc_update_memref_values(self->memrefs, peek, ud);
/* not yet active, only update the memrefs - so deltas are correct when it becomes active */
/* not yet active, only update the memrefs so deltas are correct when it becomes active */
if (self->state == RC_TRIGGER_STATE_INACTIVE)
return RC_TRIGGER_STATE_INACTIVE;
@@ -192,6 +204,11 @@ int rc_evaluate_trigger(rc_trigger_t* self, rc_peek_t peek, void* ud, lua_State*
self->state = RC_TRIGGER_STATE_ACTIVE;
}
/* if an individual condition was reset, notify the caller */
if (eval_state.was_cond_reset)
return RC_TRIGGER_STATE_RESET;
/* otherwise, just return the current state */
return self->state;
}
+146 -76
View File
@@ -1,78 +1,39 @@
#include "internal.h"
#include "rc_internal.h"
#include <string.h> /* memset */
#include <ctype.h> /* isdigit */
static void rc_parse_cond_value(rc_value_t* self, const char** memaddr, rc_parse_state_t* parse) {
rc_condition_t** next;
int has_measured;
int in_add_address;
rc_condset_t** next_clause;
has_measured = 0;
in_add_address = 0;
/* this largely duplicates rc_parse_condset, but we cannot call it directly, as we need to check the
* type of each condition as we go */
self->conditions = RC_ALLOC(rc_condset_t, parse);
self->conditions->has_pause = 0;
next = &self->conditions->conditions;
for (;;) {
*next = rc_parse_condition(memaddr, parse, in_add_address);
next_clause = &self->conditions;
do
{
parse->measured_target = 0; /* passing is_value=1 should prevent any conflicts, but clear it out anyway */
*next_clause = rc_parse_condset(memaddr, parse, 1);
if (parse->offset < 0) {
return;
}
in_add_address = (*next)->type == RC_CONDITION_ADD_ADDRESS;
switch ((*next)->type) {
case RC_CONDITION_ADD_HITS:
case RC_CONDITION_ADD_SOURCE:
case RC_CONDITION_SUB_SOURCE:
case RC_CONDITION_AND_NEXT:
case RC_CONDITION_ADD_ADDRESS:
/* combining flags are allowed */
break;
case RC_CONDITION_RESET_IF:
/* ResetIf is allowed (primarily for rich presense - leaderboard will typically cancel instead of resetting) */
break;
case RC_CONDITION_MEASURED:
if (has_measured) {
parse->offset = RC_MULTIPLE_MEASURED;
return;
}
has_measured = 1;
if ((*next)->required_hits == 0 && (*next)->oper != RC_OPERATOR_NONE)
(*next)->required_hits = (unsigned)-1;
break;
default:
/* non-combinding flags and PauseIf are not allowed */
parse->offset = RC_INVALID_VALUE_FLAG;
return;
if (**memaddr == 'S' || **memaddr == 's') {
/* alt groups not supported */
parse->offset = RC_INVALID_VALUE_FLAG;
}
else if (parse->measured_target == 0) {
parse->offset = RC_MISSING_VALUE_MEASURED;
}
else if (**memaddr == '$') {
/* maximum of */
++(*memaddr);
next_clause = &(*next_clause)->next;
continue;
}
(*next)->pause = 0;
next = &(*next)->next;
break;
} while (1);
if (**memaddr != '_') {
break;
}
(*memaddr)++;
}
if (!has_measured) {
parse->offset = RC_MISSING_VALUE_MEASURED;
}
if (parse->buffer) {
*next = 0;
self->conditions->next = 0;
}
(*next_clause)->next = 0;
}
void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_parse_state_t* parse) {
@@ -87,6 +48,7 @@ void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_parse_stat
/* convert legacy format into condset */
self->conditions = RC_ALLOC(rc_condset_t, parse);
self->conditions->has_pause = 0;
self->conditions->is_paused = 0;
next = &self->conditions->conditions;
next_clause = &self->conditions->next;
@@ -116,7 +78,7 @@ void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_parse_stat
}
else {
/* if it looks like a floating point number, add the 'f' prefix */
while (isdigit((unsigned char)*buffer_ptr))
while (isdigit(*(unsigned char*)buffer_ptr))
++buffer_ptr;
if (*buffer_ptr == '.')
*ptr++ = 'f';
@@ -162,6 +124,7 @@ void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_parse_stat
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;
@@ -184,12 +147,19 @@ void rc_parse_value_internal(rc_value_t* self, const char** memaddr, rc_parse_st
else {
rc_parse_legacy_value(self, memaddr, parse);
}
self->name = "(unnamed)";
self->value.value = self->value.prior = 0;
self->value.changed = 0;
self->next = 0;
}
int rc_value_size(const char* memaddr) {
rc_value_t* self;
rc_parse_state_t parse;
rc_memref_t* first_memref;
rc_init_parse_state(&parse, 0, 0, 0);
rc_init_parse_state_memrefs(&parse, &first_memref);
self = RC_ALLOC(rc_value_t, &parse);
rc_parse_value_internal(self, &memaddr, &parse);
@@ -203,7 +173,7 @@ rc_value_t* rc_parse_value(void* buffer, const char* memaddr, lua_State* L, int
rc_parse_state_t parse;
if (!buffer || !memaddr)
return 0;
return NULL;
rc_init_parse_state(&parse, buffer, L, funcs_ndx);
@@ -213,32 +183,132 @@ rc_value_t* rc_parse_value(void* buffer, const char* memaddr, lua_State* L, int
rc_parse_value_internal(self, &memaddr, &parse);
rc_destroy_parse_state(&parse);
return (parse.offset >= 0) ? self : 0;
return (parse.offset >= 0) ? self : NULL;
}
int rc_evaluate_value(rc_value_t* self, rc_peek_t peek, void* ud, lua_State* L) {
rc_eval_state_t eval_state;
rc_condset_t* condset;
int result = 0;
memset(&eval_state, 0, sizeof(eval_state));
eval_state.peek = peek;
eval_state.peek_userdata = ud;
eval_state.L = L;
int paused = 1;
rc_update_memref_values(self->memrefs, peek, ud);
rc_test_condset(self->conditions, &eval_state);
result = (int)eval_state.measured_value;
for (condset = self->conditions; condset != NULL; condset = condset->next) {
memset(&eval_state, 0, sizeof(eval_state));
eval_state.peek = peek;
eval_state.peek_userdata = ud;
eval_state.L = L;
condset = self->conditions->next;
while (condset != NULL) {
rc_test_condset(condset, &eval_state);
if ((int)eval_state.measured_value > result)
result = (int)eval_state.measured_value;
condset = condset->next;
if (condset->is_paused)
continue;
if (eval_state.was_reset) {
/* if any ResetIf condition was true, reset the hit counts
* NOTE: ResetIf only affects the current condset when used in values!
*/
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 (paused) {
/* capture the first valid measurement */
result = (int)eval_state.measured_value;
paused = 0;
}
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 (!paused) {
/* if not paused, store the value so that it's available when paused. */
rc_update_memref_value(&self->value, result);
}
else {
/* when paused, the Measured value will not be captured, use the last captured value. */
result = self->value.value;
}
return result;
}
void rc_reset_value(rc_value_t* self) {
rc_condset_t* condset = self->conditions;
while (condset != NULL) {
rc_reset_condset(condset);
condset = condset->next;
}
self->value.value = self->value.prior = 0;
self->value.changed = 0;
}
void rc_init_parse_state_variables(rc_parse_state_t* parse, rc_value_t** variables) {
parse->variables = variables;
*variables = 0;
}
rc_value_t* rc_alloc_helper_variable(const char* memaddr, int memaddr_len, rc_parse_state_t* parse)
{
rc_value_t** variables = parse->variables;
rc_value_t* value;
const char* name;
unsigned measured_target;
while ((value = *variables) != NULL) {
if (strncmp(value->name, memaddr, memaddr_len) == 0 && value->name[memaddr_len] == 0)
return value;
variables = &value->next;
}
value = RC_ALLOC_SCRATCH(rc_value_t, parse);
memset(&value->value, 0, sizeof(value->value));
value->value.size = RC_MEMSIZE_VARIABLE;
value->memrefs = NULL;
/* capture name before calling parse as parse will update memaddr pointer */
name = rc_alloc_str(parse, memaddr, memaddr_len);
if (!name)
return NULL;
/* the helper variable likely has a Measured condition. capture the current measured_target so we can restore it
* after generating the variable so the variable's Measured target doesn't conflict with the rest of the trigger. */
measured_target = parse->measured_target;
/* disable variable resolution when defining a variable to prevent infinite recursion */
variables = parse->variables;
parse->variables = NULL;
rc_parse_value_internal(value, &memaddr, parse);
parse->variables = variables;
/* restore the measured target */
parse->measured_target = measured_target;
/* store name after calling parse as parse will set name to (unnamed) */
value->name = name;
/* append the new variable to the end of the list (have to re-evaluate in case any others were added) */
while (*variables != NULL)
variables = &(*variables)->next;
*variables = value;
return value;
}
void rc_update_variables(rc_value_t* variable, rc_peek_t peek, void* ud, lua_State* L) {
while (variable) {
rc_evaluate_value(variable, peek, ud, L);
variable = variable->next;
}
}
+526 -164
View File
File diff suppressed because it is too large. Load diff
+4 -4
View File
@@ -1,6 +1,6 @@
#include "rurl.h"
#include "rc_url.h"
#include "../rcheevos/compat.h"
#include "../rcheevos/rc_compat.h"
#include "../rhash/md5.h"
#include <stdio.h>
@@ -98,7 +98,7 @@ int rc_url_submit_lboard(char* buffer, size_t size, const char* user_name, const
}
/* Evaluate the signature. */
snprintf(signature, sizeof(signature), "%u%s%u", lboard_id, user_name, lboard_id);
snprintf(signature, sizeof(signature), "%u%s%d", lboard_id, user_name, value);
md5_init(&state);
md5_append(&state, (unsigned char*)signature, (int)strlen(signature));
md5_finish(&state, hash);
@@ -298,7 +298,7 @@ static int rc_url_append_unum(char* buffer, size_t buffer_size, size_t* buffer_o
int written = rc_url_append_param_equals(buffer, buffer_size, *buffer_offset, param);
if (written > 0) {
char num[16];
int chars = sprintf(num, "%u", value);
int chars = snprintf(num, sizeof(num), "%u", value);
if (chars + written < (int)buffer_size)
{
+1 -1
View File
@@ -34,7 +34,7 @@
#include "../../memory/wii/mem2_manager.h"
#endif
#include "../../defines/gx_defines.h"
#include <defines/gx_defines.h>
#include <boolean.h>
+1 -1
View File
@@ -43,7 +43,7 @@ static void create_path_names(void)
{
char user_path[FILENAME_MAX];
sprintf(user_path, "%sretroarch", cwd);
snprintf(user_path, sizeof(user_path), "%sretroarch", cwd);
fill_pathname_basedir(g_defaults.dirs[DEFAULT_DIR_PORT], cwd, sizeof(g_defaults.dirs[DEFAULT_DIR_PORT]));
/* Content in the same folder */
+1 -1
View File
@@ -46,7 +46,7 @@
#include "../frontend_driver.h"
#include "../../file_path_special.h"
#include "../../defines/ps3_defines.h"
#include <defines/ps3_defines.h>
#include "../../defaults.h"
#include "../../verbosity.h"
#include "../../paths.h"
+3 -3
View File
@@ -55,7 +55,7 @@
#include "../frontend_driver.h"
#include "../../defaults.h"
#include "../../file_path_special.h"
#include "../../defines/psp_defines.h"
#include <defines/psp_defines.h>
#include "../../retroarch.h"
#include "../../paths.h"
#include "../../verbosity.h"
@@ -365,7 +365,7 @@ static void frontend_psp_exec(const char *path, bool should_load_game)
param1 += 7;
memcpy(core_name, param1, param2 - param1);
core_name[param2-param1] = 0;
sprintf(argp, param2 + 8);
strlcpy(argp, param2 + 8, sizeof(argp));
args = strlen(argp);
}
else if (param1 > param2 && (param1 - (param2+8) < sizeof(argp)) && strlen(param1+7) < sizeof(core_name))
@@ -374,7 +374,7 @@ static void frontend_psp_exec(const char *path, bool should_load_game)
param2 += 8;
memcpy(argp, param2, param1 - param2);
argp[param1-param2] = 0;
sprintf(core_name, param1 + 7);
strlcpy(core_name, param1 + 7, sizeof(core_name));
args = strlen(argp);
}
else
+20 -10
View File
@@ -1011,13 +1011,13 @@ static bool is_narrator_running_windows(void)
static bool accessibility_speak_windows(int speed,
const char* speak_text, int priority)
{
char cmd[1200];
char *cmd = NULL;
const char *voice = get_user_language_iso639_1(true);
const char *language = accessibility_win_language_code(voice);
const char *langid = accessibility_win_language_id(voice);
bool res = false;
const char* speeds[10] = {"-10", "-7.5", "-5", "-2.5", "0", "2", "4", "6", "8", "10"};
size_t nbytes_cmd = 0;
if (speed < 1)
speed = 1;
else if (speed > 10)
@@ -1036,14 +1036,29 @@ static bool accessibility_speak_windows(int speed,
if (USE_POWERSHELL)
{
if (strlen(language) > 0)
snprintf(cmd, sizeof(cmd),
"powershell.exe -NoProfile -WindowStyle Hidden -Command \"Add-Type -AssemblyName System.Speech; $synth = New-Object System.Speech.Synthesis.SpeechSynthesizer; $synth.SelectVoice(\\\"%s\\\"); $synth.Rate = %s; $synth.Speak(\\\"%s\\\");\"", language, speeds[speed-1], (char*) speak_text);
{
nbytes_cmd = snprintf(NULL, 0,
"powershell.exe -NoProfile -WindowStyle Hidden -Command \"Add-Type -AssemblyName System.Speech; $synth = New-Object System.Speech.Synthesis.SpeechSynthesizer; $synth.SelectVoice(\\\"%s\\\"); $synth.Rate = %s; $synth.Speak(\\\"%s\\\");\"", language, speeds[speed-1], (char*) speak_text) + 1;
if (!(cmd = malloc(nbytes_cmd)))
return false;
snprintf(cmd, nbytes_cmd,
"powershell.exe -NoProfile -WindowStyle Hidden -Command \"Add-Type -AssemblyName System.Speech; $synth = New-Object System.Speech.Synthesis.SpeechSynthesizer; $synth.SelectVoice(\\\"%s\\\"); $synth.Rate = %s; $synth.Speak(\\\"%s\\\");\"", language, speeds[speed-1], (char*) speak_text);
}
else
snprintf(cmd, sizeof(cmd),
{
nbytes_cmd = snprintf(NULL, 0,
"powershell.exe -NoProfile -WindowStyle Hidden -Command \"Add-Type -AssemblyName System.Speech; $synth = New-Object System.Speech.Synthesis.SpeechSynthesizer; $synth.Rate = %s; $synth.Speak(\\\"%s\\\");\"", speeds[speed-1], (char*) speak_text) + 1;
if (!(cmd = malloc(nbytes_cmd)))
return false;
snprintf(cmd, nbytes_cmd,
"powershell.exe -NoProfile -WindowStyle Hidden -Command \"Add-Type -AssemblyName System.Speech; $synth = New-Object System.Speech.Synthesis.SpeechSynthesizer; $synth.Rate = %s; $synth.Speak(\\\"%s\\\");\"", speeds[speed-1], (char*) speak_text);
}
if (pi_set)
terminate_win32_process(g_pi);
res = create_win32_process(cmd);
free(cmd);
cmd = NULL;
if (!res)
{
pi_set = false;
@@ -1096,13 +1111,8 @@ static bool accessibility_speak_windows(int speed,
if (SUCCEEDED(hr))
{
wchar_t *wc = utf8_to_utf16_string_alloc(speak_text);
snprintf(cmd, sizeof(cmd),
"<rate speed=\"%s\"/><volume level=\"80\"/><lang langid=\"%s\"/>%s", speeds[speed], langid, speak_text);
if (!wc)
return false;
hr = ISpVoice_Speak(pVoice, wc, SPF_ASYNC /*SVSFlagsAsync*/, NULL);
free(wc);
}
+1 -1
View File
@@ -23,7 +23,7 @@
#include <string/stdstring.h>
#include <gfx/math/matrix_4x4.h>
#include "../../defines/psp_defines.h"
#include <defines/psp_defines.h>
#include "../../driver.h"
#include "../../retroarch.h"
#include "../video_coord_array.h"
+50 -47
View File
@@ -684,6 +684,9 @@ static bool win32_browser(
result = browser->open(&browser_state);
/* TODO/FIXME - this is weird - why is this called
* after the browser->open call? Seems to have no effect
* anymore here */
if (filename && browser_state.path)
strlcpy(filename, browser_state.path, filename_size);
@@ -1872,100 +1875,100 @@ static const char *meta_key_to_name(unsigned int meta_key)
* and displays the current shortcut key */
static void win32_localize_menu(HMENU menu)
{
int index = 0;
#ifndef LEGACY_WIN32
MENUITEMINFOW menuItemInfo;
MENUITEMINFOW menu_item_info;
#else
MENUITEMINFOA menuItemInfo;
MENUITEMINFOA menu_item_info;
#endif
int index = 0;
for (;;)
{
BOOL okay;
enum msg_hash_enums label_enum;
memset(&menuItemInfo, 0, sizeof(menuItemInfo));
menuItemInfo.cbSize = sizeof(menuItemInfo);
menuItemInfo.dwTypeData = NULL;
memset(&menu_item_info, 0, sizeof(menu_item_info));
menu_item_info.cbSize = sizeof(menu_item_info);
menu_item_info.dwTypeData = NULL;
#if(WINVER >= 0x0500)
menuItemInfo.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID | MIIM_STATE | MIIM_SUBMENU;
menu_item_info.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID | MIIM_STATE | MIIM_SUBMENU;
#else
menuItemInfo.fMask = MIIM_ID | MIIM_STATE | MIIM_SUBMENU;
menu_item_info.fMask = MIIM_ID | MIIM_STATE | MIIM_SUBMENU;
#endif
#ifndef LEGACY_WIN32
okay = GetMenuItemInfoW(menu, index, true, &menuItemInfo);
okay = GetMenuItemInfoW(menu, index, true, &menu_item_info);
#else
okay = GetMenuItemInfoA(menu, index, true, &menuItemInfo);
okay = GetMenuItemInfoA(menu, index, true, &menu_item_info);
#endif
if (!okay)
break;
/* Recursion - call this on submenu items too */
if (menuItemInfo.hSubMenu)
win32_localize_menu(menuItemInfo.hSubMenu);
if (menu_item_info.hSubMenu)
win32_localize_menu(menu_item_info.hSubMenu);
label_enum = menu_id_to_label_enum(menuItemInfo.wID);
label_enum = menu_id_to_label_enum(menu_item_info.wID);
if (label_enum != MSG_UNKNOWN)
{
int len;
#ifndef LEGACY_WIN32
wchar_t* newLabel_unicode;
wchar_t* new_label_unicode = NULL;
#else
char* newLabel_ansi;
char* new_label_ansi = NULL;
#endif
const char* newLabel = msg_hash_to_str(label_enum);
unsigned int metaKey = menu_id_to_meta_key(menuItemInfo.wID);
const char* metaKeyName = meta_key_to_name(metaKey);
const char* newLabel2 = newLabel;
char* newLabelText = NULL;
const char* new_label = msg_hash_to_str(label_enum);
unsigned int meta_key = menu_id_to_meta_key(menu_item_info.wID);
const char* new_label2 = new_label;
const char* meta_key_name = NULL;
char* new_label_text = NULL;
/* specific replacements:
Load Content = "Ctrl+O"
Fullscreen = "Alt+Enter" */
if (label_enum ==
MENU_ENUM_LABEL_VALUE_LOAD_CONTENT_LIST)
metaKeyName = "Ctrl+O";
meta_key_name = "Ctrl+O";
else if (label_enum ==
MENU_ENUM_LABEL_VALUE_INPUT_META_FULLSCREEN_TOGGLE_KEY)
metaKeyName = "Alt+Enter";
meta_key_name = "Alt+Enter";
else
meta_key_name = meta_key_to_name(meta_key);
/* Append localized name, tab character, and Shortcut Key */
if (metaKeyName && 0 != strcmp(metaKeyName, "nul"))
if (meta_key_name && string_is_not_equal(meta_key_name, "nul"))
{
int len1 = strlen(newLabel);
int len2 = strlen(metaKeyName);
int bufSize = len1 + len2 + 2;
newLabelText = (char*)malloc(bufSize);
int len1 = strlen(new_label);
int len2 = strlen(meta_key_name);
int buf_size = len1 + len2 + 2;
new_label_text = (char*)malloc(buf_size);
if (newLabelText)
if (new_label_text)
{
newLabel2 = newLabelText;
strcpy(newLabelText, newLabel);
strcat(newLabelText, "\t");
strcat(newLabelText, metaKeyName);
new_label2 = new_label_text;
snprintf(new_label_text, buf_size, "%s\t%s", new_label, meta_key_name);
/* Make first character of shortcut name uppercase */
newLabelText[len1 + 1] = toupper(newLabelText[len1 + 1]);
new_label_text[len1 + 1] = toupper(new_label_text[len1 + 1]);
}
}
#ifndef LEGACY_WIN32
/* Convert string from UTF-8, then assign menu text */
newLabel_unicode = utf8_to_utf16_string_alloc(newLabel2);
len = wcslen(newLabel_unicode);
menuItemInfo.cch = len;
menuItemInfo.dwTypeData = newLabel_unicode;
SetMenuItemInfoW(menu, index, true, &menuItemInfo);
free(newLabel_unicode);
new_label_unicode = utf8_to_utf16_string_alloc(new_label2);
len = wcslen(new_label_unicode);
menu_item_info.cch = len;
menu_item_info.dwTypeData = new_label_unicode;
SetMenuItemInfoW(menu, index, true, &menu_item_info);
free(new_label_unicode);
#else
newLabel_ansi = utf8_to_local_string_alloc(newLabel2);
len = strlen(newLabel_ansi);
menuItemInfo.cch = len;
menuItemInfo.dwTypeData = newLabel_ansi;
SetMenuItemInfoA(menu, index, true, &menuItemInfo);
free(newLabel_ansi);
new_label_ansi = utf8_to_local_string_alloc(new_label2);
len = strlen(new_label_ansi);
menu_item_info.cch = len;
menu_item_info.dwTypeData = new_label_ansi;
SetMenuItemInfoA(menu, index, true, &menu_item_info);
free(new_label_ansi);
#endif
if (newLabelText)
free(newLabelText);
if (new_label_text)
free(new_label_text);
}
index++;
}
+2 -2
View File
@@ -1811,7 +1811,7 @@ static uintptr_t d3d11_gfx_load_texture(
switch (filter_type)
{
case TEXTURE_FILTER_MIPMAP_LINEAR:
#ifdef __WINRT__
#ifndef __WINRT__
texture->desc.MiscFlags = D3D11_RESOURCE_MISC_GENERATE_MIPS;
#endif
/* fallthrough */
@@ -1819,7 +1819,7 @@ static uintptr_t d3d11_gfx_load_texture(
texture->sampler = d3d11->samplers[RARCH_FILTER_LINEAR][RARCH_WRAP_EDGE];
break;
case TEXTURE_FILTER_MIPMAP_NEAREST:
#ifdef __WINRT__
#ifndef __WINRT__
texture->desc.MiscFlags = D3D11_RESOURCE_MISC_GENERATE_MIPS;
#endif
/* fallthrough */
+1 -1
View File
@@ -31,7 +31,7 @@
#include <d3d8.h>
#include "../../defines/d3d_defines.h"
#include <defines/d3d_defines.h>
#include "../common/d3d8_common.h"
#include "../common/d3d_common.h"
#include "../video_coord_array.h"
+1 -1
View File
@@ -35,7 +35,7 @@
#include "../../config.h"
#endif
#include "../../defines/d3d_defines.h"
#include <defines/d3d_defines.h>
#include "../common/d3d_common.h"
#include "../common/d3d9_common.h"
#include "../video_coord_array.h"
+1 -1
View File
@@ -57,7 +57,7 @@
#endif
#ifdef VITA
#include "../../defines/psp_defines.h"
#include <defines/psp_defines.h>
static bool vgl_inited = false;
#endif
+1 -1
View File
@@ -39,7 +39,7 @@
#include "../font_driver.h"
#include "../drivers_font_renderer/bitmap.h"
#include "../../defines/gx_defines.h"
#include <defines/gx_defines.h>
#include "../../configuration.h"
#include "../../driver.h"
+1 -1
View File
@@ -45,7 +45,7 @@
#include <retro_miscellaneous.h>
#include "../../defines/gx_defines.h"
#include <defines/gx_defines.h>
void udelay(int us);
+1 -1
View File
@@ -35,7 +35,7 @@
#include "../font_driver.h"
#include "../../defines/psp_defines.h"
#include <defines/psp_defines.h>
#ifndef SCEGU_SCR_WIDTH
#define SCEGU_SCR_WIDTH 480
@@ -42,7 +42,7 @@
#include "../../tasks/tasks_internal.h"
#endif
#include "../../defines/ps3_defines.h"
#include <defines/ps3_defines.h>
#include <rsx/rsx.h>
#include <rsx/nv40.h>
+48 -27
View File
@@ -39,7 +39,8 @@
#include "../../verbosity.h"
#include "../../configuration.h"
#include "../../defines/psp_defines.h"
#include <defines/psp_defines.h>
#include <psp2/kernel/sysmem.h>
extern void *memcpy_neon(void *dst, const void *src, size_t n);
@@ -50,8 +51,9 @@ static void *vita2d_gfx_init(const video_info_t *video,
input_driver_t **input, void **input_data)
{
vita_video_t *vita = (vita_video_t *)calloc(1, sizeof(vita_video_t));
unsigned temp_width = PSP_FB_WIDTH;
unsigned temp_height = PSP_FB_HEIGHT;
unsigned temp_width = PSP_FB_WIDTH;
unsigned temp_height = PSP_FB_HEIGHT;
vita2d_video_mode_data video_mode_data = {0};
if (!vita)
return NULL;
@@ -63,7 +65,9 @@ static void *vita2d_gfx_init(const video_info_t *video,
RARCH_LOG("RARCH_SCALE_BASE: %i input_scale: %i = %i\n",
RARCH_SCALE_BASE, video->input_scale, RARCH_SCALE_BASE * video->input_scale);
vita2d_init();
vita2d_init_advanced_with_msaa((1 * 1024 * 1024), SCE_GXM_MULTISAMPLE_4X,
sceKernelGetModelForCDialog() == SCE_KERNEL_MODEL_VITATV? VITA2D_VIDEO_MODE_1280x720 : VITA2D_VIDEO_MODE_960x544 );
vita2d_set_clear_color(RGBA8(0x00, 0x00, 0x00, 0xFF));
vita2d_set_vblank_wait(video->vsync);
@@ -78,6 +82,10 @@ static void *vita2d_gfx_init(const video_info_t *video,
vita->format = SCE_GXM_TEXTURE_FORMAT_R5G6B5;
}
video_mode_data = vita2d_get_video_mode_data();
temp_width = video_mode_data.width;
temp_height = video_mode_data.height;
vita->fullscreen = video->fullscreen;
vita->texture = NULL;
@@ -146,6 +154,9 @@ static bool vita2d_gfx_frame(void *data, const void *frame,
{
void *tex_p;
vita_video_t *vita = (vita_video_t *)data;
unsigned temp_width = PSP_FB_WIDTH;
unsigned temp_height = PSP_FB_HEIGHT;
vita2d_video_mode_data video_mode_data = {0};
#ifdef HAVE_MENU
bool menu_is_alive = video_info->menu_is_alive;
#endif
@@ -167,6 +178,7 @@ static bool vita2d_gfx_frame(void *data, const void *frame,
if ((width != vita->width || height != vita->height) && vita->texture)
{
vita2d_wait_rendering_done();
vita2d_free_texture(vita->texture);
vita->texture = NULL;
}
@@ -208,17 +220,21 @@ static bool vita2d_gfx_frame(void *data, const void *frame,
if (vita->should_resize)
vita2d_gfx_update_viewport(vita, video_info);
video_mode_data = vita2d_get_video_mode_data();
temp_width = video_mode_data.width;
temp_height = video_mode_data.height;
vita2d_start_drawing();
vita2d_draw_rectangle(0,0,PSP_FB_WIDTH,PSP_FB_HEIGHT,vita2d_get_clear_color());
vita2d_draw_rectangle(0,0,temp_width,temp_height,vita2d_get_clear_color());
if (vita->texture)
{
if (vita->fullscreen)
vita2d_draw_texture_scale(vita->texture,
0, 0,
PSP_FB_WIDTH / (float)vita->width,
PSP_FB_HEIGHT / (float)vita->height);
temp_width / (float)vita->width,
temp_height / (float)vita->height);
else
{
const float radian = 270 * 0.0174532925f;
@@ -241,24 +257,24 @@ static bool vita2d_gfx_frame(void *data, const void *frame,
if (vita->fullscreen)
vita2d_draw_texture_scale(vita->menu.texture,
0, 0,
PSP_FB_WIDTH / (float)vita->menu.width,
PSP_FB_HEIGHT / (float)vita->menu.height);
temp_width / (float)vita->menu.width,
temp_height / (float)vita->menu.height);
else
{
if (vita->menu.width > vita->menu.height)
{
float scale = PSP_FB_HEIGHT / (float)vita->menu.height;
float scale = temp_height / (float)vita->menu.height;
float w = vita->menu.width * scale;
vita2d_draw_texture_scale(vita->menu.texture,
PSP_FB_WIDTH / 2.0f - w/2.0f, 0.0f,
temp_width / 2.0f - w/2.0f, 0.0f,
scale, scale);
}
else
{
float scale = PSP_FB_WIDTH / (float)vita->menu.width;
float scale = temp_width / (float)vita->menu.width;
float h = vita->menu.height * scale;
vita2d_draw_texture_scale(vita->menu.texture,
0.0f, PSP_FB_HEIGHT / 2.0f - h/2.0f,
0.0f, temp_height / 2.0f - h/2.0f,
scale, scale);
}
}
@@ -373,19 +389,23 @@ static void vita2d_set_projection(vita_video_t *vita,
static void vita2d_gfx_update_viewport(vita_video_t* vita,
video_frame_info_t *video_info)
{
vita2d_video_mode_data video_mode_data = vita2d_get_video_mode_data();
unsigned temp_width = video_mode_data.width;
unsigned temp_height = video_mode_data.height;
int x = 0;
int y = 0;
float device_aspect = ((float)PSP_FB_WIDTH) / PSP_FB_HEIGHT;
float width = PSP_FB_WIDTH;
float height = PSP_FB_HEIGHT;
float device_aspect = ((float)temp_width) / temp_height;
float width = temp_width;
float height = temp_height;
settings_t *settings = config_get_ptr();
bool video_scale_integer = settings->bools.video_scale_integer;
unsigned aspect_ratio_idx = settings->uints.video_aspect_ratio_idx;
if (video_scale_integer)
{
video_viewport_get_scaled_integer(&vita->vp, PSP_FB_WIDTH,
PSP_FB_HEIGHT, video_driver_get_aspect_ratio(), vita->keep_aspect);
video_viewport_get_scaled_integer(&vita->vp, temp_width,
temp_height, video_driver_get_aspect_ratio(), vita->keep_aspect);
width = vita->vp.width;
height = vita->vp.height;
}
@@ -396,8 +416,8 @@ static void vita2d_gfx_update_viewport(vita_video_t* vita,
(vita->rotation == ORIENTATION_FLIPPED_ROTATED))
{
device_aspect = 1.0 / device_aspect;
width = PSP_FB_HEIGHT;
height = PSP_FB_WIDTH;
width = temp_height;
height = temp_width;
}
#if defined(HAVE_MENU)
if (aspect_ratio_idx == ASPECT_RATIO_CUSTOM)
@@ -438,8 +458,8 @@ static void vita2d_gfx_update_viewport(vita_video_t* vita,
(vita->rotation == ORIENTATION_FLIPPED_ROTATED)
)
{
x = (PSP_FB_WIDTH - width) * 0.5f;
y = (PSP_FB_HEIGHT - height) * 0.5f;
x = (temp_width - width) * 0.5f;
y = (temp_height - height) * 0.5f;
}
}
@@ -627,6 +647,7 @@ static void vita_set_texture_frame(void *data, const void *frame, bool rgb32,
if (width != vita->menu.width && height != vita->menu.height && vita->menu.texture)
{
vita2d_wait_rendering_done();
vita2d_free_texture(vita->menu.texture);
vita->menu.texture = NULL;
}
@@ -896,11 +917,11 @@ static void vita2d_overlay_vertex_geom(void *data, unsigned image,
if (o)
{
o->w = w*PSP_FB_WIDTH/o->width;
o->h = h*PSP_FB_HEIGHT/o->height;
o->x = PSP_FB_WIDTH*(1-w)/2+x;
o->y = PSP_FB_HEIGHT*(1-h)/2+y;
vita2d_video_mode_data video_mode_data = vita2d_get_video_mode_data();
o->w = w*video_mode_data.width/o->width;
o->h = h*video_mode_data.height/o->height;
o->x = video_mode_data.width*(1-w)/2+x;
o->y = video_mode_data.height*(1-h)/2+y;
}
}
+20
View File
@@ -57,6 +57,8 @@
static void vulkan_set_viewport(void *data, unsigned viewport_width,
unsigned viewport_height, bool force_full, bool allow_rotate);
static bool vulkan_is_mapped_swapchain_texture_ptr(const vk_t* vk,
const void* ptr);
#ifdef HAVE_OVERLAY
static void vulkan_overlay_free(vk_t *vk);
@@ -682,6 +684,12 @@ static void vulkan_deinit_textures(vk_t *vk)
{
unsigned i;
/* Avoid memcpying from a destroyed/unmapped texture later on. */
const void* cached_frame;
video_driver_cached_frame_get(&cached_frame, NULL, NULL, NULL);
if (vulkan_is_mapped_swapchain_texture_ptr(vk, cached_frame))
video_driver_set_cached_frame_ptr(NULL);
vulkan_deinit_samplers(vk);
for (i = 0; i < vk->num_swapchain_images; i++)
@@ -2389,6 +2397,18 @@ static bool vulkan_get_current_sw_framebuffer(void *data,
return true;
}
static bool vulkan_is_mapped_swapchain_texture_ptr(const vk_t* vk,
const void* ptr)
{
for (unsigned i = 0; i < vk->num_swapchain_images; i++)
{
if (ptr == vk->swapchain[i].texture.mapped)
return true;
}
return false;
}
static bool vulkan_get_hw_render_interface(void *data,
const struct retro_hw_render_interface **iface)
{
+1 -1
View File
@@ -27,7 +27,7 @@
#include "../../configuration.h"
#include "../../retroarch.h"
#include "../../verbosity.h"
#include "../../defines/ps3_defines.h"
#include <defines/ps3_defines.h>
#include "../../frontend/frontend_driver.h"
#include "../common/gl_common.h"
+1 -1
View File
@@ -26,7 +26,7 @@
#include "../../retroarch.h"
#include "../font_driver.h"
#include "../common/vita2d_common.h"
#include "../../defines/psp_defines.h"
#include <defines/psp_defines.h>
static const float vita2d_vertexes[] = {
0, 0,
+2 -2
View File
@@ -89,6 +89,7 @@ static void gdi_render_msg(
const char *msg,
const struct font_params *params)
{
char* msg_local;
float x, y, scale, drop_mod, drop_alpha;
int drop_x, drop_y, msg_strlen;
unsigned i;
@@ -142,8 +143,7 @@ static void gdi_render_msg(
blue = video_msg_color_b * 255.0f;
}
char* msg_local = utf8_to_local_string_alloc(msg);
msg_local = utf8_to_local_string_alloc(msg);
msg_strlen = strlen(msg_local);
GetTextExtentPoint32(font->gdi->memDC, msg_local, msg_strlen, &textSize);
-2
View File
@@ -100,9 +100,7 @@ static void vita2d_font_free_font(void *data, bool is_threaded)
if (font->font_driver && font->font_data)
font->font_driver->free(font->font_data);
#if 0
vita2d_wait_rendering_done();
#endif
vita2d_free_texture(font->texture);
free(font);
@@ -28,7 +28,7 @@
#include "../drivers/d3d_shaders/opaque.hlsl.d3d9.h"
#include "../../defines/d3d_defines.h"
#include <defines/d3d_defines.h>
#include "../common/d3d_common.h"
#include "../common/d3d9_common.h"
+2 -2
View File
@@ -129,7 +129,7 @@ void scope_repeat(scope_t *scope)
gen->value.val_dec = (float)((int)gen->value.val_dec << gen->shift);
else if (gen->shift < 0)
gen->value.val_dec = (float)((int)gen->value.val_dec >> -gen->shift);
sprintf(tmp, "%f", gen->value.val_dec);
snprintf(tmp, sizeof(tmp), "%f", gen->value.val_dec);
}
else
{
@@ -138,7 +138,7 @@ void scope_repeat(scope_t *scope)
gen->value.val_int <<= gen->shift;
else if (gen->shift < 0)
gen->value.val_int >>= -gen->shift;
sprintf(tmp, "%d", gen->value.val_int);
snprintf(tmp, sizeof(tmp), "%d", gen->value.val_int);
}
string_set(&param->value, tmp);
+1 -1
View File
@@ -108,7 +108,7 @@ static void gfx_widget_leaderboard_display_frame(void* data, void* userdata)
gfx_display_set_alpha(p_dispwidget->backdrop_orig, DEFAULT_BACKDROP);
gfx_display_set_alpha(pure_white, 1.0f);
for (i = 0; i < state->count; ++i)
for (i = 0; i < (unsigned)state->count; ++i)
{
const unsigned widget_width = state->info[i].width;
x = video_width - widget_width - spacing;
+6 -7
View File
@@ -517,7 +517,7 @@ VIDEO DRIVER
#endif
#if defined(__PSL1GHT__)
#include "../gfx/drivers/gcm_gfx.c"
#include "../gfx/drivers/rsx_gfx.c"
#elif defined(GEKKO)
#include "../gfx/drivers/gx_gfx.c"
#elif defined(PSP)
@@ -683,13 +683,9 @@ INPUT
#elif defined(PS2)
#include "../input/drivers/ps2_input.c"
#include "../input/drivers_joypad/ps2_joypad.c"
#elif defined(__PS3__)
#if defined(__PSL1GHT__)
#include "../input/drivers/psl1ght_input.c"
#else
#elif defined(__PS3__) || defined(__PSL1GHT__)
#include "../input/drivers/ps3_input.c"
#include "../input/drivers_joypad/ps3_joypad.c"
#endif
#elif defined(ORBIS)
#include "../input/drivers/ps4_input.c"
#include "../input/drivers_joypad/ps4_joypad.c"
@@ -894,7 +890,7 @@ RSOUND
/*============================================================
AUDIO
============================================================ */
#if defined(__PS3__)
#if defined(__PS3__) || defined (__PSL1GHT__)
#include "../audio/drivers/ps3_audio.c"
#elif defined(XENON)
#include "../audio/drivers/xenon360_audio.c"
@@ -1317,6 +1313,9 @@ DATA RUNLOOP
============================================================ */
#include "../tasks/task_powerstate.c"
#include "../tasks/task_content.c"
#ifdef HAVE_CDROM
#include "../tasks/task_content_disc.c"
#endif
#ifdef HAVE_PATCH
#include "../tasks/task_patch.c"
#endif
+5 -1
View File
@@ -17,7 +17,9 @@
#include <stdint.h>
#include <stdlib.h>
#ifndef __PSL1GHT__
#include <sdk_version.h>
#endif
#include <boolean.h>
#include <libretro.h>
@@ -26,13 +28,15 @@
#include "../../config.h"
#endif
#include "../../defines/ps3_defines.h"
#include <defines/ps3_defines.h>
#include "../input_driver.h"
#ifdef HAVE_MOUSE
#ifndef MAX_MICE
#define MAX_MICE 7
#endif
#endif
/* TODO/FIXME -
* fix game focus toggle */
+1 -1
View File
@@ -25,7 +25,7 @@
#include "../../config.h"
#endif
#include "../../defines/ps3_defines.h"
#include <defines/ps3_defines.h>
#include "../input_driver.h"
+1 -1
View File
@@ -47,7 +47,7 @@
#include "../../bootstrap/psp1/kernel_functions.h"
#endif
#include "../../defines/psp_defines.h"
#include <defines/psp_defines.h>
#include "../input_driver.h"
+5 -6
View File
@@ -25,7 +25,7 @@
#include "../../configuration.h"
#include "../../defines/psp_defines.h"
#include <defines/psp_defines.h>
#ifdef HAVE_MENU
#include "../../menu/menu_driver.h"
@@ -89,10 +89,8 @@ static void *psp_joypad_init(void *data)
unsigned players_count = DEFAULT_MAX_PADS;
#if defined(VITA)
if (!sceCtrlIsMultiControllerSupported())
psp2_model = SCE_KERNEL_MODEL_VITA;
else if(sceCtrlIsMultiControllerSupported() > 0)
psp2_model = SCE_KERNEL_MODEL_VITATV;
psp2_model = sceCtrlIsMultiControllerSupported()? SCE_KERNEL_MODEL_VITATV : SCE_KERNEL_MODEL_VITA;
if (psp2_model != SCE_KERNEL_MODEL_VITATV)
players_count = 1;
if (sceKernelGetModelForCDialog() != SCE_KERNEL_MODEL_VITATV)
@@ -378,7 +376,8 @@ static bool psp_joypad_rumble(unsigned pad,
#ifdef VITA
if (psp2_model != SCE_KERNEL_MODEL_VITATV)
return false;
if(pad >= DEFAULT_MAX_PADS)
return false;
switch (effect)
{
case RETRO_RUMBLE_WEAK:
+8
View File
@@ -103,6 +103,14 @@ enum overlay_orientation
OVERLAY_ORIENTATION_PORTRAIT
};
enum overlay_show_input_type
{
OVERLAY_SHOW_INPUT_NONE = 0,
OVERLAY_SHOW_INPUT_TOUCHED,
OVERLAY_SHOW_INPUT_PHYSICAL,
OVERLAY_SHOW_INPUT_LAST
};
struct overlay
{
struct overlay_desc *descs;
+2 -2
View File
@@ -9,8 +9,8 @@ import urllib.request
import zipfile
# Check Crowdin API Key
if len(sys.argv) < 1:
print('Please Provides Crowdin API Key!')
if len(sys.argv) < 2:
print('Please provide Crowdin API Key!')
exit()
api_key = sys.argv[1]
+4
View File
@@ -0,0 +1,4 @@
{
"main-desc": "RetroArch is an open-source project that makes use of a powerful development interface called Libretro. Libretro is an interface that allows you to make cross-platform applications that can use rich features such as OpenGL, cross-platform camera support, location support, and more in the future.\n\nIt comes with its own built-in collection of applications to provide you with a 'one-stop-shop' for entertainment.\n\nLibretro and RetroArch are perfectly suited for creating games, emulators and multimedia programs. If you want to learn more, go to our website (listed below).\nIMPORTANT!!!\n\nRetroArch is a multi-versatile program, that means that in order for it to do anything, you need modular programs that we call 'cores'. These cores DO NOT COME OUT OF THE BOX. You need to go to \"Online Updater -> Core Updater\" from within the app to download them.\n\nFEATURES:\n* Eye-candy menus to choose from!\n* Scan files / directories and add them to game system collections!\n* View database information about each game once added to a collection!\n* Download programs ('cores') online\n* Update everything!\n* Download Game & Watch games and play them with our exclusive Game & Watch emulator!\n* Built-in input remapping\n* Ability to remap controls\n* Ability to enter and load cheats\n* Multi-language support!\n* Over 80+ programs('cores') now and counting!\n* Play multiplayer with NetPlay!\n* Take screenshots, save states and more!\n\n* No DRM\n* No restrictions on usage\n* Open-source\n* No push ads\n* No spying\n* No ads period\n\nJoin us on discord for support and netplay matchmaking\nhttps://discord.gg/C4amCeV\n\nVisit our Youtube channel here for tutorials, gameplays, news and development progress!\nhttps://www.youtube.com/user/libretro\nhttps://www.youtube.com/RetroArchOfficial\n\nFor info and help, see our documentation site -\nhttps://docs.libretro.com/\n\nVisit our website!\nhttps://www.retroarch.com/\n\nwww.libretro.com",
"short-desc": "Retro games and emulators on your device!"
}
+2 -2
View File
@@ -1,4 +1,4 @@
{
"main-desc": "RetroArch on avoimen lähdekoodin projekti, joka käyttää voimakasta kehitysliittymää nimeltä Libretro. Libretro on liittymä, jolla pystyy luomaan alustariippumattomia applikaatioita, jotka pystyvät käyttämään rikkaita ominaisuuksia kuten OpenGL, alustariippumaton kameratuki, sijaintituki, ja lisää tulevaisuudessa.\n\nSen mukana tulee oma sisäänrakennettu applikaatiokokoelma tuodaksesi sinulle 'yhden pysähdyksen' viihdekeskuksen.\n\nLibretro ja RetroArch sopivat täydellisesti pelien, emulaattorien ja multimediasovellusten luomiseen. Jos haluat lukea lisää, mene meidän verkkosivulle (listattu alhaalla).\nTÄRKEÄÄ!!!\n\nRetroArch on monipuolinen ohjelma, joka tarkoittaa sitä, että jotta sillä voi tehdä mitään, tarvitset modulaarisia ohjelmia, joita me kutsumme 'ytimiksi'. Nämä ytimet EIVÄT TULE PAKKAUKSEN MUKANA. Sinun täytyy mennä \"Verkkopäivittäjä -> Ytimien Päivittäjä\" applikaation kautta ladataksesi ne.\n\nOMINAISUUDET:\n* Valikoima kauniita valikoita!\n* Skannaa tiedostoja / kansioita lisätäksesi ne pelijärjestelmäkokoelmiin!\n* Näytä tietokannasta jokaisen pelin tiedot kun ne on lisätty kokoelmaan!\n* Lataa ohjelmia ('ytimiä') verkosta\n* Päivitä kaikki!\n* Lataa Game & Watch-pelejä ja pelaa niitä meidän eksklusiivisella Game & Watch-emulaattorilla!\n* Sisäänrakennettu syötön uudelleenmääritys\n* Kyky uudelleenmäärittää ohjaimia\n* Kyky syöttää ja ladata huijauskoodeja\n* Monikielinen tuki!\n* Yli 80+ ohjelmaa('ydintä') ja lisää tulee!\n* Pelaa moninpeliä Verkkopelin avulla!\n* Ota kuvakaappauksia, tallenna tiloja ja muuta!\n\n* Ei DRM:ää\n* Ei käyttörajoituksia\n* Avoin lähdekoodi\n* Ei mainosilmoituksia\n* Ei vakoilua\n* Ei mainoksia ollenkaan\n\nLiity meihin Discordissa saadaksesi tukea ja löytääksesi verkkopelejä\nhttps://discord.gg/C4amCeV\n\nKäy meidän YouTube-kanavalla nähdäksesi opetusvideoita, pelivideoita, uutisia ja kehityksen edistymistä!\nhttps://www.youtube.com/user/libretro\nhttps://www.youtube.com/RetroArchOfficial\n\nSaadaksesi tietoa ja apua, mene meidän dokumentaatiosivulle -\nhttps://docs.libretro.com/\n\nKäy verkkosivullamme!\nhttps://www.retroarch.com/\n\nwww.libretro.com",
"short-desc": "Retropelejä ja emulaattoreita laitteellesi!"
"main-desc": "RetroArch on avoimen lähdekoodin projekti, joka käyttää loistavaa kehitysliittymää nimeltä Libretro. Libretro liittymällä, pystyy luomaan alustariippumattomia sovelluksia, jotka pystyvät käyttämään monipuolisia ominaisuuksia, kuten OpenGL, alustariippumaton kameratuki, sijaintituki, ja lisää tulevaisuudessa.\n\nSen mukana tulee oma sisäänrakennettu sovelluskokoelma tuodaksesi sinulle 'yhden pysähdyksen' viihdekeskuksen.\n\nLibretro ja RetroArch sopivat täydellisesti pelien, emulaattorien ja multimediasovellusten luomiseen. Jos haluat lukea lisää, käy meidän verkkosivuilla (listattu alhaalla).\nTÄRKEÄÄ!!!\n\nRetroArch on monipuolinen ohjelma, joka tarkoittaa sitä, että ennen kuin sillä voi tehdä mitään, tarvitset modulaarisia ohjelmia, joita me kutsumme 'ytimiksi'. Nämä ytimet EIVÄT TULE PAKKAUKSEN MUKANA. Sinun täytyy mennä sovelluksen kautta \"verkkopäivittäjä -> ydinlataaja\" ladataksesi ne.\n\nOMINAISUUDET:\n* Valikoima kauniita valikoita!\n* Skannaa tiedostoja / kansioita lisätäksesi ne pelijärjestelmä kokoelmiin!\n* Näytä tietokannasta jokaisen pelin tiedot, kun ne on lisätty kokoelmaan!\n* Lataa ohjelmia ('ytimiä') verkosta\n* Päivitä kaikki!\n* Lataa Game & Watch-pelejä ja pelaa niitä meidän eksklusiivisella Game & Watch-emulaattorilla!\n* Sisäänrakennettu syötteen uudelleenmääritys\n* Kyky uudelleenmäärittää ohjaimia\n* Kyky syöttää ja ladata huijauskoodeja\n* Monikielinen tuki!\n* Yli 80+ ohjelmaa('ydintä') ja lisää tulee!\n* Pelaa moninpeliä verkkopelin avulla!\n* Ota kuvakaappauksia, tallenna pelitiloja ja muuta!\n\n* Ei DRM:ää\n* Ei käyttörajoituksia\n* Avoin lähdekoodi\n* Ei mainosilmoituksia\n* Ei vakoilua\n* Ei ollenkaan mainoksia\n\nLiity meidän discord kanavalle, saadaksesi tukea ja löytääksesi verkkopelejä\nhttps://discord.gg/C4amCeV\n\nKäy meidän YouTube-kanavalla nähdäksesi opetusvideoita, pelivideoita, uutisia ja kehityksen edistymistä!\nhttps://www.youtube.com/user/libretro\nhttps://www.youtube.com/RetroArchOfficial\n\nSaadaksesi tietoa ja apua, mene meidän dokumentaatiosivulle -\nhttps://docs.libretro.com/\n\nKäy verkkosivullamme!\nhttps://www.retroarch.com/\n\nwww.libretro.com",
"short-desc": "Retropelejä ja emulaattoreita laitteellasi!"
}
+2 -2
View File
@@ -1,4 +1,4 @@
{
"main-desc": "RetroArch is an open-source project that makes use of a powerful development interface called Libretro. Libretro is an interface that allows you to make cross-platform applications that can use rich features such as OpenGL, cross-platform camera support, location support, and more in the future.\n\nIt comes with its own built-in collection of applications to provide you with a 'one-stop-shop' for entertainment.\n\nLibretro and RetroArch are perfectly suited for creating games, emulators and multimedia programs. If you want to learn more, go to our website (listed below).\nIMPORTANT!!!\n\nRetroArch is a multi-versatile program, that means that in order for it to do anything, you need modular programs that we call 'cores'. These cores DO NOT COME OUT OF THE BOX. You need to go to \"Online Updater -> Core Updater\" from within the app to download them.\n\nFEATURES:\n* Eye-candy menus to choose from!\n* Scan files / directories and add them to game system collections!\n* View database information about each game once added to a collection!\n* Download programs ('cores') online\n* Update everything!\n* Download Game & Watch games and play them with our exclusive Game & Watch emulator!\n* Built-in input remapping\n* Ability to remap controls\n* Ability to enter and load cheats\n* Multi-language support!\n* Over 80+ programs('cores') now and counting!\n* Play multiplayer with NetPlay!\n* Take screenshots, save states and more!\n\n* No DRM\n* No restrictions on usage\n* Open-source\n* No push ads\n* No spying\n* No ads period\n\nJoin us on discord for support and netplay matchmaking\nhttps://discord.gg/C4amCeV\n\nVisit our Youtube channel here for tutorials, gameplays, news and development progress!\nhttps://www.youtube.com/user/libretro\nhttps://www.youtube.com/RetroArchOfficial\n\nFor info and help, see our documentation site -\nhttps://docs.libretro.com/\n\nVisit our website!\nhttps://www.retroarch.com/\n\nwww.libretro.com",
"short-desc": "Retro games and emulators on your device!"
"main-desc": "RetroArch är ett open source-projekt som använder ett kraftfullt utvecklingsgränssnitt som heter Libretro. Libretro är ett gränssnitt som gör att du kan skapa kross-plattformsapplikationer som kan använda rika funktioner som OpenGL, stöd för kross-plattformskamera, platsstöd och mer i framtiden.\n\nDet kommer med dess egena inbyggda samling applikationer för att ge dig en \"one-stop-shop\" för underhållning.\n\nLibretro och RetroArch passar perfekt för att skapa spel, emulatorer och multimediaprogram. Om du vill lära dig mer, gå till vår webbplats (listad nedan).\nVIKTIGT!!!\n\nRetroArch är ett mångsidigt program, vilket betyder att du behöver modulära program som vi kallar \"kärnor\" för att det ska kunna göra någonting. Dessa kärnor FÖLJER INTE MED. Du måste gå till \"Online Uppdateraren -> Kärnuppdateraren\" inifrån appen för att ladda ner dem.\n\nFUNKTIONER:\n* Ögon-godismenyer att välja mellan!\n* Skanna filer / kataloger och lägg till dem i spelsystemsamlingar!\n* Visa databasinformation om varje spel när de är tillagda i en samling!\n* Ladda ner program (\"kärnor\") online\n* Uppdatera allt!\n* Ladda ner Game & Watch spel och spela dem med vår exklusiva Game & Watch-emulator!\n* Inbyggd mappning av kontroller\n* Möjlighet att mappa om kontroller\n* Möjlighet att skriva in och ladda fusk\n* Flerspråkigt stöd!\n* Över 80+ program (\"kärnor\") nu och det ökar!\n* Spela multiplayer med NetPlay!\n* Ta skärmdumpar, spara ögonblicksbilder och mer!\n\n* Ingen DRM\n* Inga begränsningar för användning\n* Öppen källkod\n* Inga push-annonser\n* Ingen spionering\n* Inga annonser alls\n\nGå med oss på discord för support och matchmaking via netplay\nhttps://discord.gg/C4amCeV\n\nBesök vår Youtube-kanal här för handledning, spel, nyheter och utvecklingsframsteg!\nhttps://www.youtube.com/user/libretro\nhttps://www.youtube.com/RetroArchOfficial\n\nFör information och hjälp, se vår dokumentationssida -\nhttps://docs.libretro.com/\n\nBesök vår hemsida!\nhttps://www.retroarch.com/\n\nwww.libretro.com",
"short-desc": "Retrospel och emulatorer på din enhet!"
}
+2 -4
View File
@@ -1368,6 +1368,8 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_POLLED,
"معدل التحديث كما أبلغ عنه مشغل العرض."
)
#if defined(DINGUX) && defined(DINGUX_BETA)
#endif
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_FORCE_SRGB_DISABLE,
"تعطيل القوة sRGB FBO"
@@ -6172,10 +6174,6 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_OZONE_COLOR_THEME_HACKING_THE_KERNEL,
"قرصان القلوب"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_OZONE_COLOR_THEME_DRACULA,
"دراكولا"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_LEFT_THUMBNAILS_OZONE,
"الصورة المصغرة الثانية"
+26 -76
View File
@@ -696,6 +696,8 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_SCREEN_ORIENTATION,
"Orientación de la pantalla"
)
#if defined(DINGUX) && defined(DINGUX_BETA)
#endif
/* Settings > Video > Fullscreen Mode */
@@ -915,10 +917,6 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_INPUT_META_SAVE_STATE_KEY,
"Guarda un puntu de guardáu na ralura que s'esbillare anguaño."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_INPUT_META_CLOSE_CONTENT_KEY,
"Zarrar el conteníu"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_INPUT_META_CLOSE_CONTENT_KEY,
"Zarra'l conteníu actual. Quiciabes se pierda cualesquier cambéu ensin guardar."
@@ -1382,14 +1380,6 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_LIBRETRO_INFO_PATH,
"Información de nucelos"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_DIR,
"Asolombradores de videu"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_SCREENSHOT_DIRECTORY,
"Captures de pantalla"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_PLAYLIST_DIRECTORY,
"Llistes"
@@ -1617,6 +1607,10 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_CHEAT_REPEAT_COUNT,
"Númberu de repiticiones"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CHEAT_RUMBLE_PRIMARY_STRENGTH,
"Fuercia del vibrador primariu"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CHEAT_RUMBLE_PRIMARY_DURATION,
"Duración del vibrador primariu (ms)"
@@ -1647,22 +1641,6 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_SHADERS_ENABLE,
"Asolombradores de videu"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET,
"Cargar"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE,
"Guardar"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_REMOVE,
"Quitar"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_SHADER_APPLY_CHANGES,
"Aplicar los cambeos"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_SHADER,
"Asolombrador"
@@ -2117,8 +2095,8 @@ MSG_HASH(
"Escuridá Gruvbox"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_OZONE_COLOR_THEME_DRACULA,
"Drácula"
MENU_ENUM_LABEL_VALUE_LEFT_THUMBNAILS_OZONE,
"Segunda miniatura"
)
/* MaterialUI: Settings > User Interface > Appearance */
@@ -2333,10 +2311,6 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_QT_DONT_SHOW_AGAIN,
"Nun amosar más"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_QT_STOP,
"Parar"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_QT_HIDDEN_PLAYLISTS,
"Llistes anubríes"
@@ -2361,10 +2335,6 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_SUGGEST_LOADED_CORE_FIRST,
"Suxerir un nucleu cargáu primero:"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_QT_VIEW,
"Ver"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_QT_VIEW_TYPE_ICONS,
"N'iconos"
@@ -2544,14 +2514,6 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_HORIZONTAL_MENU,
"Menú horizontal"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_LOAD_CONTENT_HISTORY,
"Historial"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_RUN_MUSIC,
"Executar"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_SECONDS,
"segundos"
@@ -2599,10 +2561,6 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_NETPLAY_ENABLE,
"Xuegu en llinia"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_HELP,
"Ayuda"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_HELP_WHAT_IS_A_CORE,
"¿Qué ye un nucleu?"
@@ -2614,30 +2572,10 @@ MSG_HASH(
/* Unused (Needs Confirmation) */
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CONFIG,
"Configuración"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_BASIC_MENU_ENUM_CONTROLS_INFO,
"Información"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_BASIC_MENU_ENUM_CONTROLS_QUIT,
"Colar"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_BASIC_MENU_ENUM_CONTROLS_SCROLL_UP,
"Desplazase p'arriba"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_BASIC_MENU_ENUM_CONTROLS_TOGGLE_KEYBOARD,
"Alternar el tecláu"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_BASIC_MENU_ENUM_CONTROLS_TOGGLE_MENU,
"Alternar el menú"
)
/* Discord Status */
@@ -2696,6 +2634,10 @@ MSG_HASH(
MSG_FETCHING_CORE_LIST,
"Diendo en cata de la llista de nucleos…"
)
MSG_HASH(
MSG_EXTRACTING_CORE,
"Estrayendo'l nucleu "
)
MSG_HASH(
MSG_ALL_CORES_SWITCHED_PFD,
"Tolos nucleos sofitaos cambiaron a les versiones de la Play Store"
@@ -2716,6 +2658,14 @@ MSG_HASH(
MSG_ERROR,
"Fallu"
)
MSG_HASH(
MSG_EXTRACTING,
"Estrayendo"
)
MSG_HASH(
MSG_EXTRACTING_FILE,
"Estrayendo'l ficheru"
)
MSG_HASH(
MSG_FILE_NOT_FOUND,
"Nun s'alcontró'l ficheru"
@@ -2780,10 +2730,6 @@ MSG_HASH(
MSG_INPUT_ENABLE_SETTINGS_PASSWORD_NOK,
"La contraseña ye incorreuta."
)
MSG_HASH(
MSG_INPUT_KIOSK_MODE_PASSWORD_NOK,
"La contraseña ye incorreuta."
)
MSG_HASH(
MSG_CHEAT_DELETE_ALL_SUCCESS,
"Desaniciáronse toles trampes."
@@ -2815,8 +2761,12 @@ MSG_HASH(
/* Environment Specific Settings */
MSG_HASH(
MENU_ENUM_LABEL_VALUE_INPUT_META_RESTART_KEY,
"Reaniciar Retroarch"
MENU_ENUM_LABEL_VALUE_CPU_PERF_MODE_MAX_PERF,
"Rindimientu máximu"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CPU_PERF_MODE_MIN_POWER,
"Potencia mínima"
)
MSG_HASH(
MSG_LOCAL,
+2 -4
View File
@@ -1476,6 +1476,8 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_POLLED,
"显示驱动报告的刷新率。"
)
#if defined(DINGUX) && defined(DINGUX_BETA)
#endif
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_FORCE_SRGB_DISABLE,
"强制禁止 sRGB 帧缓冲"
@@ -8144,10 +8146,6 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_OZONE_COLOR_THEME_TWILIGHT_ZONE,
"模糊地带"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_OZONE_COLOR_THEME_DRACULA,
"德古拉"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_LEFT_THUMBNAILS_OZONE,
"第二缩略图"
+2
View File
@@ -1336,6 +1336,8 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_POLLED,
"與顯示驅動回報相同的更新率。"
)
#if defined(DINGUX) && defined(DINGUX_BETA)
#endif
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_FORCE_SRGB_DISABLE,
"強制禁止sRGB幀緩衝"
+1406
View File
File diff suppressed because it is too large. Load diff
+2
View File
@@ -1028,6 +1028,8 @@ MSG_HASH(
/* Settings > Video > Output */
#if defined(DINGUX) && defined(DINGUX_BETA)
#endif
/* Settings > Video > Fullscreen Mode */
+2
View File
@@ -112,6 +112,8 @@ MSG_HASH(
/* Settings > Video > Output */
#if defined(DINGUX) && defined(DINGUX_BETA)
#endif
/* Settings > Video > Fullscreen Mode */
+191 -5
View File
@@ -1408,6 +1408,16 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_POLLED,
"Vom Displaytreiber angegebene Bildwiederholfrequenz."
)
#if defined(DINGUX) && defined(DINGUX_BETA)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_DINGUX_REFRESH_RATE,
"Vertikale Signalfrequenz"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_VIDEO_DINGUX_REFRESH_RATE,
"Vertikale Signalfrequenz des Bildschirms setzen. '50 Hz' ermöglicht glatte Videowiedergabe von PAL-Inhalten."
)
#endif
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_FORCE_SRGB_DISABLE,
"Deaktivierung des sRGB FBO erzwingen"
@@ -1970,7 +1980,7 @@ MSG_HASH(
)
MSG_HASH(
MENU_ENUM_SUBLABEL_INPUT_AUTO_GAME_FOCUS,
"Den 'Spielfokus'-Modus aktivieren, wenn Inhalte gestartet oder fortgesetzt werden. Wenn diese Option auf 'Erkennen' gesetzt ist, wird sie aktiviert, wenn der aktuelle Kern über die Frontend-Tastaturrückruffunktion verfügt."
"Den 'Spielfokus'-Modus aktivieren, wenn Inhalte gestartet oder fortgesetzt werden. Wenn diese Option auf 'Erkennen' gesetzt ist, wird sie aktiviert, wenn der aktuelle Core über die Frontend-Tastaturrückruffunktion verfügt."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_INPUT_AUTO_GAME_FOCUS_OFF,
@@ -2740,6 +2750,14 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_CHECK_FOR_MISSING_FIRMWARE,
"Überprüfen, ob benötigte Firmware vorhanden ist, bevor versucht wird, Inhalte zu laden."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CORE_INFO_CACHE_ENABLE,
"Core-Infodateien-Cache"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_CORE_INFO_CACHE_ENABLE,
"Einen dauerhaften lokalen Cache der Informationen installierter Cores anlegen. Reduziert erheblich die Ladezeiten auf Plattformen mit langsamem Festplattenzugriff."
)
#ifndef HAVE_DYNAMIC
MSG_HASH(
MENU_ENUM_LABEL_VALUE_ALWAYS_RELOAD_CORE_ON_RUN_CONTENT,
@@ -3098,7 +3116,7 @@ MSG_HASH(
)
MSG_HASH(
MENU_ENUM_SUBLABEL_REWIND_ENABLE,
"Zu einem vorherigen Punkt des Spiels zurückkehren. Dies verursacht einen schweren Performance-Einbruch beim Spielen."
"Zu einem vorherigen Punkt des Spiels zurückkehren. Dies verursacht einen schweren Leistungseinbruch beim Spielen."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_REWIND_GRANULARITY,
@@ -3564,6 +3582,14 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_NOTIFICATION_SHOW_CHEATS_APPLIED,
"Eine Meldung auf dem Bildschirm anzeigen, wenn Cheat-Codes angewendet werden."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_NOTIFICATION_SHOW_PATCH_APPLIED,
"Patch-Benachrichtigungen"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_NOTIFICATION_SHOW_PATCH_APPLIED,
"Eine Meldung auf dem Bildschirm anzeigen, wenn Soft-Patches angewendet werden."
)
MSG_HASH(
MENU_ENUM_SUBLABEL_NOTIFICATION_SHOW_AUTOCONFIG,
"Eine Bildschirmmeldung anzeigen, wenn Eingabegeräte angeschlossen/getrennt werden."
@@ -3799,6 +3825,38 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_QUIT_ON_CLOSE_CONTENT,
"RetroArch automatisch beenden, wenn Inhalte geschlossen werden. 'CLI' wird nur beendet, wenn der Inhalt über die Befehlszeile gestartet wird."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MENU_SCREENSAVER_TIMEOUT,
"Menü-Bildschirmschoner-Timeout"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_MENU_SCREENSAVER_TIMEOUT,
"Wenn das Menü aktiv ist, wird bei Inaktivität nach der angegebenen Zeit ein Bildschirmschoner angezeigt."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MENU_SCREENSAVER_ANIMATION,
"Menü-Bildschirmschoner-Animation"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_MENU_SCREENSAVER_ANIMATION,
"Animationseffekt anzeigen, während der Menü-Bildschirmschoner aktiv ist. Hat mäßige Performance-Kosten."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MENU_SCREENSAVER_ANIMATION_SNOW,
"Schnee"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MENU_SCREENSAVER_ANIMATION_STARFIELD,
"Sternenfeld"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MENU_SCREENSAVER_ANIMATION_SPEED,
"Menü-Bildschirmschoner-Animationsgeschwindigkeit"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_MENU_SCREENSAVER_ANIMATION_SPEED,
"Geschwindigkeit des Animationseffekts des Menü-Bildschirmschoners anpassen."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MOUSE_ENABLE,
"Maus-Unterstützung"
@@ -3847,6 +3905,14 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_MENU_SCROLL_FAST,
"Maximale Geschwindigkeit des Cursors beim Halten einer Bildlaufrichtung."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MENU_SCROLL_DELAY,
"Menü Scroll-Verzögerung"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_MENU_SCROLL_DELAY,
"Initiale Verzögerung in Millisekunden, wenn eine Richtung zum Scrollen gedrückt gehalten wird."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_UI_COMPANION_ENABLE,
"UI-Companion"
@@ -3965,7 +4031,7 @@ MSG_HASH(
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MENU_WALLPAPER,
"Hitergrundbild"
"Hintergrundbild"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_MENU_WALLPAPER,
@@ -4033,7 +4099,7 @@ MSG_HASH(
)
MSG_HASH(
MENU_ENUM_SUBLABEL_MENU_TICKER_SMOOTH,
"Eine geglättete Bildlaufanimation verwenden, wenn langer Menütext angezeigt wird. Hat einen geringen Einfluss auf die Leistung."
"Eine flüssige Bildlaufanimation verwenden, wenn langer Menütext angezeigt wird. Hat einen geringen Einfluss auf die Leistung."
)
/* Settings > AI Service */
@@ -6872,6 +6938,14 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_RGUI_MENU_THEME_PRESET,
"Im Dateibrowser ein voreingestelltes Menüthema auswählen."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MENU_RGUI_TRANSPARENCY,
"Menütransparenz"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_MENU_RGUI_TRANSPARENCY,
"Laufende Inhalte im Hintergrund anzeigen, während das Schnellmenü aktiv ist. Das Deaktivieren der Transparenz kann die Themafarbe ändern."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MENU_RGUI_SHADOWS,
"Schatteneffekte"
@@ -6896,6 +6970,14 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_MENU_RGUI_PARTICLE_EFFECT_SPEED,
"Geschwindigkeit der Hintergrundpartikelanimation anpassen."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MENU_RGUI_PARTICLE_EFFECT_SCREENSAVER,
"Bildschirmschoner-Hintergrund-Animation"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_MENU_RGUI_PARTICLE_EFFECT_SCREENSAVER,
"Einen Hintergrundpartikel-Effekt anzeigen, während der Menü-Bildschirmschoner aktiv ist."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MENU_RGUI_INLINE_THUMBNAILS,
"Wiedergabelisten-Miniaturansichten anzeigen"
@@ -8250,7 +8332,7 @@ MSG_HASH(
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_NO_PERFORMANCE_COUNTERS,
"Keine Leistungszähler."
"Keine Leistungsindikatoren."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_NO_PLAYLISTS,
@@ -9056,6 +9138,10 @@ MSG_HASH(
MSG_APPLYING_CHEAT,
"Änderungen an Cheats werden übernommen."
)
MSG_HASH(
MSG_APPLYING_PATCH,
"Patch angewandt: %s"
)
MSG_HASH(
MSG_APPLYING_SHADER,
"Shader anwenden"
@@ -10414,6 +10500,90 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_SUSTAINED_PERFORMANCE_MODE,
"Dauerleistungsmodus"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CPU_POLICY_ENTRY,
"Richtlinie"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CPU_PERF_MODE,
"Reglermodus"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CPU_PERF_MODE_MANUAL,
"Manuell"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_VALUE_CPU_PERF_MODE_MANUAL,
"Ermöglicht das manuelle Anpassen aller Details in jeder CPU: Regler, Frequenzen usw. Nur für fortgeschrittene Benutzer empfohlen."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CPU_PERF_MODE_MANAGED_PERF,
"Leistung (verwaltet)"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_VALUE_CPU_PERF_MODE_MANAGED_PERF,
"Standard- und empfohlener Modus. Maximale Leistung während der Wiedergabe und Stromsparmodus beim Pausieren oder Betrachten der Menüs."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CPU_PERF_MODE_MANAGED_PER_CONTEXT,
"Individuell verwaltet"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_VALUE_CPU_PERF_MODE_MANAGED_PER_CONTEXT,
"Ermöglicht die Auswahl der Regler, die in Menüs und während des Spiels verwendet werden sollen. Performance, Ondemand oder Schedutil werden während des Spiels empfohlen."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CPU_PERF_MODE_MAX_PERF,
"Maximale Leistung"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_VALUE_CPU_PERF_MODE_MAX_PERF,
"Immer maximale Leistung: höchste Frequenzen für bestes Erlebnis."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CPU_PERF_MODE_MIN_POWER,
"Minimaler Verbrauch"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_VALUE_CPU_PERF_MODE_MIN_POWER,
"Die niedrigste verfügbare Frequenz verwenden, um Strom zu sparen. Nützlich bei batteriebetriebenen Geräten, aber die Leistung wird deutlich reduziert."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CPU_PERF_MODE_BALANCED,
"Ausgewogen"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_VALUE_CPU_PERF_MODE_BALANCED,
"Passt sich an die aktuelle Arbeitsbelastung an. Funktioniert gut mit den meisten Geräten und Emulatoren und hilft Energie zu sparen. Anspruchsvolle Spiele und Cores könnten auf einigen Geräten einen Leistungsverlust erleiden."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CPU_POLICY_MIN_FREQ,
"Minimale Frequenz"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CPU_POLICY_MAX_FREQ,
"Maximale Frequenz"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CPU_MANAGED_MIN_FREQ,
"Minimale Kern-Frequenz"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CPU_MANAGED_MAX_FREQ,
"Maximale Kern-Frequenz"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CPU_POLICY_GOVERNOR,
"CPU-Regler"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CPU_POLICY_CORE_GOVERNOR,
"Kern-Regler"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CPU_POLICY_MENU_GOVERNOR,
"Menü-Regler"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_PAL60_ENABLE,
"Verwende PAL60-Modus"
@@ -10556,6 +10726,14 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_LOCALAP_ENABLE,
"Wi-Fi Access Point aktivieren oder deaktivieren."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_TIMEZONE,
"Zeitzone"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_TIMEZONE,
"Zeitzone wählen, um Datum und Zeit an Ihren Standort anzupassen."
)
MSG_HASH(
MSG_LOCALAP_SWITCHING_OFF,
"Wi-Fi Access Point wird ausgeschaltet."
@@ -10603,6 +10781,14 @@ MSG_HASH(
"X/Y-Skala für Wiimote Lichtpistolengeschwindigkeit anpassen."
)
#endif
MSG_HASH(
MENU_ENUM_LABEL_VALUE_INPUT_TOUCH_SCALE,
"Touchscreen-Skalierung"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_INPUT_TOUCH_SCALE,
"Die x/y-Skalierung der Touchscreen-Koordinaten an die Anzeigenskalierung des Betriebssystems anpassen."
)
#ifdef HAVE_ODROIDGO2
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_RGA_SCALING,
+378
View File
@@ -509,6 +509,10 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_CORE_DELETE_BACKUP_LIST,
"Αφαιρέστε ένα αρχείο από τη λίστα των αποθηκευμένων αντιγράφων ασφαλείας."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CORE_BACKUP_MODE_AUTO,
"[Αυτόματο]"
)
/* Main Menu > Information > System Information */
@@ -826,6 +830,10 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_RDB_ENTRY_ORIGIN,
"Προέλευση"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_RDB_ENTRY_FRANCHISE,
"Ομάδα"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_RDB_ENTRY_TGDB_RATING,
"Βαθμολογία TGDB"
@@ -1046,6 +1054,10 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_MENU_FILE_BROWSER_SETTINGS,
"Περιηγητής Αρχείων"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_MENU_FILE_BROWSER_SETTINGS,
"Αλλαγή ρυθμίσεων προγράμματος περιήγησης."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_FRAME_THROTTLE_SETTINGS,
"Περιορισμός Καρέ"
@@ -1376,6 +1388,16 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_POLLED,
"Ο ρυθμός ανανέωσης όπως αναφέρεται από τον οδηγό οθόνης."
)
#if defined(DINGUX) && defined(DINGUX_BETA)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_DINGUX_REFRESH_RATE,
"Κάθετος Ρυθμός Ανανέωσης"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_VIDEO_DINGUX_REFRESH_RATE,
"Ορίστε τον κάθετο ρυθμό ανανέωσης της οθόνης. Τα '50 Hz' θα ενεργοποιήσουν την ομαλή εκτέλεση βίντεο κατά την εκτέλεση του Ευρωπαϊκού περιεχομένου (PAL)."
)
#endif
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_FORCE_SRGB_DISABLE,
"Εξαναγκασμένη απενεργοποίηση sRGB FBO"
@@ -1475,9 +1497,25 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_VIDEO_VIEWPORT_CUSTOM_HEIGHT,
"Προτιμώμενο ύψος οπτικής γωνίας το οποίο χρησιμοποιείται εάν η Αναλογία Οθόνης είναι ορισμένη ως 'Προτιμώμενη'."
)
MSG_HASH(
MENU_ENUM_SUBLABEL_VIDEO_CROP_OVERSCAN,
"Αφαιρεί μερικά pixel γύρω από την εικόνα όπου εθιμικά οι προγραμματιστές άφηναν κενά ή και που περιέχουν άχρηστα pixel."
)
/* Settings > Video > Synchronization */
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_VSYNC,
"Κάθετος Συγχρονισμός (VSync)"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_ADAPTIVE_VSYNC,
"Προσαρμοστικός Συγχρονισμός Vsync"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_VIDEO_ADAPTIVE_VSYNC,
"Το V-Sync είναι ενεργό μέχρι τα καρέ να πέσουν χαμηλότερα από τα αναγραφόμενα. Με αυτόν το τρόπο θα μειωθεί το τραύλισμα της οθόνης (stuttering) όταν οι επιδόσεις χαμηλώνουν ενώ ταυτόχρονα είναι ενεργειακά πιο αποδοτικό."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_FRAME_DELAY,
"Καθυστέρηση Καρέ"
@@ -1768,6 +1806,14 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_INPUT_TURBO_DEFAULT_BUTTON,
"Προκαθορισμένο Πλήκτρο για Τούρμπο"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_INPUT_TURBO_FIRE_SETTINGS,
"Πλήκτρο \"Turbo\""
)
MSG_HASH(
MENU_ENUM_SUBLABEL_INPUT_TURBO_FIRE_SETTINGS,
"Αλλαγή ρυθμίσεων πλήκτου \"Turbo\"."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_INPUT_MENU_SETTINGS,
"Μενού Χειριστηρίων"
@@ -1815,6 +1861,26 @@ MSG_HASH(
/* Settings > Input > Hotkeys */
MSG_HASH(
MENU_ENUM_LABEL_VALUE_QUIT_PRESS_TWICE,
"Επιβεβαίωση Εξόδου"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_INPUT_META_FAST_FORWARD_KEY,
"Ταχεία Προώθηση (Εναλλαγή)"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_INPUT_META_FAST_FORWARD_HOLD_KEY,
"Ταχεία Προώθηση (Κράτημα Πλήκτρου)"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_INPUT_META_FAST_FORWARD_HOLD_KEY,
"Ενεργοποιεί την ταχεία προώθηση όταν παραμένει πατημένο. Το περιεχόμενο εκτελείται με κανονική ταχύτητα όταν το πλήκτρο απελευθερώνεται."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_INPUT_META_SLOWMOTION_KEY,
"Αργή Κίνηση (Εναλλαγή)"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_INPUT_META_SLOWMOTION_HOLD_KEY,
"Αργή Κίνηση (Πατημένο)"
@@ -1827,6 +1893,14 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_INPUT_META_SAVE_STATE_KEY,
"Αποθήκευση κατάστασης"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_INPUT_META_FULLSCREEN_TOGGLE_KEY,
"Πλήρης Οθόνη (Εναλλαγή)"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_INPUT_META_FULLSCREEN_TOGGLE_KEY,
"Εναλλαγή μεταξύ πλήρους οθόνης και λειτουργίας παραθύρου."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_INPUT_META_CLOSE_CONTENT_KEY,
"Κλείσιμο Περιεχομένου"
@@ -1851,10 +1925,26 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_INPUT_META_REWIND,
"Επιστροφή"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_INPUT_META_PAUSE_TOGGLE,
"Παύση (Εναλλαγή)"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_INPUT_META_PAUSE_TOGGLE,
"Εναλλάσσει εκτελούμενο περιεχόμενο μεταξύ καταστάσεων σε παύση και μη παύση."
)
MSG_HASH(
MENU_ENUM_SUBLABEL_INPUT_META_FRAMEADVANCE,
"Όταν το περιεχόμενο είναι σε παύση, το προάγει κατά ένα πλαίσιο (frame)."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_INPUT_META_RESET,
"Επαναφορά παιχνιδιού"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_INPUT_META_RESET,
"Αναπαραγωγή του τρέχοντος περιεχομένου από την αρχή."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_INPUT_META_SHADER_NEXT,
"Επόμενη σκίαση"
@@ -1871,14 +1961,34 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_INPUT_META_SCREENSHOT,
"Λήψη Στιγμιότυπου"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_INPUT_META_MUTE,
"Σίγαση Ήχου (Εναλλαγή)"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_INPUT_META_OSK,
"Πληκτρολόγιο Οθόνης (Εναλλαγή)"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_INPUT_META_OSK,
"Ενεργοποίηση/απενεργοποίηση πληκτρολόγιου οθόνης."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_INPUT_META_FPS_TOGGLE,
"Εμαφανίζει τα Καρέ ανά δευτερόλεπτο (FPS)"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_INPUT_META_FPS_TOGGLE,
"Ενεργοποίηση/Απενεργοποίηση της ένδειξης 'καρέ ανά δευτερόλεπτο'."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_INPUT_META_SEND_DEBUG_INFO,
"Αποστολή Πληροφοριων Εκσφαλματωσης (Debuging)"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_INPUT_META_SEND_DEBUG_INFO,
"Στέλνει διαγνωστικές πληροφορίες σχετικά με τη συσκευή σας και τις ρυθμίσεις του RetroArch στους διακομιστές μας για ανάλυση."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_INPUT_META_ENABLE_HOTKEY,
"Ενεργοποίηση Πλήκτρων Συντόμευσης"
@@ -2100,12 +2210,20 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_CHECK_FOR_MISSING_FIRMWARE,
"Έλεγχος για απών Firmware Πριν την Φόρτωση"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_CORE_INFO_CACHE_ENABLE,
"Διατήρηση τοπικής προσωρινής μνήμης των εγκατεστημένων βασικών πληροφοριών. Μειώνει σημαντικά τους χρόνους φόρτωσης σε πλατφόρμες με αργή πρόσβαση στο δίσκο."
)
#ifndef HAVE_DYNAMIC
#endif
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_ALLOW_ROTATE,
"Επίτρεψη περιστροφής"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CORE_MANAGER_LIST,
"Διαχείριση Πυρήνων"
)
/* Settings > Configuration */
@@ -2182,6 +2300,10 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_NAVIGATION_BROWSER_FILTER_SUPPORTED_EXTENSIONS_ENABLE,
"Φιλτράρισμα άγνωστων επεκτάσεων"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_NAVIGATION_BROWSER_FILTER_SUPPORTED_EXTENSIONS_ENABLE,
"Φιλτράρισμα αρχείων που εμφανίζονται στο πρόγραμμα περιήγησης αρχείων βάσει υποστηριζόμενων επεκτάσεων."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_USE_BUILTIN_PLAYER,
"Χρήση Ενσωματωμένου Αναπαραγωγέα Πολυμέσων Use Builtin Media Player"
@@ -2455,6 +2577,18 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_MENU_KIOSK_MODE_PASSWORD,
"Εισαγωγή Κωδικού για Απενεργοποίηση της Παιδικής Λειτουργίας"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MENU_SCREENSAVER_TIMEOUT,
"Χρονικό Όριο Προβολής Οθόνης Μενού"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_MENU_SCREENSAVER_TIMEOUT,
"Ενώ το μενού είναι ενεργό, η προφύλαξη οθόνης θα εμφανίζεται μετά την καθορισμένη περίοδο αδράνειας."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MENU_SCREENSAVER_ANIMATION_SNOW,
"Χιονόπτωση"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MOUSE_ENABLE,
"Υποστήριξη Ποντικιού"
@@ -2471,6 +2605,10 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_THREADED_DATA_RUNLOOP_ENABLE,
"Εκτέλεση εργασιών σε ξεχωριστό νήμα (Thread)."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MENU_SCROLL_DELAY,
"Καθυστέρηση Κύλισης Μενού"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_UI_COMPANION_ENABLE,
"UI Companion Enable"
@@ -2490,6 +2628,10 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_SETTINGS_VIEWS_SETTINGS,
"Ρυθμίσεις"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MENU_SHOW_ONLINE_UPDATER,
"Προβολή 'Διαδικτυακού Ενημερωτή'"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MENU_ADD_CONTENT_ENTRY_DISPLAY_MAIN_TAB,
"Κεντρικό Μενού"
@@ -2858,6 +3000,14 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_NETPLAY_DISCONNECT,
"Αποσύνδεση από τον διακομιστή netplay"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_NETPLAY_REFRESH_ROOMS,
"Ανανέωση Λίστας Διακομιστών Διαδικτυακού Παιχνιδιού"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_NETPLAY_REFRESH_ROOMS,
"Σάρωση για οικοδεσπότες netplay."
)
/* Netplay > Host */
@@ -3023,10 +3173,18 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_DELETE_ENTRY,
"Κατάργηση"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_DELETE_ENTRY,
"Αφαίρεση αυτής της καταχώρησης από τη λίστα αναπαραγωγής."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_ADD_TO_FAVORITES_PLAYLIST,
"Προσθήκη στα Αγαπημένα"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_SET_CORE_ASSOCIATION,
"Ορισμός Συσχέτισης Πυρήνα"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_RESET_CORE_ASSOCIATION,
"Επαναφορά Συσχέτισης Πυρήνα"
@@ -3035,6 +3193,18 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_INFORMATION,
"Πληροφορίες"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_INFORMATION,
"Προβολή περισσότερων πληροφορίών για το περιεχόμενο."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_DOWNLOAD_PL_ENTRY_THUMBNAILS,
"Λήψη Μικρογραφιών"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_DOWNLOAD_PL_ENTRY_THUMBNAILS,
"Λήψη στιγμιότυπου οθόνης/περίβλημα κουτιού/μικρογραφίες οθόνης τίτλου για το τρέχον περιεχόμενο. Ενημέρωση τυχόν υπάρχουσων μικρογραφιών."
)
/* Playlist Item > Set Core Association */
@@ -3792,6 +3962,14 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_RGUI_MENU_COLOR_THEME,
"Χρώμα Θέματος Μενού"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MENU_RGUI_PARTICLE_EFFECT_SCREENSAVER,
"Εφέ Φόντου Προφύλαξης Οθόνης"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_MENU_RGUI_PARTICLE_EFFECT_SCREENSAVER,
"Εμφάνιση εφέ σωματιδίων στο παρασκήνιο, ενώ το μενού είναι ενεργό."
)
/* RGUI: Settings Options */
@@ -4081,6 +4259,10 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_QT_MENU_FILE_LOAD_CORE,
"&Φόρτωση Πυρήνα..."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_QT_MENU_FILE_EXIT,
"Έξοδος"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_QT_MENU_EDIT,
"&Eπεξεργασία"
@@ -4125,6 +4307,10 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_QT_MENU_HELP_DOCUMENTATION,
"Εγχειρίδιο"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_QT_LOAD_CUSTOM_CORE,
"Φόρτωση Προσαρμοσμένου Πυρήνα..."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_QT_LOAD_CORE,
"Φόρτωση Πυρήνα΄"
@@ -4257,6 +4443,10 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_QT_SELECT_FOLDER,
"Επιλογή Φακέλου"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_QT_UPDATE_RETROARCH_NIGHTLY,
"Ενημέρωση RetroArch (Ασταθής Έκδοση)"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_QT_UPDATE_RETROARCH_FAILED,
"Αποτυχία Ενημέρωσης."
@@ -4404,6 +4594,10 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_USER,
"Χρήστης"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_USE_BUILTIN_IMAGE_VIEWER,
"Χρήση Ενσωματωμένου Προβολέα Εικόνων"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_MAX_SWAPCHAIN_IMAGES,
"Μέγιστες εικόνες swapchain"
@@ -5040,6 +5234,26 @@ MSG_HASH(
MSG_VIEWPORT_SIZE_CALCULATION_FAILED,
"Viewport size calculation failed! Will continue using raw data. This will probably not work right ..."
)
MSG_HASH(
MSG_VIRTUAL_DISK_TRAY_EJECT,
"εξάγει"
)
MSG_HASH(
MSG_VIRTUAL_DISK_TRAY_CLOSE,
"κλείσει"
)
MSG_HASH(
MSG_FAILED,
"απέτυχε"
)
MSG_HASH(
MSG_SUCCEEDED,
"επιτεύχθηκε"
)
MSG_HASH(
MSG_DEVICE_NOT_CONFIGURED,
"δεν έχει ρυθμιστεί"
)
MSG_HASH(
MSG_BLUETOOTH_SCAN_COMPLETE,
"Οκληρώθηκε η σάρωση Bluetooth."
@@ -5056,6 +5270,18 @@ MSG_HASH(
MSG_SCANNING_WIRELESS_NETWORKS,
"Σάρωση ασύρματων δικτύων..."
)
MSG_HASH(
MSG_ENABLING_WIRELESS,
"Ενεργοποίηση Wi-Fi..."
)
MSG_HASH(
MSG_DISABLING_WIRELESS,
"Απενεργοποίηση Wi-Fi..."
)
MSG_HASH(
MSG_DISCONNECTING_WIRELESS,
"Αποσύνδεση Wi-Fi..."
)
MSG_HASH(
MSG_NETPLAY_LAN_SCANNING,
"Σάρωση για οικοδεσπότες netplay..."
@@ -5088,6 +5314,10 @@ MSG_HASH(
MSG_INPUT_KIOSK_MODE_PASSWORD_NOK,
"Λανθασμένος κωδικός."
)
MSG_HASH(
MSG_SCANNING_OF_FILE_FINISHED,
"Η σάρωση του αρχείου ολοκληρώθηκε"
)
MSG_HASH(
MSG_DEVICE_CONFIGURED_IN_PORT,
"Διαμορφώθηκε στην θύρα:"
@@ -5100,14 +5330,110 @@ MSG_HASH(
MSG_RESAMPLER_QUALITY_NORMAL,
"Φυσιολογικά"
)
MSG_HASH(
MSG_NO_DISC_INSERTED,
"Δεν υπάρχει δίσκος στη μονάδα δίσκου."
)
MSG_HASH(
MSG_MANUAL_CONTENT_SCAN_INVALID_CONFIG,
"Μη έγκυρη διαμόρφωση χειροκίνητης σάρωσης"
)
MSG_HASH(
MSG_MANUAL_CONTENT_SCAN_INVALID_CONTENT,
"Δεν εντοπίστηκε έγκυρο περιεχόμενο"
)
MSG_HASH(
MSG_MANUAL_CONTENT_SCAN_START,
"Σάρωση περιεχομένου: "
)
MSG_HASH(
MSG_MANUAL_CONTENT_SCAN_IN_PROGRESS,
"Σάρωση: "
)
MSG_HASH(
MSG_MANUAL_CONTENT_SCAN_M3U_CLEANUP,
"Καθαρισμός καταχωρήσεων M3U: "
)
MSG_HASH(
MSG_MANUAL_CONTENT_SCAN_END,
"Η Σάρωση ολοκληρώθηκε: "
)
MSG_HASH(
MSG_CORE_BACKUP_SCANNING_CORE,
"Σάρωση πυρήνα: "
)
MSG_HASH(
MSG_CORE_BACKUP_ALREADY_EXISTS,
"Υπάρχει ήδη αντίγραφο ασφαλείας του εγκατεστημένου πυρήνα: "
)
MSG_HASH(
MSG_BACKING_UP_CORE,
"Δημιουργία αντίγραφου ασφαλείας πυρήνα: "
)
MSG_HASH(
MSG_PRUNING_CORE_BACKUP_HISTORY,
"Αφαίρεση παρωχημένων αντιγράφων ασφαλείας: "
)
MSG_HASH(
MSG_CORE_BACKUP_COMPLETE,
"Το αντίγραφο ασφαλείας του πυρήνα ολοκληρώθηκε: "
)
MSG_HASH(
MSG_CORE_RESTORATION_ALREADY_INSTALLED,
"Το επιλεγμένο αντίγραφο ασφαλείας πυρήνα είναι ήδη εγκατεστημένο: "
)
MSG_HASH(
MSG_RESTORING_CORE,
"Επαναφορά του πυρήνα: "
)
MSG_HASH(
MSG_CORE_RESTORATION_COMPLETE,
"Η επαναφορά του πυρήνα ολοκληρώθηκε: "
)
MSG_HASH(
MSG_CORE_INSTALLATION_ALREADY_INSTALLED,
"Το επιλεγμένο αρχείο πυρήνα είναι ήδη εγκατεστημένο: "
)
MSG_HASH(
MSG_INSTALLING_CORE,
"Εγκατάσταση πυρήνα: "
)
MSG_HASH(
MSG_CORE_INSTALLATION_COMPLETE,
"Η εγκατάσταση του πυρήνα ολοκληρώθηκε: "
)
MSG_HASH(
MSG_CORE_BACKUP_FAILED,
"Αποτυχία αντιγράφου ασφαλείας πυρήνα: "
)
MSG_HASH(
MSG_CORE_RESTORATION_FAILED,
"Αποτυχία αποκατάστασης πυρήνα: "
)
MSG_HASH(
MSG_CORE_INSTALLATION_FAILED,
"Αποτυχία Εγκατάστασης Πυρήνα: "
)
MSG_HASH(
MSG_CORE_RESTORATION_DISABLED,
"Η αποκατάσταση του πυρήνα απενεργοποιήθηκε - ο πυρήνας είναι κλειδωμένος: "
)
MSG_HASH(
MSG_CORE_INSTALLATION_DISABLED,
"Η εγκατάσταση του πυρήνα απενεργοποιήθηκε - ο πυρήνας είναι κλειδωμένος: "
)
MSG_HASH(
MSG_CORE_LOCK_FAILED,
"Αποτυχία κλειδώματος πυρήνα: "
)
MSG_HASH(
MSG_CORE_UNLOCK_FAILED,
"Αποτυχία ξεκλειδώματος πυρήνα: "
)
MSG_HASH(
MSG_CORE_DELETE_DISABLED,
"Η διαγραφή του πυρήνα απενεργοποιήθηκε - ο πυρήνας είναι κλειδωμένος: "
)
/* Lakka */
@@ -5158,6 +5484,18 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_WIFI_SETTINGS,
"Οδηγός Wi-Fi"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_WIFI_SETTINGS,
"Σάρωση για ασύρματα δίκτυα και δημιουργία σύνδεσης."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_WIFI_ENABLED,
"Ενεργοποίηση Wi-Fi"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_WIFI_DISCONNECT,
"Αποσύνδεση"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_VI_WIDTH,
"Ορισμός Πλάτους Οθόνης VI"
@@ -5186,6 +5524,26 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_INPUT_SMALL_KEYBOARD_ENABLE,
"Ενεργοποίηση Μικρού Πληκτρολογίου"
)
MSG_HASH(
MSG_INTERNET,
"Διαδίκτυο"
)
MSG_HASH(
MSG_LOCAL,
"Τοπικό"
)
MSG_HASH(
MSG_READ_WRITE,
"Ανάγνωση/Εγγραφή"
)
MSG_HASH(
MSG_READ_ONLY,
"Μόνο για Ανάγνωση"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_BRIGHTNESS_CONTROL,
"Φωτεινότητα Οθόνης"
)
#ifdef HAVE_LAKKA_SWITCH
MSG_HASH(
@@ -5232,6 +5590,18 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_SSH_ENABLE,
"Enable or disable remote command line access."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_LOCALAP_ENABLE,
"Σημείο Πρόσβασης Wi-Fi"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_TIMEZONE,
"Ζώνη ώρας"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_TIMEZONE,
"Επιλέξτε τη ζώνη ώρας σας για να προσαρμόσετε την ημερομηνία και την ώρα στην τοποθεσία σας."
)
#endif
#ifdef GEKKO
MSG_HASH(
@@ -5247,6 +5617,14 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_3DS_LCD_BOTTOM,
"Κάτω οθόνη 3DS"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CTR_VIDEO_MODE_2D_400X240,
"2D (Εφέ Πλέγματος Εικονοστοιχείων)"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CTR_VIDEO_MODE_2D_800X240,
"2D (Υψηλή Ανάλυση)"
)
#endif
#ifdef HAVE_QT
#endif
+2
View File
@@ -64,6 +64,8 @@
/* Settings > Video > Output */
#if defined(DINGUX) && defined(DINGUX_BETA)
#endif
/* Settings > Video > Fullscreen Mode */
+199 -1
View File
@@ -89,7 +89,7 @@ MSG_HASH(
)
MSG_HASH( /* FIXME Is a specific image format used? Is it determined automatically? User choice? */
MENU_ENUM_SUBLABEL_DUMP_DISC,
"Vuelca los contenidos del medio físico al almacenamiento interno. Se guardarán como un archivo de imagen."
"Vuelca los contenidos del disco físico al almacenamiento interno. Se guardarán como un archivo de imagen."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_PLAYLISTS_TAB,
@@ -1464,6 +1464,16 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_POLLED,
"Muestra la frecuencia de actualización que declara el controlador de vídeo."
)
#if defined(DINGUX) && defined(DINGUX_BETA)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_DINGUX_REFRESH_RATE,
"Frecuencia de actualización vertical"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_VIDEO_DINGUX_REFRESH_RATE,
"Ajusta la frecuencia de actualización vertical de la imagen. La opción 50 Hz mostrará una imagen más fluida al ejecutar contenidos en formato PAL."
)
#endif
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_FORCE_SRGB_DISABLE,
"Forzar desactivación del FBO sRGB"
@@ -2116,6 +2126,14 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_INPUT_TURBO_DEFAULT_BUTTON,
"Asigna el botón individual predeterminado para el modo turbo."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_INPUT_TURBO_FIRE_SETTINGS,
"Modo de turbo"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_INPUT_TURBO_FIRE_SETTINGS,
"Cambia los ajustes del modo turbo."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_INPUT_HAPTIC_FEEDBACK_SETTINGS,
"Respuesta háptica/vibración"
@@ -2812,6 +2830,14 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_CHECK_FOR_MISSING_FIRMWARE,
"Verifica que el firmware necesario esté disponible antes de cargar el contenido."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CORE_INFO_CACHE_ENABLE,
"Caché de archivos de información de núcleos"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_CORE_INFO_CACHE_ENABLE,
"Guarda una caché local con la información de los núcleos instalados. Reduce en gran medida los tiempos de carga en aquellas plataformas que tengan un acceso a disco lento."
)
#ifndef HAVE_DYNAMIC
MSG_HASH(
MENU_ENUM_LABEL_VALUE_ALWAYS_RELOAD_CORE_ON_RUN_CONTENT,
@@ -3644,6 +3670,14 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_NOTIFICATION_SHOW_CHEATS_APPLIED,
"Muestra un mensaje en pantalla al aplicar trucos."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_NOTIFICATION_SHOW_PATCH_APPLIED,
"Notificaciones de parches"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_NOTIFICATION_SHOW_PATCH_APPLIED,
"Muestra un mensaje en pantalla al parchear ROMs al vuelo."
)
MSG_HASH(
MENU_ENUM_SUBLABEL_NOTIFICATION_SHOW_AUTOCONFIG,
"Muestra un mensaje en pantalla al conectar o desconectar dispositivos de entrada."
@@ -3879,6 +3913,42 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_QUIT_ON_CLOSE_CONTENT,
"Cierra RetroArch automáticamente en cuanto se cierre un contenido. Al seleccionar «CLI», el programa se cerrará solo cuando el contenido haya sido iniciado mediante línea de comandos."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MENU_SCREENSAVER_TIMEOUT,
"Tiempo de espera del salvapantallas del menú"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_MENU_SCREENSAVER_TIMEOUT,
"Cuando el menú esté activo, se mostrará un salvapantallas una vez pase el periodo de inactividad especificado."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MENU_SCREENSAVER_ANIMATION,
"Animación del salvapantallas del menú"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_MENU_SCREENSAVER_ANIMATION,
"Cuando el salvapantallas del menú está activado, lo muestra con una animación. Esta opción afecta levemente al rendimiento."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MENU_SCREENSAVER_ANIMATION_SNOW,
"Nieve"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MENU_SCREENSAVER_ANIMATION_STARFIELD,
"Estrellas"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MENU_SCREENSAVER_ANIMATION_VORTEX,
"Vórtice"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MENU_SCREENSAVER_ANIMATION_SPEED,
"Velocidad de animación del salvapantallas del menú"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_MENU_SCREENSAVER_ANIMATION_SPEED,
"Ajusta la velocidad de la animación del salvapantallas del menú."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MOUSE_ENABLE,
"Soporte para ratón"
@@ -3927,6 +3997,14 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_MENU_SCROLL_FAST,
"Ajusta la velocidad máxima del cursor cuando se mantiene pulsada una dirección para desplazarse por un menú."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MENU_SCROLL_DELAY,
"Retraso al desplazar el menú"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_MENU_SCROLL_DELAY,
"Indica el retraso inicial (en milisegundos) entre que se pulsa un botón de dirección y el desplazamiento."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_UI_COMPANION_ENABLE,
"Interfaz asistente"
@@ -7588,6 +7666,14 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_RGUI_MENU_THEME_PRESET,
"Selecciona un preajuste de tema del menú en el explorador de archivos."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MENU_RGUI_TRANSPARENCY,
"Transparencia del menú"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_MENU_RGUI_TRANSPARENCY,
"Muestra el contenido ejecutado en el fondo del menú rápido. Desactivar la transparencia podría alterar los colores del tema."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MENU_RGUI_SHADOWS,
"Efecto de sombreado"
@@ -7612,6 +7698,14 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_MENU_RGUI_PARTICLE_EFFECT_SPEED,
"Ajusta la velocidad de los efectos de partículas de la animación de fondo."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MENU_RGUI_PARTICLE_EFFECT_SCREENSAVER,
"Animación de fondo del salvapantallas"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_MENU_RGUI_PARTICLE_EFFECT_SCREENSAVER,
"Cuando el salvapantallas del menú esté activo, se mostrarán partículas animadas en el fondo."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MENU_RGUI_INLINE_THUMBNAILS,
"Mostrar miniaturas de listas de reproducción"
@@ -9896,6 +9990,10 @@ MSG_HASH(
MSG_APPLYING_CHEAT,
"Aplicando cambios de los trucos."
)
MSG_HASH(
MSG_APPLYING_PATCH,
"Aplicando parche: %s"
)
MSG_HASH(
MSG_APPLYING_SHADER,
"Aplicando shader"
@@ -11246,6 +11344,90 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_SUSTAINED_PERFORMANCE_MODE,
"Modo de rendimiento sostenido"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CPU_PERFPOWER,
"Rendimiento y potencia de la CPU"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CPU_POLICY_ENTRY,
"Directiva"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CPU_PERF_MODE,
"Modo de regulación"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_VALUE_CPU_PERF_MODE_MANUAL,
"Permite ajustar de forma manual toda la CPU: regulador, frecuencias, etc. Recomendado solo para usuarios avanzados."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CPU_PERF_MODE_MANAGED_PERF,
"Rendimiento (administrado)"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_VALUE_CPU_PERF_MODE_MANAGED_PERF,
"El modo predeterminado y recomendado. Da un rendimiento total en juegos mientras ahorra energía al pausar la partida o navegar por los menús."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CPU_PERF_MODE_MANAGED_PER_CONTEXT,
"Administración personalizada"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_VALUE_CPU_PERF_MODE_MANAGED_PER_CONTEXT,
"Permite elegir qué reguladores se utilizarán dentro de los menús y durante una partida. Se recomienda usar rendimiento, Ondemand o Schedutil en partidas."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CPU_PERF_MODE_MAX_PERF,
"Rendimiento máximo"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_VALUE_CPU_PERF_MODE_MAX_PERF,
"Ofrece siempre el máximo rendimiento: las frecuencias más altas para una mejor experiencia."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CPU_PERF_MODE_MIN_POWER,
"Potencia mínima"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_VALUE_CPU_PERF_MODE_MIN_POWER,
"Utiliza la frecuencia más baja disponible para ahorrar energía. Ideal para dispositivos que usen baterías, pero reducirá el rendimiento significativamente."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CPU_PERF_MODE_BALANCED,
"Equilibrado"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_VALUE_CPU_PERF_MODE_BALANCED,
"Se adapta a la carga de trabajo actual. Ahorra energía y funciona bien con la mayoría de dispositivos y emuladores. Los juegos y núcleos más exigentes podrían sufrir bajadas de rendimiento en algunos dispositivos."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CPU_POLICY_MIN_FREQ,
"Frecuencia mínima"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CPU_POLICY_MAX_FREQ,
"Frecuencia máxima"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CPU_MANAGED_MIN_FREQ,
"Frecuencia mínima del núcleo"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CPU_MANAGED_MAX_FREQ,
"Frecuencia máxima del núcleo"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CPU_POLICY_GOVERNOR,
"Regulador de la CPU"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CPU_POLICY_CORE_GOVERNOR,
"Regulador de núcleo"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CPU_POLICY_MENU_GOVERNOR,
"Regulador de menús"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_PAL60_ENABLE,
"Usar modo PAL60"
@@ -11380,6 +11562,14 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_LOCALAP_ENABLE,
"Activa o desactiva el punto de acceso wifi."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_TIMEZONE,
"Zona horaria"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_TIMEZONE,
"Selecciona tu zona horaria para ajustar la fecha y hora a tu ubicación."
)
MSG_HASH(
MSG_LOCALAP_SWITCHING_OFF,
"Desconectando el punto de acceso wifi."
@@ -11427,6 +11617,14 @@ MSG_HASH(
"Ajusta la escala X/Y para la velocidad de las «lightguns» o pistolas usando el mando de Wii."
)
#endif
MSG_HASH(
MENU_ENUM_LABEL_VALUE_INPUT_TOUCH_SCALE,
"Escala del sensor táctil"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_INPUT_TOUCH_SCALE,
"Ajusta la escala X/Y de las coordenadas de la pantalla táctil para adaptarlas al escalado de la pantalla del SO."
)
#ifdef HAVE_ODROIDGO2
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_RGA_SCALING,
Loaded 100 of 180 files, more files were not shown because too many files have changed in this diff. Show more