mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-10 03:21:30 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6738fa57aa | ||
|
|
d40d7c389b | ||
|
|
3c2e3f1ce9 | ||
|
|
766c59d469 | ||
|
|
7492f5d60c | ||
|
|
bd9c35d379 | ||
|
|
5d4069cf8f | ||
|
|
0caeed6e1d | ||
|
|
98194ce135 | ||
|
|
4dcda032d0 | ||
|
|
08481e2a68 | ||
|
|
dc50ff584e | ||
|
|
3647bdef0f | ||
|
|
75fcd63b4a | ||
|
|
27cacffe54 | ||
|
|
74d85e3269 | ||
|
|
9d92888846 | ||
|
|
adfabcb742 | ||
|
|
620302ddaa | ||
|
|
da9f87c63f | ||
|
|
fe2fd18aad | ||
|
|
1c61068291 | ||
|
|
681983d1b2 | ||
|
|
ec627a9c00 | ||
|
|
462cedb654 | ||
|
|
f1347829c2 | ||
|
|
b08169255d | ||
|
|
610b0b7095 | ||
|
|
caf3f3dadd | ||
|
|
04962e1741 | ||
|
|
44e2530fbb | ||
|
|
52b827155f | ||
|
|
3eecf92f8f | ||
|
|
9184a9d04c | ||
|
|
5534fd7275 | ||
|
|
8e06aa2417 | ||
|
|
f57fcd4048 | ||
|
|
4b4576ed62 | ||
|
|
9f8092dc27 | ||
|
|
e40f0d308b | ||
|
|
2259e9b38e | ||
|
|
e44f0749a1 | ||
|
|
3fe52bb7fb | ||
|
|
2edc6d6e6a | ||
|
|
fce9da5e76 | ||
|
|
4b2f27eff8 | ||
|
|
7919a87b97 | ||
|
|
8584b122e1 | ||
|
|
ec7e528094 | ||
|
|
f0c9c542a2 | ||
|
|
4ed88ec68a | ||
|
|
140b8f5e0a | ||
|
|
b5df2b883a | ||
|
|
4fab84ca9e | ||
|
|
d8a5505204 | ||
|
|
b81ec7d631 | ||
|
|
f8479c2b5a | ||
|
|
69f3dda90a | ||
|
|
a06b303879 | ||
|
|
ef9beea9f8 | ||
|
|
cd8f6ede4e | ||
|
|
e76265e1aa | ||
|
|
6443d6f019 | ||
|
|
293e797146 | ||
|
|
55c855267e | ||
|
|
73a5ec9f42 | ||
|
|
86f8670d0b | ||
|
|
6772b89872 | ||
|
|
d95e46c2bd | ||
|
|
74d9f976a8 | ||
|
|
b1a3ea72c9 | ||
|
|
ab888629a5 | ||
|
|
a8028d5e8e | ||
|
|
cc1e080dfe | ||
|
|
35f7ef3cc2 | ||
|
|
c226bd87f4 | ||
|
|
c0b7c34264 | ||
|
|
5cf7b57064 | ||
|
|
adf6a7ff20 | ||
|
|
5a7cebf7da | ||
|
|
4a8fdaed84 | ||
|
|
4441219df9 | ||
|
|
750ad9aa7b | ||
|
|
0d7a028ce3 | ||
|
|
84975d71ea | ||
|
|
45c0dc4486 | ||
|
|
2c21e3df8b | ||
|
|
138f41160f | ||
|
|
194de13afb | ||
|
|
2c7c8def4c | ||
|
|
ff3928224e | ||
|
|
a39c38f267 | ||
|
|
8e54b4cec9 | ||
|
|
0dfb2b57fb | ||
|
|
a84c9022af | ||
|
|
0a5d4288f5 | ||
|
|
3c6ac487d0 | ||
|
|
466bd3be7f | ||
|
|
5dc7b7f7c7 | ||
|
|
3e814bc4b6 | ||
|
|
f70b6fbf58 | ||
|
|
30a90adc47 | ||
|
|
621a0b61f6 | ||
|
|
244a2ad052 | ||
|
|
bb04b0b9d3 | ||
|
|
e616b41b62 | ||
|
|
fb8c73508b | ||
|
|
f2a73d1b14 | ||
|
|
2b4f2525d0 | ||
|
|
75175ddbce | ||
|
|
4dcbd3e383 | ||
|
|
b48ab663b0 | ||
|
|
364d518726 | ||
|
|
d1c7d97b10 | ||
|
|
f8b6443b91 | ||
|
|
8344ed9db5 | ||
|
|
76f3d0c1b7 | ||
|
|
52af69e6cd | ||
|
|
d6b1836919 | ||
|
|
7c483340fc | ||
|
|
33de17548b | ||
|
|
9cd19d4944 | ||
|
|
89e4cac3f1 | ||
|
|
29e9027ec0 | ||
|
|
68c377cd55 | ||
|
|
706ab17b2c | ||
|
|
fede9eee4b | ||
|
|
d84a2ef700 | ||
|
|
4ba92b1643 | ||
|
|
57a728fd41 | ||
|
|
bc8f98358b | ||
|
|
3887db349e | ||
|
|
98448960f5 | ||
|
|
22a2b5ac1c | ||
|
|
6e4620cebc | ||
|
|
458d636861 | ||
|
|
64c01e620d | ||
|
|
57c4802ab4 | ||
|
|
74f1bf59b9 | ||
|
|
cacbd451dc | ||
|
|
7174563f7d | ||
|
|
d5f862ee07 | ||
|
|
f66d4ea33c | ||
|
|
0797fd957a | ||
|
|
b45acf2223 | ||
|
|
30143f15f2 | ||
|
|
24389bbcd5 | ||
|
|
2e50b57643 | ||
|
|
5b995dec26 | ||
|
|
5ae4524176 | ||
|
|
87a7795331 | ||
|
|
44fc3666ed | ||
|
|
334a43a7c5 | ||
|
|
29475a904f | ||
|
|
159602d697 | ||
|
|
de88edf56c | ||
|
|
4fb6bc9f33 | ||
|
|
b82a20ea0c | ||
|
|
8291b18771 | ||
|
|
181824eb00 | ||
|
|
7a2115670a | ||
|
|
951999e5ce | ||
|
|
23acacf436 | ||
|
|
925c459aed | ||
|
|
0482892a9c | ||
|
|
dd922bfc2c | ||
|
|
35543ecd91 | ||
|
|
e08290acb0 | ||
|
|
e34c84d791 | ||
|
|
2e4b734159 | ||
|
|
3b64752f4f | ||
|
|
9829051841 | ||
|
|
fc2a011a5c | ||
|
|
0e7ff6ea4e | ||
|
|
e784cc1aaf | ||
|
|
e4a8f42ad1 | ||
|
|
a04fe66032 | ||
|
|
9669cff16b | ||
|
|
09439332f3 | ||
|
|
216deda910 | ||
|
|
7bbdd6d18a | ||
|
|
b037da7264 | ||
|
|
ef35adc9d6 | ||
|
|
f6a8480f21 | ||
|
|
d3b3b40ea7 | ||
|
|
6972cc2bc6 | ||
|
|
74cef32c1d | ||
|
|
d50be7a0ab | ||
|
|
d228e5f759 | ||
|
|
10546594de | ||
|
|
e9b5f009cc | ||
|
|
d6ec36c707 | ||
|
|
be84700d0a | ||
|
|
ad5b2d614a | ||
|
|
9e87040c02 | ||
|
|
90abd49892 | ||
|
|
654aae58d6 | ||
|
|
e6adca7679 | ||
|
|
095f6d4377 | ||
|
|
b946b146f3 | ||
|
|
4e0f8bf621 | ||
|
|
de4343caeb | ||
|
|
a9426925cf | ||
|
|
aebdc8b747 | ||
|
|
c9d4775ffc | ||
|
|
3b8bd1a6e8 | ||
|
|
734401440d | ||
|
|
9fbf3b9732 | ||
|
|
67b180d63d | ||
|
|
c378665af5 | ||
|
|
3187c87451 | ||
|
|
122be0cc64 | ||
|
|
6b13e3f050 | ||
|
|
87e248f4eb | ||
|
|
2be7106055 | ||
|
|
0a24f5f38a | ||
|
|
6189fd3f42 | ||
|
|
29d5a692d0 | ||
|
|
358cafa978 | ||
|
|
b3eebbd5ec | ||
|
|
cc5b491e21 | ||
|
|
bf0b9f536a | ||
|
|
6f471e0660 | ||
|
|
484c4ad51e | ||
|
|
66c77af4a6 | ||
|
|
667568b0b8 | ||
|
|
f3ff36ac34 | ||
|
|
feaefbef04 | ||
|
|
a16aba2138 | ||
|
|
50d27fada9 | ||
|
|
a6d92de0b2 | ||
|
|
89391ccf9a | ||
|
|
e430f832cc | ||
|
|
9280340fe0 | ||
|
|
85466225de | ||
|
|
c9aaf598bb | ||
|
|
92e795aa28 | ||
|
|
fa2c789c3f | ||
|
|
475213aa0a | ||
|
|
32d6eeaf07 | ||
|
|
6f95e7819a | ||
|
|
f24f827c4c | ||
|
|
53a266728b | ||
|
|
215ed2ccf6 | ||
|
|
a671b6394c | ||
|
|
dec1fd097c | ||
|
|
a0f0d7bed2 | ||
|
|
6a21eca5df | ||
|
|
c37f540b72 | ||
|
|
5aba614b60 | ||
|
|
0680505ebf | ||
|
|
528af55abd | ||
|
|
1167dde73a | ||
|
|
b3abdefb9a | ||
|
|
1b0ae7a0b2 | ||
|
|
f3daf070e1 | ||
|
|
6241909a08 | ||
|
|
7fbb7a8007 | ||
|
|
e96cbcac04 | ||
|
|
0706cdea4c | ||
|
|
07099797bd | ||
|
|
77967f0148 | ||
|
|
56e40e8204 | ||
|
|
218c6f30b9 | ||
|
|
b2c20e8ce0 | ||
|
|
76eeee2c9a | ||
|
|
3b7e0a8db1 | ||
|
|
c5e5cf28af | ||
|
|
8ff0430370 |
No files matched your search
@@ -9,6 +9,8 @@
|
||||
*.map
|
||||
*.swp
|
||||
*.cache
|
||||
*.gcda
|
||||
*.gcno
|
||||
.tmp
|
||||
.tmp.c
|
||||
.tmp.cxx
|
||||
|
||||
+61
@@ -1,5 +1,66 @@
|
||||
# Future
|
||||
|
||||
# 1.9.5
|
||||
- ALSATHREAD: Make alsathread default for all ALSA devices with threads
|
||||
- ARCHIVE: Fix loading of archived content with file names containing '#' characters
|
||||
- CHEEVOS: Upgrade to rcheevos 10.1
|
||||
- CHEEVOS: Challenge indicators
|
||||
- CHEEVOS: Group achievements by category in quick menu
|
||||
- CHEEVOS: Relabel 'Start Active' with 'Encore Mode'
|
||||
- D3D10: Window title should now update
|
||||
- D3D11: Window title should now update
|
||||
- D3D11: Allow fastforward in fullscreen
|
||||
- D3D12: Window title should now update
|
||||
- D3D12: Allow fastforward in fullscreen
|
||||
- CRT/SWITCHRES: New implementation
|
||||
- FONTS: Improve message wrapping with CJK languages
|
||||
- FONTS: Fix garbled characters when converting encodings
|
||||
- INPUT: Allow the 8 analog stick directions to be used as keys for core keyboard mappings
|
||||
- LIBRETRO: Add API extension for setting 'need_fullpath' based on content file extension and to request persistent frontend content data buffers
|
||||
- MENU/SEARCH: Add enhanced search functionality to the 'Manage Cores' menu
|
||||
- OPENDINGUX: Fix black screens when triggering gfx driver initialisation via menu actions
|
||||
- UNIX: Get better battery stats on sysfs nodes
|
||||
- VIDEO: Extend Frame Delay range to 19 to accommodate PAL land too
|
||||
- WIFI/LAKKA: Add nmcli to wifi drivers
|
||||
- WIFI/LAKKA: Add wifi configuration menu
|
||||
- X11: fix fullscreen when swapping monitors/resolution
|
||||
|
||||
# 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
|
||||
- PSTV: Fix scaling on PSTV when not using 720p
|
||||
- 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
|
||||
|
||||
+47
-4
@@ -275,6 +275,10 @@ OBJ += \
|
||||
$(LIBRETRO_COMM_DIR)/memmap/memalign.o \
|
||||
$(LIBRETRO_COMM_DIR)/file/nbio/nbio_stdio.o
|
||||
|
||||
OBJ += \
|
||||
$(LIBRETRO_COMM_DIR)/lists/linked_list.o \
|
||||
$(LIBRETRO_COMM_DIR)/queues/generic_queue.o
|
||||
|
||||
ifneq ($(findstring Linux,$(OS)),)
|
||||
OBJ += $(LIBRETRO_COMM_DIR)/file/nbio/nbio_linux.o
|
||||
endif
|
||||
@@ -300,7 +304,6 @@ OBJ += \
|
||||
input/common/input_hid_common.o \
|
||||
led/led_driver.o \
|
||||
gfx/video_coord_array.o \
|
||||
gfx/video_crt_switch.o \
|
||||
gfx/gfx_display.o \
|
||||
gfx/gfx_animation.o \
|
||||
gfx/gfx_thumbnail_path.o \
|
||||
@@ -764,6 +767,12 @@ endif
|
||||
|
||||
ifeq ($(HAVE_LAKKA), 1)
|
||||
OBJ += wifi/drivers/connmanctl.o
|
||||
OBJ += misc/cpufreq/cpufreq.o
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_WIFI), 1)
|
||||
OBJ += wifi/drivers/nmcli.o
|
||||
DEFINES += -DHAVE_WIFI
|
||||
endif
|
||||
|
||||
# Audio
|
||||
@@ -1985,7 +1994,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)
|
||||
@@ -2101,8 +2111,7 @@ ifeq ($(HAVE_NETWORKING), 1)
|
||||
INCLUDE_DIRS += -Ideps/rcheevos/include
|
||||
|
||||
OBJ += cheevos/cheevos.o \
|
||||
cheevos/badges.o \
|
||||
cheevos/cheevos_memory.o \
|
||||
cheevos/cheevos_menu.o \
|
||||
cheevos/cheevos_parser.o \
|
||||
$(LIBRETRO_COMM_DIR)/formats/cdfs/cdfs.o \
|
||||
deps/rcheevos/src/rcheevos/alloc.o \
|
||||
@@ -2114,6 +2123,7 @@ ifeq ($(HAVE_NETWORKING), 1)
|
||||
deps/rcheevos/src/rcheevos/lboard.o \
|
||||
deps/rcheevos/src/rcheevos/memref.o \
|
||||
deps/rcheevos/src/rcheevos/operand.o \
|
||||
deps/rcheevos/src/rcheevos/rc_libretro.o \
|
||||
deps/rcheevos/src/rcheevos/richpresence.o \
|
||||
deps/rcheevos/src/rcheevos/runtime.o \
|
||||
deps/rcheevos/src/rcheevos/runtime_progress.o \
|
||||
@@ -2263,6 +2273,39 @@ ifeq ($(HAVE_FFMPEG), 1)
|
||||
INCLUDE_DIRS += -Iffmpeg
|
||||
endif
|
||||
|
||||
# CRT mode switching
|
||||
ifeq ($(HAVE_CRTSWITCHRES), 1)
|
||||
INCLUDE_DIRS += -I$(DEPS_DIR)/switchres
|
||||
OBJ += gfx/video_crt_switch.o \
|
||||
$(DEPS_DIR)/switchres/monitor.o \
|
||||
$(DEPS_DIR)/switchres/modeline.o \
|
||||
$(DEPS_DIR)/switchres/switchres.o \
|
||||
$(DEPS_DIR)/switchres/display.o \
|
||||
$(DEPS_DIR)/switchres/custom_video.o \
|
||||
$(DEPS_DIR)/switchres/log.o \
|
||||
$(DEPS_DIR)/switchres/switchres_wrapper.o \
|
||||
$(DEPS_DIR)/switchres/edid.o
|
||||
ifneq ($(findstring Win32,$(OS)),)
|
||||
OBJ += $(DEPS_DIR)/switchres/display_windows.o \
|
||||
$(DEPS_DIR)/switchres/custom_video_ati_family.o \
|
||||
$(DEPS_DIR)/switchres/custom_video_ati.o \
|
||||
$(DEPS_DIR)/switchres/custom_video_adl.o \
|
||||
$(DEPS_DIR)/switchres/custom_video_pstrip.o \
|
||||
$(DEPS_DIR)/switchres/resync_windows.o
|
||||
endif
|
||||
ifneq ($(findstring Linux,$(OS)),)
|
||||
OBJ += $(DEPS_DIR)/switchres/display_linux.o
|
||||
ifeq ($(HAVE_X11)$(HAVE_XRANDR), 11)
|
||||
OBJ += $(DEPS_DIR)/switchres/custom_video_xrandr.o
|
||||
DEFINES += -DSR_WITH_XRANDR
|
||||
endif
|
||||
endif
|
||||
ifneq ($(findstring Win32,$(OS)),)
|
||||
DEFINES += -DSR_WIN32_STATIC
|
||||
endif
|
||||
LIBS += -lstdc++
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_COMPRESSION), 1)
|
||||
DEFINES += -DHAVE_COMPRESSION
|
||||
OBJ += tasks/task_decompress.o
|
||||
|
||||
+1
-1
@@ -490,7 +490,7 @@ else ifeq ($(platform), windows_msvc2005_x86)
|
||||
CXX = cl.exe
|
||||
LD = link.exe
|
||||
|
||||
PLATCFLAGS += -D_WIN32 -D_WIN32_WINNT=0x0410 -D__STDC_CONSTANT_MACROS -D_MBCS -D__SSE__
|
||||
PLATCFLAGS += -D_WIN32 -D_WIN32_WINNT=0x0410 -D__STDC_CONSTANT_MACROS -D_MBCS
|
||||
PLATCFLAGS += -DHAVE_OPENGL -DHAVE_OPENGL1 -DHAVE_GLSL -DHAVE_OZONE -DHAVE_GFX_WIDGETS -DHAVE_CC_RESAMPLER
|
||||
PLATCFLAGS += -DHAVE_ONLINE_UPDATER -DHAVE_UPDATE_ASSETS -DHAVE_UPDATE_CORES -DHAVE_CDROM
|
||||
PLATCFLAGS += -bigobj
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+2
-2
@@ -19,7 +19,7 @@ ifeq ($(WHOLE_ARCHIVE_LINK), 1)
|
||||
WHOLE_END := -Wl,--no-whole-archive
|
||||
endif
|
||||
|
||||
INCDIR = deps deps/stb deps/7zip deps/pthreads deps/pthreads/platform/psp deps/pthreads/platform/helper libretro-common/include libretro-common/include/compat/zlib
|
||||
INCDIR = deps deps/stb deps/7zip libretro-common/include libretro-common/include/compat/zlib
|
||||
CFLAGS = $(OPTIMIZE_LV) -G0 -std=gnu99 -ffast-math -fsingle-precision-constant
|
||||
ASFLAGS = $(CFLAGS)
|
||||
|
||||
@@ -27,7 +27,7 @@ RARCH_DEFINES = -DPSP -D_MIPS_ARCH_ALLEGREX -DHAVE_LANGEXTRA -DHAVE_ZLIB -DHAVE_
|
||||
|
||||
LIBDIR =
|
||||
LDFLAGS =
|
||||
LIBS = $(WHOLE_START) -lretro_psp1 $(WHOLE_END) -lstdc++ -lpspgu -lpspgum -lm -lpspaudio -lpspfpu -lpsppower -lpsprtc
|
||||
LIBS = $(WHOLE_START) -lretro_psp1 $(WHOLE_END) -lstdc++ -lpspgu -lpspgum -lm -lpspaudio -lpspfpu -lpsppower -lpsprtc -lpthread-psp
|
||||
|
||||
ifeq ($(HAVE_THREADS), 1)
|
||||
RARCH_DEFINES += -DHAVE_THREADS
|
||||
|
||||
+6
-4
@@ -13,7 +13,7 @@ PC_DEVELOPMENT_UDP_PORT =
|
||||
OBJ :=
|
||||
|
||||
SCE_LIBC_SIZE := 4194304
|
||||
DEFINES := -DHAVE_ONLINE_UPDATER -DHAVE_UPDATE_ASSETS
|
||||
DEFINES := -DHAVE_ONLINE_UPDATER -DHAVE_UPDATE_ASSETS -DHAVE_STB_VORBIS
|
||||
INCDIRS :=
|
||||
|
||||
ifeq ($(GRIFFIN_BUILD), 1)
|
||||
@@ -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
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include <retro_inline.h>
|
||||
|
||||
#include "../../retroarch.h"
|
||||
#include "../../defines/gx_defines.h"
|
||||
#include <defines/gx_defines.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
||||
@@ -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,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>
|
||||
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2015-2016 - Andre Leiradella
|
||||
*
|
||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <file/file_path.h>
|
||||
|
||||
#include "../file_path_special.h"
|
||||
#include "../configuration.h"
|
||||
#include "../gfx/gfx_display.h"
|
||||
|
||||
#include "badges.h"
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
|
||||
#define CHEEVOS_MENU_BADGE_LIMIT 256
|
||||
/* TODO/FIXME - public global variables */
|
||||
static uintptr_t cheevos_badge_menu_texture_list[CHEEVOS_MENU_BADGE_LIMIT] = { 0 };
|
||||
|
||||
void cheevos_reset_menu_badges(void)
|
||||
{
|
||||
int index;
|
||||
for (index = 0; index < CHEEVOS_MENU_BADGE_LIMIT; ++index)
|
||||
{
|
||||
if (cheevos_badge_menu_texture_list[index])
|
||||
video_driver_texture_unload(&cheevos_badge_menu_texture_list[index]);
|
||||
}
|
||||
|
||||
memset(&cheevos_badge_menu_texture_list, 0,
|
||||
sizeof(cheevos_badge_menu_texture_list));
|
||||
}
|
||||
|
||||
void cheevos_set_menu_badge(int index, const char *badge, bool locked)
|
||||
{
|
||||
settings_t *settings = config_get_ptr();
|
||||
|
||||
if (index >= CHEEVOS_MENU_BADGE_LIMIT)
|
||||
return;
|
||||
|
||||
if (!settings || !settings->bools.cheevos_badges_enable)
|
||||
cheevos_badge_menu_texture_list[index] = 0;
|
||||
else
|
||||
cheevos_badge_menu_texture_list[index] =
|
||||
cheevos_get_badge_texture(badge, locked);
|
||||
}
|
||||
|
||||
uintptr_t cheevos_get_menu_badge_texture(int index)
|
||||
{
|
||||
if (index < CHEEVOS_MENU_BADGE_LIMIT)
|
||||
return cheevos_badge_menu_texture_list[index];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
uintptr_t cheevos_get_badge_texture(const char *badge, bool locked)
|
||||
{
|
||||
char badge_file[24];
|
||||
char fullpath[PATH_MAX_LENGTH];
|
||||
uintptr_t tex = 0;
|
||||
|
||||
if (!badge)
|
||||
return 0;
|
||||
|
||||
fullpath[0] = badge_file[0] = '\0';
|
||||
|
||||
strlcpy(badge_file, badge, sizeof(badge_file));
|
||||
if (locked)
|
||||
strlcat(badge_file, "_lock", sizeof(badge_file));
|
||||
strlcat(badge_file, FILE_PATH_PNG_EXTENSION, sizeof(badge_file));
|
||||
|
||||
fill_pathname_application_special(fullpath, sizeof(fullpath),
|
||||
APPLICATION_SPECIAL_DIRECTORY_THUMBNAILS_CHEEVOS_BADGES);
|
||||
|
||||
if (!gfx_display_reset_textures_list(badge_file, fullpath,
|
||||
&tex, TEXTURE_FILTER_MIPMAP_LINEAR, NULL, NULL))
|
||||
tex = 0;
|
||||
|
||||
return tex;
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2015-2018 - Andre Leiradella
|
||||
*
|
||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __RARCH_CHEEVOS_BADGE_H
|
||||
#define __RARCH_CHEEVOS_BADGE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <retro_common_api.h>
|
||||
#include <boolean.h>
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
void cheevos_reset_menu_badges(void);
|
||||
void cheevos_set_menu_badge(int index, const char *badge, bool locked);
|
||||
uintptr_t cheevos_get_menu_badge_texture(int index);
|
||||
#endif
|
||||
|
||||
uintptr_t cheevos_get_badge_texture(const char* badge, bool locked);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
||||
+329
-677
File diff suppressed because it is too large.
Load diff
+8
-33
@@ -21,64 +21,39 @@
|
||||
|
||||
#include <boolean.h>
|
||||
|
||||
#include "../verbosity.h"
|
||||
|
||||
#include <retro_common_api.h>
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
typedef struct rcheevos_ctx_desc
|
||||
{
|
||||
unsigned idx;
|
||||
char *s;
|
||||
size_t len;
|
||||
} rcheevos_ctx_desc_t;
|
||||
|
||||
enum
|
||||
{
|
||||
RCHEEVOS_ACTIVE_SOFTCORE = 1 << 0,
|
||||
RCHEEVOS_ACTIVE_HARDCORE = 1 << 1,
|
||||
RCHEEVOS_ACTIVE_UNOFFICIAL = 1 << 2
|
||||
};
|
||||
|
||||
bool rcheevos_load(const void *data);
|
||||
size_t rcheevos_get_serialize_size(void);
|
||||
bool rcheevos_get_serialized_data(void* buffer);
|
||||
bool rcheevos_set_serialized_data(void* buffer);
|
||||
|
||||
void rcheevos_reset_game(bool widgets_ready);
|
||||
|
||||
void rcheevos_populate_menu(void* data);
|
||||
void rcheevos_populate_hardcore_pause_menu(void* data);
|
||||
void rcheevos_get_achievement_state(unsigned index, char* buffer, size_t buffer_size);
|
||||
|
||||
bool rcheevos_get_description(rcheevos_ctx_desc_t *desc);
|
||||
|
||||
void rcheevos_pause_hardcore(void);
|
||||
|
||||
bool rcheevos_unload(void);
|
||||
|
||||
void rcheevos_test(void);
|
||||
|
||||
void rcheevos_reset_game(bool widgets_ready);
|
||||
|
||||
void rcheevos_pause_hardcore(void);
|
||||
void rcheevos_hardcore_enabled_changed(void);
|
||||
void rcheevos_toggle_hardcore_paused(void);
|
||||
bool rcheevos_hardcore_active(void);
|
||||
|
||||
void rcheevos_validate_config_settings(void);
|
||||
|
||||
void rcheevos_leaderboards_enabled_changed(void);
|
||||
|
||||
void rcheevos_test(void);
|
||||
|
||||
void rcheevos_set_support_cheevos(bool state);
|
||||
|
||||
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);
|
||||
uintptr_t rcheevos_get_badge_texture(const char *badge, bool locked);
|
||||
|
||||
uint8_t* rcheevos_patch_address(unsigned address);
|
||||
|
||||
bool rcheevos_hardcore_active(void);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif /* __RARCH_CHEEVOS_CHEEVOS_H */
|
||||
@@ -0,0 +1,158 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2015-2018 - Andre Leiradella
|
||||
* Copyright (C) 2019-2021 - Brian Weiss
|
||||
*
|
||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __RARCH_CHEEVOS_LOCALS_H
|
||||
#define __RARCH_CHEEVOS_LOCALS_H
|
||||
|
||||
#include "../deps/rcheevos/include/rc_runtime.h"
|
||||
|
||||
#include "../deps/rcheevos/src/rcheevos/rc_libretro.h"
|
||||
|
||||
#include <../command.h>
|
||||
#include <../verbosity.h>
|
||||
#include <boolean.h>
|
||||
#include <queues/task_queue.h>
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
#include <rthreads/rthreads.h>
|
||||
#endif
|
||||
|
||||
#include <retro_common_api.h>
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
/************************************************************************
|
||||
* Logging *
|
||||
************************************************************************/
|
||||
|
||||
/* Define this macro to get extra-verbose log for cheevos. */
|
||||
#define CHEEVOS_VERBOSE
|
||||
|
||||
#define RCHEEVOS_TAG "[RCHEEVOS]: "
|
||||
#define CHEEVOS_FREE(p) do { void* q = (void*)p; if (q) free(q); } while (0)
|
||||
|
||||
#ifdef CHEEVOS_VERBOSE
|
||||
#define CHEEVOS_LOG RARCH_LOG
|
||||
#define CHEEVOS_ERR RARCH_ERR
|
||||
#else
|
||||
void rcheevos_log(const char *fmt, ...);
|
||||
#define CHEEVOS_LOG rcheevos_log
|
||||
#define CHEEVOS_ERR RARCH_ERR
|
||||
#endif
|
||||
|
||||
/************************************************************************
|
||||
* State *
|
||||
************************************************************************/
|
||||
|
||||
enum
|
||||
{
|
||||
RCHEEVOS_ACTIVE_SOFTCORE = 1 << 0,
|
||||
RCHEEVOS_ACTIVE_HARDCORE = 1 << 1,
|
||||
RCHEEVOS_ACTIVE_UNOFFICIAL = 1 << 2
|
||||
};
|
||||
|
||||
typedef struct rcheevos_racheevo_t
|
||||
{
|
||||
const char* title;
|
||||
const char* description;
|
||||
const char* badge;
|
||||
const char* memaddr;
|
||||
unsigned id;
|
||||
unsigned points;
|
||||
|
||||
retro_time_t unlock_time;
|
||||
uint8_t active;
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
uint8_t menu_bucket;
|
||||
uint8_t menu_progress;
|
||||
uint8_t menu_badge_grayscale;
|
||||
uintptr_t menu_badge_texture;
|
||||
#endif
|
||||
|
||||
} rcheevos_racheevo_t;
|
||||
|
||||
typedef struct rcheevos_ralboard_t
|
||||
{
|
||||
const char* title;
|
||||
const char* description;
|
||||
const char* mem;
|
||||
unsigned id;
|
||||
unsigned format;
|
||||
} rcheevos_ralboard_t;
|
||||
|
||||
typedef struct rcheevos_rapatchdata_t
|
||||
{
|
||||
char* title;
|
||||
rcheevos_racheevo_t* core;
|
||||
rcheevos_racheevo_t* unofficial;
|
||||
rcheevos_ralboard_t* lboards;
|
||||
char* richpresence_script;
|
||||
|
||||
unsigned game_id;
|
||||
unsigned console_id;
|
||||
unsigned core_count;
|
||||
unsigned unofficial_count;
|
||||
unsigned lboard_count;
|
||||
} rcheevos_rapatchdata_t;
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
|
||||
typedef struct rcheevos_menuitem_t
|
||||
{
|
||||
rcheevos_racheevo_t* cheevo;
|
||||
enum msg_hash_enums state_label_idx;
|
||||
} rcheevos_menuitem_t;
|
||||
|
||||
void rcheevos_menu_reset_badges(void);
|
||||
|
||||
#endif
|
||||
|
||||
typedef struct rcheevos_locals_t
|
||||
{
|
||||
rc_runtime_t runtime; /* rcheevos runtime state */
|
||||
rcheevos_rapatchdata_t patchdata; /* achievement/leaderboard data from the server */
|
||||
rc_libretro_memory_regions_t memory;/* achievement addresses to core memory mappings */
|
||||
|
||||
retro_task_t* task; /* load task */
|
||||
#ifdef HAVE_THREADS
|
||||
slock_t* task_lock; /* mutex for starting/stopping load task */
|
||||
enum event_command queued_command; /* action queued by background thread to be run on main thread */
|
||||
#endif
|
||||
|
||||
char token[32]; /* user's session token */
|
||||
char hash[33]; /* retroachievements hash for current content */
|
||||
char user_agent_prefix[128]; /* RetroArch/OS version information */
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
rcheevos_menuitem_t* menuitems; /* array of items for the achievements quick menu */
|
||||
unsigned menuitem_capacity; /* maximum number of items in the menuitems array */
|
||||
unsigned menuitem_count; /* current number of items in the menuitems array */
|
||||
#endif
|
||||
|
||||
bool hardcore_active; /* hardcore functionality is active */
|
||||
bool loaded; /* load task has completed */
|
||||
bool core_supports; /* false if core explicitly disables achievements */
|
||||
bool leaderboards_enabled; /* leaderboards are enabled */
|
||||
bool leaderboard_notifications; /* leaderboard notifications are enabled */
|
||||
bool leaderboard_trackers; /* leaderboard trackers are enabled */
|
||||
} rcheevos_locals_t;
|
||||
|
||||
rcheevos_locals_t* get_rcheevos_locals();
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif /* __RARCH_CHEEVOS_LOCALS_H */
|
||||
@@ -1,352 +0,0 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2015-2016 - Andre Leiradella
|
||||
*
|
||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "cheevos_memory.h"
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#include "../retroarch.h"
|
||||
#include "../verbosity.h"
|
||||
|
||||
#include "../deps/rcheevos/include/rcheevos.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
uint8_t* rcheevos_memory_find(
|
||||
const rcheevos_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 ®ions->data[i][address];
|
||||
}
|
||||
|
||||
address -= size;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char* rcheevos_memory_type(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 rcheevos_memory_register_region(rcheevos_memory_regions_t* regions,
|
||||
int type, uint8_t* data, size_t size, const char* description)
|
||||
{
|
||||
if (size == 0)
|
||||
return;
|
||||
|
||||
if (regions->count == MAX_MEMORY_REGIONS)
|
||||
{
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "Too many memory memory regions to register\n");
|
||||
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;
|
||||
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "Registered 0x%04X bytes of %s at $%06X (%s)\n", (unsigned)size,
|
||||
rcheevos_memory_type(type), (unsigned)(regions->total_size - size), description);
|
||||
}
|
||||
|
||||
static void rcheevos_memory_init_without_regions(
|
||||
rcheevos_memory_regions_t* regions)
|
||||
{
|
||||
/* no regions specified, assume system RAM followed by save RAM */
|
||||
char description[64];
|
||||
retro_ctx_memory_info_t meminfo;
|
||||
|
||||
snprintf(description, sizeof(description), "offset 0x%06x", 0);
|
||||
|
||||
meminfo.id = RETRO_MEMORY_SYSTEM_RAM;
|
||||
core_get_memory(&meminfo);
|
||||
rcheevos_memory_register_region(regions, RC_MEMORY_TYPE_SYSTEM_RAM, (uint8_t*)meminfo.data, meminfo.size, description);
|
||||
|
||||
meminfo.id = RETRO_MEMORY_SAVE_RAM;
|
||||
core_get_memory(&meminfo);
|
||||
rcheevos_memory_register_region(regions, RC_MEMORY_TYPE_SAVE_RAM, (uint8_t*)meminfo.data, meminfo.size, description);
|
||||
}
|
||||
|
||||
static const rarch_memory_descriptor_t* rcheevos_memory_get_descriptor(const rarch_memory_map_t* mmap, unsigned real_address)
|
||||
{
|
||||
const rarch_memory_descriptor_t* desc = mmap->descriptors;
|
||||
const rarch_memory_descriptor_t* end = desc + mmap->num_descriptors;
|
||||
|
||||
if (mmap->num_descriptors == 0)
|
||||
return NULL;
|
||||
|
||||
for (; desc < end; desc++)
|
||||
{
|
||||
if (desc->core.select == 0)
|
||||
{
|
||||
/* if select is 0, attempt to explcitly match the address */
|
||||
if (real_address >= desc->core.start && real_address < desc->core.start + desc->core.len)
|
||||
return desc;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* otherwise, attempt to match the address by matching the select bits */
|
||||
if (((desc->core.start ^ real_address) & desc->core.select) == 0)
|
||||
{
|
||||
/* sanity check - make sure the descriptor is large enough to hold the target address */
|
||||
if (real_address - desc->core.start < desc->core.len)
|
||||
return desc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void rcheevos_memory_init_from_memory_map(rcheevos_memory_regions_t* regions, const rarch_memory_map_t* mmap, const rc_memory_regions_t* console_regions)
|
||||
{
|
||||
char description[64];
|
||||
unsigned i;
|
||||
uint8_t* region_start;
|
||||
uint8_t* 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 rarch_memory_descriptor_t* desc = rcheevos_memory_get_descriptor(mmap, real_address);
|
||||
if (!desc)
|
||||
{
|
||||
if (console_region->type != RC_MEMORY_TYPE_UNUSED)
|
||||
{
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "Could not map region starting at $%06X\n",
|
||||
real_address - console_region->real_address + console_region->start_address);
|
||||
}
|
||||
|
||||
rcheevos_memory_register_region(regions, console_region->type, NULL, console_region_size, "null filler");
|
||||
break;
|
||||
}
|
||||
|
||||
offset = real_address - desc->core.start;
|
||||
snprintf(description, sizeof(description),
|
||||
"descriptor %u, offset 0x%06X",
|
||||
(int)(desc - mmap->descriptors) + 1, (int)offset);
|
||||
|
||||
if (desc->core.ptr)
|
||||
{
|
||||
desc_start = (uint8_t*)desc->core.ptr + desc->core.offset;
|
||||
region_start = desc_start + offset;
|
||||
}
|
||||
else
|
||||
region_start = NULL;
|
||||
|
||||
desc_size = desc->core.len - offset;
|
||||
|
||||
if (console_region_size > desc_size)
|
||||
{
|
||||
if (desc_size == 0)
|
||||
{
|
||||
if (console_region->type != RC_MEMORY_TYPE_UNUSED)
|
||||
{
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "Could not map region starting at $%06X\n",
|
||||
real_address - console_region->real_address + console_region->start_address);
|
||||
}
|
||||
|
||||
rcheevos_memory_register_region(regions, console_region->type, NULL, console_region_size, "null filler");
|
||||
console_region_size = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
rcheevos_memory_register_region(regions, console_region->type, region_start, desc_size, description);
|
||||
console_region_size -= desc_size;
|
||||
real_address += desc_size;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rcheevos_memory_register_region(regions, console_region->type, region_start, console_region_size, description);
|
||||
console_region_size = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned rcheevos_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 rcheevos_memory_init_from_unmapped_memory(rcheevos_memory_regions_t* regions, const rc_memory_regions_t* console_regions, int console)
|
||||
{
|
||||
char description[64];
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < console_regions->num_regions; ++i)
|
||||
{
|
||||
size_t offset;
|
||||
unsigned j;
|
||||
retro_ctx_memory_info_t meminfo;
|
||||
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;
|
||||
unsigned base_address = 0;
|
||||
|
||||
meminfo.id = rcheevos_memory_console_region_to_ram_type(console_region->type);
|
||||
|
||||
for (j = 0; j <= i; ++j)
|
||||
{
|
||||
const rc_memory_region_t* console_region2 = &console_regions->region[j];
|
||||
if (rcheevos_memory_console_region_to_ram_type(
|
||||
console_region2->type) == meminfo.id)
|
||||
{
|
||||
base_address = console_region2->start_address;
|
||||
break;
|
||||
}
|
||||
}
|
||||
offset = console_region->start_address - base_address;
|
||||
|
||||
core_get_memory(&meminfo);
|
||||
|
||||
if (offset < meminfo.size)
|
||||
{
|
||||
meminfo.size -= offset;
|
||||
|
||||
if (meminfo.data)
|
||||
{
|
||||
snprintf(description, sizeof(description),
|
||||
"offset 0x%06X", (int)offset);
|
||||
meminfo.data = (uint8_t*)meminfo.data + offset;
|
||||
}
|
||||
else
|
||||
snprintf(description, sizeof(description), "null filler");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (console_region->type != RC_MEMORY_TYPE_UNUSED)
|
||||
{
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "Could not map region starting at $%06X\n", console_region->start_address);
|
||||
}
|
||||
|
||||
meminfo.data = NULL;
|
||||
meminfo.size = 0;
|
||||
}
|
||||
|
||||
if (console_region_size > meminfo.size)
|
||||
{
|
||||
/* want more than what is available, take what we can and null fill the rest */
|
||||
rcheevos_memory_register_region(regions, console_region->type, (uint8_t*)meminfo.data, meminfo.size, description);
|
||||
rcheevos_memory_register_region(regions, console_region->type, NULL, console_region_size - meminfo.size, "null filler");
|
||||
}
|
||||
else
|
||||
{
|
||||
/* only take as much as we need */
|
||||
rcheevos_memory_register_region(regions, console_region->type, (uint8_t*)meminfo.data, console_region_size, description);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rcheevos_memory_destroy(rcheevos_memory_regions_t* regions)
|
||||
{
|
||||
memset(regions, 0, sizeof(*regions));
|
||||
}
|
||||
|
||||
bool rcheevos_memory_init(rcheevos_memory_regions_t* regions, int console)
|
||||
{
|
||||
unsigned i;
|
||||
const rc_memory_regions_t* console_regions = rc_console_memory_regions(console);
|
||||
rcheevos_memory_regions_t new_regions;
|
||||
bool has_valid_region = false;
|
||||
|
||||
if (!regions)
|
||||
return false;
|
||||
|
||||
memset(&new_regions, 0, sizeof(new_regions));
|
||||
|
||||
if (console_regions == NULL || console_regions->num_regions == 0)
|
||||
{
|
||||
rcheevos_memory_init_without_regions(&new_regions);
|
||||
}
|
||||
else
|
||||
{
|
||||
rarch_system_info_t* system = runloop_get_system_info();
|
||||
if (system->mmaps.num_descriptors != 0)
|
||||
rcheevos_memory_init_from_memory_map(&new_regions, &system->mmaps, console_regions);
|
||||
else
|
||||
rcheevos_memory_init_from_unmapped_memory(&new_regions, console_regions, console);
|
||||
}
|
||||
|
||||
/* determine if any valid regions were found */
|
||||
for (i = 0; i < new_regions.count; i++)
|
||||
{
|
||||
if (new_regions.data[i])
|
||||
{
|
||||
has_valid_region = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(regions, &new_regions, sizeof(*regions));
|
||||
return has_valid_region;
|
||||
}
|
||||
@@ -0,0 +1,708 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2019-2021 - Brian Weiss
|
||||
*
|
||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "cheevos_locals.h"
|
||||
|
||||
#include "../gfx/gfx_display.h"
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
|
||||
#include "cheevos.h"
|
||||
|
||||
#include "../deps/rcheevos/include/rc_runtime_types.h"
|
||||
|
||||
#include "../file_path_special.h"
|
||||
#include "../menu/menu_driver.h"
|
||||
#include "../menu/menu_entries.h"
|
||||
|
||||
#include <features/features_cpu.h>
|
||||
|
||||
enum rcheevos_menuitem_bucket
|
||||
{
|
||||
RCHEEVOS_MENUITEM_BUCKET_UNKNOWN = 0,
|
||||
RCHEEVOS_MENUITEM_BUCKET_LOCKED,
|
||||
RCHEEVOS_MENUITEM_BUCKET_UNLOCKED,
|
||||
RCHEEVOS_MENUITEM_BUCKET_UNSUPPORTED,
|
||||
RCHEEVOS_MENUITEM_BUCKET_RECENTLY_UNLOCKED,
|
||||
RCHEEVOS_MENUITEM_BUCKET_ACTIVE_CHALLENGE,
|
||||
RCHEEVOS_MENUITEM_BUCKET_ALMOST_THERE
|
||||
};
|
||||
|
||||
static void rcheevos_menu_update_bucket(rcheevos_racheevo_t* cheevo)
|
||||
{
|
||||
if (!cheevo->memaddr)
|
||||
{
|
||||
/* non-active unsupported achievement */
|
||||
cheevo->menu_bucket = RCHEEVOS_MENUITEM_BUCKET_UNSUPPORTED;
|
||||
}
|
||||
else if (!(cheevo->active & RCHEEVOS_ACTIVE_HARDCORE))
|
||||
{
|
||||
/* non-active unlocked in hardcore achievement */
|
||||
cheevo->menu_bucket = RCHEEVOS_MENUITEM_BUCKET_UNLOCKED;
|
||||
}
|
||||
else
|
||||
{
|
||||
const rcheevos_locals_t* rcheevos_locals = get_rcheevos_locals();
|
||||
rc_trigger_t* trigger;
|
||||
|
||||
if (!rcheevos_locals->hardcore_active && !(cheevo->active & RCHEEVOS_ACTIVE_SOFTCORE))
|
||||
{
|
||||
/* non-active unlocked in softcore achievement in softcore mode */
|
||||
cheevo->menu_bucket = RCHEEVOS_MENUITEM_BUCKET_UNLOCKED;
|
||||
return;
|
||||
}
|
||||
|
||||
/* active achievement */
|
||||
cheevo->menu_bucket = RCHEEVOS_MENUITEM_BUCKET_LOCKED;
|
||||
cheevo->menu_progress = 0;
|
||||
|
||||
trigger = rc_runtime_get_achievement(&rcheevos_locals->runtime, cheevo->id);
|
||||
if (trigger)
|
||||
{
|
||||
if (trigger->measured_value && trigger->measured_target)
|
||||
{
|
||||
const unsigned long clamped_value = (unsigned long)
|
||||
MIN(trigger->measured_value, trigger->measured_target);
|
||||
cheevo->menu_progress =
|
||||
(uint8_t)((clamped_value * 100) / trigger->measured_target);
|
||||
}
|
||||
|
||||
if (trigger->state == RC_TRIGGER_STATE_PRIMED)
|
||||
cheevo->menu_bucket = RCHEEVOS_MENUITEM_BUCKET_ACTIVE_CHALLENGE;
|
||||
else if (cheevo->menu_progress >= 80)
|
||||
cheevo->menu_bucket = RCHEEVOS_MENUITEM_BUCKET_ALMOST_THERE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void rcheevos_menu_update_buckets(bool cheevos_test_unofficial)
|
||||
{
|
||||
const rcheevos_locals_t* rcheevos_locals = get_rcheevos_locals();
|
||||
rcheevos_racheevo_t* cheevo = rcheevos_locals->patchdata.core;
|
||||
rcheevos_racheevo_t* stop = cheevo + rcheevos_locals->patchdata.core_count;
|
||||
|
||||
while (cheevo < stop)
|
||||
{
|
||||
rcheevos_menu_update_bucket(cheevo);
|
||||
++cheevo;
|
||||
}
|
||||
|
||||
if (cheevos_test_unofficial)
|
||||
{
|
||||
cheevo = rcheevos_locals->patchdata.unofficial;
|
||||
stop = cheevo + rcheevos_locals->patchdata.unofficial_count;
|
||||
|
||||
while (cheevo < stop)
|
||||
{
|
||||
rcheevos_menu_update_bucket(cheevo);
|
||||
++cheevo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool rcheevos_menu_get_state(unsigned menu_offset, char *buffer, size_t len)
|
||||
{
|
||||
const rcheevos_locals_t* rcheevos_locals = get_rcheevos_locals();
|
||||
if (menu_offset < rcheevos_locals->menuitem_count)
|
||||
{
|
||||
const rcheevos_menuitem_t* menuitem = &rcheevos_locals->menuitems[menu_offset];
|
||||
const rcheevos_racheevo_t* cheevo = menuitem->cheevo;
|
||||
if (cheevo)
|
||||
{
|
||||
if (cheevo->menu_progress)
|
||||
{
|
||||
snprintf(buffer, len, "%s - %d%%",
|
||||
msg_hash_to_str(menuitem->state_label_idx),
|
||||
cheevo->menu_progress);
|
||||
}
|
||||
else
|
||||
{
|
||||
strlcpy(buffer, msg_hash_to_str(menuitem->state_label_idx), len);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer)
|
||||
buffer[0] = '\0';
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool rcheevos_menu_get_sublabel(unsigned menu_offset, char *buffer, size_t len)
|
||||
{
|
||||
const rcheevos_locals_t* rcheevos_locals = get_rcheevos_locals();
|
||||
if (menu_offset < rcheevos_locals->menuitem_count)
|
||||
{
|
||||
const rcheevos_racheevo_t* cheevo = rcheevos_locals->menuitems[menu_offset].cheevo;
|
||||
if (cheevo && buffer)
|
||||
{
|
||||
strlcpy(buffer, cheevo->description, len);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer)
|
||||
buffer[0] = '\0';
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void rcheevos_menu_reset_badges(void)
|
||||
{
|
||||
const rcheevos_locals_t* rcheevos_locals = get_rcheevos_locals();
|
||||
rcheevos_racheevo_t* cheevo = rcheevos_locals->patchdata.core;
|
||||
rcheevos_racheevo_t* stop = cheevo + rcheevos_locals->patchdata.core_count;
|
||||
|
||||
while (cheevo < stop)
|
||||
{
|
||||
if (cheevo->menu_badge_texture)
|
||||
video_driver_texture_unload(&cheevo->menu_badge_texture);
|
||||
++cheevo;
|
||||
}
|
||||
|
||||
cheevo = rcheevos_locals->patchdata.unofficial;
|
||||
stop = cheevo + rcheevos_locals->patchdata.unofficial_count;
|
||||
|
||||
while (cheevo < stop)
|
||||
{
|
||||
if (cheevo->menu_badge_texture)
|
||||
video_driver_texture_unload(&cheevo->menu_badge_texture);
|
||||
++cheevo;
|
||||
}
|
||||
}
|
||||
|
||||
static rcheevos_menuitem_t* rcheevos_menu_allocate(
|
||||
rcheevos_locals_t* rcheevos_locals, rcheevos_racheevo_t* cheevo)
|
||||
{
|
||||
rcheevos_menuitem_t* menuitem;
|
||||
|
||||
if (rcheevos_locals->menuitem_count == rcheevos_locals->menuitem_capacity)
|
||||
{
|
||||
if (rcheevos_locals->menuitems)
|
||||
{
|
||||
rcheevos_menuitem_t* new_menuitems;
|
||||
rcheevos_locals->menuitem_capacity += 32;
|
||||
new_menuitems = (rcheevos_menuitem_t*)realloc(rcheevos_locals->menuitems,
|
||||
rcheevos_locals->menuitem_capacity * sizeof(rcheevos_menuitem_t));
|
||||
|
||||
if (new_menuitems)
|
||||
{
|
||||
rcheevos_locals->menuitems = new_menuitems;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* realloc failed */
|
||||
CHEEVOS_ERR(RCHEEVOS_TAG " could not allocate space for %u menu items\n",
|
||||
rcheevos_locals->menuitem_capacity);
|
||||
rcheevos_locals->menuitem_capacity -= 32;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rcheevos_locals->menuitem_capacity = 64;
|
||||
rcheevos_locals->menuitems = (rcheevos_menuitem_t*)
|
||||
malloc(rcheevos_locals->menuitem_capacity * sizeof(rcheevos_menuitem_t));
|
||||
|
||||
if (!rcheevos_locals->menuitems)
|
||||
{
|
||||
/* malloc failed */
|
||||
CHEEVOS_ERR(RCHEEVOS_TAG " could not allocate space for %u menu items\n",
|
||||
rcheevos_locals->menuitem_capacity);
|
||||
rcheevos_locals->menuitem_capacity = 0;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
menuitem = &rcheevos_locals->menuitems[rcheevos_locals->menuitem_count++];
|
||||
menuitem->cheevo = cheevo;
|
||||
menuitem->state_label_idx = MSG_UNKNOWN;
|
||||
return menuitem;
|
||||
}
|
||||
|
||||
static void rcheevos_menu_append_header(rcheevos_locals_t* rcheevos_locals,
|
||||
enum msg_hash_enums label)
|
||||
{
|
||||
rcheevos_menuitem_t* menuitem = rcheevos_menu_allocate(rcheevos_locals, NULL);
|
||||
if (menuitem)
|
||||
menuitem->state_label_idx = label;
|
||||
}
|
||||
|
||||
static void rcheevos_menu_update_badge(rcheevos_racheevo_t* cheevo)
|
||||
{
|
||||
bool badge_grayscale = false;
|
||||
switch (cheevo->menu_bucket)
|
||||
{
|
||||
case RCHEEVOS_MENUITEM_BUCKET_LOCKED:
|
||||
case RCHEEVOS_MENUITEM_BUCKET_UNSUPPORTED:
|
||||
case RCHEEVOS_MENUITEM_BUCKET_ALMOST_THERE:
|
||||
case RCHEEVOS_MENUITEM_BUCKET_ACTIVE_CHALLENGE:
|
||||
badge_grayscale = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
badge_grayscale = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!cheevo->menu_badge_texture || cheevo->menu_badge_grayscale != badge_grayscale)
|
||||
{
|
||||
uintptr_t new_badge_texture =
|
||||
rcheevos_get_badge_texture(cheevo->badge, badge_grayscale);
|
||||
|
||||
if (new_badge_texture)
|
||||
{
|
||||
if (cheevo->menu_badge_texture)
|
||||
video_driver_texture_unload(&cheevo->menu_badge_texture);
|
||||
|
||||
cheevo->menu_badge_texture = new_badge_texture;
|
||||
cheevo->menu_badge_grayscale = badge_grayscale;
|
||||
}
|
||||
/* menu_badge_grayscale is overloaded such that any value greater than 1 indicates
|
||||
* the server default image is being used */
|
||||
else if (cheevo->menu_badge_grayscale < 2)
|
||||
{
|
||||
if (cheevo->menu_badge_texture)
|
||||
video_driver_texture_unload(&cheevo->menu_badge_texture);
|
||||
|
||||
/* requested badge is not available, check for server default */
|
||||
cheevo->menu_badge_texture =
|
||||
rcheevos_get_badge_texture("00000", false);
|
||||
|
||||
if (cheevo->menu_badge_texture)
|
||||
cheevo->menu_badge_grayscale = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void rcheevos_menu_append_items(rcheevos_locals_t* rcheevos_locals,
|
||||
bool cheevos_test_unofficial, enum rcheevos_menuitem_bucket bucket)
|
||||
{
|
||||
const settings_t *settings = config_get_ptr();
|
||||
rcheevos_racheevo_t* cheevo = rcheevos_locals->patchdata.core;
|
||||
rcheevos_racheevo_t* stop = cheevo + rcheevos_locals->patchdata.core_count;
|
||||
bool processing_unofficial = false;
|
||||
const unsigned first_index = rcheevos_locals->menuitem_count;
|
||||
|
||||
do
|
||||
{
|
||||
if (cheevo == stop)
|
||||
{
|
||||
if (!cheevos_test_unofficial || processing_unofficial)
|
||||
break;
|
||||
|
||||
processing_unofficial = true;
|
||||
cheevo = rcheevos_locals->patchdata.unofficial;
|
||||
stop = cheevo + rcheevos_locals->patchdata.unofficial_count;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cheevo->menu_bucket == bucket)
|
||||
{
|
||||
rcheevos_menuitem_t* menuitem = rcheevos_menu_allocate(rcheevos_locals, cheevo);
|
||||
if (!menuitem)
|
||||
return;
|
||||
|
||||
switch (cheevo->menu_bucket)
|
||||
{
|
||||
case RCHEEVOS_MENUITEM_BUCKET_UNSUPPORTED:
|
||||
menuitem->state_label_idx = MENU_ENUM_LABEL_VALUE_CHEEVOS_UNSUPPORTED_ENTRY;
|
||||
break;
|
||||
|
||||
case RCHEEVOS_MENUITEM_BUCKET_RECENTLY_UNLOCKED:
|
||||
{
|
||||
/* insert the item such that the unlock times are descending */
|
||||
unsigned entry_index = rcheevos_locals->menuitem_count - 1;
|
||||
while (entry_index > first_index)
|
||||
{
|
||||
rcheevos_menuitem_t* prev_menuitem = menuitem - 1;
|
||||
if (prev_menuitem->cheevo->unlock_time >= cheevo->unlock_time)
|
||||
break;
|
||||
|
||||
memcpy(menuitem, prev_menuitem, sizeof(rcheevos_menuitem_t));
|
||||
menuitem = prev_menuitem;
|
||||
--entry_index;
|
||||
}
|
||||
|
||||
menuitem->cheevo = cheevo;
|
||||
}
|
||||
/* fallthrough to RCHEEVOS_MENUITEM_BUCKET_UNLOCKED */
|
||||
|
||||
case RCHEEVOS_MENUITEM_BUCKET_UNLOCKED:
|
||||
if (!(cheevo->active & RCHEEVOS_ACTIVE_HARDCORE))
|
||||
menuitem->state_label_idx = MENU_ENUM_LABEL_VALUE_CHEEVOS_UNLOCKED_ENTRY_HARDCORE;
|
||||
else
|
||||
menuitem->state_label_idx = MENU_ENUM_LABEL_VALUE_CHEEVOS_UNLOCKED_ENTRY;
|
||||
break;
|
||||
|
||||
case RCHEEVOS_MENUITEM_BUCKET_ALMOST_THERE:
|
||||
{
|
||||
/* insert the item such that the progresses are descending */
|
||||
unsigned entry_index = rcheevos_locals->menuitem_count - 1;
|
||||
while (entry_index > first_index)
|
||||
{
|
||||
rcheevos_menuitem_t* prev_menuitem = menuitem - 1;
|
||||
if (prev_menuitem->cheevo->menu_progress >= cheevo->menu_progress)
|
||||
break;
|
||||
|
||||
memcpy(menuitem, prev_menuitem, sizeof(rcheevos_menuitem_t));
|
||||
menuitem = prev_menuitem;
|
||||
--entry_index;
|
||||
}
|
||||
|
||||
menuitem->cheevo = cheevo;
|
||||
}
|
||||
/* fallthrough to default */
|
||||
|
||||
default:
|
||||
if (processing_unofficial)
|
||||
menuitem->state_label_idx = MENU_ENUM_LABEL_VALUE_CHEEVOS_UNOFFICIAL_ENTRY;
|
||||
else if (!(cheevo->active & RCHEEVOS_ACTIVE_SOFTCORE))
|
||||
menuitem->state_label_idx = MENU_ENUM_LABEL_VALUE_CHEEVOS_UNLOCKED_ENTRY;
|
||||
else
|
||||
menuitem->state_label_idx = MENU_ENUM_LABEL_VALUE_CHEEVOS_LOCKED_ENTRY;
|
||||
break;
|
||||
}
|
||||
|
||||
if (cheevo->badge && cheevo->badge[0] && settings &&
|
||||
settings->bools.cheevos_badges_enable)
|
||||
{
|
||||
rcheevos_menu_update_badge(cheevo);
|
||||
}
|
||||
}
|
||||
|
||||
++cheevo;
|
||||
} while (true);
|
||||
}
|
||||
|
||||
uintptr_t rcheevos_menu_get_badge_texture(unsigned menu_offset)
|
||||
{
|
||||
const rcheevos_locals_t* rcheevos_locals = get_rcheevos_locals();
|
||||
if (menu_offset < rcheevos_locals->menuitem_count)
|
||||
{
|
||||
rcheevos_racheevo_t* cheevo = rcheevos_locals->menuitems[menu_offset].cheevo;
|
||||
if (cheevo)
|
||||
{
|
||||
/* if we're using the placeholder badge, check to see if the real badge
|
||||
* has become available (do this roughly once a second) */
|
||||
if (cheevo->menu_badge_grayscale >= 2)
|
||||
{
|
||||
if (++cheevo->menu_badge_grayscale == 64)
|
||||
{
|
||||
cheevo->menu_badge_grayscale = 2;
|
||||
rcheevos_menu_update_badge(cheevo);
|
||||
}
|
||||
}
|
||||
|
||||
return cheevo->menu_badge_texture;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rcheevos_menu_populate_hardcore_pause_submenu(void* data)
|
||||
{
|
||||
const rcheevos_locals_t* rcheevos_locals = get_rcheevos_locals();
|
||||
menu_displaylist_info_t* info = (menu_displaylist_info_t*)data;
|
||||
const settings_t* settings = config_get_ptr();
|
||||
const bool cheevos_hardcore_mode_enable = settings->bools.cheevos_hardcore_mode_enable;
|
||||
|
||||
if (cheevos_hardcore_mode_enable && rcheevos_locals->loaded)
|
||||
{
|
||||
if (rcheevos_locals->hardcore_active)
|
||||
{
|
||||
menu_entries_append_enum(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_PAUSE_CANCEL),
|
||||
msg_hash_to_str(MENU_ENUM_SUBLABEL_ACHIEVEMENT_PAUSE_CANCEL),
|
||||
MENU_ENUM_LABEL_ACHIEVEMENT_PAUSE_CANCEL,
|
||||
MENU_SETTING_ACTION_CLOSE, 0, 0);
|
||||
menu_entries_append_enum(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_PAUSE),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_ACHIEVEMENT_PAUSE),
|
||||
MENU_ENUM_LABEL_ACHIEVEMENT_PAUSE,
|
||||
MENU_SETTING_ACTION_PAUSE_ACHIEVEMENTS, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
menu_entries_append_enum(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_RESUME_CANCEL),
|
||||
msg_hash_to_str(MENU_ENUM_SUBLABEL_ACHIEVEMENT_RESUME_CANCEL),
|
||||
MENU_ENUM_LABEL_ACHIEVEMENT_RESUME_CANCEL,
|
||||
MENU_SETTING_ACTION_CLOSE, 0, 0);
|
||||
menu_entries_append_enum(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_RESUME),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_ACHIEVEMENT_RESUME),
|
||||
MENU_ENUM_LABEL_ACHIEVEMENT_RESUME,
|
||||
MENU_SETTING_ACTION_RESUME_ACHIEVEMENTS, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rcheevos_menu_populate(void* data)
|
||||
{
|
||||
menu_displaylist_info_t* info = (menu_displaylist_info_t*)data;
|
||||
rcheevos_locals_t* rcheevos_locals = get_rcheevos_locals();
|
||||
const settings_t* settings = config_get_ptr();
|
||||
const bool cheevos_test_unofficial = settings->bools.cheevos_test_unofficial;
|
||||
unsigned num_locked = 0;
|
||||
unsigned num_unlocked = 0;
|
||||
unsigned num_recently_unlocked = 0;
|
||||
unsigned num_unsupported = 0;
|
||||
unsigned num_active_challenges = 0;
|
||||
unsigned num_almost_there = 0;
|
||||
|
||||
if (rcheevos_locals->loaded)
|
||||
{
|
||||
const retro_time_t now = cpu_features_get_time_usec();
|
||||
const retro_time_t recent_unlock_time = now - (10 * 60 * 1000000); /* 10 minutes ago */
|
||||
bool processing_unofficial = false;
|
||||
rcheevos_racheevo_t* cheevo = NULL;
|
||||
rcheevos_racheevo_t* stop = NULL;
|
||||
|
||||
/* first menu item is the Pause/Resume Hardcore option (unless hardcore is disabled) */
|
||||
if (settings->bools.cheevos_enable && settings->bools.cheevos_hardcore_mode_enable)
|
||||
{
|
||||
if (rcheevos_locals->hardcore_active)
|
||||
menu_entries_append_enum(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_PAUSE),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_PAUSE_MENU),
|
||||
MENU_ENUM_LABEL_ACHIEVEMENT_PAUSE_MENU,
|
||||
MENU_SETTING_ACTION_PAUSE_ACHIEVEMENTS, 0, 0);
|
||||
else
|
||||
menu_entries_append_enum(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_RESUME),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_PAUSE_MENU),
|
||||
MENU_ENUM_LABEL_ACHIEVEMENT_PAUSE_MENU,
|
||||
MENU_SETTING_ACTION_RESUME_ACHIEVEMENTS, 0, 0);
|
||||
}
|
||||
|
||||
/* update the bucket for each achievement */
|
||||
rcheevos_menu_update_buckets(cheevos_test_unofficial);
|
||||
|
||||
/* count items in each bucket */
|
||||
cheevo = rcheevos_locals->patchdata.core;
|
||||
stop = cheevo + rcheevos_locals->patchdata.core_count;
|
||||
|
||||
do
|
||||
{
|
||||
if (cheevo == stop)
|
||||
{
|
||||
if (!cheevos_test_unofficial || processing_unofficial)
|
||||
break;
|
||||
|
||||
processing_unofficial = true;
|
||||
cheevo = rcheevos_locals->patchdata.unofficial;
|
||||
stop = cheevo + rcheevos_locals->patchdata.unofficial_count;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (cheevo->menu_bucket)
|
||||
{
|
||||
case RCHEEVOS_MENUITEM_BUCKET_UNLOCKED:
|
||||
if (cheevo->unlock_time && cheevo->unlock_time >= recent_unlock_time)
|
||||
{
|
||||
cheevo->menu_bucket = RCHEEVOS_MENUITEM_BUCKET_RECENTLY_UNLOCKED;
|
||||
++num_recently_unlocked;
|
||||
}
|
||||
else
|
||||
{
|
||||
++num_unlocked;
|
||||
}
|
||||
break;
|
||||
|
||||
case RCHEEVOS_MENUITEM_BUCKET_LOCKED:
|
||||
++num_locked;
|
||||
break;
|
||||
|
||||
case RCHEEVOS_MENUITEM_BUCKET_UNSUPPORTED:
|
||||
++num_unsupported;
|
||||
break;
|
||||
|
||||
case RCHEEVOS_MENUITEM_BUCKET_ACTIVE_CHALLENGE:
|
||||
++num_active_challenges;
|
||||
break;
|
||||
|
||||
case RCHEEVOS_MENUITEM_BUCKET_ALMOST_THERE:
|
||||
++num_almost_there;
|
||||
break;
|
||||
}
|
||||
|
||||
++cheevo;
|
||||
} while(true);
|
||||
|
||||
if (!rcheevos_locals->menuitems)
|
||||
{
|
||||
/* reserve space for all achievements and up to 6 headers before we need to realloc */
|
||||
rcheevos_locals->menuitem_capacity = rcheevos_locals->patchdata.core_count + 6;
|
||||
if (cheevos_test_unofficial)
|
||||
rcheevos_locals->menuitem_capacity += rcheevos_locals->patchdata.unofficial_count;
|
||||
|
||||
rcheevos_locals->menuitems = (rcheevos_menuitem_t*)
|
||||
malloc(rcheevos_locals->menuitem_capacity * sizeof(rcheevos_menuitem_t));
|
||||
if (!rcheevos_locals->menuitems)
|
||||
rcheevos_locals->menuitem_capacity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* reset menu */
|
||||
rcheevos_locals->menuitem_count = 0;
|
||||
|
||||
/* active challenges */
|
||||
if (num_active_challenges)
|
||||
{
|
||||
rcheevos_menu_append_header(rcheevos_locals,
|
||||
MENU_ENUM_LABEL_VALUE_CHEEVOS_ACTIVE_CHALLENGES_ENTRY);
|
||||
|
||||
rcheevos_menu_append_items(rcheevos_locals, cheevos_test_unofficial,
|
||||
RCHEEVOS_MENUITEM_BUCKET_ACTIVE_CHALLENGE);
|
||||
}
|
||||
|
||||
/* recently unlocked */
|
||||
if (num_recently_unlocked)
|
||||
{
|
||||
rcheevos_menu_append_header(rcheevos_locals,
|
||||
MENU_ENUM_LABEL_VALUE_CHEEVOS_RECENTLY_UNLOCKED_ENTRY);
|
||||
|
||||
rcheevos_menu_append_items(rcheevos_locals, cheevos_test_unofficial,
|
||||
RCHEEVOS_MENUITEM_BUCKET_RECENTLY_UNLOCKED);
|
||||
}
|
||||
|
||||
/* almost there */
|
||||
if (num_almost_there)
|
||||
{
|
||||
rcheevos_menu_append_header(rcheevos_locals,
|
||||
MENU_ENUM_LABEL_VALUE_CHEEVOS_ALMOST_THERE_ENTRY);
|
||||
|
||||
rcheevos_menu_append_items(rcheevos_locals, cheevos_test_unofficial,
|
||||
RCHEEVOS_MENUITEM_BUCKET_ALMOST_THERE);
|
||||
}
|
||||
|
||||
/* locked */
|
||||
if (num_locked)
|
||||
{
|
||||
if (rcheevos_locals->menuitem_count > 0)
|
||||
{
|
||||
rcheevos_menu_append_header(rcheevos_locals,
|
||||
MENU_ENUM_LABEL_VALUE_CHEEVOS_LOCKED_ENTRY);
|
||||
}
|
||||
|
||||
rcheevos_menu_append_items(rcheevos_locals, cheevos_test_unofficial,
|
||||
RCHEEVOS_MENUITEM_BUCKET_LOCKED);
|
||||
}
|
||||
|
||||
/* unlocked */
|
||||
if (num_unlocked)
|
||||
{
|
||||
if (rcheevos_locals->menuitem_count > 0)
|
||||
{
|
||||
rcheevos_menu_append_header(rcheevos_locals,
|
||||
MENU_ENUM_LABEL_VALUE_CHEEVOS_UNLOCKED_ENTRY);
|
||||
}
|
||||
|
||||
rcheevos_menu_append_items(rcheevos_locals, cheevos_test_unofficial,
|
||||
RCHEEVOS_MENUITEM_BUCKET_UNLOCKED);
|
||||
}
|
||||
|
||||
if (rcheevos_locals->menuitem_count > 0)
|
||||
{
|
||||
/* convert to menu entries */
|
||||
rcheevos_menuitem_t* menuitem = rcheevos_locals->menuitems;
|
||||
rcheevos_menuitem_t* stop = menuitem + rcheevos_locals->menuitem_count;
|
||||
char buffer[128];
|
||||
unsigned idx = 0;
|
||||
|
||||
do
|
||||
{
|
||||
if (menuitem->cheevo)
|
||||
{
|
||||
menu_entries_append_enum(info->list, menuitem->cheevo->title,
|
||||
menuitem->cheevo->description,
|
||||
MENU_ENUM_LABEL_CHEEVOS_LOCKED_ENTRY,
|
||||
MENU_SETTINGS_CHEEVOS_START + idx, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(buffer, sizeof(buffer), "----- %s -----",
|
||||
msg_hash_to_str(menuitem->state_label_idx));
|
||||
|
||||
menu_entries_append_enum(info->list, buffer, "",
|
||||
MENU_ENUM_LABEL_CHEEVOS_LOCKED_ENTRY,
|
||||
MENU_SETTINGS_CHEEVOS_START + idx, 0, 0);
|
||||
}
|
||||
|
||||
++idx;
|
||||
++menuitem;
|
||||
} while (menuitem != stop);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* no achievements found */
|
||||
if (!rcheevos_locals->core_supports)
|
||||
{
|
||||
menu_entries_append_enum(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CANNOT_ACTIVATE_ACHIEVEMENTS_WITH_THIS_CORE),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_CANNOT_ACTIVATE_ACHIEVEMENTS_WITH_THIS_CORE),
|
||||
MENU_ENUM_LABEL_CANNOT_ACTIVATE_ACHIEVEMENTS_WITH_THIS_CORE,
|
||||
FILE_TYPE_NONE, 0, 0);
|
||||
}
|
||||
else if (!rcheevos_locals->token[0])
|
||||
{
|
||||
menu_entries_append_enum(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_LOGGED_IN),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_NOT_LOGGED_IN),
|
||||
MENU_ENUM_LABEL_NOT_LOGGED_IN,
|
||||
FILE_TYPE_NONE, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
menu_entries_append_enum(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_ACHIEVEMENTS_TO_DISPLAY),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_NO_ACHIEVEMENTS_TO_DISPLAY),
|
||||
MENU_ENUM_LABEL_NO_ACHIEVEMENTS_TO_DISPLAY,
|
||||
FILE_TYPE_NONE, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HAVE_MENU */
|
||||
|
||||
uintptr_t rcheevos_get_badge_texture(const char *badge, bool locked)
|
||||
{
|
||||
char badge_file[24];
|
||||
char fullpath[PATH_MAX_LENGTH];
|
||||
uintptr_t tex = 0;
|
||||
|
||||
if (!badge)
|
||||
return 0;
|
||||
|
||||
snprintf(badge_file, sizeof(badge_file), "%s%s%s", badge,
|
||||
locked ? "_lock" : "", FILE_PATH_PNG_EXTENSION);
|
||||
|
||||
fill_pathname_application_special(fullpath, sizeof(fullpath),
|
||||
APPLICATION_SPECIAL_DIRECTORY_THUMBNAILS_CHEEVOS_BADGES);
|
||||
|
||||
if (!gfx_display_reset_textures_list(badge_file, fullpath,
|
||||
&tex, TEXTURE_FILTER_MIPMAP_LINEAR, NULL, NULL))
|
||||
{
|
||||
tex = 0;
|
||||
}
|
||||
|
||||
return tex;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2015-2018 - Andre Leiradella
|
||||
* Copyright (C) 2019-2021 - Brian Weiss
|
||||
*
|
||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
@@ -12,33 +13,29 @@
|
||||
* You should have received a copy of the GNU General Public License along with RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef __RARCH_CHEEVOS_MEMORY_H
|
||||
#define __RARCH_CHEEVOS_MEMORY_H
|
||||
|
||||
#ifndef __RARCH_CHEEVOS_MENU_H
|
||||
#define __RARCH_CHEEVOS_MENU_H
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <boolean.h>
|
||||
|
||||
#include <retro_common_api.h>
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
#define MAX_MEMORY_REGIONS 32
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t* data[MAX_MEMORY_REGIONS];
|
||||
size_t size[MAX_MEMORY_REGIONS];
|
||||
size_t total_size;
|
||||
unsigned count;
|
||||
} rcheevos_memory_regions_t;
|
||||
|
||||
bool rcheevos_memory_init(rcheevos_memory_regions_t* regions, int console);
|
||||
void rcheevos_memory_destroy(rcheevos_memory_regions_t* regions);
|
||||
|
||||
uint8_t* rcheevos_memory_find(const rcheevos_memory_regions_t* regions,
|
||||
unsigned address);
|
||||
void rcheevos_menu_populate(void* data);
|
||||
void rcheevos_menu_populate_hardcore_pause_submenu(void* data);
|
||||
bool rcheevos_menu_get_state(unsigned menu_offset, char* buffer, size_t buffer_size);
|
||||
bool rcheevos_menu_get_sublabel(unsigned menu_offset, char* buffer, size_t buffer_size);
|
||||
uintptr_t rcheevos_menu_get_badge_texture(unsigned menu_offset);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
||||
#endif /* HAVE_MENU */
|
||||
|
||||
#endif /* __RARCH_CHEEVOS_MENU_H */
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "cheevos_parser.h"
|
||||
|
||||
#include "util.h"
|
||||
#include "cheevos_locals.h"
|
||||
|
||||
#include <encodings/utf.h>
|
||||
#include <formats/rjson.h>
|
||||
|
||||
@@ -16,49 +16,10 @@
|
||||
#ifndef __RARCH_CHEEVOS_PARSER_H
|
||||
#define __RARCH_CHEEVOS_PARSER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <boolean.h>
|
||||
|
||||
#include <retro_common_api.h>
|
||||
#include "cheevos_locals.h"
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char* title;
|
||||
const char* description;
|
||||
const char* badge;
|
||||
const char* memaddr;
|
||||
unsigned points;
|
||||
unsigned id;
|
||||
unsigned active;
|
||||
} rcheevos_racheevo_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char* title;
|
||||
const char* description;
|
||||
const char* mem;
|
||||
unsigned id;
|
||||
unsigned format;
|
||||
} rcheevos_ralboard_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char* title;
|
||||
rcheevos_racheevo_t* core;
|
||||
rcheevos_racheevo_t* unofficial;
|
||||
rcheevos_ralboard_t* lboards;
|
||||
char* richpresence_script;
|
||||
|
||||
unsigned game_id;
|
||||
unsigned console_id;
|
||||
unsigned core_count;
|
||||
unsigned unofficial_count;
|
||||
unsigned lboard_count;
|
||||
} rcheevos_rapatchdata_t;
|
||||
|
||||
typedef void (*rcheevos_unlock_cb_t)(unsigned id, void* userdata);
|
||||
|
||||
int rcheevos_get_json_error(const char* json, char* token, size_t length);
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2015-2016 - Andre Leiradella
|
||||
*
|
||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __RARCH_CHEEVOS_UTIL_H
|
||||
#define __RARCH_CHEEVOS_UTIL_H
|
||||
|
||||
#include <retro_common_api.h>
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
/*****************************************************************************
|
||||
Setup - mainly for debugging
|
||||
*****************************************************************************/
|
||||
|
||||
/* Define this macro to get extra-verbose log for cheevos. */
|
||||
#define CHEEVOS_VERBOSE
|
||||
|
||||
/*****************************************************************************
|
||||
End of setup
|
||||
*****************************************************************************/
|
||||
|
||||
#define RCHEEVOS_TAG "[RCHEEVOS]: "
|
||||
#define CHEEVOS_FREE(p) do { void* q = (void*)p; if (q) free(q); } while (0)
|
||||
|
||||
#ifdef CHEEVOS_VERBOSE
|
||||
|
||||
#define CHEEVOS_LOG RARCH_LOG
|
||||
#define CHEEVOS_ERR RARCH_ERR
|
||||
|
||||
#else
|
||||
|
||||
#define CHEEVOS_LOG rcheevos_log
|
||||
#define CHEEVOS_ERR RARCH_ERR
|
||||
|
||||
void rcheevos_log(const char *fmt, ...);
|
||||
|
||||
#endif
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif /* __RARCH_CHEEVOS_UTIL_H */
|
||||
@@ -166,6 +166,7 @@ static void command_network_poll(command_t *handle)
|
||||
char buf[1024];
|
||||
|
||||
buf[0] = '\0';
|
||||
netcmd->cmd_source_len = sizeof(struct sockaddr_storage);
|
||||
ret = recvfrom(netcmd->net_fd, buf, sizeof(buf) - 1, 0,
|
||||
(struct sockaddr*)&netcmd->cmd_source,
|
||||
&netcmd->cmd_source_len);
|
||||
|
||||
+29
-1
@@ -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)
|
||||
@@ -152,6 +157,8 @@
|
||||
|
||||
#define DEFAULT_CRT_SWITCH_PORCH_ADJUST 0
|
||||
|
||||
#define DEFAULT_CRT_SWITCH_HIRES_MENU true
|
||||
|
||||
#define DEFAULT_HISTORY_LIST_ENABLE true
|
||||
|
||||
#define DEFAULT_PLAYLIST_ENTRY_RENAME true
|
||||
@@ -225,6 +232,9 @@
|
||||
#define DEFAULT_FULLSCREEN_Y 0
|
||||
#endif
|
||||
|
||||
/* Force 4k resolution */
|
||||
#define DEFAULT_FORCE_RESOLUTION false
|
||||
|
||||
/* Number of threads to use for video recording */
|
||||
#define DEFAULT_VIDEO_RECORD_THREADS 2
|
||||
|
||||
@@ -243,6 +253,11 @@
|
||||
#endif
|
||||
#define DEFAULT_CHECK_FIRMWARE_BEFORE_LOADING false
|
||||
|
||||
/* Specifies whether to cache core info
|
||||
* into a single (compressed) file for improved
|
||||
* load times on platforms with slow IO */
|
||||
#define DEFAULT_CORE_INFO_CACHE_ENABLE true
|
||||
|
||||
/* Specifies whether to 'reload' (fork and quit)
|
||||
* RetroArch when launching content with the
|
||||
* currently loaded core
|
||||
@@ -281,6 +296,7 @@
|
||||
* Can reduce latency at cost of higher risk of stuttering.
|
||||
*/
|
||||
#define DEFAULT_FRAME_DELAY 0
|
||||
#define MAXIMUM_FRAME_DELAY 19
|
||||
|
||||
/* Inserts black frame(s) inbetween frames.
|
||||
* Useful for Higher Hz monitors (set to multiples of 60 Hz) who want to play 60 Hz
|
||||
@@ -734,7 +750,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
|
||||
|
||||
@@ -1185,6 +1209,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
|
||||
|
||||
@@ -350,6 +350,12 @@
|
||||
#define SUPPORTS_CORETEXT false
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CHECK
|
||||
#define SUPPORTS_CHECK true
|
||||
#else
|
||||
#define SUPPORTS_CHECK false
|
||||
#endif
|
||||
|
||||
#if !defined(_WIN32) && !defined(GLOBAL_CONFIG_DIR)
|
||||
#if defined(__HAIKU__)
|
||||
#define GLOBAL_CONFIG_DIR "/system/settings"
|
||||
|
||||
+75
-17
@@ -93,6 +93,7 @@ enum video_driver_enum
|
||||
VIDEO_GDI,
|
||||
VIDEO_VGA,
|
||||
VIDEO_FPGA,
|
||||
VIDEO_RSX,
|
||||
VIDEO_NULL
|
||||
};
|
||||
|
||||
@@ -212,6 +213,7 @@ enum bluetooth_driver_enum
|
||||
enum wifi_driver_enum
|
||||
{
|
||||
WIFI_CONNMANCTL = BLUETOOTH_NULL + 1,
|
||||
WIFI_NMCLI,
|
||||
WIFI_NULL
|
||||
};
|
||||
|
||||
@@ -315,6 +317,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
|
||||
@@ -337,7 +341,7 @@ static const enum audio_driver_enum AUDIO_DEFAULT_DRIVER = AUDIO_CTR;
|
||||
static const enum audio_driver_enum AUDIO_DEFAULT_DRIVER = AUDIO_SWITCH;
|
||||
#elif defined(HAVE_PULSE)
|
||||
static const enum audio_driver_enum AUDIO_DEFAULT_DRIVER = AUDIO_PULSE;
|
||||
#elif defined(HAVE_ALSA) && defined(HAVE_VIDEOCORE)
|
||||
#elif defined(HAVE_ALSA) && defined(HAVE_THREADS)
|
||||
static const enum audio_driver_enum AUDIO_DEFAULT_DRIVER = AUDIO_ALSATHREAD;
|
||||
#elif defined(HAVE_ALSA)
|
||||
static const enum audio_driver_enum AUDIO_DEFAULT_DRIVER = AUDIO_ALSA;
|
||||
@@ -465,7 +469,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 +871,8 @@ const char *config_get_default_video(void)
|
||||
return "vga";
|
||||
case VIDEO_FPGA:
|
||||
return "fpga";
|
||||
case VIDEO_RSX:
|
||||
return "rsx";
|
||||
case VIDEO_NULL:
|
||||
break;
|
||||
}
|
||||
@@ -1085,6 +1091,8 @@ const char *config_get_default_wifi(void)
|
||||
{
|
||||
case WIFI_CONNMANCTL:
|
||||
return "connmanctl";
|
||||
case WIFI_NMCLI:
|
||||
return "nmcli";
|
||||
case WIFI_NULL:
|
||||
break;
|
||||
}
|
||||
@@ -1279,6 +1287,10 @@ static struct config_array_setting *populate_settings_array(settings_t *settings
|
||||
SETTING_ARRAY("discord_app_id", settings->arrays.discord_app_id, true, DEFAULT_DISCORD_APP_ID, true);
|
||||
SETTING_ARRAY("ai_service_url", settings->arrays.ai_service_url, true, DEFAULT_AI_SERVICE_URL, true);
|
||||
SETTING_ARRAY("crt_switch_timings", settings->arrays.crt_switch_timings, false, NULL, true);
|
||||
#ifdef HAVE_LAKKA
|
||||
SETTING_ARRAY("cpu_main_gov", settings->arrays.cpu_main_gov, false, NULL, true);
|
||||
SETTING_ARRAY("cpu_menu_gov", settings->arrays.cpu_menu_gov, false, NULL, true);
|
||||
#endif
|
||||
|
||||
*size = count;
|
||||
|
||||
@@ -1448,6 +1460,7 @@ static struct config_bool_setting *populate_settings_bool(
|
||||
SETTING_BOOL("frame_time_counter_reset_after_load_state", &settings->bools.frame_time_counter_reset_after_load_state, true, false, false);
|
||||
SETTING_BOOL("frame_time_counter_reset_after_save_state", &settings->bools.frame_time_counter_reset_after_save_state, true, false, false);
|
||||
SETTING_BOOL("crt_switch_resolution_use_custom_refresh_rate", &settings->bools.crt_switch_custom_refresh_enable, true, false, false);
|
||||
SETTING_BOOL("crt_switch_hires_menu", &settings->bools.crt_switch_hires_menu, true, false, true);
|
||||
SETTING_BOOL("ui_companion_start_on_boot", &settings->bools.ui_companion_start_on_boot, true, ui_companion_start_on_boot, false);
|
||||
SETTING_BOOL("ui_companion_enable", &settings->bools.ui_companion_enable, true, ui_companion_enable, false);
|
||||
SETTING_BOOL("ui_companion_toggle", &settings->bools.ui_companion_toggle, false, ui_companion_toggle, false);
|
||||
@@ -1485,6 +1498,7 @@ static struct config_bool_setting *populate_settings_bool(
|
||||
SETTING_BOOL("input_descriptor_hide_unbound", &settings->bools.input_descriptor_hide_unbound, true, input_descriptor_hide_unbound, false);
|
||||
SETTING_BOOL("load_dummy_on_core_shutdown", &settings->bools.load_dummy_on_core_shutdown, true, DEFAULT_LOAD_DUMMY_ON_CORE_SHUTDOWN, false);
|
||||
SETTING_BOOL("check_firmware_before_loading", &settings->bools.check_firmware_before_loading, true, DEFAULT_CHECK_FIRMWARE_BEFORE_LOADING, false);
|
||||
SETTING_BOOL("core_info_cache_enable", &settings->bools.core_info_cache_enable, true, DEFAULT_CORE_INFO_CACHE_ENABLE, false);
|
||||
#ifndef HAVE_DYNAMIC
|
||||
SETTING_BOOL("always_reload_core_on_run_content", &settings->bools.always_reload_core_on_run_content, true, DEFAULT_ALWAYS_RELOAD_CORE_ON_RUN_CONTENT, false);
|
||||
#endif
|
||||
@@ -1527,6 +1541,9 @@ static struct config_bool_setting *populate_settings_bool(
|
||||
SETTING_BOOL("auto_screenshot_filename", &settings->bools.auto_screenshot_filename, true, DEFAULT_AUTO_SCREENSHOT_FILENAME, false);
|
||||
SETTING_BOOL("video_force_srgb_disable", &settings->bools.video_force_srgb_disable, true, false, false);
|
||||
SETTING_BOOL("video_fullscreen", &settings->bools.video_fullscreen, true, DEFAULT_FULLSCREEN, false);
|
||||
#ifdef __WINRT__
|
||||
SETTING_BOOL("video_force_resolution", &settings->bools.video_force_resolution, true, DEFAULT_FORCE_RESOLUTION, false);
|
||||
#endif
|
||||
SETTING_BOOL("bundle_assets_extract_enable", &settings->bools.bundle_assets_extract_enable, true, DEFAULT_BUNDLE_ASSETS_EXTRACT_ENABLE, false);
|
||||
SETTING_BOOL("video_vsync", &settings->bools.video_vsync, true, DEFAULT_VSYNC, false);
|
||||
SETTING_BOOL("video_adaptive_vsync", &settings->bools.video_adaptive_vsync, true, DEFAULT_ADAPTIVE_VSYNC, false);
|
||||
@@ -1726,6 +1743,7 @@ static struct config_bool_setting *populate_settings_bool(
|
||||
SETTING_BOOL("cheevos_enable", &settings->bools.cheevos_enable, true, DEFAULT_CHEEVOS_ENABLE, false);
|
||||
SETTING_BOOL("cheevos_test_unofficial", &settings->bools.cheevos_test_unofficial, true, false, false);
|
||||
SETTING_BOOL("cheevos_hardcore_mode_enable", &settings->bools.cheevos_hardcore_mode_enable, true, false, false);
|
||||
SETTING_BOOL("cheevos_challenge_indicators", &settings->bools.cheevos_challenge_indicators, true, true, false);
|
||||
SETTING_BOOL("cheevos_richpresence_enable", &settings->bools.cheevos_richpresence_enable, true, true, false);
|
||||
SETTING_BOOL("cheevos_unlock_sound_enable", &settings->bools.cheevos_unlock_sound_enable, true, false, false);
|
||||
SETTING_BOOL("cheevos_verbose_enable", &settings->bools.cheevos_verbose_enable, true, false, false);
|
||||
@@ -1736,7 +1754,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);
|
||||
@@ -1810,6 +1827,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);
|
||||
@@ -2023,7 +2041,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);
|
||||
@@ -2060,7 +2078,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);
|
||||
@@ -2112,6 +2135,12 @@ static struct config_uint_setting *populate_settings_uint(
|
||||
|
||||
SETTING_UINT("video_black_frame_insertion", &settings->uints.video_black_frame_insertion, true, DEFAULT_BLACK_FRAME_INSERTION, false);
|
||||
|
||||
#ifdef HAVE_LAKKA
|
||||
SETTING_UINT("cpu_scaling_mode", &settings->uints.cpu_scaling_mode, true, 0, false);
|
||||
SETTING_UINT("cpu_min_freq", &settings->uints.cpu_min_freq, true, 1, false);
|
||||
SETTING_UINT("cpu_max_freq", &settings->uints.cpu_max_freq, true, ~0U, false);
|
||||
#endif
|
||||
|
||||
*size = count;
|
||||
|
||||
return tmp;
|
||||
@@ -3251,8 +3280,8 @@ static bool config_load_file(global_t *global,
|
||||
if (settings->uints.video_hard_sync_frames > 3)
|
||||
settings->uints.video_hard_sync_frames = 3;
|
||||
|
||||
if (settings->uints.video_frame_delay > 15)
|
||||
settings->uints.video_frame_delay = 15;
|
||||
if (settings->uints.video_frame_delay > MAXIMUM_FRAME_DELAY)
|
||||
settings->uints.video_frame_delay = MAXIMUM_FRAME_DELAY;
|
||||
|
||||
settings->uints.video_swap_interval = MAX(settings->uints.video_swap_interval, 1);
|
||||
settings->uints.video_swap_interval = MIN(settings->uints.video_swap_interval, 4);
|
||||
@@ -3641,20 +3670,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);
|
||||
|
||||
@@ -3670,20 +3702,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);
|
||||
|
||||
@@ -4629,9 +4664,12 @@ bool input_remapping_load_file(void *data, const char *path)
|
||||
else
|
||||
{
|
||||
char stk_ident[128];
|
||||
char key_ident[128];
|
||||
int stk_remap = -1;
|
||||
int key_remap = -1;
|
||||
|
||||
stk_ident[0] = '\0';
|
||||
key_ident[0] = '\0';
|
||||
|
||||
fill_pathname_join_delim(stk_ident, s3,
|
||||
key_string, '$', sizeof(stk_ident));
|
||||
@@ -4650,6 +4688,15 @@ bool input_remapping_load_file(void *data, const char *path)
|
||||
configuration_set_uint(settings,
|
||||
settings->uints.input_remap_ids[i][j], stk_remap);
|
||||
}
|
||||
|
||||
fill_pathname_join_delim(key_ident, s2,
|
||||
key_string, '_', sizeof(key_ident));
|
||||
|
||||
if (!config_get_int(conf, key_ident, &key_remap))
|
||||
key_remap = RETROK_UNKNOWN;
|
||||
|
||||
configuration_set_uint(settings,
|
||||
settings->uints.input_keymapper_ids[i][j], key_remap);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4747,6 +4794,7 @@ bool input_remapping_save_file(const char *path)
|
||||
{
|
||||
char stk_ident[128];
|
||||
unsigned remap_id = settings->uints.input_remap_ids[i][j];
|
||||
unsigned keymap_id = settings->uints.input_keymapper_ids[i][j];
|
||||
const char *key_string = key_strings[j];
|
||||
stk_ident[0] = '\0';
|
||||
fill_pathname_join_delim(stk_ident, s3,
|
||||
@@ -4763,6 +4811,16 @@ bool input_remapping_save_file(const char *path)
|
||||
config_set_int(conf, stk_ident,
|
||||
settings->uints.input_remap_ids[i][j]);
|
||||
}
|
||||
|
||||
if (keymap_id != RETROK_UNKNOWN)
|
||||
{
|
||||
char key_ident[128];
|
||||
key_ident[0] = '\0';
|
||||
fill_pathname_join_delim(key_ident, s2,
|
||||
key_string, '_', sizeof(key_ident));
|
||||
config_set_int(conf, key_ident,
|
||||
settings->uints.input_keymapper_ids[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(s1, sizeof(s1), "input_libretro_device_p%u", i + 1);
|
||||
|
||||
+19
-4
@@ -67,7 +67,9 @@ enum crt_switch_type
|
||||
{
|
||||
CRT_SWITCH_NONE = 0,
|
||||
CRT_SWITCH_15KHZ,
|
||||
CRT_SWITCH_31KHZ
|
||||
CRT_SWITCH_31KHZ,
|
||||
CRT_SWITCH_32_120,
|
||||
CRT_SWITCH_INI
|
||||
};
|
||||
|
||||
enum override_type
|
||||
@@ -134,7 +136,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 +268,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;
|
||||
|
||||
@@ -288,6 +291,12 @@ typedef struct settings
|
||||
unsigned core_updater_auto_backup_history_size;
|
||||
unsigned video_black_frame_insertion;
|
||||
unsigned quit_on_close_content;
|
||||
|
||||
#ifdef HAVE_LAKKA
|
||||
unsigned cpu_scaling_mode;
|
||||
unsigned cpu_min_freq;
|
||||
unsigned cpu_max_freq;
|
||||
#endif
|
||||
} uints;
|
||||
|
||||
struct
|
||||
@@ -387,6 +396,8 @@ typedef struct settings
|
||||
char crt_switch_timings[255];
|
||||
#ifdef HAVE_LAKKA
|
||||
char timezone[TIMEZONE_LENGTH];
|
||||
char cpu_main_gov[32];
|
||||
char cpu_menu_gov[32];
|
||||
#endif
|
||||
} arrays;
|
||||
|
||||
@@ -508,6 +519,7 @@ typedef struct settings
|
||||
#ifdef HAVE_VIDEO_LAYOUT
|
||||
bool video_layout_enable;
|
||||
#endif
|
||||
bool video_force_resolution;
|
||||
|
||||
/* Accessibility */
|
||||
bool accessibility_enable;
|
||||
@@ -533,7 +545,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;
|
||||
@@ -688,6 +699,7 @@ typedef struct settings
|
||||
bool kiosk_mode_enable;
|
||||
|
||||
bool crt_switch_custom_refresh_enable;
|
||||
bool crt_switch_hires_menu;
|
||||
|
||||
/* Netplay */
|
||||
bool netplay_public_announce;
|
||||
@@ -723,6 +735,7 @@ typedef struct settings
|
||||
bool cheevos_auto_screenshot;
|
||||
bool cheevos_start_active;
|
||||
bool cheevos_unlock_sound_enable;
|
||||
bool cheevos_challenge_indicators;
|
||||
|
||||
/* Camera */
|
||||
bool camera_allow;
|
||||
@@ -777,6 +790,7 @@ typedef struct settings
|
||||
bool network_remote_enable_user[MAX_USERS];
|
||||
bool load_dummy_on_core_shutdown;
|
||||
bool check_firmware_before_loading;
|
||||
bool core_info_cache_enable;
|
||||
#ifndef HAVE_DYNAMIC
|
||||
bool always_reload_core_on_run_content;
|
||||
#endif
|
||||
@@ -816,6 +830,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;
|
||||
|
||||
|
||||
@@ -124,6 +124,10 @@ bool content_set_subsystem_by_name(const char* subsystem_name);
|
||||
/* Get the current subsystem "friendly name" */
|
||||
void content_get_subsystem_friendly_name(const char* subsystem_name, char* subsystem_friendly_name, size_t len);
|
||||
|
||||
/* Sets overrides which modify frontend handling of
|
||||
* specific content file types */
|
||||
bool content_file_override_set(const struct retro_system_content_info_override *overrides);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
||||
+3
-18
@@ -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(¤t_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;
|
||||
}
|
||||
+1458
-96
File diff suppressed because it is too large.
Load diff
+8
-1
@@ -89,6 +89,7 @@ typedef struct
|
||||
bool database_match_archive_member;
|
||||
bool is_experimental;
|
||||
bool is_locked;
|
||||
bool is_installed;
|
||||
} core_info_t;
|
||||
|
||||
/* A subset of core_info parameters required for
|
||||
@@ -156,7 +157,7 @@ bool core_info_get_current_core(core_info_t **core);
|
||||
void core_info_deinit_list(void);
|
||||
|
||||
bool core_info_init_list(const char *path_info, const char *dir_cores,
|
||||
const char *exts, bool show_hidden_files);
|
||||
const char *exts, bool show_hidden_files, bool enable_cache);
|
||||
|
||||
bool core_info_get_list(core_info_list_t **core);
|
||||
|
||||
@@ -203,6 +204,12 @@ core_info_state_t *coreinfo_get_ptr(void);
|
||||
|
||||
bool core_info_core_file_id_is_equal(const char *core_path_a, const char *core_path_b);
|
||||
|
||||
/* When called, generates a temporary file
|
||||
* that will force an info cache refresh the
|
||||
* next time that core info is initialised with
|
||||
* caching enabled */
|
||||
bool core_info_cache_force_refresh(const char *path_info);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif /* CORE_INFO_H_ */
|
||||
Vendored
+2
@@ -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
|
||||
|
||||
|
||||
Vendored
+4
-1
@@ -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)); }
|
||||
|
||||
Vendored
+15
-3
@@ -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();
|
||||
|
||||
Vendored
+144
-73
@@ -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,10 @@ 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 SceGxmMultisampleMode current_msaa = SCE_GXM_MULTISAMPLE_4X;
|
||||
|
||||
static int pgf_module_was_loaded = 0;
|
||||
|
||||
static const SceGxmProgram *const clearVertexProgramGxp = &clear_v_gxp;
|
||||
@@ -60,8 +61,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 +153,101 @@ 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){
|
||||
int i,x,y,err;
|
||||
|
||||
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 = 960;
|
||||
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;
|
||||
|
||||
if(renderTarget != NULL){
|
||||
sceGxmDestroyRenderTarget(renderTarget);
|
||||
|
||||
for (i = 0; i < DISPLAY_BUFFER_COUNT; i++) {
|
||||
// clear the buffer then deallocate
|
||||
memset(displayBufferData[i], 0, video_mode_data.height*video_mode_data.stride*4);
|
||||
gpu_free(displayBufferUid[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// allocate memory and sync objects for display buffers
|
||||
for (i = 0; i < DISPLAY_BUFFER_COUNT; i++) {
|
||||
// allocate memory for display
|
||||
displayBufferData[i] = gpu_alloc(
|
||||
SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW,
|
||||
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 < 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;
|
||||
}
|
||||
}
|
||||
|
||||
// initialize a color surface for this display buffer
|
||||
err = sceGxmColorSurfaceInit(
|
||||
&displaySurface[i],
|
||||
DISPLAY_COLOR_FORMAT,
|
||||
SCE_GXM_COLOR_SURFACE_LINEAR,
|
||||
(current_msaa == SCE_GXM_MULTISAMPLE_NONE) ? SCE_GXM_COLOR_SURFACE_SCALE_NONE : SCE_GXM_COLOR_SURFACE_SCALE_MSAA_DOWNSCALE,
|
||||
SCE_GXM_OUTPUT_REGISTER_SIZE_32BIT,
|
||||
video_mode_data.width,
|
||||
video_mode_data.height,
|
||||
video_mode_data.stride,
|
||||
displayBufferData[i]);
|
||||
|
||||
}
|
||||
|
||||
// set up parameters
|
||||
SceGxmRenderTargetParams renderTargetParams;
|
||||
memset(&renderTargetParams, 0, sizeof(SceGxmRenderTargetParams));
|
||||
renderTargetParams.flags = 0;
|
||||
renderTargetParams.width = video_mode_data.width;
|
||||
renderTargetParams.height = video_mode_data.height;
|
||||
renderTargetParams.scenesPerFrame = 1;
|
||||
renderTargetParams.multisampleMode = current_msaa;
|
||||
renderTargetParams.multisampleLocations = 0;
|
||||
renderTargetParams.driverMemBlock = -1; // Invalid UID
|
||||
|
||||
// create the render target
|
||||
err = sceGxmCreateRenderTarget(&renderTargetParams, &renderTarget);
|
||||
VITA2D_DEBUG("sceGxmCreateRenderTarget(): 0x%08X\n", err);
|
||||
|
||||
|
||||
|
||||
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 +256,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) == SCE_DISPLAY_ERROR_INVALID_RESOLUTION){
|
||||
if(video_mode_initial)
|
||||
vita2d_switch_video_mode(VITA2D_VIDEO_MODE_960x544);
|
||||
}
|
||||
|
||||
if (vblank_wait) {
|
||||
sceDisplayWaitVblankStart();
|
||||
@@ -218,10 +317,10 @@ 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;
|
||||
unsigned int i;
|
||||
UNUSED(err);
|
||||
|
||||
if (vita2d_initialized) {
|
||||
@@ -229,6 +328,9 @@ static int vita2d_init_internal(unsigned int temp_pool_size, SceGxmMultisampleMo
|
||||
return 1;
|
||||
}
|
||||
|
||||
video_mode_initial = video_mode;
|
||||
current_msaa = msaa;
|
||||
|
||||
SceGxmInitializeParams initializeParams;
|
||||
memset(&initializeParams, 0, sizeof(SceGxmInitializeParams));
|
||||
initializeParams.flags = 0;
|
||||
@@ -284,65 +386,32 @@ static int vita2d_init_internal(unsigned int temp_pool_size, SceGxmMultisampleMo
|
||||
err = sceGxmCreateContext(&contextParams, &_vita2d_context);
|
||||
VITA2D_DEBUG("sceGxmCreateContext(): 0x%08X\n", err);
|
||||
|
||||
// set up parameters
|
||||
SceGxmRenderTargetParams renderTargetParams;
|
||||
memset(&renderTargetParams, 0, sizeof(SceGxmRenderTargetParams));
|
||||
renderTargetParams.flags = 0;
|
||||
renderTargetParams.width = DISPLAY_WIDTH;
|
||||
renderTargetParams.height = DISPLAY_HEIGHT;
|
||||
renderTargetParams.scenesPerFrame = 1;
|
||||
renderTargetParams.multisampleMode = msaa;
|
||||
renderTargetParams.multisampleLocations = 0;
|
||||
renderTargetParams.driverMemBlock = -1; // Invalid UID
|
||||
err = vita2d_switch_video_mode(video_mode);
|
||||
if(err<0){
|
||||
VITA2D_DEBUG("vita2d_switch_video_mode(): 0x%08X\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
// create the render target
|
||||
err = sceGxmCreateRenderTarget(&renderTargetParams, &renderTarget);
|
||||
VITA2D_DEBUG("sceGxmCreateRenderTarget(): 0x%08X\n", err);
|
||||
vita2d_display_data displayData;
|
||||
displayData.address = displayBufferData[0];
|
||||
display_callback(&displayData);
|
||||
|
||||
// allocate memory and sync objects for display buffers
|
||||
for (i = 0; i < DISPLAY_BUFFER_COUNT; i++) {
|
||||
// allocate memory for display
|
||||
displayBufferData[i] = gpu_alloc(
|
||||
SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW,
|
||||
4*DISPLAY_STRIDE_IN_PIXELS*DISPLAY_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++) {
|
||||
row[x] = 0xff000000;
|
||||
}
|
||||
}
|
||||
|
||||
// initialize a color surface for this display buffer
|
||||
err = sceGxmColorSurfaceInit(
|
||||
&displaySurface[i],
|
||||
DISPLAY_COLOR_FORMAT,
|
||||
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,
|
||||
displayBufferData[i]);
|
||||
|
||||
// create a sync object that we will associate with this buffer
|
||||
err = sceGxmSyncObjectCreate(&displayBufferSync[i]);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if (current_msaa == SCE_GXM_MULTISAMPLE_4X) {
|
||||
// samples increase in X and Y
|
||||
sampleCount *= 4;
|
||||
depthStrideInSamples *= 2;
|
||||
} else if (msaa == SCE_GXM_MULTISAMPLE_2X) {
|
||||
} else if (current_msaa == SCE_GXM_MULTISAMPLE_2X) {
|
||||
// samples increase in Y only
|
||||
sampleCount *= 2;
|
||||
}
|
||||
@@ -527,7 +596,7 @@ static int vita2d_init_internal(unsigned int temp_pool_size, SceGxmMultisampleMo
|
||||
shaderPatcher,
|
||||
clearFragmentProgramId,
|
||||
SCE_GXM_OUTPUT_REGISTER_FORMAT_UCHAR4,
|
||||
msaa,
|
||||
current_msaa,
|
||||
NULL,
|
||||
clearVertexProgramGxp,
|
||||
&clearFragmentProgram);
|
||||
@@ -686,8 +755,8 @@ static int vita2d_init_internal(unsigned int temp_pool_size, SceGxmMultisampleMo
|
||||
VITA2D_DEBUG("texture sceGxmShaderPatcherCreateVertexProgram(): 0x%08X\n", err);
|
||||
|
||||
// Create variations of the fragment program based on blending mode
|
||||
_vita2d_make_fragment_programs(&_vita2d_fragmentPrograms.blend_mode_normal, &blend_info, msaa);
|
||||
_vita2d_make_fragment_programs(&_vita2d_fragmentPrograms.blend_mode_add, &blend_info_add, msaa);
|
||||
_vita2d_make_fragment_programs(&_vita2d_fragmentPrograms.blend_mode_normal, &blend_info, current_msaa);
|
||||
_vita2d_make_fragment_programs(&_vita2d_fragmentPrograms.blend_mode_add, &blend_info_add, current_msaa);
|
||||
|
||||
// Default to "normal" blending mode (non-additive)
|
||||
vita2d_set_blend_mode_add(0);
|
||||
@@ -713,9 +782,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;
|
||||
@@ -731,22 +800,23 @@ static int vita2d_init_internal(unsigned int temp_pool_size, SceGxmMultisampleMo
|
||||
|
||||
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()
|
||||
{
|
||||
sceGxmFinish(_vita2d_context);
|
||||
if(vita2d_initialized)
|
||||
sceGxmFinish(_vita2d_context);
|
||||
}
|
||||
|
||||
int vita2d_fini()
|
||||
@@ -780,7 +850,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
|
||||
@@ -808,6 +878,7 @@ int vita2d_fini()
|
||||
|
||||
// destroy the render target
|
||||
sceGxmDestroyRenderTarget(renderTarget);
|
||||
renderTarget = NULL;
|
||||
|
||||
// destroy the _vita2d_context
|
||||
sceGxmDestroyContext(_vita2d_context);
|
||||
@@ -933,7 +1004,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 +1023,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 +1074,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 +1171,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;
|
||||
|
||||
Vendored
-476
@@ -1,476 +0,0 @@
|
||||
PTHREADS-WIN32 RELEASE 2.8.0 (2006-12-22)
|
||||
-----------------------------------------
|
||||
Web Site: http://sources.redhat.com/pthreads-win32/
|
||||
FTP Site: ftp://sources.redhat.com/pub/pthreads-win32
|
||||
Maintainer: Ross Johnson <[email protected]>
|
||||
|
||||
|
||||
We are pleased to announce the availability of a new release of
|
||||
Pthreads-win32, an Open Source Software implementation of the
|
||||
Threads component of the POSIX 1003.1 2001 Standard for Microsoft's
|
||||
Win32 environment. Some functions from other sections of POSIX
|
||||
1003.1 2001 are also supported including semaphores and scheduling
|
||||
functions.
|
||||
|
||||
Some common non-portable functions are also implemented for
|
||||
additional compatibility, as are a few functions specific
|
||||
to pthreads-win32 for easier integration with Win32 applications.
|
||||
|
||||
Pthreads-win32 is free software, distributed under the GNU Lesser
|
||||
General Public License (LGPL).
|
||||
|
||||
|
||||
Acknowledgements
|
||||
----------------
|
||||
This library is based originally on a Win32 pthreads
|
||||
implementation contributed by John Bossom <[email protected]>.
|
||||
|
||||
The implementation of Condition Variables uses algorithms developed
|
||||
by Alexander Terekhov and Louis Thomas.
|
||||
|
||||
The implementation of POSIX mutexes has been improved by Thomas Pfaff
|
||||
and later by Alexander Terekhov.
|
||||
|
||||
The implementation of Spinlocks and Barriers was contributed
|
||||
by Ross Johnson.
|
||||
|
||||
The implementation of read/write locks was contributed by
|
||||
Aurelio Medina and improved by Alexander Terekhov.
|
||||
|
||||
Many others have contributed significant time and effort to solve crutial
|
||||
problems in order to make the library workable, robust and reliable.
|
||||
|
||||
Thanks to Xavier Leroy for granting permission to use and modify his
|
||||
LinuxThreads manual pages.
|
||||
|
||||
Thanks to The Open Group for making the Single Unix Specification
|
||||
publicly available - many of the manual pages included in the package
|
||||
were extracted from it.
|
||||
|
||||
There is also a separate CONTRIBUTORS file. This file and others are
|
||||
on the web site:
|
||||
|
||||
http://sources.redhat.com/pthreads-win32
|
||||
|
||||
As much as possible, the ChangeLog file acknowledges contributions to the
|
||||
code base in more detail.
|
||||
|
||||
|
||||
Changes since the last release
|
||||
------------------------------
|
||||
These are now documented in the NEWS file.
|
||||
See the ChangeLog file also.
|
||||
|
||||
|
||||
Known Bugs
|
||||
----------
|
||||
These are now documented in the BUGS file.
|
||||
|
||||
|
||||
Level of standards conformance
|
||||
------------------------------
|
||||
|
||||
The following POSIX 1003.1 2001 options are defined and set to 200112L:
|
||||
|
||||
_POSIX_THREADS
|
||||
_POSIX_THREAD_SAFE_FUNCTIONS
|
||||
_POSIX_THREAD_ATTR_STACKSIZE
|
||||
_POSIX_THREAD_PRIORITY_SCHEDULING
|
||||
_POSIX_SEMAPHORES
|
||||
_POSIX_READER_WRITER_LOCKS
|
||||
_POSIX_SPIN_LOCKS
|
||||
_POSIX_BARRIERS
|
||||
|
||||
|
||||
The following POSIX 1003.1 2001 options are defined and set to -1:
|
||||
|
||||
_POSIX_THREAD_ATTR_STACKADDR
|
||||
_POSIX_THREAD_PRIO_INHERIT
|
||||
_POSIX_THREAD_PRIO_PROTECT
|
||||
_POSIX_THREAD_PROCESS_SHARED
|
||||
|
||||
|
||||
The following POSIX 1003.1 2001 limits are defined and set:
|
||||
|
||||
_POSIX_THREAD_THREADS_MAX
|
||||
_POSIX_SEM_VALUE_MAX
|
||||
_POSIX_SEM_NSEMS_MAX
|
||||
_POSIX_THREAD_KEYS_MAX
|
||||
_POSIX_THREAD_DESTRUCTOR_ITERATIONS
|
||||
PTHREAD_STACK_MIN
|
||||
PTHREAD_THREADS_MAX
|
||||
SEM_VALUE_MAX
|
||||
SEM_NSEMS_MAX
|
||||
PTHREAD_KEYS_MAX
|
||||
PTHREAD_DESTRUCTOR_ITERATIONS
|
||||
|
||||
|
||||
The following functions are implemented:
|
||||
|
||||
---------------------------
|
||||
PThreads
|
||||
---------------------------
|
||||
pthread_attr_init
|
||||
pthread_attr_destroy
|
||||
pthread_attr_getdetachstate
|
||||
pthread_attr_getstackaddr
|
||||
pthread_attr_getstacksize
|
||||
pthread_attr_setdetachstate
|
||||
pthread_attr_setstackaddr
|
||||
pthread_attr_setstacksize
|
||||
|
||||
pthread_create
|
||||
pthread_detach
|
||||
pthread_equal
|
||||
pthread_exit
|
||||
pthread_join
|
||||
pthread_once
|
||||
pthread_self
|
||||
|
||||
pthread_cancel
|
||||
pthread_cleanup_pop
|
||||
pthread_cleanup_push
|
||||
pthread_setcancelstate
|
||||
pthread_setcanceltype
|
||||
pthread_testcancel
|
||||
|
||||
---------------------------
|
||||
Thread Specific Data
|
||||
---------------------------
|
||||
pthread_key_create
|
||||
pthread_key_delete
|
||||
pthread_setspecific
|
||||
pthread_getspecific
|
||||
|
||||
---------------------------
|
||||
Mutexes
|
||||
---------------------------
|
||||
pthread_mutexattr_init
|
||||
pthread_mutexattr_destroy
|
||||
pthread_mutexattr_getpshared
|
||||
pthread_mutexattr_setpshared
|
||||
pthread_mutexattr_gettype
|
||||
pthread_mutexattr_settype (types: PTHREAD_MUTEX_DEFAULT
|
||||
PTHREAD_MUTEX_NORMAL
|
||||
PTHREAD_MUTEX_ERRORCHECK
|
||||
PTHREAD_MUTEX_RECURSIVE )
|
||||
pthread_mutex_init
|
||||
pthread_mutex_destroy
|
||||
pthread_mutex_lock
|
||||
pthread_mutex_trylock
|
||||
pthread_mutex_timedlock
|
||||
pthread_mutex_unlock
|
||||
|
||||
---------------------------
|
||||
Condition Variables
|
||||
---------------------------
|
||||
pthread_condattr_init
|
||||
pthread_condattr_destroy
|
||||
pthread_condattr_getpshared
|
||||
pthread_condattr_setpshared
|
||||
|
||||
pthread_cond_init
|
||||
pthread_cond_destroy
|
||||
pthread_cond_wait
|
||||
pthread_cond_timedwait
|
||||
pthread_cond_signal
|
||||
pthread_cond_broadcast
|
||||
|
||||
---------------------------
|
||||
Read/Write Locks
|
||||
---------------------------
|
||||
pthread_rwlock_init
|
||||
pthread_rwlock_destroy
|
||||
pthread_rwlock_tryrdlock
|
||||
pthread_rwlock_trywrlock
|
||||
pthread_rwlock_rdlock
|
||||
pthread_rwlock_timedrdlock
|
||||
pthread_rwlock_rwlock
|
||||
pthread_rwlock_timedwrlock
|
||||
pthread_rwlock_unlock
|
||||
pthread_rwlockattr_init
|
||||
pthread_rwlockattr_destroy
|
||||
pthread_rwlockattr_getpshared
|
||||
pthread_rwlockattr_setpshared
|
||||
|
||||
---------------------------
|
||||
Spin Locks
|
||||
---------------------------
|
||||
pthread_spin_init
|
||||
pthread_spin_destroy
|
||||
pthread_spin_lock
|
||||
pthread_spin_unlock
|
||||
pthread_spin_trylock
|
||||
|
||||
---------------------------
|
||||
Barriers
|
||||
---------------------------
|
||||
pthread_barrier_init
|
||||
pthread_barrier_destroy
|
||||
pthread_barrier_wait
|
||||
pthread_barrierattr_init
|
||||
pthread_barrierattr_destroy
|
||||
pthread_barrierattr_getpshared
|
||||
pthread_barrierattr_setpshared
|
||||
|
||||
---------------------------
|
||||
Semaphores
|
||||
---------------------------
|
||||
sem_init
|
||||
sem_destroy
|
||||
sem_post
|
||||
sem_wait
|
||||
sem_trywait
|
||||
sem_timedwait
|
||||
sem_getvalue (# free if +ve, # of waiters if -ve)
|
||||
sem_open (returns an error ENOSYS)
|
||||
sem_close (returns an error ENOSYS)
|
||||
sem_unlink (returns an error ENOSYS)
|
||||
|
||||
---------------------------
|
||||
RealTime Scheduling
|
||||
---------------------------
|
||||
pthread_attr_getschedparam
|
||||
pthread_attr_setschedparam
|
||||
pthread_attr_getinheritsched
|
||||
pthread_attr_setinheritsched
|
||||
pthread_attr_getschedpolicy (only supports SCHED_OTHER)
|
||||
pthread_attr_setschedpolicy (only supports SCHED_OTHER)
|
||||
pthread_getschedparam
|
||||
pthread_setschedparam
|
||||
pthread_getconcurrency
|
||||
pthread_setconcurrency
|
||||
pthread_attr_getscope
|
||||
pthread_attr_setscope (only supports PTHREAD_SCOPE_SYSTEM)
|
||||
sched_get_priority_max
|
||||
sched_get_priority_min
|
||||
sched_rr_get_interval (returns an error ENOTSUP)
|
||||
sched_setscheduler (only supports SCHED_OTHER)
|
||||
sched_getscheduler (only supports SCHED_OTHER)
|
||||
sched_yield
|
||||
|
||||
---------------------------
|
||||
Signals
|
||||
---------------------------
|
||||
pthread_sigmask
|
||||
pthread_kill (only supports zero sig value,
|
||||
for thread validity checking)
|
||||
|
||||
---------------------------
|
||||
Non-portable routines (see the README.NONPORTABLE file for usage)
|
||||
---------------------------
|
||||
pthread_getw32threadhandle_np
|
||||
pthread_timechange_handler_np
|
||||
pthread_delay_np
|
||||
pthread_mutexattr_getkind_np
|
||||
pthread_mutexattr_setkind_np (types: PTHREAD_MUTEX_FAST_NP,
|
||||
PTHREAD_MUTEX_ERRORCHECK_NP,
|
||||
PTHREAD_MUTEX_RECURSIVE_NP,
|
||||
PTHREAD_MUTEX_ADAPTIVE_NP,
|
||||
PTHREAD_MUTEX_TIMED_NP)
|
||||
pthread_num_processors_np
|
||||
pthread_win32_process_attach_np (Required when statically linking
|
||||
the library)
|
||||
pthread_win32_process_detach_np (Required when statically linking
|
||||
the library)
|
||||
pthread_win32_thread_attach_np (Required when statically linking
|
||||
the library)
|
||||
pthread_win32_thread_detach_np (Required when statically linking
|
||||
the library)
|
||||
|
||||
---------------------------
|
||||
Static Initializers
|
||||
---------------------------
|
||||
PTHREAD_ONCE_INIT
|
||||
PTHREAD_MUTEX_INITIALIZER
|
||||
PTHREAD_RECURSIVE_MUTEX_INITIALIZER
|
||||
PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
|
||||
PTHREAD_ERRORCHECK_MUTEX_INITIALIZER
|
||||
PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
|
||||
PTHREAD_COND_INITIALIZER
|
||||
PTHREAD_RWLOCK_INITIALIZER
|
||||
PTHREAD_SPINLOCK_INITIALIZER
|
||||
|
||||
---------------------------
|
||||
Thread-Safe C Runtime Library (macros)
|
||||
---------------------------
|
||||
strtok_r
|
||||
asctime_r
|
||||
ctime_r
|
||||
gmtime_r
|
||||
localtime_r
|
||||
rand_r
|
||||
|
||||
|
||||
The following functions are not implemented:
|
||||
|
||||
---------------------------
|
||||
RealTime Scheduling
|
||||
---------------------------
|
||||
pthread_mutex_getprioceiling
|
||||
pthread_mutex_setprioceiling
|
||||
pthread_mutex_attr_getprioceiling
|
||||
pthread_mutex_attr_getprotocol
|
||||
pthread_mutex_attr_setprioceiling
|
||||
pthread_mutex_attr_setprotocol
|
||||
|
||||
---------------------------
|
||||
Fork Handlers
|
||||
---------------------------
|
||||
pthread_atfork
|
||||
|
||||
---------------------------
|
||||
Stdio
|
||||
---------------------------
|
||||
flockfile
|
||||
ftrylockfile
|
||||
funlockfile
|
||||
getc_unlocked
|
||||
getchar_unlocked
|
||||
putc_unlocked
|
||||
putchar_unlocked
|
||||
|
||||
---------------------------
|
||||
Thread-Safe C Runtime Library
|
||||
---------------------------
|
||||
readdir_r
|
||||
getgrgid_r
|
||||
getgrnam_r
|
||||
getpwuid_r
|
||||
getpwnam_r
|
||||
|
||||
---------------------------
|
||||
Signals
|
||||
---------------------------
|
||||
sigtimedwait
|
||||
sigwait
|
||||
sigwaitinfo
|
||||
|
||||
---------------------------
|
||||
General
|
||||
---------------------------
|
||||
sysconf
|
||||
|
||||
The library includes two non-API functions for creating cancellation
|
||||
points in applications and libraries:
|
||||
|
||||
pthreadCancelableWait
|
||||
pthreadCancelableTimedWait
|
||||
|
||||
|
||||
Availability
|
||||
------------
|
||||
|
||||
The prebuilt DLL, export libs (for both MSVC and Mingw32), and the header
|
||||
files (pthread.h, semaphore.h, sched.h) are available along with the
|
||||
complete source code.
|
||||
|
||||
The source code can be found at:
|
||||
|
||||
ftp://sources.redhat.com/pub/pthreads-win32
|
||||
|
||||
and as individual source code files at
|
||||
|
||||
ftp://sources.redhat.com/pub/pthreads-win32/source
|
||||
|
||||
The pre-built DLL, export libraries and include files can be found at:
|
||||
|
||||
ftp://sources.redhat.com/pub/pthreads-win32/dll-latest
|
||||
|
||||
|
||||
|
||||
Mailing List
|
||||
------------
|
||||
|
||||
There is a mailing list for discussing pthreads on Win32. To join,
|
||||
send email to:
|
||||
|
||||
[email protected]
|
||||
|
||||
|
||||
Application Development Environments
|
||||
------------------------------------
|
||||
|
||||
See the README file for more information.
|
||||
|
||||
MSVC:
|
||||
MSVC using SEH works. Distribute pthreadVSE.dll with your application.
|
||||
MSVC using C++ EH works. Distribute pthreadVCE.dll with your application.
|
||||
MSVC using C setjmp/longjmp works. Distribute pthreadVC.dll with your application.
|
||||
|
||||
|
||||
Mingw32:
|
||||
See the FAQ, Questions 6 and 10.
|
||||
|
||||
Mingw using C++ EH works. Distribute pthreadGCE.dll with your application.
|
||||
Mingw using C setjmp/longjmp works. Distribute pthreadGC.dll with your application.
|
||||
|
||||
|
||||
Cygwin: (http://sourceware.cygnus.com/cygwin/)
|
||||
Developers using Cygwin will not need pthreads-win32 since it has POSIX threads
|
||||
support. Refer to its documentation for details and extent.
|
||||
|
||||
|
||||
UWIN:
|
||||
UWIN is a complete Unix-like environment for Windows from AT&T. Pthreads-win32
|
||||
doesn't currently support UWIN (and vice versa), but that may change in the
|
||||
future.
|
||||
|
||||
Generally:
|
||||
For convenience, the following pre-built files are available on the FTP site
|
||||
(see Availability above):
|
||||
|
||||
pthread.h - for POSIX 1c threads
|
||||
semaphore.h - for POSIX 1b semaphores
|
||||
sched.h - for POSIX 1b scheduling
|
||||
pthreadVCE.dll - built with MSVC++ compiler using C++ EH
|
||||
pthreadVCE.lib
|
||||
pthreadVC.dll - built with MSVC compiler using C setjmp/longjmp
|
||||
pthreadVC.lib
|
||||
pthreadVSE.dll - built with MSVC compiler using SEH
|
||||
pthreadVSE.lib
|
||||
pthreadGCE.dll - built with Mingw32 G++ 2.95.2-1
|
||||
pthreadGC.dll - built with Mingw32 GCC 2.95.2-1 using setjmp/longjmp
|
||||
libpthreadGCE.a - derived from pthreadGCE.dll
|
||||
libpthreadGC.a - derived from pthreadGC.dll
|
||||
gcc.dll - needed if distributing applications that use
|
||||
pthreadGCE.dll (but see the FAQ Q 10 for the latest
|
||||
related information)
|
||||
|
||||
These are the only files you need in order to build POSIX threads
|
||||
applications for Win32 using either MSVC or Mingw32.
|
||||
|
||||
See the FAQ file in the source tree for additional information.
|
||||
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
For the authoritative reference, see the online POSIX
|
||||
standard reference at:
|
||||
|
||||
http://www.OpenGroup.org
|
||||
|
||||
For POSIX Thread API programming, several reference books are
|
||||
available:
|
||||
|
||||
Programming with POSIX Threads
|
||||
David R. Butenhof
|
||||
Addison-Wesley (pub)
|
||||
|
||||
Pthreads Programming
|
||||
By Bradford Nichols, Dick Buttlar & Jacqueline Proulx Farrell
|
||||
O'Reilly (pub)
|
||||
|
||||
On the web: see the links at the bottom of the pthreads-win32 site:
|
||||
|
||||
http://sources.redhat.com/pthreads-win32/
|
||||
|
||||
Currently, there is no documentation included in the package apart
|
||||
from the copious comments in the source code.
|
||||
|
||||
|
||||
|
||||
Enjoy!
|
||||
|
||||
Ross Johnson
|
||||
Vendored
-129
@@ -1,129 +0,0 @@
|
||||
Contributors (in approximate order of appearance)
|
||||
|
||||
[See also the ChangeLog file where individuals are
|
||||
attributed in log entries. Likewise in the FAQ file.]
|
||||
|
||||
Ben Elliston bje at cygnus dot com
|
||||
Initiated the project;
|
||||
setup the project infrastructure (CVS, web page, etc.);
|
||||
early prototype routines.
|
||||
Ross Johnson rpj at callisto dot canberra dot edu dot au
|
||||
early prototype routines;
|
||||
ongoing project coordination/maintenance;
|
||||
implementation of spin locks and barriers;
|
||||
various enhancements;
|
||||
bug fixes;
|
||||
documentation;
|
||||
testsuite.
|
||||
Robert Colquhoun rjc at trump dot net dot au
|
||||
Early bug fixes.
|
||||
John E. Bossom John dot Bossom at cognos dot com
|
||||
Contributed substantial original working implementation;
|
||||
bug fixes;
|
||||
ongoing guidance and standards interpretation.
|
||||
Anders Norlander anorland at hem2 dot passagen dot se
|
||||
Early enhancements and runtime checking for supported
|
||||
Win32 routines.
|
||||
Tor Lillqvist tml at iki dot fi
|
||||
General enhancements;
|
||||
early bug fixes to condition variables.
|
||||
Scott Lightner scott at curriculum dot com
|
||||
Bug fix.
|
||||
Kevin Ruland Kevin dot Ruland at anheuser-busch dot com
|
||||
Various bug fixes.
|
||||
Mike Russo miker at eai dot com
|
||||
Bug fix.
|
||||
Mark E. Armstrong avail at pacbell dot net
|
||||
Bug fixes.
|
||||
Lorin Hochstein lmh at xiphos dot ca
|
||||
general bug fixes; bug fixes to condition variables.
|
||||
Peter Slacik Peter dot Slacik at tatramed dot sk
|
||||
Bug fixes.
|
||||
Mumit Khan khan at xraylith dot wisc dot edu
|
||||
Fixes to work with Mingw32.
|
||||
Milan Gardian mg at tatramed dot sk
|
||||
Bug fixes and reports/analyses of obscure problems.
|
||||
Aurelio Medina aureliom at crt dot com
|
||||
First implementation of read-write locks.
|
||||
Graham Dumpleton Graham dot Dumpleton at ra dot pad dot otc dot telstra dot com dot au
|
||||
Bug fix in condition variables.
|
||||
Tristan Savatier tristan at mpegtv dot com
|
||||
WinCE port.
|
||||
Erik Hensema erik at hensema dot xs4all dot nl
|
||||
Bug fixes.
|
||||
Rich Peters rpeters at micro-magic dot com
|
||||
Todd Owen towen at lucidcalm dot dropbear dot id dot au
|
||||
Bug fixes to dll loading.
|
||||
Jason Nye jnye at nbnet dot nb dot ca
|
||||
Implementation of async cancelation.
|
||||
Fred Forester fforest at eticomm dot net
|
||||
Kevin D. Clark kclark at cabletron dot com
|
||||
David Baggett dmb at itasoftware dot com
|
||||
Bug fixes.
|
||||
Paul Redondo paul at matchvision dot com
|
||||
Scott McCaskill scott at 3dfx dot com
|
||||
Bug fixes.
|
||||
Jef Gearhart jgearhart at tpssys dot com
|
||||
Bug fix.
|
||||
Arthur Kantor akantor at bexusa dot com
|
||||
Mutex enhancements.
|
||||
Steven Reddie smr at essemer dot com dot au
|
||||
Bug fix.
|
||||
Alexander Terekhov TEREKHOV at de dot ibm dot com
|
||||
Re-implemented and improved read-write locks;
|
||||
(with Louis Thomas) re-implemented and improved
|
||||
condition variables;
|
||||
enhancements to semaphores;
|
||||
enhancements to mutexes;
|
||||
new mutex implementation in 'futex' style;
|
||||
suggested a robust implementation of pthread_once
|
||||
similar to that implemented by V.Kliathcko;
|
||||
system clock change handling re CV timeouts;
|
||||
bug fixes.
|
||||
Thomas Pfaff tpfaff at gmx dot net
|
||||
Changes to make C version usable with C++ applications;
|
||||
re-implemented mutex routines to avoid Win32 mutexes
|
||||
and TryEnterCriticalSection;
|
||||
procedure to fix Mingw32 thread-safety issues.
|
||||
Franco Bez franco dot bez at gmx dot de
|
||||
procedure to fix Mingw32 thread-safety issues.
|
||||
Louis Thomas lthomas at arbitrade dot com
|
||||
(with Alexander Terekhov) re-implemented and improved
|
||||
condition variables.
|
||||
David Korn dgk at research dot att dot com
|
||||
Ported to UWIN.
|
||||
Phil Frisbie, Jr. phil at hawksoft dot com
|
||||
Bug fix.
|
||||
Ralf Brese Ralf dot Brese at pdb4 dot siemens dot de
|
||||
Bug fix.
|
||||
prionx at juno dot com prionx at juno dot com
|
||||
Bug fixes.
|
||||
Max Woodbury mtew at cds dot duke dot edu
|
||||
POSIX versioning conditionals;
|
||||
reduced namespace pollution;
|
||||
idea to separate routines to reduce statically
|
||||
linked image sizes.
|
||||
Rob Fanner rfanner at stonethree dot com
|
||||
Bug fix.
|
||||
Michael Johnson michaelj at maine dot rr dot com
|
||||
Bug fix.
|
||||
Nicolas Barry boozai at yahoo dot com
|
||||
Bug fixes.
|
||||
Piet van Bruggen pietvb at newbridges dot nl
|
||||
Bug fix.
|
||||
Makoto Kato raven at oldskool dot jp
|
||||
AMD64 port.
|
||||
Panagiotis E. Hadjidoukas peh at hpclab dot ceid dot upatras dot gr
|
||||
Contributed the QueueUserAPCEx package which
|
||||
makes preemptive async cancelation possible.
|
||||
Will Bryant will dot bryant at ecosm dot com
|
||||
Borland compiler patch and makefile.
|
||||
Anuj Goyal anuj dot goyal at gmail dot com
|
||||
Port to Digital Mars compiler.
|
||||
Gottlob Frege gottlobfrege at gmail dot com
|
||||
re-implemented pthread_once (version 2)
|
||||
(pthread_once cancellation added by rpj).
|
||||
Vladimir Kliatchko vladimir at kliatchko dot com
|
||||
reimplemented pthread_once with the same form
|
||||
as described by A.Terekhov (later version 2);
|
||||
implementation of MCS (Mellor-Crummey/Scott) locks.
|
||||
Vendored
-150
@@ -1,150 +0,0 @@
|
||||
pthreads-win32 - a POSIX threads library for Microsoft Windows
|
||||
|
||||
|
||||
This file is Copyrighted
|
||||
------------------------
|
||||
|
||||
This file is covered under the following Copyright:
|
||||
|
||||
Copyright (C) 2001,2006 Ross P. Johnson
|
||||
All rights reserved.
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Pthreads-win32 is covered by the GNU Lesser General Public License
|
||||
------------------------------------------------------------------
|
||||
|
||||
Pthreads-win32 is open software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public License
|
||||
as published by the Free Software Foundation version 2.1 of the
|
||||
License.
|
||||
|
||||
Pthreads-win32 is several binary link libraries, several modules,
|
||||
associated interface definition files and scripts used to control
|
||||
its compilation and installation.
|
||||
|
||||
Pthreads-win32 is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
A copy of the GNU Lesser General Public License is distributed with
|
||||
pthreads-win32 under the filename:
|
||||
|
||||
COPYING.LIB
|
||||
|
||||
You should have received a copy of the version 2.1 GNU Lesser General
|
||||
Public License with pthreads-win32; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place
|
||||
Suite 330
|
||||
Boston, MA 02111-1307
|
||||
USA
|
||||
|
||||
The contact addresses for pthreads-win32 is as follows:
|
||||
|
||||
Web: http://sources.redhat.com/pthreads-win32
|
||||
Email: Ross Johnson
|
||||
Please use: [email protected]
|
||||
|
||||
|
||||
|
||||
Pthreads-win32 copyrights and exception files
|
||||
---------------------------------------------
|
||||
|
||||
With the exception of the files listed below, Pthreads-win32
|
||||
is covered under the following GNU Lesser General Public License
|
||||
Copyrights:
|
||||
|
||||
Pthreads-win32 - POSIX Threads Library for Win32
|
||||
Copyright(C) 1998 John E. Bossom
|
||||
Copyright(C) 1999,2006 Pthreads-win32 contributors
|
||||
|
||||
The current list of contributors is contained
|
||||
in the file CONTRIBUTORS included with the source
|
||||
code distribution. The current list of CONTRIBUTORS
|
||||
can also be seen at the following WWW location:
|
||||
http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
|
||||
Contact Email: Ross Johnson
|
||||
Please use: [email protected]
|
||||
|
||||
These files are not covered under one of the Copyrights listed above:
|
||||
|
||||
COPYING
|
||||
COPYING.LIB
|
||||
tests/rwlock7.c
|
||||
|
||||
This file, COPYING, is distributed under the Copyright found at the
|
||||
top of this file. It is important to note that you may distribute
|
||||
verbatim copies of this file but you may not modify this file.
|
||||
|
||||
The file COPYING.LIB, which contains a copy of the version 2.1
|
||||
GNU Lesser General Public License, is itself copyrighted by the
|
||||
Free Software Foundation, Inc. Please note that the Free Software
|
||||
Foundation, Inc. does NOT have a copyright over Pthreads-win32,
|
||||
only the COPYING.LIB that is supplied with pthreads-win32.
|
||||
|
||||
The file tests/rwlock7.c is derived from code written by
|
||||
Dave Butenhof for his book 'Programming With POSIX(R) Threads'.
|
||||
The original code was obtained by free download from his website
|
||||
http://home.earthlink.net/~anneart/family/Threads/source.html
|
||||
and did not contain a copyright or author notice. It is assumed to
|
||||
be freely distributable.
|
||||
|
||||
In all cases one may use and distribute these exception files freely.
|
||||
And because one may freely distribute the LGPL covered files, the
|
||||
entire pthreads-win32 source may be freely used and distributed.
|
||||
|
||||
|
||||
|
||||
General Copyleft and License info
|
||||
---------------------------------
|
||||
|
||||
For general information on Copylefts, see:
|
||||
|
||||
http://www.gnu.org/copyleft/
|
||||
|
||||
For information on GNU Lesser General Public Licenses, see:
|
||||
|
||||
http://www.gnu.org/copyleft/lesser.html
|
||||
http://www.gnu.org/copyleft/lesser.txt
|
||||
|
||||
|
||||
Why pthreads-win32 did not use the GNU General Public License
|
||||
-------------------------------------------------------------
|
||||
|
||||
The goal of the pthreads-win32 project has been to
|
||||
provide a quality and complete implementation of the POSIX
|
||||
threads API for Microsoft Windows within the limits imposed
|
||||
by virtue of it being a stand-alone library and not
|
||||
linked directly to other POSIX compliant libraries. For
|
||||
example, some functions and features, such as those based
|
||||
on POSIX signals, are missing.
|
||||
|
||||
Pthreads-win32 is a library, available in several different
|
||||
versions depending on supported compilers, and may be used
|
||||
as a dynamically linked module or a statically linked set of
|
||||
binary modules. It is not an application on it's own.
|
||||
|
||||
It was fully intended that pthreads-win32 be usable with
|
||||
commercial software not covered by either the GPL or the LGPL
|
||||
licenses. Pthreads-win32 has many contributors to it's
|
||||
code base, many of whom have done so because they have
|
||||
used the library in commercial or proprietry software
|
||||
projects.
|
||||
|
||||
Releasing pthreads-win32 under the LGPL ensures that the
|
||||
library can be used widely, while at the same time ensures
|
||||
that bug fixes and improvements to the pthreads-win32 code
|
||||
itself is returned to benefit all current and future users
|
||||
of the library.
|
||||
|
||||
Although pthreads-win32 makes it possible for applications
|
||||
that use POSIX threads to be ported to Win32 platforms, the
|
||||
broader goal of the project is to encourage the use of open
|
||||
standards, and in particular, to make it just a little easier
|
||||
for developers writing Win32 applications to consider
|
||||
widening the potential market for their products.
|
||||
Vendored
-504
@@ -1,504 +0,0 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
|
||||
|
||||
Vendored
-516
@@ -1,516 +0,0 @@
|
||||
/*
|
||||
* implement.h
|
||||
*
|
||||
* Definitions that don't need to be public.
|
||||
*
|
||||
* Keeps all the internals out of pthread.h
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _IMPLEMENT_H
|
||||
#define _IMPLEMENT_H
|
||||
|
||||
#include "pte_osal.h"
|
||||
|
||||
/* use local include files during development */
|
||||
#include "semaphore.h"
|
||||
#include "sched.h"
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
/*
|
||||
* This enumeration represents the state of the thread;
|
||||
* The thread is still "alive" if the numeric value of the
|
||||
* state is greater or equal "PThreadStateRunning".
|
||||
*/
|
||||
PThreadStateInitial = 0, /* Thread not running */
|
||||
PThreadStateRunning, /* Thread alive & kicking */
|
||||
PThreadStateSuspended, /* Thread alive but suspended */
|
||||
PThreadStateCancelPending, /* Thread alive but is */
|
||||
/* has cancelation pending. */
|
||||
PThreadStateCanceling, /* Thread alive but is */
|
||||
/* in the process of terminating */
|
||||
/* due to a cancellation request */
|
||||
PThreadStateException, /* Thread alive but exiting */
|
||||
/* due to an exception */
|
||||
PThreadStateLast
|
||||
}
|
||||
PThreadState;
|
||||
|
||||
|
||||
typedef struct pte_thread_t_ pte_thread_t;
|
||||
|
||||
struct pte_thread_t_
|
||||
{
|
||||
pte_osThreadHandle threadId; /* OS specific thread handle */
|
||||
// pthread_t ptHandle; /* This thread's permanent pthread_t handle */
|
||||
pte_thread_t * prevReuse; /* Links threads on reuse stack */
|
||||
volatile PThreadState state;
|
||||
void *exitStatus;
|
||||
void *parms;
|
||||
int ptErrno;
|
||||
int detachState;
|
||||
pthread_mutex_t threadLock; /* Used for serialised access to public thread state */
|
||||
int sched_priority; /* As set, not as currently is */
|
||||
pthread_mutex_t cancelLock; /* Used for async-cancel safety */
|
||||
int cancelState;
|
||||
int cancelType;
|
||||
int cancelEvent;
|
||||
jmp_buf start_mark;
|
||||
int implicit:
|
||||
1;
|
||||
void *keys;
|
||||
void *nextAssoc;
|
||||
|
||||
unsigned int x; /* Extra information - reuse count etc */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Special value to mark attribute objects as valid.
|
||||
*/
|
||||
#define PTE_ATTR_VALID ((unsigned long) 0xC4C0FFEE)
|
||||
|
||||
struct pthread_attr_t_
|
||||
{
|
||||
unsigned long valid;
|
||||
void *stackaddr;
|
||||
size_t stacksize;
|
||||
int detachstate;
|
||||
struct sched_param param;
|
||||
int inheritsched;
|
||||
int contentionscope;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* ====================
|
||||
* ====================
|
||||
* Semaphores, Mutexes and Condition Variables
|
||||
* ====================
|
||||
* ====================
|
||||
*/
|
||||
|
||||
struct sem_t_
|
||||
{
|
||||
int value;
|
||||
pthread_mutex_t lock;
|
||||
pte_osSemaphoreHandle sem;
|
||||
};
|
||||
|
||||
#define PTE_OBJECT_AUTO_INIT ((void *) -1)
|
||||
#define PTE_OBJECT_INVALID 0
|
||||
|
||||
struct pthread_mutex_t_
|
||||
{
|
||||
pte_osSemaphoreHandle handle;
|
||||
int lock_idx;
|
||||
/* Provides exclusive access to mutex state
|
||||
via the Interlocked* mechanism.
|
||||
0: unlocked/free.
|
||||
1: locked - no other waiters.
|
||||
-1: locked - with possible other waiters.
|
||||
*/
|
||||
int recursive_count; /* Number of unlocks a thread needs to perform
|
||||
before the lock is released (recursive
|
||||
mutexes only). */
|
||||
int kind; /* Mutex type. */
|
||||
pthread_t ownerThread;
|
||||
};
|
||||
|
||||
struct pthread_mutexattr_t_
|
||||
{
|
||||
int pshared;
|
||||
int kind;
|
||||
};
|
||||
|
||||
/*
|
||||
* Possible values, other than PTE_OBJECT_INVALID,
|
||||
* for the "interlock" element in a spinlock.
|
||||
*
|
||||
* In this implementation, when a spinlock is initialised,
|
||||
* the number of cpus available to the process is checked.
|
||||
* If there is only one cpu then "interlock" is set equal to
|
||||
* PTE_SPIN_USE_MUTEX and u.mutex is a initialised mutex.
|
||||
* If the number of cpus is greater than 1 then "interlock"
|
||||
* is set equal to PTE_SPIN_UNLOCKED and the number is
|
||||
* stored in u.cpus. This arrangement allows the spinlock
|
||||
* routines to attempt an InterlockedCompareExchange on "interlock"
|
||||
* immediately and, if that fails, to try the inferior mutex.
|
||||
*
|
||||
* "u.cpus" isn't used for anything yet, but could be used at
|
||||
* some point to optimise spinlock behaviour.
|
||||
*/
|
||||
#define PTE_SPIN_UNLOCKED (1)
|
||||
#define PTE_SPIN_LOCKED (2)
|
||||
#define PTE_SPIN_USE_MUTEX (3)
|
||||
|
||||
struct pthread_spinlock_t_
|
||||
{
|
||||
int interlock; /* Locking element for multi-cpus. */
|
||||
union
|
||||
{
|
||||
int cpus; /* No. of cpus if multi cpus, or */
|
||||
pthread_mutex_t mutex; /* mutex if single cpu. */
|
||||
} u;
|
||||
};
|
||||
|
||||
struct pthread_barrier_t_
|
||||
{
|
||||
unsigned int nCurrentBarrierHeight;
|
||||
unsigned int nInitialBarrierHeight;
|
||||
int iStep;
|
||||
int pshared;
|
||||
sem_t semBarrierBreeched[2];
|
||||
};
|
||||
|
||||
struct pthread_barrierattr_t_
|
||||
{
|
||||
int pshared;
|
||||
};
|
||||
|
||||
struct pthread_key_t_
|
||||
{
|
||||
unsigned key;
|
||||
void (*destructor) (void *);
|
||||
pthread_mutex_t keyLock;
|
||||
void *threads;
|
||||
};
|
||||
|
||||
|
||||
typedef struct ThreadParms ThreadParms;
|
||||
typedef struct ThreadKeyAssoc ThreadKeyAssoc;
|
||||
|
||||
struct ThreadParms
|
||||
{
|
||||
pthread_t tid;
|
||||
void *(*start) (void *);
|
||||
void *arg;
|
||||
};
|
||||
|
||||
struct pthread_cond_t_
|
||||
{
|
||||
long nWaitersBlocked; /* Number of threads blocked */
|
||||
long nWaitersGone; /* Number of threads timed out */
|
||||
long nWaitersToUnblock; /* Number of threads to unblock */
|
||||
sem_t semBlockQueue; /* Queue up threads waiting for the */
|
||||
/* condition to become signalled */
|
||||
sem_t semBlockLock; /* Semaphore that guards access to */
|
||||
/* | waiters blocked count/block queue */
|
||||
/* +-> Mandatory Sync.LEVEL-1 */
|
||||
pthread_mutex_t mtxUnblockLock; /* Mutex that guards access to */
|
||||
/* | waiters (to)unblock(ed) counts */
|
||||
/* +-> Optional* Sync.LEVEL-2 */
|
||||
pthread_cond_t next; /* Doubly linked list */
|
||||
pthread_cond_t prev;
|
||||
};
|
||||
|
||||
|
||||
struct pthread_condattr_t_
|
||||
{
|
||||
int pshared;
|
||||
};
|
||||
|
||||
#define PTE_RWLOCK_MAGIC 0xfacade2
|
||||
|
||||
struct pthread_rwlock_t_
|
||||
{
|
||||
pthread_mutex_t mtxExclusiveAccess;
|
||||
pthread_mutex_t mtxSharedAccessCompleted;
|
||||
pthread_cond_t cndSharedAccessCompleted;
|
||||
int nSharedAccessCount;
|
||||
int nExclusiveAccessCount;
|
||||
int nCompletedSharedAccessCount;
|
||||
int nMagic;
|
||||
};
|
||||
|
||||
struct pthread_rwlockattr_t_
|
||||
{
|
||||
int pshared;
|
||||
};
|
||||
|
||||
/*
|
||||
* MCS lock queue node - see pte_MCS_lock.c
|
||||
*/
|
||||
struct pte_mcs_node_t_
|
||||
{
|
||||
struct pte_mcs_node_t_ **lock; /* ptr to tail of queue */
|
||||
struct pte_mcs_node_t_ *next; /* ptr to successor in queue */
|
||||
unsigned int readyFlag; /* set after lock is released by
|
||||
predecessor */
|
||||
unsigned int nextFlag; /* set after 'next' ptr is set by
|
||||
successor */
|
||||
};
|
||||
|
||||
typedef struct pte_mcs_node_t_ pte_mcs_local_node_t;
|
||||
typedef struct pte_mcs_node_t_ *pte_mcs_lock_t;
|
||||
|
||||
|
||||
struct ThreadKeyAssoc
|
||||
{
|
||||
/*
|
||||
* Purpose:
|
||||
* This structure creates an association between a thread and a key.
|
||||
* It is used to implement the implicit invocation of a user defined
|
||||
* destroy routine for thread specific data registered by a user upon
|
||||
* exiting a thread.
|
||||
*
|
||||
* Graphically, the arrangement is as follows, where:
|
||||
*
|
||||
* K - Key with destructor
|
||||
* (head of chain is key->threads)
|
||||
* T - Thread that has called pthread_setspecific(Kn)
|
||||
* (head of chain is thread->keys)
|
||||
* A - Association. Each association is a node at the
|
||||
* intersection of two doubly-linked lists.
|
||||
*
|
||||
* T1 T2 T3
|
||||
* | | |
|
||||
* | | |
|
||||
* K1 -----+-----A-----A----->
|
||||
* | | |
|
||||
* | | |
|
||||
* K2 -----A-----A-----+----->
|
||||
* | | |
|
||||
* | | |
|
||||
* K3 -----A-----+-----A----->
|
||||
* | | |
|
||||
* | | |
|
||||
* V V V
|
||||
*
|
||||
* Access to the association is guarded by two locks: the key's
|
||||
* general lock (guarding the row) and the thread's general
|
||||
* lock (guarding the column). This avoids the need for a
|
||||
* dedicated lock for each association, which not only consumes
|
||||
* more handles but requires that: before the lock handle can
|
||||
* be released - both the key must be deleted and the thread
|
||||
* must have called the destructor. The two-lock arrangement
|
||||
* allows the resources to be freed as soon as either thread or
|
||||
* key is concluded.
|
||||
*
|
||||
* To avoid deadlock: whenever both locks are required, the key
|
||||
* and thread locks are always acquired in the order: key lock
|
||||
* then thread lock. An exception to this exists when a thread
|
||||
* calls the destructors, however this is done carefully to
|
||||
* avoid deadlock.
|
||||
*
|
||||
* An association is created when a thread first calls
|
||||
* pthread_setspecific() on a key that has a specified
|
||||
* destructor.
|
||||
*
|
||||
* An association is destroyed either immediately after the
|
||||
* thread calls the key destructor function on thread exit, or
|
||||
* when the key is deleted.
|
||||
*
|
||||
* Attributes:
|
||||
* thread
|
||||
* reference to the thread that owns the
|
||||
* association. This is actually the pointer to the
|
||||
* thread struct itself. Since the association is
|
||||
* destroyed before the thread exits, this can never
|
||||
* point to a different logical thread to the one that
|
||||
* created the assoc, i.e. after thread struct reuse.
|
||||
*
|
||||
* key
|
||||
* reference to the key that owns the association.
|
||||
*
|
||||
* nextKey
|
||||
* The pthread_t->keys attribute is the head of a
|
||||
* chain of associations that runs through the nextKey
|
||||
* link. This chain provides the 1 to many relationship
|
||||
* between a pthread_t and all pthread_key_t on which
|
||||
* it called pthread_setspecific.
|
||||
*
|
||||
* prevKey
|
||||
* Similarly.
|
||||
*
|
||||
* nextThread
|
||||
* The pthread_key_t->threads attribute is the head of
|
||||
* a chain of assoctiations that runs through the
|
||||
* nextThreads link. This chain provides the 1 to many
|
||||
* relationship between a pthread_key_t and all the
|
||||
* PThreads that have called pthread_setspecific for
|
||||
* this pthread_key_t.
|
||||
*
|
||||
* prevThread
|
||||
* Similarly.
|
||||
*
|
||||
* Notes:
|
||||
* 1) As soon as either the key or the thread is no longer
|
||||
* referencing the association, it can be destroyed. The
|
||||
* association will be removed from both chains.
|
||||
*
|
||||
* 2) An association is only created by
|
||||
* pthread_setspecific if the user provided a
|
||||
* destroyRoutine when they created the key.
|
||||
*
|
||||
*
|
||||
*/
|
||||
pte_thread_t * thread;
|
||||
pthread_key_t key;
|
||||
ThreadKeyAssoc *nextKey;
|
||||
ThreadKeyAssoc *nextThread;
|
||||
ThreadKeyAssoc *prevKey;
|
||||
ThreadKeyAssoc *prevThread;
|
||||
};
|
||||
|
||||
/*
|
||||
* Services available through EXCEPTION_PTE_SERVICES
|
||||
* and also used [as parameters to pte_throw()] as
|
||||
* generic exception selectors.
|
||||
*/
|
||||
|
||||
#define PTE_EPS_EXIT (1)
|
||||
#define PTE_EPS_CANCEL (2)
|
||||
|
||||
|
||||
/* Useful macros */
|
||||
#define PTE_MAX(a,b) ((a)<(b)?(b):(a))
|
||||
#define PTE_MIN(a,b) ((a)>(b)?(b):(a))
|
||||
|
||||
|
||||
/* Thread Reuse stack bottom marker. Must not be NULL or any valid pointer to memory. */
|
||||
#define PTE_THREAD_REUSE_EMPTY ((pte_thread_t *) 1)
|
||||
|
||||
extern int pte_processInitialized;
|
||||
extern pte_thread_t * pte_threadReuseTop;
|
||||
extern pte_thread_t * pte_threadReuseBottom;
|
||||
extern pthread_key_t pte_selfThreadKey;
|
||||
extern pthread_key_t pte_cleanupKey;
|
||||
extern pthread_cond_t pte_cond_list_head;
|
||||
extern pthread_cond_t pte_cond_list_tail;
|
||||
|
||||
extern int pte_mutex_default_kind;
|
||||
|
||||
extern int pte_concurrency;
|
||||
|
||||
extern int pte_features;
|
||||
|
||||
extern pte_osMutexHandle pte_thread_reuse_lock;
|
||||
extern pte_osMutexHandle pte_mutex_test_init_lock;
|
||||
extern pte_osMutexHandle pte_cond_list_lock;
|
||||
extern pte_osMutexHandle pte_cond_test_init_lock;
|
||||
extern pte_osMutexHandle pte_rwlock_test_init_lock;
|
||||
extern pte_osMutexHandle pte_spinlock_test_init_lock;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/*
|
||||
* =====================
|
||||
* =====================
|
||||
* Forward Declarations
|
||||
* =====================
|
||||
* =====================
|
||||
*/
|
||||
|
||||
int pte_is_attr (const pthread_attr_t * attr);
|
||||
|
||||
int pte_cond_check_need_init (pthread_cond_t * cond);
|
||||
int pte_mutex_check_need_init (pthread_mutex_t * mutex);
|
||||
int pte_rwlock_check_need_init (pthread_rwlock_t * rwlock);
|
||||
int pte_spinlock_check_need_init (pthread_spinlock_t * lock);
|
||||
|
||||
int pte_processInitialize (void);
|
||||
|
||||
void pte_processTerminate (void);
|
||||
|
||||
void pte_threadDestroy (pthread_t tid);
|
||||
void pte_threadExitAndDestroy (pthread_t tid);
|
||||
|
||||
void pte_pop_cleanup_all (int execute);
|
||||
|
||||
pthread_t pte_new (void);
|
||||
|
||||
pthread_t pte_threadReusePop (void);
|
||||
|
||||
void pte_threadReusePush (pthread_t thread);
|
||||
|
||||
int pte_getprocessors (int *count);
|
||||
|
||||
int pte_setthreadpriority (pthread_t thread, int policy, int priority);
|
||||
|
||||
void pte_rwlock_cancelwrwait (void *arg);
|
||||
|
||||
int pte_threadStart (void *vthreadParms);
|
||||
|
||||
void pte_callUserDestroyRoutines (pthread_t thread);
|
||||
|
||||
int pte_tkAssocCreate (pte_thread_t * thread, pthread_key_t key);
|
||||
|
||||
void pte_tkAssocDestroy (ThreadKeyAssoc * assoc);
|
||||
|
||||
int sem_wait_nocancel (sem_t * sem);
|
||||
|
||||
unsigned int pte_relmillisecs (const struct timespec * abstime);
|
||||
|
||||
void pte_mcs_lock_acquire (pte_mcs_lock_t * lock, pte_mcs_local_node_t * node);
|
||||
|
||||
void pte_mcs_lock_release (pte_mcs_local_node_t * node);
|
||||
|
||||
/* Declared in private.c */
|
||||
void pte_throw (unsigned int exception);
|
||||
|
||||
int pte_cancellable_wait (pte_osSemaphoreHandle semHandle, unsigned int* timeout);
|
||||
|
||||
#define PTE_ATOMIC_EXCHANGE pte_osAtomicExchange
|
||||
#define PTE_ATOMIC_EXCHANGE_ADD pte_osAtomicExchangeAdd
|
||||
#define PTE_ATOMIC_COMPARE_EXCHANGE pte_osAtomicCompareExchange
|
||||
#define PTE_ATOMIC_DECREMENT pte_osAtomicDecrement
|
||||
#define PTE_ATOMIC_INCREMENT pte_osAtomicIncrement
|
||||
|
||||
int pte_thread_detach_np();
|
||||
int pte_thread_detach_and_exit_np();
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
#endif /* _IMPLEMENT_H */
|
||||
Vendored
-90
@@ -1,90 +0,0 @@
|
||||
/***
|
||||
* errno.h - system wide error numbers (set by system calls)
|
||||
*
|
||||
* Copyright (c) 1985-1997, Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Purpose:
|
||||
* This file defines the system-wide error numbers (set by
|
||||
* system calls). Conforms to the XENIX standard. Extended
|
||||
* for compatibility with Uniforum standard.
|
||||
* [System V]
|
||||
*
|
||||
* [Public]
|
||||
*
|
||||
****/
|
||||
|
||||
#ifndef _INC_ERRNO
|
||||
#define _INC_ERRNO
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* declare reference to errno */
|
||||
|
||||
extern int errno;
|
||||
|
||||
/* Error Codes */
|
||||
|
||||
#define EPERM 1
|
||||
#define ENOENT 2
|
||||
#define ESRCH 3
|
||||
#define EINTR 4
|
||||
#define EIO 5
|
||||
#define ENXIO 6
|
||||
#define E2BIG 7
|
||||
#define ENOEXEC 8
|
||||
#define EBADF 9
|
||||
#define ECHILD 10
|
||||
#define EAGAIN 11
|
||||
#define ENOMEM 12
|
||||
#define EACCES 13
|
||||
#define EFAULT 14
|
||||
#define EBUSY 16
|
||||
#define EEXIST 17
|
||||
#define EXDEV 18
|
||||
#define ENODEV 19
|
||||
#define ENOTDIR 20
|
||||
#define EISDIR 21
|
||||
#define EINVAL 22
|
||||
#define ENFILE 23
|
||||
#define EMFILE 24
|
||||
#define ENOTTY 25
|
||||
#define EFBIG 27
|
||||
#define ENOSPC 28
|
||||
#define ESPIPE 29
|
||||
#define EROFS 30
|
||||
#define EMLINK 31
|
||||
#define EPIPE 32
|
||||
#define EDOM 33
|
||||
#define ERANGE 34
|
||||
#define EDEADLK 36
|
||||
|
||||
/* defined differently in winsock.h on WinCE */
|
||||
#ifndef ENAMETOOLONG
|
||||
#define ENAMETOOLONG 38
|
||||
#endif
|
||||
|
||||
#define ENOLCK 39
|
||||
#define ENOSYS 40
|
||||
|
||||
/* defined differently in winsock.h on WinCE */
|
||||
#ifndef ENOTEMPTY
|
||||
#define ENOTEMPTY 41
|
||||
#endif
|
||||
|
||||
#define EILSEQ 42
|
||||
|
||||
/*
|
||||
* Support EDEADLOCK for compatibiity with older MS-C versions.
|
||||
*/
|
||||
#define EDEADLOCK EDEADLK
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _INC_ERRNO */
|
||||
-163
@@ -1,163 +0,0 @@
|
||||
/*
|
||||
* tls-helper.c
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "tls-helper.h"
|
||||
|
||||
static int *keysUsed;
|
||||
|
||||
/* We don't protect this - it's only written on startup */
|
||||
static int maxTlsValues;
|
||||
|
||||
pte_osMutexHandle globalTlsLock;
|
||||
|
||||
pte_osResult pteTlsGlobalInit(int maxEntries)
|
||||
{
|
||||
int i;
|
||||
pte_osResult result;
|
||||
|
||||
pte_osMutexCreate(&globalTlsLock);
|
||||
|
||||
keysUsed = (int *) malloc(maxEntries * sizeof(int));
|
||||
|
||||
if (keysUsed != NULL)
|
||||
{
|
||||
for (i=0;i<maxEntries;i++)
|
||||
keysUsed[i] = 0;
|
||||
|
||||
maxTlsValues = maxEntries;
|
||||
|
||||
result = PTE_OS_OK;
|
||||
}
|
||||
else
|
||||
result = PTE_OS_NO_RESOURCES;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void * pteTlsThreadInit(void)
|
||||
{
|
||||
int i;
|
||||
void **pTlsStruct = (void **) malloc(maxTlsValues * sizeof(void*));
|
||||
|
||||
/* PTE library assumes that keys are initialized to zero */
|
||||
for (i=0; i<maxTlsValues;i++)
|
||||
pTlsStruct[i] = 0;
|
||||
|
||||
return (void *) pTlsStruct;
|
||||
}
|
||||
|
||||
|
||||
pte_osResult pteTlsAlloc(unsigned int *pKey)
|
||||
{
|
||||
int i;
|
||||
pte_osResult result = PTE_OS_NO_RESOURCES;
|
||||
|
||||
pte_osMutexLock(globalTlsLock);
|
||||
|
||||
for (i=0;i<maxTlsValues;i++)
|
||||
{
|
||||
if (keysUsed[i] == 0)
|
||||
{
|
||||
keysUsed[i] = 1;
|
||||
|
||||
*pKey = i+1;
|
||||
result = PTE_OS_OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pte_osMutexUnlock(globalTlsLock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void * pteTlsGetValue(void *pTlsThreadStruct, unsigned int index)
|
||||
{
|
||||
void **pTls = (void **) pTlsThreadStruct;
|
||||
|
||||
if (keysUsed[index-1])
|
||||
{
|
||||
if (pTls != NULL)
|
||||
return pTls[index-1];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
pte_osResult pteTlsSetValue(void *pTlsThreadStruct, unsigned int index, void * value)
|
||||
{
|
||||
pte_osResult result;
|
||||
void ** pTls = (void **) pTlsThreadStruct;
|
||||
|
||||
if (pTls != NULL)
|
||||
{
|
||||
pTls[index-1] = value;
|
||||
result = PTE_OS_OK;
|
||||
}
|
||||
else
|
||||
result = PTE_OS_INVALID_PARAM;
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
pte_osResult pteTlsFree(unsigned int index)
|
||||
{
|
||||
pte_osResult result;
|
||||
|
||||
if (keysUsed != NULL)
|
||||
{
|
||||
pte_osMutexLock(globalTlsLock);
|
||||
|
||||
keysUsed[index-1] = 0;
|
||||
|
||||
pte_osMutexUnlock(globalTlsLock);
|
||||
|
||||
result = PTE_OS_OK;
|
||||
}
|
||||
else
|
||||
result = PTE_OS_GENERAL_FAILURE;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void pteTlsThreadDestroy(void * pTlsThreadStruct)
|
||||
{
|
||||
free(pTlsThreadStruct);
|
||||
}
|
||||
|
||||
void pteTlsGlobalDestroy(void)
|
||||
{
|
||||
pte_osMutexDelete(globalTlsLock);
|
||||
free(keysUsed);
|
||||
}
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* tls-helper.h
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#ifndef _TLS_HELPER_H_
|
||||
#define _TLS_HELPER_H_
|
||||
|
||||
#include "../psp/pte_osal.h"
|
||||
|
||||
/* @todo document.. */
|
||||
|
||||
pte_osResult pteTlsGlobalInit(int maxEntries);
|
||||
void * pteTlsThreadInit(void);
|
||||
|
||||
pte_osResult pteTlsAlloc(unsigned int *pKey);
|
||||
void * pteTlsGetValue(void *pTlsThreadStruct, unsigned int index);
|
||||
pte_osResult pteTlsSetValue(void *pTlsThreadStruct, unsigned int index, void * value);
|
||||
pte_osResult pteTlsFree(unsigned int index);
|
||||
|
||||
void pteTlsThreadDestroy(void * pTlsThreadStruct);
|
||||
void pteTlsGlobalDestroy(void);
|
||||
|
||||
#endif // _TLS_HELPER_H_
|
||||
Vendored
-51
@@ -1,51 +0,0 @@
|
||||
VPATH = ../..:../helper
|
||||
|
||||
TARGET_LIB = libpthread-psp.a
|
||||
|
||||
MUTEX_OBJS = pthread_mutex.o
|
||||
|
||||
MUTEXATTR_OBJS = pthread_mutexattr.o
|
||||
|
||||
SUPPORT_OBJS = pte.o pthread.o
|
||||
|
||||
THREAD_OBJS = sched.o
|
||||
|
||||
TLS_OBJS = pthread_key.o
|
||||
|
||||
MISC_OBJS = pthread_get.o
|
||||
|
||||
SEM_OBJS = sem.o
|
||||
|
||||
BARRIER_OBJS = pthread_barrier.o pthread_barrierattr.o
|
||||
|
||||
SPIN_OBJS = pthread_spin.o
|
||||
|
||||
CONDVAR_OBJS = pthread_cond.o pthread_condattr.o
|
||||
|
||||
RWLOCK_OBJS = pthread_rwlock.o pthread_rwlockattr.o
|
||||
|
||||
CANCEL_OBJS = pthread.o pthread_set.o
|
||||
|
||||
OS_OBJS = psp_osal.o tls-helper.o
|
||||
|
||||
OBJS = $(MUTEX_OBJS) $(MUTEXATTR_OBJS) $(THREAD_OBJS) $(SUPPORT_OBJS) $(TLS_OBJS) $(MISC_OBJS) $(SEM_OBJS) $(BARRIER_OBJS) $(SPIN_OBJS) $(CONDVAR_OBJS) $(RWLOCK_OBJS) $(CANCEL_OBJS) $(OS_OBJS)
|
||||
|
||||
INCDIR =
|
||||
CFLAGS = $(GLOBAL_CFLAGS) -G0 -O2 -Wall -g -fno-strict-aliasing -I../.. -I../helper
|
||||
CXXFLAGS = $(CFLAGS) -fexceptions -fno-rtti -Werror
|
||||
ASFLAGS = $(CFLAGS)
|
||||
|
||||
LDFLAGS =
|
||||
LIBS =
|
||||
|
||||
PSPSDK=$(shell psp-config --pspsdk-path)
|
||||
include $(PSPSDK)/lib/build.mak
|
||||
|
||||
test :
|
||||
make -C ../../tests test
|
||||
|
||||
install: $(TARGET_LIB)
|
||||
@cp -v $(TARGET_LIB) `psp-config --psp-prefix`/lib
|
||||
@cp -v *.h `psp-config --psp-prefix`/include
|
||||
@echo "Done."
|
||||
|
||||
-842
@@ -1,842 +0,0 @@
|
||||
/*
|
||||
* psp_osal.c
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* For ftime */
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/timeb.h>
|
||||
|
||||
#include <pspkerror.h>
|
||||
|
||||
#include "pte_osal.h"
|
||||
#include "../../pthread.h"
|
||||
#include "../helper/tls-helper.h"
|
||||
|
||||
#define MAX_PSP_UID 2048 /* SWAG */
|
||||
|
||||
#define DEFAULT_STACK_SIZE_BYTES 4096
|
||||
|
||||
#define PSP_MAX_TLS 32
|
||||
|
||||
#if 1
|
||||
#define PSP_DEBUG(x) printf(x)
|
||||
#else
|
||||
#define PSP_DEBUG(x)
|
||||
#endif
|
||||
|
||||
/* TLS key used to access pspThreadData struct for reach thread. */
|
||||
static unsigned int threadDataKey;
|
||||
|
||||
/*
|
||||
* Data stored on a per-thread basis - allocated in pte_osThreadCreate
|
||||
* and freed in pte_osThreadDelete.
|
||||
*/
|
||||
typedef struct pspThreadData
|
||||
{
|
||||
/* Entry point and parameters to thread's main function */
|
||||
pte_osThreadEntryPoint entryPoint;
|
||||
void * argv;
|
||||
|
||||
/* Semaphore used for cancellation. Posted to by pte_osThreadCancel,
|
||||
polled in pte_osSemaphoreCancellablePend */
|
||||
SceUID cancelSem;
|
||||
|
||||
} pspThreadData;
|
||||
|
||||
|
||||
/* Structure used to emulate TLS on non-POSIX threads.
|
||||
* This limits us to one non-POSIX thread that can
|
||||
* call pthread functions. */
|
||||
static void *globalTls;
|
||||
|
||||
/* Helper functions */
|
||||
static pspThreadData *getThreadData(SceUID threadHandle);
|
||||
static void *getTlsStructFromThread(SceUID thid);
|
||||
|
||||
/* A new thread's stub entry point. It retrieves the real entry point from the per thread control
|
||||
* data as well as any parameters to this function, and then calls the entry point.
|
||||
*/
|
||||
int pspStubThreadEntry (unsigned int argc, void *argv)
|
||||
{
|
||||
int result;
|
||||
pspThreadData *pThreadData;
|
||||
|
||||
pThreadData = getThreadData(sceKernelGetThreadId());
|
||||
|
||||
result = (*(pThreadData->entryPoint))(pThreadData->argv);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Initialization
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
pte_osResult pte_osInit(void)
|
||||
{
|
||||
pte_osResult result;
|
||||
pspThreadData *pThreadData;
|
||||
char cancelSemName[64];
|
||||
|
||||
/* Allocate and initialize TLS support */
|
||||
result = pteTlsGlobalInit(PSP_MAX_TLS);
|
||||
|
||||
if (result == PTE_OS_OK)
|
||||
{
|
||||
/* Allocate a key that we use to store control information (e.g. cancellation semaphore) per thread */
|
||||
result = pteTlsAlloc(&threadDataKey);
|
||||
|
||||
if (result == PTE_OS_OK)
|
||||
{
|
||||
|
||||
/* Initialize the structure used to emulate TLS for
|
||||
* non-POSIX threads
|
||||
*/
|
||||
globalTls = pteTlsThreadInit();
|
||||
|
||||
/* Also create a "thread data" structure for a single non-POSIX thread. */
|
||||
|
||||
/* Allocate some memory for our per-thread control data. We use this for:
|
||||
* 1. Entry point and parameters for the user thread's main function.
|
||||
* 2. Semaphore used for thread cancellation.
|
||||
*/
|
||||
pThreadData = (pspThreadData *) malloc(sizeof(pspThreadData));
|
||||
|
||||
if (pThreadData == NULL)
|
||||
{
|
||||
result = PTE_OS_NO_RESOURCES;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Save a pointer to our per-thread control data as a TLS value */
|
||||
pteTlsSetValue(globalTls, threadDataKey, pThreadData);
|
||||
|
||||
/* Create a semaphore used to cancel threads */
|
||||
snprintf(cancelSemName, sizeof(cancelSemName), "pthread_cancelSemGlobal");
|
||||
|
||||
pThreadData->cancelSem = sceKernelCreateSema(cancelSemName,
|
||||
0, /* attributes (default) */
|
||||
0, /* initial value */
|
||||
255, /* maximum value */
|
||||
0); /* options (default) */
|
||||
result = PTE_OS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Threads
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
pte_osResult pte_osThreadCreate(pte_osThreadEntryPoint entryPoint,
|
||||
int stackSize,
|
||||
int initialPriority,
|
||||
void *argv,
|
||||
pte_osThreadHandle* ppte_osThreadHandle)
|
||||
{
|
||||
char threadName[64];
|
||||
char cancelSemName[64];
|
||||
static int threadNum = 1;
|
||||
int pspAttr;
|
||||
void *pTls;
|
||||
SceUID threadId;
|
||||
pte_osResult result;
|
||||
pspThreadData *pThreadData;
|
||||
|
||||
if (threadNum++ > MAX_PSP_UID)
|
||||
threadNum = 0;
|
||||
|
||||
/* Make sure that the stack we're going to allocate is big enough */
|
||||
if (stackSize < DEFAULT_STACK_SIZE_BYTES)
|
||||
stackSize = DEFAULT_STACK_SIZE_BYTES;
|
||||
|
||||
/* Allocate TLS structure for this thread. */
|
||||
pTls = pteTlsThreadInit();
|
||||
if (pTls == NULL)
|
||||
{
|
||||
PSP_DEBUG("pteTlsThreadInit: PTE_OS_NO_RESOURCES\n");
|
||||
result = PTE_OS_NO_RESOURCES;
|
||||
goto FAIL0;
|
||||
}
|
||||
|
||||
/* Allocate some memory for our per-thread control data. We use this for:
|
||||
* 1. Entry point and parameters for the user thread's main function.
|
||||
* 2. Semaphore used for thread cancellation.
|
||||
*/
|
||||
pThreadData = (pspThreadData *) malloc(sizeof(pspThreadData));
|
||||
|
||||
if (pThreadData == NULL)
|
||||
{
|
||||
pteTlsThreadDestroy(pTls);
|
||||
|
||||
PSP_DEBUG("malloc(pspThreadData): PTE_OS_NO_RESOURCES\n");
|
||||
result = PTE_OS_NO_RESOURCES;
|
||||
goto FAIL0;
|
||||
}
|
||||
|
||||
/* Save a pointer to our per-thread control data as a TLS value */
|
||||
pteTlsSetValue(pTls, threadDataKey, pThreadData);
|
||||
|
||||
pThreadData->entryPoint = entryPoint;
|
||||
pThreadData->argv = argv;
|
||||
|
||||
/* Create a semaphore used to cancel threads */
|
||||
snprintf(cancelSemName, sizeof(cancelSemName), "pthread_cancelSem%04d", threadNum);
|
||||
|
||||
pThreadData->cancelSem = sceKernelCreateSema(cancelSemName,
|
||||
0, /* attributes (default) */
|
||||
0, /* initial value */
|
||||
255, /* maximum value */
|
||||
0); /* options (default) */
|
||||
|
||||
|
||||
/* In order to emulate TLS functionality, we append the address of the TLS structure that we
|
||||
* allocated above to the thread's name. To set or get TLS values for this thread, the user
|
||||
* needs to get the name of the thread from the OS and then parse the name to extract
|
||||
* a pointer to the TLS structure.
|
||||
*/
|
||||
snprintf(threadName, sizeof(threadName), "pthread%04d__%x", threadNum, (unsigned int) pTls);
|
||||
|
||||
pspAttr = 0;
|
||||
|
||||
#if 0
|
||||
printf("%s %p %d %d %d\n",threadName, pspStubThreadEntry, initialPriority, stackSize, pspAttr);
|
||||
#endif
|
||||
threadId = sceKernelCreateThread(threadName,
|
||||
pspStubThreadEntry,
|
||||
initialPriority,
|
||||
stackSize,
|
||||
pspAttr,
|
||||
NULL);
|
||||
|
||||
if (threadId == (SceUID) SCE_KERNEL_ERROR_NO_MEMORY)
|
||||
{
|
||||
free(pThreadData);
|
||||
pteTlsThreadDestroy(pTls);
|
||||
|
||||
PSP_DEBUG("sceKernelCreateThread: PTE_OS_NO_RESOURCES\n");
|
||||
result = PTE_OS_NO_RESOURCES;
|
||||
}
|
||||
else if (threadId < 0)
|
||||
{
|
||||
free(pThreadData);
|
||||
pteTlsThreadDestroy(pTls);
|
||||
|
||||
PSP_DEBUG("sceKernelCreateThread: PTE_OS_GENERAL_FAILURE\n");
|
||||
result = PTE_OS_GENERAL_FAILURE;
|
||||
}
|
||||
else
|
||||
{
|
||||
*ppte_osThreadHandle = threadId;
|
||||
result = PTE_OS_OK;
|
||||
}
|
||||
|
||||
FAIL0:
|
||||
return result;
|
||||
}
|
||||
|
||||
pte_osResult pte_osThreadStart(pte_osThreadHandle osThreadHandle)
|
||||
{
|
||||
sceKernelStartThread(osThreadHandle, 0, NULL);
|
||||
|
||||
return PTE_OS_OK;
|
||||
}
|
||||
|
||||
|
||||
pte_osResult pte_osThreadDelete(pte_osThreadHandle handle)
|
||||
{
|
||||
void *pTls = getTlsStructFromThread(handle);
|
||||
pspThreadData *pThreadData = getThreadData(handle);
|
||||
|
||||
sceKernelDeleteSema(pThreadData->cancelSem);
|
||||
|
||||
free(pThreadData);
|
||||
|
||||
pteTlsThreadDestroy(pTls);
|
||||
|
||||
sceKernelDeleteThread(handle);
|
||||
|
||||
return PTE_OS_OK;
|
||||
}
|
||||
|
||||
pte_osResult pte_osThreadExitAndDelete(pte_osThreadHandle handle)
|
||||
{
|
||||
pte_osThreadDelete(handle);
|
||||
|
||||
sceKernelExitDeleteThread(0);
|
||||
|
||||
return PTE_OS_OK;
|
||||
}
|
||||
|
||||
void pte_osThreadExit(void)
|
||||
{
|
||||
sceKernelExitThread(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* This has to be cancellable, so we can't just call sceKernelWaitThreadEnd.
|
||||
* Instead, poll on this in a loop, like we do for a cancellable semaphore.
|
||||
*/
|
||||
pte_osResult pte_osThreadWaitForEnd(pte_osThreadHandle threadHandle)
|
||||
{
|
||||
pte_osResult result;
|
||||
pspThreadData *pThreadData = getThreadData(sceKernelGetThreadId());
|
||||
|
||||
while (1)
|
||||
{
|
||||
SceKernelThreadRunStatus info;
|
||||
|
||||
/* Poll task to see if it has ended */
|
||||
memset(&info,0,sizeof(info));
|
||||
info.size = sizeof(info);
|
||||
sceKernelReferThreadRunStatus(threadHandle, &info);
|
||||
|
||||
if (info.status == PSP_THREAD_STOPPED)
|
||||
{
|
||||
/* Thread has ended */
|
||||
result = PTE_OS_OK;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
SceKernelSemaInfo semInfo;
|
||||
|
||||
if (pThreadData != NULL)
|
||||
{
|
||||
SceUID osResult;
|
||||
|
||||
osResult = sceKernelReferSemaStatus(pThreadData->cancelSem, &semInfo);
|
||||
|
||||
if (osResult == SCE_KERNEL_ERROR_OK)
|
||||
{
|
||||
if (semInfo.currentCount > 0)
|
||||
{
|
||||
result = PTE_OS_INTERRUPTED;
|
||||
break;
|
||||
}
|
||||
/* Nothing found and not timed out yet; let's yield so we're not
|
||||
* in busy loop.
|
||||
*/
|
||||
else
|
||||
sceKernelDelayThread(POLLING_DELAY_IN_us);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = PTE_OS_GENERAL_FAILURE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
pte_osThreadHandle pte_osThreadGetHandle(void)
|
||||
{
|
||||
return sceKernelGetThreadId();
|
||||
}
|
||||
|
||||
int pte_osThreadGetPriority(pte_osThreadHandle threadHandle)
|
||||
{
|
||||
SceKernelThreadInfo thinfo;
|
||||
|
||||
thinfo.size = sizeof(SceKernelThreadInfo);
|
||||
|
||||
sceKernelReferThreadStatus(threadHandle, &thinfo);
|
||||
|
||||
return thinfo.currentPriority;
|
||||
}
|
||||
|
||||
pte_osResult pte_osThreadSetPriority(pte_osThreadHandle threadHandle, int newPriority)
|
||||
{
|
||||
sceKernelChangeThreadPriority(threadHandle, newPriority);
|
||||
return PTE_OS_OK;
|
||||
}
|
||||
|
||||
pte_osResult pte_osThreadCancel(pte_osThreadHandle threadHandle)
|
||||
{
|
||||
pspThreadData *pThreadData = getThreadData(threadHandle);
|
||||
SceUID osResult = sceKernelSignalSema(pThreadData->cancelSem, 1);
|
||||
|
||||
if (osResult == SCE_KERNEL_ERROR_OK)
|
||||
return PTE_OS_OK;
|
||||
|
||||
return PTE_OS_GENERAL_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
pte_osResult pte_osThreadCheckCancel(pte_osThreadHandle threadHandle)
|
||||
{
|
||||
SceKernelSemaInfo semInfo;
|
||||
SceUID osResult;
|
||||
pte_osResult result;
|
||||
pspThreadData *pThreadData = getThreadData(threadHandle);
|
||||
|
||||
if (pThreadData != NULL)
|
||||
{
|
||||
osResult = sceKernelReferSemaStatus(pThreadData->cancelSem, &semInfo);
|
||||
|
||||
if (osResult == SCE_KERNEL_ERROR_OK)
|
||||
{
|
||||
if (semInfo.currentCount > 0)
|
||||
result = PTE_OS_INTERRUPTED;
|
||||
else
|
||||
result = PTE_OS_OK;
|
||||
}
|
||||
/* sceKernelReferSemaStatus returned an error */
|
||||
else
|
||||
result = PTE_OS_GENERAL_FAILURE;
|
||||
}
|
||||
/* For some reason, we couldn't get thread data */
|
||||
else
|
||||
result = PTE_OS_GENERAL_FAILURE;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void pte_osThreadSleep(unsigned int msecs)
|
||||
{
|
||||
sceKernelDelayThread(msecs*1000);
|
||||
}
|
||||
|
||||
int pte_osThreadGetMinPriority(void)
|
||||
{
|
||||
return 17;
|
||||
}
|
||||
|
||||
int pte_osThreadGetMaxPriority(void)
|
||||
{
|
||||
return 30;
|
||||
}
|
||||
|
||||
int pte_osThreadGetDefaultPriority(void)
|
||||
{
|
||||
return 18;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Mutexes
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
pte_osResult pte_osMutexCreate(pte_osMutexHandle *pHandle)
|
||||
{
|
||||
static int mutexCtr = 0;
|
||||
char mutexName[32];
|
||||
pte_osMutexHandle handle;
|
||||
|
||||
if (mutexCtr++ > MAX_PSP_UID)
|
||||
mutexCtr = 0;
|
||||
|
||||
snprintf(mutexName,sizeof(mutexName),"mutex%d",mutexCtr);
|
||||
|
||||
handle = sceKernelCreateSema(mutexName,
|
||||
0, /* attributes (default) */
|
||||
1, /* initial value */
|
||||
1, /* maximum value */
|
||||
0); /* options (default) */
|
||||
|
||||
*pHandle = handle;
|
||||
|
||||
return PTE_OS_OK;
|
||||
}
|
||||
|
||||
pte_osResult pte_osMutexDelete(pte_osMutexHandle handle)
|
||||
{
|
||||
sceKernelDeleteSema(handle);
|
||||
|
||||
return PTE_OS_OK;
|
||||
}
|
||||
|
||||
pte_osResult pte_osMutexLock(pte_osMutexHandle handle)
|
||||
{
|
||||
sceKernelWaitSema(handle, 1, NULL);
|
||||
|
||||
return PTE_OS_OK;
|
||||
}
|
||||
|
||||
pte_osResult pte_osMutexTimedLock(pte_osMutexHandle handle, unsigned int timeoutMsecs)
|
||||
{
|
||||
SceUInt timeoutUsecs = timeoutMsecs*1000;
|
||||
int status = sceKernelWaitSema(handle, 1, &timeoutUsecs);
|
||||
|
||||
/* Assume that any error from sceKernelWaitSema was due to a timeout */
|
||||
if (status < 0)
|
||||
return PTE_OS_TIMEOUT;
|
||||
|
||||
return PTE_OS_OK;
|
||||
}
|
||||
|
||||
|
||||
pte_osResult pte_osMutexUnlock(pte_osMutexHandle handle)
|
||||
{
|
||||
sceKernelSignalSema(handle, 1);
|
||||
|
||||
return PTE_OS_OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Semaphores
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
pte_osResult pte_osSemaphoreCreate(int initialValue, pte_osSemaphoreHandle *pHandle)
|
||||
{
|
||||
pte_osSemaphoreHandle handle;
|
||||
static int semCtr = 0;
|
||||
char semName[32];
|
||||
|
||||
if (semCtr++ > MAX_PSP_UID)
|
||||
semCtr = 0;
|
||||
|
||||
snprintf(semName,sizeof(semName),"pthread_sem%d",semCtr);
|
||||
|
||||
handle = sceKernelCreateSema(semName,
|
||||
0, /* attributes (default) */
|
||||
initialValue, /* initial value */
|
||||
SEM_VALUE_MAX, /* maximum value */
|
||||
0); /* options (default) */
|
||||
|
||||
*pHandle = handle;
|
||||
|
||||
return PTE_OS_OK;
|
||||
}
|
||||
|
||||
pte_osResult pte_osSemaphoreDelete(pte_osSemaphoreHandle handle)
|
||||
{
|
||||
sceKernelDeleteSema(handle);
|
||||
|
||||
return PTE_OS_OK;
|
||||
}
|
||||
|
||||
|
||||
pte_osResult pte_osSemaphorePost(pte_osSemaphoreHandle handle, int count)
|
||||
{
|
||||
sceKernelSignalSema(handle, count);
|
||||
|
||||
return PTE_OS_OK;
|
||||
}
|
||||
|
||||
pte_osResult pte_osSemaphorePend(pte_osSemaphoreHandle handle, unsigned int *pTimeoutMsecs)
|
||||
{
|
||||
unsigned int timeoutUsecs;
|
||||
unsigned int *pTimeoutUsecs;
|
||||
SceUInt result;
|
||||
pte_osResult osResult;
|
||||
|
||||
if (pTimeoutMsecs == NULL)
|
||||
pTimeoutUsecs = NULL;
|
||||
else
|
||||
{
|
||||
timeoutUsecs = *pTimeoutMsecs * 1000;
|
||||
pTimeoutUsecs = &timeoutUsecs;
|
||||
}
|
||||
|
||||
result = sceKernelWaitSema(handle, 1, pTimeoutUsecs);
|
||||
|
||||
if (result == SCE_KERNEL_ERROR_OK)
|
||||
return PTE_OS_OK;
|
||||
|
||||
if (result == SCE_KERNEL_ERROR_WAIT_TIMEOUT)
|
||||
return PTE_OS_TIMEOUT;
|
||||
|
||||
return PTE_OS_GENERAL_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Pend on a semaphore- and allow the pend to be cancelled.
|
||||
*
|
||||
* PSP OS provides no functionality to asynchronously interrupt a blocked call. We simulte
|
||||
* this by polling on the main semaphore and the cancellation semaphore and sleeping in a loop.
|
||||
*/
|
||||
pte_osResult pte_osSemaphoreCancellablePend(pte_osSemaphoreHandle semHandle, unsigned int *pTimeout)
|
||||
{
|
||||
pspThreadData *pThreadData;
|
||||
|
||||
pThreadData = getThreadData(sceKernelGetThreadId());
|
||||
|
||||
clock_t start_time;
|
||||
pte_osResult result = PTE_OS_OK;
|
||||
unsigned int timeout;
|
||||
unsigned char timeoutEnabled;
|
||||
|
||||
start_time = clock();
|
||||
|
||||
// clock() is in microseconds, timeout as passed in was in milliseconds
|
||||
if (pTimeout == NULL)
|
||||
{
|
||||
timeout = 0;
|
||||
timeoutEnabled = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
timeout = *pTimeout * 1000;
|
||||
timeoutEnabled = 1;
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
SceUInt semTimeout;
|
||||
int status;
|
||||
|
||||
/* Poll semaphore */
|
||||
semTimeout = 0;
|
||||
status = sceKernelWaitSema(semHandle, 1, &semTimeout);
|
||||
|
||||
if (status == SCE_KERNEL_ERROR_OK)
|
||||
{
|
||||
/* User semaphore posted to */
|
||||
result = PTE_OS_OK;
|
||||
break;
|
||||
}
|
||||
else if ((timeoutEnabled) && ((clock() - start_time) > timeout))
|
||||
{
|
||||
/* Timeout expired */
|
||||
result = PTE_OS_TIMEOUT;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
SceKernelSemaInfo semInfo;
|
||||
|
||||
if (pThreadData != NULL)
|
||||
{
|
||||
SceUID osResult;
|
||||
|
||||
osResult = sceKernelReferSemaStatus(pThreadData->cancelSem, &semInfo);
|
||||
|
||||
if (osResult == SCE_KERNEL_ERROR_OK)
|
||||
{
|
||||
if (semInfo.currentCount > 0)
|
||||
{
|
||||
result = PTE_OS_INTERRUPTED;
|
||||
break;
|
||||
}
|
||||
/* Nothing found and not timed out yet; let's yield so we're not
|
||||
* in busy loop.
|
||||
*/
|
||||
else
|
||||
sceKernelDelayThread(POLLING_DELAY_IN_us);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = PTE_OS_GENERAL_FAILURE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Atomic Operations
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
int pte_osAtomicExchange(int *ptarg, int val)
|
||||
{
|
||||
int intc = pspSdkDisableInterrupts();
|
||||
int origVal = *ptarg;
|
||||
|
||||
*ptarg = val;
|
||||
|
||||
pspSdkEnableInterrupts(intc);
|
||||
|
||||
return origVal;
|
||||
|
||||
}
|
||||
|
||||
int pte_osAtomicCompareExchange(int *pdest, int exchange, int comp)
|
||||
{
|
||||
int intc = pspSdkDisableInterrupts();
|
||||
int origVal = *pdest;
|
||||
|
||||
if (*pdest == comp)
|
||||
*pdest = exchange;
|
||||
|
||||
pspSdkEnableInterrupts(intc);
|
||||
|
||||
return origVal;
|
||||
}
|
||||
|
||||
|
||||
int pte_osAtomicExchangeAdd(int volatile* pAddend, int value)
|
||||
{
|
||||
int intc = pspSdkDisableInterrupts();
|
||||
int origVal = *pAddend;
|
||||
|
||||
*pAddend += value;
|
||||
|
||||
pspSdkEnableInterrupts(intc);
|
||||
|
||||
return origVal;
|
||||
}
|
||||
|
||||
int pte_osAtomicDecrement(int *pdest)
|
||||
{
|
||||
int val;
|
||||
|
||||
int intc = pspSdkDisableInterrupts();
|
||||
|
||||
(*pdest)--;
|
||||
val = *pdest;
|
||||
|
||||
pspSdkEnableInterrupts(intc);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
int pte_osAtomicIncrement(int *pdest)
|
||||
{
|
||||
int val;
|
||||
|
||||
int intc = pspSdkDisableInterrupts();
|
||||
|
||||
(*pdest)++;
|
||||
val = *pdest;
|
||||
|
||||
pspSdkEnableInterrupts(intc);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Helper functions
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
static pspThreadData *getThreadData(SceUID threadHandle)
|
||||
{
|
||||
void *pTls = getTlsStructFromThread(threadHandle);
|
||||
pspThreadData *pThreadData = (pspThreadData *)
|
||||
pteTlsGetValue(pTls, threadDataKey);
|
||||
|
||||
return pThreadData;
|
||||
}
|
||||
|
||||
static void *getTlsStructFromThread(SceUID thid)
|
||||
{
|
||||
SceKernelThreadInfo thinfo;
|
||||
unsigned int ptr;
|
||||
unsigned int thrNum;
|
||||
int numMatches;
|
||||
|
||||
thinfo.size = sizeof(SceKernelThreadInfo);
|
||||
|
||||
sceKernelReferThreadStatus(thid, &thinfo);
|
||||
|
||||
numMatches = sscanf(thinfo.name,"pthread%04d__%x", &thrNum, &ptr);
|
||||
|
||||
/* If we were called from a pthread, use the TLS allocated when the thread
|
||||
* was created. Otherwise, we were called from a non-pthread, so use the
|
||||
* "global". This is a pretty bad hack, but necessary due to lack of TLS on PSP.
|
||||
*/
|
||||
if (numMatches == 2)
|
||||
return (void *) ptr;
|
||||
|
||||
return globalTls;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Thread Local Storage
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
pte_osResult pte_osTlsSetValue(unsigned int key, void * value)
|
||||
{
|
||||
void *pTls = getTlsStructFromThread(sceKernelGetThreadId());
|
||||
|
||||
return pteTlsSetValue(pTls, key, value);
|
||||
}
|
||||
|
||||
void * pte_osTlsGetValue(unsigned int index)
|
||||
{
|
||||
void *pTls = getTlsStructFromThread(sceKernelGetThreadId());
|
||||
|
||||
return (void *) pteTlsGetValue(pTls, index);
|
||||
|
||||
}
|
||||
|
||||
|
||||
pte_osResult pte_osTlsAlloc(unsigned int *pKey)
|
||||
{
|
||||
void *pTls = getTlsStructFromThread(sceKernelGetThreadId());
|
||||
|
||||
return pteTlsAlloc(pKey);
|
||||
|
||||
}
|
||||
|
||||
pte_osResult pte_osTlsFree(unsigned int index)
|
||||
{
|
||||
return pteTlsFree(index);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Miscellaneous
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
int ftime(struct timeb *tb)
|
||||
{
|
||||
struct timeval tv;
|
||||
struct timezone tz;
|
||||
|
||||
gettimeofday(&tv, &tz);
|
||||
|
||||
tb->time = tv.tv_sec;
|
||||
tb->millitm = tv.tv_usec / 1000;
|
||||
tb->timezone = tz.tz_minuteswest;
|
||||
tb->dstflag = tz.tz_dsttime;
|
||||
|
||||
return 0;
|
||||
}
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* psp_osal.h
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <pspsdk.h>
|
||||
|
||||
typedef SceUID pte_osThreadHandle;
|
||||
|
||||
typedef SceUID pte_osSemaphoreHandle;
|
||||
|
||||
typedef SceUID pte_osMutexHandle;
|
||||
|
||||
|
||||
#define OS_IS_HANDLE_VALID(x) ((x) > 0)
|
||||
|
||||
#define OS_MAX_SIMUL_THREADS 10
|
||||
|
||||
#define OS_DEFAULT_PRIO 11
|
||||
|
||||
#define OS_MIN_PRIO 17
|
||||
#define OS_MAX_PRIO 32
|
||||
|
||||
#if 0
|
||||
#define HAVE_THREAD_SAFE_ERRNO
|
||||
#endif
|
||||
|
||||
#define POLLING_DELAY_IN_us 100
|
||||
|
||||
#define OS_MAX_SEM_VALUE 254
|
||||
|
||||
int PspInterlockedExchange(int *ptarg, int val);
|
||||
int PspInterlockedCompareExchange(int *pdest, int exchange, int comp);
|
||||
int PspInterlockedExchangeAdd(int volatile* pAddend, int value);
|
||||
int PspInterlockedDecrement(int *pdest);
|
||||
int PspInterlockedIncrement(int *pdest);
|
||||
|
||||
|
||||
|
||||
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
#ifndef _OS_SUPPORT_H_
|
||||
#define _OS_SUPPORT_H_
|
||||
|
||||
/* Platform specific one must be included first */
|
||||
#include "psp_osal.h"
|
||||
|
||||
#include "pte_generic_osal.h"
|
||||
|
||||
#endif /* _OS_SUPPORT_H */
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
/* pte_types.h */
|
||||
|
||||
#ifndef PTE_TYPES_H
|
||||
#define PTE_TYPES_H
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/timeb.h>
|
||||
|
||||
typedef int pid_t;
|
||||
|
||||
#endif /* PTE_TYPES_H */
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <pspkernel.h>
|
||||
#include <pspdebug.h>
|
||||
#include <pspsdk.h>
|
||||
#include <pspctrl.h>
|
||||
|
||||
PSP_MODULE_INFO("Pthread Test", 0, 1, 1);
|
||||
|
||||
extern void pte_test_main();
|
||||
|
||||
#ifdef JNS
|
||||
#define printf pspDebugScreenPrintf
|
||||
#endif
|
||||
|
||||
/* Exit callback */
|
||||
int exit_callback(int arg1, int arg2, void *common)
|
||||
{
|
||||
sceKernelExitGame();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Callback thread */
|
||||
int CallbackThread(SceSize args, void *argp)
|
||||
{
|
||||
int cbid = sceKernelCreateCallback("Exit Callback", exit_callback, NULL);
|
||||
sceKernelRegisterExitCallback(cbid);
|
||||
|
||||
sceKernelSleepThreadCB();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Sets up the callback thread and returns its thread id */
|
||||
int SetupCallbacks(void)
|
||||
{
|
||||
int thid = sceKernelCreateThread("update_thread", CallbackThread, 0x11, 0xFA0, 0, 0);
|
||||
if (thid >= 0)
|
||||
sceKernelStartThread(thid, 0, 0);
|
||||
|
||||
return thid;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
SceCtrlData pad;
|
||||
|
||||
pspDebugScreenInit();
|
||||
SetupCallbacks();
|
||||
|
||||
pte_test_main();
|
||||
|
||||
while (1)
|
||||
{
|
||||
sceCtrlReadBufferPositive(&pad, 1);
|
||||
if (pad.Buttons & PSP_CTRL_UP)
|
||||
{
|
||||
printf("Exiting...\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Vendored
-1059
File diff suppressed because it is too large.
Load diff
Vendored
-449
@@ -1,449 +0,0 @@
|
||||
/*
|
||||
* pte_cancellable_wait.c
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#ifndef _GENERIC_OS_SUPPORT_H_
|
||||
#define _GENERIC_OS_SUPPORT_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
/** @name Misc */
|
||||
//@{
|
||||
typedef enum pte_osResult
|
||||
{
|
||||
|
||||
/** Operation completed successfully */
|
||||
PTE_OS_OK = 0,
|
||||
|
||||
/** Operation failed because there insufficient resources */
|
||||
PTE_OS_NO_RESOURCES,
|
||||
|
||||
/** Operation failed due to a general failure */
|
||||
PTE_OS_GENERAL_FAILURE,
|
||||
|
||||
/** Operation did not complete because a user specified timeout expired. */
|
||||
PTE_OS_TIMEOUT,
|
||||
|
||||
/** The operation was interrupted before it could complete. */
|
||||
PTE_OS_INTERRUPTED,
|
||||
|
||||
/** An invalid parameter was specified */
|
||||
PTE_OS_INVALID_PARAM
|
||||
|
||||
|
||||
} pte_osResult;
|
||||
|
||||
/**
|
||||
* Provides a hook for the OSAL to implement any OS specific initialization. This is guaranteed to be
|
||||
* called before any other OSAL function.
|
||||
*/
|
||||
pte_osResult pte_osInit(void);
|
||||
//@}
|
||||
|
||||
/** @name Mutexes */
|
||||
//@{
|
||||
|
||||
/**
|
||||
* Creates a mutex
|
||||
*
|
||||
* @param pHandle Set to the handle of the newly created mutex.
|
||||
*
|
||||
* @return PTE_OS_OK - Mutex successfully created
|
||||
* @return PTE_OS_NO_RESOURCESs - Insufficient resources to create mutex
|
||||
*/
|
||||
pte_osResult pte_osMutexCreate(pte_osMutexHandle *pHandle);
|
||||
|
||||
/**
|
||||
* Deletes a mutex and frees any associated resources.
|
||||
*
|
||||
* @param handle Handle of mutex to delete.
|
||||
*
|
||||
* @return PTE_OS_OK - Mutex successfully deleted.
|
||||
*/
|
||||
pte_osResult pte_osMutexDelete(pte_osMutexHandle handle);
|
||||
|
||||
/**
|
||||
* Locks the mutex
|
||||
*
|
||||
* @param handle Handle of mutex to lock.
|
||||
*
|
||||
* @return PTE_OS_OK - Mutex successfully locked.
|
||||
*/
|
||||
pte_osResult pte_osMutexLock(pte_osMutexHandle handle);
|
||||
|
||||
/**
|
||||
* Locks the mutex, returning after @p timeoutMsecs if the resources is not
|
||||
* available. Can be used for polling mutex by using @p timeoutMsecs of zero.
|
||||
*
|
||||
* @param handle Handle of mutex to lock.
|
||||
* @param timeoutMsecs Number of milliseconds to wait for resource before returning.
|
||||
*
|
||||
* @return PTE_OS_OK - Mutex successfully locked.
|
||||
* @return PTE_OS_TIMEOUT - Timeout expired before lock was obtained.
|
||||
*/
|
||||
pte_osResult pte_osMutexTimedLock(pte_osMutexHandle handle, unsigned int timeoutMsecs);
|
||||
|
||||
/**
|
||||
* Unlocks the mutex
|
||||
*
|
||||
* @param handle Handle of mutex to unlock
|
||||
*
|
||||
* @return PTE_OS_OK - Mutex successfully unlocked.
|
||||
*/
|
||||
pte_osResult pte_osMutexUnlock(pte_osMutexHandle handle);
|
||||
//@}
|
||||
|
||||
/** @name Threads */
|
||||
//@{
|
||||
typedef int (*pte_osThreadEntryPoint)(void *params);
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new thread. The thread must be started in a suspended state - it will be
|
||||
* explicitly started when pte_osThreadStart() is called.
|
||||
*
|
||||
* @param entryPoint Entry point to the new thread.
|
||||
* @param stackSize The initial stack size, in bytes. Note that this can be considered a minimum -
|
||||
* for instance if the OS requires a larger stack space than what the caller specified.
|
||||
* @param initialPriority The priority that the new thread should be initially set to.
|
||||
* @param argv Parameter to pass to the new thread.
|
||||
* @param ppte_osThreadHandle set to the handle of the new thread.
|
||||
*
|
||||
* @return PTE_OS_OK - New thread successfully created.
|
||||
* @return PTE_OS_NO_RESOURCESs - Insufficient resources to create thread
|
||||
*/
|
||||
pte_osResult pte_osThreadCreate(pte_osThreadEntryPoint entryPoint,
|
||||
int stackSize,
|
||||
int initialPriority,
|
||||
void *argv,
|
||||
pte_osThreadHandle* ppte_osThreadHandle);
|
||||
|
||||
/**
|
||||
* Starts executing the specified thread.
|
||||
*
|
||||
* @param osThreadHandle handle of the thread to start.
|
||||
*
|
||||
* @return PTE_OS_OK - thread successfully started.
|
||||
*/
|
||||
pte_osResult pte_osThreadStart(pte_osThreadHandle osThreadHandle);
|
||||
|
||||
/**
|
||||
* Causes the current thread to stop executing.
|
||||
*
|
||||
* @return Never returns (thread terminated)
|
||||
*/
|
||||
void pte_osThreadExit();
|
||||
|
||||
/**
|
||||
* Waits for the specified thread to end. If the thread has already terminated, this returns
|
||||
* immediately.
|
||||
*
|
||||
* @param threadHandle Handle fo thread to wait for.
|
||||
*
|
||||
* @return PTE_OS_OK - specified thread terminated.
|
||||
*/
|
||||
pte_osResult pte_osThreadWaitForEnd(pte_osThreadHandle threadHandle);
|
||||
|
||||
/**
|
||||
* Returns the handle of the currently executing thread.
|
||||
*/
|
||||
pte_osThreadHandle pte_osThreadGetHandle(void);
|
||||
|
||||
/**
|
||||
* Returns the priority of the specified thread.
|
||||
*/
|
||||
int pte_osThreadGetPriority(pte_osThreadHandle threadHandle);
|
||||
|
||||
/**
|
||||
* Sets the priority of the specified thread.
|
||||
*
|
||||
* @return PTE_OS_OK - thread priority successfully set
|
||||
*/
|
||||
pte_osResult pte_osThreadSetPriority(pte_osThreadHandle threadHandle, int newPriority);
|
||||
|
||||
/**
|
||||
* Frees resources associated with the specified thread. This is called after the thread has terminated
|
||||
* and is no longer needed (e.g. after pthread_join returns). This call will always be made
|
||||
* from a different context than that of the target thread.
|
||||
*/
|
||||
pte_osResult pte_osThreadDelete(pte_osThreadHandle handle);
|
||||
|
||||
/**
|
||||
* Frees resources associated with the specified thread and then causes the thread to exit.
|
||||
* This is called after the thread has terminated and is no longer needed (e.g. after
|
||||
* pthread_join returns). This call will always be made from the context of the target thread.
|
||||
*/
|
||||
pte_osResult pte_osThreadExitAndDelete(pte_osThreadHandle handle);
|
||||
|
||||
/**
|
||||
* Cancels the specified thread. This should cause pte_osSemaphoreCancellablePend() and for pte_osThreadCheckCancel()
|
||||
* to return @p PTE_OS_INTERRUPTED.
|
||||
*
|
||||
* @param threadHandle handle to the thread to cancel.
|
||||
*
|
||||
* @return Thread successfully canceled.
|
||||
*/
|
||||
pte_osResult pte_osThreadCancel(pte_osThreadHandle threadHandle);
|
||||
|
||||
/**
|
||||
* Check if pte_osThreadCancel() has been called on the specified thread.
|
||||
*
|
||||
* @param threadHandle handle of thread to check the state of.
|
||||
*
|
||||
* @return PTE_OS_OK - Thread has not been cancelled
|
||||
* @return PTE_OS_INTERRUPTED - Thread has been cancelled.
|
||||
*/
|
||||
pte_osResult pte_osThreadCheckCancel(pte_osThreadHandle threadHandle);
|
||||
|
||||
/**
|
||||
* Causes the current thread to sleep for the specified number of milliseconds.
|
||||
*/
|
||||
void pte_osThreadSleep(unsigned int msecs);
|
||||
|
||||
/**
|
||||
* Returns the maximum allowable priority
|
||||
*/
|
||||
int pte_osThreadGetMaxPriority();
|
||||
|
||||
/**
|
||||
* Returns the minimum allowable priority
|
||||
*/
|
||||
int pte_osThreadGetMinPriority();
|
||||
|
||||
/**
|
||||
* Returns the priority that should be used if the caller to pthread_create doesn't
|
||||
* explicitly set one.
|
||||
*/
|
||||
int pte_osThreadGetDefaultPriority();
|
||||
|
||||
//@}
|
||||
|
||||
|
||||
/** @name Semaphores */
|
||||
//@{
|
||||
|
||||
/**
|
||||
* Creates a semaphore
|
||||
*
|
||||
* @param initialValue Initial value of the semaphore
|
||||
* @param pHandle Set to the handle of the newly created semaphore.
|
||||
*
|
||||
* @return PTE_OS_OK - Semaphore successfully created
|
||||
* @return PTE_OS_NO_RESOURCESs - Insufficient resources to create semaphore
|
||||
*/
|
||||
pte_osResult pte_osSemaphoreCreate(int initialValue, pte_osSemaphoreHandle *pHandle);
|
||||
|
||||
/**
|
||||
* Deletes a semaphore and frees any associated resources.
|
||||
*
|
||||
* @param handle Handle of semaphore to delete.
|
||||
*
|
||||
* @return PTE_OS_OK - Semaphore successfully deleted.
|
||||
*/
|
||||
pte_osResult pte_osSemaphoreDelete(pte_osSemaphoreHandle handle);
|
||||
|
||||
/**
|
||||
* Posts to the semaphore
|
||||
*
|
||||
* @param handle Semaphore to release
|
||||
* @param count Amount to increment the semaphore by.
|
||||
*
|
||||
* @return PTE_OS_OK - semaphore successfully released.
|
||||
*/
|
||||
pte_osResult pte_osSemaphorePost(pte_osSemaphoreHandle handle, int count);
|
||||
|
||||
/**
|
||||
* Acquire a semaphore, returning after @p timeoutMsecs if the semaphore is not
|
||||
* available. Can be used for polling a semaphore by using @p timeoutMsecs of zero.
|
||||
*
|
||||
* @param handle Handle of semaphore to acquire.
|
||||
* @param pTimeout Pointer to the number of milliseconds to wait to acquire the semaphore
|
||||
* before returning. If set to NULL, wait forever.
|
||||
*
|
||||
* @return PTE_OS_OK - Semaphore successfully acquired.
|
||||
* @return PTE_OS_TIMEOUT - Timeout expired before semaphore was obtained.
|
||||
*/
|
||||
pte_osResult pte_osSemaphorePend(pte_osSemaphoreHandle handle, unsigned int *pTimeout);
|
||||
|
||||
/**
|
||||
* Acquire a semaphore, returning after @p timeoutMsecs if the semaphore is not
|
||||
* available. Can be used for polling a semaphore by using @p timeoutMsecs of zero.
|
||||
* Call must return immediately if pte_osThreadCancel() is called on the thread waiting for
|
||||
* the semaphore.
|
||||
*
|
||||
* @param handle Handle of semaphore to acquire.
|
||||
* @param pTimeout Pointer to the number of milliseconds to wait to acquire the semaphore
|
||||
* before returning. If set to NULL, wait forever.
|
||||
*
|
||||
* @return PTE_OS_OK - Semaphore successfully acquired.
|
||||
* @return PTE_OS_TIMEOUT - Timeout expired before semaphore was obtained.
|
||||
*/
|
||||
pte_osResult pte_osSemaphoreCancellablePend(pte_osSemaphoreHandle handle, unsigned int *pTimeout);
|
||||
//@}
|
||||
|
||||
|
||||
/** @name Thread Local Storage */
|
||||
//@{
|
||||
/**
|
||||
* Sets the thread specific value for the specified key for the
|
||||
* currently executing thread.
|
||||
*
|
||||
* @param index The TLS key for the value.
|
||||
* @param value The value to save
|
||||
*/
|
||||
pte_osResult pte_osTlsSetValue(unsigned int key, void * value);
|
||||
|
||||
/**
|
||||
* Retrieves the thread specific value for the specified key for
|
||||
* the currently executing thread. If a value has not been set
|
||||
* for this key, NULL should be returned (i.e. TLS values default
|
||||
* to NULL).
|
||||
*
|
||||
* @param index The TLS key for the value.
|
||||
*
|
||||
* @return The value associated with @p key for the current thread.
|
||||
*/
|
||||
void * pte_osTlsGetValue(unsigned int key);
|
||||
|
||||
/**
|
||||
* Initializes the OS TLS support. This is called by the PTE library
|
||||
* prior to performing ANY TLS operation.
|
||||
*/
|
||||
void pte_osTlsInit(void);
|
||||
|
||||
/**
|
||||
* Allocates a new TLS key.
|
||||
*
|
||||
* @param pKey On success will be set to the newly allocated key.
|
||||
*
|
||||
* @return PTE_OS_OK - TLS key successfully allocated.
|
||||
* @return PTE_OS_NO_RESOURCESs - Insufficient resources to allocate key (e.g.
|
||||
* maximum number of keys reached).
|
||||
*/
|
||||
pte_osResult pte_osTlsAlloc(unsigned int *pKey);
|
||||
|
||||
/**
|
||||
* Frees the specified TLS key.
|
||||
*
|
||||
* @param index TLS key to free
|
||||
*
|
||||
* @return PTE_OS_OK - TLS key was successfully freed.
|
||||
*/
|
||||
pte_osResult pte_osTlsFree(unsigned int key);
|
||||
//@}
|
||||
|
||||
/** @name Atomic operations */
|
||||
//@{
|
||||
|
||||
/**
|
||||
* Sets the target to the specified value as an atomic operation.
|
||||
*
|
||||
* \code
|
||||
* origVal = *ptarg
|
||||
* *ptarg = val
|
||||
* return origVal
|
||||
* \endcode
|
||||
*
|
||||
* @param pTarg Pointer to the value to be exchanged.
|
||||
* @param val Value to be exchanged
|
||||
*
|
||||
* @return original value of destination
|
||||
*/
|
||||
int pte_osAtomicExchange(int *pTarg, int val);
|
||||
|
||||
/**
|
||||
* Performs an atomic compare-and-exchange oepration on the specified
|
||||
* value. That is:
|
||||
*
|
||||
* \code
|
||||
* origVal = *pdest
|
||||
* if (*pdest == comp)
|
||||
* then *pdest = exchange
|
||||
* return origVal
|
||||
* \endcode
|
||||
*
|
||||
* @param pdest Pointer to the destination value.
|
||||
* @param exchange Exchange value (value to set destination to if destination == comparand)
|
||||
* @param comp The value to compare to destination.
|
||||
*
|
||||
* @return Original value of destination
|
||||
*/
|
||||
int pte_osAtomicCompareExchange(int *pdest, int exchange, int comp);
|
||||
|
||||
/**
|
||||
* Adds the value to target as an atomic operation
|
||||
*
|
||||
* \code
|
||||
* origVal = *pdest
|
||||
* *pAddend += value
|
||||
* return origVal
|
||||
* \endcode
|
||||
*
|
||||
* @param pdest Pointer to the variable to be updated.
|
||||
* @param value Value to be added to the variable.
|
||||
*
|
||||
* @return Original value of destination
|
||||
*/
|
||||
int pte_osAtomicExchangeAdd(int volatile* pdest, int value);
|
||||
|
||||
/**
|
||||
* Decrements the destination.
|
||||
*
|
||||
* \code
|
||||
* origVal = *pdest
|
||||
* *pdest++
|
||||
* return origVal
|
||||
* \endcode
|
||||
*
|
||||
* @param pdest Destination value to decrement
|
||||
*
|
||||
* @return Original destination value
|
||||
*/
|
||||
int pte_osAtomicDecrement(int *pdest);
|
||||
|
||||
/**
|
||||
* Increments the destination value
|
||||
*
|
||||
* \code
|
||||
* origVal = *pdest;
|
||||
* *pdest++;
|
||||
* return origVal;
|
||||
*/
|
||||
int pte_osAtomicIncrement(int *pdest);
|
||||
//@}
|
||||
|
||||
struct timeb;
|
||||
|
||||
int ftime(struct timeb *tb);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // _OS_SUPPORT_H_
|
||||
Vendored
-396
@@ -1,396 +0,0 @@
|
||||
/*
|
||||
* pte_main.c
|
||||
*
|
||||
* Description:
|
||||
* This translation unit instantiates data associated with the implementation
|
||||
* as a whole.
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "pte_osal.h"
|
||||
|
||||
#include "pthread.h"
|
||||
#include "implement.h"
|
||||
|
||||
|
||||
int pte_processInitialized = PTE_FALSE;
|
||||
pte_thread_t * pte_threadReuseTop = PTE_THREAD_REUSE_EMPTY;
|
||||
pte_thread_t * pte_threadReuseBottom = PTE_THREAD_REUSE_EMPTY;
|
||||
pthread_key_t pte_selfThreadKey = NULL;
|
||||
pthread_key_t pte_cleanupKey = NULL;
|
||||
pthread_cond_t pte_cond_list_head = NULL;
|
||||
pthread_cond_t pte_cond_list_tail = NULL;
|
||||
|
||||
int pte_concurrency = 0;
|
||||
|
||||
/* What features have been auto-detaected */
|
||||
int pte_features = 0;
|
||||
|
||||
unsigned char pte_smp_system = PTE_TRUE; /* Safer if assumed true initially. */
|
||||
|
||||
/*
|
||||
* Global lock for managing pthread_t struct reuse.
|
||||
*/
|
||||
pte_osMutexHandle pte_thread_reuse_lock;
|
||||
|
||||
/*
|
||||
* Global lock for testing internal state of statically declared mutexes.
|
||||
*/
|
||||
pte_osMutexHandle pte_mutex_test_init_lock;
|
||||
|
||||
/*
|
||||
* Global lock for testing internal state of PTHREAD_COND_INITIALIZER
|
||||
* created condition variables.
|
||||
*/
|
||||
pte_osMutexHandle pte_cond_test_init_lock;
|
||||
|
||||
/*
|
||||
* Global lock for testing internal state of PTHREAD_RWLOCK_INITIALIZER
|
||||
* created read/write locks.
|
||||
*/
|
||||
pte_osMutexHandle pte_rwlock_test_init_lock;
|
||||
|
||||
/*
|
||||
* Global lock for testing internal state of PTHREAD_SPINLOCK_INITIALIZER
|
||||
* created spin locks.
|
||||
*/
|
||||
pte_osMutexHandle pte_spinlock_test_init_lock;
|
||||
|
||||
/*
|
||||
* Global lock for condition variable linked list. The list exists
|
||||
* to wake up CVs when a WM_TIMECHANGE message arrives. See
|
||||
* w32_TimeChangeHandler.c.
|
||||
*/
|
||||
pte_osMutexHandle pte_cond_list_lock;
|
||||
|
||||
int pthread_create (pthread_t * tid,
|
||||
const pthread_attr_t * attr,
|
||||
void *(*start) (void *), void *arg)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function creates a thread running the start function,
|
||||
* passing it the parameter value, 'arg'. The 'attr'
|
||||
* argument specifies optional creation attributes.
|
||||
* The identity of the new thread is returned
|
||||
* via 'tid', which should not be NULL.
|
||||
*
|
||||
* PARAMETERS
|
||||
* tid
|
||||
* pointer to an instance of pthread_t
|
||||
*
|
||||
* attr
|
||||
* optional pointer to an instance of pthread_attr_t
|
||||
*
|
||||
* start
|
||||
* pointer to the starting routine for the new thread
|
||||
*
|
||||
* arg
|
||||
* optional parameter passed to 'start'
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function creates a thread running the start function,
|
||||
* passing it the parameter value, 'arg'. The 'attr'
|
||||
* argument specifies optional creation attributes.
|
||||
* The identity of the new thread is returned
|
||||
* via 'tid', which should not be the NULL pointer.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully created thread,
|
||||
* EINVAL attr invalid,
|
||||
* EAGAIN insufficient resources.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
pthread_t thread;
|
||||
pte_thread_t * tp;
|
||||
int result = EAGAIN;
|
||||
int run = PTE_TRUE;
|
||||
ThreadParms *parms = NULL;
|
||||
long stackSize;
|
||||
int priority = 0;
|
||||
pthread_t self;
|
||||
pte_osResult osResult;
|
||||
register pthread_attr_t a = NULL;
|
||||
|
||||
/*
|
||||
* Before doing anything, check that tid can be stored through
|
||||
* without invoking a memory protection error (segfault).
|
||||
* Make sure that the assignment below can't be optimised out by the compiler.
|
||||
* This is assured by conditionally assigning *tid again at the end.
|
||||
*/
|
||||
|
||||
if (attr != NULL)
|
||||
a = *attr;
|
||||
|
||||
if ((thread = pte_new ()) == NULL)
|
||||
goto FAIL0;
|
||||
|
||||
tp = (pte_thread_t *) thread;
|
||||
|
||||
priority = tp->sched_priority;
|
||||
|
||||
if ((parms = (ThreadParms *) malloc (sizeof (*parms))) == NULL)
|
||||
goto FAIL0;
|
||||
|
||||
parms->tid = thread;
|
||||
parms->start = start;
|
||||
parms->arg = arg;
|
||||
|
||||
if (a != NULL)
|
||||
{
|
||||
stackSize = a->stacksize;
|
||||
tp->detachState = a->detachstate;
|
||||
priority = a->param.sched_priority;
|
||||
|
||||
if ( (priority > pte_osThreadGetMaxPriority()) ||
|
||||
(priority < pte_osThreadGetMinPriority()) )
|
||||
{
|
||||
result = EINVAL;
|
||||
goto FAIL0;
|
||||
}
|
||||
|
||||
/* Everything else */
|
||||
|
||||
/*
|
||||
* Thread priority must be set to a valid system level
|
||||
* without altering the value set by pthread_attr_setschedparam().
|
||||
*/
|
||||
|
||||
if (PTHREAD_INHERIT_SCHED == a->inheritsched)
|
||||
{
|
||||
/*
|
||||
* If the thread that called pthread_create() is an OS thread
|
||||
* then the inherited priority could be the result of a temporary
|
||||
* system adjustment. This is not the case for POSIX threads.
|
||||
*/
|
||||
self = pthread_self ();
|
||||
priority = ((pte_thread_t *) self)->sched_priority;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Default stackSize
|
||||
*/
|
||||
stackSize = PTHREAD_STACK_MIN;
|
||||
|
||||
}
|
||||
|
||||
tp->state = run ? PThreadStateInitial : PThreadStateSuspended;
|
||||
|
||||
tp->keys = NULL;
|
||||
|
||||
/*
|
||||
* Threads must be started in suspended mode and resumed if necessary
|
||||
* after _beginthreadex returns us the handle. Otherwise we set up a
|
||||
* race condition between the creating and the created threads.
|
||||
* Note that we also retain a local copy of the handle for use
|
||||
* by us in case thread.p->threadH gets NULLed later but before we've
|
||||
* finished with it here.
|
||||
*/
|
||||
result = pthread_mutex_lock (&tp->threadLock);
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
/*
|
||||
* Must record the thread's sched_priority as given,
|
||||
* not as finally adjusted.
|
||||
*/
|
||||
tp->sched_priority = priority;
|
||||
|
||||
(void) pthread_mutex_unlock (&tp->threadLock);
|
||||
}
|
||||
|
||||
osResult = pte_osThreadCreate(pte_threadStart,
|
||||
stackSize,
|
||||
priority,
|
||||
parms,
|
||||
&(tp->threadId));
|
||||
|
||||
if (osResult == PTE_OS_OK)
|
||||
{
|
||||
pte_osThreadStart(tp->threadId);
|
||||
result = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
tp->threadId = 0;
|
||||
result = EAGAIN;
|
||||
goto FAIL0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fall Through Intentionally
|
||||
*/
|
||||
|
||||
/*
|
||||
* ------------
|
||||
* Failure Code
|
||||
* ------------
|
||||
*/
|
||||
|
||||
FAIL0:
|
||||
if (result != 0)
|
||||
{
|
||||
|
||||
pte_threadDestroy (thread);
|
||||
tp = NULL;
|
||||
|
||||
if (parms != NULL)
|
||||
free (parms);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tid != NULL)
|
||||
*tid = thread;
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
/*
|
||||
* The functions pte_pop_cleanup and pte_push_cleanup
|
||||
* are implemented here for applications written in C with no
|
||||
* C++ destructor support.
|
||||
*/
|
||||
|
||||
pte_cleanup_t *pte_pop_cleanup (int execute)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function pops the most recently pushed cleanup
|
||||
* handler. If execute is nonzero, then the cleanup handler
|
||||
* is executed if non-null.
|
||||
*
|
||||
* PARAMETERS
|
||||
* execute
|
||||
* if nonzero, execute the cleanup handler
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function pops the most recently pushed cleanup
|
||||
* handler. If execute is nonzero, then the cleanup handler
|
||||
* is executed if non-null.
|
||||
* NOTE: specify 'execute' as nonzero to avoid duplication
|
||||
* of common cleanup code.
|
||||
*
|
||||
* RESULTS
|
||||
* N/A
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
pte_cleanup_t *cleanup;
|
||||
|
||||
cleanup = (pte_cleanup_t *) pthread_getspecific (pte_cleanupKey);
|
||||
|
||||
if (cleanup != NULL)
|
||||
{
|
||||
if (execute && (cleanup->routine != NULL))
|
||||
{
|
||||
|
||||
(*cleanup->routine) (cleanup->arg);
|
||||
|
||||
}
|
||||
|
||||
pthread_setspecific (pte_cleanupKey, (void *) cleanup->prev);
|
||||
|
||||
}
|
||||
|
||||
return (cleanup);
|
||||
|
||||
} /* pte_pop_cleanup */
|
||||
|
||||
|
||||
void
|
||||
pte_push_cleanup (pte_cleanup_t * cleanup,
|
||||
pte_cleanup_callback_t routine, void *arg)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function pushes a new cleanup handler onto the thread's stack
|
||||
* of cleanup handlers. Each cleanup handler pushed onto the stack is
|
||||
* popped and invoked with the argument 'arg' when
|
||||
* a) the thread exits by calling 'pthread_exit',
|
||||
* b) when the thread acts on a cancellation request,
|
||||
* c) or when the thread calls pthread_cleanup_pop with a nonzero
|
||||
* 'execute' argument
|
||||
*
|
||||
* PARAMETERS
|
||||
* cleanup
|
||||
* a pointer to an instance of pthread_cleanup_t,
|
||||
*
|
||||
* routine
|
||||
* pointer to a cleanup handler,
|
||||
*
|
||||
* arg
|
||||
* parameter to be passed to the cleanup handler
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function pushes a new cleanup handler onto the thread's stack
|
||||
* of cleanup handlers. Each cleanup handler pushed onto the stack is
|
||||
* popped and invoked with the argument 'arg' when
|
||||
* a) the thread exits by calling 'pthread_exit',
|
||||
* b) when the thread acts on a cancellation request,
|
||||
* c) or when the thrad calls pthread_cleanup_pop with a nonzero
|
||||
* 'execute' argument
|
||||
* NOTE: pthread_push_cleanup, pte_pop_cleanup must be paired
|
||||
* in the same lexical scope.
|
||||
*
|
||||
* RESULTS
|
||||
* pthread_cleanup_t *
|
||||
* pointer to the previous cleanup
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
cleanup->routine = routine;
|
||||
cleanup->arg = arg;
|
||||
|
||||
cleanup->prev = (pte_cleanup_t *) pthread_getspecific (pte_cleanupKey);
|
||||
|
||||
pthread_setspecific (pte_cleanupKey, (void *) cleanup);
|
||||
}
|
||||
Vendored
-1017
File diff suppressed because it is too large.
Load diff
Vendored
-891
@@ -1,891 +0,0 @@
|
||||
/* This is an implementation of the threads API of POSIX 1003.1-2001.
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
#if !defined( PTHREAD_H )
|
||||
#define PTHREAD_H
|
||||
|
||||
#include <pte_types.h>
|
||||
|
||||
#include <sched.h>
|
||||
|
||||
#define PTE_VERSION 2,8,0,0
|
||||
#define PTE_VERSION_STRING "2, 8, 0, 0\0"
|
||||
|
||||
/* There are two implementations of cancel cleanup.
|
||||
* Note that pthread.h is included in both application
|
||||
* compilation units and also internally for the library.
|
||||
* The code here and within the library aims to work
|
||||
* for all reasonable combinations of environments.
|
||||
*
|
||||
* The two implementations are:
|
||||
*
|
||||
* C
|
||||
* C++
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Define defaults for cleanup code.
|
||||
* Note: Unless the build explicitly defines one of the following, then
|
||||
* we default to standard C style cleanup. This style uses setjmp/longjmp
|
||||
* in the cancelation and thread exit implementations and therefore won't
|
||||
* do stack unwinding if linked to applications that have it (e.g.
|
||||
* C++ apps). This is currently consistent with most/all commercial Unix
|
||||
* POSIX threads implementations.
|
||||
*/
|
||||
|
||||
#undef PTE_LEVEL
|
||||
|
||||
#if defined(_POSIX_SOURCE)
|
||||
#define PTE_LEVEL 0
|
||||
/* Early POSIX */
|
||||
#endif
|
||||
|
||||
#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309
|
||||
#undef PTE_LEVEL
|
||||
#define PTE_LEVEL 1
|
||||
/* Include 1b, 1c and 1d */
|
||||
#endif
|
||||
|
||||
#if defined(INCLUDE_NP)
|
||||
#undef PTE_LEVEL
|
||||
#define PTE_LEVEL 2
|
||||
/* Include Non-Portable extensions */
|
||||
#endif
|
||||
|
||||
#define PTE_LEVEL_MAX 3
|
||||
|
||||
#if !defined(PTE_LEVEL)
|
||||
#define PTE_LEVEL PTE_LEVEL_MAX
|
||||
/* Include everything */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* -------------------------------------------------------------
|
||||
*
|
||||
*
|
||||
* Module: pthread.h
|
||||
*
|
||||
* Purpose:
|
||||
* Provides an implementation of PThreads based upon the
|
||||
* standard:
|
||||
*
|
||||
* POSIX 1003.1-2001
|
||||
* and
|
||||
* The Single Unix Specification version 3
|
||||
*
|
||||
* (these two are equivalent)
|
||||
*
|
||||
* in order to enhance code portability between Windows,
|
||||
* various commercial Unix implementations, and Linux.
|
||||
*
|
||||
* See the ANNOUNCE file for a full list of conforming
|
||||
* routines and defined constants, and a list of missing
|
||||
* routines and constants not defined in this implementation.
|
||||
*
|
||||
* Authors:
|
||||
* There have been many contributors to this library.
|
||||
* The initial implementation was contributed by
|
||||
* John Bossom, and several others have provided major
|
||||
* sections or revisions of parts of the implementation.
|
||||
* Often significant effort has been contributed to
|
||||
* find and fix important bugs and other problems to
|
||||
* improve the reliability of the library, which sometimes
|
||||
* is not reflected in the amount of code which changed as
|
||||
* result.
|
||||
* As much as possible, the contributors are acknowledged
|
||||
* in the ChangeLog file in the source code distribution
|
||||
* where their changes are noted in detail.
|
||||
*
|
||||
* Contributors are listed in the CONTRIBUTORS file.
|
||||
*
|
||||
* As usual, all bouquets go to the contributors, and all
|
||||
* brickbats go to the project maintainer.
|
||||
*
|
||||
* Maintainer:
|
||||
* The code base for this project is coordinated and
|
||||
* eventually pre-tested, packaged, and made available by
|
||||
*
|
||||
* Ross Johnson <[email protected]>
|
||||
*
|
||||
* QA Testers:
|
||||
* Ultimately, the library is tested in the real world by
|
||||
* a host of competent and demanding scientists and
|
||||
* engineers who report bugs and/or provide solutions
|
||||
* which are then fixed or incorporated into subsequent
|
||||
* versions of the library. Each time a bug is fixed, a
|
||||
* test case is written to prove the fix and ensure
|
||||
* that later changes to the code don't reintroduce the
|
||||
* same error. The number of test cases is slowly growing
|
||||
* and therefore so is the code reliability.
|
||||
*
|
||||
* Compliance:
|
||||
* See the file ANNOUNCE for the list of implemented
|
||||
* and not-implemented routines and defined options.
|
||||
* Of course, these are all defined is this file as well.
|
||||
*
|
||||
* Web site:
|
||||
* The source code and other information about this library
|
||||
* are available from
|
||||
*
|
||||
* http://sources.redhat.com/pthreads-win32/
|
||||
*
|
||||
* -------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <setjmp.h>
|
||||
#include <limits.h>
|
||||
|
||||
/*
|
||||
* Boolean values to make us independent of system includes.
|
||||
*/
|
||||
enum
|
||||
{
|
||||
PTE_FALSE = 0,
|
||||
PTE_TRUE = (! PTE_FALSE)
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* -------------------------------------------------------------
|
||||
*
|
||||
* POSIX 1003.1-2001 Options
|
||||
* =========================
|
||||
*
|
||||
* Options are normally set in <unistd.h>, which is not provided
|
||||
* with pthreads-embedded.
|
||||
*
|
||||
* For conformance with the Single Unix Specification (version 3), all of the
|
||||
* options below are defined, and have a value of either -1 (not supported)
|
||||
* or 200112L (supported).
|
||||
*
|
||||
* These options can neither be left undefined nor have a value of 0, because
|
||||
* either indicates that sysconf(), which is not implemented, may be used at
|
||||
* runtime to check the status of the option.
|
||||
*
|
||||
* _POSIX_THREADS (== 200112L)
|
||||
* If == 200112L, you can use threads
|
||||
*
|
||||
* _POSIX_THREAD_ATTR_STACKSIZE (== 200112L)
|
||||
* If == 200112L, you can control the size of a thread's
|
||||
* stack
|
||||
* pthread_attr_getstacksize
|
||||
* pthread_attr_setstacksize
|
||||
*
|
||||
* _POSIX_THREAD_ATTR_STACKADDR (== -1)
|
||||
* If == 200112L, you can allocate and control a thread's
|
||||
* stack. If not supported, the following functions
|
||||
* will return ENOSYS, indicating they are not
|
||||
* supported:
|
||||
* pthread_attr_getstackaddr
|
||||
* pthread_attr_setstackaddr
|
||||
*
|
||||
* _POSIX_THREAD_PRIORITY_SCHEDULING (== -1)
|
||||
* If == 200112L, you can use realtime scheduling.
|
||||
* This option indicates that the behaviour of some
|
||||
* implemented functions conforms to the additional TPS
|
||||
* requirements in the standard. E.g. rwlocks favour
|
||||
* writers over readers when threads have equal priority.
|
||||
*
|
||||
* _POSIX_THREAD_PRIO_INHERIT (== -1)
|
||||
* If == 200112L, you can create priority inheritance
|
||||
* mutexes.
|
||||
* pthread_mutexattr_getprotocol +
|
||||
* pthread_mutexattr_setprotocol +
|
||||
*
|
||||
* _POSIX_THREAD_PRIO_PROTECT (== -1)
|
||||
* If == 200112L, you can create priority ceiling mutexes
|
||||
* Indicates the availability of:
|
||||
* pthread_mutex_getprioceiling
|
||||
* pthread_mutex_setprioceiling
|
||||
* pthread_mutexattr_getprioceiling
|
||||
* pthread_mutexattr_getprotocol +
|
||||
* pthread_mutexattr_setprioceiling
|
||||
* pthread_mutexattr_setprotocol +
|
||||
*
|
||||
* _POSIX_THREAD_PROCESS_SHARED (== -1)
|
||||
* If set, you can create mutexes and condition
|
||||
* variables that can be shared with another
|
||||
* process.If set, indicates the availability
|
||||
* of:
|
||||
* pthread_mutexattr_getpshared
|
||||
* pthread_mutexattr_setpshared
|
||||
* pthread_condattr_getpshared
|
||||
* pthread_condattr_setpshared
|
||||
*
|
||||
* _POSIX_THREAD_SAFE_FUNCTIONS (== 200112L)
|
||||
* If == 200112L you can use the special *_r library
|
||||
* functions that provide thread-safe behaviour
|
||||
*
|
||||
* _POSIX_READER_WRITER_LOCKS (== 200112L)
|
||||
* If == 200112L, you can use read/write locks
|
||||
*
|
||||
* _POSIX_SPIN_LOCKS (== 200112L)
|
||||
* If == 200112L, you can use spin locks
|
||||
*
|
||||
* _POSIX_BARRIERS (== 200112L)
|
||||
* If == 200112L, you can use barriers
|
||||
*
|
||||
* + These functions provide both 'inherit' and/or
|
||||
* 'protect' protocol, based upon these macro
|
||||
* settings.
|
||||
*
|
||||
* -------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
* POSIX Options
|
||||
*/
|
||||
#undef _POSIX_THREADS
|
||||
#define _POSIX_THREADS 200112L
|
||||
|
||||
#undef _POSIX_READER_WRITER_LOCKS
|
||||
#define _POSIX_READER_WRITER_LOCKS 200112L
|
||||
|
||||
#undef _POSIX_SPIN_LOCKS
|
||||
#define _POSIX_SPIN_LOCKS 200112L
|
||||
|
||||
#undef _POSIX_BARRIERS
|
||||
#define _POSIX_BARRIERS 200112L
|
||||
|
||||
#undef _POSIX_THREAD_SAFE_FUNCTIONS
|
||||
#define _POSIX_THREAD_SAFE_FUNCTIONS 200112L
|
||||
|
||||
#undef _POSIX_THREAD_ATTR_STACKSIZE
|
||||
#define _POSIX_THREAD_ATTR_STACKSIZE 200112L
|
||||
|
||||
/*
|
||||
* The following options are not supported
|
||||
*/
|
||||
#undef _POSIX_THREAD_ATTR_STACKADDR
|
||||
#define _POSIX_THREAD_ATTR_STACKADDR -1
|
||||
|
||||
#undef _POSIX_THREAD_PRIO_INHERIT
|
||||
#define _POSIX_THREAD_PRIO_INHERIT -1
|
||||
|
||||
#undef _POSIX_THREAD_PRIO_PROTECT
|
||||
#define _POSIX_THREAD_PRIO_PROTECT -1
|
||||
|
||||
/* TPS is not fully supported. */
|
||||
#undef _POSIX_THREAD_PRIORITY_SCHEDULING
|
||||
#define _POSIX_THREAD_PRIORITY_SCHEDULING -1
|
||||
|
||||
#undef _POSIX_THREAD_PROCESS_SHARED
|
||||
#define _POSIX_THREAD_PROCESS_SHARED -1
|
||||
|
||||
|
||||
/*
|
||||
* POSIX 1003.1-2001 Limits
|
||||
* ===========================
|
||||
*
|
||||
* These limits are normally set in <limits.h>, which is not provided with
|
||||
* pthreads-embedded.
|
||||
*
|
||||
* PTHREAD_DESTRUCTOR_ITERATIONS
|
||||
* Maximum number of attempts to destroy
|
||||
* a thread's thread-specific data on
|
||||
* termination (must be at least 4)
|
||||
*
|
||||
* PTHREAD_KEYS_MAX
|
||||
* Maximum number of thread-specific data keys
|
||||
* available per process (must be at least 128)
|
||||
*
|
||||
* PTHREAD_STACK_MIN
|
||||
* Minimum supported stack size for a thread
|
||||
*
|
||||
* PTHREAD_THREADS_MAX
|
||||
* Maximum number of threads supported per
|
||||
* process (must be at least 64).
|
||||
*
|
||||
* SEM_NSEMS_MAX
|
||||
* The maximum number of semaphores a process can have.
|
||||
* (must be at least 256)
|
||||
*
|
||||
* SEM_VALUE_MAX
|
||||
* The maximum value a semaphore can have.
|
||||
* (must be at least 32767)
|
||||
*
|
||||
*/
|
||||
#undef _POSIX_THREAD_DESTRUCTOR_ITERATIONS
|
||||
#define _POSIX_THREAD_DESTRUCTOR_ITERATIONS 4
|
||||
|
||||
#undef PTHREAD_DESTRUCTOR_ITERATIONS
|
||||
#define PTHREAD_DESTRUCTOR_ITERATIONS _POSIX_THREAD_DESTRUCTOR_ITERATIONS
|
||||
|
||||
#undef _POSIX_THREAD_KEYS_MAX
|
||||
#define _POSIX_THREAD_KEYS_MAX 128
|
||||
|
||||
#undef PTHREAD_KEYS_MAX
|
||||
#define PTHREAD_KEYS_MAX _POSIX_THREAD_KEYS_MAX
|
||||
|
||||
#undef PTHREAD_STACK_MIN
|
||||
#define PTHREAD_STACK_MIN 0
|
||||
|
||||
#undef _POSIX_THREAD_THREADS_MAX
|
||||
#define _POSIX_THREAD_THREADS_MAX 64
|
||||
|
||||
/* Arbitrary value */
|
||||
#undef PTHREAD_THREADS_MAX
|
||||
#define PTHREAD_THREADS_MAX 2019
|
||||
|
||||
#undef _POSIX_SEM_NSEMS_MAX
|
||||
#define _POSIX_SEM_NSEMS_MAX 256
|
||||
|
||||
/* Arbitrary value */
|
||||
#undef SEM_NSEMS_MAX
|
||||
#define SEM_NSEMS_MAX 1024
|
||||
|
||||
#undef _POSIX_SEM_VALUE_MAX
|
||||
#define _POSIX_SEM_VALUE_MAX 32767
|
||||
|
||||
#undef SEM_VALUE_MAX
|
||||
#define SEM_VALUE_MAX INT_MAX
|
||||
|
||||
|
||||
/*
|
||||
* Generic handle type - intended to extend uniqueness beyond
|
||||
* that available with a simple pointer. It should scale for either
|
||||
* IA-32 or IA-64.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
void * p; /* Pointer to actual object */
|
||||
unsigned int x; /* Extra information - reuse count etc */
|
||||
} pte_handle_t;
|
||||
|
||||
typedef /* pte_handle_t */ void * pthread_t;
|
||||
typedef struct pthread_attr_t_ * pthread_attr_t;
|
||||
typedef struct pthread_once_t_ pthread_once_t;
|
||||
typedef struct pthread_key_t_ * pthread_key_t;
|
||||
typedef struct pthread_mutex_t_ * pthread_mutex_t;
|
||||
typedef struct pthread_mutexattr_t_ * pthread_mutexattr_t;
|
||||
typedef struct pthread_cond_t_ * pthread_cond_t;
|
||||
typedef struct pthread_condattr_t_ * pthread_condattr_t;
|
||||
typedef struct pthread_rwlock_t_ * pthread_rwlock_t;
|
||||
typedef struct pthread_rwlockattr_t_ * pthread_rwlockattr_t;
|
||||
typedef struct pthread_spinlock_t_ * pthread_spinlock_t;
|
||||
typedef struct pthread_barrier_t_ * pthread_barrier_t;
|
||||
typedef struct pthread_barrierattr_t_ * pthread_barrierattr_t;
|
||||
|
||||
/*
|
||||
* ====================
|
||||
* ====================
|
||||
* POSIX Threads
|
||||
* ====================
|
||||
* ====================
|
||||
*/
|
||||
|
||||
enum
|
||||
{
|
||||
/*
|
||||
* pthread_attr_{get,set}detachstate
|
||||
*/
|
||||
PTHREAD_CREATE_JOINABLE = 0, /* Default */
|
||||
PTHREAD_CREATE_DETACHED = 1,
|
||||
|
||||
/*
|
||||
* pthread_attr_{get,set}inheritsched
|
||||
*/
|
||||
PTHREAD_INHERIT_SCHED = 0,
|
||||
PTHREAD_EXPLICIT_SCHED = 1, /* Default */
|
||||
|
||||
/*
|
||||
* pthread_{get,set}scope
|
||||
*/
|
||||
PTHREAD_SCOPE_PROCESS = 0,
|
||||
PTHREAD_SCOPE_SYSTEM = 1, /* Default */
|
||||
PTHREAD_SCOPE_PROCESS_VFPU = 2, /* PSP specific */
|
||||
|
||||
/*
|
||||
* pthread_setcancelstate paramters
|
||||
*/
|
||||
PTHREAD_CANCEL_ENABLE = 0, /* Default */
|
||||
PTHREAD_CANCEL_DISABLE = 1,
|
||||
|
||||
/*
|
||||
* pthread_setcanceltype parameters
|
||||
*/
|
||||
PTHREAD_CANCEL_ASYNCHRONOUS = 0,
|
||||
PTHREAD_CANCEL_DEFERRED = 1, /* Default */
|
||||
|
||||
/*
|
||||
* pthread_mutexattr_{get,set}pshared
|
||||
* pthread_condattr_{get,set}pshared
|
||||
*/
|
||||
PTHREAD_PROCESS_PRIVATE = 0,
|
||||
PTHREAD_PROCESS_SHARED = 1,
|
||||
|
||||
/*
|
||||
* pthread_barrier_wait
|
||||
*/
|
||||
PTHREAD_BARRIER_SERIAL_THREAD = -1
|
||||
};
|
||||
|
||||
/*
|
||||
* ====================
|
||||
* ====================
|
||||
* Cancelation
|
||||
* ====================
|
||||
* ====================
|
||||
*/
|
||||
#define PTHREAD_CANCELED ((void *) -1)
|
||||
|
||||
|
||||
/*
|
||||
* ====================
|
||||
* ====================
|
||||
* Once Key
|
||||
* ====================
|
||||
* ====================
|
||||
*/
|
||||
#define PTHREAD_ONCE_INIT { PTE_FALSE, 0, 0, 0}
|
||||
|
||||
struct pthread_once_t_
|
||||
{
|
||||
int state;
|
||||
void * semaphore;
|
||||
int numSemaphoreUsers;
|
||||
int done; /* indicates if user function has been executed */
|
||||
// void * lock;
|
||||
// int reserved1;
|
||||
// int reserved2;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* ====================
|
||||
* ====================
|
||||
* Object initialisers
|
||||
* ====================
|
||||
* ====================
|
||||
*/
|
||||
#define PTHREAD_MUTEX_INITIALIZER ((pthread_mutex_t) -1)
|
||||
#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER ((pthread_mutex_t) -2)
|
||||
#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER ((pthread_mutex_t) -3)
|
||||
|
||||
/*
|
||||
* Compatibility with LinuxThreads
|
||||
*/
|
||||
#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP PTHREAD_RECURSIVE_MUTEX_INITIALIZER
|
||||
#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP PTHREAD_ERRORCHECK_MUTEX_INITIALIZER
|
||||
|
||||
#define PTHREAD_COND_INITIALIZER ((pthread_cond_t) -1)
|
||||
|
||||
#define PTHREAD_RWLOCK_INITIALIZER ((pthread_rwlock_t) -1)
|
||||
|
||||
#define PTHREAD_SPINLOCK_INITIALIZER ((pthread_spinlock_t) -1)
|
||||
|
||||
/*
|
||||
* Mutex types.
|
||||
*/
|
||||
enum
|
||||
{
|
||||
/* Compatibility with LinuxThreads */
|
||||
PTHREAD_MUTEX_FAST_NP,
|
||||
PTHREAD_MUTEX_RECURSIVE_NP,
|
||||
PTHREAD_MUTEX_ERRORCHECK_NP,
|
||||
PTHREAD_MUTEX_TIMED_NP = PTHREAD_MUTEX_FAST_NP,
|
||||
PTHREAD_MUTEX_ADAPTIVE_NP = PTHREAD_MUTEX_FAST_NP,
|
||||
/* For compatibility with POSIX */
|
||||
PTHREAD_MUTEX_NORMAL = PTHREAD_MUTEX_FAST_NP,
|
||||
PTHREAD_MUTEX_RECURSIVE = PTHREAD_MUTEX_RECURSIVE_NP,
|
||||
PTHREAD_MUTEX_ERRORCHECK = PTHREAD_MUTEX_ERRORCHECK_NP,
|
||||
PTHREAD_MUTEX_DEFAULT = PTHREAD_MUTEX_NORMAL
|
||||
};
|
||||
|
||||
|
||||
typedef struct pte_cleanup_t pte_cleanup_t;
|
||||
|
||||
typedef void (* pte_cleanup_callback_t)(void *);
|
||||
|
||||
struct pte_cleanup_t
|
||||
{
|
||||
pte_cleanup_callback_t routine;
|
||||
void *arg;
|
||||
struct pte_cleanup_t *prev;
|
||||
};
|
||||
|
||||
/*
|
||||
* C implementation of PThreads cancel cleanup
|
||||
*/
|
||||
|
||||
#define pthread_cleanup_push( _rout, _arg ) \
|
||||
{ \
|
||||
pte_cleanup_t _cleanup; \
|
||||
\
|
||||
pte_push_cleanup( &_cleanup, (pte_cleanup_callback_t) (_rout), (_arg) ); \
|
||||
|
||||
#define pthread_cleanup_pop( _execute ) \
|
||||
(void) pte_pop_cleanup( _execute ); \
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/*
|
||||
* ===============
|
||||
* ===============
|
||||
* Methods
|
||||
* ===============
|
||||
* ===============
|
||||
*/
|
||||
|
||||
int pthread_init (void);
|
||||
void pthread_terminate (void);
|
||||
|
||||
/*
|
||||
* PThread Attribute Functions
|
||||
*/
|
||||
int pthread_attr_init (pthread_attr_t * attr);
|
||||
|
||||
int pthread_attr_destroy (pthread_attr_t * attr);
|
||||
|
||||
int pthread_attr_getdetachstate (const pthread_attr_t * attr,
|
||||
int *detachstate);
|
||||
|
||||
int pthread_attr_getstackaddr (const pthread_attr_t * attr,
|
||||
void **stackaddr);
|
||||
|
||||
int pthread_attr_getstacksize (const pthread_attr_t * attr,
|
||||
size_t * stacksize);
|
||||
|
||||
int pthread_attr_setdetachstate (pthread_attr_t * attr,
|
||||
int detachstate);
|
||||
|
||||
int pthread_attr_setstackaddr (pthread_attr_t * attr,
|
||||
void *stackaddr);
|
||||
|
||||
int pthread_attr_setstacksize (pthread_attr_t * attr,
|
||||
size_t stacksize);
|
||||
|
||||
int pthread_attr_getschedparam (const pthread_attr_t *attr,
|
||||
struct sched_param *param);
|
||||
|
||||
int pthread_attr_setschedparam (pthread_attr_t *attr,
|
||||
const struct sched_param *param);
|
||||
|
||||
int pthread_attr_setschedpolicy (pthread_attr_t *,
|
||||
int);
|
||||
|
||||
int pthread_attr_getschedpolicy (pthread_attr_t *,
|
||||
int *);
|
||||
|
||||
int pthread_attr_setinheritsched(pthread_attr_t * attr,
|
||||
int inheritsched);
|
||||
|
||||
int pthread_attr_getinheritsched(pthread_attr_t * attr,
|
||||
int * inheritsched);
|
||||
|
||||
int pthread_attr_setscope (pthread_attr_t *,
|
||||
int);
|
||||
|
||||
int pthread_attr_getscope (const pthread_attr_t *,
|
||||
int *);
|
||||
|
||||
/*
|
||||
* PThread Functions
|
||||
*/
|
||||
int pthread_create (pthread_t * tid,
|
||||
const pthread_attr_t * attr,
|
||||
void *(*start) (void *),
|
||||
void *arg);
|
||||
|
||||
int pthread_detach (pthread_t tid);
|
||||
|
||||
int pthread_equal (pthread_t t1,
|
||||
pthread_t t2);
|
||||
|
||||
void pthread_exit (void *value_ptr);
|
||||
|
||||
int pthread_join (pthread_t thread,
|
||||
void **value_ptr);
|
||||
|
||||
pthread_t pthread_self (void);
|
||||
|
||||
int pthread_cancel (pthread_t thread);
|
||||
|
||||
int pthread_setcancelstate (int state,
|
||||
int *oldstate);
|
||||
|
||||
int pthread_setcanceltype (int type,
|
||||
int *oldtype);
|
||||
|
||||
void pthread_testcancel (void);
|
||||
|
||||
int pthread_once (pthread_once_t * once_control,
|
||||
void (*init_routine) (void));
|
||||
|
||||
#if PTE_LEVEL >= PTE_LEVEL_MAX
|
||||
pte_cleanup_t * pte_pop_cleanup (int execute);
|
||||
|
||||
void pte_push_cleanup (pte_cleanup_t * cleanup,
|
||||
void (*routine) (void *),
|
||||
void *arg);
|
||||
#endif /* PTE_LEVEL >= PTE_LEVEL_MAX */
|
||||
|
||||
/*
|
||||
* Thread Specific Data Functions
|
||||
*/
|
||||
int pthread_key_create (pthread_key_t * key,
|
||||
void (*destructor) (void *));
|
||||
|
||||
int pthread_key_delete (pthread_key_t key);
|
||||
|
||||
int pthread_setspecific (pthread_key_t key,
|
||||
const void *value);
|
||||
|
||||
void * pthread_getspecific (pthread_key_t key);
|
||||
|
||||
|
||||
/*
|
||||
* Mutex Attribute Functions
|
||||
*/
|
||||
int pthread_mutexattr_init (pthread_mutexattr_t * attr);
|
||||
|
||||
int pthread_mutexattr_destroy (pthread_mutexattr_t * attr);
|
||||
|
||||
int pthread_mutexattr_getpshared (const pthread_mutexattr_t
|
||||
* attr,
|
||||
int *pshared);
|
||||
|
||||
int pthread_mutexattr_setpshared (pthread_mutexattr_t * attr,
|
||||
int pshared);
|
||||
|
||||
int pthread_mutexattr_settype (pthread_mutexattr_t * attr, int kind);
|
||||
int pthread_mutexattr_gettype (pthread_mutexattr_t * attr, int *kind);
|
||||
|
||||
/*
|
||||
* Barrier Attribute Functions
|
||||
*/
|
||||
int pthread_barrierattr_init (pthread_barrierattr_t * attr);
|
||||
|
||||
int pthread_barrierattr_destroy (pthread_barrierattr_t * attr);
|
||||
|
||||
int pthread_barrierattr_getpshared (const pthread_barrierattr_t
|
||||
* attr,
|
||||
int *pshared);
|
||||
|
||||
int pthread_barrierattr_setpshared (pthread_barrierattr_t * attr,
|
||||
int pshared);
|
||||
|
||||
/*
|
||||
* Mutex Functions
|
||||
*/
|
||||
int pthread_mutex_init (pthread_mutex_t * mutex,
|
||||
const pthread_mutexattr_t * attr);
|
||||
|
||||
int pthread_mutex_destroy (pthread_mutex_t * mutex);
|
||||
|
||||
int pthread_mutex_lock (pthread_mutex_t * mutex);
|
||||
|
||||
int pthread_mutex_timedlock(pthread_mutex_t *mutex,
|
||||
const struct timespec *abstime);
|
||||
|
||||
int pthread_mutex_trylock (pthread_mutex_t * mutex);
|
||||
|
||||
int pthread_mutex_unlock (pthread_mutex_t * mutex);
|
||||
|
||||
/*
|
||||
* Spinlock Functions
|
||||
*/
|
||||
int pthread_spin_init (pthread_spinlock_t * lock, int pshared);
|
||||
|
||||
int pthread_spin_destroy (pthread_spinlock_t * lock);
|
||||
|
||||
int pthread_spin_lock (pthread_spinlock_t * lock);
|
||||
|
||||
int pthread_spin_trylock (pthread_spinlock_t * lock);
|
||||
|
||||
int pthread_spin_unlock (pthread_spinlock_t * lock);
|
||||
|
||||
/*
|
||||
* Barrier Functions
|
||||
*/
|
||||
int pthread_barrier_init (pthread_barrier_t * barrier,
|
||||
const pthread_barrierattr_t * attr,
|
||||
unsigned int count);
|
||||
|
||||
int pthread_barrier_destroy (pthread_barrier_t * barrier);
|
||||
|
||||
int pthread_barrier_wait (pthread_barrier_t * barrier);
|
||||
|
||||
/*
|
||||
* Condition Variable Attribute Functions
|
||||
*/
|
||||
int pthread_condattr_init (pthread_condattr_t * attr);
|
||||
|
||||
int pthread_condattr_destroy (pthread_condattr_t * attr);
|
||||
|
||||
int pthread_condattr_getpshared (const pthread_condattr_t * attr,
|
||||
int *pshared);
|
||||
|
||||
int pthread_condattr_setpshared (pthread_condattr_t * attr,
|
||||
int pshared);
|
||||
|
||||
/*
|
||||
* Condition Variable Functions
|
||||
*/
|
||||
int pthread_cond_init (pthread_cond_t * cond,
|
||||
const pthread_condattr_t * attr);
|
||||
|
||||
int pthread_cond_destroy (pthread_cond_t * cond);
|
||||
|
||||
int pthread_cond_wait (pthread_cond_t * cond,
|
||||
pthread_mutex_t * mutex);
|
||||
|
||||
int pthread_cond_timedwait (pthread_cond_t * cond,
|
||||
pthread_mutex_t * mutex,
|
||||
const struct timespec *abstime);
|
||||
|
||||
int pthread_cond_signal (pthread_cond_t * cond);
|
||||
|
||||
int pthread_cond_broadcast (pthread_cond_t * cond);
|
||||
|
||||
/*
|
||||
* Scheduling
|
||||
*/
|
||||
int pthread_setschedparam (pthread_t thread,
|
||||
int policy,
|
||||
const struct sched_param *param);
|
||||
|
||||
int pthread_getschedparam (pthread_t thread,
|
||||
int *policy,
|
||||
struct sched_param *param);
|
||||
|
||||
int pthread_setconcurrency (int);
|
||||
|
||||
int pthread_getconcurrency (void);
|
||||
|
||||
/*
|
||||
* Read-Write Lock Functions
|
||||
*/
|
||||
int pthread_rwlock_init(pthread_rwlock_t *lock,
|
||||
const pthread_rwlockattr_t *attr);
|
||||
|
||||
int pthread_rwlock_destroy(pthread_rwlock_t *lock);
|
||||
|
||||
int pthread_rwlock_tryrdlock(pthread_rwlock_t *);
|
||||
|
||||
int pthread_rwlock_trywrlock(pthread_rwlock_t *);
|
||||
|
||||
int pthread_rwlock_rdlock(pthread_rwlock_t *lock);
|
||||
|
||||
int pthread_rwlock_timedrdlock(pthread_rwlock_t *lock,
|
||||
const struct timespec *abstime);
|
||||
|
||||
int pthread_rwlock_wrlock(pthread_rwlock_t *lock);
|
||||
|
||||
int pthread_rwlock_timedwrlock(pthread_rwlock_t *lock,
|
||||
const struct timespec *abstime);
|
||||
|
||||
int pthread_rwlock_unlock(pthread_rwlock_t *lock);
|
||||
|
||||
int pthread_rwlockattr_init (pthread_rwlockattr_t * attr);
|
||||
|
||||
int pthread_rwlockattr_destroy (pthread_rwlockattr_t * attr);
|
||||
|
||||
int pthread_rwlockattr_getpshared (const pthread_rwlockattr_t * attr,
|
||||
int *pshared);
|
||||
|
||||
int pthread_rwlockattr_setpshared (pthread_rwlockattr_t * attr,
|
||||
int pshared);
|
||||
|
||||
#if PTE_LEVEL >= PTE_LEVEL_MAX - 1
|
||||
|
||||
/*
|
||||
* Signal Functions. Should be defined in <signal.h> but we might
|
||||
* already have signal.h that don't define these.
|
||||
*/
|
||||
int pthread_kill(pthread_t thread, int sig);
|
||||
|
||||
/*
|
||||
* Non-portable functions
|
||||
*/
|
||||
|
||||
/*
|
||||
* Compatibility with Linux.
|
||||
*/
|
||||
int pthread_mutexattr_setkind_np(pthread_mutexattr_t * attr,
|
||||
int kind);
|
||||
int pthread_mutexattr_getkind_np(pthread_mutexattr_t * attr,
|
||||
int *kind);
|
||||
|
||||
/*
|
||||
* Possibly supported by other POSIX threads implementations
|
||||
*/
|
||||
int pthread_delay_np (struct timespec * interval);
|
||||
int pthread_num_processors_np(void);
|
||||
|
||||
/*
|
||||
* Register a system time change with the library.
|
||||
* Causes the library to perform various functions
|
||||
* in response to the change. Should be called whenever
|
||||
* the application's top level window receives a
|
||||
* WM_TIMECHANGE message. It can be passed directly to
|
||||
* pthread_create() as a new thread if desired.
|
||||
*/
|
||||
void * pthread_timechange_handler_np(void *);
|
||||
|
||||
#endif /*PTE_LEVEL >= PTE_LEVEL_MAX - 1 */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* cplusplus */
|
||||
|
||||
#if PTE_LEVEL >= PTE_LEVEL_MAX
|
||||
|
||||
#endif /* PTE_LEVEL >= PTE_LEVEL_MAX */
|
||||
|
||||
/*
|
||||
* Some compiler environments don't define some things.
|
||||
*/
|
||||
# define _ftime ftime
|
||||
# define _timeb timeb
|
||||
|
||||
#undef PTE_LEVEL
|
||||
#undef PTE_LEVEL_MAX
|
||||
|
||||
#endif /* PTHREAD_H */
|
||||
Vendored
-581
@@ -1,581 +0,0 @@
|
||||
/*
|
||||
* pthread_attr.c
|
||||
*
|
||||
* Description:
|
||||
* This translation unit implements operations on thread attribute objects.
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "pthread.h"
|
||||
#include "implement.h"
|
||||
#include "sched.h"
|
||||
|
||||
int
|
||||
pthread_attr_destroy (pthread_attr_t * attr)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Destroys a thread attributes object.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_attr_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Destroys a thread attributes object.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) Does not affect threads created with 'attr'.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully destroyed attr,
|
||||
* EINVAL 'attr' is invalid.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
if (pte_is_attr (attr) != 0)
|
||||
return EINVAL;
|
||||
|
||||
/*
|
||||
* Set the attribute object to a specific invalid value.
|
||||
*/
|
||||
(*attr)->valid = 0;
|
||||
free (*attr);
|
||||
*attr = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_attr_getdetachstate (const pthread_attr_t * attr, int *detachstate)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function determines whether threads created with
|
||||
* 'attr' will run detached.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_attr_t
|
||||
*
|
||||
* detachstate
|
||||
* pointer to an integer into which is returned one
|
||||
* of:
|
||||
*
|
||||
* PTHREAD_CREATE_JOINABLE
|
||||
* Thread ID is valid, must be joined
|
||||
*
|
||||
* PTHREAD_CREATE_DETACHED
|
||||
* Thread ID is invalid, cannot be joined,
|
||||
* canceled, or modified
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function determines whether threads created with
|
||||
* 'attr' will run detached.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) You cannot join or cancel detached threads.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully retrieved detach state,
|
||||
* EINVAL 'attr' is invalid
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
if (pte_is_attr (attr) != 0 || detachstate == NULL)
|
||||
{
|
||||
*detachstate = PTHREAD_CREATE_DETACHED;
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
*detachstate = (*attr)->detachstate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_attr_getinheritsched (pthread_attr_t * attr, int *inheritsched)
|
||||
{
|
||||
if (pte_is_attr (attr) != 0 || inheritsched == NULL)
|
||||
return EINVAL;
|
||||
|
||||
*inheritsched = (*attr)->inheritsched;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_attr_getschedparam (const pthread_attr_t * attr,
|
||||
struct sched_param *param)
|
||||
{
|
||||
if (pte_is_attr (attr) != 0 || param == NULL)
|
||||
return EINVAL;
|
||||
|
||||
memcpy (param, &(*attr)->param, sizeof (*param));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_attr_getschedpolicy (pthread_attr_t * attr, int *policy)
|
||||
{
|
||||
if (pte_is_attr (attr) != 0 || policy == NULL)
|
||||
return EINVAL;
|
||||
|
||||
/*
|
||||
* Validate the policy arg.
|
||||
* Check that a policy constant wasn't passed rather than &policy.
|
||||
*/
|
||||
if (policy <= (int *) SCHED_MAX)
|
||||
return EINVAL;
|
||||
|
||||
*policy = SCHED_OTHER;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_attr_getscope (const pthread_attr_t * attr, int *contentionscope)
|
||||
{
|
||||
#ifdef _POSIX_THREAD_PRIORITY_SCHEDULING
|
||||
*contentionscope = (*attr)->contentionscope;
|
||||
return 0;
|
||||
#else
|
||||
return ENOSYS;
|
||||
#endif
|
||||
}
|
||||
|
||||
int pthread_attr_getstackaddr (const pthread_attr_t * attr, void **stackaddr)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function determines the address of the stack
|
||||
* on which threads created with 'attr' will run.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_attr_t
|
||||
*
|
||||
* stackaddr
|
||||
* pointer into which is returned the stack address.
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function determines the address of the stack
|
||||
* on which threads created with 'attr' will run.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) Function supported only if this macro is
|
||||
* defined:
|
||||
*
|
||||
* _POSIX_THREAD_ATTR_STACKADDR
|
||||
*
|
||||
* 2) Create only one thread for each stack
|
||||
* address..
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully retreived stack address,
|
||||
* EINVAL 'attr' is invalid
|
||||
* ENOSYS function not supported
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
#if defined( _POSIX_THREAD_ATTR_STACKADDR )
|
||||
|
||||
if (pte_is_attr (attr) != 0)
|
||||
return EINVAL;
|
||||
|
||||
*stackaddr = (*attr)->stackaddr;
|
||||
return 0;
|
||||
|
||||
#else
|
||||
|
||||
return ENOSYS;
|
||||
|
||||
#endif /* _POSIX_THREAD_ATTR_STACKADDR */
|
||||
}
|
||||
|
||||
int
|
||||
pthread_attr_getstacksize (const pthread_attr_t * attr, size_t * stacksize)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function determines the size of the stack on
|
||||
* which threads created with 'attr' will run.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_attr_t
|
||||
*
|
||||
* stacksize
|
||||
* pointer to size_t into which is returned the
|
||||
* stack size, in bytes.
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function determines the size of the stack on
|
||||
* which threads created with 'attr' will run.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) Function supported only if this macro is
|
||||
* defined:
|
||||
*
|
||||
* _POSIX_THREAD_ATTR_STACKSIZE
|
||||
*
|
||||
* 2) Use on newly created attributes object to
|
||||
* find the default stack size.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully retrieved stack size,
|
||||
* EINVAL 'attr' is invalid
|
||||
* ENOSYS function not supported
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
#ifdef _POSIX_THREAD_ATTR_STACKSIZE
|
||||
|
||||
if (pte_is_attr (attr) != 0)
|
||||
return EINVAL;
|
||||
|
||||
/* Everything is okay. */
|
||||
*stacksize = (*attr)->stacksize;
|
||||
return 0;
|
||||
|
||||
#else
|
||||
|
||||
return ENOSYS;
|
||||
|
||||
#endif /* _POSIX_THREAD_ATTR_STACKSIZE */
|
||||
}
|
||||
|
||||
int pthread_attr_init (pthread_attr_t * attr)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Initializes a thread attributes object with default
|
||||
* attributes.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_attr_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Initializes a thread attributes object with default
|
||||
* attributes.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) Used to define thread attributes
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully initialized attr,
|
||||
* ENOMEM insufficient memory for attr.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
pthread_attr_t attr_result;
|
||||
|
||||
/* This is disallowed. */
|
||||
if (attr == NULL)
|
||||
return EINVAL;
|
||||
|
||||
attr_result = (pthread_attr_t) malloc (sizeof (*attr_result));
|
||||
|
||||
if (attr_result == NULL)
|
||||
return ENOMEM;
|
||||
|
||||
#ifdef _POSIX_THREAD_ATTR_STACKSIZE
|
||||
/*
|
||||
* Default to zero size?
|
||||
*/
|
||||
attr_result->stacksize = 0;
|
||||
#endif
|
||||
|
||||
#ifdef _POSIX_THREAD_ATTR_STACKADDR
|
||||
/* FIXME: Set this to something sensible when we support it. */
|
||||
attr_result->stackaddr = NULL;
|
||||
#endif
|
||||
|
||||
attr_result->detachstate = PTHREAD_CREATE_JOINABLE;
|
||||
|
||||
/*
|
||||
* Win32 sets new threads to THREAD_PRIORITY_NORMAL and
|
||||
* not to that of the parent thread. We choose to default to
|
||||
* this arrangement.
|
||||
*/
|
||||
attr_result->param.sched_priority = pte_osThreadGetDefaultPriority();
|
||||
attr_result->inheritsched = PTHREAD_EXPLICIT_SCHED;
|
||||
attr_result->contentionscope = PTHREAD_SCOPE_SYSTEM;
|
||||
|
||||
attr_result->valid = PTE_ATTR_VALID;
|
||||
|
||||
*attr = attr_result;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_attr_setdetachstate (pthread_attr_t * attr, int detachstate)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function specifies whether threads created with
|
||||
* 'attr' will run detached.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_attr_t
|
||||
*
|
||||
* detachstate
|
||||
* an integer containing one of:
|
||||
*
|
||||
* PTHREAD_CREATE_JOINABLE
|
||||
* Thread ID is valid, must be joined
|
||||
*
|
||||
* PTHREAD_CREATE_DETACHED
|
||||
* Thread ID is invalid, cannot be joined,
|
||||
* canceled, or modified
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function specifies whether threads created with
|
||||
* 'attr' will run detached.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) You cannot join or cancel detached threads.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully set detach state,
|
||||
* EINVAL 'attr' or 'detachstate' is invalid
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
if (pte_is_attr (attr) != 0)
|
||||
return EINVAL;
|
||||
|
||||
if (detachstate != PTHREAD_CREATE_JOINABLE &&
|
||||
detachstate != PTHREAD_CREATE_DETACHED)
|
||||
return EINVAL;
|
||||
|
||||
(*attr)->detachstate = detachstate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_attr_setinheritsched (pthread_attr_t * attr, int inheritsched)
|
||||
{
|
||||
if (pte_is_attr (attr) != 0)
|
||||
return EINVAL;
|
||||
|
||||
if (PTHREAD_INHERIT_SCHED != inheritsched
|
||||
&& PTHREAD_EXPLICIT_SCHED != inheritsched)
|
||||
return EINVAL;
|
||||
|
||||
(*attr)->inheritsched = inheritsched;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_attr_setschedparam (pthread_attr_t * attr,
|
||||
const struct sched_param *param)
|
||||
{
|
||||
int priority;
|
||||
|
||||
if (pte_is_attr (attr) != 0 || param == NULL)
|
||||
return EINVAL;
|
||||
|
||||
priority = param->sched_priority;
|
||||
|
||||
/* Validate priority level. */
|
||||
if (priority < sched_get_priority_min (SCHED_OTHER) ||
|
||||
priority > sched_get_priority_max (SCHED_OTHER))
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
memcpy (&(*attr)->param, param, sizeof (*param));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_attr_setschedpolicy (pthread_attr_t * attr, int policy)
|
||||
{
|
||||
if (pte_is_attr (attr) != 0)
|
||||
return EINVAL;
|
||||
|
||||
if (policy != SCHED_OTHER)
|
||||
return ENOTSUP;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_attr_setscope (pthread_attr_t * attr, int contentionscope)
|
||||
{
|
||||
#ifdef _POSIX_THREAD_PRIORITY_SCHEDULING
|
||||
switch (contentionscope)
|
||||
{
|
||||
case PTHREAD_SCOPE_SYSTEM:
|
||||
(*attr)->contentionscope = contentionscope;
|
||||
return 0;
|
||||
case PTHREAD_SCOPE_PROCESS:
|
||||
return ENOTSUP;
|
||||
default:
|
||||
return EINVAL;
|
||||
}
|
||||
#else
|
||||
return ENOSYS;
|
||||
#endif
|
||||
}
|
||||
|
||||
int pthread_attr_setstackaddr (pthread_attr_t * attr, void *stackaddr)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Threads created with 'attr' will run on the stack
|
||||
* starting at 'stackaddr'.
|
||||
* Stack must be at least PTHREAD_STACK_MIN bytes.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_attr_t
|
||||
*
|
||||
* stacksize
|
||||
* stack size, in bytes.
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Threads created with 'attr' will run on the stack
|
||||
* starting at 'stackaddr'.
|
||||
* Stack must be at least PTHREAD_STACK_MIN bytes.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) Function supported only if this macro is
|
||||
* defined:
|
||||
*
|
||||
* _POSIX_THREAD_ATTR_STACKADDR
|
||||
*
|
||||
* 2) Create only one thread for each stack
|
||||
* address..
|
||||
*
|
||||
* 3) Ensure that stackaddr is aligned.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully set stack address,
|
||||
* EINVAL 'attr' is invalid
|
||||
* ENOSYS function not supported
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
#if defined( _POSIX_THREAD_ATTR_STACKADDR )
|
||||
|
||||
if (pte_is_attr (attr) != 0)
|
||||
return EINVAL;
|
||||
|
||||
(*attr)->stackaddr = stackaddr;
|
||||
return 0;
|
||||
|
||||
#else
|
||||
|
||||
return ENOSYS;
|
||||
|
||||
#endif /* _POSIX_THREAD_ATTR_STACKADDR */
|
||||
}
|
||||
|
||||
int pthread_attr_setstacksize (pthread_attr_t * attr, size_t stacksize)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function specifies the size of the stack on
|
||||
* which threads created with 'attr' will run.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_attr_t
|
||||
*
|
||||
* stacksize
|
||||
* stack size, in bytes.
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function specifies the size of the stack on
|
||||
* which threads created with 'attr' will run.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) Function supported only if this macro is
|
||||
* defined:
|
||||
*
|
||||
* _POSIX_THREAD_ATTR_STACKSIZE
|
||||
*
|
||||
* 2) Find the default first (using
|
||||
* pthread_attr_getstacksize), then increase
|
||||
* by multiplying.
|
||||
*
|
||||
* 3) Only use if thread needs more than the
|
||||
* default.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully set stack size,
|
||||
* EINVAL 'attr' is invalid or stacksize too
|
||||
* small or too big.
|
||||
* ENOSYS function not supported
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
#ifdef _POSIX_THREAD_ATTR_STACKSIZE
|
||||
|
||||
#if PTHREAD_STACK_MIN > 0
|
||||
|
||||
/* Verify that the stack size is within range. */
|
||||
if (stacksize < PTHREAD_STACK_MIN)
|
||||
return EINVAL;
|
||||
|
||||
#endif
|
||||
|
||||
if (pte_is_attr (attr) != 0)
|
||||
return EINVAL;
|
||||
|
||||
/* Everything is okay. */
|
||||
(*attr)->stacksize = stacksize;
|
||||
return 0;
|
||||
|
||||
#else
|
||||
|
||||
return ENOSYS;
|
||||
|
||||
#endif /* _POSIX_THREAD_ATTR_STACKSIZE */
|
||||
}
|
||||
Vendored
-163
@@ -1,163 +0,0 @@
|
||||
/*
|
||||
* pthread_barrier.c
|
||||
*
|
||||
* Description:
|
||||
* This translation unit implements barrier primitives.
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "pthread.h"
|
||||
#include "implement.h"
|
||||
|
||||
int pthread_barrier_destroy (pthread_barrier_t * barrier)
|
||||
{
|
||||
int result = 0;
|
||||
pthread_barrier_t b;
|
||||
|
||||
if (barrier == NULL || *barrier == (pthread_barrier_t) PTE_OBJECT_INVALID)
|
||||
return EINVAL;
|
||||
|
||||
b = *barrier;
|
||||
*barrier = NULL;
|
||||
|
||||
if (0 == (result = sem_destroy (&(b->semBarrierBreeched[0]))))
|
||||
{
|
||||
if (0 == (result = sem_destroy (&(b->semBarrierBreeched[1]))))
|
||||
{
|
||||
(void) free (b);
|
||||
return 0;
|
||||
}
|
||||
(void) sem_init (&(b->semBarrierBreeched[0]), b->pshared, 0);
|
||||
}
|
||||
|
||||
*barrier = b;
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_barrier_init (pthread_barrier_t * barrier,
|
||||
const pthread_barrierattr_t * attr, unsigned int count)
|
||||
{
|
||||
pthread_barrier_t b;
|
||||
|
||||
if (barrier == NULL || count == 0)
|
||||
return EINVAL;
|
||||
|
||||
if (NULL != (b = (pthread_barrier_t) calloc (1, sizeof (*b))))
|
||||
{
|
||||
b->pshared = (attr != NULL && *attr != NULL
|
||||
? (*attr)->pshared : PTHREAD_PROCESS_PRIVATE);
|
||||
|
||||
b->nCurrentBarrierHeight = b->nInitialBarrierHeight = count;
|
||||
b->iStep = 0;
|
||||
|
||||
/*
|
||||
* Two semaphores are used in the same way as two stepping
|
||||
* stones might be used in crossing a stream. Once all
|
||||
* threads are safely on one stone, the other stone can
|
||||
* be moved ahead, and the threads can start moving to it.
|
||||
* If some threads decide to eat their lunch before moving
|
||||
* then the other threads have to wait.
|
||||
*/
|
||||
if (0 == sem_init (&(b->semBarrierBreeched[0]), b->pshared, 0))
|
||||
{
|
||||
if (0 == sem_init (&(b->semBarrierBreeched[1]), b->pshared, 0))
|
||||
{
|
||||
*barrier = b;
|
||||
return 0;
|
||||
}
|
||||
(void) sem_destroy (&(b->semBarrierBreeched[0]));
|
||||
}
|
||||
(void) free (b);
|
||||
}
|
||||
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
int pthread_barrier_wait (pthread_barrier_t * barrier)
|
||||
{
|
||||
int result;
|
||||
int step;
|
||||
pthread_barrier_t b;
|
||||
|
||||
if (barrier == NULL || *barrier == (pthread_barrier_t) PTE_OBJECT_INVALID)
|
||||
return EINVAL;
|
||||
|
||||
b = *barrier;
|
||||
step = b->iStep;
|
||||
|
||||
if (0 == PTE_ATOMIC_DECREMENT ((int *) &(b->nCurrentBarrierHeight)))
|
||||
{
|
||||
/* Must be done before posting the semaphore. */
|
||||
b->nCurrentBarrierHeight = b->nInitialBarrierHeight;
|
||||
|
||||
/*
|
||||
* There is no race condition between the semaphore wait and post
|
||||
* because we are using two alternating semas and all threads have
|
||||
* entered barrier_wait and checked nCurrentBarrierHeight before this
|
||||
* barrier's sema can be posted. Any threads that have not quite
|
||||
* entered sem_wait below when the multiple_post has completed
|
||||
* will nevertheless continue through the semaphore (barrier)
|
||||
* and will not be left stranded.
|
||||
*/
|
||||
result = (b->nInitialBarrierHeight > 1
|
||||
? sem_post_multiple (&(b->semBarrierBreeched[step]),
|
||||
b->nInitialBarrierHeight - 1) : 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Use the non-cancelable version of sem_wait().
|
||||
*/
|
||||
result = sem_wait (&(b->semBarrierBreeched[step]));
|
||||
// result = sem_wait_nocancel (&(b->semBarrierBreeched[step]));
|
||||
}
|
||||
|
||||
/*
|
||||
* The first thread across will be the PTHREAD_BARRIER_SERIAL_THREAD.
|
||||
* This also sets up the alternate semaphore as the next barrier.
|
||||
*/
|
||||
if (0 == result)
|
||||
{
|
||||
result = (step ==
|
||||
PTE_ATOMIC_COMPARE_EXCHANGE (& (b->iStep),(1L - step),step) ?
|
||||
PTHREAD_BARRIER_SERIAL_THREAD : 0);
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
Vendored
-257
@@ -1,257 +0,0 @@
|
||||
/*
|
||||
* pthread_barrier_attr.c
|
||||
*
|
||||
* Description:
|
||||
* This translation unit implements barrier primitives.
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "pthread.h"
|
||||
#include "implement.h"
|
||||
|
||||
|
||||
int
|
||||
pthread_barrierattr_destroy (pthread_barrierattr_t * attr)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Destroys a barrier attributes object. The object can
|
||||
* no longer be used.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_barrierattr_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Destroys a barrier attributes object. The object can
|
||||
* no longer be used.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) Does not affect barrieres created using 'attr'
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully released attr,
|
||||
* EINVAL 'attr' is invalid.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if (attr == NULL || *attr == NULL)
|
||||
result = EINVAL;
|
||||
else
|
||||
{
|
||||
pthread_barrierattr_t ba = *attr;
|
||||
|
||||
*attr = NULL;
|
||||
free (ba);
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int
|
||||
pthread_barrierattr_getpshared (const pthread_barrierattr_t * attr,
|
||||
int *pshared)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Determine whether barriers created with 'attr' can be
|
||||
* shared between processes.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_barrierattr_t
|
||||
*
|
||||
* pshared
|
||||
* will be set to one of:
|
||||
*
|
||||
* PTHREAD_PROCESS_SHARED
|
||||
* May be shared if in shared memory
|
||||
*
|
||||
* PTHREAD_PROCESS_PRIVATE
|
||||
* Cannot be shared.
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Mutexes creatd with 'attr' can be shared between
|
||||
* processes if pthread_barrier_t variable is allocated
|
||||
* in memory shared by these processes.
|
||||
* NOTES:
|
||||
* 1) pshared barriers MUST be allocated in shared
|
||||
* memory.
|
||||
* 2) The following macro is defined if shared barriers
|
||||
* are supported:
|
||||
* _POSIX_THREAD_PROCESS_SHARED
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully retrieved attribute,
|
||||
* EINVAL 'attr' is invalid,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result;
|
||||
|
||||
if ((attr != NULL && *attr != NULL) && (pshared != NULL))
|
||||
{
|
||||
*pshared = (*attr)->pshared;
|
||||
result = 0;
|
||||
}
|
||||
else
|
||||
result = EINVAL;
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_barrierattr_init (pthread_barrierattr_t * attr)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Initializes a barrier attributes object with default
|
||||
* attributes.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_barrierattr_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Initializes a barrier attributes object with default
|
||||
* attributes.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) Used to define barrier types
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully initialized attr,
|
||||
* ENOMEM insufficient memory for attr.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
pthread_barrierattr_t ba = (pthread_barrierattr_t) calloc (1, sizeof (*ba));
|
||||
|
||||
if (ba == NULL)
|
||||
result = ENOMEM;
|
||||
else
|
||||
ba->pshared = PTHREAD_PROCESS_PRIVATE;
|
||||
|
||||
*attr = ba;
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_barrierattr_setpshared (pthread_barrierattr_t * attr, int pshared)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Barriers created with 'attr' can be shared between
|
||||
* processes if pthread_barrier_t variable is allocated
|
||||
* in memory shared by these processes.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_barrierattr_t
|
||||
*
|
||||
* pshared
|
||||
* must be one of:
|
||||
*
|
||||
* PTHREAD_PROCESS_SHARED
|
||||
* May be shared if in shared memory
|
||||
*
|
||||
* PTHREAD_PROCESS_PRIVATE
|
||||
* Cannot be shared.
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Mutexes creatd with 'attr' can be shared between
|
||||
* processes if pthread_barrier_t variable is allocated
|
||||
* in memory shared by these processes.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) pshared barriers MUST be allocated in shared
|
||||
* memory.
|
||||
*
|
||||
* 2) The following macro is defined if shared barriers
|
||||
* are supported:
|
||||
* _POSIX_THREAD_PROCESS_SHARED
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully set attribute,
|
||||
* EINVAL 'attr' or pshared is invalid,
|
||||
* ENOSYS PTHREAD_PROCESS_SHARED not supported,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result;
|
||||
|
||||
if ((attr != NULL && *attr != NULL) &&
|
||||
((pshared == PTHREAD_PROCESS_SHARED) ||
|
||||
(pshared == PTHREAD_PROCESS_PRIVATE)))
|
||||
{
|
||||
if (pshared == PTHREAD_PROCESS_SHARED)
|
||||
{
|
||||
|
||||
#if !defined( _POSIX_THREAD_PROCESS_SHARED )
|
||||
|
||||
result = ENOSYS;
|
||||
pshared = PTHREAD_PROCESS_PRIVATE;
|
||||
|
||||
#else
|
||||
|
||||
result = 0;
|
||||
|
||||
#endif /* _POSIX_THREAD_PROCESS_SHARED */
|
||||
|
||||
}
|
||||
else
|
||||
result = 0;
|
||||
|
||||
(*attr)->pshared = pshared;
|
||||
}
|
||||
else
|
||||
result = EINVAL;
|
||||
|
||||
return (result);
|
||||
|
||||
} /* pthread_barrierattr_setpshared */
|
||||
Vendored
-807
@@ -1,807 +0,0 @@
|
||||
/*
|
||||
* pthread_cond.c
|
||||
*
|
||||
* Description:
|
||||
* This translation unit implements condition variables and their primitives.
|
||||
*
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "pthread.h"
|
||||
#include "implement.h"
|
||||
|
||||
/*
|
||||
* Arguments for cond_wait_cleanup, since we can only pass a
|
||||
* single void * to it.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
pthread_mutex_t *mutexPtr;
|
||||
pthread_cond_t cv;
|
||||
int *resultPtr;
|
||||
} pte_cond_wait_cleanup_args_t;
|
||||
|
||||
static void pte_cond_wait_cleanup (void *args)
|
||||
{
|
||||
pte_cond_wait_cleanup_args_t *cleanup_args =
|
||||
(pte_cond_wait_cleanup_args_t *) args;
|
||||
pthread_cond_t cv = cleanup_args->cv;
|
||||
int *resultPtr = cleanup_args->resultPtr;
|
||||
int nSignalsWasLeft;
|
||||
int result;
|
||||
|
||||
/*
|
||||
* Whether we got here as a result of signal/broadcast or because of
|
||||
* timeout on wait or thread cancellation we indicate that we are no
|
||||
* longer waiting. The waiter is responsible for adjusting waiters
|
||||
* (to)unblock(ed) counts (protected by unblock lock).
|
||||
*/
|
||||
if ((result = pthread_mutex_lock (&(cv->mtxUnblockLock))) != 0)
|
||||
{
|
||||
*resultPtr = result;
|
||||
return;
|
||||
}
|
||||
|
||||
if (0 != (nSignalsWasLeft = cv->nWaitersToUnblock))
|
||||
{
|
||||
--(cv->nWaitersToUnblock);
|
||||
}
|
||||
else if (INT_MAX / 2 == ++(cv->nWaitersGone))
|
||||
{
|
||||
/* Use the non-cancellable version of sem_wait() */
|
||||
// if (sem_wait_nocancel (&(cv->semBlockLock)) != 0)
|
||||
if (sem_wait (&(cv->semBlockLock)) != 0)
|
||||
{
|
||||
*resultPtr = errno;
|
||||
/*
|
||||
* This is a fatal error for this CV,
|
||||
* so we deliberately don't unlock
|
||||
* cv->mtxUnblockLock before returning.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
cv->nWaitersBlocked -= cv->nWaitersGone;
|
||||
if (sem_post (&(cv->semBlockLock)) != 0)
|
||||
{
|
||||
*resultPtr = errno;
|
||||
/*
|
||||
* This is a fatal error for this CV,
|
||||
* so we deliberately don't unlock
|
||||
* cv->mtxUnblockLock before returning.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
cv->nWaitersGone = 0;
|
||||
}
|
||||
|
||||
if ((result = pthread_mutex_unlock (&(cv->mtxUnblockLock))) != 0)
|
||||
{
|
||||
*resultPtr = result;
|
||||
return;
|
||||
}
|
||||
|
||||
if (1 == nSignalsWasLeft)
|
||||
{
|
||||
if (sem_post (&(cv->semBlockLock)) != 0)
|
||||
{
|
||||
*resultPtr = errno;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* XSH: Upon successful return, the mutex has been locked and is owned
|
||||
* by the calling thread.
|
||||
*/
|
||||
if ((result = pthread_mutex_lock (cleanup_args->mutexPtr)) != 0)
|
||||
*resultPtr = result;
|
||||
}
|
||||
|
||||
static int pte_cond_timedwait (pthread_cond_t * cond,
|
||||
pthread_mutex_t * mutex, const struct timespec *abstime)
|
||||
{
|
||||
int result = 0;
|
||||
pthread_cond_t cv;
|
||||
pte_cond_wait_cleanup_args_t cleanup_args;
|
||||
|
||||
if (cond == NULL || *cond == NULL)
|
||||
return EINVAL;
|
||||
|
||||
/*
|
||||
* We do a quick check to see if we need to do more work
|
||||
* to initialise a static condition variable. We check
|
||||
* again inside the guarded section of pte_cond_check_need_init()
|
||||
* to avoid race conditions.
|
||||
*/
|
||||
if (*cond == PTHREAD_COND_INITIALIZER)
|
||||
result = pte_cond_check_need_init (cond);
|
||||
|
||||
if (result != 0 && result != EBUSY)
|
||||
return result;
|
||||
|
||||
cv = *cond;
|
||||
|
||||
/* Thread can be cancelled in sem_wait() but this is OK */
|
||||
if (sem_wait (&(cv->semBlockLock)) != 0)
|
||||
return errno;
|
||||
|
||||
++(cv->nWaitersBlocked);
|
||||
|
||||
if (sem_post (&(cv->semBlockLock)) != 0)
|
||||
return errno;
|
||||
|
||||
/*
|
||||
* Setup this waiter cleanup handler
|
||||
*/
|
||||
cleanup_args.mutexPtr = mutex;
|
||||
cleanup_args.cv = cv;
|
||||
cleanup_args.resultPtr = &result;
|
||||
|
||||
pthread_cleanup_push (pte_cond_wait_cleanup, (void *) &cleanup_args);
|
||||
|
||||
/*
|
||||
* Now we can release 'mutex' and...
|
||||
*/
|
||||
if ((result = pthread_mutex_unlock (mutex)) == 0)
|
||||
{
|
||||
/*
|
||||
* ...wait to be awakened by
|
||||
* pthread_cond_signal, or
|
||||
* pthread_cond_broadcast, or
|
||||
* timeout, or
|
||||
* thread cancellation
|
||||
*
|
||||
* Note:
|
||||
*
|
||||
* sem_timedwait is a cancellation point,
|
||||
* hence providing the mechanism for making
|
||||
* pthread_cond_wait a cancellation point.
|
||||
* We use the cleanup mechanism to ensure we
|
||||
* re-lock the mutex and adjust (to)unblock(ed) waiters
|
||||
* counts if we are cancelled, timed out or signalled.
|
||||
*/
|
||||
if (sem_timedwait (&(cv->semBlockQueue), abstime) != 0)
|
||||
result = errno;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Always cleanup
|
||||
*/
|
||||
pthread_cleanup_pop (1);
|
||||
|
||||
/*
|
||||
* "result" can be modified by the cleanup handler.
|
||||
*/
|
||||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
pthread_cond_destroy (pthread_cond_t * cond)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function destroys a condition variable
|
||||
*
|
||||
*
|
||||
* PARAMETERS
|
||||
* cond
|
||||
* pointer to an instance of pthread_cond_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function destroys a condition variable.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) A condition variable can be destroyed
|
||||
* immediately after all the threads that
|
||||
* are blocked on it are awakened. e.g.
|
||||
*
|
||||
* struct list {
|
||||
* pthread_mutex_t lm;
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* struct elt {
|
||||
* key k;
|
||||
* int busy;
|
||||
* pthread_cond_t notbusy;
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
*
|
||||
* struct elt *
|
||||
* list_find(struct list *lp, key k)
|
||||
* {
|
||||
* struct elt *ep;
|
||||
*
|
||||
* pthread_mutex_lock(&lp->lm);
|
||||
* while ((ep = find_elt(l,k) != NULL) && ep->busy)
|
||||
* pthread_cond_wait(&ep->notbusy, &lp->lm);
|
||||
* if (ep != NULL)
|
||||
* ep->busy = 1;
|
||||
* pthread_mutex_unlock(&lp->lm);
|
||||
* return(ep);
|
||||
* }
|
||||
*
|
||||
* delete_elt(struct list *lp, struct elt *ep)
|
||||
* {
|
||||
* pthread_mutex_lock(&lp->lm);
|
||||
* assert(ep->busy);
|
||||
* ... remove ep from list ...
|
||||
* ep->busy = 0;
|
||||
* (A) pthread_cond_broadcast(&ep->notbusy);
|
||||
* pthread_mutex_unlock(&lp->lm);
|
||||
* (B) pthread_cond_destroy(&rp->notbusy);
|
||||
* free(ep);
|
||||
* }
|
||||
*
|
||||
* In this example, the condition variable
|
||||
* and its list element may be freed (line B)
|
||||
* immediately after all threads waiting for
|
||||
* it are awakened (line A), since the mutex
|
||||
* and the code ensure that no other thread
|
||||
* can touch the element to be deleted.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully released condition variable,
|
||||
* EINVAL 'cond' is invalid,
|
||||
* EBUSY 'cond' is in use,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
pthread_cond_t cv;
|
||||
int result = 0, result1 = 0, result2 = 0;
|
||||
|
||||
/*
|
||||
* Assuming any race condition here is harmless.
|
||||
*/
|
||||
if (cond == NULL || *cond == NULL)
|
||||
return EINVAL;
|
||||
|
||||
if (*cond != PTHREAD_COND_INITIALIZER)
|
||||
{
|
||||
|
||||
pte_osMutexLock (pte_cond_list_lock);
|
||||
|
||||
cv = *cond;
|
||||
|
||||
/*
|
||||
* Close the gate; this will synchronize this thread with
|
||||
* all already signaled waiters to let them retract their
|
||||
* waiter status - SEE NOTE 1 ABOVE!!!
|
||||
*/
|
||||
if (sem_wait (&(cv->semBlockLock)) != 0)
|
||||
return errno;
|
||||
|
||||
/*
|
||||
* !TRY! lock mtxUnblockLock; try will detect busy condition
|
||||
* and will not cause a deadlock with respect to concurrent
|
||||
* signal/broadcast.
|
||||
*/
|
||||
if ((result = pthread_mutex_trylock (&(cv->mtxUnblockLock))) != 0)
|
||||
{
|
||||
(void) sem_post (&(cv->semBlockLock));
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether cv is still busy (still has waiters)
|
||||
*/
|
||||
if (cv->nWaitersBlocked > cv->nWaitersGone)
|
||||
{
|
||||
if (sem_post (&(cv->semBlockLock)) != 0)
|
||||
result = errno;
|
||||
result1 = pthread_mutex_unlock (&(cv->mtxUnblockLock));
|
||||
result2 = EBUSY;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Now it is safe to destroy
|
||||
*/
|
||||
*cond = NULL;
|
||||
|
||||
if (sem_destroy (&(cv->semBlockLock)) != 0)
|
||||
result = errno;
|
||||
if (sem_destroy (&(cv->semBlockQueue)) != 0)
|
||||
result1 = errno;
|
||||
if ((result2 = pthread_mutex_unlock (&(cv->mtxUnblockLock))) == 0)
|
||||
result2 = pthread_mutex_destroy (&(cv->mtxUnblockLock));
|
||||
|
||||
/* Unlink the CV from the list */
|
||||
|
||||
if (pte_cond_list_head == cv)
|
||||
pte_cond_list_head = cv->next;
|
||||
else
|
||||
cv->prev->next = cv->next;
|
||||
|
||||
if (pte_cond_list_tail == cv)
|
||||
pte_cond_list_tail = cv->prev;
|
||||
else
|
||||
cv->next->prev = cv->prev;
|
||||
|
||||
(void) free (cv);
|
||||
}
|
||||
|
||||
pte_osMutexUnlock(pte_cond_list_lock);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* See notes in pte_cond_check_need_init() above also.
|
||||
*/
|
||||
|
||||
pte_osMutexLock (pte_cond_test_init_lock);
|
||||
|
||||
/*
|
||||
* Check again.
|
||||
*/
|
||||
if (*cond == PTHREAD_COND_INITIALIZER)
|
||||
{
|
||||
/*
|
||||
* This is all we need to do to destroy a statically
|
||||
* initialised cond that has not yet been used (initialised).
|
||||
* If we get to here, another thread waiting to initialise
|
||||
* this cond will get an EINVAL. That's OK.
|
||||
*/
|
||||
*cond = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* The cv has been initialised while we were waiting
|
||||
* so assume it's in use.
|
||||
*/
|
||||
result = EBUSY;
|
||||
}
|
||||
|
||||
pte_osMutexUnlock(pte_cond_test_init_lock);
|
||||
}
|
||||
|
||||
return ((result != 0) ? result : ((result1 != 0) ? result1 : result2));
|
||||
}
|
||||
|
||||
int pthread_cond_init (pthread_cond_t * cond, const pthread_condattr_t * attr)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function initializes a condition variable.
|
||||
*
|
||||
* PARAMETERS
|
||||
* cond
|
||||
* pointer to an instance of pthread_cond_t
|
||||
*
|
||||
* attr
|
||||
* specifies optional creation attributes.
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function initializes a condition variable.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully created condition variable,
|
||||
* EINVAL 'attr' is invalid,
|
||||
* EAGAIN insufficient resources (other than
|
||||
* memory,
|
||||
* ENOMEM insufficient memory,
|
||||
* EBUSY 'cond' is already initialized,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result;
|
||||
pthread_cond_t cv = NULL;
|
||||
|
||||
if (cond == NULL)
|
||||
return EINVAL;
|
||||
|
||||
if ((attr != NULL && *attr != NULL) &&
|
||||
((*attr)->pshared == PTHREAD_PROCESS_SHARED))
|
||||
{
|
||||
/*
|
||||
* Creating condition variable that can be shared between
|
||||
* processes.
|
||||
*/
|
||||
result = ENOSYS;
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
cv = (pthread_cond_t) calloc (1, sizeof (*cv));
|
||||
|
||||
if (cv == NULL)
|
||||
{
|
||||
result = ENOMEM;
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
cv->nWaitersBlocked = 0;
|
||||
cv->nWaitersToUnblock = 0;
|
||||
cv->nWaitersGone = 0;
|
||||
|
||||
if (sem_init (&(cv->semBlockLock), 0, 1) != 0)
|
||||
{
|
||||
result = errno;
|
||||
goto FAIL0;
|
||||
}
|
||||
|
||||
if (sem_init (&(cv->semBlockQueue), 0, 0) != 0)
|
||||
{
|
||||
result = errno;
|
||||
goto FAIL1;
|
||||
}
|
||||
|
||||
if ((result = pthread_mutex_init (&(cv->mtxUnblockLock), 0)) != 0)
|
||||
{
|
||||
goto FAIL2;
|
||||
}
|
||||
|
||||
result = 0;
|
||||
|
||||
goto DONE;
|
||||
|
||||
/*
|
||||
* -------------
|
||||
* Failed...
|
||||
* -------------
|
||||
*/
|
||||
FAIL2:
|
||||
(void) sem_destroy (&(cv->semBlockQueue));
|
||||
|
||||
FAIL1:
|
||||
(void) sem_destroy (&(cv->semBlockLock));
|
||||
|
||||
FAIL0:
|
||||
(void) free (cv);
|
||||
cv = NULL;
|
||||
|
||||
DONE:
|
||||
if (0 == result)
|
||||
{
|
||||
|
||||
pte_osMutexLock (pte_cond_list_lock);
|
||||
|
||||
cv->next = NULL;
|
||||
cv->prev = pte_cond_list_tail;
|
||||
|
||||
if (pte_cond_list_tail != NULL)
|
||||
pte_cond_list_tail->next = cv;
|
||||
|
||||
pte_cond_list_tail = cv;
|
||||
|
||||
if (pte_cond_list_head == NULL)
|
||||
pte_cond_list_head = cv;
|
||||
|
||||
pte_osMutexUnlock(pte_cond_list_lock);
|
||||
}
|
||||
|
||||
*cond = cv;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int pte_cond_unblock (pthread_cond_t * cond, int unblockAll)
|
||||
/*
|
||||
* Notes.
|
||||
*
|
||||
* Does not use the external mutex for synchronisation,
|
||||
* therefore semBlockLock is needed.
|
||||
* mtxUnblockLock is for LEVEL-2 synch. LEVEL-2 is the
|
||||
* state where the external mutex is not necessarily locked by
|
||||
* any thread, ie. between cond_wait unlocking and re-acquiring
|
||||
* the lock after having been signaled or a timeout or
|
||||
* cancellation.
|
||||
*
|
||||
* Uses the following CV elements:
|
||||
* nWaitersBlocked
|
||||
* nWaitersToUnblock
|
||||
* nWaitersGone
|
||||
* mtxUnblockLock
|
||||
* semBlockLock
|
||||
* semBlockQueue
|
||||
*/
|
||||
{
|
||||
int result;
|
||||
pthread_cond_t cv;
|
||||
int nSignalsToIssue;
|
||||
|
||||
if (cond == NULL || *cond == NULL)
|
||||
return EINVAL;
|
||||
|
||||
cv = *cond;
|
||||
|
||||
/*
|
||||
* No-op if the CV is static and hasn't been initialised yet.
|
||||
* Assuming that any race condition is harmless.
|
||||
*/
|
||||
if (cv == PTHREAD_COND_INITIALIZER)
|
||||
return 0;
|
||||
|
||||
if ((result = pthread_mutex_lock (&(cv->mtxUnblockLock))) != 0)
|
||||
return result;
|
||||
|
||||
if (0 != cv->nWaitersToUnblock)
|
||||
{
|
||||
if (0 == cv->nWaitersBlocked)
|
||||
return pthread_mutex_unlock (&(cv->mtxUnblockLock));
|
||||
|
||||
if (unblockAll)
|
||||
{
|
||||
cv->nWaitersToUnblock += (nSignalsToIssue = cv->nWaitersBlocked);
|
||||
cv->nWaitersBlocked = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
nSignalsToIssue = 1;
|
||||
cv->nWaitersToUnblock++;
|
||||
cv->nWaitersBlocked--;
|
||||
}
|
||||
}
|
||||
else if (cv->nWaitersBlocked > cv->nWaitersGone)
|
||||
{
|
||||
/* Use the non-cancellable version of sem_wait() */
|
||||
// if (sem_wait_nocancel (&(cv->semBlockLock)) != 0)
|
||||
if (sem_wait (&(cv->semBlockLock)) != 0)
|
||||
{
|
||||
result = errno;
|
||||
(void) pthread_mutex_unlock (&(cv->mtxUnblockLock));
|
||||
return result;
|
||||
}
|
||||
if (0 != cv->nWaitersGone)
|
||||
{
|
||||
cv->nWaitersBlocked -= cv->nWaitersGone;
|
||||
cv->nWaitersGone = 0;
|
||||
}
|
||||
if (unblockAll)
|
||||
{
|
||||
nSignalsToIssue = cv->nWaitersToUnblock = cv->nWaitersBlocked;
|
||||
cv->nWaitersBlocked = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
nSignalsToIssue = cv->nWaitersToUnblock = 1;
|
||||
cv->nWaitersBlocked--;
|
||||
}
|
||||
}
|
||||
else
|
||||
return pthread_mutex_unlock (&(cv->mtxUnblockLock));
|
||||
|
||||
if ((result = pthread_mutex_unlock (&(cv->mtxUnblockLock))) == 0)
|
||||
{
|
||||
if (sem_post_multiple (&(cv->semBlockQueue), nSignalsToIssue) != 0)
|
||||
result = errno;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int pthread_cond_signal (pthread_cond_t * cond)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function signals a condition variable, waking
|
||||
* one waiting thread.
|
||||
* If SCHED_FIFO or SCHED_RR policy threads are waiting
|
||||
* the highest priority waiter is awakened; otherwise,
|
||||
* an unspecified waiter is awakened.
|
||||
*
|
||||
* PARAMETERS
|
||||
* cond
|
||||
* pointer to an instance of pthread_cond_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function signals a condition variable, waking
|
||||
* one waiting thread.
|
||||
* If SCHED_FIFO or SCHED_RR policy threads are waiting
|
||||
* the highest priority waiter is awakened; otherwise,
|
||||
* an unspecified waiter is awakened.
|
||||
*
|
||||
* NOTES:
|
||||
*
|
||||
* 1) Use when any waiter can respond and only one need
|
||||
* respond (all waiters being equal).
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully signaled condition,
|
||||
* EINVAL 'cond' is invalid,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
/*
|
||||
* The '0'(FALSE) unblockAll arg means unblock ONE waiter.
|
||||
*/
|
||||
return (pte_cond_unblock (cond, 0));
|
||||
|
||||
} /* pthread_cond_signal */
|
||||
|
||||
int
|
||||
pthread_cond_broadcast (pthread_cond_t * cond)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function broadcasts the condition variable,
|
||||
* waking all current waiters.
|
||||
*
|
||||
* PARAMETERS
|
||||
* cond
|
||||
* pointer to an instance of pthread_cond_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function signals a condition variable, waking
|
||||
* all waiting threads.
|
||||
*
|
||||
* NOTES:
|
||||
*
|
||||
* 1) Use when more than one waiter may respond to
|
||||
* predicate change or if any waiting thread may
|
||||
* not be able to respond
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully signalled condition to all
|
||||
* waiting threads,
|
||||
* EINVAL 'cond' is invalid
|
||||
* ENOSPC a required resource has been exhausted,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
/*
|
||||
* The TRUE unblockAll arg means unblock ALL waiters.
|
||||
*/
|
||||
return (pte_cond_unblock (cond, PTE_TRUE));
|
||||
}
|
||||
|
||||
int pthread_cond_wait (pthread_cond_t * cond, pthread_mutex_t * mutex)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function waits on a condition variable until
|
||||
* awakened by a signal or broadcast.
|
||||
*
|
||||
* Caller MUST be holding the mutex lock; the
|
||||
* lock is released and the caller is blocked waiting
|
||||
* on 'cond'. When 'cond' is signaled, the mutex
|
||||
* is re-acquired before returning to the caller.
|
||||
*
|
||||
* PARAMETERS
|
||||
* cond
|
||||
* pointer to an instance of pthread_cond_t
|
||||
*
|
||||
* mutex
|
||||
* pointer to an instance of pthread_mutex_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function waits on a condition variable until
|
||||
* awakened by a signal or broadcast.
|
||||
*
|
||||
* NOTES:
|
||||
*
|
||||
* 1) The function must be called with 'mutex' LOCKED
|
||||
* by the calling thread, or undefined behaviour
|
||||
* will result.
|
||||
*
|
||||
* 2) This routine atomically releases 'mutex' and causes
|
||||
* the calling thread to block on the condition variable.
|
||||
* The blocked thread may be awakened by
|
||||
* pthread_cond_signal or
|
||||
* pthread_cond_broadcast.
|
||||
*
|
||||
* Upon successful completion, the 'mutex' has been locked and
|
||||
* is owned by the calling thread.
|
||||
*
|
||||
*
|
||||
* RESULTS
|
||||
* 0 caught condition; mutex released,
|
||||
* EINVAL 'cond' or 'mutex' is invalid,
|
||||
* EINVAL different mutexes for concurrent waits,
|
||||
* EINVAL mutex is not held by the calling thread,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
/*
|
||||
* The NULL abstime arg means INFINITE waiting.
|
||||
*/
|
||||
return (pte_cond_timedwait (cond, mutex, NULL));
|
||||
|
||||
} /* pthread_cond_wait */
|
||||
|
||||
|
||||
int
|
||||
pthread_cond_timedwait (pthread_cond_t * cond,
|
||||
pthread_mutex_t * mutex,
|
||||
const struct timespec *abstime)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function waits on a condition variable either until
|
||||
* awakened by a signal or broadcast; or until the time
|
||||
* specified by abstime passes.
|
||||
*
|
||||
* PARAMETERS
|
||||
* cond
|
||||
* pointer to an instance of pthread_cond_t
|
||||
*
|
||||
* mutex
|
||||
* pointer to an instance of pthread_mutex_t
|
||||
*
|
||||
* abstime
|
||||
* pointer to an instance of (const struct timespec)
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function waits on a condition variable either until
|
||||
* awakened by a signal or broadcast; or until the time
|
||||
* specified by abstime passes.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) The function must be called with 'mutex' LOCKED
|
||||
* by the calling thread, or undefined behaviour
|
||||
* will result.
|
||||
*
|
||||
* 2) This routine atomically releases 'mutex' and causes
|
||||
* the calling thread to block on the condition variable.
|
||||
* The blocked thread may be awakened by
|
||||
* pthread_cond_signal or
|
||||
* pthread_cond_broadcast.
|
||||
*
|
||||
*
|
||||
* RESULTS
|
||||
* 0 caught condition; mutex released,
|
||||
* EINVAL 'cond', 'mutex', or abstime is invalid,
|
||||
* EINVAL different mutexes for concurrent waits,
|
||||
* EINVAL mutex is not held by the calling thread,
|
||||
* ETIMEDOUT abstime ellapsed before cond was signaled.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
if (abstime == NULL)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
return (pte_cond_timedwait (cond, mutex, abstime));
|
||||
}
|
||||
Vendored
-259
@@ -1,259 +0,0 @@
|
||||
/*
|
||||
* condvar_attr_destroy.c
|
||||
*
|
||||
* Description:
|
||||
* This translation unit implements condition variables and their primitives.
|
||||
*
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "pthread.h"
|
||||
#include "implement.h"
|
||||
|
||||
|
||||
int
|
||||
pthread_condattr_destroy (pthread_condattr_t * attr)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Destroys a condition variable attributes object.
|
||||
* The object can no longer be used.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_condattr_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Destroys a condition variable attributes object.
|
||||
* The object can no longer be used.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) Does not affect condition variables created
|
||||
* using 'attr'
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully released attr,
|
||||
* EINVAL 'attr' is invalid.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if (attr == NULL || *attr == NULL)
|
||||
result = EINVAL;
|
||||
else
|
||||
{
|
||||
(void) free (*attr);
|
||||
|
||||
*attr = NULL;
|
||||
result = 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int pthread_condattr_getpshared (const pthread_condattr_t * attr, int *pshared)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Determine whether condition variables created with 'attr'
|
||||
* can be shared between processes.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_condattr_t
|
||||
*
|
||||
* pshared
|
||||
* will be set to one of:
|
||||
*
|
||||
* PTHREAD_PROCESS_SHARED
|
||||
* May be shared if in shared memory
|
||||
*
|
||||
* PTHREAD_PROCESS_PRIVATE
|
||||
* Cannot be shared.
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Condition Variables created with 'attr' can be shared
|
||||
* between processes if pthread_cond_t variable is allocated
|
||||
* in memory shared by these processes.
|
||||
* NOTES:
|
||||
* 1) pshared condition variables MUST be allocated in
|
||||
* shared memory.
|
||||
*
|
||||
* 2) The following macro is defined if shared mutexes
|
||||
* are supported:
|
||||
* _POSIX_THREAD_PROCESS_SHARED
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully retrieved attribute,
|
||||
* EINVAL 'attr' or 'pshared' is invalid,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result;
|
||||
|
||||
if ((attr != NULL && *attr != NULL) && (pshared != NULL))
|
||||
{
|
||||
*pshared = (*attr)->pshared;
|
||||
result = 0;
|
||||
}
|
||||
else
|
||||
result = EINVAL;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int pthread_condattr_init (pthread_condattr_t * attr)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Initializes a condition variable attributes object
|
||||
* with default attributes.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_condattr_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Initializes a condition variable attributes object
|
||||
* with default attributes.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) Use to define condition variable types
|
||||
* 2) It is up to the application to ensure
|
||||
* that it doesn't re-init an attribute
|
||||
* without destroying it first. Otherwise
|
||||
* a memory leak is created.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully initialized attr,
|
||||
* ENOMEM insufficient memory for attr.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
pthread_condattr_t attr_result;
|
||||
int result = 0;
|
||||
|
||||
attr_result = (pthread_condattr_t) calloc (1, sizeof (*attr_result));
|
||||
|
||||
if (attr_result == NULL)
|
||||
result = ENOMEM;
|
||||
|
||||
*attr = attr_result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int pthread_condattr_setpshared (pthread_condattr_t * attr, int pshared)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Mutexes created with 'attr' can be shared between
|
||||
* processes if pthread_mutex_t variable is allocated
|
||||
* in memory shared by these processes.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_mutexattr_t
|
||||
*
|
||||
* pshared
|
||||
* must be one of:
|
||||
*
|
||||
* PTHREAD_PROCESS_SHARED
|
||||
* May be shared if in shared memory
|
||||
*
|
||||
* PTHREAD_PROCESS_PRIVATE
|
||||
* Cannot be shared.
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Mutexes creatd with 'attr' can be shared between
|
||||
* processes if pthread_mutex_t variable is allocated
|
||||
* in memory shared by these processes.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) pshared mutexes MUST be allocated in shared
|
||||
* memory.
|
||||
*
|
||||
* 2) The following macro is defined if shared mutexes
|
||||
* are supported:
|
||||
* _POSIX_THREAD_PROCESS_SHARED
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully set attribute,
|
||||
* EINVAL 'attr' or pshared is invalid,
|
||||
* ENOSYS PTHREAD_PROCESS_SHARED not supported,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result;
|
||||
|
||||
if ((attr != NULL && *attr != NULL)
|
||||
&& ((pshared == PTHREAD_PROCESS_SHARED)
|
||||
|| (pshared == PTHREAD_PROCESS_PRIVATE)))
|
||||
{
|
||||
if (pshared == PTHREAD_PROCESS_SHARED)
|
||||
{
|
||||
|
||||
#if !defined( _POSIX_THREAD_PROCESS_SHARED )
|
||||
result = ENOSYS;
|
||||
pshared = PTHREAD_PROCESS_PRIVATE;
|
||||
#else
|
||||
result = 0;
|
||||
|
||||
#endif /* _POSIX_THREAD_PROCESS_SHARED */
|
||||
|
||||
}
|
||||
else
|
||||
result = 0;
|
||||
|
||||
(*attr)->pshared = pshared;
|
||||
}
|
||||
else
|
||||
result = EINVAL;
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
Vendored
-111
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* pthread_get.c
|
||||
*
|
||||
* Description:
|
||||
* This translation unit implements miscellaneous thread functions.
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include "pthread.h"
|
||||
#include "implement.h"
|
||||
#include "sched.h"
|
||||
|
||||
int pthread_getconcurrency (void)
|
||||
{
|
||||
return pte_concurrency;
|
||||
}
|
||||
|
||||
int pthread_getschedparam (pthread_t thread, int *policy,
|
||||
struct sched_param *param)
|
||||
{
|
||||
int result;
|
||||
|
||||
/* Validate the thread id. */
|
||||
result = pthread_kill (thread, 0);
|
||||
if (0 != result)
|
||||
return result;
|
||||
|
||||
/*
|
||||
* Validate the policy and param args.
|
||||
* Check that a policy constant wasn't passed rather than &policy.
|
||||
*/
|
||||
if (policy <= (int *) SCHED_MAX || param == NULL)
|
||||
return EINVAL;
|
||||
|
||||
/* Fill out the policy. */
|
||||
*policy = SCHED_OTHER;
|
||||
|
||||
/*
|
||||
* This function must return the priority value set by
|
||||
* the most recent pthread_setschedparam() or pthread_create()
|
||||
* for the target thread. It must not return the actual thread
|
||||
* priority as altered by any system priority adjustments etc.
|
||||
*/
|
||||
param->sched_priority = ((pte_thread_t *)thread)->sched_priority;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *pthread_getspecific (pthread_key_t key)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function returns the current value of key in the
|
||||
* calling thread. If no value has been set for 'key' in
|
||||
* the thread, NULL is returned.
|
||||
*
|
||||
* PARAMETERS
|
||||
* key
|
||||
* an instance of pthread_key_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function returns the current value of key in the
|
||||
* calling thread. If no value has been set for 'key' in
|
||||
* the thread, NULL is returned.
|
||||
*
|
||||
* RESULTS
|
||||
* key value or NULL on failure
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
if (key == NULL)
|
||||
return NULL;
|
||||
|
||||
return pte_osTlsGetValue (key->key);
|
||||
}
|
||||
Vendored
-204
@@ -1,204 +0,0 @@
|
||||
/*
|
||||
* pthread_key.c
|
||||
*
|
||||
* Description:
|
||||
* POSIX thread functions which implement thread-specific data (TSD).
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "pte_osal.h"
|
||||
|
||||
#include "pthread.h"
|
||||
#include "implement.h"
|
||||
|
||||
|
||||
int
|
||||
pthread_key_create (pthread_key_t * key, void (*destructor) (void *))
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function creates a thread-specific data key visible
|
||||
* to all threads. All existing and new threads have a value
|
||||
* NULL for key until set using pthread_setspecific. When any
|
||||
* thread with a non-NULL value for key terminates, 'destructor'
|
||||
* is called with key's current value for that thread.
|
||||
*
|
||||
* PARAMETERS
|
||||
* key
|
||||
* pointer to an instance of pthread_key_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function creates a thread-specific data key visible
|
||||
* to all threads. All existing and new threads have a value
|
||||
* NULL for key until set using pthread_setspecific. When any
|
||||
* thread with a non-NULL value for key terminates, 'destructor'
|
||||
* is called with key's current value for that thread.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully created semaphore,
|
||||
* EAGAIN insufficient resources or PTHREAD_KEYS_MAX
|
||||
* exceeded,
|
||||
* ENOMEM insufficient memory to create the key,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
pthread_key_t newkey;
|
||||
|
||||
if ((newkey = (pthread_key_t) calloc (1, sizeof (*newkey))) == NULL)
|
||||
result = ENOMEM;
|
||||
else
|
||||
{
|
||||
pte_osResult osResult = pte_osTlsAlloc(&(newkey->key));
|
||||
|
||||
if (osResult != PTE_OS_OK)
|
||||
{
|
||||
result = EAGAIN;
|
||||
|
||||
free (newkey);
|
||||
newkey = NULL;
|
||||
}
|
||||
else if (destructor != NULL)
|
||||
{
|
||||
/*
|
||||
* Have to manage associations between thread and key;
|
||||
* Therefore, need a lock that allows multiple threads
|
||||
* to gain exclusive access to the key->threads list.
|
||||
*
|
||||
* The mutex will only be created when it is first locked.
|
||||
*/
|
||||
newkey->keyLock = PTHREAD_MUTEX_INITIALIZER;
|
||||
newkey->destructor = destructor;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
*key = newkey;
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_key_delete (pthread_key_t key)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function deletes a thread-specific data key. This
|
||||
* does not change the value of the thread specific data key
|
||||
* for any thread and does not run the key's destructor
|
||||
* in any thread so it should be used with caution.
|
||||
*
|
||||
* PARAMETERS
|
||||
* key
|
||||
* pointer to an instance of pthread_key_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function deletes a thread-specific data key. This
|
||||
* does not change the value of the thread specific data key
|
||||
* for any thread and does not run the key's destructor
|
||||
* in any thread so it should be used with caution.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully deleted the key,
|
||||
* EINVAL key is invalid,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if (key != NULL)
|
||||
{
|
||||
if (key->threads != NULL &&
|
||||
key->destructor != NULL &&
|
||||
pthread_mutex_lock (&(key->keyLock)) == 0)
|
||||
{
|
||||
ThreadKeyAssoc *assoc;
|
||||
/*
|
||||
* Run through all Thread<-->Key associations
|
||||
* for this key.
|
||||
*
|
||||
* While we hold at least one of the locks guarding
|
||||
* the assoc, we know that the assoc pointed to by
|
||||
* key->threads is valid.
|
||||
*/
|
||||
while ((assoc = (ThreadKeyAssoc *) key->threads) != NULL)
|
||||
{
|
||||
pte_thread_t * thread = assoc->thread;
|
||||
|
||||
/* Finished */
|
||||
if (assoc == NULL)
|
||||
break;
|
||||
|
||||
if (pthread_mutex_lock (&(thread->threadLock)) == 0)
|
||||
{
|
||||
/*
|
||||
* Since we are starting at the head of the key's threads
|
||||
* chain, this will also point key->threads at the next assoc.
|
||||
* While we hold key->keyLock, no other thread can insert
|
||||
* a new assoc via pthread_setspecific.
|
||||
*/
|
||||
pte_tkAssocDestroy (assoc);
|
||||
(void) pthread_mutex_unlock (&(thread->threadLock));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Thread or lock is no longer valid? */
|
||||
pte_tkAssocDestroy (assoc);
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock (&(key->keyLock));
|
||||
}
|
||||
|
||||
pte_osTlsFree (key->key);
|
||||
if (key->destructor != NULL)
|
||||
{
|
||||
/* A thread could be holding the keyLock */
|
||||
while (EBUSY == (result = pthread_mutex_destroy (&(key->keyLock))))
|
||||
pte_osThreadSleep(1); /* Ugly. */
|
||||
}
|
||||
|
||||
free (key);
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
Vendored
-500
@@ -1,500 +0,0 @@
|
||||
/*
|
||||
* pthread_mutex.c
|
||||
*
|
||||
* Description:
|
||||
* This translation unit implements mutual exclusion (mutex) primitives.
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <pte_osal.h>
|
||||
|
||||
#include "pthread.h"
|
||||
#include "implement.h"
|
||||
|
||||
#define TEST_IE InterlockedExchange
|
||||
|
||||
int pthread_mutex_destroy (pthread_mutex_t * mutex)
|
||||
{
|
||||
int result = 0;
|
||||
pthread_mutex_t mx;
|
||||
|
||||
/*
|
||||
* Let the system deal with invalid pointers.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Check to see if we have something to delete.
|
||||
*/
|
||||
if (*mutex < PTHREAD_ERRORCHECK_MUTEX_INITIALIZER)
|
||||
{
|
||||
mx = *mutex;
|
||||
|
||||
result = pthread_mutex_trylock (&mx);
|
||||
|
||||
/*
|
||||
* If trylock succeeded and the mutex is not recursively locked it
|
||||
* can be destroyed.
|
||||
*/
|
||||
if (result == 0)
|
||||
{
|
||||
if (mx->kind != PTHREAD_MUTEX_RECURSIVE || 1 == mx->recursive_count)
|
||||
{
|
||||
/*
|
||||
* FIXME!!!
|
||||
* The mutex isn't held by another thread but we could still
|
||||
* be too late invalidating the mutex below since another thread
|
||||
* may already have entered mutex_lock and the check for a valid
|
||||
* *mutex != NULL.
|
||||
*
|
||||
* Note that this would be an unusual situation because it is not
|
||||
* common that mutexes are destroyed while they are still in
|
||||
* use by other threads.
|
||||
*/
|
||||
*mutex = NULL;
|
||||
|
||||
result = pthread_mutex_unlock (&mx);
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
pte_osSemaphoreDelete(mx->handle);
|
||||
|
||||
free(mx);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Restore the mutex before we return the error.
|
||||
*/
|
||||
*mutex = mx;
|
||||
}
|
||||
}
|
||||
else /* mx->recursive_count > 1 */
|
||||
{
|
||||
/*
|
||||
* The mutex must be recursive and already locked by us (this thread).
|
||||
*/
|
||||
mx->recursive_count--; /* Undo effect of pthread_mutex_trylock() above */
|
||||
result = EBUSY;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* See notes in pte_mutex_check_need_init() above also.
|
||||
*/
|
||||
|
||||
pte_osMutexLock (pte_mutex_test_init_lock);
|
||||
|
||||
|
||||
/*
|
||||
* Check again.
|
||||
*/
|
||||
if (*mutex >= PTHREAD_ERRORCHECK_MUTEX_INITIALIZER)
|
||||
{
|
||||
/*
|
||||
* This is all we need to do to destroy a statically
|
||||
* initialised mutex that has not yet been used (initialised).
|
||||
* If we get to here, another thread
|
||||
* waiting to initialise this mutex will get an EINVAL.
|
||||
*/
|
||||
*mutex = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* The mutex has been initialised while we were waiting
|
||||
* so assume it's in use.
|
||||
*/
|
||||
result = EBUSY;
|
||||
}
|
||||
|
||||
pte_osMutexUnlock(pte_mutex_test_init_lock);
|
||||
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_mutex_init (pthread_mutex_t * mutex, const pthread_mutexattr_t * attr)
|
||||
{
|
||||
int result = 0;
|
||||
pthread_mutex_t mx;
|
||||
|
||||
if (mutex == NULL)
|
||||
return EINVAL;
|
||||
|
||||
mx = (pthread_mutex_t) calloc (1, sizeof (*mx));
|
||||
|
||||
if (mx == NULL)
|
||||
result = ENOMEM;
|
||||
else
|
||||
{
|
||||
mx->lock_idx = 0;
|
||||
mx->recursive_count = 0;
|
||||
mx->kind = (attr == NULL || *attr == NULL
|
||||
? PTHREAD_MUTEX_DEFAULT : (*attr)->kind);
|
||||
mx->ownerThread = NULL;
|
||||
|
||||
pte_osSemaphoreCreate(0,&mx->handle);
|
||||
|
||||
}
|
||||
|
||||
*mutex = mx;
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_mutex_lock (pthread_mutex_t * mutex)
|
||||
{
|
||||
int result = 0;
|
||||
pthread_mutex_t mx;
|
||||
|
||||
/*
|
||||
* Let the system deal with invalid pointers.
|
||||
*/
|
||||
if (*mutex == NULL)
|
||||
return EINVAL;
|
||||
|
||||
/*
|
||||
* We do a quick check to see if we need to do more work
|
||||
* to initialise a static mutex. We check
|
||||
* again inside the guarded section of pte_mutex_check_need_init()
|
||||
* to avoid race conditions.
|
||||
*/
|
||||
if (*mutex >= PTHREAD_ERRORCHECK_MUTEX_INITIALIZER)
|
||||
{
|
||||
if ((result = pte_mutex_check_need_init (mutex)) != 0)
|
||||
return (result);
|
||||
}
|
||||
|
||||
mx = *mutex;
|
||||
|
||||
if (mx->kind == PTHREAD_MUTEX_NORMAL)
|
||||
{
|
||||
if (PTE_ATOMIC_EXCHANGE(
|
||||
&mx->lock_idx,
|
||||
1) != 0)
|
||||
{
|
||||
while (PTE_ATOMIC_EXCHANGE(&mx->lock_idx,-1) != 0)
|
||||
{
|
||||
if (pte_osSemaphorePend(mx->handle,NULL) != PTE_OS_OK)
|
||||
{
|
||||
result = EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_t self = pthread_self();
|
||||
|
||||
if (PTE_ATOMIC_COMPARE_EXCHANGE(&mx->lock_idx,1,0) == 0)
|
||||
{
|
||||
mx->recursive_count = 1;
|
||||
mx->ownerThread = self;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pthread_equal (mx->ownerThread, self))
|
||||
{
|
||||
if (mx->kind == PTHREAD_MUTEX_RECURSIVE)
|
||||
mx->recursive_count++;
|
||||
else
|
||||
result = EDEADLK;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (PTE_ATOMIC_EXCHANGE(&mx->lock_idx,-1) != 0)
|
||||
{
|
||||
if (pte_osSemaphorePend(mx->handle,NULL) != PTE_OS_OK)
|
||||
{
|
||||
result = EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 == result)
|
||||
{
|
||||
mx->recursive_count = 1;
|
||||
mx->ownerThread = self;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
static int pte_timed_eventwait (pte_osSemaphoreHandle event, const struct timespec *abstime)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DESCRIPTION
|
||||
* This function waits on an event until signaled or until
|
||||
* abstime passes.
|
||||
* If abstime has passed when this routine is called then
|
||||
* it returns a result to indicate this.
|
||||
*
|
||||
* If 'abstime' is a NULL pointer then this function will
|
||||
* block until it can successfully decrease the value or
|
||||
* until interrupted by a signal.
|
||||
*
|
||||
* This routine is not a cancelation point.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully signaled,
|
||||
* ETIMEDOUT abstime passed
|
||||
* EINVAL 'event' is not a valid event,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
unsigned int milliseconds;
|
||||
pte_osResult status;
|
||||
int retval;
|
||||
|
||||
if (abstime == NULL)
|
||||
status = pte_osSemaphorePend(event, NULL);
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Calculate timeout as milliseconds from current system time.
|
||||
*/
|
||||
milliseconds = pte_relmillisecs (abstime);
|
||||
|
||||
status = pte_osSemaphorePend(event, &milliseconds);
|
||||
}
|
||||
|
||||
|
||||
if (status == PTE_OS_TIMEOUT)
|
||||
{
|
||||
retval = ETIMEDOUT;
|
||||
}
|
||||
else
|
||||
{
|
||||
retval = 0;
|
||||
}
|
||||
|
||||
return retval;
|
||||
|
||||
}
|
||||
|
||||
int pthread_mutex_timedlock (pthread_mutex_t * mutex,
|
||||
const struct timespec *abstime)
|
||||
{
|
||||
int result;
|
||||
pthread_mutex_t mx;
|
||||
|
||||
/*
|
||||
* Let the system deal with invalid pointers.
|
||||
*/
|
||||
|
||||
/*
|
||||
* We do a quick check to see if we need to do more work
|
||||
* to initialise a static mutex. We check
|
||||
* again inside the guarded section of pte_mutex_check_need_init()
|
||||
* to avoid race conditions.
|
||||
*/
|
||||
if (*mutex >= PTHREAD_ERRORCHECK_MUTEX_INITIALIZER)
|
||||
{
|
||||
if ((result = pte_mutex_check_need_init (mutex)) != 0)
|
||||
return (result);
|
||||
}
|
||||
|
||||
mx = *mutex;
|
||||
|
||||
if (mx->kind == PTHREAD_MUTEX_NORMAL)
|
||||
{
|
||||
if (PTE_ATOMIC_EXCHANGE(&mx->lock_idx,1) != 0)
|
||||
{
|
||||
while (PTE_ATOMIC_EXCHANGE(&mx->lock_idx,-1) != 0)
|
||||
{
|
||||
if (0 != (result = pte_timed_eventwait (mx->handle, abstime)))
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_t self = pthread_self();
|
||||
|
||||
if (PTE_ATOMIC_COMPARE_EXCHANGE(&mx->lock_idx,1,0) == 0)
|
||||
{
|
||||
mx->recursive_count = 1;
|
||||
mx->ownerThread = self;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pthread_equal (mx->ownerThread, self))
|
||||
{
|
||||
if (mx->kind == PTHREAD_MUTEX_RECURSIVE)
|
||||
mx->recursive_count++;
|
||||
else
|
||||
return EDEADLK;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (PTE_ATOMIC_EXCHANGE(&mx->lock_idx,-1) != 0)
|
||||
{
|
||||
if (0 != (result = pte_timed_eventwait (mx->handle, abstime)))
|
||||
return result;
|
||||
}
|
||||
|
||||
mx->recursive_count = 1;
|
||||
mx->ownerThread = self;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_mutex_trylock (pthread_mutex_t * mutex)
|
||||
{
|
||||
int result = 0;
|
||||
pthread_mutex_t mx;
|
||||
|
||||
/*
|
||||
* Let the system deal with invalid pointers.
|
||||
*/
|
||||
|
||||
/*
|
||||
* We do a quick check to see if we need to do more work
|
||||
* to initialise a static mutex. We check
|
||||
* again inside the guarded section of pte_mutex_check_need_init()
|
||||
* to avoid race conditions.
|
||||
*/
|
||||
if (*mutex >= PTHREAD_ERRORCHECK_MUTEX_INITIALIZER)
|
||||
{
|
||||
if ((result = pte_mutex_check_need_init (mutex)) != 0)
|
||||
return (result);
|
||||
}
|
||||
|
||||
mx = *mutex;
|
||||
|
||||
if (0 == PTE_ATOMIC_COMPARE_EXCHANGE (&mx->lock_idx,1,0))
|
||||
{
|
||||
if (mx->kind != PTHREAD_MUTEX_NORMAL)
|
||||
{
|
||||
mx->recursive_count = 1;
|
||||
mx->ownerThread = pthread_self ();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mx->kind == PTHREAD_MUTEX_RECURSIVE &&
|
||||
pthread_equal (mx->ownerThread, pthread_self ()))
|
||||
mx->recursive_count++;
|
||||
else
|
||||
result = EBUSY;
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_mutex_unlock (pthread_mutex_t * mutex)
|
||||
{
|
||||
int result = 0;
|
||||
pthread_mutex_t mx = *mutex;
|
||||
|
||||
/*
|
||||
* Let the system deal with invalid pointers.
|
||||
*/
|
||||
|
||||
/*
|
||||
* If the thread calling us holds the mutex then there is no
|
||||
* race condition. If another thread holds the
|
||||
* lock then we shouldn't be in here.
|
||||
*/
|
||||
if (mx < PTHREAD_ERRORCHECK_MUTEX_INITIALIZER)
|
||||
{
|
||||
if (mx->kind == PTHREAD_MUTEX_NORMAL)
|
||||
{
|
||||
int idx;
|
||||
|
||||
idx = PTE_ATOMIC_EXCHANGE (&mx->lock_idx,0);
|
||||
if (idx != 0)
|
||||
{
|
||||
if (idx < 0)
|
||||
{
|
||||
/*
|
||||
* Someone may be waiting on that mutex.
|
||||
*/
|
||||
if (pte_osSemaphorePost(mx->handle,1) != PTE_OS_OK)
|
||||
result = EINVAL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Was not locked (so can't be owned by us).
|
||||
*/
|
||||
result = EPERM;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pthread_equal (mx->ownerThread, pthread_self ()))
|
||||
{
|
||||
if (mx->kind != PTHREAD_MUTEX_RECURSIVE
|
||||
|| 0 == --mx->recursive_count)
|
||||
{
|
||||
mx->ownerThread = NULL;
|
||||
|
||||
if (PTE_ATOMIC_EXCHANGE (&mx->lock_idx,0) < 0)
|
||||
{
|
||||
if (pte_osSemaphorePost(mx->handle,1) != PTE_OS_OK)
|
||||
result = EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
result = EPERM;
|
||||
}
|
||||
}
|
||||
else
|
||||
result = EINVAL;
|
||||
|
||||
return (result);
|
||||
}
|
||||
Vendored
-377
@@ -1,377 +0,0 @@
|
||||
/*
|
||||
* pthread_mutexattr.c
|
||||
*
|
||||
* Description:
|
||||
* This translation unit implements mutual exclusion (mutex) primitives.
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "pthread.h"
|
||||
#include "implement.h"
|
||||
|
||||
int pthread_mutexattr_destroy (pthread_mutexattr_t * attr)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Destroys a mutex attributes object. The object can
|
||||
* no longer be used.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_mutexattr_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Destroys a mutex attributes object. The object can
|
||||
* no longer be used.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) Does not affect mutexes created using 'attr'
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully released attr,
|
||||
* EINVAL 'attr' is invalid.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if (attr == NULL || *attr == NULL)
|
||||
result = EINVAL;
|
||||
else
|
||||
{
|
||||
pthread_mutexattr_t ma = *attr;
|
||||
|
||||
*attr = NULL;
|
||||
free (ma);
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_mutexattr_getkind_np (pthread_mutexattr_t * attr, int *kind)
|
||||
{
|
||||
return pthread_mutexattr_gettype (attr, kind);
|
||||
}
|
||||
|
||||
int pthread_mutexattr_gettype (pthread_mutexattr_t * attr, int *kind)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if (attr != NULL && *attr != NULL && kind != NULL)
|
||||
*kind = (*attr)->kind;
|
||||
else
|
||||
result = EINVAL;
|
||||
|
||||
return (result);
|
||||
}
|
||||
int pthread_mutexattr_getpshared (const pthread_mutexattr_t * attr, int *pshared)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Determine whether mutexes created with 'attr' can be
|
||||
* shared between processes.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_mutexattr_t
|
||||
*
|
||||
* pshared
|
||||
* will be set to one of:
|
||||
*
|
||||
* PTHREAD_PROCESS_SHARED
|
||||
* May be shared if in shared memory
|
||||
*
|
||||
* PTHREAD_PROCESS_PRIVATE
|
||||
* Cannot be shared.
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Mutexes creatd with 'attr' can be shared between
|
||||
* processes if pthread_mutex_t variable is allocated
|
||||
* in memory shared by these processes.
|
||||
* NOTES:
|
||||
* 1) pshared mutexes MUST be allocated in shared
|
||||
* memory.
|
||||
* 2) The following macro is defined if shared mutexes
|
||||
* are supported:
|
||||
* _POSIX_THREAD_PROCESS_SHARED
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully retrieved attribute,
|
||||
* EINVAL 'attr' is invalid,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result;
|
||||
|
||||
if ((attr != NULL && *attr != NULL) && (pshared != NULL))
|
||||
{
|
||||
*pshared = (*attr)->pshared;
|
||||
result = 0;
|
||||
}
|
||||
else
|
||||
result = EINVAL;
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_mutexattr_init (pthread_mutexattr_t * attr)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Initializes a mutex attributes object with default
|
||||
* attributes.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_mutexattr_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Initializes a mutex attributes object with default
|
||||
* attributes.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) Used to define mutex types
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully initialized attr,
|
||||
* ENOMEM insufficient memory for attr.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
pthread_mutexattr_t ma = (pthread_mutexattr_t) calloc (1, sizeof (*ma));
|
||||
|
||||
if (ma == NULL)
|
||||
result = ENOMEM;
|
||||
else
|
||||
{
|
||||
ma->pshared = PTHREAD_PROCESS_PRIVATE;
|
||||
ma->kind = PTHREAD_MUTEX_DEFAULT;
|
||||
}
|
||||
|
||||
*attr = ma;
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_mutexattr_setkind_np (pthread_mutexattr_t * attr, int kind)
|
||||
{
|
||||
return pthread_mutexattr_settype (attr, kind);
|
||||
}
|
||||
|
||||
int pthread_mutexattr_setpshared (pthread_mutexattr_t * attr, int pshared)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Mutexes created with 'attr' can be shared between
|
||||
* processes if pthread_mutex_t variable is allocated
|
||||
* in memory shared by these processes.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_mutexattr_t
|
||||
*
|
||||
* pshared
|
||||
* must be one of:
|
||||
*
|
||||
* PTHREAD_PROCESS_SHARED
|
||||
* May be shared if in shared memory
|
||||
*
|
||||
* PTHREAD_PROCESS_PRIVATE
|
||||
* Cannot be shared.
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Mutexes creatd with 'attr' can be shared between
|
||||
* processes if pthread_mutex_t variable is allocated
|
||||
* in memory shared by these processes.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) pshared mutexes MUST be allocated in shared
|
||||
* memory.
|
||||
*
|
||||
* 2) The following macro is defined if shared mutexes
|
||||
* are supported:
|
||||
* _POSIX_THREAD_PROCESS_SHARED
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully set attribute,
|
||||
* EINVAL 'attr' or pshared is invalid,
|
||||
* ENOSYS PTHREAD_PROCESS_SHARED not supported,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result;
|
||||
|
||||
if ((attr != NULL && *attr != NULL) &&
|
||||
((pshared == PTHREAD_PROCESS_SHARED) ||
|
||||
(pshared == PTHREAD_PROCESS_PRIVATE)))
|
||||
{
|
||||
if (pshared == PTHREAD_PROCESS_SHARED)
|
||||
{
|
||||
|
||||
#if !defined( _POSIX_THREAD_PROCESS_SHARED )
|
||||
|
||||
result = ENOSYS;
|
||||
pshared = PTHREAD_PROCESS_PRIVATE;
|
||||
|
||||
#else
|
||||
|
||||
result = 0;
|
||||
|
||||
#endif /* _POSIX_THREAD_PROCESS_SHARED */
|
||||
|
||||
}
|
||||
else
|
||||
result = 0;
|
||||
|
||||
(*attr)->pshared = pshared;
|
||||
}
|
||||
else
|
||||
result = EINVAL;
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_mutexattr_settype (pthread_mutexattr_t * attr, int kind)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
*
|
||||
* DOCPUBLIC
|
||||
* The pthread_mutexattr_settype() and
|
||||
* pthread_mutexattr_gettype() functions respectively set and
|
||||
* get the mutex type attribute. This attribute is set in the
|
||||
* type parameter to these functions.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_mutexattr_t
|
||||
*
|
||||
* type
|
||||
* must be one of:
|
||||
*
|
||||
* PTHREAD_MUTEX_DEFAULT
|
||||
*
|
||||
* PTHREAD_MUTEX_NORMAL
|
||||
*
|
||||
* PTHREAD_MUTEX_ERRORCHECK
|
||||
*
|
||||
* PTHREAD_MUTEX_RECURSIVE
|
||||
*
|
||||
* DESCRIPTION
|
||||
* The pthread_mutexattr_settype() and
|
||||
* pthread_mutexattr_gettype() functions respectively set and
|
||||
* get the mutex type attribute. This attribute is set in the
|
||||
* type parameter to these functions. The default value of the
|
||||
* type attribute is PTHREAD_MUTEX_DEFAULT.
|
||||
*
|
||||
* The type of mutex is contained in the type attribute of the
|
||||
* mutex attributes. Valid mutex types include:
|
||||
*
|
||||
* PTHREAD_MUTEX_NORMAL
|
||||
* This type of mutex does not detect deadlock. A
|
||||
* thread attempting to relock this mutex without
|
||||
* first unlocking it will deadlock. Attempting to
|
||||
* unlock a mutex locked by a different thread
|
||||
* results in undefined behavior. Attempting to
|
||||
* unlock an unlocked mutex results in undefined
|
||||
* behavior.
|
||||
*
|
||||
* PTHREAD_MUTEX_ERRORCHECK
|
||||
* This type of mutex provides error checking. A
|
||||
* thread attempting to relock this mutex without
|
||||
* first unlocking it will return with an error. A
|
||||
* thread attempting to unlock a mutex which another
|
||||
* thread has locked will return with an error. A
|
||||
* thread attempting to unlock an unlocked mutex will
|
||||
* return with an error.
|
||||
*
|
||||
* PTHREAD_MUTEX_DEFAULT
|
||||
* Same as PTHREAD_MUTEX_NORMAL.
|
||||
*
|
||||
* PTHREAD_MUTEX_RECURSIVE
|
||||
* A thread attempting to relock this mutex without
|
||||
* first unlocking it will succeed in locking the
|
||||
* mutex. The relocking deadlock which can occur with
|
||||
* mutexes of type PTHREAD_MUTEX_NORMAL cannot occur
|
||||
* with this type of mutex. Multiple locks of this
|
||||
* mutex require the same number of unlocks to
|
||||
* release the mutex before another thread can
|
||||
* acquire the mutex. A thread attempting to unlock a
|
||||
* mutex which another thread has locked will return
|
||||
* with an error. A thread attempting to unlock an
|
||||
* unlocked mutex will return with an error. This
|
||||
* type of mutex is only supported for mutexes whose
|
||||
* process shared attribute is
|
||||
* PTHREAD_PROCESS_PRIVATE.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully set attribute,
|
||||
* EINVAL 'attr' or 'type' is invalid,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if ((attr != NULL && *attr != NULL))
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case PTHREAD_MUTEX_FAST_NP:
|
||||
case PTHREAD_MUTEX_RECURSIVE_NP:
|
||||
case PTHREAD_MUTEX_ERRORCHECK_NP:
|
||||
(*attr)->kind = kind;
|
||||
break;
|
||||
default:
|
||||
result = EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
result = EINVAL;
|
||||
|
||||
return (result);
|
||||
}
|
||||
Vendored
-676
@@ -1,676 +0,0 @@
|
||||
/*
|
||||
* pthread_rwlock.c
|
||||
*
|
||||
* Description:
|
||||
* This translation unit implements read/write lock primitives.
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "pthread.h"
|
||||
#include "implement.h"
|
||||
|
||||
int pthread_rwlock_destroy (pthread_rwlock_t * rwlock)
|
||||
{
|
||||
pthread_rwlock_t rwl;
|
||||
int result = 0, result1 = 0, result2 = 0;
|
||||
|
||||
if (rwlock == NULL || *rwlock == NULL)
|
||||
return EINVAL;
|
||||
|
||||
if (*rwlock != PTHREAD_RWLOCK_INITIALIZER)
|
||||
{
|
||||
rwl = *rwlock;
|
||||
|
||||
if (rwl->nMagic != PTE_RWLOCK_MAGIC)
|
||||
return EINVAL;
|
||||
|
||||
if ((result = pthread_mutex_lock (&(rwl->mtxExclusiveAccess))) != 0)
|
||||
return result;
|
||||
|
||||
if ((result =
|
||||
pthread_mutex_lock (&(rwl->mtxSharedAccessCompleted))) != 0)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether any threads own/wait for the lock (wait for ex.access);
|
||||
* report "BUSY" if so.
|
||||
*/
|
||||
if (rwl->nExclusiveAccessCount > 0
|
||||
|| rwl->nSharedAccessCount > rwl->nCompletedSharedAccessCount)
|
||||
{
|
||||
result = pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted));
|
||||
result1 = pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));
|
||||
result2 = EBUSY;
|
||||
}
|
||||
else
|
||||
{
|
||||
rwl->nMagic = 0;
|
||||
|
||||
if ((result =
|
||||
pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted))) != 0)
|
||||
{
|
||||
pthread_mutex_unlock (&rwl->mtxExclusiveAccess);
|
||||
return result;
|
||||
}
|
||||
|
||||
if ((result =
|
||||
pthread_mutex_unlock (&(rwl->mtxExclusiveAccess))) != 0)
|
||||
return result;
|
||||
|
||||
*rwlock = NULL; /* Invalidate rwlock before anything else */
|
||||
result = pthread_cond_destroy (&(rwl->cndSharedAccessCompleted));
|
||||
result1 = pthread_mutex_destroy (&(rwl->mtxSharedAccessCompleted));
|
||||
result2 = pthread_mutex_destroy (&(rwl->mtxExclusiveAccess));
|
||||
(void) free (rwl);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* See notes in pte_rwlock_check_need_init() above also.
|
||||
*/
|
||||
|
||||
pte_osMutexLock (pte_rwlock_test_init_lock);
|
||||
|
||||
/*
|
||||
* Check again.
|
||||
*/
|
||||
if (*rwlock == PTHREAD_RWLOCK_INITIALIZER)
|
||||
{
|
||||
/*
|
||||
* This is all we need to do to destroy a statically
|
||||
* initialised rwlock that has not yet been used (initialised).
|
||||
* If we get to here, another thread
|
||||
* waiting to initialise this rwlock will get an EINVAL.
|
||||
*/
|
||||
*rwlock = NULL;
|
||||
}
|
||||
/*
|
||||
* The rwlock has been initialised while we were waiting
|
||||
* so assume it's in use.
|
||||
*/
|
||||
else
|
||||
result = EBUSY;
|
||||
|
||||
pte_osMutexUnlock(pte_rwlock_test_init_lock);
|
||||
|
||||
}
|
||||
|
||||
return ((result != 0) ? result : ((result1 != 0) ? result1 : result2));
|
||||
}
|
||||
|
||||
int pthread_rwlock_init (pthread_rwlock_t * rwlock,
|
||||
const pthread_rwlockattr_t * attr)
|
||||
{
|
||||
int result;
|
||||
pthread_rwlock_t rwl = 0;
|
||||
|
||||
if (rwlock == NULL)
|
||||
return EINVAL;
|
||||
|
||||
if (attr != NULL && *attr != NULL)
|
||||
{
|
||||
result = EINVAL; /* Not supported */
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
rwl = (pthread_rwlock_t) calloc (1, sizeof (*rwl));
|
||||
|
||||
if (rwl == NULL)
|
||||
{
|
||||
result = ENOMEM;
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
rwl->nSharedAccessCount = 0;
|
||||
rwl->nExclusiveAccessCount = 0;
|
||||
rwl->nCompletedSharedAccessCount = 0;
|
||||
|
||||
result = pthread_mutex_init (&rwl->mtxExclusiveAccess, NULL);
|
||||
if (result != 0)
|
||||
{
|
||||
goto FAIL0;
|
||||
}
|
||||
|
||||
result = pthread_mutex_init (&rwl->mtxSharedAccessCompleted, NULL);
|
||||
if (result != 0)
|
||||
{
|
||||
goto FAIL1;
|
||||
}
|
||||
|
||||
result = pthread_cond_init (&rwl->cndSharedAccessCompleted, NULL);
|
||||
if (result != 0)
|
||||
{
|
||||
goto FAIL2;
|
||||
}
|
||||
|
||||
rwl->nMagic = PTE_RWLOCK_MAGIC;
|
||||
|
||||
result = 0;
|
||||
goto DONE;
|
||||
|
||||
FAIL2:
|
||||
(void) pthread_mutex_destroy (&(rwl->mtxSharedAccessCompleted));
|
||||
|
||||
FAIL1:
|
||||
(void) pthread_mutex_destroy (&(rwl->mtxExclusiveAccess));
|
||||
|
||||
FAIL0:
|
||||
(void) free (rwl);
|
||||
rwl = NULL;
|
||||
|
||||
DONE:
|
||||
*rwlock = rwl;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int pthread_rwlock_rdlock (pthread_rwlock_t * rwlock)
|
||||
{
|
||||
int result;
|
||||
pthread_rwlock_t rwl;
|
||||
|
||||
if (rwlock == NULL || *rwlock == NULL)
|
||||
return EINVAL;
|
||||
|
||||
/*
|
||||
* We do a quick check to see if we need to do more work
|
||||
* to initialise a static rwlock. We check
|
||||
* again inside the guarded section of pte_rwlock_check_need_init()
|
||||
* to avoid race conditions.
|
||||
*/
|
||||
if (*rwlock == PTHREAD_RWLOCK_INITIALIZER)
|
||||
{
|
||||
result = pte_rwlock_check_need_init (rwlock);
|
||||
|
||||
if (result != 0 && result != EBUSY)
|
||||
return result;
|
||||
}
|
||||
|
||||
rwl = *rwlock;
|
||||
|
||||
if (rwl->nMagic != PTE_RWLOCK_MAGIC)
|
||||
return EINVAL;
|
||||
|
||||
if ((result = pthread_mutex_lock (&(rwl->mtxExclusiveAccess))) != 0)
|
||||
return result;
|
||||
|
||||
if (++rwl->nSharedAccessCount == INT_MAX)
|
||||
{
|
||||
if ((result =
|
||||
pthread_mutex_lock (&(rwl->mtxSharedAccessCompleted))) != 0)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));
|
||||
return result;
|
||||
}
|
||||
|
||||
rwl->nSharedAccessCount -= rwl->nCompletedSharedAccessCount;
|
||||
rwl->nCompletedSharedAccessCount = 0;
|
||||
|
||||
if ((result =
|
||||
pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted))) != 0)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return (pthread_mutex_unlock (&(rwl->mtxExclusiveAccess)));
|
||||
}
|
||||
|
||||
int pthread_rwlock_timedrdlock (pthread_rwlock_t * rwlock,
|
||||
const struct timespec *abstime)
|
||||
{
|
||||
int result;
|
||||
pthread_rwlock_t rwl;
|
||||
|
||||
if (rwlock == NULL || *rwlock == NULL)
|
||||
return EINVAL;
|
||||
|
||||
/*
|
||||
* We do a quick check to see if we need to do more work
|
||||
* to initialise a static rwlock. We check
|
||||
* again inside the guarded section of pte_rwlock_check_need_init()
|
||||
* to avoid race conditions.
|
||||
*/
|
||||
if (*rwlock == PTHREAD_RWLOCK_INITIALIZER)
|
||||
{
|
||||
result = pte_rwlock_check_need_init (rwlock);
|
||||
|
||||
if (result != 0 && result != EBUSY)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
rwl = *rwlock;
|
||||
|
||||
if (rwl->nMagic != PTE_RWLOCK_MAGIC)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if ((result =
|
||||
pthread_mutex_timedlock (&(rwl->mtxExclusiveAccess), abstime)) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if (++rwl->nSharedAccessCount == INT_MAX)
|
||||
{
|
||||
if ((result =
|
||||
pthread_mutex_timedlock (&(rwl->mtxSharedAccessCompleted),
|
||||
abstime)) != 0)
|
||||
{
|
||||
if (result == ETIMEDOUT)
|
||||
{
|
||||
++rwl->nCompletedSharedAccessCount;
|
||||
}
|
||||
(void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));
|
||||
return result;
|
||||
}
|
||||
|
||||
rwl->nSharedAccessCount -= rwl->nCompletedSharedAccessCount;
|
||||
rwl->nCompletedSharedAccessCount = 0;
|
||||
|
||||
if ((result =
|
||||
pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted))) != 0)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return (pthread_mutex_unlock (&(rwl->mtxExclusiveAccess)));
|
||||
}
|
||||
|
||||
int pthread_rwlock_timedwrlock (pthread_rwlock_t * rwlock,
|
||||
const struct timespec *abstime)
|
||||
{
|
||||
int result;
|
||||
pthread_rwlock_t rwl;
|
||||
|
||||
if (rwlock == NULL || *rwlock == NULL)
|
||||
return EINVAL;
|
||||
|
||||
/*
|
||||
* We do a quick check to see if we need to do more work
|
||||
* to initialise a static rwlock. We check
|
||||
* again inside the guarded section of pte_rwlock_check_need_init()
|
||||
* to avoid race conditions.
|
||||
*/
|
||||
if (*rwlock == PTHREAD_RWLOCK_INITIALIZER)
|
||||
{
|
||||
result = pte_rwlock_check_need_init (rwlock);
|
||||
|
||||
if (result != 0 && result != EBUSY)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
rwl = *rwlock;
|
||||
|
||||
if (rwl->nMagic != PTE_RWLOCK_MAGIC)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if ((result =
|
||||
pthread_mutex_timedlock (&(rwl->mtxExclusiveAccess), abstime)) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if ((result =
|
||||
pthread_mutex_timedlock (&(rwl->mtxSharedAccessCompleted),
|
||||
abstime)) != 0)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));
|
||||
return result;
|
||||
}
|
||||
|
||||
if (rwl->nExclusiveAccessCount == 0)
|
||||
{
|
||||
if (rwl->nCompletedSharedAccessCount > 0)
|
||||
{
|
||||
rwl->nSharedAccessCount -= rwl->nCompletedSharedAccessCount;
|
||||
rwl->nCompletedSharedAccessCount = 0;
|
||||
}
|
||||
|
||||
if (rwl->nSharedAccessCount > 0)
|
||||
{
|
||||
rwl->nCompletedSharedAccessCount = -rwl->nSharedAccessCount;
|
||||
|
||||
/*
|
||||
* This routine may be a cancelation point
|
||||
* according to POSIX 1003.1j section 18.1.2.
|
||||
*/
|
||||
pthread_cleanup_push (pte_rwlock_cancelwrwait, (void *) rwl);
|
||||
|
||||
do
|
||||
{
|
||||
result =
|
||||
pthread_cond_timedwait (&(rwl->cndSharedAccessCompleted),
|
||||
&(rwl->mtxSharedAccessCompleted),
|
||||
abstime);
|
||||
}
|
||||
while (result == 0 && rwl->nCompletedSharedAccessCount < 0);
|
||||
|
||||
pthread_cleanup_pop ((result != 0) ? 1 : 0);
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
rwl->nSharedAccessCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result == 0)
|
||||
rwl->nExclusiveAccessCount++;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int pthread_rwlock_tryrdlock (pthread_rwlock_t * rwlock)
|
||||
{
|
||||
int result;
|
||||
pthread_rwlock_t rwl;
|
||||
|
||||
if (rwlock == NULL || *rwlock == NULL)
|
||||
return EINVAL;
|
||||
|
||||
/*
|
||||
* We do a quick check to see if we need to do more work
|
||||
* to initialise a static rwlock. We check
|
||||
* again inside the guarded section of pte_rwlock_check_need_init()
|
||||
* to avoid race conditions.
|
||||
*/
|
||||
if (*rwlock == PTHREAD_RWLOCK_INITIALIZER)
|
||||
{
|
||||
result = pte_rwlock_check_need_init (rwlock);
|
||||
|
||||
if (result != 0 && result != EBUSY)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
rwl = *rwlock;
|
||||
|
||||
if (rwl->nMagic != PTE_RWLOCK_MAGIC)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if ((result = pthread_mutex_trylock (&(rwl->mtxExclusiveAccess))) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if (++rwl->nSharedAccessCount == INT_MAX)
|
||||
{
|
||||
if ((result =
|
||||
pthread_mutex_lock (&(rwl->mtxSharedAccessCompleted))) != 0)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));
|
||||
return result;
|
||||
}
|
||||
|
||||
rwl->nSharedAccessCount -= rwl->nCompletedSharedAccessCount;
|
||||
rwl->nCompletedSharedAccessCount = 0;
|
||||
|
||||
if ((result =
|
||||
pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted))) != 0)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return (pthread_mutex_unlock (&rwl->mtxExclusiveAccess));
|
||||
}
|
||||
|
||||
int pthread_rwlock_trywrlock (pthread_rwlock_t * rwlock)
|
||||
{
|
||||
int result, result1;
|
||||
pthread_rwlock_t rwl;
|
||||
|
||||
if (rwlock == NULL || *rwlock == NULL)
|
||||
return EINVAL;
|
||||
|
||||
/*
|
||||
* We do a quick check to see if we need to do more work
|
||||
* to initialise a static rwlock. We check
|
||||
* again inside the guarded section of pte_rwlock_check_need_init()
|
||||
* to avoid race conditions.
|
||||
*/
|
||||
if (*rwlock == PTHREAD_RWLOCK_INITIALIZER)
|
||||
{
|
||||
result = pte_rwlock_check_need_init (rwlock);
|
||||
|
||||
if (result != 0 && result != EBUSY)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
rwl = *rwlock;
|
||||
|
||||
if (rwl->nMagic != PTE_RWLOCK_MAGIC)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if ((result = pthread_mutex_trylock (&(rwl->mtxExclusiveAccess))) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if ((result =
|
||||
pthread_mutex_trylock (&(rwl->mtxSharedAccessCompleted))) != 0)
|
||||
{
|
||||
result1 = pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));
|
||||
return ((result1 != 0) ? result1 : result);
|
||||
}
|
||||
|
||||
if (rwl->nExclusiveAccessCount == 0)
|
||||
{
|
||||
if (rwl->nCompletedSharedAccessCount > 0)
|
||||
{
|
||||
rwl->nSharedAccessCount -= rwl->nCompletedSharedAccessCount;
|
||||
rwl->nCompletedSharedAccessCount = 0;
|
||||
}
|
||||
|
||||
if (rwl->nSharedAccessCount > 0)
|
||||
{
|
||||
if ((result =
|
||||
pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted))) != 0)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));
|
||||
return result;
|
||||
}
|
||||
|
||||
if ((result =
|
||||
pthread_mutex_unlock (&(rwl->mtxExclusiveAccess))) == 0)
|
||||
{
|
||||
result = EBUSY;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rwl->nExclusiveAccessCount = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result = EBUSY;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int pthread_rwlock_unlock (pthread_rwlock_t * rwlock)
|
||||
{
|
||||
int result, result1;
|
||||
pthread_rwlock_t rwl;
|
||||
|
||||
if (rwlock == NULL || *rwlock == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
if (*rwlock == PTHREAD_RWLOCK_INITIALIZER)
|
||||
{
|
||||
/*
|
||||
* Assume any race condition here is harmless.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
rwl = *rwlock;
|
||||
|
||||
if (rwl->nMagic != PTE_RWLOCK_MAGIC)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if (rwl->nExclusiveAccessCount == 0)
|
||||
{
|
||||
if ((result =
|
||||
pthread_mutex_lock (&(rwl->mtxSharedAccessCompleted))) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if (++rwl->nCompletedSharedAccessCount == 0)
|
||||
{
|
||||
result = pthread_cond_signal (&(rwl->cndSharedAccessCompleted));
|
||||
}
|
||||
|
||||
result1 = pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted));
|
||||
}
|
||||
else
|
||||
{
|
||||
rwl->nExclusiveAccessCount--;
|
||||
|
||||
result = pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted));
|
||||
result1 = pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));
|
||||
|
||||
}
|
||||
|
||||
return ((result != 0) ? result : result1);
|
||||
}
|
||||
|
||||
int pthread_rwlock_wrlock (pthread_rwlock_t * rwlock)
|
||||
{
|
||||
int result;
|
||||
pthread_rwlock_t rwl;
|
||||
|
||||
if (rwlock == NULL || *rwlock == NULL)
|
||||
return EINVAL;
|
||||
|
||||
/*
|
||||
* We do a quick check to see if we need to do more work
|
||||
* to initialise a static rwlock. We check
|
||||
* again inside the guarded section of pte_rwlock_check_need_init()
|
||||
* to avoid race conditions.
|
||||
*/
|
||||
if (*rwlock == PTHREAD_RWLOCK_INITIALIZER)
|
||||
{
|
||||
result = pte_rwlock_check_need_init (rwlock);
|
||||
|
||||
if (result != 0 && result != EBUSY)
|
||||
return result;
|
||||
}
|
||||
|
||||
rwl = *rwlock;
|
||||
|
||||
if (rwl->nMagic != PTE_RWLOCK_MAGIC)
|
||||
return EINVAL;
|
||||
|
||||
if ((result = pthread_mutex_lock (&(rwl->mtxExclusiveAccess))) != 0)
|
||||
return result;
|
||||
|
||||
if ((result = pthread_mutex_lock (&(rwl->mtxSharedAccessCompleted))) != 0)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));
|
||||
return result;
|
||||
}
|
||||
|
||||
if (rwl->nExclusiveAccessCount == 0)
|
||||
{
|
||||
if (rwl->nCompletedSharedAccessCount > 0)
|
||||
{
|
||||
rwl->nSharedAccessCount -= rwl->nCompletedSharedAccessCount;
|
||||
rwl->nCompletedSharedAccessCount = 0;
|
||||
}
|
||||
|
||||
if (rwl->nSharedAccessCount > 0)
|
||||
{
|
||||
rwl->nCompletedSharedAccessCount = -rwl->nSharedAccessCount;
|
||||
|
||||
/*
|
||||
* This routine may be a cancelation point
|
||||
* according to POSIX 1003.1j section 18.1.2.
|
||||
*/
|
||||
pthread_cleanup_push (pte_rwlock_cancelwrwait, (void *) rwl);
|
||||
|
||||
do
|
||||
{
|
||||
result = pthread_cond_wait (&(rwl->cndSharedAccessCompleted),
|
||||
&(rwl->mtxSharedAccessCompleted));
|
||||
}
|
||||
while (result == 0 && rwl->nCompletedSharedAccessCount < 0);
|
||||
|
||||
pthread_cleanup_pop ((result != 0) ? 1 : 0);
|
||||
|
||||
if (result == 0)
|
||||
rwl->nSharedAccessCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (result == 0)
|
||||
rwl->nExclusiveAccessCount++;
|
||||
|
||||
return result;
|
||||
}
|
||||
Vendored
-259
@@ -1,259 +0,0 @@
|
||||
/*
|
||||
* pthread_rwlockattr_destroy.c
|
||||
*
|
||||
* Description:
|
||||
* This translation unit implements read/write lock primitives.
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "pthread.h"
|
||||
#include "implement.h"
|
||||
|
||||
int
|
||||
pthread_rwlockattr_destroy (pthread_rwlockattr_t * attr)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Destroys a rwlock attributes object. The object can
|
||||
* no longer be used.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_rwlockattr_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Destroys a rwlock attributes object. The object can
|
||||
* no longer be used.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) Does not affect rwlockss created using 'attr'
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully released attr,
|
||||
* EINVAL 'attr' is invalid.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if (attr == NULL || *attr == NULL)
|
||||
{
|
||||
result = EINVAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_rwlockattr_t rwa = *attr;
|
||||
|
||||
*attr = NULL;
|
||||
free (rwa);
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int
|
||||
pthread_rwlockattr_getpshared (const pthread_rwlockattr_t * attr,
|
||||
int *pshared)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Determine whether rwlocks created with 'attr' can be
|
||||
* shared between processes.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_rwlockattr_t
|
||||
*
|
||||
* pshared
|
||||
* will be set to one of:
|
||||
*
|
||||
* PTHREAD_PROCESS_SHARED
|
||||
* May be shared if in shared memory
|
||||
*
|
||||
* PTHREAD_PROCESS_PRIVATE
|
||||
* Cannot be shared.
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Rwlocks creatd with 'attr' can be shared between
|
||||
* processes if pthread_rwlock_t variable is allocated
|
||||
* in memory shared by these processes.
|
||||
* NOTES:
|
||||
* 1) pshared rwlocks MUST be allocated in shared
|
||||
* memory.
|
||||
* 2) The following macro is defined if shared rwlocks
|
||||
* are supported:
|
||||
* _POSIX_THREAD_PROCESS_SHARED
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully retrieved attribute,
|
||||
* EINVAL 'attr' is invalid,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result;
|
||||
|
||||
if ((attr != NULL && *attr != NULL) && (pshared != NULL))
|
||||
{
|
||||
*pshared = (*attr)->pshared;
|
||||
result = 0;
|
||||
}
|
||||
else
|
||||
result = EINVAL;
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_rwlockattr_init (pthread_rwlockattr_t * attr)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Initializes a rwlock attributes object with default
|
||||
* attributes.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_rwlockattr_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Initializes a rwlock attributes object with default
|
||||
* attributes.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully initialized attr,
|
||||
* ENOMEM insufficient memory for attr.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
pthread_rwlockattr_t rwa;
|
||||
|
||||
rwa = (pthread_rwlockattr_t) calloc (1, sizeof (*rwa));
|
||||
|
||||
if (rwa == NULL)
|
||||
result = ENOMEM;
|
||||
else
|
||||
rwa->pshared = PTHREAD_PROCESS_PRIVATE;
|
||||
|
||||
*attr = rwa;
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_rwlockattr_setpshared (pthread_rwlockattr_t * attr, int pshared)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Rwlocks created with 'attr' can be shared between
|
||||
* processes if pthread_rwlock_t variable is allocated
|
||||
* in memory shared by these processes.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_rwlockattr_t
|
||||
*
|
||||
* pshared
|
||||
* must be one of:
|
||||
*
|
||||
* PTHREAD_PROCESS_SHARED
|
||||
* May be shared if in shared memory
|
||||
*
|
||||
* PTHREAD_PROCESS_PRIVATE
|
||||
* Cannot be shared.
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Rwlocks creatd with 'attr' can be shared between
|
||||
* processes if pthread_rwlock_t variable is allocated
|
||||
* in memory shared by these processes.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) pshared rwlocks MUST be allocated in shared
|
||||
* memory.
|
||||
*
|
||||
* 2) The following macro is defined if shared rwlocks
|
||||
* are supported:
|
||||
* _POSIX_THREAD_PROCESS_SHARED
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully set attribute,
|
||||
* EINVAL 'attr' or pshared is invalid,
|
||||
* ENOSYS PTHREAD_PROCESS_SHARED not supported,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result;
|
||||
|
||||
if ((attr != NULL && *attr != NULL) &&
|
||||
((pshared == PTHREAD_PROCESS_SHARED) ||
|
||||
(pshared == PTHREAD_PROCESS_PRIVATE)))
|
||||
{
|
||||
if (pshared == PTHREAD_PROCESS_SHARED)
|
||||
{
|
||||
|
||||
#if !defined( _POSIX_THREAD_PROCESS_SHARED )
|
||||
|
||||
result = ENOSYS;
|
||||
pshared = PTHREAD_PROCESS_PRIVATE;
|
||||
|
||||
#else
|
||||
|
||||
result = 0;
|
||||
|
||||
#endif /* _POSIX_THREAD_PROCESS_SHARED */
|
||||
|
||||
}
|
||||
else
|
||||
result = 0;
|
||||
|
||||
(*attr)->pshared = pshared;
|
||||
}
|
||||
else
|
||||
result = EINVAL;
|
||||
|
||||
return (result);
|
||||
|
||||
} /* pthread_rwlockattr_setpshared */
|
||||
Vendored
-399
@@ -1,399 +0,0 @@
|
||||
/*
|
||||
* pthread_set.c
|
||||
*
|
||||
* Description:
|
||||
* POSIX thread functions related to state.
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include "pthread.h"
|
||||
#include "implement.h"
|
||||
#include "sched.h"
|
||||
|
||||
int pthread_setcancelstate (int state, int *oldstate)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function atomically sets the calling thread's
|
||||
* cancelability state to 'state' and returns the previous
|
||||
* cancelability state at the location referenced by
|
||||
* 'oldstate'
|
||||
*
|
||||
* PARAMETERS
|
||||
* state,
|
||||
* oldstate
|
||||
* PTHREAD_CANCEL_ENABLE
|
||||
* cancellation is enabled,
|
||||
*
|
||||
* PTHREAD_CANCEL_DISABLE
|
||||
* cancellation is disabled
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function atomically sets the calling thread's
|
||||
* cancelability state to 'state' and returns the previous
|
||||
* cancelability state at the location referenced by
|
||||
* 'oldstate'.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) Use to disable cancellation around 'atomic' code that
|
||||
* includes cancellation points
|
||||
*
|
||||
* COMPATIBILITY ADDITIONS
|
||||
* If 'oldstate' is NULL then the previous state is not returned
|
||||
* but the function still succeeds. (Solaris)
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully set cancelability type,
|
||||
* EINVAL 'state' is invalid
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
pthread_t self = pthread_self ();
|
||||
pte_thread_t * sp = (pte_thread_t *) self;
|
||||
|
||||
if (sp == NULL
|
||||
|| (state != PTHREAD_CANCEL_ENABLE && state != PTHREAD_CANCEL_DISABLE))
|
||||
return EINVAL;
|
||||
|
||||
/*
|
||||
* Lock for async-cancel safety.
|
||||
*/
|
||||
(void) pthread_mutex_lock (&sp->cancelLock);
|
||||
|
||||
if (oldstate != NULL)
|
||||
*oldstate = sp->cancelState;
|
||||
|
||||
sp->cancelState = state;
|
||||
|
||||
/*
|
||||
* Check if there is a pending asynchronous cancel
|
||||
*/
|
||||
if (state == PTHREAD_CANCEL_ENABLE
|
||||
&& (sp->cancelType == PTHREAD_CANCEL_ASYNCHRONOUS)
|
||||
&& (pte_osThreadCheckCancel(sp->threadId) == PTE_OS_INTERRUPTED) )
|
||||
{
|
||||
sp->state = PThreadStateCanceling;
|
||||
sp->cancelState = PTHREAD_CANCEL_DISABLE;
|
||||
(void) pthread_mutex_unlock (&sp->cancelLock);
|
||||
pte_throw (PTE_EPS_CANCEL);
|
||||
|
||||
/* Never reached */
|
||||
}
|
||||
|
||||
(void) pthread_mutex_unlock (&sp->cancelLock);
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_setcanceltype (int type, int *oldtype)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function atomically sets the calling thread's
|
||||
* cancelability type to 'type' and returns the previous
|
||||
* cancelability type at the location referenced by
|
||||
* 'oldtype'
|
||||
*
|
||||
* PARAMETERS
|
||||
* type,
|
||||
* oldtype
|
||||
* PTHREAD_CANCEL_DEFERRED
|
||||
* only deferred cancelation is allowed,
|
||||
*
|
||||
* PTHREAD_CANCEL_ASYNCHRONOUS
|
||||
* Asynchronous cancellation is allowed
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function atomically sets the calling thread's
|
||||
* cancelability type to 'type' and returns the previous
|
||||
* cancelability type at the location referenced by
|
||||
* 'oldtype'
|
||||
*
|
||||
* NOTES:
|
||||
* 1) Use with caution; most code is not safe for use
|
||||
* with asynchronous cancelability.
|
||||
*
|
||||
* COMPATIBILITY ADDITIONS
|
||||
* If 'oldtype' is NULL then the previous type is not returned
|
||||
* but the function still succeeds. (Solaris)
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully set cancelability type,
|
||||
* EINVAL 'type' is invalid
|
||||
* EPERM Async cancellation is not supported.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
pthread_t self = pthread_self ();
|
||||
pte_thread_t * sp = (pte_thread_t *) self;
|
||||
|
||||
#ifndef PTE_SUPPORT_ASYNC_CANCEL
|
||||
if (type == PTHREAD_CANCEL_ASYNCHRONOUS)
|
||||
{
|
||||
/* Async cancellation is not supported at this time. See notes in
|
||||
* pthread_cancel.
|
||||
*/
|
||||
return EPERM;
|
||||
}
|
||||
#endif /* PTE_SUPPORT_ASYNC_CANCEL */
|
||||
|
||||
if (sp == NULL
|
||||
|| (type != PTHREAD_CANCEL_DEFERRED
|
||||
&& type != PTHREAD_CANCEL_ASYNCHRONOUS))
|
||||
return EINVAL;
|
||||
|
||||
/*
|
||||
* Lock for async-cancel safety.
|
||||
*/
|
||||
(void) pthread_mutex_lock (&sp->cancelLock);
|
||||
|
||||
if (oldtype != NULL)
|
||||
*oldtype = sp->cancelType;
|
||||
|
||||
sp->cancelType = type;
|
||||
|
||||
/*
|
||||
* Check if there is a pending asynchronous cancel
|
||||
*/
|
||||
|
||||
if (sp->cancelState == PTHREAD_CANCEL_ENABLE
|
||||
&& (type == PTHREAD_CANCEL_ASYNCHRONOUS)
|
||||
&& (pte_osThreadCheckCancel(sp->threadId) == PTE_OS_INTERRUPTED) )
|
||||
{
|
||||
sp->state = PThreadStateCanceling;
|
||||
sp->cancelState = PTHREAD_CANCEL_DISABLE;
|
||||
(void) pthread_mutex_unlock (&sp->cancelLock);
|
||||
pte_throw (PTE_EPS_CANCEL);
|
||||
|
||||
/* Never reached */
|
||||
}
|
||||
|
||||
(void) pthread_mutex_unlock (&sp->cancelLock);
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_setconcurrency (int level)
|
||||
{
|
||||
if (level < 0)
|
||||
return EINVAL;
|
||||
|
||||
pte_concurrency = level;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_setschedparam (pthread_t thread, int policy,
|
||||
const struct sched_param *param)
|
||||
{
|
||||
int result;
|
||||
|
||||
/* Validate the thread id. */
|
||||
result = pthread_kill (thread, 0);
|
||||
if (0 != result)
|
||||
return result;
|
||||
|
||||
/* Validate the scheduling policy. */
|
||||
if (policy < SCHED_MIN || policy > SCHED_MAX)
|
||||
return EINVAL;
|
||||
|
||||
/* Ensure the policy is SCHED_OTHER. */
|
||||
if (policy != SCHED_OTHER)
|
||||
return ENOTSUP;
|
||||
|
||||
return (pte_setthreadpriority (thread, policy, param->sched_priority));
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
pte_setthreadpriority (pthread_t thread, int policy, int priority)
|
||||
{
|
||||
int prio;
|
||||
int result;
|
||||
pte_thread_t * tp = (pte_thread_t *) thread;
|
||||
|
||||
prio = priority;
|
||||
|
||||
/* Validate priority level. */
|
||||
if (prio < sched_get_priority_min (policy) ||
|
||||
prio > sched_get_priority_max (policy))
|
||||
return EINVAL;
|
||||
|
||||
result = pthread_mutex_lock (&tp->threadLock);
|
||||
|
||||
if (0 == result)
|
||||
{
|
||||
/* If this fails, the current priority is unchanged. */
|
||||
|
||||
if (0 != pte_osThreadSetPriority(tp->threadId, prio))
|
||||
result = EINVAL;
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Must record the thread's sched_priority as given,
|
||||
* not as finally adjusted.
|
||||
*/
|
||||
tp->sched_priority = priority;
|
||||
}
|
||||
|
||||
(void) pthread_mutex_unlock (&tp->threadLock);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int pthread_setspecific (pthread_key_t key, const void *value)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function sets the value of the thread specific
|
||||
* key in the calling thread.
|
||||
*
|
||||
* PARAMETERS
|
||||
* key
|
||||
* an instance of pthread_key_t
|
||||
* value
|
||||
* the value to set key to
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function sets the value of the thread specific
|
||||
* key in the calling thread.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully set value
|
||||
* EAGAIN could not set value
|
||||
* ENOENT SERIOUS!!
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
pthread_t self;
|
||||
int result = 0;
|
||||
|
||||
if (key != pte_selfThreadKey)
|
||||
{
|
||||
/*
|
||||
* Using pthread_self will implicitly create
|
||||
* an instance of pthread_t for the current
|
||||
* thread if one wasn't explicitly created
|
||||
*/
|
||||
self = pthread_self ();
|
||||
if (self == NULL)
|
||||
return ENOENT;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Resolve catch-22 of registering thread with selfThread
|
||||
* key
|
||||
*/
|
||||
pte_thread_t * sp = (pte_thread_t *) pthread_getspecific (pte_selfThreadKey);
|
||||
|
||||
if (sp == NULL)
|
||||
{
|
||||
if (value == NULL)
|
||||
return ENOENT;
|
||||
|
||||
self = *((pthread_t *) value);
|
||||
}
|
||||
else
|
||||
self = sp;
|
||||
}
|
||||
|
||||
result = 0;
|
||||
|
||||
if (key != NULL)
|
||||
{
|
||||
if (self != NULL && key->destructor != NULL && value != NULL)
|
||||
{
|
||||
/*
|
||||
* Only require associations if we have to
|
||||
* call user destroy routine.
|
||||
* Don't need to locate an existing association
|
||||
* when setting data to NULL since the
|
||||
* data is stored with the operating system; not
|
||||
* on the association; setting assoc to NULL short
|
||||
* circuits the search.
|
||||
*/
|
||||
ThreadKeyAssoc *assoc;
|
||||
|
||||
if (pthread_mutex_lock(&(key->keyLock)) == 0)
|
||||
{
|
||||
pte_thread_t * sp = (pte_thread_t *) self;
|
||||
|
||||
(void) pthread_mutex_lock(&(sp->threadLock));
|
||||
|
||||
assoc = (ThreadKeyAssoc *) sp->keys;
|
||||
/*
|
||||
* Locate existing association
|
||||
*/
|
||||
while (assoc != NULL)
|
||||
{
|
||||
/*
|
||||
* Association already exists
|
||||
*/
|
||||
if (assoc->key == key)
|
||||
break;
|
||||
assoc = assoc->nextKey;
|
||||
}
|
||||
|
||||
/*
|
||||
* create an association if not found
|
||||
*/
|
||||
if (assoc == NULL)
|
||||
result = pte_tkAssocCreate (sp, key);
|
||||
|
||||
(void) pthread_mutex_unlock(&(sp->threadLock));
|
||||
}
|
||||
(void) pthread_mutex_unlock(&(key->keyLock));
|
||||
}
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
if (pte_osTlsSetValue (key->key, (void *) value) != PTE_OS_OK)
|
||||
result = EAGAIN;
|
||||
}
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
Vendored
-295
@@ -1,295 +0,0 @@
|
||||
/*
|
||||
* pthread_spin.c
|
||||
*
|
||||
* Description:
|
||||
* This translation unit implements spin lock primitives.
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "pthread.h"
|
||||
#include "implement.h"
|
||||
|
||||
|
||||
int
|
||||
pthread_spin_destroy (pthread_spinlock_t * lock)
|
||||
{
|
||||
register pthread_spinlock_t s;
|
||||
int result = 0;
|
||||
|
||||
if (lock == NULL || *lock == NULL)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if ((s = *lock) != PTHREAD_SPINLOCK_INITIALIZER)
|
||||
{
|
||||
if (s->interlock == PTE_SPIN_USE_MUTEX)
|
||||
{
|
||||
result = pthread_mutex_destroy (&(s->u.mutex));
|
||||
}
|
||||
else if (PTE_SPIN_UNLOCKED !=
|
||||
PTE_ATOMIC_COMPARE_EXCHANGE (
|
||||
& (s->interlock),
|
||||
(int) PTE_OBJECT_INVALID,
|
||||
PTE_SPIN_UNLOCKED))
|
||||
{
|
||||
result = EINVAL;
|
||||
}
|
||||
|
||||
if (0 == result)
|
||||
{
|
||||
/*
|
||||
* We are relying on the application to ensure that all other threads
|
||||
* have finished with the spinlock before destroying it.
|
||||
*/
|
||||
*lock = NULL;
|
||||
(void) free (s);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* See notes in pte_spinlock_check_need_init() above also.
|
||||
*/
|
||||
|
||||
pte_osMutexLock (pte_spinlock_test_init_lock);
|
||||
|
||||
/*
|
||||
* Check again.
|
||||
*/
|
||||
if (*lock == PTHREAD_SPINLOCK_INITIALIZER)
|
||||
{
|
||||
/*
|
||||
* This is all we need to do to destroy a statically
|
||||
* initialised spinlock that has not yet been used (initialised).
|
||||
* If we get to here, another thread
|
||||
* waiting to initialise this mutex will get an EINVAL.
|
||||
*/
|
||||
*lock = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* The spinlock has been initialised while we were waiting
|
||||
* so assume it's in use.
|
||||
*/
|
||||
result = EBUSY;
|
||||
}
|
||||
|
||||
pte_osMutexUnlock(pte_spinlock_test_init_lock);
|
||||
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_spin_init (pthread_spinlock_t * lock, int pshared)
|
||||
{
|
||||
pthread_spinlock_t s;
|
||||
int cpus = 0;
|
||||
int result = 0;
|
||||
|
||||
if (lock == NULL)
|
||||
return EINVAL;
|
||||
|
||||
if (0 != pte_getprocessors (&cpus))
|
||||
{
|
||||
cpus = 1;
|
||||
}
|
||||
|
||||
if (cpus > 1)
|
||||
{
|
||||
if (pshared == PTHREAD_PROCESS_SHARED)
|
||||
{
|
||||
/*
|
||||
* Creating spinlock that can be shared between
|
||||
* processes.
|
||||
*/
|
||||
#if _POSIX_THREAD_PROCESS_SHARED >= 0
|
||||
|
||||
/*
|
||||
* Not implemented yet.
|
||||
*/
|
||||
|
||||
#error ERROR [__FILE__, line __LINE__]: Process shared spin locks are not supported yet.
|
||||
|
||||
#else
|
||||
|
||||
return ENOSYS;
|
||||
|
||||
#endif /* _POSIX_THREAD_PROCESS_SHARED */
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
s = (pthread_spinlock_t) calloc (1, sizeof (*s));
|
||||
|
||||
if (s == NULL)
|
||||
{
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
if (cpus > 1)
|
||||
{
|
||||
s->u.cpus = cpus;
|
||||
s->interlock = PTE_SPIN_UNLOCKED;
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_mutexattr_t ma;
|
||||
result = pthread_mutexattr_init (&ma);
|
||||
|
||||
if (0 == result)
|
||||
{
|
||||
ma->pshared = pshared;
|
||||
result = pthread_mutex_init (&(s->u.mutex), &ma);
|
||||
if (0 == result)
|
||||
{
|
||||
s->interlock = PTE_SPIN_USE_MUTEX;
|
||||
}
|
||||
}
|
||||
(void) pthread_mutexattr_destroy (&ma);
|
||||
}
|
||||
|
||||
if (0 == result)
|
||||
{
|
||||
*lock = s;
|
||||
}
|
||||
else
|
||||
{
|
||||
(void) free (s);
|
||||
*lock = NULL;
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_spin_lock (pthread_spinlock_t * lock)
|
||||
{
|
||||
register pthread_spinlock_t s;
|
||||
|
||||
if (NULL == lock || NULL == *lock)
|
||||
return (EINVAL);
|
||||
|
||||
if (*lock == PTHREAD_SPINLOCK_INITIALIZER)
|
||||
{
|
||||
int result;
|
||||
|
||||
if ((result = pte_spinlock_check_need_init (lock)) != 0)
|
||||
return (result);
|
||||
}
|
||||
|
||||
s = *lock;
|
||||
|
||||
while ( PTE_SPIN_LOCKED ==
|
||||
PTE_ATOMIC_COMPARE_EXCHANGE (&(s->interlock),
|
||||
PTE_SPIN_LOCKED,
|
||||
PTE_SPIN_UNLOCKED))
|
||||
{
|
||||
}
|
||||
|
||||
if (s->interlock == PTE_SPIN_LOCKED)
|
||||
return 0;
|
||||
|
||||
if (s->interlock == PTE_SPIN_USE_MUTEX)
|
||||
return pthread_mutex_lock (&(s->u.mutex));
|
||||
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
int pthread_spin_trylock (pthread_spinlock_t * lock)
|
||||
{
|
||||
register pthread_spinlock_t s;
|
||||
|
||||
if (NULL == lock || NULL == *lock)
|
||||
return (EINVAL);
|
||||
|
||||
if (*lock == PTHREAD_SPINLOCK_INITIALIZER)
|
||||
{
|
||||
int result;
|
||||
|
||||
if ((result = pte_spinlock_check_need_init (lock)) != 0)
|
||||
return (result);
|
||||
}
|
||||
|
||||
s = *lock;
|
||||
|
||||
switch ((long)
|
||||
PTE_ATOMIC_COMPARE_EXCHANGE (&(s->interlock),
|
||||
PTE_SPIN_LOCKED,
|
||||
PTE_SPIN_UNLOCKED))
|
||||
{
|
||||
case PTE_SPIN_UNLOCKED:
|
||||
return 0;
|
||||
case PTE_SPIN_LOCKED:
|
||||
return EBUSY;
|
||||
case PTE_SPIN_USE_MUTEX:
|
||||
return pthread_mutex_trylock (&(s->u.mutex));
|
||||
}
|
||||
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
int pthread_spin_unlock (pthread_spinlock_t * lock)
|
||||
{
|
||||
register pthread_spinlock_t s;
|
||||
|
||||
if (NULL == lock || NULL == *lock)
|
||||
return (EINVAL);
|
||||
|
||||
s = *lock;
|
||||
|
||||
if (s == PTHREAD_SPINLOCK_INITIALIZER)
|
||||
return EPERM;
|
||||
|
||||
switch ((long)
|
||||
PTE_ATOMIC_COMPARE_EXCHANGE (&(s->interlock),
|
||||
PTE_SPIN_UNLOCKED,
|
||||
PTE_SPIN_LOCKED))
|
||||
{
|
||||
case PTE_SPIN_LOCKED:
|
||||
return 0;
|
||||
case PTE_SPIN_UNLOCKED:
|
||||
return EPERM;
|
||||
case PTE_SPIN_USE_MUTEX:
|
||||
return pthread_mutex_unlock (&(s->u.mutex));
|
||||
}
|
||||
|
||||
return EINVAL;
|
||||
}
|
||||
Vendored
-101
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* sched_setscheduler.c
|
||||
*
|
||||
* Description:
|
||||
* POSIX thread functions that deal with thread scheduling.
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "pte_osal.h"
|
||||
|
||||
#include "pthread.h"
|
||||
#include "implement.h"
|
||||
#include "sched.h"
|
||||
|
||||
int
|
||||
sched_setscheduler (pid_t pid, int policy)
|
||||
{
|
||||
errno = EPERM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
sched_yield (void)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function indicates that the calling thread is
|
||||
* willing to give up some time slices to other threads.
|
||||
*
|
||||
* PARAMETERS
|
||||
* N/A
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function indicates that the calling thread is
|
||||
* willing to give up some time slices to other threads.
|
||||
* NOTE: Since this is part of POSIX 1003.1b
|
||||
* (realtime extensions), it is defined as returning
|
||||
* -1 if an error occurs and sets errno to the actual
|
||||
* error.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully created semaphore,
|
||||
* ENOSYS sched_yield not supported,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
pte_osThreadSleep (1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sched_get_priority_min (int policy)
|
||||
{
|
||||
return pte_osThreadGetMinPriority();
|
||||
}
|
||||
|
||||
int
|
||||
sched_get_priority_max (int policy)
|
||||
{
|
||||
return pte_osThreadGetMaxPriority();
|
||||
}
|
||||
Vendored
-137
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
* Module: sched.h
|
||||
*
|
||||
* Purpose:
|
||||
* Provides an implementation of POSIX realtime extensions
|
||||
* as defined in
|
||||
*
|
||||
* POSIX 1003.1b-1993 (POSIX.1b)
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
#ifndef _SCHED_H
|
||||
#define _SCHED_H
|
||||
|
||||
#include <pte_types.h>
|
||||
|
||||
#undef PTE_LEVEL
|
||||
|
||||
#if defined(_POSIX_SOURCE)
|
||||
#define PTE_LEVEL 0
|
||||
/* Early POSIX */
|
||||
#endif
|
||||
|
||||
#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309
|
||||
#undef PTE_LEVEL
|
||||
#define PTE_LEVEL 1
|
||||
/* Include 1b, 1c and 1d */
|
||||
#endif
|
||||
|
||||
#if defined(INCLUDE_NP)
|
||||
#undef PTE_LEVEL
|
||||
#define PTE_LEVEL 2
|
||||
/* Include Non-Portable extensions */
|
||||
#endif
|
||||
|
||||
#define PTE_LEVEL_MAX 3
|
||||
|
||||
#if !defined(PTE_LEVEL)
|
||||
#define PTE_LEVEL PTE_LEVEL_MAX
|
||||
/* Include everything */
|
||||
#endif
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
/* Thread scheduling policies */
|
||||
|
||||
enum
|
||||
{
|
||||
SCHED_OTHER = 0,
|
||||
SCHED_FIFO,
|
||||
SCHED_RR,
|
||||
SCHED_MIN = SCHED_OTHER,
|
||||
SCHED_MAX = SCHED_RR
|
||||
};
|
||||
|
||||
struct sched_param
|
||||
{
|
||||
int sched_priority;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int sched_yield (void);
|
||||
|
||||
int sched_get_priority_min (int policy);
|
||||
|
||||
int sched_get_priority_max (int policy);
|
||||
|
||||
int sched_setscheduler (pid_t pid, int policy);
|
||||
|
||||
/*
|
||||
* Note that this macro returns ENOTSUP rather than
|
||||
* ENOSYS as might be expected. However, returning ENOSYS
|
||||
* should mean that sched_get_priority_{min,max} are
|
||||
* not implemented as well as sched_rr_get_interval.
|
||||
* This is not the case, since we just don't support
|
||||
* round-robin scheduling. Therefore I have chosen to
|
||||
* return the same value as sched_setscheduler when
|
||||
* SCHED_RR is passed to it.
|
||||
*/
|
||||
#define sched_rr_get_interval(_pid, _interval) \
|
||||
( errno = ENOTSUP, (int) -1 )
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* End of extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#undef PTE_LEVEL
|
||||
#undef PTE_LEVEL_MAX
|
||||
|
||||
#endif /* !_SCHED_H */
|
||||
|
||||
Vendored
-895
@@ -1,895 +0,0 @@
|
||||
/*
|
||||
* -------------------------------------------------------------
|
||||
*
|
||||
* Module: sem.c
|
||||
*
|
||||
* Purpose:
|
||||
* Semaphores aren't actually part of the PThreads standard.
|
||||
* They are defined by the POSIX Standard:
|
||||
*
|
||||
* POSIX 1003.1b-1993 (POSIX.1b)
|
||||
*
|
||||
* -------------------------------------------------------------
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "pte_osal.h"
|
||||
|
||||
#include "pthread.h"
|
||||
#include "semaphore.h"
|
||||
#include "implement.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
sem_t sem;
|
||||
int * resultPtr;
|
||||
} sem_timedwait_cleanup_args_t;
|
||||
|
||||
static void pte_sem_wait_cleanup(void * sem)
|
||||
{
|
||||
sem_t s = (sem_t) sem;
|
||||
unsigned int timeout;
|
||||
|
||||
if (pthread_mutex_lock (&s->lock) == 0)
|
||||
{
|
||||
/*
|
||||
* If sema is destroyed do nothing, otherwise:-
|
||||
* If the sema is posted between us being cancelled and us locking
|
||||
* the sema again above then we need to consume that post but cancel
|
||||
* anyway. If we don't get the semaphore we indicate that we're no
|
||||
* longer waiting.
|
||||
*/
|
||||
timeout = 0;
|
||||
if (pte_osSemaphorePend(s->sem, &timeout) != PTE_OS_OK)
|
||||
{
|
||||
++s->value;
|
||||
|
||||
/*
|
||||
* Don't release the W32 sema, it doesn't need adjustment
|
||||
* because it doesn't record the number of waiters.
|
||||
*/
|
||||
|
||||
}
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
}
|
||||
}
|
||||
|
||||
static void pte_sem_timedwait_cleanup (void * args)
|
||||
{
|
||||
sem_timedwait_cleanup_args_t * a = (sem_timedwait_cleanup_args_t *)args;
|
||||
sem_t s = a->sem;
|
||||
|
||||
if (pthread_mutex_lock (&s->lock) == 0)
|
||||
{
|
||||
/*
|
||||
* We either timed out or were cancelled.
|
||||
* If someone has posted between then and now we try to take the semaphore.
|
||||
* Otherwise the semaphore count may be wrong after we
|
||||
* return. In the case of a cancellation, it is as if we
|
||||
* were cancelled just before we return (after taking the semaphore)
|
||||
* which is ok.
|
||||
*/
|
||||
unsigned int timeout = 0;
|
||||
if (pte_osSemaphorePend(s->sem, &timeout) == PTE_OS_OK)
|
||||
{
|
||||
/* We got the semaphore on the second attempt */
|
||||
*(a->resultPtr) = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Indicate we're no longer waiting */
|
||||
s->value++;
|
||||
|
||||
/*
|
||||
* Don't release the OS sema, it doesn't need adjustment
|
||||
* because it doesn't record the number of waiters.
|
||||
*/
|
||||
|
||||
}
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
}
|
||||
}
|
||||
|
||||
int sem_close (sem_t * sem)
|
||||
{
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int sem_destroy (sem_t * sem)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function destroys an unnamed semaphore.
|
||||
*
|
||||
* PARAMETERS
|
||||
* sem
|
||||
* pointer to an instance of sem_t
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function destroys an unnamed semaphore.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully destroyed semaphore,
|
||||
* -1 failed, error in errno
|
||||
* ERRNO
|
||||
* EINVAL 'sem' is not a valid semaphore,
|
||||
* ENOSYS semaphores are not supported,
|
||||
* EBUSY threads (or processes) are currently
|
||||
* blocked on 'sem'
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
sem_t s = NULL;
|
||||
|
||||
if (sem == NULL || *sem == NULL)
|
||||
result = EINVAL;
|
||||
else
|
||||
{
|
||||
s = *sem;
|
||||
|
||||
if ((result = pthread_mutex_lock (&s->lock)) == 0)
|
||||
{
|
||||
if (s->value < 0)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
result = EBUSY;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* There are no threads currently blocked on this semaphore. */
|
||||
pte_osResult osResult = pte_osSemaphoreDelete(s->sem);
|
||||
|
||||
if (osResult != PTE_OS_OK)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
result = EINVAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Invalidate the semaphore handle when we have the lock.
|
||||
* Other sema operations should test this after acquiring the lock
|
||||
* to check that the sema is still valid, i.e. before performing any
|
||||
* operations. This may only be necessary before the sema op routine
|
||||
* returns so that the routine can return EINVAL - e.g. if setting
|
||||
* s->value to SEM_VALUE_MAX below does force a fall-through.
|
||||
*/
|
||||
*sem = NULL;
|
||||
|
||||
/* Prevent anyone else actually waiting on or posting this sema.
|
||||
*/
|
||||
s->value = SEM_VALUE_MAX;
|
||||
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
|
||||
do
|
||||
{
|
||||
/* Give other threads a chance to run and exit any sema op
|
||||
* routines. Due to the SEM_VALUE_MAX value, if sem_post or
|
||||
* sem_wait were blocked by us they should fall through.
|
||||
*/
|
||||
pte_osThreadSleep(1);
|
||||
}
|
||||
while (pthread_mutex_destroy (&s->lock) == EBUSY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
errno = result;
|
||||
return -1;
|
||||
}
|
||||
|
||||
free (s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sem_getvalue (sem_t * sem, int *sval)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function stores the current count value of the
|
||||
* semaphore.
|
||||
* RESULTS
|
||||
*
|
||||
* Return value
|
||||
*
|
||||
* 0 sval has been set.
|
||||
* -1 failed, error in errno
|
||||
*
|
||||
* in global errno
|
||||
*
|
||||
* EINVAL 'sem' is not a valid semaphore,
|
||||
* ENOSYS this function is not supported,
|
||||
*
|
||||
*
|
||||
* PARAMETERS
|
||||
*
|
||||
* sem pointer to an instance of sem_t
|
||||
*
|
||||
* sval pointer to int.
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function stores the current count value of the semaphore
|
||||
* pointed to by sem in the int pointed to by sval.
|
||||
*/
|
||||
{
|
||||
if (sem == NULL || *sem == NULL || sval == NULL)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
long value;
|
||||
register sem_t s = *sem;
|
||||
int result = 0;
|
||||
|
||||
if ((result = pthread_mutex_lock(&s->lock)) == 0)
|
||||
{
|
||||
if (*sem == NULL)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
value = s->value;
|
||||
(void) pthread_mutex_unlock(&s->lock);
|
||||
*sval = value;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
sem_init (sem_t * sem, int pshared, unsigned int value)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function initializes a semaphore. The
|
||||
* initial value of the semaphore is 'value'
|
||||
*
|
||||
* PARAMETERS
|
||||
* sem
|
||||
* pointer to an instance of sem_t
|
||||
*
|
||||
* pshared
|
||||
* if zero, this semaphore may only be shared between
|
||||
* threads in the same process.
|
||||
* if nonzero, the semaphore can be shared between
|
||||
* processes
|
||||
*
|
||||
* value
|
||||
* initial value of the semaphore counter
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function initializes a semaphore. The
|
||||
* initial value of the semaphore is set to 'value'.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully created semaphore,
|
||||
* -1 failed, error in errno
|
||||
* ERRNO
|
||||
* EINVAL 'sem' is not a valid semaphore, or
|
||||
* 'value' >= SEM_VALUE_MAX
|
||||
* ENOMEM out of memory,
|
||||
* ENOSPC a required resource has been exhausted,
|
||||
* ENOSYS semaphores are not supported,
|
||||
* EPERM the process lacks appropriate privilege
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
sem_t s = NULL;
|
||||
|
||||
if (pshared != 0)
|
||||
{
|
||||
/*
|
||||
* Creating a semaphore that can be shared between
|
||||
* processes
|
||||
*/
|
||||
result = EPERM;
|
||||
}
|
||||
else if (value > (unsigned int)SEM_VALUE_MAX)
|
||||
{
|
||||
result = EINVAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
s = (sem_t) calloc (1, sizeof (*s));
|
||||
|
||||
if (NULL == s)
|
||||
{
|
||||
result = ENOMEM;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
s->value = value;
|
||||
if (pthread_mutex_init(&s->lock, NULL) == 0)
|
||||
{
|
||||
|
||||
pte_osResult osResult = pte_osSemaphoreCreate(0, &s->sem);
|
||||
|
||||
|
||||
|
||||
if (osResult != PTE_OS_OK)
|
||||
{
|
||||
(void) pthread_mutex_destroy(&s->lock);
|
||||
result = ENOSPC;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
result = ENOSPC;
|
||||
}
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
free(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
errno = result;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*sem = s;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sem_open (const char *name, int oflag, mode_t mode, unsigned int value)
|
||||
{
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
sem_post (sem_t * sem)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function posts a wakeup to a semaphore.
|
||||
*
|
||||
* PARAMETERS
|
||||
* sem
|
||||
* pointer to an instance of sem_t
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function posts a wakeup to a semaphore. If there
|
||||
* are waiting threads (or processes), one is awakened;
|
||||
* otherwise, the semaphore value is incremented by one.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully posted semaphore,
|
||||
* -1 failed, error in errno
|
||||
* ERRNO
|
||||
* EINVAL 'sem' is not a valid semaphore,
|
||||
* ENOSYS semaphores are not supported,
|
||||
* ERANGE semaphore count is too big
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
sem_t s = *sem;
|
||||
|
||||
if (s == NULL)
|
||||
result = EINVAL;
|
||||
else if ((result = pthread_mutex_lock (&s->lock)) == 0)
|
||||
{
|
||||
/* See sem_destroy.c
|
||||
*/
|
||||
if (*sem == NULL)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
result = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (s->value < SEM_VALUE_MAX)
|
||||
{
|
||||
pte_osResult osResult = pte_osSemaphorePost(s->sem, 1);
|
||||
|
||||
if (++s->value <= 0
|
||||
&& (osResult != PTE_OS_OK))
|
||||
{
|
||||
s->value--;
|
||||
result = EINVAL;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
result = ERANGE;
|
||||
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
}
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
errno = result;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sem_post_multiple (sem_t * sem, int count)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function posts multiple wakeups to a semaphore.
|
||||
*
|
||||
* PARAMETERS
|
||||
* sem
|
||||
* pointer to an instance of sem_t
|
||||
*
|
||||
* count
|
||||
* counter, must be greater than zero.
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function posts multiple wakeups to a semaphore. If there
|
||||
* are waiting threads (or processes), n <= count are awakened;
|
||||
* the semaphore value is incremented by count - n.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully posted semaphore,
|
||||
* -1 failed, error in errno
|
||||
* ERRNO
|
||||
* EINVAL 'sem' is not a valid semaphore
|
||||
* or count is less than or equal to zero.
|
||||
* ERANGE semaphore count is too big
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
long waiters;
|
||||
sem_t s = *sem;
|
||||
|
||||
if (s == NULL || count <= 0)
|
||||
result = EINVAL;
|
||||
else if ((result = pthread_mutex_lock (&s->lock)) == 0)
|
||||
{
|
||||
/* See sem_destroy.c
|
||||
*/
|
||||
if (*sem == NULL)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
result = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (s->value <= (SEM_VALUE_MAX - count))
|
||||
{
|
||||
waiters = -s->value;
|
||||
s->value += count;
|
||||
if (waiters > 0)
|
||||
{
|
||||
|
||||
pte_osSemaphorePost(s->sem, (waiters<=count)?waiters:count);
|
||||
result = 0;
|
||||
}
|
||||
/*
|
||||
else
|
||||
{
|
||||
s->value -= count;
|
||||
result = EINVAL;
|
||||
}
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
result = ERANGE;
|
||||
}
|
||||
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
}
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
errno = result;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sem_timedwait (sem_t * sem, const struct timespec *abstime)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function waits on a semaphore possibly until
|
||||
* 'abstime' time.
|
||||
*
|
||||
* PARAMETERS
|
||||
* sem
|
||||
* pointer to an instance of sem_t
|
||||
*
|
||||
* abstime
|
||||
* pointer to an instance of struct timespec
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function waits on a semaphore. If the
|
||||
* semaphore value is greater than zero, it decreases
|
||||
* its value by one. If the semaphore value is zero, then
|
||||
* the calling thread (or process) is blocked until it can
|
||||
* successfully decrease the value or until interrupted by
|
||||
* a signal.
|
||||
*
|
||||
* If 'abstime' is a NULL pointer then this function will
|
||||
* block until it can successfully decrease the value or
|
||||
* until interrupted by a signal.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully decreased semaphore,
|
||||
* -1 failed, error in errno
|
||||
* ERRNO
|
||||
* EINVAL 'sem' is not a valid semaphore,
|
||||
* ENOSYS semaphores are not supported,
|
||||
* EINTR the function was interrupted by a signal,
|
||||
* EDEADLK a deadlock condition was detected.
|
||||
* ETIMEDOUT abstime elapsed before success.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
sem_t s = *sem;
|
||||
|
||||
pthread_testcancel();
|
||||
|
||||
if (sem == NULL)
|
||||
result = EINVAL;
|
||||
else
|
||||
{
|
||||
unsigned int milliseconds;
|
||||
unsigned int *pTimeout;
|
||||
|
||||
if (abstime == NULL)
|
||||
{
|
||||
pTimeout = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Calculate timeout as milliseconds from current system time.
|
||||
*/
|
||||
milliseconds = pte_relmillisecs (abstime);
|
||||
pTimeout = &milliseconds;
|
||||
}
|
||||
|
||||
if ((result = pthread_mutex_lock (&s->lock)) == 0)
|
||||
{
|
||||
int v;
|
||||
|
||||
/* See sem_destroy.c
|
||||
*/
|
||||
if (*sem == NULL)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
v = --s->value;
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
|
||||
if (v < 0)
|
||||
{
|
||||
|
||||
{
|
||||
sem_timedwait_cleanup_args_t cleanup_args;
|
||||
|
||||
cleanup_args.sem = s;
|
||||
cleanup_args.resultPtr = &result;
|
||||
|
||||
/* Must wait */
|
||||
pthread_cleanup_push(pte_sem_timedwait_cleanup, (void *) &cleanup_args);
|
||||
|
||||
result = pte_cancellable_wait(s->sem,pTimeout);
|
||||
|
||||
pthread_cleanup_pop(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
|
||||
errno = result;
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sem_trywait (sem_t * sem)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function tries to wait on a semaphore.
|
||||
*
|
||||
* PARAMETERS
|
||||
* sem
|
||||
* pointer to an instance of sem_t
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function tries to wait on a semaphore. If the
|
||||
* semaphore value is greater than zero, it decreases
|
||||
* its value by one. If the semaphore value is zero, then
|
||||
* this function returns immediately with the error EAGAIN
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully decreased semaphore,
|
||||
* -1 failed, error in errno
|
||||
* ERRNO
|
||||
* EAGAIN the semaphore was already locked,
|
||||
* EINVAL 'sem' is not a valid semaphore,
|
||||
* ENOTSUP sem_trywait is not supported,
|
||||
* EINTR the function was interrupted by a signal,
|
||||
* EDEADLK a deadlock condition was detected.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
sem_t s = *sem;
|
||||
|
||||
if (s == NULL)
|
||||
result = EINVAL;
|
||||
else if ((result = pthread_mutex_lock (&s->lock)) == 0)
|
||||
{
|
||||
/* See sem_destroy.c
|
||||
*/
|
||||
if (*sem == NULL)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (s->value > 0)
|
||||
s->value--;
|
||||
else
|
||||
result = EAGAIN;
|
||||
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
}
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
errno = result;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sem_unlink (const char *name)
|
||||
{
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int sem_wait (sem_t * sem)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function waits on a semaphore.
|
||||
*
|
||||
* PARAMETERS
|
||||
* sem
|
||||
* pointer to an instance of sem_t
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function waits on a semaphore. If the
|
||||
* semaphore value is greater than zero, it decreases
|
||||
* its value by one. If the semaphore value is zero, then
|
||||
* the calling thread (or process) is blocked until it can
|
||||
* successfully decrease the value or until interrupted by
|
||||
* a signal.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully decreased semaphore,
|
||||
* -1 failed, error in errno
|
||||
* ERRNO
|
||||
* EINVAL 'sem' is not a valid semaphore,
|
||||
* ENOSYS semaphores are not supported,
|
||||
* EINTR the function was interrupted by a signal,
|
||||
* EDEADLK a deadlock condition was detected.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
sem_t s = *sem;
|
||||
|
||||
pthread_testcancel();
|
||||
|
||||
if (s == NULL)
|
||||
{
|
||||
result = EINVAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((result = pthread_mutex_lock (&s->lock)) == 0)
|
||||
{
|
||||
int v;
|
||||
|
||||
/* See sem_destroy.c
|
||||
*/
|
||||
if (*sem == NULL)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
v = --s->value;
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
|
||||
if (v < 0)
|
||||
{
|
||||
/* Must wait */
|
||||
pthread_cleanup_push(pte_sem_wait_cleanup, (void *) s);
|
||||
result = pte_cancellable_wait(s->sem,NULL);
|
||||
/* Cleanup if we're canceled or on any other error */
|
||||
pthread_cleanup_pop(result);
|
||||
|
||||
// Wait was cancelled, indicate that we're no longer waiting on this semaphore.
|
||||
/*
|
||||
if (result == PTE_OS_INTERRUPTED)
|
||||
{
|
||||
result = EINTR;
|
||||
++s->value;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
errno = result;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
} /* sem_wait */
|
||||
|
||||
|
||||
int
|
||||
sem_wait_nocancel (sem_t * sem)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function waits on a semaphore, and doesn't
|
||||
* allow cancellation.
|
||||
*
|
||||
* PARAMETERS
|
||||
* sem
|
||||
* pointer to an instance of sem_t
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function waits on a semaphore. If the
|
||||
* semaphore value is greater than zero, it decreases
|
||||
* its value by one. If the semaphore value is zero, then
|
||||
* the calling thread (or process) is blocked until it can
|
||||
* successfully decrease the value or until interrupted by
|
||||
* a signal.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully decreased semaphore,
|
||||
* -1 failed, error in errno
|
||||
* ERRNO
|
||||
* EINVAL 'sem' is not a valid semaphore,
|
||||
* ENOSYS semaphores are not supported,
|
||||
* EINTR the function was interrupted by a signal,
|
||||
* EDEADLK a deadlock condition was detected.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
sem_t s = *sem;
|
||||
|
||||
pthread_testcancel();
|
||||
|
||||
if (s == NULL)
|
||||
{
|
||||
result = EINVAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((result = pthread_mutex_lock (&s->lock)) == 0)
|
||||
{
|
||||
int v;
|
||||
|
||||
/* See sem_destroy.c
|
||||
*/
|
||||
if (*sem == NULL)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
v = --s->value;
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
|
||||
if (v < 0)
|
||||
{
|
||||
pte_osSemaphorePend(s->sem, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
errno = result;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Vendored
-114
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Module: semaphore.h
|
||||
*
|
||||
* Purpose:
|
||||
* Semaphores aren't actually part of the PThreads standard.
|
||||
* They are defined by the POSIX Standard:
|
||||
*
|
||||
* POSIX 1003.1b-1993 (POSIX.1b)
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: [email protected]
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#if !defined( SEMAPHORE_H )
|
||||
#define SEMAPHORE_H
|
||||
|
||||
#if defined(_POSIX_SOURCE)
|
||||
#define PTE_LEVEL 0
|
||||
/* Early POSIX */
|
||||
#endif
|
||||
|
||||
#if defined(INCLUDE_NP)
|
||||
#undef PTE_LEVEL
|
||||
#define PTE_LEVEL 2
|
||||
/* Include Non-Portable extensions */
|
||||
#endif
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
#define _POSIX_SEMAPHORES
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
typedef struct sem_t_ * sem_t;
|
||||
|
||||
int sem_init (sem_t * sem,
|
||||
int pshared,
|
||||
unsigned int value);
|
||||
|
||||
int sem_destroy (sem_t * sem);
|
||||
|
||||
int sem_trywait (sem_t * sem);
|
||||
|
||||
int sem_wait (sem_t * sem);
|
||||
|
||||
int sem_timedwait (sem_t * sem,
|
||||
const struct timespec * abstime);
|
||||
|
||||
int sem_post (sem_t * sem);
|
||||
|
||||
int sem_post_multiple (sem_t * sem,
|
||||
int count);
|
||||
|
||||
int sem_open (const char * name,
|
||||
int oflag,
|
||||
mode_t mode,
|
||||
unsigned int value);
|
||||
|
||||
int sem_close (sem_t * sem);
|
||||
|
||||
int sem_unlink (const char * name);
|
||||
|
||||
int sem_getvalue (sem_t * sem,
|
||||
int * sval);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* End of extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* !SEMAPHORE_H */
|
||||
Vendored
+50
@@ -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
|
||||
|
||||
Vendored
+69
-397
@@ -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
@@ -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
@@ -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 */
|
||||
Vendored
+115
@@ -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 */
|
||||
Vendored
+32
@@ -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
|
||||
|
||||
Vendored
+48
@@ -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 */
|
||||
Vendored
+18
-10
@@ -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 */
|
||||
Vendored
+146
@@ -0,0 +1,146 @@
|
||||
#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* runtime, 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,
|
||||
RC_RUNTIME_EVENT_ACHIEVEMENT_UNPRIMED
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
typedef int (*rc_runtime_validate_address_t)(unsigned address);
|
||||
void rc_runtime_validate_addresses(rc_runtime_t* runtime, rc_runtime_event_handler_t event_handler, rc_runtime_validate_address_t validate_handler);
|
||||
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
@@ -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 */
|
||||
Vendored
+3
-508
@@ -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 */
|
||||
+1035
File diff suppressed because it is too large.
Load diff
+70
@@ -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
@@ -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);
|
||||
}
|
||||
Vendored
+150
@@ -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);
|
||||
}
|
||||
Vendored
+120
-14
@@ -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";
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
||||
#include "compat.h"
|
||||
#include "rc_compat.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
+105
-63
@@ -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 {
|
||||
@@ -205,10 +239,18 @@ int rc_evaluate_condition_value(rc_condition_t* self, rc_eval_state_t* eval_stat
|
||||
|
||||
switch (self->oper) {
|
||||
case RC_OPERATOR_MULT:
|
||||
if (self->operand2.type == RC_OPERAND_FP)
|
||||
if (self->operand2.type == RC_OPERAND_FP) {
|
||||
value = (int)((double)value * self->operand2.value.dbl);
|
||||
else
|
||||
}
|
||||
else {
|
||||
/* the c standard for unsigned multiplication is well defined as non-overflowing truncation
|
||||
* to the type's size. this allows negative multiplication through twos-complements. i.e.
|
||||
* 1 * -1 (0xFFFFFFFF) = 0xFFFFFFFF = -1
|
||||
* 3 * -2 (0xFFFFFFFE) = 0x2FFFFFFFA & 0xFFFFFFFF = 0xFFFFFFFA = -6
|
||||
* 10 * -5 (0xFFFFFFFB) = 0x9FFFFFFCE & 0xFFFFFFFF = 0xFFFFFFCE = -50
|
||||
*/
|
||||
value *= rc_evaluate_operand(&self->operand2, eval_state);
|
||||
}
|
||||
break;
|
||||
|
||||
case RC_OPERATOR_DIV:
|
||||
|
||||
Vendored
+109
-22
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+9
-5
@@ -1,4 +1,3 @@
|
||||
#include "rcheevos.h"
|
||||
#include "rc_consoles.h"
|
||||
|
||||
#include <ctype.h>
|
||||
@@ -339,10 +338,14 @@ static const rc_memory_region_t _rc_memory_regions_intellivision[] = {
|
||||
static const rc_memory_regions_t rc_memory_regions_intellivision = { _rc_memory_regions_intellivision, 9 };
|
||||
|
||||
/* ===== Magnavox Odyssey 2 ===== */
|
||||
/* https://sudonull.com/post/76885-Architecture-and-programming-Philips-Videopac-Magnavox-Odyssey-2 */
|
||||
static const rc_memory_region_t _rc_memory_regions_magnavox_odyssey_2[] = {
|
||||
{ 0x000000U, 0x00003FU, 0x000040U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
|
||||
/* Internal and external RAMs are reachable using unique instructions.
|
||||
* The real addresses provided are virtual and for mapping purposes only. */
|
||||
{ 0x000000U, 0x00003FU, 0x000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Internal RAM" },
|
||||
{ 0x000040U, 0x00013FU, 0x000040U, RC_MEMORY_TYPE_SYSTEM_RAM, "External RAM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_magnavox_odyssey_2 = { _rc_memory_regions_magnavox_odyssey_2, 1 };
|
||||
static const rc_memory_regions_t rc_memory_regions_magnavox_odyssey_2 = { _rc_memory_regions_magnavox_odyssey_2, 2 };
|
||||
|
||||
/* ===== Master System ===== */
|
||||
/* http://www.smspower.org/Development/MemoryMap */
|
||||
@@ -375,8 +378,9 @@ static const rc_memory_regions_t rc_memory_regions_msx = { _rc_memory_regions_ms
|
||||
/* ===== Neo Geo Pocket ===== */
|
||||
/* http://neopocott.emuunlim.com/docs/tech-11.txt */
|
||||
static const rc_memory_region_t _rc_memory_regions_neo_geo_pocket[] = {
|
||||
/* MednafenNGP exposes 16KB, but the doc suggests there's 24-32KB */
|
||||
{ 0x000000U, 0x003FFFU, 0x000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
|
||||
/* The docs suggest there's Work RAM exposed from $0000-$6FFF, Sound RAM from $7000-$7FFF, and Video
|
||||
* RAM from $8000-$BFFF, but both MednafenNGP and FBNeo only expose system RAM from $4000-$7FFF */
|
||||
{ 0x000000U, 0x003FFFU, 0x004000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_neo_geo_pocket = { _rc_memory_regions_neo_geo_pocket, 1 };
|
||||
|
||||
|
||||
Vendored
+2
-2
@@ -1,6 +1,6 @@
|
||||
#include "internal.h"
|
||||
#include "rc_internal.h"
|
||||
|
||||
#include "compat.h"
|
||||
#include "rc_compat.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
-111
@@ -1,111 +0,0 @@
|
||||
#ifndef INTERNAL_H
|
||||
#define INTERNAL_H
|
||||
|
||||
#include "rcheevos.h"
|
||||
|
||||
#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_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_trigger_t)
|
||||
RC_ALLOW_ALIGN(rc_value_t)
|
||||
RC_ALLOW_ALIGN(char)
|
||||
|
||||
#define RC_ALIGNOF(T) (sizeof(struct __align_ ## T) - sizeof(T))
|
||||
|
||||
#define RC_ALLOC(t, p) ((t*)rc_alloc((p)->buffer, &(p)->offset, sizeof(t), RC_ALIGNOF(t), &(p)->scratch))
|
||||
|
||||
typedef struct {
|
||||
rc_memref_t memref_buffer[16];
|
||||
rc_memref_t *memref;
|
||||
int memref_count;
|
||||
int memref_size;
|
||||
|
||||
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;
|
||||
}
|
||||
rc_scratch_t;
|
||||
|
||||
typedef struct {
|
||||
unsigned add_value; /* AddSource/SubSource */
|
||||
unsigned add_hits; /* AddHits */
|
||||
unsigned add_address; /* AddAddress */
|
||||
|
||||
rc_peek_t peek;
|
||||
void* peek_userdata;
|
||||
lua_State* L;
|
||||
|
||||
unsigned measured_value; /* Measured */
|
||||
char was_reset; /* ResetIf triggered */
|
||||
char has_hits; /* one of more hit counts is non-zero */
|
||||
char primed; /* true if all non-Trigger conditions are true */
|
||||
char measured_from_hits; /* true if the measured_value came from a condition's hit count */
|
||||
}
|
||||
rc_eval_state_t;
|
||||
|
||||
typedef struct {
|
||||
int offset;
|
||||
|
||||
lua_State* L;
|
||||
int funcs_ndx;
|
||||
|
||||
void* buffer;
|
||||
rc_scratch_t scratch;
|
||||
|
||||
rc_memref_value_t** first_memref;
|
||||
|
||||
unsigned measured_target;
|
||||
}
|
||||
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_destroy_parse_state(rc_parse_state_t* parse);
|
||||
|
||||
void* rc_alloc(void* pointer, int* offset, int size, int alignment, rc_scratch_t* scratch);
|
||||
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);
|
||||
|
||||
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);
|
||||
int rc_test_condset(rc_condset_t* self, rc_eval_state_t* eval_state);
|
||||
void rc_reset_condset(rc_condset_t* self);
|
||||
|
||||
rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse, int is_indirect);
|
||||
int rc_test_condition(rc_condition_t* self, rc_eval_state_t* eval_state);
|
||||
int rc_evaluate_condition_value(rc_condition_t* self, rc_eval_state_t* eval_state);
|
||||
|
||||
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);
|
||||
|
||||
void rc_parse_value_internal(rc_value_t* self, const char** memaddr, rc_parse_state_t* parse);
|
||||
|
||||
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);
|
||||
|
||||
#endif /* INTERNAL_H */
|
||||
Vendored
+15
-6
@@ -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);
|
||||
}
|
||||
Vendored
+197
-168
@@ -1,244 +1,273 @@
|
||||
#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;
|
||||
|
||||
/* 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;
|
||||
|
||||
memref_value->next = indirect_memref_value;
|
||||
}
|
||||
|
||||
return memref_value;
|
||||
return memref;
|
||||
}
|
||||
|
||||
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);
|
||||
int rc_parse_memref(const char** memaddr, char* size, unsigned* address) {
|
||||
const char* aux = *memaddr;
|
||||
char* end;
|
||||
unsigned long value;
|
||||
|
||||
return rc_alloc_memref_value_constuct_mode(parse, address, size, is_indirect);
|
||||
}
|
||||
if (*aux++ != '0')
|
||||
return RC_INVALID_MEMORY_OPERAND;
|
||||
|
||||
static unsigned rc_memref_get_value(rc_memref_t* self, rc_peek_t peek, void* ud) {
|
||||
unsigned value;
|
||||
if (*aux != 'x' && *aux != 'X')
|
||||
return RC_INVALID_MEMORY_OPERAND;
|
||||
aux++;
|
||||
|
||||
if (!peek)
|
||||
return 0;
|
||||
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;
|
||||
|
||||
switch (self->size)
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static const unsigned char rc_bits_set[16] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 };
|
||||
|
||||
unsigned rc_transform_memref_value(unsigned value, char size)
|
||||
{
|
||||
switch (size)
|
||||
{
|
||||
case RC_MEMSIZE_BIT_0:
|
||||
value = (peek(self->address, 1, ud) >> 0) & 1;
|
||||
value = (value >> 0) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_1:
|
||||
value = (peek(self->address, 1, ud) >> 1) & 1;
|
||||
value = (value >> 1) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_2:
|
||||
value = (peek(self->address, 1, ud) >> 2) & 1;
|
||||
value = (value >> 2) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_3:
|
||||
value = (peek(self->address, 1, ud) >> 3) & 1;
|
||||
value = (value >> 3) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_4:
|
||||
value = (peek(self->address, 1, ud) >> 4) & 1;
|
||||
value = (value >> 4) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_5:
|
||||
value = (peek(self->address, 1, ud) >> 5) & 1;
|
||||
value = (value >> 5) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_6:
|
||||
value = (peek(self->address, 1, ud) >> 6) & 1;
|
||||
value = (value >> 6) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_7:
|
||||
value = (peek(self->address, 1, ud) >> 7) & 1;
|
||||
value = (value >> 7) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_LOW:
|
||||
value = peek(self->address, 1, ud) & 0x0f;
|
||||
value = value & 0x0f;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_HIGH:
|
||||
value = (peek(self->address, 1, ud) >> 4) & 0x0f;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_8_BITS:
|
||||
value = peek(self->address, 1, ud);
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_16_BITS:
|
||||
value = peek(self->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 = (value >> 4) & 0x0f;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_32_BITS:
|
||||
value = peek(self->address, 4, ud);
|
||||
case RC_MEMSIZE_BITCOUNT:
|
||||
value = rc_bits_set[(value & 0x0F)]
|
||||
+ rc_bits_set[((value >> 4) & 0x0F)];
|
||||
break;
|
||||
|
||||
default:
|
||||
value = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
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;
|
||||
char rc_memref_shared_size(char size)
|
||||
{
|
||||
switch (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_transform_memref_value */
|
||||
return RC_MEMSIZE_8_BITS;
|
||||
|
||||
default:
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
void rc_update_memref_values(rc_memref_value_t* memref, 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 (size)
|
||||
{
|
||||
case RC_MEMSIZE_8_BITS:
|
||||
value = peek(address, 1, ud);
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_16_BITS:
|
||||
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(address, 4, ud) & 0x00FFFFFF;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_32_BITS:
|
||||
value = peek(address, 4, ud);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (rc_memref_shared_size(size) == RC_MEMSIZE_8_BITS)
|
||||
{
|
||||
value = peek(address, 1, ud);
|
||||
value = rc_transform_memref_value(value, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
value = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
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_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;
|
||||
}
|
||||
}
|
||||
Vendored
+40
-125
@@ -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,46 @@ 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;
|
||||
break;
|
||||
|
||||
default:
|
||||
return RC_INVALID_MEMORY_OPERAND;
|
||||
}
|
||||
|
||||
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);
|
||||
size = rc_memref_shared_size(self->size);
|
||||
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 +162,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 +197,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 +210,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,14 +263,24 @@ static int rc_luapeek(lua_State* L) {
|
||||
|
||||
#endif /* RC_DISABLE_LUA */
|
||||
|
||||
static const unsigned char rc_bits_set[16] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 };
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
|
||||
#ifndef RC_DISABLE_LUA
|
||||
rc_luapeek_t luapeek;
|
||||
#endif /* RC_DISABLE_LUA */
|
||||
|
||||
unsigned value = 0;
|
||||
unsigned value;
|
||||
|
||||
/* step 1: read memory */
|
||||
switch (self->type) {
|
||||
@@ -324,8 +292,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,69 +320,13 @@ 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;
|
||||
}
|
||||
|
||||
/* step 2: mask off appropriate bits */
|
||||
switch (self->size)
|
||||
{
|
||||
case RC_MEMSIZE_BIT_0:
|
||||
value = (value >> 0) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_1:
|
||||
value = (value >> 1) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_2:
|
||||
value = (value >> 2) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_3:
|
||||
value = (value >> 3) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_4:
|
||||
value = (value >> 4) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_5:
|
||||
value = (value >> 5) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_6:
|
||||
value = (value >> 6) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_7:
|
||||
value = (value >> 7) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_LOW:
|
||||
value = value & 0x0f;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_HIGH:
|
||||
value = (value >> 4) & 0x0f;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BITCOUNT:
|
||||
value = rc_bits_set[(value & 0x0F)]
|
||||
+ rc_bits_set[((value >> 4) & 0x0F)];
|
||||
break;
|
||||
}
|
||||
value = rc_transform_memref_value(value, self->size);
|
||||
|
||||
/* step 3: apply logic */
|
||||
switch (self->type)
|
||||
@@ -443,6 +356,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 +393,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.
+151
@@ -0,0 +1,151 @@
|
||||
#ifndef INTERNAL_H
|
||||
#define INTERNAL_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_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, 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_scratch_buffer_t buffer;
|
||||
rc_scratch_string_t* strings;
|
||||
|
||||
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 */
|
||||
int add_hits; /* AddHits */
|
||||
unsigned add_address; /* AddAddress */
|
||||
|
||||
rc_peek_t peek;
|
||||
void* peek_userdata;
|
||||
lua_State* L;
|
||||
|
||||
unsigned measured_value; /* Measured */
|
||||
char was_reset; /* ResetIf triggered */
|
||||
char has_hits; /* one of more hit counts is non-zero */
|
||||
char primed; /* true if all non-Trigger conditions are true */
|
||||
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;
|
||||
|
||||
typedef struct {
|
||||
int offset;
|
||||
|
||||
lua_State* L;
|
||||
int funcs_ndx;
|
||||
|
||||
void* buffer;
|
||||
rc_scratch_t scratch;
|
||||
|
||||
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_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, 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_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);
|
||||
char rc_memref_shared_size(char size);
|
||||
unsigned rc_transform_memref_value(unsigned value, char size);
|
||||
|
||||
void rc_parse_trigger_internal(rc_trigger_t* self, const char** memaddr, rc_parse_state_t* parse);
|
||||
int rc_trigger_state_active(int state);
|
||||
|
||||
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);
|
||||
|
||||
rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse, int is_indirect);
|
||||
int rc_test_condition(rc_condition_t* self, rc_eval_state_t* eval_state);
|
||||
int rc_evaluate_condition_value(rc_condition_t* self, rc_eval_state_t* eval_state);
|
||||
|
||||
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
@@ -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;
|
||||
size_t 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 ®ions->data[i][address];
|
||||
}
|
||||
|
||||
address -= (unsigned)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%s",
|
||||
(unsigned)(desc - mmap->descriptors) + 1, (int)offset, desc->ptr ? "" : " [no pointer]");
|
||||
|
||||
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 += (unsigned)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));
|
||||
}
|
||||
Loaded 100 of 363 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user