mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-13 04:51:38 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c226bd87f4 | ||
|
|
c0b7c34264 | ||
|
|
5cf7b57064 | ||
|
|
adf6a7ff20 | ||
|
|
5a7cebf7da | ||
|
|
4a8fdaed84 | ||
|
|
4441219df9 | ||
|
|
750ad9aa7b | ||
|
|
0d7a028ce3 | ||
|
|
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 | ||
|
|
bc23d070de | ||
|
|
951ea6baaf | ||
|
|
84ab14cb83 | ||
|
|
019130298a | ||
|
|
99917368b4 | ||
|
|
5f12edaae4 | ||
|
|
8eb8b44e83 | ||
|
|
9c534c3550 | ||
|
|
10c1361751 | ||
|
|
2153cd8e2c | ||
|
|
95cea8cc5d | ||
|
|
02630d998e | ||
|
|
77d04af845 | ||
|
|
42e01acfa1 | ||
|
|
b2faff6bf9 | ||
|
|
a58236d69d | ||
|
|
3bc2758d51 | ||
|
|
40f6f7b803 | ||
|
|
b1a7d3e995 | ||
|
|
7fc636c623 | ||
|
|
2c5c4647e3 | ||
|
|
2702ce693b | ||
|
|
71836cacdb | ||
|
|
a501dfd680 | ||
|
|
7e7615c97c | ||
|
|
a4d955357d | ||
|
|
9c0aec1d19 | ||
|
|
28541fe826 | ||
|
|
8466a213e8 | ||
|
|
0cd0ce87f5 | ||
|
|
c7f3fa2b5d | ||
|
|
f601f4ce70 | ||
|
|
56a0300db5 | ||
|
|
d7075296e3 | ||
|
|
a904b1665f | ||
|
|
f4511f6fa4 | ||
|
|
1d81841091 | ||
|
|
d339d5eab5 | ||
|
|
9d13b090d8 | ||
|
|
6f898e611c | ||
|
|
1235a7435e | ||
|
|
2ebf46e241 | ||
|
|
d2af1e905c | ||
|
|
be255ff269 | ||
|
|
0211e7749e | ||
|
|
d24f808d79 | ||
|
|
6d42481c37 | ||
|
|
80753ac741 | ||
|
|
93e467913e | ||
|
|
fc283123bb | ||
|
|
5998c561ee | ||
|
|
4e8a71d034 | ||
|
|
d458729f6c | ||
|
|
3191fec54c | ||
|
|
7a5559aba0 | ||
|
|
29d5963cad | ||
|
|
90529c62b2 | ||
|
|
ae21979b7c | ||
|
|
09c3ef0f6a | ||
|
|
ba791b7de8 | ||
|
|
0ddfbd976c | ||
|
|
d4ba7579d1 | ||
|
|
220bc434be | ||
|
|
2932a835fc | ||
|
|
e88fad00a5 | ||
|
|
36d274d460 | ||
|
|
4c4d5e5837 | ||
|
|
769fb7cd02 | ||
|
|
0cc5b29558 | ||
|
|
191171802b | ||
|
|
32b626704e | ||
|
|
b193fd0ba9 | ||
|
|
c5e5cf28af | ||
|
|
8ff0430370 | ||
|
|
7317b6b98e | ||
|
|
799fcdb5e0 | ||
|
|
6b021f8d14 | ||
|
|
a56fcfb24a | ||
|
|
1811bd6885 | ||
|
|
46a37b9bd9 | ||
|
|
3fcd645a73 | ||
|
|
7cbee3dc8f | ||
|
|
9812f368e7 | ||
|
|
0f4dbcfc53 | ||
|
|
fb79927c05 | ||
|
|
fc05c0805b | ||
|
|
b69e023736 | ||
|
|
54c45bd54e | ||
|
|
67c06d17bb | ||
|
|
b91a1f4788 | ||
|
|
9f902c8bd4 | ||
|
|
52454591aa | ||
|
|
cfe9d60f51 | ||
|
|
36bf1ad112 | ||
|
|
6f29c1ff94 | ||
|
|
58080d6157 | ||
|
|
45bf867d38 | ||
|
|
eb578557c0 | ||
|
|
299a471af2 | ||
|
|
4ec8b82388 | ||
|
|
5ae5463f52 | ||
|
|
9a994eb777 | ||
|
|
bdfbc86ecb | ||
|
|
b5d983cddf | ||
|
|
65c01b35ec | ||
|
|
a6cffc0e29 | ||
|
|
5658ea7047 | ||
|
|
7f3b9e8a89 | ||
|
|
7fe2ad81a0 | ||
|
|
dcc266df12 | ||
|
|
d07043fe26 | ||
|
|
2f0f724813 | ||
|
|
fc5b557a10 | ||
|
|
116bf8058a | ||
|
|
050f44382b | ||
|
|
1392eebfa0 | ||
|
|
544a17926b | ||
|
|
637cff628f | ||
|
|
3841fecc59 | ||
|
|
f1cf5ceb82 | ||
|
|
40c39de5e3 | ||
|
|
0087be2a53 | ||
|
|
d9cacc0b8c | ||
|
|
370751551f | ||
|
|
ac8e1b88e4 | ||
|
|
aa0a9f18d5 | ||
|
|
05a2623875 | ||
|
|
1f506615d3 | ||
|
|
bc08e325ce | ||
|
|
a8f410ed40 | ||
|
|
f29a4b51cc | ||
|
|
924b5830ba | ||
|
|
ab877c429f | ||
|
|
97e5e6beee | ||
|
|
5530b7022f | ||
|
|
0aa266a6ca | ||
|
|
0b52823f2c | ||
|
|
1273536752 | ||
|
|
59449600ad | ||
|
|
c392a475ee | ||
|
|
d024ff553f | ||
|
|
b5d49b9e79 | ||
|
|
f0fa3c5861 | ||
|
|
966dd62551 | ||
|
|
ca5ce839ce | ||
|
|
760fc6b3f7 | ||
|
|
372f939cc9 | ||
|
|
5df321fcaa | ||
|
|
10c9d485c4 | ||
|
|
8f3e02e49c | ||
|
|
8b197e49bb | ||
|
|
200983f938 | ||
|
|
dd7fe2e7e0 | ||
|
|
1e94070dca | ||
|
|
40ab6f839a | ||
|
|
43cb646870 | ||
|
|
d1d9f96ab3 | ||
|
|
d910f520d9 | ||
|
|
5ca666acee | ||
|
|
338c2eb2f0 | ||
|
|
deccc4d8c6 | ||
|
|
204eaf0e76 | ||
|
|
421dbb19c2 | ||
|
|
808f326209 | ||
|
|
9c5f365b2b | ||
|
|
f7a5833cb0 | ||
|
|
0d549fd42b | ||
|
|
2a2f77f0dc | ||
|
|
42bd709f67 | ||
|
|
2c7226de20 | ||
|
|
d6c243143d | ||
|
|
9ab6b9b308 | ||
|
|
d80833d892 | ||
|
|
b7bc1fc51d | ||
|
|
9f6a5e35de | ||
|
|
f8ec815467 | ||
|
|
fb1258b817 | ||
|
|
5334ddd199 | ||
|
|
e5e8e9b5d8 | ||
|
|
2e6393f082 | ||
|
|
95527fb566 | ||
|
|
03849b3e87 | ||
|
|
3a4c19f82d | ||
|
|
c23e32a047 | ||
|
|
5e432a6586 | ||
|
|
ea9f3f13ca | ||
|
|
412a1885b2 | ||
|
|
886a692f1a | ||
|
|
d5ebf9039c | ||
|
|
789cf29d28 | ||
|
|
7cb5de7df4 | ||
|
|
c68c71f7b2 | ||
|
|
09f1404c45 | ||
|
|
f8a88fcb94 | ||
|
|
29a358ee3a | ||
|
|
29513919ca | ||
|
|
45beb1bcdd | ||
|
|
166333a2cd | ||
|
|
ebc567d03c | ||
|
|
529c0f24da | ||
|
|
4855f371f1 | ||
|
|
76f000e4c8 | ||
|
|
2aea7a375d | ||
|
|
5cf2a7abd1 | ||
|
|
d11c7cbbb1 | ||
|
|
5c6cd1b48a | ||
|
|
cbec423413 | ||
|
|
9910dfbee2 | ||
|
|
e2b084b715 | ||
|
|
5f9197bcc2 | ||
|
|
0b9bc85ddd | ||
|
|
66ad300515 | ||
|
|
4ce66f0764 | ||
|
|
0fc6116082 | ||
|
|
48af4fc704 | ||
|
|
8c08b531d6 | ||
|
|
966e0640af | ||
|
|
0e26077a64 | ||
|
|
b830b33cf1 | ||
|
|
a989cfb987 | ||
|
|
5a0c0a26e3 | ||
|
|
cd016824f8 | ||
|
|
af00dca08b | ||
|
|
115b08e0e1 | ||
|
|
daa6152868 | ||
|
|
a81280939e | ||
|
|
8df97a0c75 | ||
|
|
2fbcc99b24 | ||
|
|
77f48c8c8e | ||
|
|
aaf7e1fb0d | ||
|
|
9577f1a46f | ||
|
|
63e5a8b1b5 | ||
|
|
be8dd0dba1 | ||
|
|
9f17cb7a86 | ||
|
|
70acb573d5 | ||
|
|
45047732a7 | ||
|
|
f7d0908ff1 | ||
|
|
9f90833d2b | ||
|
|
93174dcd19 | ||
|
|
72f08bb1b4 | ||
|
|
371d0b7395 | ||
|
|
21b2ec6bd5 | ||
|
|
311c5c133c | ||
|
|
551a671cc6 | ||
|
|
f54098618e | ||
|
|
fc69e73dcf | ||
|
|
41855bb6d4 | ||
|
|
e3ee0ac22b | ||
|
|
e65a891274 | ||
|
|
01944dd80e | ||
|
|
ed6dd6d6d1 | ||
|
|
6d13ace83a | ||
|
|
32bdb49496 | ||
|
|
cc8160d044 | ||
|
|
ba7900a3fb | ||
|
|
b9ff6cb1d9 |
No files matched your search
@@ -9,6 +9,8 @@
|
||||
*.map
|
||||
*.swp
|
||||
*.cache
|
||||
*.gcda
|
||||
*.gcno
|
||||
.tmp
|
||||
.tmp.c
|
||||
.tmp.cxx
|
||||
|
||||
@@ -392,6 +392,7 @@ build-retroarch-dingux-mips32:
|
||||
- export NUMPROC=$(($(nproc)/3))
|
||||
artifacts:
|
||||
paths:
|
||||
- retroarch
|
||||
- retroarch_rg350.opk
|
||||
- ${MEDIA_PATH}
|
||||
expire_in: 10 min
|
||||
@@ -413,6 +414,7 @@ build-retroarch-dingux-odbeta-mips32:
|
||||
- export NUMPROC=$(($(nproc)/3))
|
||||
artifacts:
|
||||
paths:
|
||||
- retroarch
|
||||
- retroarch_rg350_odbeta.opk
|
||||
- ${MEDIA_PATH}
|
||||
expire_in: 10 min
|
||||
|
||||
+82
@@ -1,5 +1,87 @@
|
||||
# Future
|
||||
|
||||
# 1.9.4
|
||||
- CHEEVOS: update rcheevos to v10.0.0
|
||||
- CONTENT LOADING/FILE IO: Prevent unnecessary extraction (to disk) of compressed content files when need_fullpath is false
|
||||
- CORE INFO/FILE IO: Enable core info cache by default now for all platforms
|
||||
- CORE INFO/REGRESSION FIX: Fix regression caused by core info file caching - Downloads was no longer showing up in Load Content
|
||||
- FILE IO/COMPRESSED: Ability to load content inside ZIP files directly into RAM
|
||||
- INPUT/OVERLAYS: Add option to select between 'touched' elements and physical controller inputs when showing inputs on overlays
|
||||
- INPUT REMAPPING/OVERLAYS: Prevent duplicate inputs when using remaps with input overlays
|
||||
- LAKKA: Add brightness restore hook
|
||||
- LOCALIZATION: Fetch translations from Crowdin
|
||||
- MENU/OZONE: Added simple playlist entry enumeration
|
||||
- MENU/XMB: Fix display of 'Maximum Users' menu entry dropdown list
|
||||
- PS3/PSL1GHT: Joypad driver works again
|
||||
- PSTV: Fix Vita input driver for PSTV
|
||||
- PSTV: Support for 720p on PSTV when using 'Unlock framebuffer' in Sharpscale plugin
|
||||
- RPNG: Fix some memory corruption if processing broken input PNG file
|
||||
- SECURITY: Fix CVE-2021-28927
|
||||
|
||||
# 1.9.3
|
||||
- 3DS: Disable menu screensaver animations in XMB/GLUI
|
||||
- COMMAND: Initialize netcmd->cmd_source_len before recvfrom()
|
||||
- CONTENT LOADING/STATICALLY LINKED: Ensure 'Always Reload Core on Run Content' setting is applied when loading content via the file browser
|
||||
- CONTENT LOADING/EMSCRIPTEN: Fix content loading via file browser on platforms with 'broken' core handling (i.e. emscripten)
|
||||
- CORE INFO: Skip whitespace when writing compressed core info cache files
|
||||
- CORE INFO/FILE IO: Core Info cache; significant file I/O performance improvements on systems with slow disk file I/O
|
||||
- CORE INFO/FILE IO: Enable core info cache by default on all 'console' platforms
|
||||
- FREEBSD: FreeBSD build fix
|
||||
- LAKKA: Support for tweaking CPU governors/scaling policies
|
||||
- LAKKA: This adds managed policies and settings to store them and reload them at startup
|
||||
- LIBRETRO API: Add API extension for cores to override frontend fast-forward state
|
||||
- MENU/RGUI: Fix saving of config files/overrides when 'Lock Menu Aspect Ratio' is enabled
|
||||
- SHADERS: Fix 'Auto-Shader Delay' functionality
|
||||
- UWP/D3D11: Disable mipmap generation
|
||||
- UWP/XBOX: Add 'Force 4K resolution' option (Force the resolution to the fullscreen size on Xbox, if set to 0, a fixed value of 3840 x 2160 will be used)
|
||||
|
||||
# 1.9.2
|
||||
- 3DS: Enable graphics widgets / overlays
|
||||
- 3DS: Load texture images as BGR colors
|
||||
- 3DS: Change default asset directory
|
||||
- CHEEVOS: Allow rcheevos_patch_address to be called on game without achievements
|
||||
- CHEEVOS: Update achievement memory maps (add Supervision)
|
||||
- CONFIG/FILE: Use hash map to optimise key/value lookups
|
||||
- CORE INFO: Performance optimisations + code clean-ups/refactors
|
||||
- CRT/SWITCHRES: Fixed CRTSwitchRes framebuffer bug
|
||||
- DISCORD/RP: Fix regression
|
||||
- DRM: set the correct video mode
|
||||
- FASTFORWARD: Enforce minimum fastforward_ratio of 1.0
|
||||
- FONTS/FREETYPE/STB_UNICODE/BITMAPFONT: Prevent texture bleed when rendering text at non-integer scales
|
||||
- INPUT: Ensure that 'retro_set_controller_port_device' is called when updating 'Max Users'
|
||||
- INPUT/XEGL/MOUSE: Fix xegl_ctx.c mouse activation
|
||||
- INPUT/SDL: Fix crash in SDL input driver when analogs are bound.
|
||||
- INPUT/POINTER: Add scaling to pointer input.
|
||||
- INPUT REMAPPING: Fix regression on loading file
|
||||
- INPUT REMAPPING: Fix regression where disabling input remapping would disable input
|
||||
- IOS: Disable system button gestures on iOS 14
|
||||
- IOS: Prevent potential crash on controller connect - randomly would get a crash when connecting a game controller while RA was running
|
||||
- LAKKA: Add menu for time zone setting
|
||||
- LOGGING: RARCH_LOG_V checking for verbosity level is not necessary and can cause issues; removed said check.
|
||||
- LOGGING: Silence inappropriate cheatfile logging
|
||||
- IOS/MAC: Add CPU VFP unit detection for Darwin
|
||||
- MAC: Swallow 'escape' key being pressed - would previously make the game toggle between fullscreen and windowed
|
||||
- MAC: Intel and ARM64 timer change proposal.
|
||||
- MAC/ARM64: run_fast_mode disabled for 64 bits invalid instructions.
|
||||
- MAC/OPENGL: Fix codesigning issue
|
||||
- MAC/UNIVERSAL: Add TTS translation service support.
|
||||
- MENU: Add optional menu screensaver
|
||||
- MENU: Add search filter support to cheats and overlays file browser menus
|
||||
- MENU/FILEBROWSER: Enhanced 'Load Content' file browser search functionality
|
||||
- MENU/INPUT: Block accidental diagonals in menu navigation
|
||||
- MENU/RGUI: Add option to disable menu transparency
|
||||
- MENU/RGUI: Fix display of 'Video > Scaling' menu when 'Lock Menu Aspect Ratio' is enabled
|
||||
- MENU/MATERIALUI: Add icon to 'Turbo Fire' menu entry
|
||||
- MENU/OZONE: Ozone Dracula theme
|
||||
- OPENDINGUX/BETA: Fix IPU scaling when running GBA-resolution content
|
||||
- OPENDINGUX/BETA: Add 50Hz support
|
||||
- OPENDINGUX: Enable 'SaveRAM Autosave Interval' by default
|
||||
- PATCHES: Added multi-softpatching support + OSD messages for patches
|
||||
- RHMAP: Track the complete string in rhmap
|
||||
- VITA: Align VM block size to 1MB - should fix one of the issues with gpSP
|
||||
- WINDOWS/GDI: Fix non-ASCII text rendering
|
||||
- XEGL: Fix mouse not working when using OpenGLES with X11
|
||||
|
||||
# 1.9.1
|
||||
- 3DS: Graphics widgets support
|
||||
- 3DS: On error, only init gfx on salamander build. This prevents graphical issues if the gfx is already initialized.
|
||||
|
||||
@@ -22,7 +22,7 @@ endif
|
||||
|
||||
OBJ :=
|
||||
LIBS :=
|
||||
DEF_FLAGS :=
|
||||
DEF_FLAGS := -I.
|
||||
ASFLAGS :=
|
||||
DEFINES := -DHAVE_CONFIG_H -DRARCH_INTERNAL -D_FILE_OFFSET_BITS=64
|
||||
DEFINES += -DGLOBAL_CONFIG_DIR='"$(GLOBAL_CONFIG_DIR)"'
|
||||
@@ -118,7 +118,7 @@ ifneq ($(CXX_BUILD), 1)
|
||||
CFLAGS += -D_GNU_SOURCE
|
||||
endif
|
||||
|
||||
DEF_FLAGS += $(INCLUDE_DIRS) -I. -Ideps -Ideps/stb
|
||||
DEF_FLAGS += $(INCLUDE_DIRS) -Ideps -Ideps/stb
|
||||
|
||||
CFLAGS += $(DEF_FLAGS)
|
||||
CXXFLAGS += $(DEF_FLAGS) -D__STDC_CONSTANT_MACROS
|
||||
|
||||
+32
-20
@@ -195,8 +195,8 @@ endif
|
||||
ifeq ($(TARGET), retroarch_3ds)
|
||||
OBJ += frontend/drivers/platform_ctr.o
|
||||
endif
|
||||
|
||||
# Git Version
|
||||
ifeq ($(HAVE_GIT_VERSION), 1)
|
||||
GIT_VERSION_CACHEDIR = $(if $(OBJDIR),$(OBJDIR),$(CURDIR))
|
||||
GIT_VERSION_CACHEFILE = $(GIT_VERSION_CACHEDIR)/git-version.cache
|
||||
|
||||
@@ -224,7 +224,6 @@ ifneq ($(GIT_VERSION),) # Enable version_git.o?
|
||||
DEFINES += -DHAVE_GIT_VERSION -DGIT_VERSION=$(GIT_VERSION)
|
||||
OBJ += version_git.o
|
||||
endif
|
||||
endif
|
||||
|
||||
# General object files
|
||||
ifeq ($(HAVE_DR_MP3), 1)
|
||||
@@ -233,6 +232,7 @@ endif
|
||||
|
||||
OBJ += frontend/frontend_driver.o \
|
||||
retroarch.o \
|
||||
command.o \
|
||||
msg_hash.o \
|
||||
intl/msg_hash_us.o \
|
||||
$(LIBRETRO_COMM_DIR)/queues/task_queue.o \
|
||||
@@ -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
|
||||
@@ -764,6 +768,7 @@ endif
|
||||
|
||||
ifeq ($(HAVE_LAKKA), 1)
|
||||
OBJ += wifi/drivers/connmanctl.o
|
||||
OBJ += misc/cpufreq/cpufreq.o
|
||||
endif
|
||||
|
||||
# Audio
|
||||
@@ -988,12 +993,14 @@ ifeq ($(HAVE_MENU), 1)
|
||||
OBJ += menu/drivers/materialui.o
|
||||
DEFINES += -DHAVE_MATERIALUI
|
||||
HAVE_ASSETS = 1
|
||||
HAVE_MENU_SCREENSAVER = 1
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_XMB), 1)
|
||||
OBJ += menu/drivers/xmb.o
|
||||
DEFINES += -DHAVE_XMB
|
||||
HAVE_ASSETS = 1
|
||||
HAVE_MENU_SCREENSAVER = 1
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_OZONE), 1)
|
||||
@@ -1005,12 +1012,17 @@ ifeq ($(HAVE_MENU), 1)
|
||||
menu/drivers/ozone/ozone_sidebar.o
|
||||
DEFINES += -DHAVE_OZONE
|
||||
HAVE_ASSETS = 1
|
||||
HAVE_MENU_SCREENSAVER = 1
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_STRIPES), 1)
|
||||
OBJ += menu/drivers/stripes.o
|
||||
DEFINES += -DHAVE_STRIPES
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_MENU_SCREENSAVER), 1)
|
||||
OBJ += menu/menu_screensaver.o
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_BLUETOOTH), 1)
|
||||
@@ -1863,20 +1875,24 @@ ifeq ($(HAVE_7ZIP),1)
|
||||
INCLUDE_DIRS += -I$(DEPS_DIR)/7zip
|
||||
HAVE_COMPRESSION = 1
|
||||
DEFINES += -DHAVE_7ZIP -D_7ZIP_ST
|
||||
7ZOBJ = $(DEPS_DIR)/7zip/7zIn.o \
|
||||
$(DEPS_DIR)/7zip/Bra86.o \
|
||||
$(DEPS_DIR)/7zip/7zFile.o \
|
||||
$(DEPS_DIR)/7zip/7zStream.o \
|
||||
7ZOBJ = $(DEPS_DIR)/7zip/7zArcIn.o \
|
||||
$(DEPS_DIR)/7zip/7zBuf.o \
|
||||
$(DEPS_DIR)/7zip/7zCrc.o \
|
||||
$(DEPS_DIR)/7zip/7zCrcOpt.o \
|
||||
$(DEPS_DIR)/7zip/7zDec.o \
|
||||
$(DEPS_DIR)/7zip/CpuArch.o \
|
||||
$(DEPS_DIR)/7zip/Delta.o \
|
||||
$(DEPS_DIR)/7zip/LzFind.o \
|
||||
$(DEPS_DIR)/7zip/LzmaDec.o \
|
||||
$(DEPS_DIR)/7zip/LzmaEnc.o \
|
||||
$(DEPS_DIR)/7zip/7zCrcOpt.o \
|
||||
$(DEPS_DIR)/7zip/Bra.o \
|
||||
$(DEPS_DIR)/7zip/7zDec.o \
|
||||
$(DEPS_DIR)/7zip/Bcj2.o \
|
||||
$(DEPS_DIR)/7zip/7zCrc.o \
|
||||
$(DEPS_DIR)/7zip/Lzma2Dec.o \
|
||||
$(DEPS_DIR)/7zip/7zBuf.o
|
||||
$(DEPS_DIR)/7zip/LzmaEnc.o \
|
||||
$(DEPS_DIR)/7zip/Bra.o \
|
||||
$(DEPS_DIR)/7zip/Bra86.o \
|
||||
$(DEPS_DIR)/7zip/BraIA64.o \
|
||||
$(DEPS_DIR)/7zip/Bcj2.o \
|
||||
$(DEPS_DIR)/7zip/7zFile.o \
|
||||
$(DEPS_DIR)/7zip/7zStream.o
|
||||
|
||||
OBJ += $(LIBRETRO_COMM_DIR)/file/archive_file_7z.o \
|
||||
$(7ZOBJ)
|
||||
endif
|
||||
@@ -1974,7 +1990,8 @@ ifeq ($(HAVE_CDROM), 1)
|
||||
DEFINES += -DHAVE_CDROM
|
||||
OBJ += $(LIBRETRO_COMM_DIR)/cdrom/cdrom.o \
|
||||
$(LIBRETRO_COMM_DIR)/vfs/vfs_implementation_cdrom.o \
|
||||
$(LIBRETRO_COMM_DIR)/media/media_detect_cd.o
|
||||
$(LIBRETRO_COMM_DIR)/media/media_detect_cd.o \
|
||||
tasks/task_content_disc.o
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_RTGA), 1)
|
||||
@@ -2079,14 +2096,9 @@ ifeq ($(HAVE_NETWORKING), 1)
|
||||
|
||||
# Netplay
|
||||
DEFINES += -DHAVE_NETWORK_CMD
|
||||
OBJ += network/netplay/netplay_delta.o \
|
||||
network/netplay/netplay_handshake.o \
|
||||
network/netplay/netplay_init.o \
|
||||
OBJ += network/netplay/netplay_handshake.o \
|
||||
network/netplay/netplay_io.o \
|
||||
network/netplay/netplay_keyboard.o \
|
||||
network/netplay/netplay_sync.o \
|
||||
network/netplay/netplay_discovery.o \
|
||||
network/netplay/netplay_buf.o \
|
||||
network/netplay/netplay_room_parse.o
|
||||
|
||||
# RetroAchievements
|
||||
|
||||
+3
-2
@@ -51,14 +51,15 @@ DEFINES :=
|
||||
ifeq ($(GRIFFIN_BUILD), 1)
|
||||
OBJ += griffin/griffin.o
|
||||
DEFINES += -DHAVE_GRIFFIN=1 -DHAVE_MENU -DHAVE_CONFIGFILE -DHAVE_RGUI -DHAVE_XMB -DHAVE_MATERIALUI -DHAVE_LIBRETRODB -DHAVE_CC_RESAMPLER
|
||||
DEFINES += -DHAVE_ZLIB -DHAVE_7ZIP -DHAVE_RPNG -DHAVE_RJPEG -DHAVE_RBMP -DHAVE_RTGA
|
||||
DEFINES += -DHAVE_ZLIB -DHAVE_7ZIP -D_7ZIP_ST -DHAVE_RPNG -DHAVE_RJPEG -DHAVE_RBMP -DHAVE_RTGA
|
||||
DEFINES += -DHAVE_NETWORKING -DHAVE_CHEEVOS -DRC_DISABLE_LUA -DHAVE_ONLINE_UPDATER -DHAVE_UPDATE_CORES
|
||||
#DEFINES += -DHAVE_UPDATE_ASSETS
|
||||
DEFINES += -DHAVE_PATCH -DHAVE_RWAV
|
||||
DEFINES += -DHAVE_SCREENSHOTS
|
||||
DEFINES += -DHAVE_REWIND
|
||||
DEFINES += -DHAVE_THREADS
|
||||
#DEFINES += -DHAVE_GFX_WIDGETS
|
||||
DEFINES += -DHAVE_GFX_WIDGETS
|
||||
DEFINES += -DHAVE_OVERLAY
|
||||
#DEFINES += -DHAVE_SOCKET_LEGACY
|
||||
#-DHAVE_SSL -DHAVE_BUILTINMBEDTLS -DMBEDTLS_SSL_DEBUG_ALL
|
||||
#ssl is currently incompatible with griffin due to use of the "static" flag on repeating functions that will conflict when included in one file
|
||||
|
||||
+1
-1
@@ -1102,7 +1102,7 @@ ifeq ($(HAVE_RUNAHEAD), 1)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_7ZIP), 1)
|
||||
CFLAGS += -DHAVE_7ZIP
|
||||
CFLAGS += -DHAVE_7ZIP -D_7ZIP_ST
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_SCREENSHOTS), 1)
|
||||
|
||||
+1
-1
@@ -205,7 +205,7 @@ ifeq ($(HAVE_RUNAHEAD), 1)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_7ZIP), 1)
|
||||
CFLAGS += -DHAVE_7ZIP
|
||||
CFLAGS += -DHAVE_7ZIP -D_7ZIP_ST
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_SCREENSHOTS), 1)
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ CFLAGS = $(OPTIMIZE_LV) $(DISABLE_WARNINGS) -ffast-math -fsingle-precision-const
|
||||
ASFLAGS = $(CFLAGS)
|
||||
|
||||
RARCH_DEFINES += -DPS2 -DHAVE_GRIFFIN=1 -DRARCH_INTERNAL -DHAVE_SCREENSHOTS -DHAVE_REWIND -DRARCH_CONSOLE -DHAVE_MENU -DHAVE_CONFIGFILE -DHAVE_PATCH -DHAVE_CHEATS -DHAVE_RGUI
|
||||
RARCH_DEFINES += -DHAVE_ZLIB -DHAVE_NO_BUILTINZLIB -DHAVE_RPNG -DHAVE_RJPEG -DHAVE_FILTERS_BUILTIN -DHAVE_7ZIP -DHAVE_CC_RESAMPLER -DHAVE_AUDIOMIXER
|
||||
RARCH_DEFINES += -DHAVE_ZLIB -DHAVE_NO_BUILTINZLIB -DHAVE_RPNG -DHAVE_RJPEG -DHAVE_FILTERS_BUILTIN -DHAVE_7ZIP -D_7ZIP_ST -DHAVE_CC_RESAMPLER -DHAVE_AUDIOMIXER
|
||||
RARCH_DEFINES += -DHAVE_DSP_FILTER
|
||||
RARCH_DEFINES += -DHAVE_VIDEO_FILTER
|
||||
|
||||
|
||||
+3
-3
@@ -27,13 +27,13 @@ ELF_TARGET := retroarch_psl1ght.elf
|
||||
SELF_TARGET := $(ELF_TARGET:.elf=.self)
|
||||
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
|
||||
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
|
||||
LIBS := -lretro_psl1ght -laudio -lrsx -lgcm_sys -lnet -lio -lsysutil -lsysmodule -lm -ljpgdec -lpngdec -llv2 -lnet -lnetctl -lsysfs
|
||||
|
||||
# system platform
|
||||
system_platform = unix
|
||||
|
||||
@@ -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
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ INCDIR = deps deps/stb deps/7zip deps/pthreads deps/pthreads/platform/psp deps/p
|
||||
CFLAGS = $(OPTIMIZE_LV) -G0 -std=gnu99 -ffast-math -fsingle-precision-constant
|
||||
ASFLAGS = $(CFLAGS)
|
||||
|
||||
RARCH_DEFINES = -DPSP -D_MIPS_ARCH_ALLEGREX -DHAVE_LANGEXTRA -DHAVE_ZLIB -DHAVE_AUDIOMIXER -DHAVE_RWAV -DHAVE_RPNG -DHAVE_RJPEG -DHAVE_GRIFFIN=1 -DRARCH_INTERNAL -DHAVE_SCREENSHOTS -DHAVE_REWIND -DRARCH_CONSOLE -DHAVE_MENU -DHAVE_CONFIGFILE -DHAVE_PATCH -DHAVE_CHEATS -DHAVE_RGUI -DHAVE_FILTERS_BUILTIN -DHAVE_DSP_FILTER -DHAVE_VIDEO_FILTER -DHAVE_7ZIP -DHAVE_CC_RESAMPLER
|
||||
RARCH_DEFINES = -DPSP -D_MIPS_ARCH_ALLEGREX -DHAVE_LANGEXTRA -DHAVE_ZLIB -DHAVE_AUDIOMIXER -DHAVE_RWAV -DHAVE_RPNG -DHAVE_RJPEG -DHAVE_GRIFFIN=1 -DRARCH_INTERNAL -DHAVE_SCREENSHOTS -DHAVE_REWIND -DRARCH_CONSOLE -DHAVE_MENU -DHAVE_CONFIGFILE -DHAVE_PATCH -DHAVE_CHEATS -DHAVE_RGUI -DHAVE_FILTERS_BUILTIN -DHAVE_DSP_FILTER -DHAVE_VIDEO_FILTER -DHAVE_7ZIP -D_7ZIP_ST -DHAVE_CC_RESAMPLER
|
||||
|
||||
LIBDIR =
|
||||
LDFLAGS =
|
||||
|
||||
+5
-3
@@ -113,16 +113,18 @@ LD := $(CXX)
|
||||
LIBDIRS := -L.
|
||||
|
||||
ARCHFLAGS := -march=armv7-a -mfpu=neon -mfloat-abi=hard -DVITA -DSCE_LIBC_SIZE=$(SCE_LIBC_SIZE)
|
||||
CFLAGS += $(ARCHFLAGS) -mword-relocations -fno-optimize-sibling-calls -O2
|
||||
CFLAGS += $(ARCHFLAGS) -mword-relocations -fno-optimize-sibling-calls
|
||||
|
||||
ifeq ($(DEBUG), 1)
|
||||
CFLAGS += -g
|
||||
CFLAGS += -g -Og
|
||||
else
|
||||
CFLAGS += -O2
|
||||
endif
|
||||
|
||||
ASFLAGS := $(CFLAGS)
|
||||
LDFLAGS := -Wl,-q
|
||||
|
||||
CFLAGS += -Wall -ffast-math -g
|
||||
CFLAGS += -Wall -ffast-math
|
||||
CFLAGS += -DRARCH_INTERNAL -DHAVE_SCREENSHOTS -DRARCH_CONSOLE
|
||||
CFLAGS += -DHAVE_DSP_FILTER
|
||||
CFLAGS += -DHAVE_VIDEO_FILTER
|
||||
|
||||
+1
-1
@@ -222,7 +222,7 @@ ifeq ($(HAVE_RUNAHEAD), 1)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_7ZIP), 1)
|
||||
CFLAGS += -DHAVE_7ZIP
|
||||
CFLAGS += -DHAVE_7ZIP -D_7ZIP_ST
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_SCREENSHOTS), 1)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -15,6 +15,8 @@ static int _newlib_vm_memblock;
|
||||
extern int _newlib_heap_size_user __attribute__((weak));
|
||||
extern int _newlib_vm_size_user __attribute__((weak));
|
||||
|
||||
#define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1))
|
||||
|
||||
void * _sbrk_r(struct _reent *reent, ptrdiff_t incr) {
|
||||
if (sceKernelLockLwMutex((struct SceKernelLwMutexWork*)_newlib_sbrk_mutex, 1, 0) < 0)
|
||||
goto fail;
|
||||
@@ -36,8 +38,8 @@ void _init_vita_heap(void) {
|
||||
|
||||
int _newlib_vm_size = 0;
|
||||
if (&_newlib_vm_size_user != NULL) {
|
||||
_newlib_vm_size = _newlib_vm_size_user;
|
||||
_newlib_vm_memblock = sceKernelAllocMemBlockForVM("code", _newlib_vm_size_user);
|
||||
_newlib_vm_size = ALIGN(_newlib_vm_size_user, 0x100000);
|
||||
_newlib_vm_memblock = sceKernelAllocMemBlockForVM("code", _newlib_vm_size);
|
||||
|
||||
if (_newlib_vm_memblock < 0){
|
||||
//sceClibPrintf("sceKernelAllocMemBlockForVM failed\n");
|
||||
|
||||
+10
-12
@@ -140,7 +140,6 @@ bool cheat_manager_save(
|
||||
{
|
||||
bool ret;
|
||||
unsigned i;
|
||||
char buf[PATH_MAX_LENGTH];
|
||||
char cheats_file[PATH_MAX_LENGTH];
|
||||
config_file_t *conf = NULL;
|
||||
cheat_manager_t *cheat_st = &cheat_manager_state;
|
||||
@@ -164,7 +163,7 @@ bool cheat_manager_save(
|
||||
(char*)"cheat%u_repeat_add_to_address"
|
||||
};
|
||||
|
||||
buf[0] = cheats_file[0] = '\0';
|
||||
cheats_file[0] = '\0';
|
||||
|
||||
if (!cheat_st->cheats || cheat_st->size == 0)
|
||||
return false;
|
||||
@@ -172,11 +171,10 @@ bool cheat_manager_save(
|
||||
if (!cheat_database)
|
||||
strlcpy(cheats_file, path, sizeof(cheats_file));
|
||||
else
|
||||
{
|
||||
fill_pathname_join(buf, cheat_database, path, sizeof(buf));
|
||||
|
||||
fill_pathname_noext(cheats_file, buf, ".cht", sizeof(cheats_file));
|
||||
}
|
||||
fill_pathname_join_concat(cheats_file,
|
||||
cheat_database, path,
|
||||
".cht",
|
||||
sizeof(cheats_file));
|
||||
|
||||
if (!overwrite)
|
||||
conf = config_file_new_from_path_to_string(cheats_file);
|
||||
@@ -705,7 +703,7 @@ static bool cheat_manager_get_game_specific_filename(
|
||||
return false;
|
||||
|
||||
core_name = system_info.library_name;
|
||||
game_name = path_basename(global->name.cheatfile);
|
||||
game_name = path_basename_nocompression(global->name.cheatfile);
|
||||
|
||||
if (string_is_empty(path_cheat_database) ||
|
||||
string_is_empty(core_name) ||
|
||||
@@ -739,8 +737,8 @@ void cheat_manager_load_game_specific_cheats(const char *path_cheat_database)
|
||||
path_cheat_database,
|
||||
false))
|
||||
{
|
||||
RARCH_LOG("[Cheats]: Load game-specific cheatfile: %s\n", cheat_file);
|
||||
cheat_manager_load(cheat_file, true);
|
||||
if (cheat_manager_load(cheat_file, true))
|
||||
RARCH_LOG("[Cheats]: Load game-specific cheatfile: %s\n", cheat_file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -753,8 +751,8 @@ void cheat_manager_save_game_specific_cheats(const char *path_cheat_database)
|
||||
path_cheat_database,
|
||||
true))
|
||||
{
|
||||
RARCH_LOG("[Cheats]: Save game-specific cheatfile: %s\n", cheat_file);
|
||||
cheat_manager_save(cheat_file, NULL, true);
|
||||
if (cheat_manager_save(cheat_file, NULL, true))
|
||||
RARCH_LOG("[Cheats]: Save game-specific cheatfile: %s\n", cheat_file);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+163
-97
@@ -54,6 +54,10 @@
|
||||
#include "../cheat_manager.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CHD
|
||||
#include "streams/chd_stream.h"
|
||||
#endif
|
||||
|
||||
#include "badges.h"
|
||||
#include "cheevos.h"
|
||||
#include "cheevos_memory.h"
|
||||
@@ -76,8 +80,9 @@
|
||||
#include "../network/net_http_special.h"
|
||||
#include "../tasks/tasks_internal.h"
|
||||
|
||||
#include "../deps/rcheevos/include/rcheevos.h"
|
||||
#include "../deps/rcheevos/include/rurl.h"
|
||||
#include "../deps/rcheevos/include/rc_runtime.h"
|
||||
#include "../deps/rcheevos/include/rc_runtime_types.h"
|
||||
#include "../deps/rcheevos/include/rc_url.h"
|
||||
#include "../deps/rcheevos/include/rc_hash.h"
|
||||
|
||||
/* Define this macro to prevent cheevos from being deactivated. */
|
||||
@@ -199,73 +204,79 @@ void rcheevos_log(const char *fmt, ...)
|
||||
}
|
||||
#endif
|
||||
|
||||
static int append_no_spaces(char* buffer, char* stop, const char* text)
|
||||
{
|
||||
char* ptr = buffer;
|
||||
|
||||
while (ptr < stop && *text)
|
||||
{
|
||||
if (*text == ' ')
|
||||
{
|
||||
*ptr++ = '_';
|
||||
++text;
|
||||
}
|
||||
else
|
||||
{
|
||||
*ptr++ = *text++;
|
||||
}
|
||||
}
|
||||
|
||||
*ptr = '\0';
|
||||
return (ptr - buffer);
|
||||
}
|
||||
|
||||
static void rcheevos_get_user_agent(
|
||||
rcheevos_locals_t *locals,
|
||||
char *buffer, size_t len)
|
||||
{
|
||||
struct retro_system_info *system = runloop_get_libretro_system_info();
|
||||
const char* scan;
|
||||
char* ptr;
|
||||
|
||||
/* if we haven't calculated the non-changing portion yet, do so now [retroarch version + os version] */
|
||||
if (!locals->user_agent_prefix[0])
|
||||
{
|
||||
const frontend_ctx_driver_t *frontend = frontend_get_ptr();
|
||||
int major, minor;
|
||||
char tmp[64];
|
||||
|
||||
ptr = locals->user_agent_prefix + snprintf(locals->user_agent_prefix, sizeof(locals->user_agent_prefix), "RetroArch/%s", PACKAGE_VERSION);
|
||||
|
||||
if (frontend && frontend->get_os)
|
||||
{
|
||||
frontend->get_os(tmp, sizeof(tmp), &major, &minor);
|
||||
ptr += sprintf(ptr, " (%s %d.%d)", tmp, major, minor);
|
||||
(void)ptr;
|
||||
snprintf(locals->user_agent_prefix, sizeof(locals->user_agent_prefix),
|
||||
"RetroArch/%s (%s %d.%d)", PACKAGE_VERSION, tmp, major, minor);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(locals->user_agent_prefix, sizeof(locals->user_agent_prefix),
|
||||
"RetroArch/%s", PACKAGE_VERSION);
|
||||
}
|
||||
}
|
||||
|
||||
ptr = buffer + snprintf(buffer, len, "%s", locals->user_agent_prefix);
|
||||
/* append the non-changing portion */
|
||||
ptr = buffer + strlcpy(buffer, locals->user_agent_prefix, len);
|
||||
|
||||
/* if a core is loaded, append its information */
|
||||
if (system && !string_is_empty(system->library_name))
|
||||
{
|
||||
char* stop = buffer + len - 1;
|
||||
const char* path = path_get(RARCH_PATH_CORE);
|
||||
*ptr++ = ' ';
|
||||
|
||||
if (!string_is_empty(path))
|
||||
{
|
||||
sprintf(ptr, " %s", path_basename(path));
|
||||
append_no_spaces(ptr, stop, path_basename(path));
|
||||
path_remove_extension(ptr);
|
||||
ptr += strlen(ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
*ptr++ = ' ';
|
||||
|
||||
scan = system->library_name;
|
||||
while (*scan)
|
||||
{
|
||||
if (*scan == ' ')
|
||||
{
|
||||
*ptr++ = '_';
|
||||
++scan;
|
||||
}
|
||||
else
|
||||
*ptr++ = *scan++;
|
||||
}
|
||||
ptr += append_no_spaces(ptr, stop, system->library_name);
|
||||
}
|
||||
|
||||
if (system->library_version)
|
||||
{
|
||||
*ptr++ = '/';
|
||||
|
||||
scan = system->library_version;
|
||||
while (*scan)
|
||||
{
|
||||
if (*scan == ' ')
|
||||
{
|
||||
*ptr++ = '_';
|
||||
++scan;
|
||||
}
|
||||
else
|
||||
*ptr++ = *scan++;
|
||||
}
|
||||
ptr += append_no_spaces(ptr, stop, system->library_version);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -362,7 +373,7 @@ static void rcheevos_log_post_url(
|
||||
}
|
||||
|
||||
static bool rcheevos_condset_contains_memref(const rc_condset_t* condset,
|
||||
const rc_memref_value_t* memref)
|
||||
const rc_memref_t* memref)
|
||||
{
|
||||
if (condset)
|
||||
{
|
||||
@@ -379,7 +390,7 @@ static bool rcheevos_condset_contains_memref(const rc_condset_t* condset,
|
||||
}
|
||||
|
||||
static bool rcheevos_trigger_contains_memref(const rc_trigger_t* trigger,
|
||||
const rc_memref_value_t* memref)
|
||||
const rc_memref_t* memref)
|
||||
{
|
||||
rc_condset_t* condset;
|
||||
if (!trigger)
|
||||
@@ -397,6 +408,26 @@ static bool rcheevos_trigger_contains_memref(const rc_trigger_t* trigger,
|
||||
return false;
|
||||
}
|
||||
|
||||
static void rcheevos_achievement_disabled(rcheevos_racheevo_t* cheevo, unsigned address)
|
||||
{
|
||||
if (!cheevo)
|
||||
return;
|
||||
|
||||
CHEEVOS_ERR(RCHEEVOS_TAG "Achievement disabled (invalid address %06X): %s\n", address, cheevo->title);
|
||||
CHEEVOS_FREE(cheevo->memaddr);
|
||||
cheevo->memaddr = NULL;
|
||||
}
|
||||
|
||||
static void rcheevos_lboard_disabled(rcheevos_ralboard_t* lboard, unsigned address)
|
||||
{
|
||||
if (!lboard)
|
||||
return;
|
||||
|
||||
CHEEVOS_ERR(RCHEEVOS_TAG "Leaderboard disabled (invalid address %06X): %s\n", address, lboard->title);
|
||||
CHEEVOS_FREE(lboard->mem);
|
||||
lboard->mem = NULL;
|
||||
}
|
||||
|
||||
static void rcheevos_invalidate_address(unsigned address)
|
||||
{
|
||||
unsigned i, count;
|
||||
@@ -406,12 +437,12 @@ static void rcheevos_invalidate_address(unsigned address)
|
||||
* try to evaluate it in the future.
|
||||
* It's still there, so anything referencing it will
|
||||
* continue to fetch 0. */
|
||||
rc_memref_value_t **last_memref = &rcheevos_locals.runtime.memrefs;
|
||||
rc_memref_value_t *memref = *last_memref;
|
||||
rc_memref_t **last_memref = &rcheevos_locals.runtime.memrefs;
|
||||
rc_memref_t *memref = *last_memref;
|
||||
|
||||
do
|
||||
{
|
||||
if (memref->memref.address == address && !memref->memref.is_indirect)
|
||||
if (memref->address == address && !memref->value.is_indirect)
|
||||
{
|
||||
*last_memref = memref->next;
|
||||
break;
|
||||
@@ -449,12 +480,9 @@ static void rcheevos_invalidate_address(unsigned address)
|
||||
|
||||
if (trigger && rcheevos_trigger_contains_memref(trigger, memref))
|
||||
{
|
||||
CHEEVOS_ERR(RCHEEVOS_TAG "Achievement disabled (invalid address %06X): %s\n", address, cheevo->title);
|
||||
rcheevos_achievement_disabled(cheevo, address);
|
||||
rc_runtime_deactivate_achievement(&rcheevos_locals.runtime,
|
||||
cheevo->id);
|
||||
|
||||
CHEEVOS_FREE(cheevo->memaddr);
|
||||
cheevo->memaddr = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -479,11 +507,8 @@ static void rcheevos_invalidate_address(unsigned address)
|
||||
memref))
|
||||
)
|
||||
{
|
||||
CHEEVOS_ERR(RCHEEVOS_TAG "Leaderboard disabled (invalid address %06X): %s\n", address, lboard->title);
|
||||
rcheevos_lboard_disabled(lboard, address);
|
||||
rc_runtime_deactivate_lboard(&rcheevos_locals.runtime, lboard->id);
|
||||
|
||||
CHEEVOS_FREE(lboard->mem);
|
||||
lboard->mem = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -491,6 +516,12 @@ static void rcheevos_invalidate_address(unsigned address)
|
||||
|
||||
uint8_t* rcheevos_patch_address(unsigned address)
|
||||
{
|
||||
if (rcheevos_locals.memory.count == 0)
|
||||
{
|
||||
/* memory map was not previously initialized (no achievements for this game?) try now */
|
||||
rcheevos_memory_init(&rcheevos_locals.memory, rcheevos_locals.patchdata.console_id);
|
||||
}
|
||||
|
||||
return rcheevos_memory_find(&rcheevos_locals.memory, address);
|
||||
}
|
||||
|
||||
@@ -552,15 +583,17 @@ static retro_time_t rcheevos_async_send_rich_presence(
|
||||
rcheevos_async_io_request* request)
|
||||
{
|
||||
char url[256], post_data[1024];
|
||||
settings_t *settings = config_get_ptr();
|
||||
const char *cheevos_username = settings->arrays.cheevos_username;
|
||||
bool cheevos_richpresence_enable = settings->bools.cheevos_richpresence_enable;
|
||||
int ret = rc_url_ping(
|
||||
url, sizeof(url), post_data, sizeof(post_data),
|
||||
cheevos_username, locals->token, locals->patchdata.game_id,
|
||||
cheevos_richpresence_enable
|
||||
? rc_runtime_get_richpresence(&locals->runtime)
|
||||
: "");
|
||||
char buffer[256] = "";
|
||||
const settings_t *settings = config_get_ptr();
|
||||
const char *cheevos_username = settings->arrays.cheevos_username;
|
||||
const bool cheevos_richpresence_enable = settings->bools.cheevos_richpresence_enable;
|
||||
int ret;
|
||||
|
||||
if (cheevos_richpresence_enable)
|
||||
rcheevos_get_richpresence(buffer, sizeof(buffer));
|
||||
|
||||
ret = rc_url_ping(url, sizeof(url), post_data, sizeof(post_data),
|
||||
cheevos_username, locals->token, locals->patchdata.game_id, buffer);
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
@@ -576,12 +609,8 @@ static retro_time_t rcheevos_async_send_rich_presence(
|
||||
}
|
||||
|
||||
#ifdef HAVE_DISCORD
|
||||
if (locals->runtime.richpresence_display_buffer)
|
||||
{
|
||||
if (settings->bools.discord_enable
|
||||
&& discord_is_ready())
|
||||
discord_update(DISCORD_PRESENCE_RETROACHIEVEMENTS);
|
||||
}
|
||||
if (settings->bools.discord_enable && discord_is_ready())
|
||||
discord_update(DISCORD_PRESENCE_RETROACHIEVEMENTS);
|
||||
#endif
|
||||
|
||||
/* Update rich presence every two minutes */
|
||||
@@ -722,15 +751,16 @@ static void rcheevos_async_task_callback(
|
||||
|
||||
static void rcheevos_validate_memrefs(rcheevos_locals_t* locals)
|
||||
{
|
||||
rc_memref_value_t* memref = locals->runtime.memrefs;
|
||||
rc_memref_t* memref = locals->runtime.memrefs;
|
||||
|
||||
while (memref)
|
||||
{
|
||||
if (!memref->memref.is_indirect)
|
||||
if (!memref->value.is_indirect)
|
||||
{
|
||||
uint8_t* data = rcheevos_memory_find(&rcheevos_locals.memory,
|
||||
memref->memref.address);
|
||||
memref->address);
|
||||
if (!data)
|
||||
rcheevos_invalidate_address(memref->memref.address);
|
||||
rcheevos_invalidate_address(memref->address);
|
||||
}
|
||||
|
||||
memref = memref->next;
|
||||
@@ -895,22 +925,6 @@ static int rcheevos_parse(rcheevos_locals_t *locals, const char* json)
|
||||
}
|
||||
}
|
||||
|
||||
if (res < 0 && locals->patchdata.title)
|
||||
{
|
||||
size_t len = strlen(locals->patchdata.title);
|
||||
|
||||
if (!locals->runtime.richpresence_display_buffer)
|
||||
locals->runtime.richpresence_display_buffer = (char*)malloc(len + 9);
|
||||
|
||||
if (locals->runtime.richpresence_display_buffer)
|
||||
{
|
||||
memcpy(locals->runtime.richpresence_display_buffer,
|
||||
"Playing ", 8);
|
||||
memcpy(&locals->runtime.richpresence_display_buffer[8],
|
||||
locals->patchdata.title, len + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* schedule the first rich presence call in 30 seconds */
|
||||
{
|
||||
rcheevos_async_io_request* request = (rcheevos_async_io_request*)
|
||||
@@ -1009,13 +1023,17 @@ static void rcheevos_award_achievement(rcheevos_locals_t *locals,
|
||||
/* Take a screenshot of the achievement. */
|
||||
if (settings && settings->bools.cheevos_auto_screenshot)
|
||||
{
|
||||
char shotname[8192];
|
||||
size_t shotname_len = sizeof(char) * 8192;
|
||||
char *shotname = (char*)malloc(shotname_len);
|
||||
|
||||
snprintf(shotname, sizeof(shotname), "%s/%s-cheevo-%u",
|
||||
if (!shotname)
|
||||
return;
|
||||
|
||||
snprintf(shotname, shotname_len, "%s/%s-cheevo-%u",
|
||||
settings->paths.directory_screenshot,
|
||||
path_basename(path_get(RARCH_PATH_BASENAME)),
|
||||
cheevo->id);
|
||||
shotname[sizeof(shotname) - 1] = '\0';
|
||||
shotname[shotname_len - 1] = '\0';
|
||||
|
||||
if (take_screenshot(settings->paths.directory_screenshot,
|
||||
shotname, true,
|
||||
@@ -1028,6 +1046,7 @@ static void rcheevos_award_achievement(rcheevos_locals_t *locals,
|
||||
CHEEVOS_LOG(
|
||||
RCHEEVOS_TAG "Failed to capture screenshot for achievement %u\n",
|
||||
cheevo->id);
|
||||
free(shotname);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1182,9 +1201,14 @@ static void rcheevos_lboard_updated(rcheevos_ralboard_t* lboard, int value,
|
||||
}
|
||||
#endif
|
||||
|
||||
const char* rcheevos_get_richpresence(void)
|
||||
int rcheevos_get_richpresence(char buffer[], int buffer_size)
|
||||
{
|
||||
return rc_runtime_get_richpresence(&rcheevos_locals.runtime);
|
||||
int ret = rc_runtime_get_richpresence(&rcheevos_locals.runtime, buffer, buffer_size, &rcheevos_peek, NULL, NULL);
|
||||
|
||||
if (ret <= 0 && rcheevos_locals.patchdata.title)
|
||||
ret = snprintf(buffer, buffer_size, "Playing %s", rcheevos_locals.patchdata.title);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void rcheevos_reset_game(bool widgets_ready)
|
||||
@@ -1471,10 +1495,12 @@ bool rcheevos_unload(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
if (rcheevos_locals.memory.count > 0)
|
||||
rcheevos_memory_destroy(&rcheevos_locals.memory);
|
||||
|
||||
if (rcheevos_locals.loaded)
|
||||
{
|
||||
rcheevos_free_patchdata(&rcheevos_locals.patchdata);
|
||||
rcheevos_memory_destroy(&rcheevos_locals.memory);
|
||||
#ifdef HAVE_MENU
|
||||
cheevos_reset_menu_badges();
|
||||
#endif
|
||||
@@ -1833,7 +1859,7 @@ void rcheevos_validate_config_settings(void)
|
||||
|
||||
if (rarch_ctl(RARCH_CTL_CORE_OPTIONS_LIST_GET, &coreopts))
|
||||
{
|
||||
int i;
|
||||
size_t i;
|
||||
const char *val = NULL;
|
||||
const rc_disallowed_setting_t
|
||||
*disallowed_setting = core_filter->disallowed_settings;
|
||||
@@ -1938,6 +1964,14 @@ static void rcheevos_runtime_event_handler(const rc_runtime_event_t* runtime_eve
|
||||
rcheevos_lboard_submit(&rcheevos_locals, rcheevos_find_lboard(runtime_event->id), runtime_event->value, widgets_ready);
|
||||
break;
|
||||
|
||||
case RC_RUNTIME_EVENT_ACHIEVEMENT_DISABLED:
|
||||
rcheevos_achievement_disabled(rcheevos_find_cheevo(runtime_event->id), runtime_event->value);
|
||||
break;
|
||||
|
||||
case RC_RUNTIME_EVENT_LBOARD_DISABLED:
|
||||
rcheevos_lboard_disabled(rcheevos_find_lboard(runtime_event->id), runtime_event->value);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -2823,12 +2857,12 @@ static void* rc_hash_handle_file_open(const char* path)
|
||||
return intfstream_open_file(path, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
}
|
||||
|
||||
static void rc_hash_handle_file_seek(void* file_handle, size_t offset, int origin)
|
||||
static void rc_hash_handle_file_seek(void* file_handle, int64_t offset, int origin)
|
||||
{
|
||||
intfstream_seek((intfstream_t*)file_handle, offset, origin);
|
||||
}
|
||||
|
||||
static size_t rc_hash_handle_file_tell(void* file_handle)
|
||||
static int64_t rc_hash_handle_file_tell(void* file_handle)
|
||||
{
|
||||
return intfstream_tell((intfstream_t*)file_handle);
|
||||
}
|
||||
@@ -2848,21 +2882,51 @@ static void* rc_hash_handle_cd_open_track(const char* path, uint32_t track)
|
||||
{
|
||||
cdfs_track_t* cdfs_track;
|
||||
|
||||
if (track == 0)
|
||||
cdfs_track = cdfs_open_data_track(path);
|
||||
else
|
||||
cdfs_track = cdfs_open_track(path, track);
|
||||
switch (track)
|
||||
{
|
||||
case RC_HASH_CDTRACK_FIRST_DATA:
|
||||
cdfs_track = cdfs_open_data_track(path);
|
||||
break;
|
||||
|
||||
case RC_HASH_CDTRACK_LAST:
|
||||
#ifdef HAVE_CHD
|
||||
if (string_is_equal_noncase(path_get_extension(path), "chd"))
|
||||
{
|
||||
cdfs_track = cdfs_open_track(path, CHDSTREAM_TRACK_LAST);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "Last track only supported for CHD\n");
|
||||
cdfs_track = NULL;
|
||||
break;
|
||||
|
||||
case RC_HASH_CDTRACK_LARGEST:
|
||||
#ifdef HAVE_CHD
|
||||
if (string_is_equal_noncase(path_get_extension(path), "chd"))
|
||||
{
|
||||
cdfs_track = cdfs_open_track(path, CHDSTREAM_TRACK_PRIMARY);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "Largest track only supported for CHD, using first data track\n");
|
||||
cdfs_track = cdfs_open_data_track(path);
|
||||
break;
|
||||
|
||||
default:
|
||||
cdfs_track = cdfs_open_track(path, track);
|
||||
break;
|
||||
}
|
||||
|
||||
if (cdfs_track)
|
||||
{
|
||||
cdfs_file_t* file = (cdfs_file_t*)malloc(sizeof(cdfs_file_t));
|
||||
if (cdfs_open_file(file, cdfs_track, NULL))
|
||||
return file;
|
||||
return file; /* ASSERT: file owns cdfs_track now */
|
||||
|
||||
CHEEVOS_FREE(file);
|
||||
cdfs_close_track(cdfs_track); /* ASSERT: this free()s cdfs_track */
|
||||
}
|
||||
|
||||
cdfs_close_track(cdfs_track); /* ASSERT: this free()s cdfs_track */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -2925,6 +2989,7 @@ bool rcheevos_load(const void *data)
|
||||
return false;
|
||||
|
||||
/* provide hooks for reading files */
|
||||
memset(&filereader, 0, sizeof(filereader));
|
||||
filereader.open = rc_hash_handle_file_open;
|
||||
filereader.seek = rc_hash_handle_file_seek;
|
||||
filereader.tell = rc_hash_handle_file_tell;
|
||||
@@ -2932,6 +2997,7 @@ bool rcheevos_load(const void *data)
|
||||
filereader.close = rc_hash_handle_file_close;
|
||||
rc_hash_init_custom_filereader(&filereader);
|
||||
|
||||
memset(&cdreader, 0, sizeof(cdreader));
|
||||
cdreader.open_track = rc_hash_handle_cd_open_track;
|
||||
cdreader.read_sector = rc_hash_handle_cd_read_sector;
|
||||
cdreader.close_track = rc_hash_handle_cd_close_track;
|
||||
|
||||
+1
-1
@@ -73,7 +73,7 @@ bool rcheevos_get_support_cheevos(void);
|
||||
|
||||
const char* rcheevos_get_hash(void);
|
||||
|
||||
const char *rcheevos_get_richpresence(void);
|
||||
int rcheevos_get_richpresence(char buffer[], int buffer_size);
|
||||
|
||||
uint8_t* rcheevos_patch_address(unsigned address);
|
||||
|
||||
|
||||
@@ -0,0 +1,535 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2017 - Daniel De Matteis
|
||||
* Copyright (C) 2021 - David G.F.
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#ifdef HAVE_NETWORKING
|
||||
#include <net/net_compat.h>
|
||||
#include <net/net_socket.h>
|
||||
#endif
|
||||
#include <streams/stdin_stream.h>
|
||||
#include <string/stdstring.h>
|
||||
|
||||
#include "verbosity.h"
|
||||
#include "command.h"
|
||||
|
||||
#define CMD_BUF_SIZE 4096
|
||||
|
||||
#if defined(HAVE_COMMAND)
|
||||
|
||||
/* Generic command parse utilities */
|
||||
|
||||
static bool command_get_arg(const char *tok,
|
||||
const char **arg, unsigned *index)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(map); i++)
|
||||
{
|
||||
if (string_is_equal(tok, map[i].str))
|
||||
{
|
||||
if (arg)
|
||||
*arg = NULL;
|
||||
|
||||
if (index)
|
||||
*index = i;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(action_map); i++)
|
||||
{
|
||||
const char *str = strstr(tok, action_map[i].str);
|
||||
if (str == tok)
|
||||
{
|
||||
const char *argument = str + strlen(action_map[i].str);
|
||||
if (*argument != ' ' && *argument != '\0')
|
||||
return false;
|
||||
|
||||
if (arg)
|
||||
*arg = argument + 1;
|
||||
|
||||
if (index)
|
||||
*index = i;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void command_parse_sub_msg(command_t *handle, const char *tok)
|
||||
{
|
||||
const char *arg = NULL;
|
||||
unsigned index = 0;
|
||||
|
||||
if (command_get_arg(tok, &arg, &index))
|
||||
{
|
||||
if (arg)
|
||||
{
|
||||
if (!action_map[index].action(handle, arg))
|
||||
RARCH_ERR("Command \"%s\" failed.\n", arg);
|
||||
}
|
||||
else
|
||||
handle->state[map[index].id] = true;
|
||||
}
|
||||
else
|
||||
RARCH_WARN("%s \"%s\" %s.\n",
|
||||
msg_hash_to_str(MSG_UNRECOGNIZED_COMMAND),
|
||||
tok,
|
||||
msg_hash_to_str(MSG_RECEIVED));
|
||||
}
|
||||
|
||||
static void command_parse_msg(
|
||||
command_t *handle, char *buf)
|
||||
{
|
||||
char *save = NULL;
|
||||
const char *tok = strtok_r(buf, "\n", &save);
|
||||
|
||||
while (tok)
|
||||
{
|
||||
command_parse_sub_msg(handle, tok);
|
||||
tok = strtok_r(NULL, "\n", &save);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(HAVE_NETWORK_CMD)
|
||||
typedef struct
|
||||
{
|
||||
/* Network socket FD */
|
||||
int net_fd;
|
||||
/* Source address for the command received */
|
||||
struct sockaddr_storage cmd_source;
|
||||
/* Size of the previous structure in use */
|
||||
socklen_t cmd_source_len;
|
||||
} command_network_t;
|
||||
|
||||
static void network_command_reply(
|
||||
command_t *cmd,
|
||||
const char * data, size_t len)
|
||||
{
|
||||
command_network_t *netcmd = (command_network_t*)cmd->userptr;
|
||||
/* Respond (fire and forget since it's UDP) */
|
||||
sendto(netcmd->net_fd, data, len, 0,
|
||||
(struct sockaddr*)&netcmd->cmd_source, netcmd->cmd_source_len);
|
||||
}
|
||||
|
||||
static void network_command_free(command_t *handle)
|
||||
{
|
||||
command_network_t *netcmd = (command_network_t*)handle->userptr;
|
||||
|
||||
if (netcmd->net_fd >= 0)
|
||||
socket_close(netcmd->net_fd);
|
||||
|
||||
free(netcmd);
|
||||
free(handle);
|
||||
}
|
||||
|
||||
static void command_network_poll(command_t *handle)
|
||||
{
|
||||
fd_set fds;
|
||||
struct timeval tmp_tv = {0};
|
||||
command_network_t *netcmd = (command_network_t*)handle->userptr;
|
||||
|
||||
if (netcmd->net_fd < 0)
|
||||
return;
|
||||
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(netcmd->net_fd, &fds);
|
||||
|
||||
if (socket_select(netcmd->net_fd + 1, &fds, NULL, NULL, &tmp_tv) <= 0)
|
||||
return;
|
||||
|
||||
if (!FD_ISSET(netcmd->net_fd, &fds))
|
||||
return;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
ssize_t ret;
|
||||
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);
|
||||
|
||||
if (ret <= 0)
|
||||
break;
|
||||
|
||||
buf[ret] = '\0';
|
||||
|
||||
command_parse_msg(handle, buf);
|
||||
}
|
||||
}
|
||||
|
||||
command_t* command_network_new(uint16_t port)
|
||||
{
|
||||
struct addrinfo *res = NULL;
|
||||
command_t *cmd = (command_t*)calloc(1, sizeof(command_t));
|
||||
command_network_t *netcmd = (command_network_t*)calloc(
|
||||
1, sizeof(command_network_t));
|
||||
int fd = socket_init(
|
||||
(void**)&res, port, NULL, SOCKET_TYPE_DATAGRAM);
|
||||
|
||||
RARCH_LOG("%s %hu.\n",
|
||||
msg_hash_to_str(MSG_BRINGING_UP_COMMAND_INTERFACE_ON_PORT),
|
||||
(unsigned short)port);
|
||||
|
||||
if (fd < 0)
|
||||
goto error;
|
||||
|
||||
netcmd->net_fd = fd;
|
||||
cmd->userptr = netcmd;
|
||||
cmd->poll = command_network_poll;
|
||||
cmd->replier = network_command_reply;
|
||||
cmd->destroy = network_command_free;
|
||||
|
||||
if (!socket_nonblock(netcmd->net_fd))
|
||||
goto error;
|
||||
|
||||
if (!socket_bind(netcmd->net_fd, (void*)res))
|
||||
{
|
||||
RARCH_ERR("%s.\n",
|
||||
msg_hash_to_str(MSG_FAILED_TO_BIND_SOCKET));
|
||||
goto error;
|
||||
}
|
||||
|
||||
freeaddrinfo_retro(res);
|
||||
return cmd;
|
||||
|
||||
error:
|
||||
if (res)
|
||||
freeaddrinfo_retro(res);
|
||||
free(netcmd);
|
||||
free(cmd);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(HAVE_STDIN_CMD)
|
||||
typedef struct
|
||||
{
|
||||
/* Buffer and pointer for stdin reads */
|
||||
size_t stdin_buf_ptr;
|
||||
char stdin_buf[CMD_BUF_SIZE];
|
||||
} command_stdin_t;
|
||||
|
||||
static void stdin_command_reply(
|
||||
command_t *cmd,
|
||||
const char * data, size_t len)
|
||||
{
|
||||
/* Just write to stdout! */
|
||||
fwrite(data, 1, len, stdout);
|
||||
}
|
||||
|
||||
static void stdin_command_free(command_t *handle)
|
||||
{
|
||||
free(handle->userptr);
|
||||
free(handle);
|
||||
}
|
||||
|
||||
static void command_stdin_poll(command_t *handle)
|
||||
{
|
||||
ptrdiff_t msg_len;
|
||||
char *last_newline = NULL;
|
||||
command_stdin_t *stdincmd = (command_stdin_t*)handle->userptr;
|
||||
ssize_t ret = read_stdin(
|
||||
stdincmd->stdin_buf + stdincmd->stdin_buf_ptr,
|
||||
CMD_BUF_SIZE - stdincmd->stdin_buf_ptr - 1);
|
||||
|
||||
if (ret == 0)
|
||||
return;
|
||||
|
||||
stdincmd->stdin_buf_ptr += ret;
|
||||
stdincmd->stdin_buf[stdincmd->stdin_buf_ptr] = '\0';
|
||||
|
||||
last_newline = strrchr(stdincmd->stdin_buf, '\n');
|
||||
|
||||
if (!last_newline)
|
||||
{
|
||||
/* We're receiving bogus data in pipe
|
||||
* (no terminating newline), flush out the buffer. */
|
||||
if (stdincmd->stdin_buf_ptr + 1 >= CMD_BUF_SIZE)
|
||||
{
|
||||
stdincmd->stdin_buf_ptr = 0;
|
||||
stdincmd->stdin_buf[0] = '\0';
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
*last_newline++ = '\0';
|
||||
msg_len = last_newline - stdincmd->stdin_buf;
|
||||
|
||||
command_parse_msg(handle, stdincmd->stdin_buf);
|
||||
|
||||
memmove(stdincmd->stdin_buf, last_newline,
|
||||
stdincmd->stdin_buf_ptr - msg_len);
|
||||
stdincmd->stdin_buf_ptr -= msg_len;
|
||||
}
|
||||
|
||||
command_t* command_stdin_new(void)
|
||||
{
|
||||
command_t *cmd;
|
||||
command_stdin_t *stdincmd;
|
||||
|
||||
#ifndef _WIN32
|
||||
#ifdef HAVE_NETWORKING
|
||||
if (!socket_nonblock(STDIN_FILENO))
|
||||
return NULL;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
cmd = (command_t*)calloc(1, sizeof(command_t));
|
||||
stdincmd = (command_stdin_t*)calloc(1, sizeof(command_stdin_t));
|
||||
cmd->userptr = stdincmd;
|
||||
cmd->poll = command_stdin_poll;
|
||||
cmd->replier = stdin_command_reply;
|
||||
cmd->destroy = stdin_command_free;
|
||||
|
||||
return cmd;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_LAKKA)
|
||||
#include <sys/un.h>
|
||||
#define MAX_USER_CONNECTIONS 4
|
||||
typedef struct
|
||||
{
|
||||
/* File descriptor for the domain socket */
|
||||
int sfd;
|
||||
/* Client sockets */
|
||||
int userfd[MAX_USER_CONNECTIONS];
|
||||
/* Last received user socket */
|
||||
int last_fd;
|
||||
} command_uds_t;
|
||||
|
||||
static void uds_command_reply(
|
||||
command_t *cmd,
|
||||
const char * data, size_t len)
|
||||
{
|
||||
command_uds_t *subcmd = (command_uds_t*)cmd->userptr;
|
||||
write(subcmd->last_fd, data, len);
|
||||
}
|
||||
|
||||
static void uds_command_free(command_t *handle)
|
||||
{
|
||||
int i;
|
||||
command_uds_t *udscmd = (command_uds_t*)handle->userptr;
|
||||
|
||||
for (i = 0; i < MAX_USER_CONNECTIONS; i++)
|
||||
if (udscmd->userfd[i] >= 0)
|
||||
socket_close(udscmd->userfd[i]);
|
||||
socket_close(udscmd->sfd);
|
||||
|
||||
free(handle->userptr);
|
||||
free(handle);
|
||||
}
|
||||
|
||||
static void command_uds_poll(command_t *handle)
|
||||
{
|
||||
int i;
|
||||
fd_set fds;
|
||||
command_uds_t *udscmd = (command_uds_t*)handle->userptr;
|
||||
int maxfd = udscmd->sfd;
|
||||
struct timeval tmp_tv = {0};
|
||||
|
||||
if (udscmd->sfd < 0)
|
||||
return;
|
||||
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(udscmd->sfd, &fds);
|
||||
|
||||
for (i = 0; i < MAX_USER_CONNECTIONS; i++)
|
||||
{
|
||||
if (udscmd->userfd[i] >= 0)
|
||||
{
|
||||
maxfd = MAX(udscmd->userfd[i], maxfd);
|
||||
FD_SET(udscmd->userfd[i], &fds);
|
||||
}
|
||||
}
|
||||
|
||||
if (socket_select(maxfd + 1, &fds, NULL, NULL, &tmp_tv) <= 0)
|
||||
return;
|
||||
|
||||
/* Read data from clients and process commands */
|
||||
for (i = 0; i < MAX_USER_CONNECTIONS; i++)
|
||||
{
|
||||
if (udscmd->userfd[i] >= 0 && FD_ISSET(udscmd->userfd[i], &fds))
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
char buf[2048];
|
||||
ssize_t ret = recv(udscmd->userfd[i], buf, sizeof(buf) - 1, 0);
|
||||
|
||||
if (ret < 0)
|
||||
break; /* no more data */
|
||||
if (!ret)
|
||||
{
|
||||
socket_close(udscmd->userfd[i]);
|
||||
udscmd->userfd[i] = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
buf[ret] = 0;
|
||||
udscmd->last_fd = udscmd->userfd[i];
|
||||
command_parse_msg(handle, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (FD_ISSET(udscmd->sfd, &fds))
|
||||
{
|
||||
/* Accepts new connections from clients */
|
||||
int cfd = accept(udscmd->sfd, NULL, NULL);
|
||||
if (cfd >= 0) {
|
||||
if (!socket_nonblock(cfd))
|
||||
socket_close(cfd);
|
||||
else {
|
||||
for (i = 0; i < MAX_USER_CONNECTIONS; i++)
|
||||
if (udscmd->userfd[i] < 0)
|
||||
{
|
||||
udscmd->userfd[i] = cfd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
command_t* command_uds_new(void)
|
||||
{
|
||||
int i;
|
||||
command_t *cmd;
|
||||
command_uds_t *subcmd;
|
||||
struct sockaddr_un addr;
|
||||
const char *sp = "retroarch/cmd";
|
||||
socklen_t addrsz = offsetof(struct sockaddr_un, sun_path) + strlen(sp) + 1;
|
||||
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd < 0)
|
||||
return NULL;
|
||||
|
||||
/* use an abstract socket for simplicity */
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
strcpy(&addr.sun_path[1], sp);
|
||||
|
||||
if (bind(fd, (struct sockaddr*)&addr, addrsz) < 0 ||
|
||||
listen(fd, MAX_USER_CONNECTIONS) < 0)
|
||||
{
|
||||
socket_close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!socket_nonblock(fd))
|
||||
{
|
||||
socket_close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cmd = (command_t*)calloc(1, sizeof(command_t));
|
||||
subcmd = (command_uds_t*)calloc(1, sizeof(command_uds_t));
|
||||
subcmd->sfd = fd;
|
||||
subcmd->last_fd = -1;
|
||||
for (i = 0; i < MAX_USER_CONNECTIONS; i++)
|
||||
subcmd->userfd[i] = -1;
|
||||
|
||||
cmd->userptr = subcmd;
|
||||
cmd->poll = command_uds_poll;
|
||||
cmd->replier = uds_command_reply;
|
||||
cmd->destroy = uds_command_free;
|
||||
|
||||
return cmd;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* Routines used to invoke retroarch command ... */
|
||||
|
||||
#ifdef HAVE_NETWORK_CMD
|
||||
static bool command_verify(const char *cmd)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
if (command_get_arg(cmd, NULL, NULL))
|
||||
return true;
|
||||
|
||||
RARCH_ERR("Command \"%s\" is not recognized by the program.\n", cmd);
|
||||
RARCH_ERR("\tValid commands:\n");
|
||||
for (i = 0; i < ARRAY_SIZE(map); i++)
|
||||
RARCH_ERR("\t\t%s\n", map[i].str);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(action_map); i++)
|
||||
RARCH_ERR("\t\t%s %s\n", action_map[i].str, action_map[i].arg_desc);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool command_network_send(const char *cmd_)
|
||||
{
|
||||
char *command = NULL;
|
||||
char *save = NULL;
|
||||
const char *cmd = NULL;
|
||||
|
||||
if (!network_init())
|
||||
return false;
|
||||
|
||||
if (!(command = strdup(cmd_)))
|
||||
return false;
|
||||
|
||||
cmd = strtok_r(command, ";", &save);
|
||||
if (cmd)
|
||||
{
|
||||
uint16_t port = DEFAULT_NETWORK_CMD_PORT;
|
||||
const char *port_ = NULL;
|
||||
const char *host = strtok_r(NULL, ";", &save);
|
||||
if (host)
|
||||
port_ = strtok_r(NULL, ";", &save);
|
||||
else
|
||||
{
|
||||
#ifdef _WIN32
|
||||
host = "127.0.0.1";
|
||||
#else
|
||||
host = "localhost";
|
||||
#endif
|
||||
}
|
||||
|
||||
if (port_)
|
||||
port = strtoul(port_, NULL, 0);
|
||||
|
||||
RARCH_LOG("%s: \"%s\" to %s:%hu\n",
|
||||
msg_hash_to_str(MSG_SENDING_COMMAND),
|
||||
cmd, host, (unsigned short)port);
|
||||
|
||||
if (command_verify(cmd) && udp_send_packet(host, port, cmd))
|
||||
{
|
||||
free(command);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
free(command);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,7 +1,7 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2016 - Daniel De Matteis
|
||||
* Copyright (C) 2016-2019 - Brad Parker
|
||||
* Copyright (C) 2021 - David G.F.
|
||||
*
|
||||
* 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-
|
||||
@@ -23,12 +23,46 @@
|
||||
#include <boolean.h>
|
||||
#include <retro_common_api.h>
|
||||
|
||||
#include "retroarch.h"
|
||||
#include "input/input_defines.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
#define MAX_CMD_DRIVERS 3
|
||||
#define DEFAULT_NETWORK_CMD_PORT 55355
|
||||
|
||||
struct cmd_map
|
||||
{
|
||||
const char *str;
|
||||
unsigned id;
|
||||
};
|
||||
|
||||
struct command_handler;
|
||||
|
||||
typedef void (*command_poller_t)(struct command_handler *cmd);
|
||||
typedef void (*command_replier_t)(struct command_handler *cmd, const char * data, size_t len);
|
||||
typedef void (*command_destructor_t)(struct command_handler *cmd);
|
||||
|
||||
struct command_handler
|
||||
{
|
||||
/* Interface to poll the driver */
|
||||
command_poller_t poll;
|
||||
/* Interface to reply */
|
||||
command_replier_t replier;
|
||||
/* Interface to delete the underlying command */
|
||||
command_destructor_t destroy;
|
||||
/* Underlying command storage */
|
||||
void *userptr;
|
||||
/* State received */
|
||||
bool state[RARCH_BIND_LIST_END];
|
||||
};
|
||||
|
||||
typedef struct command_handler command_t;
|
||||
|
||||
enum event_command
|
||||
{
|
||||
CMD_EVENT_NONE = 0,
|
||||
@@ -166,7 +200,6 @@ enum event_command
|
||||
CMD_EVENT_AUDIO_REINIT,
|
||||
/* Resizes windowed scale. Will reinitialize video driver. */
|
||||
CMD_EVENT_RESIZE_WINDOWED_SCALE,
|
||||
CMD_EVENT_LOG_FILE_DEINIT,
|
||||
/* Toggles disk eject. */
|
||||
CMD_EVENT_DISK_EJECT_TOGGLE,
|
||||
/* Cycle to next disk. */
|
||||
@@ -205,17 +238,25 @@ enum event_command
|
||||
CMD_EVENT_CHEAT_INDEX_MINUS,
|
||||
CMD_EVENT_CHEAT_TOGGLE,
|
||||
CMD_EVENT_AI_SERVICE_CALL,
|
||||
CMD_EVENT_SAVE_FILES
|
||||
CMD_EVENT_SAVE_FILES,
|
||||
CMD_EVENT_CONTROLLER_INIT
|
||||
};
|
||||
|
||||
typedef struct command command_t;
|
||||
|
||||
typedef struct command_handle
|
||||
{
|
||||
command_t *handle;
|
||||
unsigned id;
|
||||
} command_handle_t;
|
||||
|
||||
enum cmd_source_t
|
||||
{
|
||||
CMD_NONE = 0,
|
||||
CMD_STDIN,
|
||||
CMD_NETWORK
|
||||
};
|
||||
|
||||
struct rarch_state;
|
||||
|
||||
/**
|
||||
* command_event:
|
||||
* @cmd : Command index.
|
||||
@@ -226,6 +267,107 @@ typedef struct command_handle
|
||||
**/
|
||||
bool command_event(enum event_command action, void *data);
|
||||
|
||||
/* Constructors for the supported drivers */
|
||||
command_t* command_network_new(uint16_t port);
|
||||
command_t* command_stdin_new(void);
|
||||
command_t* command_uds_new(void);
|
||||
|
||||
bool command_network_send(const char *cmd_);
|
||||
|
||||
/* These forward declarations need to be declared before
|
||||
* the global state is declared */
|
||||
#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
|
||||
bool command_set_shader(command_t *cmd, const char *arg);
|
||||
#endif
|
||||
#if defined(HAVE_COMMAND)
|
||||
bool command_version(command_t *cmd, const char* arg);
|
||||
bool command_get_status(command_t *cmd, const char* arg);
|
||||
bool command_get_config_param(command_t *cmd, const char* arg);
|
||||
bool command_show_osd_msg(command_t *cmd, const char* arg);
|
||||
#ifdef HAVE_CHEEVOS
|
||||
bool command_read_ram(command_t *cmd, const char *arg);
|
||||
bool command_write_ram(command_t *cmd, const char *arg);
|
||||
#endif
|
||||
bool command_read_memory(command_t *cmd, const char *arg);
|
||||
bool command_write_memory(command_t *cmd, const char *arg);
|
||||
|
||||
struct cmd_action_map
|
||||
{
|
||||
const char *str;
|
||||
bool (*action)(command_t* cmd, const char *arg);
|
||||
const char *arg_desc;
|
||||
};
|
||||
|
||||
static const struct cmd_action_map action_map[] = {
|
||||
#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
|
||||
{ "SET_SHADER", command_set_shader, "<shader path>" },
|
||||
#endif
|
||||
{ "VERSION", command_version, "No argument"},
|
||||
{ "GET_STATUS", command_get_status, "No argument" },
|
||||
{ "GET_CONFIG_PARAM", command_get_config_param, "<param name>" },
|
||||
{ "SHOW_MSG", command_show_osd_msg, "No argument" },
|
||||
#if defined(HAVE_CHEEVOS)
|
||||
/* These functions use achievement addresses and only work if a game with achievements is
|
||||
* loaded. READ_CORE_MEMORY and WRITE_CORE_MEMORY are preferred and use system addresses. */
|
||||
{ "READ_CORE_RAM", command_read_ram, "<address> <number of bytes>" },
|
||||
{ "WRITE_CORE_RAM", command_write_ram, "<address> <byte1> <byte2> ..." },
|
||||
#endif
|
||||
{ "READ_CORE_MEMORY", command_read_memory, "<address> <number of bytes>" },
|
||||
{ "WRITE_CORE_MEMORY",command_write_memory, "<address> <byte1> <byte2> ..." },
|
||||
};
|
||||
|
||||
static const struct cmd_map map[] = {
|
||||
{ "FAST_FORWARD", RARCH_FAST_FORWARD_KEY },
|
||||
{ "FAST_FORWARD_HOLD", RARCH_FAST_FORWARD_HOLD_KEY },
|
||||
{ "SLOWMOTION", RARCH_SLOWMOTION_KEY },
|
||||
{ "SLOWMOTION_HOLD", RARCH_SLOWMOTION_HOLD_KEY },
|
||||
{ "LOAD_STATE", RARCH_LOAD_STATE_KEY },
|
||||
{ "SAVE_STATE", RARCH_SAVE_STATE_KEY },
|
||||
{ "FULLSCREEN_TOGGLE", RARCH_FULLSCREEN_TOGGLE_KEY },
|
||||
{ "CLOSE_CONTENT", RARCH_CLOSE_CONTENT_KEY },
|
||||
{ "QUIT", RARCH_QUIT_KEY },
|
||||
{ "STATE_SLOT_PLUS", RARCH_STATE_SLOT_PLUS },
|
||||
{ "STATE_SLOT_MINUS", RARCH_STATE_SLOT_MINUS },
|
||||
{ "REWIND", RARCH_REWIND },
|
||||
{ "BSV_RECORD_TOGGLE", RARCH_BSV_RECORD_TOGGLE },
|
||||
{ "PAUSE_TOGGLE", RARCH_PAUSE_TOGGLE },
|
||||
{ "FRAMEADVANCE", RARCH_FRAMEADVANCE },
|
||||
{ "RESET", RARCH_RESET },
|
||||
{ "SHADER_NEXT", RARCH_SHADER_NEXT },
|
||||
{ "SHADER_PREV", RARCH_SHADER_PREV },
|
||||
{ "CHEAT_INDEX_PLUS", RARCH_CHEAT_INDEX_PLUS },
|
||||
{ "CHEAT_INDEX_MINUS", RARCH_CHEAT_INDEX_MINUS },
|
||||
{ "CHEAT_TOGGLE", RARCH_CHEAT_TOGGLE },
|
||||
{ "SCREENSHOT", RARCH_SCREENSHOT },
|
||||
{ "MUTE", RARCH_MUTE },
|
||||
{ "OSK", RARCH_OSK },
|
||||
{ "FPS_TOGGLE", RARCH_FPS_TOGGLE },
|
||||
{ "SEND_DEBUG_INFO", RARCH_SEND_DEBUG_INFO },
|
||||
{ "NETPLAY_HOST_TOGGLE", RARCH_NETPLAY_HOST_TOGGLE },
|
||||
{ "NETPLAY_GAME_WATCH", RARCH_NETPLAY_GAME_WATCH },
|
||||
{ "VOLUME_UP", RARCH_VOLUME_UP },
|
||||
{ "VOLUME_DOWN", RARCH_VOLUME_DOWN },
|
||||
{ "OVERLAY_NEXT", RARCH_OVERLAY_NEXT },
|
||||
{ "DISK_EJECT_TOGGLE", RARCH_DISK_EJECT_TOGGLE },
|
||||
{ "DISK_NEXT", RARCH_DISK_NEXT },
|
||||
{ "DISK_PREV", RARCH_DISK_PREV },
|
||||
{ "GRAB_MOUSE_TOGGLE", RARCH_GRAB_MOUSE_TOGGLE },
|
||||
{ "UI_COMPANION_TOGGLE", RARCH_UI_COMPANION_TOGGLE },
|
||||
{ "GAME_FOCUS_TOGGLE", RARCH_GAME_FOCUS_TOGGLE },
|
||||
{ "MENU_TOGGLE", RARCH_MENU_TOGGLE },
|
||||
{ "RECORDING_TOGGLE", RARCH_RECORDING_TOGGLE },
|
||||
{ "STREAMING_TOGGLE", RARCH_STREAMING_TOGGLE },
|
||||
{ "RUNAHEAD_TOGGLE", RARCH_RUNAHEAD_TOGGLE },
|
||||
{ "MENU_UP", RETRO_DEVICE_ID_JOYPAD_UP },
|
||||
{ "MENU_DOWN", RETRO_DEVICE_ID_JOYPAD_DOWN },
|
||||
{ "MENU_LEFT", RETRO_DEVICE_ID_JOYPAD_LEFT },
|
||||
{ "MENU_RIGHT", RETRO_DEVICE_ID_JOYPAD_RIGHT },
|
||||
{ "MENU_A", RETRO_DEVICE_ID_JOYPAD_A },
|
||||
{ "MENU_B", RETRO_DEVICE_ID_JOYPAD_B },
|
||||
{ "AI_SERVICE", RARCH_AI_SERVICE },
|
||||
};
|
||||
#endif
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
||||
+68
-4
@@ -37,11 +37,22 @@
|
||||
#include "gfx/common/ctr_common.h"
|
||||
#endif
|
||||
|
||||
/* Required for OpenDingux IPU filter setting */
|
||||
/* Required for OpenDingux IPU filter + refresh
|
||||
* rate settings */
|
||||
#if defined(DINGUX)
|
||||
#include "dingux/dingux_utils.h"
|
||||
#endif
|
||||
|
||||
/* Required for menu screensaver animation */
|
||||
#if defined(HAVE_MATERIALUI) || defined(HAVE_XMB) || defined(HAVE_OZONE)
|
||||
#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)
|
||||
@@ -62,6 +73,8 @@
|
||||
#define DEFAULT_MOUSE_SCALE 1
|
||||
#endif
|
||||
|
||||
#define DEFAULT_TOUCH_SCALE 1
|
||||
|
||||
#if defined(RARCH_MOBILE) || defined(HAVE_LIBNX) || defined(__WINRT__)
|
||||
#define DEFAULT_POINTER_ENABLE true
|
||||
#else
|
||||
@@ -217,6 +230,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
|
||||
|
||||
@@ -235,6 +251,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
|
||||
@@ -381,6 +402,11 @@
|
||||
/* Sets image filtering method when using the
|
||||
* IPU hardware scaler in Dingux devices */
|
||||
#define DEFAULT_DINGUX_IPU_FILTER_TYPE DINGUX_IPU_FILTER_BICUBIC
|
||||
#if defined(DINGUX_BETA)
|
||||
/* Sets refresh rate of integral LCD panel
|
||||
* in Dingux devices */
|
||||
#define DEFAULT_DINGUX_REFRESH_RATE DINGUX_REFRESH_RATE_60HZ
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Save configuration file on exit. */
|
||||
@@ -574,6 +600,19 @@ static const bool menu_savestate_resume = false;
|
||||
|
||||
#define DEFAULT_QUIT_ON_CLOSE_CONTENT QUIT_ON_CLOSE_CONTENT_DISABLED
|
||||
|
||||
/* While the menu is active, supported drivers
|
||||
* will display a screensaver after SCREENSAVER_TIMEOUT
|
||||
* seconds of inactivity. A timeout of zero disables
|
||||
* the screensaver */
|
||||
#define DEFAULT_MENU_SCREENSAVER_TIMEOUT 0
|
||||
|
||||
#if defined(HAVE_MATERIALUI) || defined(HAVE_XMB) || defined(HAVE_OZONE)
|
||||
/* When menu screensaver is enabled, specifies
|
||||
* animation effect and animation speed */
|
||||
#define DEFAULT_MENU_SCREENSAVER_ANIMATION MENU_SCREENSAVER_BLANK
|
||||
#define DEFAULT_MENU_SCREENSAVER_ANIMATION_SPEED 1.0f
|
||||
#endif
|
||||
|
||||
static const bool content_show_settings = true;
|
||||
static const bool content_show_favorites = true;
|
||||
#ifdef HAVE_IMAGEVIEWER
|
||||
@@ -643,6 +682,7 @@ static const float menu_header_opacity = 1.000;
|
||||
#define DEFAULT_SHOW_ADVANCED_SETTINGS false
|
||||
|
||||
#define DEFAULT_RGUI_COLOR_THEME RGUI_THEME_CLASSIC_GREEN
|
||||
#define DEFAULT_RGUI_TRANSPARENCY true
|
||||
|
||||
static const bool rgui_inline_thumbnails = false;
|
||||
static const bool rgui_swap_thumbnails = false;
|
||||
@@ -655,6 +695,7 @@ static const unsigned rgui_aspect_lock = RGUI_ASPECT_RATIO_LOCK_NONE;
|
||||
static const bool rgui_shadows = false;
|
||||
static const unsigned rgui_particle_effect = RGUI_PARTICLE_EFFECT_NONE;
|
||||
#define DEFAULT_RGUI_PARTICLE_EFFECT_SPEED 1.0f
|
||||
#define DEFAULT_RGUI_PARTICLE_EFFECT_SCREENSAVER true
|
||||
static const bool rgui_extended_ascii = false;
|
||||
#define DEFAULT_RGUI_SWITCH_ICONS true
|
||||
#endif
|
||||
@@ -706,7 +747,15 @@ static const unsigned input_backtouch_toggle = false;
|
||||
|
||||
#define DEFAULT_OVERLAY_ENABLE_AUTOPREFERRED true
|
||||
|
||||
#define DEFAULT_SHOW_PHYSICAL_INPUTS true
|
||||
#if defined(HAVE_OVERLAY)
|
||||
#if defined(RARCH_MOBILE)
|
||||
#define DEFAULT_OVERLAY_SHOW_INPUTS OVERLAY_SHOW_INPUT_TOUCHED
|
||||
#else
|
||||
#define DEFAULT_OVERLAY_SHOW_INPUTS OVERLAY_SHOW_INPUT_PHYSICAL
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define DEFAULT_OVERLAY_SHOW_INPUTS_PORT 0
|
||||
|
||||
#define DEFAULT_ALL_USERS_CONTROL_MENU false
|
||||
|
||||
@@ -838,6 +887,10 @@ static const bool audio_enable_menu_bgm = false;
|
||||
* applied */
|
||||
#define DEFAULT_NOTIFICATION_SHOW_CHEATS_APPLIED true
|
||||
|
||||
/* Display a notification when applying an
|
||||
* IPS/BPS/UPS patch file */
|
||||
#define DEFAULT_NOTIFICATION_SHOW_PATCH_APPLIED true
|
||||
|
||||
/* Display a notification when loading an
|
||||
* input remap file */
|
||||
#define DEFAULT_NOTIFICATION_SHOW_REMAP_LOAD true
|
||||
@@ -866,8 +919,15 @@ static const bool audio_enable_menu_bgm = false;
|
||||
#define DEFAULT_NOTIFICATION_SHOW_SCREENSHOT_FLASH 0
|
||||
#endif
|
||||
|
||||
/*Display a notification when setting the refresh rate*/
|
||||
/* Display a notification when setting the refresh rate*/
|
||||
#if defined(_3DS) || (defined(DINGUX) && defined(DINGUX_BETA))
|
||||
/* 3DS and OpenDingux Beta devices set refresh rate
|
||||
* on gfx driver init - set default notification
|
||||
* state to 'false' in order to avoid OSD log spam */
|
||||
#define DEFAULT_NOTIFICATION_SHOW_REFRESH_RATE false
|
||||
#else
|
||||
#define DEFAULT_NOTIFICATION_SHOW_REFRESH_RATE true
|
||||
#endif
|
||||
|
||||
/* Output samplerate. */
|
||||
#ifdef GEKKO
|
||||
@@ -975,7 +1035,7 @@ static const bool audio_enable_menu_bgm = false;
|
||||
|
||||
/* Saves non-volatile SRAM at a regular interval.
|
||||
* It is measured in seconds. A value of 0 disables autosave. */
|
||||
#if defined(__i386__) || defined(__i486__) || defined(__i686__) || defined(__x86_64__) || defined(_M_X64) || defined(_WIN32) || defined(OSX) || defined(ANDROID) || defined(IOS)
|
||||
#if defined(__i386__) || defined(__i486__) || defined(__i686__) || defined(__x86_64__) || defined(_M_X64) || defined(_WIN32) || defined(OSX) || defined(ANDROID) || defined(IOS) || defined(DINGUX)
|
||||
/* Flush to file every 10 seconds on modern platforms by default */
|
||||
#define DEFAULT_AUTOSAVE_INTERVAL 10
|
||||
#else
|
||||
@@ -1146,6 +1206,10 @@ static const int default_content_favorites_size = 200;
|
||||
#define DEFAULT_PLAYLIST_SHOW_SUBLABELS true
|
||||
#endif
|
||||
|
||||
/* Show the indices of playlist entries in
|
||||
* a menu-driver-specific fashion */
|
||||
#define DEFAULT_PLAYLIST_SHOW_ENTRY_IDX true
|
||||
|
||||
#define DEFAULT_PLAYLIST_FUZZY_ARCHIVE_MATCH false
|
||||
|
||||
#define DEFAULT_PLAYLIST_PORTABLE_PATHS false
|
||||
|
||||
@@ -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"
|
||||
|
||||
+234
-193
@@ -93,6 +93,7 @@ enum video_driver_enum
|
||||
VIDEO_GDI,
|
||||
VIDEO_VGA,
|
||||
VIDEO_FPGA,
|
||||
VIDEO_RSX,
|
||||
VIDEO_NULL
|
||||
};
|
||||
|
||||
@@ -315,6 +316,8 @@ static const enum video_driver_enum VIDEO_DEFAULT_DRIVER = VIDEO_VGA;
|
||||
static const enum video_driver_enum VIDEO_DEFAULT_DRIVER = VIDEO_FPGA;
|
||||
#elif defined(HAVE_DYLIB) && !defined(ANDROID)
|
||||
static const enum video_driver_enum VIDEO_DEFAULT_DRIVER = VIDEO_EXT;
|
||||
#elif defined(__PSL1GHT__)
|
||||
static const enum video_driver_enum VIDEO_DEFAULT_DRIVER = VIDEO_RSX;
|
||||
#else
|
||||
static const enum video_driver_enum VIDEO_DEFAULT_DRIVER = VIDEO_NULL;
|
||||
#endif
|
||||
@@ -465,7 +468,7 @@ static const enum joypad_driver_enum JOYPAD_DEFAULT_DRIVER = JOYPAD_WIIU;
|
||||
static const enum joypad_driver_enum JOYPAD_DEFAULT_DRIVER = JOYPAD_XDK;
|
||||
#elif defined(PS2)
|
||||
static const enum joypad_driver_enum JOYPAD_DEFAULT_DRIVER = JOYPAD_PS2;
|
||||
#elif defined(__PS3__) && !defined(__PSL1GHT__)
|
||||
#elif defined(__PS3__) || defined(__PSL1GHT__)
|
||||
static const enum joypad_driver_enum JOYPAD_DEFAULT_DRIVER = JOYPAD_PS3;
|
||||
#elif defined(ORBIS)
|
||||
static const enum joypad_driver_enum JOYPAD_DEFAULT_DRIVER = JOYPAD_PS4;
|
||||
@@ -661,10 +664,6 @@ static void config_parse_file(global_t *global);
|
||||
|
||||
struct defaults g_defaults;
|
||||
|
||||
/* TODO/FIXME - static public global variables */
|
||||
static unsigned old_analog_dpad_mode[MAX_USERS];
|
||||
static unsigned old_libretro_device[MAX_USERS];
|
||||
|
||||
/**
|
||||
* config_get_default_audio:
|
||||
*
|
||||
@@ -871,6 +870,8 @@ const char *config_get_default_video(void)
|
||||
return "vga";
|
||||
case VIDEO_FPGA:
|
||||
return "fpga";
|
||||
case VIDEO_RSX:
|
||||
return "rsx";
|
||||
case VIDEO_NULL:
|
||||
break;
|
||||
}
|
||||
@@ -1189,6 +1190,47 @@ const char *config_get_midi_driver_options(void)
|
||||
return char_list_new_special(STRING_LIST_MIDI_DRIVERS, NULL);
|
||||
}
|
||||
|
||||
#ifdef HAVE_LAKKA
|
||||
void config_set_timezone(char *timezone)
|
||||
{
|
||||
setenv("TZ", timezone, 1);
|
||||
tzset();
|
||||
}
|
||||
|
||||
const char *config_get_all_timezones(void)
|
||||
{
|
||||
return char_list_new_special(STRING_LIST_TIMEZONES, NULL);
|
||||
}
|
||||
|
||||
static void load_timezone(char *setting)
|
||||
{
|
||||
char haystack[TIMEZONE_LENGTH+32];
|
||||
static char *needle = "TIMEZONE=";
|
||||
size_t needle_len = strlen(needle);
|
||||
|
||||
RFILE *tzfp = filestream_open(LAKKA_TIMEZONE_PATH,
|
||||
RETRO_VFS_FILE_ACCESS_READ,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
|
||||
if (tzfp != NULL)
|
||||
{
|
||||
filestream_gets(tzfp, haystack, sizeof(haystack)-1);
|
||||
filestream_close(tzfp);
|
||||
|
||||
char *start = strstr(haystack, needle);
|
||||
|
||||
if (start != NULL)
|
||||
snprintf(setting, TIMEZONE_LENGTH, "%s", start + needle_len);
|
||||
else
|
||||
snprintf(setting, TIMEZONE_LENGTH, "%s", DEFAULT_TIMEZONE);
|
||||
}
|
||||
else
|
||||
snprintf(setting, TIMEZONE_LENGTH, "%s", DEFAULT_TIMEZONE);
|
||||
|
||||
config_set_timezone(setting);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool config_overlay_enable_default(void)
|
||||
{
|
||||
if (g_defaults.overlay_set)
|
||||
@@ -1242,6 +1284,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 +1494,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
|
||||
@@ -1490,6 +1537,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);
|
||||
@@ -1506,6 +1556,7 @@ static struct config_bool_setting *populate_settings_bool(
|
||||
SETTING_BOOL("menu_show_load_content_animation", &settings->bools.menu_show_load_content_animation, true, DEFAULT_MENU_SHOW_LOAD_CONTENT_ANIMATION, false);
|
||||
SETTING_BOOL("notification_show_autoconfig", &settings->bools.notification_show_autoconfig, true, DEFAULT_NOTIFICATION_SHOW_AUTOCONFIG, false);
|
||||
SETTING_BOOL("notification_show_cheats_applied", &settings->bools.notification_show_cheats_applied, true, DEFAULT_NOTIFICATION_SHOW_CHEATS_APPLIED, false);
|
||||
SETTING_BOOL("notification_show_patch_applied", &settings->bools.notification_show_patch_applied, true, DEFAULT_NOTIFICATION_SHOW_PATCH_APPLIED, false);
|
||||
SETTING_BOOL("notification_show_remap_load", &settings->bools.notification_show_remap_load, true, DEFAULT_NOTIFICATION_SHOW_REMAP_LOAD, false);
|
||||
SETTING_BOOL("notification_show_config_override_load", &settings->bools.notification_show_config_override_load, true, DEFAULT_NOTIFICATION_SHOW_CONFIG_OVERRIDE_LOAD, false);
|
||||
SETTING_BOOL("notification_show_set_initial_disk", &settings->bools.notification_show_set_initial_disk, true, DEFAULT_NOTIFICATION_SHOW_SET_INITIAL_DISK, false);
|
||||
@@ -1670,12 +1721,14 @@ static struct config_bool_setting *populate_settings_bool(
|
||||
SETTING_BOOL("rgui_background_filler_thickness_enable", &settings->bools.menu_rgui_background_filler_thickness_enable, true, true, false);
|
||||
SETTING_BOOL("rgui_border_filler_thickness_enable", &settings->bools.menu_rgui_border_filler_thickness_enable, true, true, false);
|
||||
SETTING_BOOL("rgui_border_filler_enable", &settings->bools.menu_rgui_border_filler_enable, true, true, false);
|
||||
SETTING_BOOL("menu_rgui_transparency", &settings->bools.menu_rgui_transparency, true, DEFAULT_RGUI_TRANSPARENCY, false);
|
||||
SETTING_BOOL("menu_rgui_shadows", &settings->bools.menu_rgui_shadows, true, rgui_shadows, false);
|
||||
SETTING_BOOL("menu_rgui_full_width_layout", &settings->bools.menu_rgui_full_width_layout, true, rgui_full_width_layout, false);
|
||||
SETTING_BOOL("rgui_inline_thumbnails", &settings->bools.menu_rgui_inline_thumbnails, true, rgui_inline_thumbnails, false);
|
||||
SETTING_BOOL("rgui_swap_thumbnails", &settings->bools.menu_rgui_swap_thumbnails, true, rgui_swap_thumbnails, false);
|
||||
SETTING_BOOL("rgui_extended_ascii", &settings->bools.menu_rgui_extended_ascii, true, rgui_extended_ascii, false);
|
||||
SETTING_BOOL("rgui_switch_icons", &settings->bools.menu_rgui_switch_icons, true, DEFAULT_RGUI_SWITCH_ICONS, false);
|
||||
SETTING_BOOL("rgui_particle_effect_screensaver", &settings->bools.menu_rgui_particle_effect_screensaver, true, DEFAULT_RGUI_PARTICLE_EFFECT_SCREENSAVER, false);
|
||||
#endif
|
||||
#ifdef HAVE_XMB
|
||||
SETTING_BOOL("xmb_shadows_enable", &settings->bools.menu_xmb_shadows_enable, true, DEFAULT_XMB_SHADOWS_ENABLE, false);
|
||||
@@ -1696,7 +1749,6 @@ static struct config_bool_setting *populate_settings_bool(
|
||||
#ifdef HAVE_OVERLAY
|
||||
SETTING_BOOL("input_overlay_enable", &settings->bools.input_overlay_enable, true, config_overlay_enable_default(), false);
|
||||
SETTING_BOOL("input_overlay_enable_autopreferred", &settings->bools.input_overlay_enable_autopreferred, true, DEFAULT_OVERLAY_ENABLE_AUTOPREFERRED, false);
|
||||
SETTING_BOOL("input_overlay_show_physical_inputs", &settings->bools.input_overlay_show_physical_inputs, true, DEFAULT_SHOW_PHYSICAL_INPUTS, false);
|
||||
SETTING_BOOL("input_overlay_hide_in_menu", &settings->bools.input_overlay_hide_in_menu, true, DEFAULT_OVERLAY_HIDE_IN_MENU, false);
|
||||
SETTING_BOOL("input_overlay_hide_when_gamepad_connected", &settings->bools.input_overlay_hide_when_gamepad_connected, true, DEFAULT_OVERLAY_HIDE_WHEN_GAMEPAD_CONNECTED, false);
|
||||
SETTING_BOOL("input_overlay_show_mouse_cursor", &settings->bools.input_overlay_show_mouse_cursor, true, DEFAULT_OVERLAY_SHOW_MOUSE_CURSOR, false);
|
||||
@@ -1770,6 +1822,7 @@ static struct config_bool_setting *populate_settings_bool(
|
||||
SETTING_BOOL("content_runtime_log", &settings->bools.content_runtime_log, true, DEFAULT_CONTENT_RUNTIME_LOG, false);
|
||||
SETTING_BOOL("content_runtime_log_aggregate", &settings->bools.content_runtime_log_aggregate, true, DEFAULT_CONTENT_RUNTIME_LOG_AGGREGATE, false);
|
||||
SETTING_BOOL("playlist_show_sublabels", &settings->bools.playlist_show_sublabels, true, DEFAULT_PLAYLIST_SHOW_SUBLABELS, false);
|
||||
SETTING_BOOL("playlist_show_entry_idx", &settings->bools.playlist_show_entry_idx, true, DEFAULT_PLAYLIST_SHOW_ENTRY_IDX, false);
|
||||
SETTING_BOOL("playlist_sort_alphabetical", &settings->bools.playlist_sort_alphabetical, true, DEFAULT_PLAYLIST_SORT_ALPHABETICAL, false);
|
||||
SETTING_BOOL("playlist_fuzzy_archive_match", &settings->bools.playlist_fuzzy_archive_match, true, DEFAULT_PLAYLIST_FUZZY_ARCHIVE_MATCH, false);
|
||||
SETTING_BOOL("playlist_portable_paths", &settings->bools.playlist_portable_paths, true, DEFAULT_PLAYLIST_PORTABLE_PATHS, false);
|
||||
@@ -1842,6 +1895,9 @@ static struct config_float_setting *populate_settings_float(
|
||||
SETTING_FLOAT("menu_header_opacity", &settings->floats.menu_header_opacity, true, menu_header_opacity, false);
|
||||
SETTING_FLOAT("menu_ticker_speed", &settings->floats.menu_ticker_speed, true, menu_ticker_speed, false);
|
||||
SETTING_FLOAT("rgui_particle_effect_speed", &settings->floats.menu_rgui_particle_effect_speed, true, DEFAULT_RGUI_PARTICLE_EFFECT_SPEED, false);
|
||||
#if defined(HAVE_MATERIALUI) || defined(HAVE_XMB) || defined(HAVE_OZONE)
|
||||
SETTING_FLOAT("menu_screensaver_animation_speed", &settings->floats.menu_screensaver_animation_speed, true, DEFAULT_MENU_SCREENSAVER_ANIMATION_SPEED, false);
|
||||
#endif
|
||||
#endif
|
||||
SETTING_FLOAT("video_message_pos_x", &settings->floats.video_msg_pos_x, true, message_pos_offset_x, false);
|
||||
SETTING_FLOAT("video_message_pos_y", &settings->floats.video_msg_pos_y, true, message_pos_offset_y, false);
|
||||
@@ -1887,6 +1943,7 @@ static struct config_uint_setting *populate_settings_uint(
|
||||
#ifdef GEKKO
|
||||
SETTING_UINT("input_mouse_scale", &settings->uints.input_mouse_scale, true, DEFAULT_MOUSE_SCALE, false);
|
||||
#endif
|
||||
SETTING_UINT("input_touch_scale", &settings->uints.input_touch_scale, true, DEFAULT_TOUCH_SCALE, false);
|
||||
#if defined(DINGUX) && defined(HAVE_LIBSHAKE)
|
||||
SETTING_UINT("input_dingux_rumble_gain", &settings->uints.input_dingux_rumble_gain, true, DEFAULT_DINGUX_RUMBLE_GAIN, false);
|
||||
#endif
|
||||
@@ -1933,6 +1990,10 @@ static struct config_uint_setting *populate_settings_uint(
|
||||
SETTING_UINT("menu_ticker_type", &settings->uints.menu_ticker_type, true, DEFAULT_MENU_TICKER_TYPE, false);
|
||||
SETTING_UINT("menu_scroll_delay", &settings->uints.menu_scroll_delay, true, DEFAULT_MENU_SCROLL_DELAY, false);
|
||||
SETTING_UINT("content_show_add_entry", &settings->uints.menu_content_show_add_entry, true, DEFAULT_MENU_CONTENT_SHOW_ADD_ENTRY, false);
|
||||
SETTING_UINT("menu_screensaver_timeout", &settings->uints.menu_screensaver_timeout, true, DEFAULT_MENU_SCREENSAVER_TIMEOUT, false);
|
||||
#if defined(HAVE_MATERIALUI) || defined(HAVE_XMB) || defined(HAVE_OZONE)
|
||||
SETTING_UINT("menu_screensaver_animation", &settings->uints.menu_screensaver_animation, true, DEFAULT_MENU_SCREENSAVER_ANIMATION, false);
|
||||
#endif
|
||||
#ifdef HAVE_RGUI
|
||||
SETTING_UINT("rgui_menu_color_theme", &settings->uints.menu_rgui_color_theme, true, DEFAULT_RGUI_COLOR_THEME, false);
|
||||
SETTING_UINT("rgui_thumbnail_downscaler", &settings->uints.menu_rgui_thumbnail_downscaler, true, rgui_thumbnail_downscaler, false);
|
||||
@@ -1975,7 +2036,7 @@ static struct config_uint_setting *populate_settings_uint(
|
||||
SETTING_UINT("ozone_menu_color_theme", &settings->uints.menu_ozone_color_theme, true, DEFAULT_OZONE_COLOR_THEME, false);
|
||||
#endif
|
||||
#endif
|
||||
SETTING_UINT("audio_out_rate", &settings->uints.audio_out_rate, true, DEFAULT_OUTPUT_RATE, false);
|
||||
SETTING_UINT("audio_out_rate", &settings->uints.audio_output_sample_rate, true, DEFAULT_OUTPUT_RATE, false);
|
||||
SETTING_UINT("custom_viewport_width", &settings->video_viewport_custom.width, false, 0 /* TODO */, false);
|
||||
SETTING_UINT("crt_switch_resolution_super", &settings->uints.crt_switch_resolution_super, true, DEFAULT_CRT_SWITCH_RESOLUTION_SUPER, false);
|
||||
SETTING_UINT("custom_viewport_height", &settings->video_viewport_custom.height, false, 0 /* TODO */, false);
|
||||
@@ -2012,7 +2073,12 @@ static struct config_uint_setting *populate_settings_uint(
|
||||
SETTING_UINT("bundle_assets_extract_version_current", &settings->uints.bundle_assets_extract_version_current, true, 0, false);
|
||||
#endif
|
||||
SETTING_UINT("bundle_assets_extract_last_version", &settings->uints.bundle_assets_extract_last_version, true, 0, false);
|
||||
SETTING_UINT("input_overlay_show_physical_inputs_port", &settings->uints.input_overlay_show_physical_inputs_port, true, 0, false);
|
||||
|
||||
#if defined(HAVE_OVERLAY)
|
||||
SETTING_UINT("input_overlay_show_inputs", &settings->uints.input_overlay_show_inputs, true, DEFAULT_OVERLAY_SHOW_INPUTS, false);
|
||||
SETTING_UINT("input_overlay_show_inputs_port", &settings->uints.input_overlay_show_inputs_port, true, DEFAULT_OVERLAY_SHOW_INPUTS_PORT, false);
|
||||
#endif
|
||||
|
||||
SETTING_UINT("video_msg_bgcolor_red", &settings->uints.video_msg_bgcolor_red, true, message_bgcolor_red, false);
|
||||
SETTING_UINT("video_msg_bgcolor_green", &settings->uints.video_msg_bgcolor_green, true, message_bgcolor_green, false);
|
||||
SETTING_UINT("video_msg_bgcolor_blue", &settings->uints.video_msg_bgcolor_blue, true, message_bgcolor_blue, false);
|
||||
@@ -2046,6 +2112,9 @@ static struct config_uint_setting *populate_settings_uint(
|
||||
|
||||
#if defined(DINGUX)
|
||||
SETTING_UINT("video_dingux_ipu_filter_type", &settings->uints.video_dingux_ipu_filter_type, true, DEFAULT_DINGUX_IPU_FILTER_TYPE, false);
|
||||
#if defined(DINGUX_BETA)
|
||||
SETTING_UINT("video_dingux_refresh_rate", &settings->uints.video_dingux_refresh_rate, true, DEFAULT_DINGUX_REFRESH_RATE, false);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
@@ -2061,6 +2130,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;
|
||||
@@ -2120,10 +2195,8 @@ static struct config_int_setting *populate_settings_int(
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static void video_driver_default_settings(void)
|
||||
static void video_driver_default_settings(global_t *global)
|
||||
{
|
||||
global_t *global = global_get_ptr();
|
||||
|
||||
if (!global)
|
||||
return;
|
||||
|
||||
@@ -2340,6 +2413,7 @@ void config_set_defaults(void *data)
|
||||
configuration_set_bool(settings,
|
||||
settings->bools.bluetooth_enable, filestream_exists(LAKKA_BLUETOOTH_PATH));
|
||||
configuration_set_bool(settings, settings->bools.localap_enable, false);
|
||||
load_timezone(settings->arrays.timezone);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
@@ -2357,7 +2431,8 @@ void config_set_defaults(void *data)
|
||||
|
||||
input_config_reset();
|
||||
#ifdef HAVE_CONFIGFILE
|
||||
input_remapping_set_defaults(true);
|
||||
input_remapping_deinit();
|
||||
input_remapping_set_defaults();
|
||||
#endif
|
||||
|
||||
/* Verify that binds are in proper order. */
|
||||
@@ -2467,7 +2542,7 @@ void config_set_defaults(void *data)
|
||||
|
||||
*settings->paths.log_dir = '\0';
|
||||
|
||||
video_driver_default_settings();
|
||||
video_driver_default_settings(global);
|
||||
|
||||
if (!string_is_empty(g_defaults.dirs[DEFAULT_DIR_WALLPAPERS]))
|
||||
configuration_set_string(settings,
|
||||
@@ -2927,13 +3002,10 @@ error:
|
||||
}
|
||||
|
||||
#ifdef RARCH_CONSOLE
|
||||
static void video_driver_load_settings(config_file_t *conf)
|
||||
static void video_driver_load_settings(global_t *global,
|
||||
config_file_t *conf)
|
||||
{
|
||||
bool tmp_bool = false;
|
||||
global_t *global = global_get_ptr();
|
||||
|
||||
if (!conf)
|
||||
return;
|
||||
|
||||
CONFIG_GET_INT_BASE(conf, global,
|
||||
console.screen.gamma_correction, "gamma_correction");
|
||||
@@ -3186,7 +3258,8 @@ static bool config_load_file(global_t *global,
|
||||
settings->paths.directory_libretro, tmp_str);
|
||||
|
||||
#ifdef RARCH_CONSOLE
|
||||
video_driver_load_settings(conf);
|
||||
if (conf)
|
||||
video_driver_load_settings(global, conf);
|
||||
#endif
|
||||
|
||||
/* Post-settings load */
|
||||
@@ -3592,20 +3665,23 @@ bool config_load_override(void *data)
|
||||
{
|
||||
char temp_path[PATH_MAX_LENGTH];
|
||||
|
||||
temp_path[0] = '\0';
|
||||
|
||||
RARCH_LOG("[Overrides]: Content dir-specific overrides found at \"%s\".\n",
|
||||
content_path);
|
||||
|
||||
if (should_append)
|
||||
{
|
||||
RARCH_LOG("[Overrides]: Content dir-specific overrides stacking on top of previous overrides.\n");
|
||||
strlcpy(temp_path, path_get(RARCH_PATH_CONFIG_APPEND), sizeof(temp_path));
|
||||
strlcat(temp_path, "|", sizeof(temp_path));
|
||||
strlcat(temp_path, content_path, sizeof(temp_path));
|
||||
snprintf(temp_path, sizeof(temp_path),
|
||||
"%s|%s",
|
||||
path_get(RARCH_PATH_CONFIG_APPEND),
|
||||
content_path
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
temp_path[0] = '\0';
|
||||
strlcpy(temp_path, content_path, sizeof(temp_path));
|
||||
}
|
||||
|
||||
path_set(RARCH_PATH_CONFIG_APPEND, temp_path);
|
||||
|
||||
@@ -3621,20 +3697,23 @@ bool config_load_override(void *data)
|
||||
{
|
||||
char temp_path[PATH_MAX_LENGTH];
|
||||
|
||||
temp_path[0] = '\0';
|
||||
|
||||
RARCH_LOG("[Overrides]: Game-specific overrides found at \"%s\".\n",
|
||||
game_path);
|
||||
|
||||
if (should_append)
|
||||
{
|
||||
RARCH_LOG("[Overrides]: Game-specific overrides stacking on top of previous overrides.\n");
|
||||
strlcpy(temp_path, path_get(RARCH_PATH_CONFIG_APPEND), sizeof(temp_path));
|
||||
strlcat(temp_path, "|", sizeof(temp_path));
|
||||
strlcat(temp_path, game_path, sizeof(temp_path));
|
||||
snprintf(temp_path, sizeof(temp_path),
|
||||
"%s|%s",
|
||||
path_get(RARCH_PATH_CONFIG_APPEND),
|
||||
game_path
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
temp_path[0] = '\0';
|
||||
strlcpy(temp_path, game_path, sizeof(temp_path));
|
||||
}
|
||||
|
||||
path_set(RARCH_PATH_CONFIG_APPEND, temp_path);
|
||||
|
||||
@@ -3717,7 +3796,7 @@ bool config_unload_override(void)
|
||||
bool config_load_remap(const char *directory_input_remapping,
|
||||
void *data)
|
||||
{
|
||||
char content_dir_name[PATH_MAX_LENGTH];
|
||||
char content_dir_name[PATH_MAX_LENGTH] = { 0 };
|
||||
/* path to the directory containing retroarch.cfg (prefix) */
|
||||
char remap_directory[PATH_MAX_LENGTH];
|
||||
/* final path for core-specific configuration (prefix+suffix) */
|
||||
@@ -3773,14 +3852,17 @@ bool config_load_remap(const char *directory_input_remapping,
|
||||
sizeof(game_path));
|
||||
|
||||
#ifdef HAVE_CONFIGFILE
|
||||
input_remapping_set_defaults(false);
|
||||
input_remapping_set_defaults();
|
||||
#endif
|
||||
|
||||
/* If a game remap file exists, load it. */
|
||||
if ((new_conf = config_file_new_from_path_to_string(game_path)))
|
||||
{
|
||||
bool ret = input_remapping_load_file(new_conf, game_path);
|
||||
config_file_free(new_conf);
|
||||
new_conf = NULL;
|
||||
RARCH_LOG("[Remaps]: Game-specific remap found at \"%s\".\n", game_path);
|
||||
if (input_remapping_load_file(new_conf, game_path))
|
||||
if (ret)
|
||||
{
|
||||
rarch_ctl(RARCH_CTL_SET_REMAPS_GAME_ACTIVE, NULL);
|
||||
/* msg_remap_loaded is set to MSG_GAME_REMAP_FILE_LOADED
|
||||
@@ -3792,8 +3874,11 @@ bool config_load_remap(const char *directory_input_remapping,
|
||||
/* If a content-dir remap file exists, load it. */
|
||||
if ((new_conf = config_file_new_from_path_to_string(content_path)))
|
||||
{
|
||||
bool ret = input_remapping_load_file(new_conf, content_path);
|
||||
config_file_free(new_conf);
|
||||
new_conf = NULL;
|
||||
RARCH_LOG("[Remaps]: Content-dir-specific remap found at \"%s\".\n", content_path);
|
||||
if (input_remapping_load_file(new_conf, content_path))
|
||||
if (ret)
|
||||
{
|
||||
rarch_ctl(RARCH_CTL_SET_REMAPS_CONTENT_DIR_ACTIVE, NULL);
|
||||
msg_remap_loaded = MSG_DIRECTORY_REMAP_FILE_LOADED;
|
||||
@@ -3804,8 +3889,11 @@ bool config_load_remap(const char *directory_input_remapping,
|
||||
/* If a core remap file exists, load it. */
|
||||
if ((new_conf = config_file_new_from_path_to_string(core_path)))
|
||||
{
|
||||
bool ret = input_remapping_load_file(new_conf, core_path);
|
||||
config_file_free(new_conf);
|
||||
new_conf = NULL;
|
||||
RARCH_LOG("[Remaps]: Core-specific remap found at \"%s\".\n", core_path);
|
||||
if (input_remapping_load_file(new_conf, core_path))
|
||||
if (ret)
|
||||
{
|
||||
rarch_ctl(RARCH_CTL_SET_REMAPS_CORE_ACTIVE, NULL);
|
||||
msg_remap_loaded = MSG_CORE_REMAP_FILE_LOADED;
|
||||
@@ -3813,6 +3901,8 @@ bool config_load_remap(const char *directory_input_remapping,
|
||||
}
|
||||
}
|
||||
|
||||
if (new_conf)
|
||||
config_file_free(new_conf);
|
||||
new_conf = NULL;
|
||||
|
||||
return false;
|
||||
@@ -3850,12 +3940,8 @@ static void config_parse_file(global_t *global)
|
||||
}
|
||||
}
|
||||
|
||||
static void video_driver_save_settings(config_file_t *conf)
|
||||
static void video_driver_save_settings(global_t *global, config_file_t *conf)
|
||||
{
|
||||
global_t *global = global_get_ptr();
|
||||
if (!conf)
|
||||
return;
|
||||
|
||||
config_set_int(conf, "gamma_correction",
|
||||
global->console.screen.gamma_correction);
|
||||
config_set_bool(conf, "flicker_filter_enable",
|
||||
@@ -3940,23 +4026,11 @@ bool config_save_autoconf_profile(const
|
||||
fill_pathname_join(buf, autoconf_dir, joypad_driver, sizeof(buf));
|
||||
|
||||
if (path_is_directory(buf))
|
||||
{
|
||||
char buf_new[PATH_MAX_LENGTH];
|
||||
|
||||
buf_new[0] = '\0';
|
||||
|
||||
fill_pathname_join(buf_new, buf,
|
||||
sanitised_name, sizeof(buf_new));
|
||||
fill_pathname_noext(autoconf_file,
|
||||
buf_new, ".cfg", sizeof(autoconf_file));
|
||||
}
|
||||
fill_pathname_join_concat(autoconf_file, buf,
|
||||
sanitised_name, ".cfg", sizeof(autoconf_file));
|
||||
else
|
||||
{
|
||||
fill_pathname_join(buf, autoconf_dir,
|
||||
sanitised_name, sizeof(buf));
|
||||
fill_pathname_noext(autoconf_file,
|
||||
buf, ".cfg", sizeof(autoconf_file));
|
||||
}
|
||||
fill_pathname_join_concat(autoconf_file, autoconf_dir,
|
||||
sanitised_name, ".cfg", sizeof(autoconf_file));
|
||||
|
||||
/* Open config file */
|
||||
conf = config_file_new_from_path_to_string(autoconf_file);
|
||||
@@ -4029,6 +4103,7 @@ bool config_save_file(const char *path)
|
||||
struct config_path_setting *path_settings = NULL;
|
||||
config_file_t *conf = config_file_new_from_path_to_string(path);
|
||||
settings_t *settings = config_get_ptr();
|
||||
global_t *global = global_get_ptr();
|
||||
int bool_settings_size = sizeof(settings->bools) / sizeof(settings->bools.placeholder);
|
||||
int float_settings_size = sizeof(settings->floats)/ sizeof(settings->floats.placeholder);
|
||||
int int_settings_size = sizeof(settings->ints) / sizeof(settings->ints.placeholder);
|
||||
@@ -4196,7 +4271,8 @@ bool config_save_file(const char *path)
|
||||
/* Hexadecimal settings */
|
||||
config_set_hex(conf, "video_message_color", msg_color);
|
||||
|
||||
video_driver_save_settings(conf);
|
||||
if (conf)
|
||||
video_driver_save_settings(global, conf);
|
||||
|
||||
#ifdef HAVE_LAKKA
|
||||
if (settings->bools.ssh_enable)
|
||||
@@ -4512,33 +4588,32 @@ bool config_replace(bool config_replace_save_on_exit, char *path)
|
||||
**/
|
||||
bool input_remapping_load_file(void *data, const char *path)
|
||||
{
|
||||
unsigned i, j, k;
|
||||
config_file_t *conf = (config_file_t*)data;
|
||||
settings_t *settings = config_get_ptr();
|
||||
global_t *global = global_get_ptr();
|
||||
unsigned i, j;
|
||||
config_file_t *conf = (config_file_t*)data;
|
||||
settings_t *settings = config_get_ptr();
|
||||
global_t *global = global_get_ptr();
|
||||
char key_strings[RARCH_FIRST_CUSTOM_BIND + 8][8] = {
|
||||
"b", "y", "select", "start",
|
||||
"up", "down", "left", "right",
|
||||
"a", "x", "l", "r", "l2", "r2",
|
||||
"l3", "r3", "l_x+", "l_x-", "l_y+", "l_y-", "r_x+", "r_x-", "r_y+", "r_y-" };
|
||||
|
||||
if (!conf || string_is_empty(path))
|
||||
if (!conf || string_is_empty(path))
|
||||
return false;
|
||||
|
||||
if (!string_is_empty(global->name.remapfile))
|
||||
input_remapping_set_defaults(true);
|
||||
{
|
||||
input_remapping_deinit();
|
||||
input_remapping_set_defaults();
|
||||
}
|
||||
global->name.remapfile = strdup(path);
|
||||
|
||||
for (i = 0; i < MAX_USERS; i++)
|
||||
{
|
||||
char s1[64], s2[64], s3[64];
|
||||
char btn_ident[RARCH_FIRST_CUSTOM_BIND][128] = {{0}};
|
||||
char key_ident[RARCH_FIRST_CUSTOM_BIND][128] = {{0}};
|
||||
char stk_ident[8][4096] = {{0}};
|
||||
char s1[32], s2[32], s3[32];
|
||||
|
||||
char key_strings[RARCH_FIRST_CUSTOM_BIND + 8][128] = {
|
||||
"b", "y", "select", "start",
|
||||
"up", "down", "left", "right",
|
||||
"a", "x", "l", "r", "l2", "r2",
|
||||
"l3", "r3", "l_x+", "l_x-", "l_y+", "l_y-", "r_x+", "r_x-", "r_y+", "r_y-" };
|
||||
|
||||
old_analog_dpad_mode[i] = settings->uints.input_analog_dpad_mode[i];
|
||||
old_libretro_device[i] = settings->uints.input_libretro_device[i];
|
||||
global->old_analog_dpad_mode[i] = settings->uints.input_analog_dpad_mode[i];
|
||||
global->old_libretro_device[i] = settings->uints.input_libretro_device[i];
|
||||
|
||||
s1[0] = '\0';
|
||||
s2[0] = '\0';
|
||||
@@ -4550,49 +4625,57 @@ bool input_remapping_load_file(void *data, const char *path)
|
||||
|
||||
for (j = 0; j < RARCH_FIRST_CUSTOM_BIND + 8; j++)
|
||||
{
|
||||
const char *key_string = key_strings[j];
|
||||
|
||||
if (j < RARCH_FIRST_CUSTOM_BIND)
|
||||
{
|
||||
int btn_remap = -1;
|
||||
int key_remap = -1;
|
||||
char btn_ident[128];
|
||||
char key_ident[128];
|
||||
|
||||
btn_ident[0] = key_ident[0] = '\0';
|
||||
|
||||
fill_pathname_join_delim(btn_ident[j], s1,
|
||||
key_strings[j], '_', sizeof(btn_ident[j]));
|
||||
fill_pathname_join_delim(key_ident[j], s2,
|
||||
key_strings[j], '_', sizeof(btn_ident[j]));
|
||||
fill_pathname_join_delim(btn_ident, s1,
|
||||
key_string, '_', sizeof(btn_ident));
|
||||
fill_pathname_join_delim(key_ident, s2,
|
||||
key_string, '_', sizeof(key_ident));
|
||||
|
||||
if (config_get_int(conf, btn_ident[j], &btn_remap))
|
||||
if (config_get_int(conf, btn_ident, &btn_remap))
|
||||
{
|
||||
if (btn_remap == -1)
|
||||
btn_remap = RARCH_UNMAPPED;
|
||||
btn_remap = RARCH_UNMAPPED;
|
||||
|
||||
configuration_set_uint(settings,
|
||||
settings->uints.input_remap_ids[i][j], btn_remap);
|
||||
}
|
||||
|
||||
if (!config_get_int(conf, key_ident[j], &key_remap))
|
||||
key_remap = RETROK_UNKNOWN;
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
char stk_ident[128];
|
||||
int stk_remap = -1;
|
||||
k = j - RARCH_FIRST_CUSTOM_BIND;
|
||||
|
||||
stk_ident[0] = '\0';
|
||||
|
||||
fill_pathname_join_delim(stk_ident[k], s3,
|
||||
key_strings[j], '$', sizeof(stk_ident[k]));
|
||||
fill_pathname_join_delim(stk_ident, s3,
|
||||
key_string, '$', sizeof(stk_ident));
|
||||
|
||||
snprintf(stk_ident[k],
|
||||
sizeof(stk_ident[k]),
|
||||
snprintf(stk_ident,
|
||||
sizeof(stk_ident),
|
||||
"%s_%s",
|
||||
s3,
|
||||
key_strings[j]);
|
||||
key_string);
|
||||
|
||||
if (config_get_int(conf, stk_ident[k], &stk_remap))
|
||||
if (config_get_int(conf, stk_ident, &stk_remap))
|
||||
{
|
||||
if (stk_remap == -1)
|
||||
stk_remap = RARCH_UNMAPPED;
|
||||
stk_remap = RARCH_UNMAPPED;
|
||||
|
||||
configuration_set_uint(settings,
|
||||
settings->uints.input_remap_ids[i][j], stk_remap);
|
||||
@@ -4607,8 +4690,6 @@ bool input_remapping_load_file(void *data, const char *path)
|
||||
CONFIG_GET_INT_BASE(conf, settings, uints.input_libretro_device[i], s1);
|
||||
}
|
||||
|
||||
config_file_free(conf);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -4623,19 +4704,23 @@ bool input_remapping_load_file(void *data, const char *path)
|
||||
bool input_remapping_save_file(const char *path)
|
||||
{
|
||||
bool ret;
|
||||
unsigned i, j, k;
|
||||
char buf[PATH_MAX_LENGTH];
|
||||
unsigned i, j;
|
||||
char remap_file[PATH_MAX_LENGTH];
|
||||
char key_strings[RARCH_FIRST_CUSTOM_BIND + 8][8] = {
|
||||
"b", "y", "select", "start",
|
||||
"up", "down", "left", "right",
|
||||
"a", "x", "l", "r", "l2", "r2",
|
||||
"l3", "r3", "l_x+", "l_x-", "l_y+", "l_y-", "r_x+", "r_x-", "r_y+", "r_y-" };
|
||||
config_file_t *conf = NULL;
|
||||
unsigned max_users = *(input_driver_get_uint(INPUT_ACTION_MAX_USERS));
|
||||
settings_t *settings = config_get_ptr();
|
||||
const char *dir_input_remapping = settings->paths.directory_input_remapping;
|
||||
|
||||
buf[0] = remap_file[0] = '\0';
|
||||
remap_file[0] = '\0';
|
||||
|
||||
fill_pathname_join(buf, dir_input_remapping, path, sizeof(buf));
|
||||
fill_pathname_noext(remap_file, buf,
|
||||
FILE_PATH_REMAP_EXTENSION, sizeof(remap_file));
|
||||
fill_pathname_join_concat(remap_file, dir_input_remapping, path,
|
||||
FILE_PATH_REMAP_EXTENSION,
|
||||
sizeof(remap_file));
|
||||
|
||||
if (!(conf = config_file_new_from_path_to_string(remap_file)))
|
||||
{
|
||||
@@ -4645,62 +4730,68 @@ bool input_remapping_save_file(const char *path)
|
||||
|
||||
for (i = 0; i < max_users; i++)
|
||||
{
|
||||
char s1[64], s2[64], s3[64];
|
||||
char btn_ident[RARCH_FIRST_CUSTOM_BIND][128] = {{0}};
|
||||
char key_ident[RARCH_FIRST_CUSTOM_BIND][128] = {{0}};
|
||||
char stk_ident[8][128] = {{0}};
|
||||
|
||||
char key_strings[RARCH_FIRST_CUSTOM_BIND + 8][128] = {
|
||||
"b", "y", "select", "start",
|
||||
"up", "down", "left", "right",
|
||||
"a", "x", "l", "r", "l2", "r2",
|
||||
"l3", "r3", "l_x+", "l_x-", "l_y+", "l_y-", "r_x+", "r_x-", "r_y+", "r_y-" };
|
||||
char s1[32], s2[32], s3[32];
|
||||
|
||||
s1[0] = '\0';
|
||||
s2[0] = '\0';
|
||||
s3[0] = '\0';
|
||||
|
||||
snprintf(s1, sizeof(s1), "input_player%u_btn", i + 1);
|
||||
snprintf(s2, sizeof(s2), "input_player%u_key", i + 1);
|
||||
snprintf(s3, sizeof(s1), "input_player%u_stk", i + 1);
|
||||
snprintf(s3, sizeof(s3), "input_player%u_stk", i + 1);
|
||||
|
||||
for (j = 0; j < RARCH_FIRST_CUSTOM_BIND + 8; j++)
|
||||
for (j = 0; j < RARCH_FIRST_CUSTOM_BIND; j++)
|
||||
{
|
||||
unsigned remap_id = settings->uints.input_remap_ids[i][j];
|
||||
unsigned keymap_id = settings->uints.input_keymapper_ids[i][j];
|
||||
char btn_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];
|
||||
btn_ident[0] = '\0';
|
||||
fill_pathname_join_delim(btn_ident, s1,
|
||||
key_string, '_', sizeof(btn_ident));
|
||||
|
||||
if (j < RARCH_FIRST_CUSTOM_BIND)
|
||||
{
|
||||
fill_pathname_join_delim(btn_ident[j], s1,
|
||||
key_strings[j], '_', sizeof(btn_ident[j]));
|
||||
fill_pathname_join_delim(key_ident[j], s2,
|
||||
key_strings[j], '_', sizeof(btn_ident[j]));
|
||||
|
||||
/* only save values that have been modified */
|
||||
if (remap_id != j && remap_id != RARCH_UNMAPPED)
|
||||
config_set_int(conf, btn_ident[j],
|
||||
settings->uints.input_remap_ids[i][j]);
|
||||
else if (remap_id != j && remap_id == RARCH_UNMAPPED)
|
||||
config_set_int(conf, btn_ident[j], -1);
|
||||
else
|
||||
config_unset(conf, btn_ident[j]);
|
||||
|
||||
if (keymap_id != RETROK_UNKNOWN)
|
||||
config_set_int(conf, key_ident[j],
|
||||
settings->uints.input_keymapper_ids[i][j]);
|
||||
}
|
||||
/* only save values that have been modified */
|
||||
if (remap_id == j)
|
||||
config_unset(conf, btn_ident);
|
||||
else
|
||||
{
|
||||
k = j - RARCH_FIRST_CUSTOM_BIND;
|
||||
fill_pathname_join_delim(stk_ident[k], s3,
|
||||
key_strings[j], '_', sizeof(stk_ident[k]));
|
||||
if (remap_id != j && remap_id != RARCH_UNMAPPED)
|
||||
config_set_int(conf, stk_ident[k],
|
||||
settings->uints.input_remap_ids[i][j]);
|
||||
else if (remap_id != j && remap_id == RARCH_UNMAPPED)
|
||||
config_set_int(conf, stk_ident[k],
|
||||
if (remap_id == RARCH_UNMAPPED)
|
||||
config_set_int(conf, btn_ident, -1);
|
||||
else
|
||||
config_set_int(conf, btn_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]);
|
||||
}
|
||||
}
|
||||
|
||||
for (j = RARCH_FIRST_CUSTOM_BIND; j < (RARCH_FIRST_CUSTOM_BIND + 8); j++)
|
||||
{
|
||||
char stk_ident[128];
|
||||
unsigned remap_id = settings->uints.input_remap_ids[i][j];
|
||||
const char *key_string = key_strings[j];
|
||||
stk_ident[0] = '\0';
|
||||
fill_pathname_join_delim(stk_ident, s3,
|
||||
key_string, '_', sizeof(stk_ident));
|
||||
|
||||
if (remap_id == j)
|
||||
config_unset(conf, stk_ident);
|
||||
else
|
||||
{
|
||||
if (remap_id == RARCH_UNMAPPED)
|
||||
config_set_int(conf, stk_ident,
|
||||
-1);
|
||||
else
|
||||
config_unset(conf, stk_ident[k]);
|
||||
config_set_int(conf, stk_ident,
|
||||
settings->uints.input_remap_ids[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4719,63 +4810,13 @@ bool input_remapping_save_file(const char *path)
|
||||
bool input_remapping_remove_file(const char *path,
|
||||
const char *dir_input_remapping)
|
||||
{
|
||||
char buf[PATH_MAX_LENGTH];
|
||||
char remap_file[PATH_MAX_LENGTH];
|
||||
buf[0] = remap_file[0] = '\0';
|
||||
|
||||
fill_pathname_join(buf, dir_input_remapping, path, sizeof(buf));
|
||||
fill_pathname_noext(remap_file, buf,
|
||||
remap_file[0] = '\0';
|
||||
fill_pathname_join_concat(remap_file, dir_input_remapping, path,
|
||||
FILE_PATH_REMAP_EXTENSION,
|
||||
sizeof(remap_file));
|
||||
|
||||
return filestream_delete(remap_file) == 0 ? true : false;
|
||||
}
|
||||
|
||||
void input_remapping_set_defaults(bool deinit)
|
||||
{
|
||||
unsigned i, j;
|
||||
settings_t *settings = config_get_ptr();
|
||||
global_t *global = global_get_ptr();
|
||||
|
||||
if (!global)
|
||||
return;
|
||||
|
||||
if (deinit)
|
||||
{
|
||||
if (!string_is_empty(global->name.remapfile))
|
||||
free(global->name.remapfile);
|
||||
global->name.remapfile = NULL;
|
||||
rarch_ctl(RARCH_CTL_UNSET_REMAPS_CORE_ACTIVE, NULL);
|
||||
rarch_ctl(RARCH_CTL_UNSET_REMAPS_CONTENT_DIR_ACTIVE, NULL);
|
||||
rarch_ctl(RARCH_CTL_UNSET_REMAPS_GAME_ACTIVE, NULL);
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_USERS; i++)
|
||||
{
|
||||
for (j = 0; j < RARCH_FIRST_CUSTOM_BIND + 8; j++)
|
||||
{
|
||||
if (j < RARCH_FIRST_CUSTOM_BIND)
|
||||
{
|
||||
const struct retro_keybind *keybind = &input_config_binds[i][j];
|
||||
if (keybind)
|
||||
configuration_set_uint(settings,
|
||||
settings->uints.input_remap_ids[i][j], keybind->id);
|
||||
configuration_set_uint(settings,
|
||||
settings->uints.input_keymapper_ids[i][j], RETROK_UNKNOWN);
|
||||
}
|
||||
else
|
||||
configuration_set_uint(settings,
|
||||
settings->uints.input_remap_ids[i][j], j);
|
||||
}
|
||||
|
||||
if (old_analog_dpad_mode[i])
|
||||
configuration_set_uint(settings,
|
||||
settings->uints.input_analog_dpad_mode[i], old_analog_dpad_mode[i]);
|
||||
if (old_libretro_device[i])
|
||||
configuration_set_uint(settings,
|
||||
settings->uints.input_libretro_device[i], old_libretro_device[i]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(HAVE_DYNAMIC)
|
||||
|
||||
+34
-3
@@ -29,6 +29,10 @@
|
||||
#include "input/input_defines.h"
|
||||
#include "led/led_defines.h"
|
||||
|
||||
#ifdef HAVE_LAKKA
|
||||
#include "lakka.h"
|
||||
#endif
|
||||
|
||||
#define configuration_set_float(settings, var, newvar) \
|
||||
{ \
|
||||
settings->modified = true; \
|
||||
@@ -130,7 +134,7 @@ typedef struct settings
|
||||
|
||||
unsigned led_map[MAX_LEDS];
|
||||
|
||||
unsigned audio_out_rate;
|
||||
unsigned audio_output_sample_rate;
|
||||
unsigned audio_block_frames;
|
||||
unsigned audio_latency;
|
||||
|
||||
@@ -151,6 +155,7 @@ typedef struct settings
|
||||
#ifdef GEKKO
|
||||
unsigned input_mouse_scale;
|
||||
#endif
|
||||
unsigned input_touch_scale;
|
||||
unsigned input_hotkey_block_delay;
|
||||
unsigned input_menu_toggle_gamepad_combo;
|
||||
unsigned input_keyboard_gamepad_mapping_type;
|
||||
@@ -200,6 +205,7 @@ typedef struct settings
|
||||
unsigned video_stream_scale_factor;
|
||||
unsigned video_3ds_display_mode;
|
||||
unsigned video_dingux_ipu_filter_type;
|
||||
unsigned video_dingux_refresh_rate;
|
||||
#ifdef HAVE_VIDEO_LAYOUT
|
||||
unsigned video_layout_selected_view;
|
||||
#endif
|
||||
@@ -249,6 +255,8 @@ typedef struct settings
|
||||
unsigned menu_ticker_type;
|
||||
unsigned menu_scroll_delay;
|
||||
unsigned menu_content_show_add_entry;
|
||||
unsigned menu_screensaver_timeout;
|
||||
unsigned menu_screensaver_animation;
|
||||
|
||||
unsigned playlist_entry_remove_enable;
|
||||
unsigned playlist_show_inline_core_name;
|
||||
@@ -258,7 +266,8 @@ typedef struct settings
|
||||
unsigned camera_width;
|
||||
unsigned camera_height;
|
||||
|
||||
unsigned input_overlay_show_physical_inputs_port;
|
||||
unsigned input_overlay_show_inputs;
|
||||
unsigned input_overlay_show_inputs_port;
|
||||
|
||||
unsigned run_ahead_frames;
|
||||
|
||||
@@ -280,6 +289,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
|
||||
@@ -306,6 +321,7 @@ typedef struct settings
|
||||
float menu_header_opacity;
|
||||
float menu_ticker_speed;
|
||||
float menu_rgui_particle_effect_speed;
|
||||
float menu_screensaver_animation_speed;
|
||||
|
||||
float audio_max_timing_skew;
|
||||
float audio_volume; /* dB scale. */
|
||||
@@ -376,6 +392,11 @@ typedef struct settings
|
||||
char ai_service_url[PATH_MAX_LENGTH];
|
||||
|
||||
char crt_switch_timings[255];
|
||||
#ifdef HAVE_LAKKA
|
||||
char timezone[TIMEZONE_LENGTH];
|
||||
char cpu_main_gov[32];
|
||||
char cpu_menu_gov[32];
|
||||
#endif
|
||||
} arrays;
|
||||
|
||||
struct
|
||||
@@ -496,6 +517,7 @@ typedef struct settings
|
||||
#ifdef HAVE_VIDEO_LAYOUT
|
||||
bool video_layout_enable;
|
||||
#endif
|
||||
bool video_force_resolution;
|
||||
|
||||
/* Accessibility */
|
||||
bool accessibility_enable;
|
||||
@@ -521,7 +543,6 @@ typedef struct settings
|
||||
bool input_overlay_enable_autopreferred;
|
||||
bool input_overlay_hide_in_menu;
|
||||
bool input_overlay_hide_when_gamepad_connected;
|
||||
bool input_overlay_show_physical_inputs;
|
||||
bool input_overlay_show_mouse_cursor;
|
||||
bool input_overlay_auto_rotate;
|
||||
bool input_overlay_auto_scale;
|
||||
@@ -547,6 +568,7 @@ typedef struct settings
|
||||
bool menu_show_load_content_animation;
|
||||
bool notification_show_autoconfig;
|
||||
bool notification_show_cheats_applied;
|
||||
bool notification_show_patch_applied;
|
||||
bool notification_show_remap_load;
|
||||
bool notification_show_config_override_load;
|
||||
bool notification_show_set_initial_disk;
|
||||
@@ -605,11 +627,13 @@ typedef struct settings
|
||||
bool menu_rgui_border_filler_thickness_enable;
|
||||
bool menu_rgui_border_filler_enable;
|
||||
bool menu_rgui_full_width_layout;
|
||||
bool menu_rgui_transparency;
|
||||
bool menu_rgui_shadows;
|
||||
bool menu_rgui_inline_thumbnails;
|
||||
bool menu_rgui_swap_thumbnails;
|
||||
bool menu_rgui_extended_ascii;
|
||||
bool menu_rgui_switch_icons;
|
||||
bool menu_rgui_particle_effect_screensaver;
|
||||
bool menu_xmb_shadows_enable;
|
||||
bool menu_xmb_vertical_thumbnails;
|
||||
bool menu_content_show_settings;
|
||||
@@ -762,6 +786,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
|
||||
@@ -801,6 +826,7 @@ typedef struct settings
|
||||
|
||||
bool playlist_sort_alphabetical;
|
||||
bool playlist_show_sublabels;
|
||||
bool playlist_show_entry_idx;
|
||||
bool playlist_fuzzy_archive_match;
|
||||
bool playlist_portable_paths;
|
||||
|
||||
@@ -1004,6 +1030,11 @@ void config_save_file_salamander(void);
|
||||
|
||||
settings_t *config_get_ptr(void);
|
||||
|
||||
#ifdef HAVE_LAKKA
|
||||
const char *config_get_all_timezones(void);
|
||||
void config_set_timezone(char *timezone);
|
||||
#endif
|
||||
|
||||
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;
|
||||
}
|
||||
+1866
-591
File diff suppressed because it is too large.
Load diff
+21
-36
@@ -45,25 +45,19 @@ typedef struct
|
||||
|
||||
/* Simple container/convenience struct for
|
||||
* holding the 'id' of a core file
|
||||
* > 'id' is the filename without extension or
|
||||
* > 'str' is the filename without extension or
|
||||
* platform-specific suffix
|
||||
* > 'id' is used for core info searches - enables
|
||||
* matching regardless of core file base path,
|
||||
* and is platform-independent (e.g. an Android
|
||||
* core file will be correctly identified on Linux)
|
||||
* > 'len' is used to cache the length of 'str', for
|
||||
* improved performance when performing string
|
||||
* comparisons */
|
||||
* > 'hash' is a hash key used for efficient core
|
||||
* list searches */
|
||||
typedef struct
|
||||
{
|
||||
char *str;
|
||||
size_t len;
|
||||
uint32_t hash;
|
||||
} core_file_id_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *path;
|
||||
void *config_data;
|
||||
char *display_name;
|
||||
char *display_version;
|
||||
char *core_name;
|
||||
@@ -89,12 +83,13 @@ typedef struct
|
||||
struct string_list *required_hw_api_list;
|
||||
core_info_firmware_t *firmware;
|
||||
core_file_id_t core_file_id; /* ptr alignment */
|
||||
void *userdata;
|
||||
size_t firmware_count;
|
||||
bool has_info;
|
||||
bool supports_no_game;
|
||||
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
|
||||
@@ -112,6 +107,7 @@ typedef struct
|
||||
core_info_t *list;
|
||||
char *all_ext;
|
||||
size_t count;
|
||||
size_t info_count;
|
||||
} core_info_list_t;
|
||||
|
||||
typedef struct core_info_ctx_firmware
|
||||
@@ -123,12 +119,6 @@ typedef struct core_info_ctx_firmware
|
||||
} directory;
|
||||
} core_info_ctx_firmware_t;
|
||||
|
||||
typedef struct core_info_ctx_find
|
||||
{
|
||||
core_info_t *inf;
|
||||
const char *path;
|
||||
} core_info_ctx_find_t;
|
||||
|
||||
struct core_info_state
|
||||
{
|
||||
#ifdef HAVE_COMPRESSION
|
||||
@@ -141,30 +131,21 @@ struct core_info_state
|
||||
|
||||
typedef struct core_info_state core_info_state_t;
|
||||
|
||||
size_t core_info_list_num_info_files(core_info_list_t *list);
|
||||
|
||||
/* Non-reentrant, does not allocate. Returns pointer to internal state. */
|
||||
void core_info_list_get_supported_cores(core_info_list_t *list,
|
||||
const char *path, const core_info_t **infos, size_t *num_infos);
|
||||
|
||||
bool core_info_list_get_display_name(core_info_list_t *list,
|
||||
const char *path, char *s, size_t len);
|
||||
|
||||
bool core_info_get_display_name(const char *path, char *s, size_t len);
|
||||
const char *core_path, char *s, size_t len);
|
||||
|
||||
/* Returns core_info parameters required for
|
||||
* core updater tasks, read from specified file.
|
||||
* Returned core_updater_info_t object must be
|
||||
* freed using core_info_free_core_updater_info().
|
||||
* Returns NULL if 'path' is invalid. */
|
||||
core_updater_info_t *core_info_get_core_updater_info(const char *path);
|
||||
core_updater_info_t *core_info_get_core_updater_info(const char *info_path);
|
||||
void core_info_free_core_updater_info(core_updater_info_t *info);
|
||||
|
||||
void core_info_get_name(const char *path, char *s, size_t len,
|
||||
const char *path_info, const char *dir_cores,
|
||||
const char *exts, bool show_hidden_files,
|
||||
bool get_display_name);
|
||||
|
||||
core_info_t *core_info_get(core_info_list_t *list, size_t i);
|
||||
|
||||
void core_info_free_current_core(core_info_state_t *p_coreinfo);
|
||||
@@ -176,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);
|
||||
|
||||
@@ -186,22 +167,20 @@ size_t core_info_count(void);
|
||||
bool core_info_list_update_missing_firmware(core_info_ctx_firmware_t *info,
|
||||
bool *set_missing_bios);
|
||||
|
||||
bool core_info_find(core_info_ctx_find_t *info);
|
||||
bool core_info_find(const char *core_path,
|
||||
core_info_t **core_info);
|
||||
|
||||
bool core_info_load(
|
||||
core_info_ctx_find_t *info,
|
||||
bool core_info_load(const char *core_path,
|
||||
core_info_state_t *p_coreinfo);
|
||||
|
||||
bool core_info_database_supports_content_path(const char *database_path, const char *path);
|
||||
|
||||
bool core_info_database_match_archive_member(const char *database_path);
|
||||
|
||||
bool core_info_unsupported_content_path(const char *path);
|
||||
|
||||
void core_info_qsort(core_info_list_t *core_info_list, enum core_info_list_qsort_type qsort_type);
|
||||
|
||||
bool core_info_list_get_info(core_info_list_t *core_info_list,
|
||||
core_info_t *out_info, const char *path);
|
||||
core_info_t *out_info, const char *core_path);
|
||||
|
||||
bool core_info_hw_api_supported(core_info_t *info);
|
||||
|
||||
@@ -223,7 +202,13 @@ bool core_info_get_core_lock(const char *core_path, bool validate_path);
|
||||
|
||||
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);
|
||||
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
|
||||
|
||||
|
||||
+3
-1
@@ -37,7 +37,9 @@ int database_info_build_query_enum(char *s, size_t len,
|
||||
bool add_quotes = true;
|
||||
bool add_glob = false;
|
||||
|
||||
strcpy_literal(s, "{'");
|
||||
s[0] = '{';
|
||||
s[1] = '\'';
|
||||
s[2] = '\0';
|
||||
|
||||
switch (type)
|
||||
{
|
||||
|
||||
Vendored
+128
-133
@@ -1,132 +1,157 @@
|
||||
/* 7z.h -- 7z interface
|
||||
2010-03-11 : Igor Pavlov : Public domain */
|
||||
2017-04-03 : Igor Pavlov : Public domain */
|
||||
|
||||
#ifndef __7Z_H
|
||||
#define __7Z_H
|
||||
|
||||
#include "7zBuf.h"
|
||||
#include "7zTypes.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
EXTERN_C_BEGIN
|
||||
|
||||
#define k7zStartHeaderSize 0x20
|
||||
#define k7zSignatureSize 6
|
||||
extern uint8_t k7zSignature[k7zSignatureSize];
|
||||
#define k7zMajorVersion 0
|
||||
|
||||
enum EIdEnum
|
||||
{
|
||||
k7zIdEnd,
|
||||
k7zIdHeader,
|
||||
k7zIdArchiveProperties,
|
||||
k7zIdAdditionalStreamsInfo,
|
||||
k7zIdMainStreamsInfo,
|
||||
k7zIdFilesInfo,
|
||||
k7zIdPackInfo,
|
||||
k7zIdUnpackInfo,
|
||||
k7zIdSubStreamsInfo,
|
||||
k7zIdSize,
|
||||
k7zIdCRC,
|
||||
k7zIdFolder,
|
||||
k7zIdCodersUnpackSize,
|
||||
k7zIdNumUnpackStream,
|
||||
k7zIdEmptyStream,
|
||||
k7zIdEmptyFile,
|
||||
k7zIdAnti,
|
||||
k7zIdName,
|
||||
k7zIdCTime,
|
||||
k7zIdATime,
|
||||
k7zIdMTime,
|
||||
k7zIdWinAttributes,
|
||||
k7zIdComment,
|
||||
k7zIdEncodedHeader,
|
||||
k7zIdStartPos,
|
||||
k7zIdDummy
|
||||
};
|
||||
extern const Byte k7zSignature[k7zSignatureSize];
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t NumInStreams;
|
||||
uint32_t NumOutStreams;
|
||||
uint64_t MethodID;
|
||||
CBuf Props;
|
||||
const Byte *Data;
|
||||
size_t Size;
|
||||
} CSzData;
|
||||
|
||||
/* CSzCoderInfo & CSzFolder support only default methods */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
size_t PropsOffset;
|
||||
UInt32 MethodID;
|
||||
Byte NumStreams;
|
||||
Byte PropsSize;
|
||||
} CSzCoderInfo;
|
||||
|
||||
void SzCoderInfo_Init(CSzCoderInfo *p);
|
||||
void SzCoderInfo_Free(CSzCoderInfo *p, ISzAlloc *alloc);
|
||||
typedef struct
|
||||
{
|
||||
UInt32 InIndex;
|
||||
UInt32 OutIndex;
|
||||
} CSzBond;
|
||||
|
||||
#define SZ_NUM_CODERS_IN_FOLDER_MAX 4
|
||||
#define SZ_NUM_BONDS_IN_FOLDER_MAX 3
|
||||
#define SZ_NUM_PACK_STREAMS_IN_FOLDER_MAX 4
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t InIndex;
|
||||
uint32_t OutIndex;
|
||||
} CSzBindPair;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CSzCoderInfo *Coders;
|
||||
CSzBindPair *BindPairs;
|
||||
uint32_t *PackStreams;
|
||||
uint64_t *UnpackSizes;
|
||||
uint32_t NumCoders;
|
||||
uint32_t NumBindPairs;
|
||||
uint32_t NumPackStreams;
|
||||
int UnpackCRCDefined;
|
||||
uint32_t UnpackCRC;
|
||||
|
||||
uint32_t NumUnpackStreams;
|
||||
UInt32 NumCoders;
|
||||
UInt32 NumBonds;
|
||||
UInt32 NumPackStreams;
|
||||
UInt32 UnpackStream;
|
||||
UInt32 PackStreams[SZ_NUM_PACK_STREAMS_IN_FOLDER_MAX];
|
||||
CSzBond Bonds[SZ_NUM_BONDS_IN_FOLDER_MAX];
|
||||
CSzCoderInfo Coders[SZ_NUM_CODERS_IN_FOLDER_MAX];
|
||||
} CSzFolder;
|
||||
|
||||
void SzFolder_Init(CSzFolder *p);
|
||||
uint64_t SzFolder_GetUnpackSize(CSzFolder *p);
|
||||
int SzFolder_FindBindPairForInStream(CSzFolder *p, uint32_t inStreamIndex);
|
||||
uint32_t SzFolder_GetNumOutStreams(CSzFolder *p);
|
||||
uint64_t SzFolder_GetUnpackSize(CSzFolder *p);
|
||||
|
||||
SRes SzFolder_Decode(const CSzFolder *folder, const uint64_t *packSizes,
|
||||
ILookInStream *stream, uint64_t startPos,
|
||||
uint8_t *outBuffer, size_t outSize, ISzAlloc *allocMain);
|
||||
SRes SzGetNextFolderItem(CSzFolder *f, CSzData *sd);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t Low;
|
||||
uint32_t High;
|
||||
UInt32 Low;
|
||||
UInt32 High;
|
||||
} CNtfsFileTime;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CNtfsFileTime MTime;
|
||||
uint64_t Size;
|
||||
uint32_t Crc;
|
||||
uint32_t Attrib;
|
||||
uint8_t HasStream;
|
||||
uint8_t IsDir;
|
||||
uint8_t IsAnti;
|
||||
uint8_t CrcDefined;
|
||||
uint8_t MTimeDefined;
|
||||
uint8_t AttribDefined;
|
||||
} CSzFileItem;
|
||||
|
||||
void SzFile_Init(CSzFileItem *p);
|
||||
Byte *Defs; /* MSB 0 bit numbering */
|
||||
UInt32 *Vals;
|
||||
} CSzBitUi32s;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint64_t *PackSizes;
|
||||
uint8_t *PackCRCsDefined;
|
||||
uint32_t *PackCRCs;
|
||||
CSzFolder *Folders;
|
||||
CSzFileItem *Files;
|
||||
uint32_t NumPackStreams;
|
||||
uint32_t NumFolders;
|
||||
uint32_t NumFiles;
|
||||
Byte *Defs; /* MSB 0 bit numbering */
|
||||
/* UInt64 *Vals; */
|
||||
CNtfsFileTime *Vals;
|
||||
} CSzBitUi64s;
|
||||
|
||||
#define SzBitArray_Check(p, i) (((p)[(i) >> 3] & (0x80 >> ((i) & 7))) != 0)
|
||||
|
||||
#define SzBitWithVals_Check(p, i) ((p)->Defs && ((p)->Defs[(i) >> 3] & (0x80 >> ((i) & 7))) != 0)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
UInt32 NumPackStreams;
|
||||
UInt32 NumFolders;
|
||||
|
||||
UInt64 *PackPositions; /* NumPackStreams + 1 */
|
||||
CSzBitUi32s FolderCRCs; /* NumFolders */
|
||||
|
||||
size_t *FoCodersOffsets; /* NumFolders + 1 */
|
||||
UInt32 *FoStartPackStreamIndex; /* NumFolders + 1 */
|
||||
UInt32 *FoToCoderUnpackSizes; /* NumFolders + 1 */
|
||||
Byte *FoToMainUnpackSizeIndex; /* NumFolders */
|
||||
UInt64 *CoderUnpackSizes; /* for all coders in all folders */
|
||||
|
||||
Byte *CodersData;
|
||||
} CSzAr;
|
||||
|
||||
void SzAr_Init(CSzAr *p);
|
||||
void SzAr_Free(CSzAr *p, ISzAlloc *alloc);
|
||||
UInt64 SzAr_GetFolderUnpackSize(const CSzAr *p, UInt32 folderIndex);
|
||||
|
||||
SRes SzAr_DecodeFolder(const CSzAr *p, UInt32 folderIndex,
|
||||
ILookInStream *stream, UInt64 startPos,
|
||||
Byte *outBuffer, size_t outSize,
|
||||
ISzAllocPtr allocMain);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CSzAr db;
|
||||
|
||||
UInt64 startPosAfterHeader;
|
||||
UInt64 dataPos;
|
||||
|
||||
UInt32 NumFiles;
|
||||
|
||||
UInt64 *UnpackPositions; /* NumFiles + 1 */
|
||||
/* Byte *IsEmptyFiles; */
|
||||
Byte *IsDirs;
|
||||
CSzBitUi32s CRCs;
|
||||
|
||||
CSzBitUi32s Attribs;
|
||||
/* CSzBitUi32s Parents; */
|
||||
CSzBitUi64s MTime;
|
||||
CSzBitUi64s CTime;
|
||||
|
||||
UInt32 *FolderToFile; /* NumFolders + 1 */
|
||||
UInt32 *FileToFolder; /* NumFiles */
|
||||
|
||||
size_t *FileNameOffsets; /* in 2-byte steps */
|
||||
Byte *FileNames; /* UTF-16-LE */
|
||||
} CSzArEx;
|
||||
|
||||
#define SzArEx_IsDir(p, i) (SzBitArray_Check((p)->IsDirs, i))
|
||||
|
||||
#define SzArEx_GetFileSize(p, i) ((p)->UnpackPositions[(i) + 1] - (p)->UnpackPositions[i])
|
||||
|
||||
void SzArEx_Init(CSzArEx *p);
|
||||
void SzArEx_Free(CSzArEx *p, ISzAllocPtr alloc);
|
||||
UInt64 SzArEx_GetFolderStreamPos(const CSzArEx *p, UInt32 folderIndex, UInt32 indexInFolder);
|
||||
int SzArEx_GetFolderFullPackSize(const CSzArEx *p, UInt32 folderIndex, UInt64 *resSize);
|
||||
|
||||
/*
|
||||
if dest == NULL, the return value specifies the required size of the buffer,
|
||||
in 16-bit characters, including the null-terminating character.
|
||||
if dest != NULL, the return value specifies the number of 16-bit characters that
|
||||
are written to the dest, including the null-terminating character. */
|
||||
|
||||
size_t SzArEx_GetFileNameUtf16(const CSzArEx *p, size_t fileIndex, UInt16 *dest);
|
||||
|
||||
/*
|
||||
size_t SzArEx_GetFullNameLen(const CSzArEx *p, size_t fileIndex);
|
||||
UInt16 *SzArEx_GetFullNameUtf16_Back(const CSzArEx *p, size_t fileIndex, UInt16 *dest);
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
SzExtract extracts file from archive
|
||||
SzArEx_Extract extracts file from archive
|
||||
|
||||
*outBuffer must be 0 before first call for each new archive.
|
||||
|
||||
@@ -138,53 +163,24 @@ void SzAr_Free(CSzAr *p, ISzAlloc *alloc);
|
||||
*outBufferSize
|
||||
You can consider "*outBuffer" as cache of solid block. If your archive is solid,
|
||||
it will increase decompression speed.
|
||||
|
||||
|
||||
If you use external function, you can declare these 3 cache variables
|
||||
(blockIndex, outBuffer, outBufferSize) as static in that external function.
|
||||
|
||||
|
||||
Free *outBuffer and set *outBuffer to 0, if you want to flush cache.
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CSzAr db;
|
||||
|
||||
uint64_t startPosAfterHeader;
|
||||
uint64_t dataPos;
|
||||
|
||||
uint32_t *FolderStartPackStreamIndex;
|
||||
uint64_t *PackStreamStartPositions;
|
||||
uint32_t *FolderStartFileIndex;
|
||||
uint32_t *FileIndexToFolderIndexMap;
|
||||
|
||||
size_t *FileNameOffsets; /* in 2-byte steps */
|
||||
CBuf FileNames; /* UTF-16-LE */
|
||||
} CSzArEx;
|
||||
|
||||
void SzArEx_Init(CSzArEx *p);
|
||||
void SzArEx_Free(CSzArEx *p, ISzAlloc *alloc);
|
||||
uint64_t SzArEx_GetFolderStreamPos(const CSzArEx *p, uint32_t folderIndex, uint32_t indexInFolder);
|
||||
int SzArEx_GetFolderFullPackSize(const CSzArEx *p, uint32_t folderIndex, uint64_t *resSize);
|
||||
|
||||
/*
|
||||
if dest == NULL, the return value specifies the required size of the buffer,
|
||||
in 16-bit characters, including the null-terminating character.
|
||||
if dest != NULL, the return value specifies the number of 16-bit characters that
|
||||
are written to the dest, including the null-terminating character. */
|
||||
|
||||
size_t SzArEx_GetFileNameUtf16(const CSzArEx *p, size_t fileIndex, uint16_t *dest);
|
||||
|
||||
SRes SzArEx_Extract(
|
||||
const CSzArEx *db,
|
||||
ILookInStream *inStream,
|
||||
uint32_t fileIndex, /* index of file */
|
||||
uint32_t *blockIndex, /* index of solid block */
|
||||
uint8_t **outBuffer, /* pointer to pointer to output buffer (allocated with allocMain) */
|
||||
UInt32 fileIndex, /* index of file */
|
||||
UInt32 *blockIndex, /* index of solid block */
|
||||
Byte **outBuffer, /* pointer to pointer to output buffer (allocated with allocMain) */
|
||||
size_t *outBufferSize, /* buffer size for output buffer */
|
||||
size_t *offset, /* offset of stream for required file in *outBuffer */
|
||||
size_t *outSizeProcessed, /* size of file in *outBuffer */
|
||||
ISzAlloc *allocMain,
|
||||
ISzAlloc *allocTemp);
|
||||
ISzAllocPtr allocMain,
|
||||
ISzAllocPtr allocTemp);
|
||||
|
||||
|
||||
/*
|
||||
@@ -198,10 +194,9 @@ SZ_ERROR_INPUT_EOF
|
||||
SZ_ERROR_FAIL
|
||||
*/
|
||||
|
||||
SRes SzArEx_Open(CSzArEx *p, ILookInStream *inStream, ISzAlloc *allocMain, ISzAlloc *allocTemp);
|
||||
SRes SzArEx_Open(CSzArEx *p, ILookInStream *inStream,
|
||||
ISzAllocPtr allocMain, ISzAllocPtr allocTemp);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
EXTERN_C_END
|
||||
|
||||
#endif
|
||||
Vendored
+1771
File diff suppressed because it is too large.
Load diff
Vendored
+23
-24
@@ -1,37 +1,36 @@
|
||||
/* 7zBuf.c -- Byte Buffer
|
||||
2008-03-28
|
||||
Igor Pavlov
|
||||
Public domain */
|
||||
2017-04-03 : Igor Pavlov : Public domain */
|
||||
|
||||
#include "Precomp.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include "7zBuf.h"
|
||||
|
||||
void Buf_Init(CBuf *p)
|
||||
{
|
||||
p->data = 0;
|
||||
p->size = 0;
|
||||
p->data = 0;
|
||||
p->size = 0;
|
||||
}
|
||||
|
||||
int Buf_Create(CBuf *p, size_t size, ISzAlloc *alloc)
|
||||
int Buf_Create(CBuf *p, size_t size, ISzAllocPtr alloc)
|
||||
{
|
||||
p->size = 0;
|
||||
if (size == 0)
|
||||
{
|
||||
p->data = 0;
|
||||
return 1;
|
||||
}
|
||||
p->data = (uint8_t *)alloc->Alloc(alloc, size);
|
||||
if (p->data != 0)
|
||||
{
|
||||
p->size = size;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
p->size = 0;
|
||||
if (size == 0)
|
||||
{
|
||||
p->data = 0;
|
||||
return 1;
|
||||
}
|
||||
p->data = (Byte *)ISzAlloc_Alloc(alloc, size);
|
||||
if (p->data)
|
||||
{
|
||||
p->size = size;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Buf_Free(CBuf *p, ISzAlloc *alloc)
|
||||
void Buf_Free(CBuf *p, ISzAllocPtr alloc)
|
||||
{
|
||||
alloc->Free(alloc, p->data);
|
||||
p->data = 0;
|
||||
p->size = 0;
|
||||
ISzAlloc_Free(alloc, p->data);
|
||||
p->data = 0;
|
||||
p->size = 0;
|
||||
}
|
||||
Vendored
+18
-10
@@ -1,27 +1,35 @@
|
||||
/* 7zBuf.h -- Byte Buffer
|
||||
2009-02-07 : Igor Pavlov : Public domain */
|
||||
2017-04-03 : Igor Pavlov : Public domain */
|
||||
|
||||
#ifndef __7Z_BUF_H
|
||||
#define __7Z_BUF_H
|
||||
|
||||
#include "7zTypes.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
EXTERN_C_BEGIN
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t *data;
|
||||
Byte *data;
|
||||
size_t size;
|
||||
} CBuf;
|
||||
|
||||
void Buf_Init(CBuf *p);
|
||||
int Buf_Create(CBuf *p, size_t size, ISzAlloc *alloc);
|
||||
void Buf_Free(CBuf *p, ISzAlloc *alloc);
|
||||
int Buf_Create(CBuf *p, size_t size, ISzAllocPtr alloc);
|
||||
void Buf_Free(CBuf *p, ISzAllocPtr alloc);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
typedef struct
|
||||
{
|
||||
Byte *data;
|
||||
size_t size;
|
||||
size_t pos;
|
||||
} CDynBuf;
|
||||
|
||||
void DynBuf_Construct(CDynBuf *p);
|
||||
void DynBuf_SeekToBeg(CDynBuf *p);
|
||||
int DynBuf_Write(CDynBuf *p, const Byte *buf, size_t size, ISzAllocPtr alloc);
|
||||
void DynBuf_Free(CDynBuf *p, ISzAllocPtr alloc);
|
||||
|
||||
EXTERN_C_END
|
||||
|
||||
#endif
|
||||
Vendored
+106
-54
@@ -1,76 +1,128 @@
|
||||
/* 7zCrc.c -- CRC32 calculation
|
||||
2009-11-23 : Igor Pavlov : Public domain */
|
||||
/* 7zCrc.c -- CRC32 init
|
||||
2017-06-06 : Igor Pavlov : Public domain */
|
||||
|
||||
#include "Precomp.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include "7zCrc.h"
|
||||
#include "CpuArch.h"
|
||||
|
||||
#define kCrcPoly 0xEDB88320
|
||||
|
||||
#ifdef MSB_FIRST
|
||||
#define CRC_NUM_TABLES 1
|
||||
#ifdef MY_CPU_LE
|
||||
#define CRC_NUM_TABLES 8
|
||||
#else
|
||||
#define CRC_NUM_TABLES 8
|
||||
#define CRC_NUM_TABLES 9
|
||||
|
||||
#define CRC_UINT32_SWAP(v) ((v >> 24) | ((v >> 8) & 0xFF00) | ((v << 8) & 0xFF0000) | (v << 24))
|
||||
|
||||
UInt32 MY_FAST_CALL CrcUpdateT1_BeT4(UInt32 v, const void *data, size_t size, const UInt32 *table);
|
||||
UInt32 MY_FAST_CALL CrcUpdateT1_BeT8(UInt32 v, const void *data, size_t size, const UInt32 *table);
|
||||
#endif
|
||||
|
||||
typedef uint32_t (MY_FAST_CALL *CRC_FUNC)(uint32_t v, const void *data, size_t size, const uint32_t *table);
|
||||
#ifndef MY_CPU_BE
|
||||
UInt32 MY_FAST_CALL CrcUpdateT4(UInt32 v, const void *data, size_t size, const UInt32 *table);
|
||||
UInt32 MY_FAST_CALL CrcUpdateT8(UInt32 v, const void *data, size_t size, const UInt32 *table);
|
||||
#endif
|
||||
|
||||
static CRC_FUNC g_CrcUpdate;
|
||||
uint32_t g_CrcTable[256 * CRC_NUM_TABLES];
|
||||
typedef UInt32 (MY_FAST_CALL *CRC_FUNC)(UInt32 v, const void *data, size_t size, const UInt32 *table);
|
||||
|
||||
#if CRC_NUM_TABLES == 1
|
||||
CRC_FUNC g_CrcUpdateT4;
|
||||
CRC_FUNC g_CrcUpdateT8;
|
||||
CRC_FUNC g_CrcUpdate;
|
||||
|
||||
UInt32 g_CrcTable[256 * CRC_NUM_TABLES];
|
||||
|
||||
UInt32 MY_FAST_CALL CrcUpdate(UInt32 v, const void *data, size_t size)
|
||||
{
|
||||
return g_CrcUpdate(v, data, size, g_CrcTable);
|
||||
}
|
||||
|
||||
UInt32 MY_FAST_CALL CrcCalc(const void *data, size_t size)
|
||||
{
|
||||
return g_CrcUpdate(CRC_INIT_VAL, data, size, g_CrcTable) ^ CRC_INIT_VAL;
|
||||
}
|
||||
|
||||
#define CRC_UPDATE_BYTE_2(crc, b) (table[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8))
|
||||
|
||||
static uint32_t MY_FAST_CALL CrcUpdateT1(uint32_t v, const void *data, size_t size, const uint32_t *table)
|
||||
UInt32 MY_FAST_CALL CrcUpdateT1(UInt32 v, const void *data, size_t size, const UInt32 *table)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)data;
|
||||
for (; size > 0; size--, p++)
|
||||
v = CRC_UPDATE_BYTE_2(v, *p);
|
||||
return v;
|
||||
const Byte *p = (const Byte *)data;
|
||||
const Byte *pEnd = p + size;
|
||||
for (; p != pEnd; p++)
|
||||
v = CRC_UPDATE_BYTE_2(v, *p);
|
||||
return v;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
uint32_t MY_FAST_CALL CrcUpdateT4(uint32_t v, const void *data, size_t size, const uint32_t *table);
|
||||
uint32_t MY_FAST_CALL CrcUpdateT8(uint32_t v, const void *data, size_t size, const uint32_t *table);
|
||||
|
||||
#endif
|
||||
|
||||
uint32_t MY_FAST_CALL CrcUpdate(uint32_t v, const void *data, size_t size)
|
||||
void MY_FAST_CALL CrcGenerateTable()
|
||||
{
|
||||
return g_CrcUpdate(v, data, size, g_CrcTable);
|
||||
}
|
||||
UInt32 i;
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
UInt32 r = i;
|
||||
unsigned j;
|
||||
for (j = 0; j < 8; j++)
|
||||
r = (r >> 1) ^ (kCrcPoly & ((UInt32)0 - (r & 1)));
|
||||
g_CrcTable[i] = r;
|
||||
}
|
||||
for (i = 256; i < 256 * CRC_NUM_TABLES; i++)
|
||||
{
|
||||
UInt32 r = g_CrcTable[(size_t)i - 256];
|
||||
g_CrcTable[i] = g_CrcTable[r & 0xFF] ^ (r >> 8);
|
||||
}
|
||||
|
||||
uint32_t MY_FAST_CALL CrcCalc(const void *data, size_t size)
|
||||
{
|
||||
return g_CrcUpdate(CRC_INIT_VAL, data, size, g_CrcTable) ^ CRC_INIT_VAL;
|
||||
}
|
||||
#if CRC_NUM_TABLES < 4
|
||||
|
||||
g_CrcUpdate = CrcUpdateT1;
|
||||
|
||||
#else
|
||||
|
||||
#ifdef MY_CPU_LE
|
||||
|
||||
void MY_FAST_CALL CrcGenerateTable(void)
|
||||
{
|
||||
uint32_t i;
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
uint32_t r = i;
|
||||
unsigned j;
|
||||
for (j = 0; j < 8; j++)
|
||||
r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1));
|
||||
g_CrcTable[i] = r;
|
||||
}
|
||||
#if CRC_NUM_TABLES == 1
|
||||
g_CrcUpdate = CrcUpdateT1;
|
||||
#else
|
||||
for (; i < 256 * CRC_NUM_TABLES; i++)
|
||||
{
|
||||
uint32_t r = g_CrcTable[i - 256];
|
||||
g_CrcTable[i] = g_CrcTable[r & 0xFF] ^ (r >> 8);
|
||||
}
|
||||
g_CrcUpdateT4 = CrcUpdateT4;
|
||||
g_CrcUpdate = CrcUpdateT4;
|
||||
|
||||
#ifdef MY_CPU_X86_OR_AMD64
|
||||
g_CrcUpdate = CrcUpdateT8;
|
||||
#else
|
||||
g_CrcUpdate = CrcUpdateT4;
|
||||
#endif
|
||||
#endif
|
||||
#if CRC_NUM_TABLES >= 8
|
||||
g_CrcUpdateT8 = CrcUpdateT8;
|
||||
|
||||
#ifdef MY_CPU_X86_OR_AMD64
|
||||
if (!CPU_Is_InOrder())
|
||||
#endif
|
||||
g_CrcUpdate = CrcUpdateT8;
|
||||
#endif
|
||||
|
||||
#else
|
||||
{
|
||||
#ifndef MY_CPU_BE
|
||||
UInt32 k = 0x01020304;
|
||||
const Byte *p = (const Byte *)&k;
|
||||
if (p[0] == 4 && p[1] == 3)
|
||||
{
|
||||
g_CrcUpdateT4 = CrcUpdateT4;
|
||||
g_CrcUpdate = CrcUpdateT4;
|
||||
#if CRC_NUM_TABLES >= 8
|
||||
g_CrcUpdateT8 = CrcUpdateT8;
|
||||
g_CrcUpdate = CrcUpdateT8;
|
||||
#endif
|
||||
}
|
||||
else if (p[0] != 1 || p[1] != 2)
|
||||
g_CrcUpdate = CrcUpdateT1;
|
||||
else
|
||||
#endif
|
||||
{
|
||||
for (i = 256 * CRC_NUM_TABLES - 1; i >= 256; i--)
|
||||
{
|
||||
UInt32 x = g_CrcTable[(size_t)i - 256];
|
||||
g_CrcTable[i] = CRC_UINT32_SWAP(x);
|
||||
}
|
||||
g_CrcUpdateT4 = CrcUpdateT1_BeT4;
|
||||
g_CrcUpdate = CrcUpdateT1_BeT4;
|
||||
#if CRC_NUM_TABLES >= 8
|
||||
g_CrcUpdateT8 = CrcUpdateT1_BeT8;
|
||||
g_CrcUpdate = CrcUpdateT1_BeT8;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
Vendored
+6
-10
@@ -1,16 +1,14 @@
|
||||
/* 7zCrc.h -- CRC32 calculation
|
||||
2009-11-21 : Igor Pavlov : Public domain */
|
||||
2013-01-18 : Igor Pavlov : Public domain */
|
||||
|
||||
#ifndef __7Z_CRC_H
|
||||
#define __7Z_CRC_H
|
||||
|
||||
#include "7zTypes.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
EXTERN_C_BEGIN
|
||||
|
||||
extern uint32_t g_CrcTable[];
|
||||
extern UInt32 g_CrcTable[];
|
||||
|
||||
/* Call CrcGenerateTable one time before other CRC functions */
|
||||
void MY_FAST_CALL CrcGenerateTable(void);
|
||||
@@ -19,11 +17,9 @@ void MY_FAST_CALL CrcGenerateTable(void);
|
||||
#define CRC_GET_DIGEST(crc) ((crc) ^ CRC_INIT_VAL)
|
||||
#define CRC_UPDATE_BYTE(crc, b) (g_CrcTable[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8))
|
||||
|
||||
uint32_t MY_FAST_CALL CrcUpdate(uint32_t crc, const void *data, size_t size);
|
||||
uint32_t MY_FAST_CALL CrcCalc(const void *data, size_t size);
|
||||
UInt32 MY_FAST_CALL CrcUpdate(UInt32 crc, const void *data, size_t size);
|
||||
UInt32 MY_FAST_CALL CrcCalc(const void *data, size_t size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
EXTERN_C_END
|
||||
|
||||
#endif
|
||||
Vendored
+102
-25
@@ -1,38 +1,115 @@
|
||||
/* 7zCrcOpt.c -- CRC32 calculation : optimized version
|
||||
2009-11-23 : Igor Pavlov : Public domain */
|
||||
/* 7zCrcOpt.c -- CRC32 calculation
|
||||
2017-04-03 : Igor Pavlov : Public domain */
|
||||
|
||||
#include "Precomp.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include "CpuArch.h"
|
||||
|
||||
#ifndef MSB_FIRST
|
||||
#ifndef MY_CPU_BE
|
||||
|
||||
#define CRC_UPDATE_BYTE_2(crc, b) (table[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8))
|
||||
|
||||
uint32_t MY_FAST_CALL CrcUpdateT4(uint32_t v, const void *data, size_t size, const uint32_t *table);
|
||||
uint32_t MY_FAST_CALL CrcUpdateT8(uint32_t v, const void *data, size_t size, const uint32_t *table);
|
||||
|
||||
uint32_t MY_FAST_CALL CrcUpdateT4(uint32_t v, const void *data, size_t size, const uint32_t *table)
|
||||
UInt32 MY_FAST_CALL CrcUpdateT4(UInt32 v, const void *data, size_t size, const UInt32 *table)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t*)data;
|
||||
for (; size > 0 && ((unsigned)(ptrdiff_t)p & 3) != 0; size--, p++)
|
||||
v = CRC_UPDATE_BYTE_2(v, *p);
|
||||
for (; size >= 4; size -= 4, p += 4)
|
||||
{
|
||||
v ^= *(const uint32_t *)p;
|
||||
v =
|
||||
table[0x300 + (v & 0xFF)] ^
|
||||
table[0x200 + ((v >> 8) & 0xFF)] ^
|
||||
table[0x100 + ((v >> 16) & 0xFF)] ^
|
||||
table[0x000 + ((v >> 24))];
|
||||
}
|
||||
for (; size > 0; size--, p++)
|
||||
v = CRC_UPDATE_BYTE_2(v, *p);
|
||||
return v;
|
||||
const Byte *p = (const Byte *)data;
|
||||
for (; size > 0 && ((unsigned)(ptrdiff_t)p & 3) != 0; size--, p++)
|
||||
v = CRC_UPDATE_BYTE_2(v, *p);
|
||||
for (; size >= 4; size -= 4, p += 4)
|
||||
{
|
||||
v ^= *(const UInt32 *)p;
|
||||
v =
|
||||
(table + 0x300)[((v ) & 0xFF)]
|
||||
^ (table + 0x200)[((v >> 8) & 0xFF)]
|
||||
^ (table + 0x100)[((v >> 16) & 0xFF)]
|
||||
^ (table + 0x000)[((v >> 24))];
|
||||
}
|
||||
for (; size > 0; size--, p++)
|
||||
v = CRC_UPDATE_BYTE_2(v, *p);
|
||||
return v;
|
||||
}
|
||||
|
||||
uint32_t MY_FAST_CALL CrcUpdateT8(uint32_t v, const void *data, size_t size, const uint32_t *table)
|
||||
UInt32 MY_FAST_CALL CrcUpdateT8(UInt32 v, const void *data, size_t size, const UInt32 *table)
|
||||
{
|
||||
return CrcUpdateT4(v, data, size, table);
|
||||
const Byte *p = (const Byte *)data;
|
||||
for (; size > 0 && ((unsigned)(ptrdiff_t)p & 7) != 0; size--, p++)
|
||||
v = CRC_UPDATE_BYTE_2(v, *p);
|
||||
for (; size >= 8; size -= 8, p += 8)
|
||||
{
|
||||
UInt32 d;
|
||||
v ^= *(const UInt32 *)p;
|
||||
v =
|
||||
(table + 0x700)[((v ) & 0xFF)]
|
||||
^ (table + 0x600)[((v >> 8) & 0xFF)]
|
||||
^ (table + 0x500)[((v >> 16) & 0xFF)]
|
||||
^ (table + 0x400)[((v >> 24))];
|
||||
d = *((const UInt32 *)p + 1);
|
||||
v ^=
|
||||
(table + 0x300)[((d ) & 0xFF)]
|
||||
^ (table + 0x200)[((d >> 8) & 0xFF)]
|
||||
^ (table + 0x100)[((d >> 16) & 0xFF)]
|
||||
^ (table + 0x000)[((d >> 24))];
|
||||
}
|
||||
for (; size > 0; size--, p++)
|
||||
v = CRC_UPDATE_BYTE_2(v, *p);
|
||||
return v;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef MY_CPU_LE
|
||||
|
||||
#define CRC_UINT32_SWAP(v) ((v >> 24) | ((v >> 8) & 0xFF00) | ((v << 8) & 0xFF0000) | (v << 24))
|
||||
|
||||
#define CRC_UPDATE_BYTE_2_BE(crc, b) (table[(((crc) >> 24) ^ (b))] ^ ((crc) << 8))
|
||||
|
||||
UInt32 MY_FAST_CALL CrcUpdateT1_BeT4(UInt32 v, const void *data, size_t size, const UInt32 *table)
|
||||
{
|
||||
const Byte *p = (const Byte *)data;
|
||||
table += 0x100;
|
||||
v = CRC_UINT32_SWAP(v);
|
||||
for (; size > 0 && ((unsigned)(ptrdiff_t)p & 3) != 0; size--, p++)
|
||||
v = CRC_UPDATE_BYTE_2_BE(v, *p);
|
||||
for (; size >= 4; size -= 4, p += 4)
|
||||
{
|
||||
v ^= *(const UInt32 *)p;
|
||||
v =
|
||||
(table + 0x000)[((v ) & 0xFF)]
|
||||
^ (table + 0x100)[((v >> 8) & 0xFF)]
|
||||
^ (table + 0x200)[((v >> 16) & 0xFF)]
|
||||
^ (table + 0x300)[((v >> 24))];
|
||||
}
|
||||
for (; size > 0; size--, p++)
|
||||
v = CRC_UPDATE_BYTE_2_BE(v, *p);
|
||||
return CRC_UINT32_SWAP(v);
|
||||
}
|
||||
|
||||
UInt32 MY_FAST_CALL CrcUpdateT1_BeT8(UInt32 v, const void *data, size_t size, const UInt32 *table)
|
||||
{
|
||||
const Byte *p = (const Byte *)data;
|
||||
table += 0x100;
|
||||
v = CRC_UINT32_SWAP(v);
|
||||
for (; size > 0 && ((unsigned)(ptrdiff_t)p & 7) != 0; size--, p++)
|
||||
v = CRC_UPDATE_BYTE_2_BE(v, *p);
|
||||
for (; size >= 8; size -= 8, p += 8)
|
||||
{
|
||||
UInt32 d;
|
||||
v ^= *(const UInt32 *)p;
|
||||
v =
|
||||
(table + 0x400)[((v ) & 0xFF)]
|
||||
^ (table + 0x500)[((v >> 8) & 0xFF)]
|
||||
^ (table + 0x600)[((v >> 16) & 0xFF)]
|
||||
^ (table + 0x700)[((v >> 24))];
|
||||
d = *((const UInt32 *)p + 1);
|
||||
v ^=
|
||||
(table + 0x000)[((d ) & 0xFF)]
|
||||
^ (table + 0x100)[((d >> 8) & 0xFF)]
|
||||
^ (table + 0x200)[((d >> 16) & 0xFF)]
|
||||
^ (table + 0x300)[((d >> 24))];
|
||||
}
|
||||
for (; size > 0; size--, p++)
|
||||
v = CRC_UPDATE_BYTE_2_BE(v, *p);
|
||||
return CRC_UINT32_SWAP(v);
|
||||
}
|
||||
|
||||
#endif
|
||||
Vendored
+488
-340
@@ -1,83 +1,184 @@
|
||||
/* 7zDec.c -- Decoding from 7z folder
|
||||
2010-11-02 : Igor Pavlov : Public domain */
|
||||
2019-02-02 : Igor Pavlov : Public domain */
|
||||
|
||||
#include "Precomp.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <boolean.h>
|
||||
|
||||
/* #define _7ZIP_PPMD_SUPPPORT */
|
||||
|
||||
#include "7z.h"
|
||||
#include "7zCrc.h"
|
||||
|
||||
#include "Bcj2.h"
|
||||
#include "Bra.h"
|
||||
#include "CpuArch.h"
|
||||
#include "Delta.h"
|
||||
#include "LzmaDec.h"
|
||||
#include "Lzma2Dec.h"
|
||||
#ifdef _7ZIP_PPMD_SUPPPORT
|
||||
#include "Ppmd7.h"
|
||||
#endif
|
||||
|
||||
#define k_Copy 0
|
||||
#define k_Delta 3
|
||||
#define k_LZMA2 0x21
|
||||
#define k_LZMA 0x30101
|
||||
#define k_BCJ 0x03030103
|
||||
#define k_PPC 0x03030205
|
||||
#define k_ARM 0x03030501
|
||||
#define k_ARMT 0x03030701
|
||||
#define k_SPARC 0x03030805
|
||||
#define k_BCJ2 0x0303011B
|
||||
#define k_BCJ 0x3030103
|
||||
#define k_BCJ2 0x303011B
|
||||
#define k_PPC 0x3030205
|
||||
#define k_IA64 0x3030401
|
||||
#define k_ARM 0x3030501
|
||||
#define k_ARMT 0x3030701
|
||||
#define k_SPARC 0x3030805
|
||||
|
||||
static SRes SzDecodeLzma(CSzCoderInfo *coder,
|
||||
uint64_t inSize, ILookInStream *inStream,
|
||||
uint8_t *outBuffer, size_t outSize, ISzAlloc *allocMain)
|
||||
|
||||
#ifdef _7ZIP_PPMD_SUPPPORT
|
||||
|
||||
#define k_PPMD 0x30401
|
||||
|
||||
typedef struct
|
||||
{
|
||||
IByteIn vt;
|
||||
const Byte *cur;
|
||||
const Byte *end;
|
||||
const Byte *begin;
|
||||
UInt64 processed;
|
||||
BoolInt extra;
|
||||
SRes res;
|
||||
const ILookInStream *inStream;
|
||||
} CByteInToLook;
|
||||
|
||||
static Byte ReadByte(const IByteIn *pp)
|
||||
{
|
||||
CByteInToLook *p = CONTAINER_FROM_VTBL(pp, CByteInToLook, vt);
|
||||
if (p->cur != p->end)
|
||||
return *p->cur++;
|
||||
if (p->res == SZ_OK)
|
||||
{
|
||||
size_t size = p->cur - p->begin;
|
||||
p->processed += size;
|
||||
p->res = ILookInStream_Skip(p->inStream, size);
|
||||
size = (1 << 25);
|
||||
p->res = ILookInStream_Look(p->inStream, (const void **)&p->begin, &size);
|
||||
p->cur = p->begin;
|
||||
p->end = p->begin + size;
|
||||
if (size != 0)
|
||||
return *p->cur++;;
|
||||
}
|
||||
p->extra = True;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SRes SzDecodePpmd(const Byte *props, unsigned propsSize, UInt64 inSize, const ILookInStream *inStream,
|
||||
Byte *outBuffer, SizeT outSize, ISzAllocPtr allocMain)
|
||||
{
|
||||
CPpmd7 ppmd;
|
||||
CByteInToLook s;
|
||||
SRes res = SZ_OK;
|
||||
|
||||
s.vt.Read = ReadByte;
|
||||
s.inStream = inStream;
|
||||
s.begin = s.end = s.cur = NULL;
|
||||
s.extra = False;
|
||||
s.res = SZ_OK;
|
||||
s.processed = 0;
|
||||
|
||||
if (propsSize != 5)
|
||||
return SZ_ERROR_UNSUPPORTED;
|
||||
|
||||
{
|
||||
unsigned order = props[0];
|
||||
UInt32 memSize = GetUi32(props + 1);
|
||||
if (order < PPMD7_MIN_ORDER ||
|
||||
order > PPMD7_MAX_ORDER ||
|
||||
memSize < PPMD7_MIN_MEM_SIZE ||
|
||||
memSize > PPMD7_MAX_MEM_SIZE)
|
||||
return SZ_ERROR_UNSUPPORTED;
|
||||
Ppmd7_Construct(&ppmd);
|
||||
if (!Ppmd7_Alloc(&ppmd, memSize, allocMain))
|
||||
return SZ_ERROR_MEM;
|
||||
Ppmd7_Init(&ppmd, order);
|
||||
}
|
||||
{
|
||||
CPpmd7z_RangeDec rc;
|
||||
Ppmd7z_RangeDec_CreateVTable(&rc);
|
||||
rc.Stream = &s.vt;
|
||||
if (!Ppmd7z_RangeDec_Init(&rc))
|
||||
res = SZ_ERROR_DATA;
|
||||
else if (s.extra)
|
||||
res = (s.res != SZ_OK ? s.res : SZ_ERROR_DATA);
|
||||
else
|
||||
{
|
||||
SizeT i;
|
||||
for (i = 0; i < outSize; i++)
|
||||
{
|
||||
int sym = Ppmd7_DecodeSymbol(&ppmd, &rc.vt);
|
||||
if (s.extra || sym < 0)
|
||||
break;
|
||||
outBuffer[i] = (Byte)sym;
|
||||
}
|
||||
if (i != outSize)
|
||||
res = (s.res != SZ_OK ? s.res : SZ_ERROR_DATA);
|
||||
else if (s.processed + (s.cur - s.begin) != inSize || !Ppmd7z_RangeDec_IsFinishedOK(&rc))
|
||||
res = SZ_ERROR_DATA;
|
||||
}
|
||||
}
|
||||
Ppmd7_Free(&ppmd, allocMain);
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static SRes SzDecodeLzma(const Byte *props, unsigned propsSize, UInt64 inSize, ILookInStream *inStream,
|
||||
Byte *outBuffer, SizeT outSize, ISzAllocPtr allocMain)
|
||||
{
|
||||
SRes result;
|
||||
CLzmaDec state;
|
||||
SRes res = SZ_OK;
|
||||
SRes res = SZ_OK;
|
||||
|
||||
LzmaDec_Construct(&state);
|
||||
result = LzmaDec_AllocateProbs(
|
||||
&state, coder->Props.data,
|
||||
(unsigned)coder->Props.size, allocMain);
|
||||
if (result != 0)
|
||||
return result;
|
||||
state.dic = outBuffer;
|
||||
RINOK(LzmaDec_AllocateProbs(&state, props, propsSize, allocMain));
|
||||
state.dic = outBuffer;
|
||||
state.dicBufSize = outSize;
|
||||
LzmaDec_Init(&state);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
uint8_t *inBuf = NULL;
|
||||
const void *inBuf = NULL;
|
||||
size_t lookahead = (1 << 18);
|
||||
if (lookahead > inSize)
|
||||
lookahead = (size_t)inSize;
|
||||
res = inStream->Look(
|
||||
(void *)inStream, (const void **)&inBuf, &lookahead);
|
||||
|
||||
lookahead = (size_t)inSize;
|
||||
res = ILookInStream_Look(inStream, &inBuf, &lookahead);
|
||||
if (res != SZ_OK)
|
||||
break;
|
||||
|
||||
{
|
||||
SizeT inProcessed = (SizeT)lookahead, dicPos = state.dicPos;
|
||||
ELzmaStatus status;
|
||||
size_t inProcessed = (size_t)lookahead, dicPos = state.dicPos;
|
||||
res = LzmaDec_DecodeToDic(
|
||||
&state, outSize, inBuf, &inProcessed, LZMA_FINISH_END, &status);
|
||||
lookahead -= inProcessed;
|
||||
inSize -= inProcessed;
|
||||
|
||||
res = LzmaDec_DecodeToDic(&state, outSize, (const Byte *)inBuf, &inProcessed, LZMA_FINISH_END, &status);
|
||||
lookahead -= inProcessed;
|
||||
inSize -= inProcessed;
|
||||
if (res != SZ_OK)
|
||||
break;
|
||||
|
||||
if (
|
||||
state.dicPos == state.dicBufSize ||
|
||||
(inProcessed == 0 && dicPos == state.dicPos))
|
||||
if (status == LZMA_STATUS_FINISHED_WITH_MARK)
|
||||
{
|
||||
if (
|
||||
state.dicBufSize != outSize ||
|
||||
lookahead != 0 ||
|
||||
(status != LZMA_STATUS_FINISHED_WITH_MARK &&
|
||||
status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK))
|
||||
if (outSize != state.dicPos || inSize != 0)
|
||||
res = SZ_ERROR_DATA;
|
||||
break;
|
||||
}
|
||||
|
||||
res = inStream->Skip((void *)inStream, inProcessed);
|
||||
if (outSize == state.dicPos && inSize == 0 && status == LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK)
|
||||
break;
|
||||
|
||||
if (inProcessed == 0 && dicPos == state.dicPos)
|
||||
{
|
||||
res = SZ_ERROR_DATA;
|
||||
break;
|
||||
}
|
||||
|
||||
res = ILookInStream_Skip(inStream, inProcessed);
|
||||
if (res != SZ_OK)
|
||||
break;
|
||||
}
|
||||
@@ -87,357 +188,404 @@ static SRes SzDecodeLzma(CSzCoderInfo *coder,
|
||||
return res;
|
||||
}
|
||||
|
||||
static SRes SzDecodeLzma2(CSzCoderInfo *coder,
|
||||
uint64_t inSize, ILookInStream *inStream,
|
||||
uint8_t *outBuffer, size_t outSize, ISzAlloc *allocMain)
|
||||
|
||||
#ifndef _7Z_NO_METHOD_LZMA2
|
||||
|
||||
static SRes SzDecodeLzma2(const Byte *props, unsigned propsSize, UInt64 inSize, ILookInStream *inStream,
|
||||
Byte *outBuffer, SizeT outSize, ISzAllocPtr allocMain)
|
||||
{
|
||||
SRes result;
|
||||
CLzma2Dec state;
|
||||
SRes res = SZ_OK;
|
||||
CLzma2Dec state;
|
||||
SRes res = SZ_OK;
|
||||
|
||||
Lzma2Dec_Construct(&state);
|
||||
if (coder->Props.size != 1)
|
||||
return SZ_ERROR_DATA;
|
||||
Lzma2Dec_Construct(&state);
|
||||
if (propsSize != 1)
|
||||
return SZ_ERROR_DATA;
|
||||
RINOK(Lzma2Dec_AllocateProbs(&state, props[0], allocMain));
|
||||
state.decoder.dic = outBuffer;
|
||||
state.decoder.dicBufSize = outSize;
|
||||
Lzma2Dec_Init(&state);
|
||||
|
||||
result = Lzma2Dec_AllocateProbs(&state, coder->Props.data[0], allocMain);
|
||||
if (result != 0)
|
||||
return result;
|
||||
for (;;)
|
||||
{
|
||||
const void *inBuf = NULL;
|
||||
size_t lookahead = (1 << 18);
|
||||
if (lookahead > inSize)
|
||||
lookahead = (size_t)inSize;
|
||||
res = ILookInStream_Look(inStream, &inBuf, &lookahead);
|
||||
if (res != SZ_OK)
|
||||
break;
|
||||
|
||||
state.decoder.dic = outBuffer;
|
||||
state.decoder.dicBufSize = outSize;
|
||||
Lzma2Dec_Init(&state);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
uint8_t *inBuf = NULL;
|
||||
size_t lookahead = (1 << 18);
|
||||
if (lookahead > inSize)
|
||||
lookahead = (size_t)inSize;
|
||||
res = inStream->Look(
|
||||
(void *)inStream, (const void **)&inBuf, &lookahead);
|
||||
{
|
||||
SizeT inProcessed = (SizeT)lookahead, dicPos = state.decoder.dicPos;
|
||||
ELzmaStatus status;
|
||||
res = Lzma2Dec_DecodeToDic(&state, outSize, (const Byte *)inBuf, &inProcessed, LZMA_FINISH_END, &status);
|
||||
lookahead -= inProcessed;
|
||||
inSize -= inProcessed;
|
||||
if (res != SZ_OK)
|
||||
break;
|
||||
break;
|
||||
|
||||
if (status == LZMA_STATUS_FINISHED_WITH_MARK)
|
||||
{
|
||||
ELzmaStatus status;
|
||||
size_t inProcessed = (size_t)lookahead, dicPos = state.decoder.dicPos;
|
||||
res = Lzma2Dec_DecodeToDic(
|
||||
&state, outSize, inBuf, &inProcessed,
|
||||
LZMA_FINISH_END, &status);
|
||||
lookahead -= inProcessed;
|
||||
inSize -= inProcessed;
|
||||
if (res != SZ_OK)
|
||||
break;
|
||||
if (
|
||||
state.decoder.dicPos == state.decoder.dicBufSize ||
|
||||
(inProcessed == 0 && dicPos == state.decoder.dicPos))
|
||||
{
|
||||
if (state.decoder.dicBufSize != outSize || lookahead != 0 ||
|
||||
(status != LZMA_STATUS_FINISHED_WITH_MARK))
|
||||
res = SZ_ERROR_DATA;
|
||||
break;
|
||||
}
|
||||
res = inStream->Skip((void *)inStream, inProcessed);
|
||||
if (res != SZ_OK)
|
||||
break;
|
||||
if (outSize != state.decoder.dicPos || inSize != 0)
|
||||
res = SZ_ERROR_DATA;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Lzma2Dec_FreeProbs(&state, allocMain);
|
||||
return res;
|
||||
if (inProcessed == 0 && dicPos == state.decoder.dicPos)
|
||||
{
|
||||
res = SZ_ERROR_DATA;
|
||||
break;
|
||||
}
|
||||
|
||||
res = ILookInStream_Skip(inStream, inProcessed);
|
||||
if (res != SZ_OK)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Lzma2Dec_FreeProbs(&state, allocMain);
|
||||
return res;
|
||||
}
|
||||
|
||||
static SRes SzDecodeCopy(uint64_t inSize,
|
||||
ILookInStream *inStream, uint8_t *outBuffer)
|
||||
#endif
|
||||
|
||||
|
||||
static SRes SzDecodeCopy(UInt64 inSize, ILookInStream *inStream, Byte *outBuffer)
|
||||
{
|
||||
while (inSize > 0)
|
||||
{
|
||||
SRes result;
|
||||
void *inBuf = NULL;
|
||||
size_t curSize = (1 << 18);
|
||||
if (curSize > inSize)
|
||||
curSize = (size_t)inSize;
|
||||
result = inStream->Look(
|
||||
(void *)inStream, (const void **)&inBuf, &curSize);
|
||||
|
||||
if (result != 0)
|
||||
return result;
|
||||
if (curSize == 0)
|
||||
return SZ_ERROR_INPUT_EOF;
|
||||
|
||||
memcpy(outBuffer, inBuf, curSize);
|
||||
outBuffer += curSize;
|
||||
inSize -= curSize;
|
||||
result = inStream->Skip((void *)inStream, curSize);
|
||||
|
||||
if (result != 0)
|
||||
return result;
|
||||
}
|
||||
return SZ_OK;
|
||||
while (inSize > 0)
|
||||
{
|
||||
const void *inBuf;
|
||||
size_t curSize = (1 << 18);
|
||||
if (curSize > inSize)
|
||||
curSize = (size_t)inSize;
|
||||
RINOK(ILookInStream_Look(inStream, &inBuf, &curSize));
|
||||
if (curSize == 0)
|
||||
return SZ_ERROR_INPUT_EOF;
|
||||
memcpy(outBuffer, inBuf, curSize);
|
||||
outBuffer += curSize;
|
||||
inSize -= curSize;
|
||||
RINOK(ILookInStream_Skip(inStream, curSize));
|
||||
}
|
||||
return SZ_OK;
|
||||
}
|
||||
|
||||
static bool is_main_method(uint32_t m)
|
||||
static BoolInt IS_MAIN_METHOD(UInt32 m)
|
||||
{
|
||||
switch(m)
|
||||
{
|
||||
case k_Copy:
|
||||
case k_LZMA:
|
||||
case k_LZMA2:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
switch (m)
|
||||
{
|
||||
case k_Copy:
|
||||
case k_LZMA:
|
||||
#ifndef _7Z_NO_METHOD_LZMA2
|
||||
case k_LZMA2:
|
||||
#endif
|
||||
#ifdef _7ZIP_PPMD_SUPPPORT
|
||||
case k_PPMD:
|
||||
#endif
|
||||
return True;
|
||||
}
|
||||
return False;
|
||||
}
|
||||
|
||||
static bool is_supported_coder(const CSzCoderInfo *c)
|
||||
static BoolInt IS_SUPPORTED_CODER(const CSzCoderInfo *c)
|
||||
{
|
||||
return
|
||||
c->NumInStreams == 1 &&
|
||||
c->NumOutStreams == 1 &&
|
||||
c->MethodID <= (uint32_t)0xFFFFFFFF &&
|
||||
is_main_method((uint32_t)c->MethodID);
|
||||
c->NumStreams == 1
|
||||
/* && c->MethodID <= (UInt32)0xFFFFFFFF */
|
||||
&& IS_MAIN_METHOD((UInt32)c->MethodID);
|
||||
}
|
||||
|
||||
#define IS_BCJ2(c) ((c)->MethodID == k_BCJ2 && (c)->NumInStreams == 4 && (c)->NumOutStreams == 1)
|
||||
#define IS_BCJ2(c) ((c)->MethodID == k_BCJ2 && (c)->NumStreams == 4)
|
||||
|
||||
static SRes check_supported_folder(const CSzFolder *f)
|
||||
static SRes CheckSupportedFolder(const CSzFolder *f)
|
||||
{
|
||||
if (f->NumCoders < 1 || f->NumCoders > 4)
|
||||
return SZ_ERROR_UNSUPPORTED;
|
||||
|
||||
if (!is_supported_coder(&f->Coders[0]))
|
||||
if (!IS_SUPPORTED_CODER(&f->Coders[0]))
|
||||
return SZ_ERROR_UNSUPPORTED;
|
||||
|
||||
switch (f->NumCoders)
|
||||
if (f->NumCoders == 1)
|
||||
{
|
||||
case 1:
|
||||
if ( f->NumPackStreams != 1
|
||||
|| f->PackStreams[0] != 0
|
||||
|| f->NumBindPairs != 0)
|
||||
return SZ_ERROR_UNSUPPORTED;
|
||||
return SZ_OK;
|
||||
case 2:
|
||||
{
|
||||
CSzCoderInfo *c = &f->Coders[1];
|
||||
|
||||
if (c->MethodID > (uint32_t)0xFFFFFFFF ||
|
||||
c->NumInStreams != 1 ||
|
||||
c->NumOutStreams != 1 ||
|
||||
f->NumPackStreams != 1 ||
|
||||
f->PackStreams[0] != 0 ||
|
||||
f->NumBindPairs != 1 ||
|
||||
f->BindPairs[0].InIndex != 1 ||
|
||||
f->BindPairs[0].OutIndex != 0)
|
||||
return SZ_ERROR_UNSUPPORTED;
|
||||
|
||||
switch ((uint32_t)c->MethodID)
|
||||
{
|
||||
case k_BCJ:
|
||||
case k_ARM:
|
||||
break;
|
||||
default:
|
||||
return SZ_ERROR_UNSUPPORTED;
|
||||
}
|
||||
}
|
||||
return SZ_OK;
|
||||
case 4:
|
||||
if (!is_supported_coder(&f->Coders[1]) ||
|
||||
!is_supported_coder(&f->Coders[2]) ||
|
||||
!IS_BCJ2(&f->Coders[3]))
|
||||
return SZ_ERROR_UNSUPPORTED;
|
||||
|
||||
if (f->NumPackStreams != 4 ||
|
||||
f->PackStreams[0] != 2 ||
|
||||
f->PackStreams[1] != 6 ||
|
||||
f->PackStreams[2] != 1 ||
|
||||
f->PackStreams[3] != 0 ||
|
||||
f->NumBindPairs != 3 ||
|
||||
f->BindPairs[0].InIndex != 5 ||
|
||||
f->BindPairs[0].OutIndex != 0 ||
|
||||
f->BindPairs[1].InIndex != 4 ||
|
||||
f->BindPairs[1].OutIndex != 1 ||
|
||||
f->BindPairs[2].InIndex != 3 ||
|
||||
f->BindPairs[2].OutIndex != 2)
|
||||
return SZ_ERROR_UNSUPPORTED;
|
||||
return SZ_OK;
|
||||
if (f->NumPackStreams != 1 || f->PackStreams[0] != 0 || f->NumBonds != 0)
|
||||
return SZ_ERROR_UNSUPPORTED;
|
||||
return SZ_OK;
|
||||
}
|
||||
|
||||
|
||||
#ifndef _7Z_NO_METHODS_FILTERS
|
||||
|
||||
if (f->NumCoders == 2)
|
||||
{
|
||||
const CSzCoderInfo *c = &f->Coders[1];
|
||||
if (
|
||||
/* c->MethodID > (UInt32)0xFFFFFFFF || */
|
||||
c->NumStreams != 1
|
||||
|| f->NumPackStreams != 1
|
||||
|| f->PackStreams[0] != 0
|
||||
|| f->NumBonds != 1
|
||||
|| f->Bonds[0].InIndex != 1
|
||||
|| f->Bonds[0].OutIndex != 0)
|
||||
return SZ_ERROR_UNSUPPORTED;
|
||||
switch ((UInt32)c->MethodID)
|
||||
{
|
||||
case k_Delta:
|
||||
case k_BCJ:
|
||||
case k_PPC:
|
||||
case k_IA64:
|
||||
case k_SPARC:
|
||||
case k_ARM:
|
||||
case k_ARMT:
|
||||
break;
|
||||
default:
|
||||
return SZ_ERROR_UNSUPPORTED;
|
||||
}
|
||||
return SZ_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
if (f->NumCoders == 4)
|
||||
{
|
||||
if (!IS_SUPPORTED_CODER(&f->Coders[1])
|
||||
|| !IS_SUPPORTED_CODER(&f->Coders[2])
|
||||
|| !IS_BCJ2(&f->Coders[3]))
|
||||
return SZ_ERROR_UNSUPPORTED;
|
||||
if (f->NumPackStreams != 4
|
||||
|| f->PackStreams[0] != 2
|
||||
|| f->PackStreams[1] != 6
|
||||
|| f->PackStreams[2] != 1
|
||||
|| f->PackStreams[3] != 0
|
||||
|| f->NumBonds != 3
|
||||
|| f->Bonds[0].InIndex != 5 || f->Bonds[0].OutIndex != 0
|
||||
|| f->Bonds[1].InIndex != 4 || f->Bonds[1].OutIndex != 1
|
||||
|| f->Bonds[2].InIndex != 3 || f->Bonds[2].OutIndex != 2)
|
||||
return SZ_ERROR_UNSUPPORTED;
|
||||
return SZ_OK;
|
||||
}
|
||||
|
||||
return SZ_ERROR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static uint64_t get_sum(const uint64_t *values, uint32_t idx)
|
||||
{
|
||||
uint64_t sum = 0;
|
||||
uint32_t i;
|
||||
for (i = 0; i < idx; i++)
|
||||
sum += values[i];
|
||||
return sum;
|
||||
}
|
||||
#define CASE_BRA_CONV(isa) case k_ ## isa: isa ## _Convert(outBuffer, outSize, 0, 0); break;
|
||||
|
||||
static SRes SzFolder_Decode2(const CSzFolder *folder,
|
||||
const uint64_t *packSizes,
|
||||
ILookInStream *inStream, uint64_t startPos,
|
||||
uint8_t *outBuffer, size_t outSize, ISzAlloc *allocMain,
|
||||
uint8_t *tempBuf[])
|
||||
const Byte *propsData,
|
||||
const UInt64 *unpackSizes,
|
||||
const UInt64 *packPositions,
|
||||
ILookInStream *inStream, UInt64 startPos,
|
||||
Byte *outBuffer, SizeT outSize, ISzAllocPtr allocMain,
|
||||
Byte *tempBuf[])
|
||||
{
|
||||
uint32_t ci;
|
||||
size_t tempSizes[3] = { 0, 0, 0};
|
||||
size_t tempSize3 = 0;
|
||||
uint8_t *tempBuf3 = 0;
|
||||
SRes result = check_supported_folder(folder);
|
||||
UInt32 ci;
|
||||
SizeT tempSizes[3] = { 0, 0, 0};
|
||||
SizeT tempSize3 = 0;
|
||||
Byte *tempBuf3 = 0;
|
||||
|
||||
if (result != 0)
|
||||
return result;
|
||||
RINOK(CheckSupportedFolder(folder));
|
||||
|
||||
for (ci = 0; ci < folder->NumCoders; ci++)
|
||||
{
|
||||
CSzCoderInfo *coder = &folder->Coders[ci];
|
||||
for (ci = 0; ci < folder->NumCoders; ci++)
|
||||
{
|
||||
const CSzCoderInfo *coder = &folder->Coders[ci];
|
||||
|
||||
if (is_main_method((uint32_t)coder->MethodID))
|
||||
if (IS_MAIN_METHOD((UInt32)coder->MethodID))
|
||||
{
|
||||
UInt32 si = 0;
|
||||
UInt64 offset;
|
||||
UInt64 inSize;
|
||||
Byte *outBufCur = outBuffer;
|
||||
SizeT outSizeCur = outSize;
|
||||
if (folder->NumCoders == 4)
|
||||
{
|
||||
SRes result;
|
||||
uint64_t offset = 0;
|
||||
uint64_t inSize = 0;
|
||||
uint32_t si = 0;
|
||||
uint8_t *outBufCur = outBuffer;
|
||||
size_t outSizeCur = outSize;
|
||||
|
||||
if (folder->NumCoders == 4)
|
||||
{
|
||||
uint32_t indices[] = { 3, 2, 0 };
|
||||
uint64_t unpackSize = folder->UnpackSizes[ci];
|
||||
si = indices[ci];
|
||||
|
||||
if (ci < 2)
|
||||
{
|
||||
uint8_t *temp;
|
||||
outSizeCur = (size_t)unpackSize;
|
||||
if (outSizeCur != unpackSize)
|
||||
return SZ_ERROR_MEM;
|
||||
temp = (uint8_t *)IAlloc_Alloc(allocMain, outSizeCur);
|
||||
if (temp == 0 && outSizeCur != 0)
|
||||
return SZ_ERROR_MEM;
|
||||
outBufCur = tempBuf[1 - ci] = temp;
|
||||
tempSizes[1 - ci] = outSizeCur;
|
||||
}
|
||||
else if (ci == 2)
|
||||
{
|
||||
if (unpackSize > outSize) /* check it */
|
||||
return SZ_ERROR_PARAM;
|
||||
tempBuf3 = outBufCur = outBuffer
|
||||
+ (outSize - (size_t)unpackSize);
|
||||
tempSize3 = outSizeCur = (size_t)unpackSize;
|
||||
}
|
||||
else
|
||||
return SZ_ERROR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
offset = get_sum(packSizes, si);
|
||||
inSize = packSizes[si];
|
||||
result = LookInStream_SeekTo(inStream, startPos + offset);
|
||||
|
||||
if (result != 0)
|
||||
return result;
|
||||
|
||||
switch (coder->MethodID)
|
||||
{
|
||||
case k_Copy:
|
||||
if (inSize != outSizeCur) /* check it */
|
||||
return SZ_ERROR_DATA;
|
||||
result = SzDecodeCopy(inSize, inStream, outBufCur);
|
||||
if (result != 0)
|
||||
return result;
|
||||
break;
|
||||
case k_LZMA:
|
||||
result = SzDecodeLzma(
|
||||
coder, inSize, inStream,
|
||||
outBufCur, outSizeCur, allocMain);
|
||||
if (result != 0)
|
||||
return result;
|
||||
break;
|
||||
case k_LZMA2:
|
||||
result = SzDecodeLzma2(
|
||||
coder, inSize, inStream,
|
||||
outBufCur, outSizeCur, allocMain);
|
||||
if (result != 0)
|
||||
return result;
|
||||
break;
|
||||
default:
|
||||
return SZ_ERROR_UNSUPPORTED;
|
||||
}
|
||||
UInt32 indices[] = { 3, 2, 0 };
|
||||
UInt64 unpackSize = unpackSizes[ci];
|
||||
si = indices[ci];
|
||||
if (ci < 2)
|
||||
{
|
||||
Byte *temp;
|
||||
outSizeCur = (SizeT)unpackSize;
|
||||
if (outSizeCur != unpackSize)
|
||||
return SZ_ERROR_MEM;
|
||||
temp = (Byte *)ISzAlloc_Alloc(allocMain, outSizeCur);
|
||||
if (!temp && outSizeCur != 0)
|
||||
return SZ_ERROR_MEM;
|
||||
outBufCur = tempBuf[1 - ci] = temp;
|
||||
tempSizes[1 - ci] = outSizeCur;
|
||||
}
|
||||
else if (ci == 2)
|
||||
{
|
||||
if (unpackSize > outSize) /* check it */
|
||||
return SZ_ERROR_PARAM;
|
||||
tempBuf3 = outBufCur = outBuffer + (outSize - (size_t)unpackSize);
|
||||
tempSize3 = outSizeCur = (SizeT)unpackSize;
|
||||
}
|
||||
else
|
||||
return SZ_ERROR_UNSUPPORTED;
|
||||
}
|
||||
else if (coder->MethodID == k_BCJ2)
|
||||
offset = packPositions[si];
|
||||
inSize = packPositions[(size_t)si + 1] - offset;
|
||||
RINOK(LookInStream_SeekTo(inStream, startPos + offset));
|
||||
|
||||
if (coder->MethodID == k_Copy)
|
||||
{
|
||||
SRes res;
|
||||
SRes result;
|
||||
uint64_t offset = get_sum(packSizes, 1);
|
||||
uint64_t s3Size = packSizes[1];
|
||||
if (inSize != outSizeCur) /* check it */
|
||||
return SZ_ERROR_DATA;
|
||||
RINOK(SzDecodeCopy(inSize, inStream, outBufCur));
|
||||
}
|
||||
else if (coder->MethodID == k_LZMA)
|
||||
{
|
||||
RINOK(SzDecodeLzma(propsData + coder->PropsOffset, coder->PropsSize, inSize, inStream, outBufCur, outSizeCur, allocMain));
|
||||
}
|
||||
#ifndef _7Z_NO_METHOD_LZMA2
|
||||
else if (coder->MethodID == k_LZMA2)
|
||||
{
|
||||
RINOK(SzDecodeLzma2(propsData + coder->PropsOffset, coder->PropsSize, inSize, inStream, outBufCur, outSizeCur, allocMain));
|
||||
}
|
||||
#endif
|
||||
#ifdef _7ZIP_PPMD_SUPPPORT
|
||||
else if (coder->MethodID == k_PPMD)
|
||||
{
|
||||
RINOK(SzDecodePpmd(propsData + coder->PropsOffset, coder->PropsSize, inSize, inStream, outBufCur, outSizeCur, allocMain));
|
||||
}
|
||||
#endif
|
||||
else
|
||||
return SZ_ERROR_UNSUPPORTED;
|
||||
}
|
||||
else if (coder->MethodID == k_BCJ2)
|
||||
{
|
||||
UInt64 offset = packPositions[1];
|
||||
UInt64 s3Size = packPositions[2] - offset;
|
||||
|
||||
if (ci != 3)
|
||||
return SZ_ERROR_UNSUPPORTED;
|
||||
|
||||
tempSizes[2] = (SizeT)s3Size;
|
||||
if (tempSizes[2] != s3Size)
|
||||
return SZ_ERROR_MEM;
|
||||
tempBuf[2] = (Byte *)ISzAlloc_Alloc(allocMain, tempSizes[2]);
|
||||
if (!tempBuf[2] && tempSizes[2] != 0)
|
||||
return SZ_ERROR_MEM;
|
||||
|
||||
RINOK(LookInStream_SeekTo(inStream, startPos + offset));
|
||||
RINOK(SzDecodeCopy(s3Size, inStream, tempBuf[2]));
|
||||
|
||||
if (ci != 3)
|
||||
return SZ_ERROR_UNSUPPORTED;
|
||||
if ((tempSizes[0] & 3) != 0 ||
|
||||
(tempSizes[1] & 3) != 0 ||
|
||||
tempSize3 + tempSizes[0] + tempSizes[1] != outSize)
|
||||
return SZ_ERROR_DATA;
|
||||
|
||||
result = LookInStream_SeekTo(inStream, startPos + offset);
|
||||
{
|
||||
CBcj2Dec p;
|
||||
|
||||
p.bufs[0] = tempBuf3; p.lims[0] = tempBuf3 + tempSize3;
|
||||
p.bufs[1] = tempBuf[0]; p.lims[1] = tempBuf[0] + tempSizes[0];
|
||||
p.bufs[2] = tempBuf[1]; p.lims[2] = tempBuf[1] + tempSizes[1];
|
||||
p.bufs[3] = tempBuf[2]; p.lims[3] = tempBuf[2] + tempSizes[2];
|
||||
|
||||
p.dest = outBuffer;
|
||||
p.destLim = outBuffer + outSize;
|
||||
|
||||
Bcj2Dec_Init(&p);
|
||||
RINOK(Bcj2Dec_Decode(&p));
|
||||
|
||||
if (result != 0)
|
||||
return result;
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < 4; i++)
|
||||
if (p.bufs[i] != p.lims[i])
|
||||
return SZ_ERROR_DATA;
|
||||
|
||||
if (!Bcj2Dec_IsFinished(&p))
|
||||
return SZ_ERROR_DATA;
|
||||
|
||||
tempSizes[2] = (size_t)s3Size;
|
||||
if (tempSizes[2] != s3Size)
|
||||
return SZ_ERROR_MEM;
|
||||
|
||||
tempBuf[2] = (uint8_t *)IAlloc_Alloc(allocMain, tempSizes[2]);
|
||||
if (tempBuf[2] == 0 && tempSizes[2] != 0)
|
||||
return SZ_ERROR_MEM;
|
||||
|
||||
res = SzDecodeCopy(s3Size, inStream, tempBuf[2]);
|
||||
if (res != 0)
|
||||
return res;
|
||||
|
||||
res = Bcj2_Decode(
|
||||
tempBuf3, tempSize3,
|
||||
tempBuf[0], tempSizes[0],
|
||||
tempBuf[1], tempSizes[1],
|
||||
tempBuf[2], tempSizes[2],
|
||||
outBuffer, outSize);
|
||||
|
||||
if (res != 0)
|
||||
return res;
|
||||
if (p.dest != p.destLim
|
||||
|| p.state != BCJ2_STREAM_MAIN)
|
||||
return SZ_ERROR_DATA;
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifndef _7Z_NO_METHODS_FILTERS
|
||||
else if (ci == 1)
|
||||
{
|
||||
if (coder->MethodID == k_Delta)
|
||||
{
|
||||
if (coder->PropsSize != 1)
|
||||
return SZ_ERROR_UNSUPPORTED;
|
||||
{
|
||||
Byte state[DELTA_STATE_SIZE];
|
||||
Delta_Init(state);
|
||||
Delta_Decode(state, (unsigned)(propsData[coder->PropsOffset]) + 1, outBuffer, outSize);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ci != 1)
|
||||
if (coder->PropsSize != 0)
|
||||
return SZ_ERROR_UNSUPPORTED;
|
||||
switch (coder->MethodID)
|
||||
{
|
||||
case k_BCJ:
|
||||
{
|
||||
UInt32 state;
|
||||
x86_Convert_Init(state);
|
||||
x86_Convert(outBuffer, outSize, 0, &state, 0);
|
||||
break;
|
||||
}
|
||||
CASE_BRA_CONV(PPC)
|
||||
CASE_BRA_CONV(IA64)
|
||||
CASE_BRA_CONV(SPARC)
|
||||
CASE_BRA_CONV(ARM)
|
||||
CASE_BRA_CONV(ARMT)
|
||||
default:
|
||||
return SZ_ERROR_UNSUPPORTED;
|
||||
switch(coder->MethodID)
|
||||
{
|
||||
case k_BCJ:
|
||||
{
|
||||
uint32_t state;
|
||||
x86_Convert_Init(state);
|
||||
x86_Convert(outBuffer, outSize, 0, &state, 0);
|
||||
break;
|
||||
}
|
||||
case k_ARM:
|
||||
ARM_Convert(outBuffer, outSize, 0, 0);
|
||||
break;
|
||||
default:
|
||||
return SZ_ERROR_UNSUPPORTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return SZ_OK;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
return SZ_ERROR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
return SZ_OK;
|
||||
}
|
||||
|
||||
SRes SzFolder_Decode(const CSzFolder *folder, const uint64_t *packSizes,
|
||||
ILookInStream *inStream, uint64_t startPos,
|
||||
uint8_t *outBuffer, size_t outSize, ISzAlloc *allocMain)
|
||||
|
||||
SRes SzAr_DecodeFolder(const CSzAr *p, UInt32 folderIndex,
|
||||
ILookInStream *inStream, UInt64 startPos,
|
||||
Byte *outBuffer, size_t outSize,
|
||||
ISzAllocPtr allocMain)
|
||||
{
|
||||
int i;
|
||||
uint8_t *tempBuf[3] = { 0, 0, 0};
|
||||
SRes res = SzFolder_Decode2(folder,
|
||||
packSizes, inStream, startPos,
|
||||
outBuffer, (size_t)outSize, allocMain, tempBuf);
|
||||
SRes res;
|
||||
CSzFolder folder;
|
||||
CSzData sd;
|
||||
|
||||
const Byte *data = p->CodersData + p->FoCodersOffsets[folderIndex];
|
||||
sd.Data = data;
|
||||
sd.Size = p->FoCodersOffsets[(size_t)folderIndex + 1] - p->FoCodersOffsets[folderIndex];
|
||||
|
||||
res = SzGetNextFolderItem(&folder, &sd);
|
||||
|
||||
if (res != SZ_OK)
|
||||
return res;
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
IAlloc_Free(allocMain, tempBuf[i]);
|
||||
return res;
|
||||
if (sd.Size != 0
|
||||
|| folder.UnpackStream != p->FoToMainUnpackSizeIndex[folderIndex]
|
||||
|| outSize != SzAr_GetFolderUnpackSize(p, folderIndex))
|
||||
return SZ_ERROR_FAIL;
|
||||
{
|
||||
unsigned i;
|
||||
Byte *tempBuf[3] = { 0, 0, 0};
|
||||
|
||||
res = SzFolder_Decode2(&folder, data,
|
||||
&p->CoderUnpackSizes[p->FoToCoderUnpackSizes[folderIndex]],
|
||||
p->PackPositions + p->FoStartPackStreamIndex[folderIndex],
|
||||
inStream, startPos,
|
||||
outBuffer, (SizeT)outSize, allocMain, tempBuf);
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
ISzAlloc_Free(allocMain, tempBuf[i]);
|
||||
|
||||
if (res == SZ_OK)
|
||||
if (SzBitWithVals_Check(&p->FolderCRCs, folderIndex))
|
||||
if (CrcCalc(outBuffer, outSize) != p->FolderCRCs.Vals[folderIndex])
|
||||
res = SZ_ERROR_CRC;
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
Vendored
+216
-90
@@ -1,160 +1,286 @@
|
||||
/* 7zFile.c -- File IO
|
||||
2009-11-24 : Igor Pavlov : Public domain */
|
||||
2017-04-03 : Igor Pavlov : Public domain */
|
||||
|
||||
#include "Precomp.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#define SKIP_STDIO_REDEFINES
|
||||
#include <streams/file_stream_transforms.h>
|
||||
#include "7zFile.h"
|
||||
|
||||
#ifndef USE_WINDOWS_FILE
|
||||
|
||||
#ifndef UNDER_CE
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
ReadFile and WriteFile functions in Windows have BUG:
|
||||
If you Read or Write 64MB or more (probably min_failure_size = 64MB - 32KB + 1)
|
||||
from/to Network file, it returns ERROR_NO_SYSTEM_RESOURCES
|
||||
(Insufficient system resources exist to complete the requested service).
|
||||
Probably in some version of Windows there are problems with other sizes:
|
||||
for 32 MB (maybe also for 16 MB).
|
||||
And message can be "Network connection was lost"
|
||||
*/
|
||||
|
||||
#define kChunkSizeMax (1 << 22)
|
||||
|
||||
#endif
|
||||
|
||||
void File_Construct(CSzFile *p)
|
||||
{
|
||||
p->file = NULL;
|
||||
#ifdef USE_WINDOWS_FILE
|
||||
p->handle = INVALID_HANDLE_VALUE;
|
||||
#else
|
||||
p->file = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !defined(UNDER_CE) || !defined(USE_WINDOWS_FILE)
|
||||
static WRes File_Open(CSzFile *p, const char *name, int writeMode)
|
||||
{
|
||||
p->file = rfopen(name, writeMode ? "wb+" : "rb");
|
||||
if (!p->file)
|
||||
{
|
||||
#ifdef UNDER_CE
|
||||
return 2; /* ENOENT */
|
||||
#else
|
||||
return errno;
|
||||
#ifdef USE_WINDOWS_FILE
|
||||
p->handle = CreateFileA(name,
|
||||
writeMode ? GENERIC_WRITE : GENERIC_READ,
|
||||
FILE_SHARE_READ, NULL,
|
||||
writeMode ? CREATE_ALWAYS : OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
return (p->handle != INVALID_HANDLE_VALUE) ? 0 : GetLastError();
|
||||
#else
|
||||
p->file = fopen(name, writeMode ? "wb+" : "rb");
|
||||
return (p->file != 0) ? 0 :
|
||||
#ifdef UNDER_CE
|
||||
2; /* ENOENT */
|
||||
#else
|
||||
errno;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
WRes InFile_Open(CSzFile *p, const char *name) { return File_Open(p, name, 0); }
|
||||
WRes OutFile_Open(CSzFile *p, const char *name) { return File_Open(p, name, 1); }
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
WRes InFile_Open(CSzFile *p, const char *name)
|
||||
#ifdef USE_WINDOWS_FILE
|
||||
static WRes File_OpenW(CSzFile *p, const WCHAR *name, int writeMode)
|
||||
{
|
||||
return File_Open(p, name, 0);
|
||||
}
|
||||
|
||||
WRes OutFile_Open(CSzFile *p, const char *name)
|
||||
{
|
||||
return File_Open(p, name, 1);
|
||||
p->handle = CreateFileW(name,
|
||||
writeMode ? GENERIC_WRITE : GENERIC_READ,
|
||||
FILE_SHARE_READ, NULL,
|
||||
writeMode ? CREATE_ALWAYS : OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
return (p->handle != INVALID_HANDLE_VALUE) ? 0 : GetLastError();
|
||||
}
|
||||
WRes InFile_OpenW(CSzFile *p, const WCHAR *name) { return File_OpenW(p, name, 0); }
|
||||
WRes OutFile_OpenW(CSzFile *p, const WCHAR *name) { return File_OpenW(p, name, 1); }
|
||||
#endif
|
||||
|
||||
WRes File_Close(CSzFile *p)
|
||||
{
|
||||
if (p->file)
|
||||
{
|
||||
int res = rfclose((RFILE*)p->file);
|
||||
if (res != 0)
|
||||
return res;
|
||||
p->file = NULL;
|
||||
}
|
||||
return 0;
|
||||
#ifdef USE_WINDOWS_FILE
|
||||
if (p->handle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
if (!CloseHandle(p->handle))
|
||||
return GetLastError();
|
||||
p->handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
#else
|
||||
if (p->file != NULL)
|
||||
{
|
||||
int res = fclose(p->file);
|
||||
if (res != 0)
|
||||
return res;
|
||||
p->file = NULL;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
WRes File_Read(CSzFile *p, void *data, size_t *size)
|
||||
{
|
||||
int64_t originalSize = *size;
|
||||
if (originalSize == 0)
|
||||
return 0;
|
||||
size_t originalSize = *size;
|
||||
if (originalSize == 0)
|
||||
return 0;
|
||||
|
||||
*size = rfread(data, 1, originalSize, (RFILE*)p->file);
|
||||
if (*size == originalSize)
|
||||
return 0;
|
||||
return rferror((RFILE*)p->file);
|
||||
#ifdef USE_WINDOWS_FILE
|
||||
|
||||
*size = 0;
|
||||
do
|
||||
{
|
||||
DWORD curSize = (originalSize > kChunkSizeMax) ? kChunkSizeMax : (DWORD)originalSize;
|
||||
DWORD processed = 0;
|
||||
BOOL res = ReadFile(p->handle, data, curSize, &processed, NULL);
|
||||
data = (void *)((Byte *)data + processed);
|
||||
originalSize -= processed;
|
||||
*size += processed;
|
||||
if (!res)
|
||||
return GetLastError();
|
||||
if (processed == 0)
|
||||
break;
|
||||
}
|
||||
while (originalSize > 0);
|
||||
return 0;
|
||||
|
||||
#else
|
||||
|
||||
*size = fread(data, 1, originalSize, p->file);
|
||||
if (*size == originalSize)
|
||||
return 0;
|
||||
return ferror(p->file);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
WRes File_Write(CSzFile *p, const void *data, size_t *size)
|
||||
{
|
||||
int64_t originalSize = *size;
|
||||
if (originalSize == 0)
|
||||
return 0;
|
||||
size_t originalSize = *size;
|
||||
if (originalSize == 0)
|
||||
return 0;
|
||||
|
||||
#ifdef USE_WINDOWS_FILE
|
||||
|
||||
*size = rfwrite(data, 1, originalSize, (RFILE*)p->file);
|
||||
if (*size == originalSize)
|
||||
return 0;
|
||||
return rferror((RFILE*)p->file);
|
||||
*size = 0;
|
||||
do
|
||||
{
|
||||
DWORD curSize = (originalSize > kChunkSizeMax) ? kChunkSizeMax : (DWORD)originalSize;
|
||||
DWORD processed = 0;
|
||||
BOOL res = WriteFile(p->handle, data, curSize, &processed, NULL);
|
||||
data = (void *)((Byte *)data + processed);
|
||||
originalSize -= processed;
|
||||
*size += processed;
|
||||
if (!res)
|
||||
return GetLastError();
|
||||
if (processed == 0)
|
||||
break;
|
||||
}
|
||||
while (originalSize > 0);
|
||||
return 0;
|
||||
|
||||
#else
|
||||
|
||||
*size = fwrite(data, 1, originalSize, p->file);
|
||||
if (*size == originalSize)
|
||||
return 0;
|
||||
return ferror(p->file);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
WRes File_Seek(CSzFile *p, int64_t *pos, ESzSeek origin)
|
||||
WRes File_Seek(CSzFile *p, Int64 *pos, ESzSeek origin)
|
||||
{
|
||||
int whence;
|
||||
int64_t res;
|
||||
switch (origin)
|
||||
{
|
||||
case SZ_SEEK_SET:
|
||||
whence = SEEK_SET;
|
||||
break;
|
||||
case SZ_SEEK_CUR:
|
||||
whence = SEEK_CUR;
|
||||
break;
|
||||
case SZ_SEEK_END:
|
||||
whence = SEEK_END;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
res = rfseek((RFILE*)p->file, (int64_t)*pos, whence);
|
||||
*pos = rftell((RFILE*)p->file);
|
||||
return res;
|
||||
#ifdef USE_WINDOWS_FILE
|
||||
|
||||
LARGE_INTEGER value;
|
||||
DWORD moveMethod;
|
||||
value.LowPart = (DWORD)*pos;
|
||||
value.HighPart = (LONG)((UInt64)*pos >> 16 >> 16); /* for case when UInt64 is 32-bit only */
|
||||
switch (origin)
|
||||
{
|
||||
case SZ_SEEK_SET: moveMethod = FILE_BEGIN; break;
|
||||
case SZ_SEEK_CUR: moveMethod = FILE_CURRENT; break;
|
||||
case SZ_SEEK_END: moveMethod = FILE_END; break;
|
||||
default: return ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
value.LowPart = SetFilePointer(p->handle, value.LowPart, &value.HighPart, moveMethod);
|
||||
if (value.LowPart == 0xFFFFFFFF)
|
||||
{
|
||||
WRes res = GetLastError();
|
||||
if (res != NO_ERROR)
|
||||
return res;
|
||||
}
|
||||
*pos = ((Int64)value.HighPart << 32) | value.LowPart;
|
||||
return 0;
|
||||
|
||||
#else
|
||||
|
||||
int moveMethod;
|
||||
int res;
|
||||
switch (origin)
|
||||
{
|
||||
case SZ_SEEK_SET: moveMethod = SEEK_SET; break;
|
||||
case SZ_SEEK_CUR: moveMethod = SEEK_CUR; break;
|
||||
case SZ_SEEK_END: moveMethod = SEEK_END; break;
|
||||
default: return 1;
|
||||
}
|
||||
res = fseek(p->file, (long)*pos, moveMethod);
|
||||
*pos = ftell(p->file);
|
||||
return res;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
WRes File_GetLength(CSzFile *p, uint64_t *length)
|
||||
WRes File_GetLength(CSzFile *p, UInt64 *length)
|
||||
{
|
||||
int64_t pos = rftell((RFILE*)p->file);
|
||||
int64_t res = rfseek((RFILE*)p->file, 0, SEEK_END);
|
||||
*length = rftell((RFILE*)p->file);
|
||||
rfseek((RFILE*)p->file, pos, SEEK_SET);
|
||||
return res;
|
||||
#ifdef USE_WINDOWS_FILE
|
||||
|
||||
DWORD sizeHigh;
|
||||
DWORD sizeLow = GetFileSize(p->handle, &sizeHigh);
|
||||
if (sizeLow == 0xFFFFFFFF)
|
||||
{
|
||||
DWORD res = GetLastError();
|
||||
if (res != NO_ERROR)
|
||||
return res;
|
||||
}
|
||||
*length = (((UInt64)sizeHigh) << 32) + sizeLow;
|
||||
return 0;
|
||||
|
||||
#else
|
||||
|
||||
long pos = ftell(p->file);
|
||||
int res = fseek(p->file, 0, SEEK_END);
|
||||
*length = ftell(p->file);
|
||||
fseek(p->file, pos, SEEK_SET);
|
||||
return res;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* ---------- FileSeqInStream ---------- */
|
||||
|
||||
static SRes FileSeqInStream_Read(void *pp, void *buf, size_t *size)
|
||||
static SRes FileSeqInStream_Read(const ISeqInStream *pp, void *buf, size_t *size)
|
||||
{
|
||||
CFileSeqInStream *p = (CFileSeqInStream *)pp;
|
||||
return File_Read(&p->file, buf, size) == 0 ? SZ_OK : SZ_ERROR_READ;
|
||||
CFileSeqInStream *p = CONTAINER_FROM_VTBL(pp, CFileSeqInStream, vt);
|
||||
return File_Read(&p->file, buf, size) == 0 ? SZ_OK : SZ_ERROR_READ;
|
||||
}
|
||||
|
||||
void FileSeqInStream_CreateVTable(CFileSeqInStream *p)
|
||||
{
|
||||
p->s.Read = FileSeqInStream_Read;
|
||||
p->vt.Read = FileSeqInStream_Read;
|
||||
}
|
||||
|
||||
|
||||
/* ---------- FileInStream ---------- */
|
||||
|
||||
static SRes FileInStream_Read(void *pp, void *buf, size_t *size)
|
||||
static SRes FileInStream_Read(const ISeekInStream *pp, void *buf, size_t *size)
|
||||
{
|
||||
CFileInStream *p = (CFileInStream *)pp;
|
||||
return (File_Read(&p->file, buf, size) == 0) ? SZ_OK : SZ_ERROR_READ;
|
||||
CFileInStream *p = CONTAINER_FROM_VTBL(pp, CFileInStream, vt);
|
||||
return (File_Read(&p->file, buf, size) == 0) ? SZ_OK : SZ_ERROR_READ;
|
||||
}
|
||||
|
||||
static SRes FileInStream_Seek(void *pp, int64_t *pos, ESzSeek origin)
|
||||
static SRes FileInStream_Seek(const ISeekInStream *pp, Int64 *pos, ESzSeek origin)
|
||||
{
|
||||
CFileInStream *p = (CFileInStream *)pp;
|
||||
return File_Seek(&p->file, pos, origin);
|
||||
CFileInStream *p = CONTAINER_FROM_VTBL(pp, CFileInStream, vt);
|
||||
return File_Seek(&p->file, pos, origin);
|
||||
}
|
||||
|
||||
void FileInStream_CreateVTable(CFileInStream *p)
|
||||
{
|
||||
p->s.Read = FileInStream_Read;
|
||||
p->s.Seek = FileInStream_Seek;
|
||||
p->vt.Read = FileInStream_Read;
|
||||
p->vt.Seek = FileInStream_Seek;
|
||||
}
|
||||
|
||||
|
||||
/* ---------- FileOutStream ---------- */
|
||||
|
||||
static size_t FileOutStream_Write(void *pp, const void *data, size_t size)
|
||||
static size_t FileOutStream_Write(const ISeqOutStream *pp, const void *data, size_t size)
|
||||
{
|
||||
CFileOutStream *p = (CFileOutStream *)pp;
|
||||
File_Write(&p->file, data, &size);
|
||||
return size;
|
||||
CFileOutStream *p = CONTAINER_FROM_VTBL(pp, CFileOutStream, vt);
|
||||
File_Write(&p->file, data, &size);
|
||||
return size;
|
||||
}
|
||||
|
||||
void FileOutStream_CreateVTable(CFileOutStream *p)
|
||||
{
|
||||
p->s.Write = FileOutStream_Write;
|
||||
p->vt.Write = FileOutStream_Write;
|
||||
}
|
||||
|
||||
#undef SKIP_STDIO_REDEFINES
|
||||
Vendored
+27
-13
@@ -1,27 +1,43 @@
|
||||
/* 7zFile.h -- File IO
|
||||
2009-11-24 : Igor Pavlov : Public domain */
|
||||
2017-04-03 : Igor Pavlov : Public domain */
|
||||
|
||||
#ifndef __7Z_FILE_H
|
||||
#define __7Z_FILE_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#define USE_WINDOWS_FILE
|
||||
#endif
|
||||
|
||||
#ifdef USE_WINDOWS_FILE
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#include "7zTypes.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
EXTERN_C_BEGIN
|
||||
|
||||
/* ---------- File ---------- */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
void *file;
|
||||
#ifdef USE_WINDOWS_FILE
|
||||
HANDLE handle;
|
||||
#else
|
||||
FILE *file;
|
||||
#endif
|
||||
} CSzFile;
|
||||
|
||||
void File_Construct(CSzFile *p);
|
||||
#if !defined(UNDER_CE) || !defined(USE_WINDOWS_FILE)
|
||||
WRes InFile_Open(CSzFile *p, const char *name);
|
||||
WRes OutFile_Open(CSzFile *p, const char *name);
|
||||
#endif
|
||||
#ifdef USE_WINDOWS_FILE
|
||||
WRes InFile_OpenW(CSzFile *p, const WCHAR *name);
|
||||
WRes OutFile_OpenW(CSzFile *p, const WCHAR *name);
|
||||
#endif
|
||||
WRes File_Close(CSzFile *p);
|
||||
|
||||
/* reads max(*size, remain file's size) bytes */
|
||||
@@ -30,15 +46,15 @@ WRes File_Read(CSzFile *p, void *data, size_t *size);
|
||||
/* writes *size bytes */
|
||||
WRes File_Write(CSzFile *p, const void *data, size_t *size);
|
||||
|
||||
WRes File_Seek(CSzFile *p, int64_t *pos, ESzSeek origin);
|
||||
WRes File_GetLength(CSzFile *p, uint64_t *length);
|
||||
WRes File_Seek(CSzFile *p, Int64 *pos, ESzSeek origin);
|
||||
WRes File_GetLength(CSzFile *p, UInt64 *length);
|
||||
|
||||
|
||||
/* ---------- FileInStream ---------- */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ISeqInStream s;
|
||||
ISeqInStream vt;
|
||||
CSzFile file;
|
||||
} CFileSeqInStream;
|
||||
|
||||
@@ -47,7 +63,7 @@ void FileSeqInStream_CreateVTable(CFileSeqInStream *p);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ISeekInStream s;
|
||||
ISeekInStream vt;
|
||||
CSzFile file;
|
||||
} CFileInStream;
|
||||
|
||||
@@ -56,14 +72,12 @@ void FileInStream_CreateVTable(CFileInStream *p);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ISeqOutStream s;
|
||||
ISeqOutStream vt;
|
||||
CSzFile file;
|
||||
} CFileOutStream;
|
||||
|
||||
void FileOutStream_CreateVTable(CFileOutStream *p);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
EXTERN_C_END
|
||||
|
||||
#endif
|
||||
Vendored
-1633
File diff suppressed because it is too large.
Load diff
Vendored
+116
-124
@@ -1,184 +1,176 @@
|
||||
/* 7zStream.c -- 7z Stream functions
|
||||
2010-03-11 : Igor Pavlov : Public domain */
|
||||
2017-04-03 : Igor Pavlov : Public domain */
|
||||
|
||||
#include "Precomp.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "7zTypes.h"
|
||||
|
||||
SRes SeqInStream_Readuint8_t(ISeqInStream *stream, uint8_t *buf)
|
||||
SRes SeqInStream_Read2(const ISeqInStream *stream, void *buf, size_t size, SRes errorType)
|
||||
{
|
||||
size_t processed = 1;
|
||||
SRes result = stream->Read(stream, buf, &processed);
|
||||
if (result != 0)
|
||||
return result;
|
||||
if (processed != 1)
|
||||
return SZ_ERROR_INPUT_EOF;
|
||||
return SZ_OK;
|
||||
while (size != 0)
|
||||
{
|
||||
size_t processed = size;
|
||||
RINOK(ISeqInStream_Read(stream, buf, &processed));
|
||||
if (processed == 0)
|
||||
return errorType;
|
||||
buf = (void *)((Byte *)buf + processed);
|
||||
size -= processed;
|
||||
}
|
||||
return SZ_OK;
|
||||
}
|
||||
|
||||
SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType)
|
||||
SRes SeqInStream_Read(const ISeqInStream *stream, void *buf, size_t size)
|
||||
{
|
||||
while (size != 0)
|
||||
{
|
||||
size_t processed = size;
|
||||
SRes result = stream->Read(stream, buf, &processed);
|
||||
if (result != 0)
|
||||
return result;
|
||||
if (processed == 0)
|
||||
return errorType;
|
||||
buf = (void *)((uint8_t *)buf + processed);
|
||||
size -= processed;
|
||||
}
|
||||
return SZ_OK;
|
||||
return SeqInStream_Read2(stream, buf, size, SZ_ERROR_INPUT_EOF);
|
||||
}
|
||||
|
||||
SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size)
|
||||
SRes SeqInStream_ReadByte(const ISeqInStream *stream, Byte *buf)
|
||||
{
|
||||
return SeqInStream_Read2(stream, buf, size, SZ_ERROR_INPUT_EOF);
|
||||
size_t processed = 1;
|
||||
RINOK(ISeqInStream_Read(stream, buf, &processed));
|
||||
return (processed == 1) ? SZ_OK : SZ_ERROR_INPUT_EOF;
|
||||
}
|
||||
|
||||
|
||||
SRes LookInStream_SeekTo(ILookInStream *stream, uint64_t offset)
|
||||
|
||||
SRes LookInStream_SeekTo(const ILookInStream *stream, UInt64 offset)
|
||||
{
|
||||
int64_t t = offset;
|
||||
return stream->Seek(stream, &t, SZ_SEEK_SET);
|
||||
Int64 t = offset;
|
||||
return ILookInStream_Seek(stream, &t, SZ_SEEK_SET);
|
||||
}
|
||||
|
||||
SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size)
|
||||
SRes LookInStream_LookRead(const ILookInStream *stream, void *buf, size_t *size)
|
||||
{
|
||||
SRes result;
|
||||
const void *lookBuf;
|
||||
if (*size == 0)
|
||||
return SZ_OK;
|
||||
result = stream->Look(stream, &lookBuf, size);
|
||||
if (result != 0)
|
||||
return result;
|
||||
memcpy(buf, lookBuf, *size);
|
||||
return stream->Skip(stream, *size);
|
||||
const void *lookBuf;
|
||||
if (*size == 0)
|
||||
return SZ_OK;
|
||||
RINOK(ILookInStream_Look(stream, &lookBuf, size));
|
||||
memcpy(buf, lookBuf, *size);
|
||||
return ILookInStream_Skip(stream, *size);
|
||||
}
|
||||
|
||||
SRes LookInStream_Read2(ILookInStream *stream,
|
||||
void *buf, size_t size, SRes errorType)
|
||||
SRes LookInStream_Read2(const ILookInStream *stream, void *buf, size_t size, SRes errorType)
|
||||
{
|
||||
while (size != 0)
|
||||
{
|
||||
size_t processed = size;
|
||||
SRes result = stream->Read(stream, buf, &processed);
|
||||
if (result != 0)
|
||||
return result;
|
||||
if (processed == 0)
|
||||
return errorType;
|
||||
buf = (void *)((uint8_t *)buf + processed);
|
||||
size -= processed;
|
||||
}
|
||||
return SZ_OK;
|
||||
while (size != 0)
|
||||
{
|
||||
size_t processed = size;
|
||||
RINOK(ILookInStream_Read(stream, buf, &processed));
|
||||
if (processed == 0)
|
||||
return errorType;
|
||||
buf = (void *)((Byte *)buf + processed);
|
||||
size -= processed;
|
||||
}
|
||||
return SZ_OK;
|
||||
}
|
||||
|
||||
SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size)
|
||||
SRes LookInStream_Read(const ILookInStream *stream, void *buf, size_t size)
|
||||
{
|
||||
return LookInStream_Read2(stream, buf, size, SZ_ERROR_INPUT_EOF);
|
||||
return LookInStream_Read2(stream, buf, size, SZ_ERROR_INPUT_EOF);
|
||||
}
|
||||
|
||||
static SRes LookToRead_Look_Lookahead(void *pp, const void **buf, size_t *size)
|
||||
|
||||
|
||||
#define GET_LookToRead2 CLookToRead2 *p = CONTAINER_FROM_VTBL(pp, CLookToRead2, vt);
|
||||
|
||||
static SRes LookToRead2_Look_Lookahead(const ILookInStream *pp, const void **buf, size_t *size)
|
||||
{
|
||||
SRes res = SZ_OK;
|
||||
CLookToRead *p = (CLookToRead *)pp;
|
||||
size_t size2 = p->size - p->pos;
|
||||
if (size2 == 0 && *size > 0)
|
||||
{
|
||||
p->pos = 0;
|
||||
size2 = LookToRead_BUF_SIZE;
|
||||
res = p->realStream->Read(p->realStream, p->buf, &size2);
|
||||
p->size = size2;
|
||||
}
|
||||
if (size2 < *size)
|
||||
*size = size2;
|
||||
*buf = p->buf + p->pos;
|
||||
return res;
|
||||
SRes res = SZ_OK;
|
||||
GET_LookToRead2
|
||||
size_t size2 = p->size - p->pos;
|
||||
if (size2 == 0 && *size != 0)
|
||||
{
|
||||
p->pos = 0;
|
||||
p->size = 0;
|
||||
size2 = p->bufSize;
|
||||
res = ISeekInStream_Read(p->realStream, p->buf, &size2);
|
||||
p->size = size2;
|
||||
}
|
||||
if (*size > size2)
|
||||
*size = size2;
|
||||
*buf = p->buf + p->pos;
|
||||
return res;
|
||||
}
|
||||
|
||||
static SRes LookToRead_Look_Exact(void *pp, const void **buf, size_t *size)
|
||||
static SRes LookToRead2_Look_Exact(const ILookInStream *pp, const void **buf, size_t *size)
|
||||
{
|
||||
SRes res = SZ_OK;
|
||||
CLookToRead *p = (CLookToRead *)pp;
|
||||
size_t size2 = p->size - p->pos;
|
||||
if (size2 == 0 && *size > 0)
|
||||
{
|
||||
p->pos = 0;
|
||||
if (*size > LookToRead_BUF_SIZE)
|
||||
*size = LookToRead_BUF_SIZE;
|
||||
res = p->realStream->Read(p->realStream, p->buf, size);
|
||||
size2 = p->size = *size;
|
||||
}
|
||||
if (size2 < *size)
|
||||
*size = size2;
|
||||
*buf = p->buf + p->pos;
|
||||
return res;
|
||||
SRes res = SZ_OK;
|
||||
GET_LookToRead2
|
||||
size_t size2 = p->size - p->pos;
|
||||
if (size2 == 0 && *size != 0)
|
||||
{
|
||||
p->pos = 0;
|
||||
p->size = 0;
|
||||
if (*size > p->bufSize)
|
||||
*size = p->bufSize;
|
||||
res = ISeekInStream_Read(p->realStream, p->buf, size);
|
||||
size2 = p->size = *size;
|
||||
}
|
||||
if (*size > size2)
|
||||
*size = size2;
|
||||
*buf = p->buf + p->pos;
|
||||
return res;
|
||||
}
|
||||
|
||||
static SRes LookToRead_Skip(void *pp, size_t offset)
|
||||
static SRes LookToRead2_Skip(const ILookInStream *pp, size_t offset)
|
||||
{
|
||||
CLookToRead *p = (CLookToRead *)pp;
|
||||
p->pos += offset;
|
||||
return SZ_OK;
|
||||
GET_LookToRead2
|
||||
p->pos += offset;
|
||||
return SZ_OK;
|
||||
}
|
||||
|
||||
static SRes LookToRead_Read(void *pp, void *buf, size_t *size)
|
||||
static SRes LookToRead2_Read(const ILookInStream *pp, void *buf, size_t *size)
|
||||
{
|
||||
CLookToRead *p = (CLookToRead *)pp;
|
||||
size_t rem = p->size - p->pos;
|
||||
if (rem == 0)
|
||||
return p->realStream->Read(p->realStream, buf, size);
|
||||
if (rem > *size)
|
||||
rem = *size;
|
||||
memcpy(buf, p->buf + p->pos, rem);
|
||||
p->pos += rem;
|
||||
*size = rem;
|
||||
return SZ_OK;
|
||||
GET_LookToRead2
|
||||
size_t rem = p->size - p->pos;
|
||||
if (rem == 0)
|
||||
return ISeekInStream_Read(p->realStream, buf, size);
|
||||
if (rem > *size)
|
||||
rem = *size;
|
||||
memcpy(buf, p->buf + p->pos, rem);
|
||||
p->pos += rem;
|
||||
*size = rem;
|
||||
return SZ_OK;
|
||||
}
|
||||
|
||||
static SRes LookToRead_Seek(void *pp, int64_t *pos, ESzSeek origin)
|
||||
static SRes LookToRead2_Seek(const ILookInStream *pp, Int64 *pos, ESzSeek origin)
|
||||
{
|
||||
CLookToRead *p = (CLookToRead *)pp;
|
||||
p->pos = p->size = 0;
|
||||
return p->realStream->Seek(p->realStream, pos, origin);
|
||||
GET_LookToRead2
|
||||
p->pos = p->size = 0;
|
||||
return ISeekInStream_Seek(p->realStream, pos, origin);
|
||||
}
|
||||
|
||||
void LookToRead_CreateVTable(CLookToRead *p, int lookahead)
|
||||
void LookToRead2_CreateVTable(CLookToRead2 *p, int lookahead)
|
||||
{
|
||||
p->s.Look = lookahead ?
|
||||
LookToRead_Look_Lookahead :
|
||||
LookToRead_Look_Exact;
|
||||
p->s.Skip = LookToRead_Skip;
|
||||
p->s.Read = LookToRead_Read;
|
||||
p->s.Seek = LookToRead_Seek;
|
||||
p->vt.Look = lookahead ?
|
||||
LookToRead2_Look_Lookahead :
|
||||
LookToRead2_Look_Exact;
|
||||
p->vt.Skip = LookToRead2_Skip;
|
||||
p->vt.Read = LookToRead2_Read;
|
||||
p->vt.Seek = LookToRead2_Seek;
|
||||
}
|
||||
|
||||
void LookToRead_Init(CLookToRead *p)
|
||||
{
|
||||
p->pos = p->size = 0;
|
||||
}
|
||||
|
||||
static SRes SecToLook_Read(void *pp, void *buf, size_t *size)
|
||||
|
||||
static SRes SecToLook_Read(const ISeqInStream *pp, void *buf, size_t *size)
|
||||
{
|
||||
CSecToLook *p = (CSecToLook *)pp;
|
||||
return LookInStream_LookRead(p->realStream, buf, size);
|
||||
CSecToLook *p = CONTAINER_FROM_VTBL(pp, CSecToLook, vt);
|
||||
return LookInStream_LookRead(p->realStream, buf, size);
|
||||
}
|
||||
|
||||
void SecToLook_CreateVTable(CSecToLook *p)
|
||||
{
|
||||
p->s.Read = SecToLook_Read;
|
||||
p->vt.Read = SecToLook_Read;
|
||||
}
|
||||
|
||||
static SRes SecToRead_Read(void *pp, void *buf, size_t *size)
|
||||
static SRes SecToRead_Read(const ISeqInStream *pp, void *buf, size_t *size)
|
||||
{
|
||||
CSecToRead *p = (CSecToRead *)pp;
|
||||
return p->realStream->Read(p->realStream, buf, size);
|
||||
CSecToRead *p = CONTAINER_FROM_VTBL(pp, CSecToRead, vt);
|
||||
return ILookInStream_Read(p->realStream, buf, size);
|
||||
}
|
||||
|
||||
void SecToRead_CreateVTable(CSecToRead *p)
|
||||
{
|
||||
p->s.Read = SecToRead_Read;
|
||||
p->vt.Read = SecToRead_Read;
|
||||
}
|
||||
Vendored
+274
-66
@@ -1,10 +1,13 @@
|
||||
/* 7zTypes.h -- Basic types
|
||||
2013-11-12 : Igor Pavlov : Public domain */
|
||||
2018-08-04 : Igor Pavlov : Public domain */
|
||||
|
||||
#ifndef __7Z_TYPES_H
|
||||
#define __7Z_TYPES_H
|
||||
|
||||
#include <stdint.h>
|
||||
#ifdef _WIN32
|
||||
/* #include <windows.h> */
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifndef EXTERN_C_BEGIN
|
||||
@@ -19,10 +22,6 @@
|
||||
|
||||
EXTERN_C_BEGIN
|
||||
|
||||
#ifndef _7ZIP_ST
|
||||
#define _7ZIP_ST
|
||||
#endif
|
||||
|
||||
#define SZ_OK 0
|
||||
|
||||
#define SZ_ERROR_DATA 1
|
||||
@@ -41,50 +40,157 @@ EXTERN_C_BEGIN
|
||||
#define SZ_ERROR_ARCHIVE 16
|
||||
#define SZ_ERROR_NO_ARCHIVE 17
|
||||
|
||||
typedef int64_t SRes;
|
||||
typedef int SRes;
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
/* typedef DWORD WRes; */
|
||||
typedef unsigned WRes;
|
||||
#define MY_SRes_HRESULT_FROM_WRes(x) HRESULT_FROM_WIN32(x)
|
||||
|
||||
#else
|
||||
|
||||
typedef int WRes;
|
||||
#define MY__FACILITY_WIN32 7
|
||||
#define MY__FACILITY__WRes MY__FACILITY_WIN32
|
||||
#define MY_SRes_HRESULT_FROM_WRes(x) ((HRESULT)(x) <= 0 ? ((HRESULT)(x)) : ((HRESULT) (((x) & 0x0000FFFF) | (MY__FACILITY__WRes << 16) | 0x80000000)))
|
||||
|
||||
#endif
|
||||
|
||||
typedef int64_t WRes;
|
||||
|
||||
#ifndef RINOK
|
||||
#define RINOK(x) { int __result__ = (x); if (__result__ != 0) return __result__; }
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define MY_FAST_CALL __fastcall
|
||||
#else
|
||||
#define MY_FAST_CALL
|
||||
#ifndef ZCONF_H
|
||||
typedef unsigned char Byte;
|
||||
#endif
|
||||
typedef short Int16;
|
||||
typedef unsigned short UInt16;
|
||||
|
||||
#if defined(_LZMA_UINT32_IS_ULONG) && !defined(__LP64__)
|
||||
typedef long Int32;
|
||||
typedef unsigned long UInt32;
|
||||
#else
|
||||
typedef int Int32;
|
||||
typedef unsigned int UInt32;
|
||||
#endif
|
||||
|
||||
#ifdef _SZ_NO_INT_64
|
||||
|
||||
/* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers.
|
||||
NOTES: Some code will work incorrectly in that case! */
|
||||
|
||||
typedef long Int64;
|
||||
typedef unsigned long UInt64;
|
||||
|
||||
#else
|
||||
|
||||
#if defined(_MSC_VER) || defined(__BORLANDC__)
|
||||
typedef __int64 Int64;
|
||||
typedef unsigned __int64 UInt64;
|
||||
#define UINT64_CONST(n) n
|
||||
#else
|
||||
typedef long long int Int64;
|
||||
typedef unsigned long long int UInt64;
|
||||
#define UINT64_CONST(n) n ## ULL
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _LZMA_NO_SYSTEM_SIZE_T
|
||||
typedef UInt32 SizeT;
|
||||
#else
|
||||
typedef size_t SizeT;
|
||||
#endif
|
||||
|
||||
typedef int BoolInt;
|
||||
/* typedef BoolInt Bool; */
|
||||
#define True 1
|
||||
#define False 0
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#define MY_STD_CALL __stdcall
|
||||
#else
|
||||
#define MY_STD_CALL
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
#if _MSC_VER >= 1300
|
||||
#define MY_NO_INLINE __declspec(noinline)
|
||||
#else
|
||||
#define MY_NO_INLINE
|
||||
#endif
|
||||
|
||||
#define MY_FORCE_INLINE __forceinline
|
||||
|
||||
#define MY_CDECL __cdecl
|
||||
#define MY_FAST_CALL __fastcall
|
||||
|
||||
#else
|
||||
|
||||
#define MY_NO_INLINE
|
||||
#define MY_FORCE_INLINE
|
||||
#define MY_CDECL
|
||||
#define MY_FAST_CALL
|
||||
|
||||
/* inline keyword : for C++ / C99 */
|
||||
|
||||
/* GCC, clang: */
|
||||
/*
|
||||
#if defined (__GNUC__) && (__GNUC__ >= 4)
|
||||
#define MY_FORCE_INLINE __attribute__((always_inline))
|
||||
#define MY_NO_INLINE __attribute__((noinline))
|
||||
#endif
|
||||
*/
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/* The following interfaces use first parameter as pointer to structure */
|
||||
|
||||
typedef struct
|
||||
typedef struct IByteIn IByteIn;
|
||||
struct IByteIn
|
||||
{
|
||||
unsigned char (*Read)(void *p); /* reads one byte, returns 0 in case of EOF or error */
|
||||
} IByteIn;
|
||||
Byte (*Read)(const IByteIn *p); /* reads one byte, returns 0 in case of EOF or error */
|
||||
};
|
||||
#define IByteIn_Read(p) (p)->Read(p)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
void (*Write)(void *p, unsigned char b);
|
||||
} IByteOut;
|
||||
|
||||
typedef struct
|
||||
typedef struct IByteOut IByteOut;
|
||||
struct IByteOut
|
||||
{
|
||||
SRes (*Read)(void *p, void *buf, size_t *size);
|
||||
void (*Write)(const IByteOut *p, Byte b);
|
||||
};
|
||||
#define IByteOut_Write(p, b) (p)->Write(p, b)
|
||||
|
||||
|
||||
typedef struct ISeqInStream ISeqInStream;
|
||||
struct ISeqInStream
|
||||
{
|
||||
SRes (*Read)(const ISeqInStream *p, void *buf, size_t *size);
|
||||
/* if (input(*size) != 0 && output(*size) == 0) means end_of_stream.
|
||||
(output(*size) < input(*size)) is allowed */
|
||||
} ISeqInStream;
|
||||
};
|
||||
#define ISeqInStream_Read(p, buf, size) (p)->Read(p, buf, size)
|
||||
|
||||
/* it can return SZ_ERROR_INPUT_EOF */
|
||||
SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size);
|
||||
SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType);
|
||||
SRes SeqInStream_ReadByte(ISeqInStream *stream, unsigned char *buf);
|
||||
SRes SeqInStream_Read(const ISeqInStream *stream, void *buf, size_t size);
|
||||
SRes SeqInStream_Read2(const ISeqInStream *stream, void *buf, size_t size, SRes errorType);
|
||||
SRes SeqInStream_ReadByte(const ISeqInStream *stream, Byte *buf);
|
||||
|
||||
typedef struct
|
||||
|
||||
typedef struct ISeqOutStream ISeqOutStream;
|
||||
struct ISeqOutStream
|
||||
{
|
||||
size_t (*Write)(void *p, const void *buf, size_t size);
|
||||
size_t (*Write)(const ISeqOutStream *p, const void *buf, size_t size);
|
||||
/* Returns: result - the number of actually written bytes.
|
||||
(result < size) means error */
|
||||
} ISeqOutStream;
|
||||
};
|
||||
#define ISeqOutStream_Write(p, buf, size) (p)->Write(p, buf, size)
|
||||
|
||||
typedef enum
|
||||
{
|
||||
@@ -93,76 +199,178 @@ typedef enum
|
||||
SZ_SEEK_END = 2
|
||||
} ESzSeek;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SRes (*Read)(void *p, void *buf, size_t *size); /* same as ISeqInStream::Read */
|
||||
SRes (*Seek)(void *p, int64_t *pos, ESzSeek origin);
|
||||
} ISeekInStream;
|
||||
|
||||
typedef struct
|
||||
typedef struct ISeekInStream ISeekInStream;
|
||||
struct ISeekInStream
|
||||
{
|
||||
SRes (*Look)(void *p, const void **buf, size_t *size);
|
||||
SRes (*Read)(const ISeekInStream *p, void *buf, size_t *size); /* same as ISeqInStream::Read */
|
||||
SRes (*Seek)(const ISeekInStream *p, Int64 *pos, ESzSeek origin);
|
||||
};
|
||||
#define ISeekInStream_Read(p, buf, size) (p)->Read(p, buf, size)
|
||||
#define ISeekInStream_Seek(p, pos, origin) (p)->Seek(p, pos, origin)
|
||||
|
||||
|
||||
typedef struct ILookInStream ILookInStream;
|
||||
struct ILookInStream
|
||||
{
|
||||
SRes (*Look)(const ILookInStream *p, const void **buf, size_t *size);
|
||||
/* if (input(*size) != 0 && output(*size) == 0) means end_of_stream.
|
||||
(output(*size) > input(*size)) is not allowed
|
||||
(output(*size) < input(*size)) is allowed */
|
||||
SRes (*Skip)(void *p, size_t offset);
|
||||
SRes (*Skip)(const ILookInStream *p, size_t offset);
|
||||
/* offset must be <= output(*size) of Look */
|
||||
|
||||
SRes (*Read)(void *p, void *buf, size_t *size);
|
||||
SRes (*Read)(const ILookInStream *p, void *buf, size_t *size);
|
||||
/* reads directly (without buffer). It's same as ISeqInStream::Read */
|
||||
SRes (*Seek)(void *p, int64_t *pos, ESzSeek origin);
|
||||
} ILookInStream;
|
||||
SRes (*Seek)(const ILookInStream *p, Int64 *pos, ESzSeek origin);
|
||||
};
|
||||
|
||||
SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size);
|
||||
SRes LookInStream_SeekTo(ILookInStream *stream, uint64_t offset);
|
||||
#define ILookInStream_Look(p, buf, size) (p)->Look(p, buf, size)
|
||||
#define ILookInStream_Skip(p, offset) (p)->Skip(p, offset)
|
||||
#define ILookInStream_Read(p, buf, size) (p)->Read(p, buf, size)
|
||||
#define ILookInStream_Seek(p, pos, origin) (p)->Seek(p, pos, origin)
|
||||
|
||||
|
||||
SRes LookInStream_LookRead(const ILookInStream *stream, void *buf, size_t *size);
|
||||
SRes LookInStream_SeekTo(const ILookInStream *stream, UInt64 offset);
|
||||
|
||||
/* reads via ILookInStream::Read */
|
||||
SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType);
|
||||
SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size);
|
||||
SRes LookInStream_Read2(const ILookInStream *stream, void *buf, size_t size, SRes errorType);
|
||||
SRes LookInStream_Read(const ILookInStream *stream, void *buf, size_t size);
|
||||
|
||||
|
||||
#define LookToRead_BUF_SIZE (1 << 14)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ILookInStream s;
|
||||
ISeekInStream *realStream;
|
||||
ILookInStream vt;
|
||||
const ISeekInStream *realStream;
|
||||
|
||||
size_t pos;
|
||||
size_t size;
|
||||
unsigned char buf[LookToRead_BUF_SIZE];
|
||||
} CLookToRead;
|
||||
size_t size; /* it's data size */
|
||||
|
||||
/* the following variables must be set outside */
|
||||
Byte *buf;
|
||||
size_t bufSize;
|
||||
} CLookToRead2;
|
||||
|
||||
void LookToRead2_CreateVTable(CLookToRead2 *p, int lookahead);
|
||||
|
||||
#define LookToRead2_Init(p) { (p)->pos = (p)->size = 0; }
|
||||
|
||||
void LookToRead_CreateVTable(CLookToRead *p, int lookahead);
|
||||
void LookToRead_Init(CLookToRead *p);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ISeqInStream s;
|
||||
ILookInStream *realStream;
|
||||
ISeqInStream vt;
|
||||
const ILookInStream *realStream;
|
||||
} CSecToLook;
|
||||
|
||||
void SecToLook_CreateVTable(CSecToLook *p);
|
||||
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ISeqInStream s;
|
||||
ILookInStream *realStream;
|
||||
ISeqInStream vt;
|
||||
const ILookInStream *realStream;
|
||||
} CSecToRead;
|
||||
|
||||
void SecToRead_CreateVTable(CSecToRead *p);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SRes (*Progress)(void *p, uint64_t inSize, uint64_t outSize);
|
||||
} ICompressProgress;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
void *(*Alloc)(void *p, size_t size);
|
||||
void (*Free)(void *p, void *address); /* address can be 0 */
|
||||
} ISzAlloc;
|
||||
typedef struct ICompressProgress ICompressProgress;
|
||||
|
||||
#define IAlloc_Alloc(p, size) (p)->Alloc((p), size)
|
||||
#define IAlloc_Free(p, a) (p)->Free((p), a)
|
||||
struct ICompressProgress
|
||||
{
|
||||
SRes (*Progress)(const ICompressProgress *p, UInt64 inSize, UInt64 outSize);
|
||||
/* Returns: result. (result != SZ_OK) means break.
|
||||
Value (UInt64)(Int64)-1 for size means unknown value. */
|
||||
};
|
||||
#define ICompressProgress_Progress(p, inSize, outSize) (p)->Progress(p, inSize, outSize)
|
||||
|
||||
|
||||
|
||||
typedef struct ISzAlloc ISzAlloc;
|
||||
typedef const ISzAlloc * ISzAllocPtr;
|
||||
|
||||
struct ISzAlloc
|
||||
{
|
||||
void *(*Alloc)(ISzAllocPtr p, size_t size);
|
||||
void (*Free)(ISzAllocPtr p, void *address); /* address can be 0 */
|
||||
};
|
||||
|
||||
#define ISzAlloc_Alloc(p, size) (p)->Alloc(p, size)
|
||||
#define ISzAlloc_Free(p, a) (p)->Free(p, a)
|
||||
|
||||
/* deprecated */
|
||||
#define IAlloc_Alloc(p, size) ISzAlloc_Alloc(p, size)
|
||||
#define IAlloc_Free(p, a) ISzAlloc_Free(p, a)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifndef MY_offsetof
|
||||
#ifdef offsetof
|
||||
#define MY_offsetof(type, m) offsetof(type, m)
|
||||
/*
|
||||
#define MY_offsetof(type, m) FIELD_OFFSET(type, m)
|
||||
*/
|
||||
#else
|
||||
#define MY_offsetof(type, m) ((size_t)&(((type *)0)->m))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifndef MY_container_of
|
||||
|
||||
/*
|
||||
#define MY_container_of(ptr, type, m) container_of(ptr, type, m)
|
||||
#define MY_container_of(ptr, type, m) CONTAINING_RECORD(ptr, type, m)
|
||||
#define MY_container_of(ptr, type, m) ((type *)((char *)(ptr) - offsetof(type, m)))
|
||||
#define MY_container_of(ptr, type, m) (&((type *)0)->m == (ptr), ((type *)(((char *)(ptr)) - MY_offsetof(type, m))))
|
||||
*/
|
||||
|
||||
/*
|
||||
GCC shows warning: "perhaps the 'offsetof' macro was used incorrectly"
|
||||
GCC 3.4.4 : classes with constructor
|
||||
GCC 4.8.1 : classes with non-public variable members"
|
||||
*/
|
||||
|
||||
#define MY_container_of(ptr, type, m) ((type *)((char *)(1 ? (ptr) : &((type *)0)->m) - MY_offsetof(type, m)))
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#define CONTAINER_FROM_VTBL_SIMPLE(ptr, type, m) ((type *)(ptr))
|
||||
|
||||
/*
|
||||
#define CONTAINER_FROM_VTBL(ptr, type, m) CONTAINER_FROM_VTBL_SIMPLE(ptr, type, m)
|
||||
*/
|
||||
#define CONTAINER_FROM_VTBL(ptr, type, m) MY_container_of(ptr, type, m)
|
||||
|
||||
#define CONTAINER_FROM_VTBL_CLS(ptr, type, m) CONTAINER_FROM_VTBL_SIMPLE(ptr, type, m)
|
||||
/*
|
||||
#define CONTAINER_FROM_VTBL_CLS(ptr, type, m) CONTAINER_FROM_VTBL(ptr, type, m)
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#define CHAR_PATH_SEPARATOR '\\'
|
||||
#define WCHAR_PATH_SEPARATOR L'\\'
|
||||
#define STRING_PATH_SEPARATOR "\\"
|
||||
#define WSTRING_PATH_SEPARATOR L"\\"
|
||||
|
||||
#else
|
||||
|
||||
#define CHAR_PATH_SEPARATOR '/'
|
||||
#define WCHAR_PATH_SEPARATOR L'/'
|
||||
#define STRING_PATH_SEPARATOR "/"
|
||||
#define WSTRING_PATH_SEPARATOR L"/"
|
||||
|
||||
#endif
|
||||
|
||||
EXTERN_C_END
|
||||
|
||||
|
||||
Vendored
+26
-6
@@ -1,7 +1,27 @@
|
||||
#define MY_VER_MAJOR 9
|
||||
#define MY_VER_MINOR 20
|
||||
#define MY_VER_MAJOR 19
|
||||
#define MY_VER_MINOR 00
|
||||
#define MY_VER_BUILD 0
|
||||
#define MY_VERSION "9.20"
|
||||
#define MY_DATE "2010-11-18"
|
||||
#define MY_COPYRIGHT ": Igor Pavlov : Public domain"
|
||||
#define MY_VERSION_COPYRIGHT_DATE MY_VERSION " " MY_COPYRIGHT " : " MY_DATE
|
||||
#define MY_VERSION_NUMBERS "19.00"
|
||||
#define MY_VERSION MY_VERSION_NUMBERS
|
||||
|
||||
#ifdef MY_CPU_NAME
|
||||
#define MY_VERSION_CPU MY_VERSION " (" MY_CPU_NAME ")"
|
||||
#else
|
||||
#define MY_VERSION_CPU MY_VERSION
|
||||
#endif
|
||||
|
||||
#define MY_DATE "2019-02-21"
|
||||
#undef MY_COPYRIGHT
|
||||
#undef MY_VERSION_COPYRIGHT_DATE
|
||||
#define MY_AUTHOR_NAME "Igor Pavlov"
|
||||
#define MY_COPYRIGHT_PD "Igor Pavlov : Public domain"
|
||||
#define MY_COPYRIGHT_CR "Copyright (c) 1999-2018 Igor Pavlov"
|
||||
|
||||
#ifdef USE_COPYRIGHT_CR
|
||||
#define MY_COPYRIGHT MY_COPYRIGHT_CR
|
||||
#else
|
||||
#define MY_COPYRIGHT MY_COPYRIGHT_PD
|
||||
#endif
|
||||
|
||||
#define MY_COPYRIGHT_DATE MY_COPYRIGHT " : " MY_DATE
|
||||
#define MY_VERSION_COPYRIGHT_DATE MY_VERSION_CPU " : " MY_COPYRIGHT " : " MY_DATE
|
||||
Vendored
+235
-125
@@ -1,149 +1,259 @@
|
||||
/* Bcj2.c -- Converter for x86 code (BCJ2)
|
||||
2008-10-04 : Igor Pavlov : Public domain */
|
||||
/* Bcj2.c -- BCJ2 Decoder (Converter for x86 code)
|
||||
2018-04-28 : Igor Pavlov : Public domain */
|
||||
|
||||
#include "Precomp.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include "Bcj2.h"
|
||||
#include "CpuArch.h"
|
||||
|
||||
#define IsJcc(b0, b1) ((b0) == 0x0F && ((b1) & 0xF0) == 0x80)
|
||||
#define IsJ(b0, b1) ((b1 & 0xFE) == 0xE8 || IsJcc(b0, b1))
|
||||
#define CProb UInt16
|
||||
|
||||
#define kNumTopBits 24
|
||||
#define kTopValue ((uint32_t)1 << kNumTopBits)
|
||||
|
||||
#define kNumBitModelTotalBits 11
|
||||
#define kBitModelTotal (1 << kNumBitModelTotalBits)
|
||||
#undef kTopValue
|
||||
#define kTopValue ((UInt32)1 << 24)
|
||||
#define kNumModelBits 11
|
||||
#undef kBitModelTotal
|
||||
#define kBitModelTotal (1 << kNumModelBits)
|
||||
#define kNumMoveBits 5
|
||||
|
||||
#define RC_READ_BYTE (*buffer++)
|
||||
#define _IF_BIT_0 ttt = *prob; bound = (p->range >> kNumModelBits) * ttt; if (p->code < bound)
|
||||
#define _UPDATE_0 p->range = bound; *prob = (CProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits));
|
||||
#define _UPDATE_1 p->range -= bound; p->code -= bound; *prob = (CProb)(ttt - (ttt >> kNumMoveBits));
|
||||
|
||||
int Bcj2_Decode(
|
||||
const uint8_t *buf0, size_t size0,
|
||||
const uint8_t *buf1, size_t size1,
|
||||
const uint8_t *buf2, size_t size2,
|
||||
const uint8_t *buf3, size_t size3,
|
||||
uint8_t *outBuf, size_t outSize)
|
||||
void Bcj2Dec_Init(CBcj2Dec *p)
|
||||
{
|
||||
uint16_t p[256 + 2];
|
||||
size_t inPos = 0, outPos = 0;
|
||||
unsigned i;
|
||||
|
||||
const uint8_t *buffer, *bufferLim;
|
||||
uint32_t range, codes = 0;
|
||||
uint8_t prevuint8_t = 0;
|
||||
p->state = BCJ2_DEC_STATE_OK;
|
||||
p->ip = 0;
|
||||
p->temp[3] = 0;
|
||||
p->range = 0;
|
||||
p->code = 0;
|
||||
for (i = 0; i < sizeof(p->probs) / sizeof(p->probs[0]); i++)
|
||||
p->probs[i] = kBitModelTotal >> 1;
|
||||
}
|
||||
|
||||
unsigned int i;
|
||||
for (i = 0; i < sizeof(p) / sizeof(p[0]); i++)
|
||||
p[i] = kBitModelTotal >> 1;
|
||||
SRes Bcj2Dec_Decode(CBcj2Dec *p)
|
||||
{
|
||||
if (p->range <= 5)
|
||||
{
|
||||
p->state = BCJ2_DEC_STATE_OK;
|
||||
for (; p->range != 5; p->range++)
|
||||
{
|
||||
if (p->range == 1 && p->code != 0)
|
||||
return SZ_ERROR_DATA;
|
||||
|
||||
if (p->bufs[BCJ2_STREAM_RC] == p->lims[BCJ2_STREAM_RC])
|
||||
{
|
||||
p->state = BCJ2_STREAM_RC;
|
||||
return SZ_OK;
|
||||
}
|
||||
|
||||
buffer = buf3;
|
||||
bufferLim = buffer + size3;
|
||||
range = 0xFFFFFFFF;
|
||||
p->code = (p->code << 8) | *(p->bufs[BCJ2_STREAM_RC])++;
|
||||
}
|
||||
|
||||
if (p->code == 0xFFFFFFFF)
|
||||
return SZ_ERROR_DATA;
|
||||
|
||||
p->range = 0xFFFFFFFF;
|
||||
}
|
||||
else if (p->state >= BCJ2_DEC_STATE_ORIG_0)
|
||||
{
|
||||
while (p->state <= BCJ2_DEC_STATE_ORIG_3)
|
||||
{
|
||||
Byte *dest = p->dest;
|
||||
if (dest == p->destLim)
|
||||
return SZ_OK;
|
||||
*dest = p->temp[(size_t)p->state - BCJ2_DEC_STATE_ORIG_0];
|
||||
p->state++;
|
||||
p->dest = dest + 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 5; i++)
|
||||
{
|
||||
if (buffer == bufferLim)
|
||||
return SZ_ERROR_DATA;
|
||||
codes = (codes << 8) | RC_READ_BYTE;
|
||||
}
|
||||
/*
|
||||
if (BCJ2_IS_32BIT_STREAM(p->state))
|
||||
{
|
||||
const Byte *cur = p->bufs[p->state];
|
||||
if (cur == p->lims[p->state])
|
||||
return SZ_OK;
|
||||
p->bufs[p->state] = cur + 4;
|
||||
|
||||
{
|
||||
UInt32 val;
|
||||
Byte *dest;
|
||||
SizeT rem;
|
||||
|
||||
p->ip += 4;
|
||||
val = GetBe32(cur) - p->ip;
|
||||
dest = p->dest;
|
||||
rem = p->destLim - dest;
|
||||
if (rem < 4)
|
||||
{
|
||||
SizeT i;
|
||||
SetUi32(p->temp, val);
|
||||
for (i = 0; i < rem; i++)
|
||||
dest[i] = p->temp[i];
|
||||
p->dest = dest + rem;
|
||||
p->state = BCJ2_DEC_STATE_ORIG_0 + (unsigned)rem;
|
||||
return SZ_OK;
|
||||
}
|
||||
SetUi32(dest, val);
|
||||
p->temp[3] = (Byte)(val >> 24);
|
||||
p->dest = dest + 4;
|
||||
p->state = BCJ2_DEC_STATE_OK;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if (outSize == 0)
|
||||
return SZ_OK;
|
||||
for (;;)
|
||||
{
|
||||
if (BCJ2_IS_32BIT_STREAM(p->state))
|
||||
p->state = BCJ2_DEC_STATE_OK;
|
||||
else
|
||||
{
|
||||
if (p->range < kTopValue)
|
||||
{
|
||||
if (p->bufs[BCJ2_STREAM_RC] == p->lims[BCJ2_STREAM_RC])
|
||||
{
|
||||
p->state = BCJ2_STREAM_RC;
|
||||
return SZ_OK;
|
||||
}
|
||||
p->range <<= 8;
|
||||
p->code = (p->code << 8) | *(p->bufs[BCJ2_STREAM_RC])++;
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
uint8_t b;
|
||||
uint16_t *prob;
|
||||
uint32_t bound;
|
||||
uint32_t ttt;
|
||||
{
|
||||
const Byte *src = p->bufs[BCJ2_STREAM_MAIN];
|
||||
const Byte *srcLim;
|
||||
Byte *dest;
|
||||
SizeT num = p->lims[BCJ2_STREAM_MAIN] - src;
|
||||
|
||||
if (num == 0)
|
||||
{
|
||||
p->state = BCJ2_STREAM_MAIN;
|
||||
return SZ_OK;
|
||||
}
|
||||
|
||||
dest = p->dest;
|
||||
if (num > (SizeT)(p->destLim - dest))
|
||||
{
|
||||
num = p->destLim - dest;
|
||||
if (num == 0)
|
||||
{
|
||||
p->state = BCJ2_DEC_STATE_ORIG;
|
||||
return SZ_OK;
|
||||
}
|
||||
}
|
||||
|
||||
srcLim = src + num;
|
||||
|
||||
size_t limit = size0 - inPos;
|
||||
if (outSize - outPos < limit)
|
||||
limit = outSize - outPos;
|
||||
while (limit != 0)
|
||||
{
|
||||
b = buf0[inPos];
|
||||
outBuf[outPos++] = b;
|
||||
if (IsJ(prevuint8_t, b))
|
||||
if (p->temp[3] == 0x0F && (src[0] & 0xF0) == 0x80)
|
||||
*dest = src[0];
|
||||
else for (;;)
|
||||
{
|
||||
Byte b = *src;
|
||||
*dest = b;
|
||||
if (b != 0x0F)
|
||||
{
|
||||
if ((b & 0xFE) == 0xE8)
|
||||
break;
|
||||
dest++;
|
||||
if (++src != srcLim)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
dest++;
|
||||
if (++src == srcLim)
|
||||
break;
|
||||
inPos++;
|
||||
prevuint8_t = b;
|
||||
limit--;
|
||||
if ((*src & 0xF0) != 0x80)
|
||||
continue;
|
||||
*dest = *src;
|
||||
break;
|
||||
}
|
||||
|
||||
num = src - p->bufs[BCJ2_STREAM_MAIN];
|
||||
|
||||
if (src == srcLim)
|
||||
{
|
||||
p->temp[3] = src[-1];
|
||||
p->bufs[BCJ2_STREAM_MAIN] = src;
|
||||
p->ip += (UInt32)num;
|
||||
p->dest += num;
|
||||
p->state =
|
||||
p->bufs[BCJ2_STREAM_MAIN] ==
|
||||
p->lims[BCJ2_STREAM_MAIN] ?
|
||||
(unsigned)BCJ2_STREAM_MAIN :
|
||||
(unsigned)BCJ2_DEC_STATE_ORIG;
|
||||
return SZ_OK;
|
||||
}
|
||||
|
||||
{
|
||||
UInt32 bound, ttt;
|
||||
CProb *prob;
|
||||
Byte b = src[0];
|
||||
Byte prev = (Byte)(num == 0 ? p->temp[3] : src[-1]);
|
||||
|
||||
p->temp[3] = b;
|
||||
p->bufs[BCJ2_STREAM_MAIN] = src + 1;
|
||||
num++;
|
||||
p->ip += (UInt32)num;
|
||||
p->dest += num;
|
||||
|
||||
prob = p->probs + (unsigned)(b == 0xE8 ? 2 + (unsigned)prev : (b == 0xE9 ? 1 : 0));
|
||||
|
||||
_IF_BIT_0
|
||||
{
|
||||
_UPDATE_0
|
||||
continue;
|
||||
}
|
||||
_UPDATE_1
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (limit == 0 || outPos == outSize)
|
||||
break;
|
||||
{
|
||||
UInt32 val;
|
||||
unsigned cj = (p->temp[3] == 0xE8) ? BCJ2_STREAM_CALL : BCJ2_STREAM_JUMP;
|
||||
const Byte *cur = p->bufs[cj];
|
||||
Byte *dest;
|
||||
SizeT rem;
|
||||
|
||||
if (cur == p->lims[cj])
|
||||
{
|
||||
p->state = cj;
|
||||
break;
|
||||
}
|
||||
|
||||
val = GetBe32(cur);
|
||||
p->bufs[cj] = cur + 4;
|
||||
|
||||
b = buf0[inPos++];
|
||||
|
||||
if (b == 0xE8)
|
||||
prob = p + prevuint8_t;
|
||||
else if (b == 0xE9)
|
||||
prob = p + 256;
|
||||
else
|
||||
prob = p + 257;
|
||||
|
||||
ttt = *(prob);
|
||||
bound = (range >> kNumBitModelTotalBits) * ttt;
|
||||
|
||||
if (codes < bound)
|
||||
p->ip += 4;
|
||||
val -= p->ip;
|
||||
dest = p->dest;
|
||||
rem = p->destLim - dest;
|
||||
|
||||
if (rem < 4)
|
||||
{
|
||||
range = bound;
|
||||
*(prob) = (uint16_t)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits));
|
||||
|
||||
if (range < kTopValue)
|
||||
{
|
||||
if (buffer == bufferLim)
|
||||
return SZ_ERROR_DATA;
|
||||
range <<= 8;
|
||||
codes = (codes << 8) | RC_READ_BYTE;
|
||||
}
|
||||
prevuint8_t = b;
|
||||
p->temp[0] = (Byte)val; if (rem > 0) dest[0] = (Byte)val; val >>= 8;
|
||||
p->temp[1] = (Byte)val; if (rem > 1) dest[1] = (Byte)val; val >>= 8;
|
||||
p->temp[2] = (Byte)val; if (rem > 2) dest[2] = (Byte)val; val >>= 8;
|
||||
p->temp[3] = (Byte)val;
|
||||
p->dest = dest + rem;
|
||||
p->state = BCJ2_DEC_STATE_ORIG_0 + (unsigned)rem;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t dest;
|
||||
const uint8_t *v;
|
||||
|
||||
SetUi32(dest, val);
|
||||
p->temp[3] = (Byte)(val >> 24);
|
||||
p->dest = dest + 4;
|
||||
}
|
||||
}
|
||||
|
||||
range -= bound;
|
||||
codes -= bound;
|
||||
*(prob) = (uint16_t)(ttt - (ttt >> kNumMoveBits));
|
||||
if (p->range < kTopValue && p->bufs[BCJ2_STREAM_RC] != p->lims[BCJ2_STREAM_RC])
|
||||
{
|
||||
p->range <<= 8;
|
||||
p->code = (p->code << 8) | *(p->bufs[BCJ2_STREAM_RC])++;
|
||||
}
|
||||
|
||||
if (range < kTopValue)
|
||||
{
|
||||
if (buffer == bufferLim)
|
||||
return SZ_ERROR_DATA;
|
||||
range <<= 8;
|
||||
codes = (codes << 8) | RC_READ_BYTE;
|
||||
}
|
||||
|
||||
if (b == 0xE8)
|
||||
{
|
||||
v = buf1;
|
||||
if (size1 < 4)
|
||||
return SZ_ERROR_DATA;
|
||||
buf1 += 4;
|
||||
size1 -= 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
v = buf2;
|
||||
if (size2 < 4)
|
||||
return SZ_ERROR_DATA;
|
||||
buf2 += 4;
|
||||
size2 -= 4;
|
||||
}
|
||||
dest = (((uint32_t)v[0] << 24) | ((uint32_t)v[1] << 16) |
|
||||
((uint32_t)v[2] << 8) | ((uint32_t)v[3])) - ((uint32_t)outPos + 4);
|
||||
outBuf[outPos++] = (uint8_t)dest;
|
||||
if (outPos == outSize)
|
||||
break;
|
||||
outBuf[outPos++] = (uint8_t)(dest >> 8);
|
||||
if (outPos == outSize)
|
||||
break;
|
||||
outBuf[outPos++] = (uint8_t)(dest >> 16);
|
||||
if (outPos == outSize)
|
||||
break;
|
||||
outBuf[outPos++] = prevuint8_t = (uint8_t)(dest >> 24);
|
||||
}
|
||||
}
|
||||
return (outPos == outSize) ? SZ_OK : SZ_ERROR_DATA;
|
||||
return SZ_OK;
|
||||
}
|
||||
Vendored
+133
-25
@@ -1,38 +1,146 @@
|
||||
/* Bcj2.h -- Converter for x86 code (BCJ2)
|
||||
2009-02-07 : Igor Pavlov : Public domain */
|
||||
/* Bcj2.h -- BCJ2 Converter for x86 code
|
||||
2014-11-10 : Igor Pavlov : Public domain */
|
||||
|
||||
#ifndef __BCJ2_H
|
||||
#define __BCJ2_H
|
||||
|
||||
#include "7zTypes.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
EXTERN_C_BEGIN
|
||||
|
||||
#define BCJ2_NUM_STREAMS 4
|
||||
|
||||
enum
|
||||
{
|
||||
BCJ2_STREAM_MAIN,
|
||||
BCJ2_STREAM_CALL,
|
||||
BCJ2_STREAM_JUMP,
|
||||
BCJ2_STREAM_RC
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
BCJ2_DEC_STATE_ORIG_0 = BCJ2_NUM_STREAMS,
|
||||
BCJ2_DEC_STATE_ORIG_1,
|
||||
BCJ2_DEC_STATE_ORIG_2,
|
||||
BCJ2_DEC_STATE_ORIG_3,
|
||||
|
||||
BCJ2_DEC_STATE_ORIG,
|
||||
BCJ2_DEC_STATE_OK
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
BCJ2_ENC_STATE_ORIG = BCJ2_NUM_STREAMS,
|
||||
BCJ2_ENC_STATE_OK
|
||||
};
|
||||
|
||||
|
||||
#define BCJ2_IS_32BIT_STREAM(s) ((s) == BCJ2_STREAM_CALL || (s) == BCJ2_STREAM_JUMP)
|
||||
|
||||
/*
|
||||
Conditions:
|
||||
outSize <= FullOutputSize,
|
||||
where FullOutputSize is full size of output stream of x86_2 filter.
|
||||
|
||||
If buf0 overlaps outBuf, there are two required conditions:
|
||||
1) (buf0 >= outBuf)
|
||||
2) (buf0 + size0 >= outBuf + FullOutputSize).
|
||||
|
||||
Returns:
|
||||
SZ_OK
|
||||
SZ_ERROR_DATA - Data error
|
||||
CBcj2Dec / CBcj2Enc
|
||||
bufs sizes:
|
||||
BUF_SIZE(n) = lims[n] - bufs[n]
|
||||
bufs sizes for BCJ2_STREAM_CALL and BCJ2_STREAM_JUMP must be mutliply of 4:
|
||||
(BUF_SIZE(BCJ2_STREAM_CALL) & 3) == 0
|
||||
(BUF_SIZE(BCJ2_STREAM_JUMP) & 3) == 0
|
||||
*/
|
||||
|
||||
int Bcj2_Decode(
|
||||
const uint8_t *buf0, size_t size0,
|
||||
const uint8_t *buf1, size_t size1,
|
||||
const uint8_t *buf2, size_t size2,
|
||||
const uint8_t *buf3, size_t size3,
|
||||
uint8_t *outBuf, size_t outSize);
|
||||
/*
|
||||
CBcj2Dec:
|
||||
dest is allowed to overlap with bufs[BCJ2_STREAM_MAIN], with the following conditions:
|
||||
bufs[BCJ2_STREAM_MAIN] >= dest &&
|
||||
bufs[BCJ2_STREAM_MAIN] - dest >= tempReserv +
|
||||
BUF_SIZE(BCJ2_STREAM_CALL) +
|
||||
BUF_SIZE(BCJ2_STREAM_JUMP)
|
||||
tempReserv = 0 : for first call of Bcj2Dec_Decode
|
||||
tempReserv = 4 : for any other calls of Bcj2Dec_Decode
|
||||
overlap with offset = 1 is not allowed
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
typedef struct
|
||||
{
|
||||
const Byte *bufs[BCJ2_NUM_STREAMS];
|
||||
const Byte *lims[BCJ2_NUM_STREAMS];
|
||||
Byte *dest;
|
||||
const Byte *destLim;
|
||||
|
||||
unsigned state; /* BCJ2_STREAM_MAIN has more priority than BCJ2_STATE_ORIG */
|
||||
|
||||
UInt32 ip;
|
||||
Byte temp[4];
|
||||
UInt32 range;
|
||||
UInt32 code;
|
||||
UInt16 probs[2 + 256];
|
||||
} CBcj2Dec;
|
||||
|
||||
void Bcj2Dec_Init(CBcj2Dec *p);
|
||||
|
||||
/* Returns: SZ_OK or SZ_ERROR_DATA */
|
||||
SRes Bcj2Dec_Decode(CBcj2Dec *p);
|
||||
|
||||
#define Bcj2Dec_IsFinished(_p_) ((_p_)->code == 0)
|
||||
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
BCJ2_ENC_FINISH_MODE_CONTINUE,
|
||||
BCJ2_ENC_FINISH_MODE_END_BLOCK,
|
||||
BCJ2_ENC_FINISH_MODE_END_STREAM
|
||||
} EBcj2Enc_FinishMode;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Byte *bufs[BCJ2_NUM_STREAMS];
|
||||
const Byte *lims[BCJ2_NUM_STREAMS];
|
||||
const Byte *src;
|
||||
const Byte *srcLim;
|
||||
|
||||
unsigned state;
|
||||
EBcj2Enc_FinishMode finishMode;
|
||||
|
||||
Byte prevByte;
|
||||
|
||||
Byte cache;
|
||||
UInt32 range;
|
||||
UInt64 low;
|
||||
UInt64 cacheSize;
|
||||
|
||||
UInt32 ip;
|
||||
|
||||
/* 32-bit ralative offset in JUMP/CALL commands is
|
||||
- (mod 4 GB) in 32-bit mode
|
||||
- signed Int32 in 64-bit mode
|
||||
We use (mod 4 GB) check for fileSize.
|
||||
Use fileSize up to 2 GB, if you want to support 32-bit and 64-bit code conversion. */
|
||||
UInt32 fileIp;
|
||||
UInt32 fileSize; /* (fileSize <= ((UInt32)1 << 31)), 0 means no_limit */
|
||||
UInt32 relatLimit; /* (relatLimit <= ((UInt32)1 << 31)), 0 means desable_conversion */
|
||||
|
||||
UInt32 tempTarget;
|
||||
unsigned tempPos;
|
||||
Byte temp[4 * 2];
|
||||
|
||||
unsigned flushPos;
|
||||
|
||||
UInt16 probs[2 + 256];
|
||||
} CBcj2Enc;
|
||||
|
||||
void Bcj2Enc_Init(CBcj2Enc *p);
|
||||
void Bcj2Enc_Encode(CBcj2Enc *p);
|
||||
|
||||
#define Bcj2Enc_Get_InputData_Size(p) ((SizeT)((p)->srcLim - (p)->src) + (p)->tempPos)
|
||||
#define Bcj2Enc_IsFinished(p) ((p)->flushPos == 5)
|
||||
|
||||
|
||||
#define BCJ2_RELAT_LIMIT_NUM_BITS 26
|
||||
#define BCJ2_RELAT_LIMIT ((UInt32)1 << BCJ2_RELAT_LIMIT_NUM_BITS)
|
||||
|
||||
/* limit for CBcj2Enc::fileSize variable */
|
||||
#define BCJ2_FileSize_MAX ((UInt32)1 << 31)
|
||||
|
||||
EXTERN_C_END
|
||||
|
||||
#endif
|
||||
Vendored
+212
-116
@@ -1,134 +1,230 @@
|
||||
/* Bra.c -- Converters for RISC code
|
||||
2010-04-16 : Igor Pavlov : Public domain */
|
||||
2017-04-04 : Igor Pavlov : Public domain */
|
||||
|
||||
#include <stdint.h>
|
||||
#include "Precomp.h"
|
||||
|
||||
#include "CpuArch.h"
|
||||
#include "Bra.h"
|
||||
|
||||
size_t ARM_Convert(uint8_t *data, size_t size, uint32_t ip, int encoding)
|
||||
SizeT ARM_Convert(Byte *data, SizeT size, UInt32 ip, int encoding)
|
||||
{
|
||||
size_t i;
|
||||
if (size < 4)
|
||||
return 0;
|
||||
size -= 4;
|
||||
ip += 8;
|
||||
for (i = 0; i <= size; i += 4)
|
||||
{
|
||||
if (data[i + 3] == 0xEB)
|
||||
{
|
||||
uint32_t dest;
|
||||
uint32_t src = ((uint32_t)data[i + 2] << 16) | ((uint32_t)data[i + 1] << 8) | (data[i + 0]);
|
||||
src <<= 2;
|
||||
if (encoding)
|
||||
dest = ip + (uint32_t)i + src;
|
||||
else
|
||||
dest = src - (ip + (uint32_t)i);
|
||||
dest >>= 2;
|
||||
data[i + 2] = (uint8_t)(dest >> 16);
|
||||
data[i + 1] = (uint8_t)(dest >> 8);
|
||||
data[i + 0] = (uint8_t)dest;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
Byte *p;
|
||||
const Byte *lim;
|
||||
size &= ~(size_t)3;
|
||||
ip += 4;
|
||||
p = data;
|
||||
lim = data + size;
|
||||
|
||||
if (encoding)
|
||||
|
||||
for (;;)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
if (p >= lim)
|
||||
return p - data;
|
||||
p += 4;
|
||||
if (p[-1] == 0xEB)
|
||||
break;
|
||||
}
|
||||
{
|
||||
UInt32 v = GetUi32(p - 4);
|
||||
v <<= 2;
|
||||
v += ip + (UInt32)(p - data);
|
||||
v >>= 2;
|
||||
v &= 0x00FFFFFF;
|
||||
v |= 0xEB000000;
|
||||
SetUi32(p - 4, v);
|
||||
}
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
if (p >= lim)
|
||||
return p - data;
|
||||
p += 4;
|
||||
if (p[-1] == 0xEB)
|
||||
break;
|
||||
}
|
||||
{
|
||||
UInt32 v = GetUi32(p - 4);
|
||||
v <<= 2;
|
||||
v -= ip + (UInt32)(p - data);
|
||||
v >>= 2;
|
||||
v &= 0x00FFFFFF;
|
||||
v |= 0xEB000000;
|
||||
SetUi32(p - 4, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t ARMT_Convert(uint8_t *data, size_t size, uint32_t ip, int encoding)
|
||||
|
||||
SizeT ARMT_Convert(Byte *data, SizeT size, UInt32 ip, int encoding)
|
||||
{
|
||||
size_t i;
|
||||
if (size < 4)
|
||||
return 0;
|
||||
size -= 4;
|
||||
ip += 4;
|
||||
for (i = 0; i <= size; i += 2)
|
||||
{
|
||||
if ((data[i + 1] & 0xF8) == 0xF0 &&
|
||||
(data[i + 3] & 0xF8) == 0xF8)
|
||||
Byte *p;
|
||||
const Byte *lim;
|
||||
size &= ~(size_t)1;
|
||||
p = data;
|
||||
lim = data + size - 4;
|
||||
|
||||
if (encoding)
|
||||
|
||||
for (;;)
|
||||
{
|
||||
UInt32 b1;
|
||||
for (;;)
|
||||
{
|
||||
UInt32 b3;
|
||||
if (p > lim)
|
||||
return p - data;
|
||||
b1 = p[1];
|
||||
b3 = p[3];
|
||||
p += 2;
|
||||
b1 ^= 8;
|
||||
if ((b3 & b1) >= 0xF8)
|
||||
break;
|
||||
}
|
||||
{
|
||||
UInt32 v =
|
||||
((UInt32)b1 << 19)
|
||||
+ (((UInt32)p[1] & 0x7) << 8)
|
||||
+ (((UInt32)p[-2] << 11))
|
||||
+ (p[0]);
|
||||
|
||||
p += 2;
|
||||
{
|
||||
uint32_t dest;
|
||||
uint32_t src =
|
||||
(((uint32_t)data[i + 1] & 0x7) << 19) |
|
||||
((uint32_t)data[i + 0] << 11) |
|
||||
(((uint32_t)data[i + 3] & 0x7) << 8) |
|
||||
(data[i + 2]);
|
||||
|
||||
src <<= 1;
|
||||
if (encoding)
|
||||
dest = ip + (uint32_t)i + src;
|
||||
else
|
||||
dest = src - (ip + (uint32_t)i);
|
||||
dest >>= 1;
|
||||
|
||||
data[i + 1] = (uint8_t)(0xF0 | ((dest >> 19) & 0x7));
|
||||
data[i + 0] = (uint8_t)(dest >> 11);
|
||||
data[i + 3] = (uint8_t)(0xF8 | ((dest >> 8) & 0x7));
|
||||
data[i + 2] = (uint8_t)dest;
|
||||
i += 2;
|
||||
UInt32 cur = (ip + (UInt32)(p - data)) >> 1;
|
||||
v += cur;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
|
||||
p[-4] = (Byte)(v >> 11);
|
||||
p[-3] = (Byte)(0xF0 | ((v >> 19) & 0x7));
|
||||
p[-2] = (Byte)v;
|
||||
p[-1] = (Byte)(0xF8 | (v >> 8));
|
||||
}
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
UInt32 b1;
|
||||
for (;;)
|
||||
{
|
||||
UInt32 b3;
|
||||
if (p > lim)
|
||||
return p - data;
|
||||
b1 = p[1];
|
||||
b3 = p[3];
|
||||
p += 2;
|
||||
b1 ^= 8;
|
||||
if ((b3 & b1) >= 0xF8)
|
||||
break;
|
||||
}
|
||||
{
|
||||
UInt32 v =
|
||||
((UInt32)b1 << 19)
|
||||
+ (((UInt32)p[1] & 0x7) << 8)
|
||||
+ (((UInt32)p[-2] << 11))
|
||||
+ (p[0]);
|
||||
|
||||
p += 2;
|
||||
{
|
||||
UInt32 cur = (ip + (UInt32)(p - data)) >> 1;
|
||||
v -= cur;
|
||||
}
|
||||
|
||||
/*
|
||||
SetUi16(p - 4, (UInt16)(((v >> 11) & 0x7FF) | 0xF000));
|
||||
SetUi16(p - 2, (UInt16)(v | 0xF800));
|
||||
*/
|
||||
|
||||
p[-4] = (Byte)(v >> 11);
|
||||
p[-3] = (Byte)(0xF0 | ((v >> 19) & 0x7));
|
||||
p[-2] = (Byte)v;
|
||||
p[-1] = (Byte)(0xF8 | (v >> 8));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t PPC_Convert(uint8_t *data, size_t size, uint32_t ip, int encoding)
|
||||
{
|
||||
size_t i;
|
||||
if (size < 4)
|
||||
return 0;
|
||||
size -= 4;
|
||||
for (i = 0; i <= size; i += 4)
|
||||
{
|
||||
if ((data[i] >> 2) == 0x12 && (data[i + 3] & 3) == 1)
|
||||
{
|
||||
uint32_t src = ((uint32_t)(data[i + 0] & 3) << 24) |
|
||||
((uint32_t)data[i + 1] << 16) |
|
||||
((uint32_t)data[i + 2] << 8) |
|
||||
((uint32_t)data[i + 3] & (~3));
|
||||
|
||||
uint32_t dest;
|
||||
if (encoding)
|
||||
dest = ip + (uint32_t)i + src;
|
||||
else
|
||||
dest = src - (ip + (uint32_t)i);
|
||||
data[i + 0] = (uint8_t)(0x48 | ((dest >> 24) & 0x3));
|
||||
data[i + 1] = (uint8_t)(dest >> 16);
|
||||
data[i + 2] = (uint8_t)(dest >> 8);
|
||||
data[i + 3] &= 0x3;
|
||||
data[i + 3] |= dest;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
SizeT PPC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding)
|
||||
{
|
||||
Byte *p;
|
||||
const Byte *lim;
|
||||
size &= ~(size_t)3;
|
||||
ip -= 4;
|
||||
p = data;
|
||||
lim = data + size;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
if (p >= lim)
|
||||
return p - data;
|
||||
p += 4;
|
||||
/* if ((v & 0xFC000003) == 0x48000001) */
|
||||
if ((p[-4] & 0xFC) == 0x48 && (p[-1] & 3) == 1)
|
||||
break;
|
||||
}
|
||||
{
|
||||
UInt32 v = GetBe32(p - 4);
|
||||
if (encoding)
|
||||
v += ip + (UInt32)(p - data);
|
||||
else
|
||||
v -= ip + (UInt32)(p - data);
|
||||
v &= 0x03FFFFFF;
|
||||
v |= 0x48000000;
|
||||
SetBe32(p - 4, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t SPARC_Convert(uint8_t *data, size_t size, uint32_t ip, int encoding)
|
||||
|
||||
SizeT SPARC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding)
|
||||
{
|
||||
uint32_t i;
|
||||
if (size < 4)
|
||||
return 0;
|
||||
size -= 4;
|
||||
for (i = 0; i <= size; i += 4)
|
||||
{
|
||||
if ((data[i] == 0x40 && (data[i + 1] & 0xC0) == 0x00) ||
|
||||
(data[i] == 0x7F && (data[i + 1] & 0xC0) == 0xC0))
|
||||
{
|
||||
uint32_t src =
|
||||
((uint32_t)data[i + 0] << 24) |
|
||||
((uint32_t)data[i + 1] << 16) |
|
||||
((uint32_t)data[i + 2] << 8) |
|
||||
((uint32_t)data[i + 3]);
|
||||
uint32_t dest;
|
||||
Byte *p;
|
||||
const Byte *lim;
|
||||
size &= ~(size_t)3;
|
||||
ip -= 4;
|
||||
p = data;
|
||||
lim = data + size;
|
||||
|
||||
src <<= 2;
|
||||
if (encoding)
|
||||
dest = ip + i + src;
|
||||
else
|
||||
dest = src - (ip + i);
|
||||
dest >>= 2;
|
||||
|
||||
dest = (((0 - ((dest >> 22) & 1)) << 22) & 0x3FFFFFFF) | (dest & 0x3FFFFF) | 0x40000000;
|
||||
|
||||
data[i + 0] = (uint8_t)(dest >> 24);
|
||||
data[i + 1] = (uint8_t)(dest >> 16);
|
||||
data[i + 2] = (uint8_t)(dest >> 8);
|
||||
data[i + 3] = (uint8_t)dest;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
for (;;)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
if (p >= lim)
|
||||
return p - data;
|
||||
/*
|
||||
v = GetBe32(p);
|
||||
p += 4;
|
||||
m = v + ((UInt32)5 << 29);
|
||||
m ^= (UInt32)7 << 29;
|
||||
m += (UInt32)1 << 22;
|
||||
if ((m & ((UInt32)0x1FF << 23)) == 0)
|
||||
break;
|
||||
*/
|
||||
p += 4;
|
||||
if ((p[-4] == 0x40 && (p[-3] & 0xC0) == 0) ||
|
||||
(p[-4] == 0x7F && (p[-3] >= 0xC0)))
|
||||
break;
|
||||
}
|
||||
{
|
||||
UInt32 v = GetBe32(p - 4);
|
||||
v <<= 2;
|
||||
if (encoding)
|
||||
v += ip + (UInt32)(p - data);
|
||||
else
|
||||
v -= ip + (UInt32)(p - data);
|
||||
|
||||
v &= 0x01FFFFFF;
|
||||
v -= (UInt32)1 << 24;
|
||||
v ^= 0xFF000000;
|
||||
v >>= 2;
|
||||
v |= 0x40000000;
|
||||
SetBe32(p - 4, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+46
-50
@@ -1,68 +1,64 @@
|
||||
/* Bra.h -- Branch converters for executables
|
||||
2009-02-07 : Igor Pavlov : Public domain */
|
||||
2013-01-18 : Igor Pavlov : Public domain */
|
||||
|
||||
#ifndef __BRA_H
|
||||
#define __BRA_H
|
||||
|
||||
#include "7zTypes.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
EXTERN_C_BEGIN
|
||||
|
||||
/*
|
||||
These functions convert relative addresses to absolute addresses
|
||||
in CALL instructions to increase the compression ratio.
|
||||
/*
|
||||
These functions convert relative addresses to absolute addresses
|
||||
in CALL instructions to increase the compression ratio.
|
||||
|
||||
In:
|
||||
data - data buffer
|
||||
size - size of data
|
||||
ip - current virtual Instruction Pinter (IP) value
|
||||
state - state variable for x86 converter
|
||||
encoding - 0 (for decoding), 1 (for encoding)
|
||||
|
||||
Out:
|
||||
state - state variable for x86 converter
|
||||
|
||||
In:
|
||||
data - data buffer
|
||||
size - size of data
|
||||
ip - current virtual Instruction Pinter (IP) value
|
||||
state - state variable for x86 converter
|
||||
encoding - 0 (for decoding), 1 (for encoding)
|
||||
Returns:
|
||||
The number of processed bytes. If you call these functions with multiple calls,
|
||||
you must start next call with first byte after block of processed bytes.
|
||||
|
||||
Type Endian Alignment LookAhead
|
||||
|
||||
x86 little 1 4
|
||||
ARMT little 2 2
|
||||
ARM little 4 0
|
||||
PPC big 4 0
|
||||
SPARC big 4 0
|
||||
IA64 little 16 0
|
||||
|
||||
Out:
|
||||
state - state variable for x86 converter
|
||||
size must be >= Alignment + LookAhead, if it's not last block.
|
||||
If (size < Alignment + LookAhead), converter returns 0.
|
||||
|
||||
Returns:
|
||||
The number of processed bytes. If you call these functions with multiple calls,
|
||||
you must start next call with first byte after block of processed bytes.
|
||||
Example:
|
||||
|
||||
Type Endian Alignment LookAhead
|
||||
|
||||
x86 little 1 4
|
||||
ARMT little 2 2
|
||||
ARM little 4 0
|
||||
PPC big 4 0
|
||||
SPARC big 4 0
|
||||
IA64 little 16 0
|
||||
|
||||
size must be >= Alignment + LookAhead, if it's not last block.
|
||||
If (size < Alignment + LookAhead), converter returns 0.
|
||||
|
||||
Example:
|
||||
|
||||
uint32_t ip = 0;
|
||||
for ()
|
||||
{
|
||||
; size must be >= Alignment + LookAhead, if it's not last block
|
||||
size_t processed = Convert(data, size, ip, 1);
|
||||
data += processed;
|
||||
size -= processed;
|
||||
ip += processed;
|
||||
}
|
||||
UInt32 ip = 0;
|
||||
for ()
|
||||
{
|
||||
; size must be >= Alignment + LookAhead, if it's not last block
|
||||
SizeT processed = Convert(data, size, ip, 1);
|
||||
data += processed;
|
||||
size -= processed;
|
||||
ip += processed;
|
||||
}
|
||||
*/
|
||||
|
||||
#define x86_Convert_Init(state) { state = 0; }
|
||||
size_t x86_Convert(uint8_t *data, size_t size, uint32_t ip, uint32_t *state, int encoding);
|
||||
size_t ARM_Convert(uint8_t *data, size_t size, uint32_t ip, int encoding);
|
||||
size_t ARMT_Convert(uint8_t *data, size_t size, uint32_t ip, int encoding);
|
||||
size_t PPC_Convert(uint8_t *data, size_t size, uint32_t ip, int encoding);
|
||||
size_t SPARC_Convert(uint8_t *data, size_t size, uint32_t ip, int encoding);
|
||||
size_t IA64_Convert(uint8_t *data, size_t size, uint32_t ip, int encoding);
|
||||
SizeT x86_Convert(Byte *data, SizeT size, UInt32 ip, UInt32 *state, int encoding);
|
||||
SizeT ARM_Convert(Byte *data, SizeT size, UInt32 ip, int encoding);
|
||||
SizeT ARMT_Convert(Byte *data, SizeT size, UInt32 ip, int encoding);
|
||||
SizeT PPC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding);
|
||||
SizeT SPARC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding);
|
||||
SizeT IA64_Convert(Byte *data, SizeT size, UInt32 ip, int encoding);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
EXTERN_C_END
|
||||
|
||||
#endif
|
||||
Vendored
+66
-70
@@ -1,86 +1,82 @@
|
||||
/* Bra86.c -- Converter for x86 code (BCJ)
|
||||
2008-10-04 : Igor Pavlov : Public domain */
|
||||
2017-04-03 : Igor Pavlov : Public domain */
|
||||
|
||||
#include "Precomp.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include "Bra.h"
|
||||
|
||||
#define Test86MSuint8_t(b) ((b) == 0 || (b) == 0xFF)
|
||||
#define Test86MSByte(b) ((((b) + 1) & 0xFE) == 0)
|
||||
|
||||
const uint8_t kMaskToAllowedStatus[8] = {1, 1, 1, 0, 1, 0, 0, 0};
|
||||
const uint8_t kMaskToBitNumber[8] = {0, 1, 2, 2, 3, 3, 3, 3};
|
||||
|
||||
size_t x86_Convert(uint8_t *data, size_t size, uint32_t ip, uint32_t *state, int encoding)
|
||||
SizeT x86_Convert(Byte *data, SizeT size, UInt32 ip, UInt32 *state, int encoding)
|
||||
{
|
||||
size_t bufferPos = 0, prevPosT;
|
||||
uint32_t prevMask = *state & 0x7;
|
||||
if (size < 5)
|
||||
return 0;
|
||||
ip += 5;
|
||||
prevPosT = (size_t)0 - 1;
|
||||
SizeT pos = 0;
|
||||
UInt32 mask = *state & 7;
|
||||
if (size < 5)
|
||||
return 0;
|
||||
size -= 4;
|
||||
ip += 5;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
uint8_t *p = data + bufferPos;
|
||||
uint8_t *limit = data + size - 4;
|
||||
for (; p < limit; p++)
|
||||
if ((*p & 0xFE) == 0xE8)
|
||||
break;
|
||||
bufferPos = (size_t)(p - data);
|
||||
for (;;)
|
||||
{
|
||||
Byte *p = data + pos;
|
||||
const Byte *limit = data + size;
|
||||
for (; p < limit; p++)
|
||||
if ((*p & 0xFE) == 0xE8)
|
||||
break;
|
||||
|
||||
{
|
||||
SizeT d = (SizeT)(p - data - pos);
|
||||
pos = (SizeT)(p - data);
|
||||
if (p >= limit)
|
||||
break;
|
||||
prevPosT = bufferPos - prevPosT;
|
||||
if (prevPosT > 3)
|
||||
prevMask = 0;
|
||||
{
|
||||
*state = (d > 2 ? 0 : mask >> (unsigned)d);
|
||||
return pos;
|
||||
}
|
||||
if (d > 2)
|
||||
mask = 0;
|
||||
else
|
||||
{
|
||||
prevMask = (prevMask << ((int)prevPosT - 1)) & 0x7;
|
||||
if (prevMask != 0)
|
||||
{
|
||||
uint8_t b = p[4 - kMaskToBitNumber[prevMask]];
|
||||
if (!kMaskToAllowedStatus[prevMask] || Test86MSuint8_t(b))
|
||||
{
|
||||
prevPosT = bufferPos;
|
||||
prevMask = ((prevMask << 1) & 0x7) | 1;
|
||||
bufferPos++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
mask >>= (unsigned)d;
|
||||
if (mask != 0 && (mask > 4 || mask == 3 || Test86MSByte(p[(size_t)(mask >> 1) + 1])))
|
||||
{
|
||||
mask = (mask >> 1) | 4;
|
||||
pos++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
prevPosT = bufferPos;
|
||||
}
|
||||
|
||||
if (Test86MSuint8_t(p[4]))
|
||||
{
|
||||
uint32_t src = ((uint32_t)p[4] << 24) | ((uint32_t)p[3] << 16) | ((uint32_t)p[2] << 8) | ((uint32_t)p[1]);
|
||||
uint32_t dest;
|
||||
for (;;)
|
||||
{
|
||||
uint8_t b;
|
||||
int idx;
|
||||
if (encoding)
|
||||
dest = (ip + (uint32_t)bufferPos) + src;
|
||||
else
|
||||
dest = src - (ip + (uint32_t)bufferPos);
|
||||
if (prevMask == 0)
|
||||
break;
|
||||
idx = kMaskToBitNumber[prevMask] * 8;
|
||||
b = (uint8_t)(dest >> (24 - idx));
|
||||
if (!Test86MSuint8_t(b))
|
||||
break;
|
||||
src = dest ^ ((1 << (32 - idx)) - 1);
|
||||
}
|
||||
p[4] = (uint8_t)(~(((dest >> 24) & 1) - 1));
|
||||
p[3] = (uint8_t)(dest >> 16);
|
||||
p[2] = (uint8_t)(dest >> 8);
|
||||
p[1] = (uint8_t)dest;
|
||||
bufferPos += 5;
|
||||
}
|
||||
if (Test86MSByte(p[4]))
|
||||
{
|
||||
UInt32 v = ((UInt32)p[4] << 24) | ((UInt32)p[3] << 16) | ((UInt32)p[2] << 8) | ((UInt32)p[1]);
|
||||
UInt32 cur = ip + (UInt32)pos;
|
||||
pos += 5;
|
||||
if (encoding)
|
||||
v += cur;
|
||||
else
|
||||
v -= cur;
|
||||
if (mask != 0)
|
||||
{
|
||||
prevMask = ((prevMask << 1) & 0x7) | 1;
|
||||
bufferPos++;
|
||||
unsigned sh = (mask & 6) << 2;
|
||||
if (Test86MSByte((Byte)(v >> sh)))
|
||||
{
|
||||
v ^= (((UInt32)0x100 << sh) - 1);
|
||||
if (encoding)
|
||||
v += cur;
|
||||
else
|
||||
v -= cur;
|
||||
}
|
||||
mask = 0;
|
||||
}
|
||||
}
|
||||
prevPosT = bufferPos - prevPosT;
|
||||
*state = ((prevPosT > 3) ? 0 : ((prevMask << ((int)prevPosT - 1)) & 0x7));
|
||||
return bufferPos;
|
||||
p[1] = (Byte)v;
|
||||
p[2] = (Byte)(v >> 8);
|
||||
p[3] = (Byte)(v >> 16);
|
||||
p[4] = (Byte)(0 - ((v >> 24) & 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
mask = (mask >> 1) | 4;
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+53
@@ -0,0 +1,53 @@
|
||||
/* BraIA64.c -- Converter for IA-64 code
|
||||
2017-01-26 : Igor Pavlov : Public domain */
|
||||
|
||||
#include "Precomp.h"
|
||||
|
||||
#include "CpuArch.h"
|
||||
#include "Bra.h"
|
||||
|
||||
SizeT IA64_Convert(Byte *data, SizeT size, UInt32 ip, int encoding)
|
||||
{
|
||||
SizeT i;
|
||||
if (size < 16)
|
||||
return 0;
|
||||
size -= 16;
|
||||
i = 0;
|
||||
do
|
||||
{
|
||||
unsigned m = ((UInt32)0x334B0000 >> (data[i] & 0x1E)) & 3;
|
||||
if (m)
|
||||
{
|
||||
m++;
|
||||
do
|
||||
{
|
||||
Byte *p = data + (i + (size_t)m * 5 - 8);
|
||||
if (((p[3] >> m) & 15) == 5
|
||||
&& (((p[-1] | ((UInt32)p[0] << 8)) >> m) & 0x70) == 0)
|
||||
{
|
||||
unsigned raw = GetUi32(p);
|
||||
unsigned v = raw >> m;
|
||||
v = (v & 0xFFFFF) | ((v & (1 << 23)) >> 3);
|
||||
|
||||
v <<= 4;
|
||||
if (encoding)
|
||||
v += ip + (UInt32)i;
|
||||
else
|
||||
v -= ip + (UInt32)i;
|
||||
v >>= 4;
|
||||
|
||||
v &= 0x1FFFFF;
|
||||
v += 0x700000;
|
||||
v &= 0x8FFFFF;
|
||||
raw &= ~((UInt32)0x8FFFFF << m);
|
||||
raw |= (v << m);
|
||||
SetUi32(p, raw);
|
||||
}
|
||||
}
|
||||
while (++m <= 4);
|
||||
}
|
||||
i += 16;
|
||||
}
|
||||
while (i <= size);
|
||||
return i;
|
||||
}
|
||||
Vendored
+33
@@ -0,0 +1,33 @@
|
||||
/* Compiler.h
|
||||
2017-04-03 : Igor Pavlov : Public domain */
|
||||
|
||||
#ifndef __7Z_COMPILER_H
|
||||
#define __7Z_COMPILER_H
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
#ifdef UNDER_CE
|
||||
#define RPC_NO_WINDOWS_H
|
||||
/* #pragma warning(disable : 4115) // '_RPC_ASYNC_STATE' : named type definition in parentheses */
|
||||
#pragma warning(disable : 4201) /* nonstandard extension used : nameless struct/union */
|
||||
#pragma warning(disable : 4214) /* nonstandard extension used : bit field types other than int */
|
||||
#endif
|
||||
|
||||
#if _MSC_VER >= 1300
|
||||
#pragma warning(disable : 4996) /* This function or variable may be unsafe */
|
||||
#else
|
||||
#pragma warning(disable : 4511) /* copy constructor could not be generated */
|
||||
#pragma warning(disable : 4512) /* assignment operator could not be generated */
|
||||
#pragma warning(disable : 4514) /* unreferenced inline function has been removed */
|
||||
#pragma warning(disable : 4702) /* unreachable code */
|
||||
#pragma warning(disable : 4710) /* not inlined */
|
||||
#pragma warning(disable : 4714) /* function marked as __forceinline not inlined */
|
||||
#pragma warning(disable : 4786) /* identifier was truncated to '255' characters in the debug information */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#define UNUSED_VAR(x) (void)x;
|
||||
/* #define UNUSED_VAR(x) x=x; */
|
||||
|
||||
#endif
|
||||
Vendored
+218
@@ -0,0 +1,218 @@
|
||||
/* CpuArch.c -- CPU specific code
|
||||
2018-02-18: Igor Pavlov : Public domain */
|
||||
|
||||
#include "Precomp.h"
|
||||
|
||||
#include "CpuArch.h"
|
||||
|
||||
#ifdef MY_CPU_X86_OR_AMD64
|
||||
|
||||
#if (defined(_MSC_VER) && !defined(MY_CPU_AMD64)) || defined(__GNUC__)
|
||||
#define USE_ASM
|
||||
#endif
|
||||
|
||||
#if !defined(USE_ASM) && _MSC_VER >= 1500
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
#if defined(USE_ASM) && !defined(MY_CPU_AMD64)
|
||||
static UInt32 CheckFlag(UInt32 flag)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
__asm pushfd;
|
||||
__asm pop EAX;
|
||||
__asm mov EDX, EAX;
|
||||
__asm xor EAX, flag;
|
||||
__asm push EAX;
|
||||
__asm popfd;
|
||||
__asm pushfd;
|
||||
__asm pop EAX;
|
||||
__asm xor EAX, EDX;
|
||||
__asm push EDX;
|
||||
__asm popfd;
|
||||
__asm and flag, EAX;
|
||||
#else
|
||||
__asm__ __volatile__ (
|
||||
"pushf\n\t"
|
||||
"pop %%EAX\n\t"
|
||||
"movl %%EAX,%%EDX\n\t"
|
||||
"xorl %0,%%EAX\n\t"
|
||||
"push %%EAX\n\t"
|
||||
"popf\n\t"
|
||||
"pushf\n\t"
|
||||
"pop %%EAX\n\t"
|
||||
"xorl %%EDX,%%EAX\n\t"
|
||||
"push %%EDX\n\t"
|
||||
"popf\n\t"
|
||||
"andl %%EAX, %0\n\t":
|
||||
"=c" (flag) : "c" (flag) :
|
||||
"%eax", "%edx");
|
||||
#endif
|
||||
return flag;
|
||||
}
|
||||
#define CHECK_CPUID_IS_SUPPORTED if (CheckFlag(1 << 18) == 0 || CheckFlag(1 << 21) == 0) return False;
|
||||
#else
|
||||
#define CHECK_CPUID_IS_SUPPORTED
|
||||
#endif
|
||||
|
||||
void MyCPUID(UInt32 function, UInt32 *a, UInt32 *b, UInt32 *c, UInt32 *d)
|
||||
{
|
||||
#ifdef USE_ASM
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
UInt32 a2, b2, c2, d2;
|
||||
__asm xor EBX, EBX;
|
||||
__asm xor ECX, ECX;
|
||||
__asm xor EDX, EDX;
|
||||
__asm mov EAX, function;
|
||||
__asm cpuid;
|
||||
__asm mov a2, EAX;
|
||||
__asm mov b2, EBX;
|
||||
__asm mov c2, ECX;
|
||||
__asm mov d2, EDX;
|
||||
|
||||
*a = a2;
|
||||
*b = b2;
|
||||
*c = c2;
|
||||
*d = d2;
|
||||
|
||||
#else
|
||||
|
||||
__asm__ __volatile__ (
|
||||
#if defined(MY_CPU_AMD64) && defined(__PIC__)
|
||||
"mov %%rbx, %%rdi;"
|
||||
"cpuid;"
|
||||
"xchg %%rbx, %%rdi;"
|
||||
: "=a" (*a) ,
|
||||
"=D" (*b) ,
|
||||
#elif defined(MY_CPU_X86) && defined(__PIC__)
|
||||
"mov %%ebx, %%edi;"
|
||||
"cpuid;"
|
||||
"xchgl %%ebx, %%edi;"
|
||||
: "=a" (*a) ,
|
||||
"=D" (*b) ,
|
||||
#else
|
||||
"cpuid"
|
||||
: "=a" (*a) ,
|
||||
"=b" (*b) ,
|
||||
#endif
|
||||
"=c" (*c) ,
|
||||
"=d" (*d)
|
||||
: "0" (function)) ;
|
||||
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
int CPUInfo[4];
|
||||
__cpuid(CPUInfo, function);
|
||||
*a = CPUInfo[0];
|
||||
*b = CPUInfo[1];
|
||||
*c = CPUInfo[2];
|
||||
*d = CPUInfo[3];
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
BoolInt x86cpuid_CheckAndRead(Cx86cpuid *p)
|
||||
{
|
||||
CHECK_CPUID_IS_SUPPORTED
|
||||
MyCPUID(0, &p->maxFunc, &p->vendor[0], &p->vendor[2], &p->vendor[1]);
|
||||
MyCPUID(1, &p->ver, &p->b, &p->c, &p->d);
|
||||
return True;
|
||||
}
|
||||
|
||||
static const UInt32 kVendors[][3] =
|
||||
{
|
||||
{ 0x756E6547, 0x49656E69, 0x6C65746E},
|
||||
{ 0x68747541, 0x69746E65, 0x444D4163},
|
||||
{ 0x746E6543, 0x48727561, 0x736C7561}
|
||||
};
|
||||
|
||||
int x86cpuid_GetFirm(const Cx86cpuid *p)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < sizeof(kVendors) / sizeof(kVendors[i]); i++)
|
||||
{
|
||||
const UInt32 *v = kVendors[i];
|
||||
if (v[0] == p->vendor[0] &&
|
||||
v[1] == p->vendor[1] &&
|
||||
v[2] == p->vendor[2])
|
||||
return (int)i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
BoolInt CPU_Is_InOrder()
|
||||
{
|
||||
Cx86cpuid p;
|
||||
int firm;
|
||||
UInt32 family, model;
|
||||
if (!x86cpuid_CheckAndRead(&p))
|
||||
return True;
|
||||
|
||||
family = x86cpuid_GetFamily(p.ver);
|
||||
model = x86cpuid_GetModel(p.ver);
|
||||
|
||||
firm = x86cpuid_GetFirm(&p);
|
||||
|
||||
switch (firm)
|
||||
{
|
||||
case CPU_FIRM_INTEL: return (family < 6 || (family == 6 && (
|
||||
/* In-Order Atom CPU */
|
||||
model == 0x1C /* 45 nm, N4xx, D4xx, N5xx, D5xx, 230, 330 */
|
||||
|| model == 0x26 /* 45 nm, Z6xx */
|
||||
|| model == 0x27 /* 32 nm, Z2460 */
|
||||
|| model == 0x35 /* 32 nm, Z2760 */
|
||||
|| model == 0x36 /* 32 nm, N2xxx, D2xxx */
|
||||
)));
|
||||
case CPU_FIRM_AMD: return (family < 5 || (family == 5 && (model < 6 || model == 0xA)));
|
||||
case CPU_FIRM_VIA: return (family < 6 || (family == 6 && model < 0xF));
|
||||
}
|
||||
return True;
|
||||
}
|
||||
|
||||
#if !defined(MY_CPU_AMD64) && defined(_WIN32)
|
||||
#include <windows.h>
|
||||
static BoolInt CPU_Sys_Is_SSE_Supported()
|
||||
{
|
||||
OSVERSIONINFO vi;
|
||||
vi.dwOSVersionInfoSize = sizeof(vi);
|
||||
if (!GetVersionEx(&vi))
|
||||
return False;
|
||||
return (vi.dwMajorVersion >= 5);
|
||||
}
|
||||
#define CHECK_SYS_SSE_SUPPORT if (!CPU_Sys_Is_SSE_Supported()) return False;
|
||||
#else
|
||||
#define CHECK_SYS_SSE_SUPPORT
|
||||
#endif
|
||||
|
||||
BoolInt CPU_Is_Aes_Supported()
|
||||
{
|
||||
Cx86cpuid p;
|
||||
CHECK_SYS_SSE_SUPPORT
|
||||
if (!x86cpuid_CheckAndRead(&p))
|
||||
return False;
|
||||
return (p.c >> 25) & 1;
|
||||
}
|
||||
|
||||
BoolInt CPU_IsSupported_PageGB()
|
||||
{
|
||||
Cx86cpuid cpuid;
|
||||
if (!x86cpuid_CheckAndRead(&cpuid))
|
||||
return False;
|
||||
{
|
||||
UInt32 d[4] = { 0 };
|
||||
MyCPUID(0x80000000, &d[0], &d[1], &d[2], &d[3]);
|
||||
if (d[0] < 0x80000001)
|
||||
return False;
|
||||
}
|
||||
{
|
||||
UInt32 d[4] = { 0 };
|
||||
MyCPUID(0x80000001, &d[0], &d[1], &d[2], &d[3]);
|
||||
return (d[3] >> 26) & 1;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
Vendored
+309
-29
@@ -1,56 +1,336 @@
|
||||
/* CpuArch.h -- CPU specific code
|
||||
2010-10-26: Igor Pavlov : Public domain */
|
||||
2018-02-18 : Igor Pavlov : Public domain */
|
||||
|
||||
#ifndef __CPU_ARCH_H
|
||||
#define __CPU_ARCH_H
|
||||
|
||||
#include <retro_endianness.h>
|
||||
#include "7zTypes.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
EXTERN_C_BEGIN
|
||||
|
||||
/* MY_CPU_LE_UNALIGN means that CPU is LITTLE ENDIAN and
|
||||
CPU supports unaligned memory accesses.
|
||||
If MY_CPU_LE_UNALIGN is not defined, we don't know
|
||||
about these properties of platform.
|
||||
/*
|
||||
MY_CPU_LE means that CPU is LITTLE ENDIAN.
|
||||
MY_CPU_BE means that CPU is BIG ENDIAN.
|
||||
If MY_CPU_LE and MY_CPU_BE are not defined, we don't know about ENDIANNESS of platform.
|
||||
|
||||
MY_CPU_LE_UNALIGN means that CPU is LITTLE ENDIAN and CPU supports unaligned memory accesses.
|
||||
*/
|
||||
|
||||
#if defined(_M_X64) || defined(_M_AMD64) || defined(__x86_64__)
|
||||
#define MY_CPU_AMD64
|
||||
#if defined(_M_X64) \
|
||||
|| defined(_M_AMD64) \
|
||||
|| defined(__x86_64__) \
|
||||
|| defined(__AMD64__) \
|
||||
|| defined(__amd64__)
|
||||
#define MY_CPU_AMD64
|
||||
#ifdef __ILP32__
|
||||
#define MY_CPU_NAME "x32"
|
||||
#else
|
||||
#define MY_CPU_NAME "x64"
|
||||
#endif
|
||||
#define MY_CPU_64BIT
|
||||
#endif
|
||||
|
||||
#if defined(MY_CPU_AMD64) || defined(_M_IA64)
|
||||
#define MY_CPU_64BIT
|
||||
|
||||
#if defined(_M_IX86) \
|
||||
|| defined(__i386__)
|
||||
#define MY_CPU_X86
|
||||
#define MY_CPU_NAME "x86"
|
||||
#define MY_CPU_32BIT
|
||||
#endif
|
||||
|
||||
#if defined(_M_IX86) || defined(__i386__)
|
||||
#define MY_CPU_X86
|
||||
|
||||
#if defined(_M_ARM64) \
|
||||
|| defined(__AARCH64EL__) \
|
||||
|| defined(__AARCH64EB__) \
|
||||
|| defined(__aarch64__)
|
||||
#define MY_CPU_ARM64
|
||||
#define MY_CPU_NAME "arm64"
|
||||
#define MY_CPU_64BIT
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(_M_ARM) \
|
||||
|| defined(_M_ARM_NT) \
|
||||
|| defined(_M_ARMT) \
|
||||
|| defined(__arm__) \
|
||||
|| defined(__thumb__) \
|
||||
|| defined(__ARMEL__) \
|
||||
|| defined(__ARMEB__) \
|
||||
|| defined(__THUMBEL__) \
|
||||
|| defined(__THUMBEB__)
|
||||
#define MY_CPU_ARM
|
||||
#define MY_CPU_NAME "arm"
|
||||
#define MY_CPU_32BIT
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(_M_IA64) \
|
||||
|| defined(__ia64__)
|
||||
#define MY_CPU_IA64
|
||||
#define MY_CPU_NAME "ia64"
|
||||
#define MY_CPU_64BIT
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(__mips64) \
|
||||
|| defined(__mips64__) \
|
||||
|| (defined(__mips) && (__mips == 64 || __mips == 4 || __mips == 3))
|
||||
#define MY_CPU_NAME "mips64"
|
||||
#define MY_CPU_64BIT
|
||||
#elif defined(__mips__)
|
||||
#define MY_CPU_NAME "mips"
|
||||
/* #define MY_CPU_32BIT */
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(__ppc64__) \
|
||||
|| defined(__powerpc64__)
|
||||
#ifdef __ILP32__
|
||||
#define MY_CPU_NAME "ppc64-32"
|
||||
#else
|
||||
#define MY_CPU_NAME "ppc64"
|
||||
#endif
|
||||
#define MY_CPU_64BIT
|
||||
#elif defined(__ppc__) \
|
||||
|| defined(__powerpc__)
|
||||
#define MY_CPU_NAME "ppc"
|
||||
#define MY_CPU_32BIT
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(__sparc64__)
|
||||
#define MY_CPU_NAME "sparc64"
|
||||
#define MY_CPU_64BIT
|
||||
#elif defined(__sparc__)
|
||||
#define MY_CPU_NAME "sparc"
|
||||
/* #define MY_CPU_32BIT */
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(MY_CPU_X86) || defined(MY_CPU_AMD64)
|
||||
#define MY_CPU_X86_OR_AMD64
|
||||
#endif
|
||||
|
||||
#if defined(MY_CPU_X86) || defined(_M_ARM)
|
||||
#define MY_CPU_32BIT
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && defined(_M_ARM)
|
||||
#define MY_CPU_ARM_LE
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
|
||||
#if defined(_WIN32) && defined(_M_IA64)
|
||||
#define MY_CPU_IA64_LE
|
||||
#endif
|
||||
#ifdef MY_CPU_ARM
|
||||
#define MY_CPU_ARM_LE
|
||||
#endif
|
||||
|
||||
#if defined(MY_CPU_X86_OR_AMD64)
|
||||
#define MY_CPU_LE_UNALIGN
|
||||
#endif
|
||||
#ifdef MY_CPU_ARM64
|
||||
#define MY_CPU_ARM64_LE
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#ifdef _M_IA64
|
||||
#define MY_CPU_IA64_LE
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(MY_CPU_X86_OR_AMD64) \
|
||||
|| defined(MY_CPU_ARM_LE) \
|
||||
|| defined(MY_CPU_ARM64_LE) \
|
||||
|| defined(MY_CPU_IA64_LE) \
|
||||
|| defined(__LITTLE_ENDIAN__) \
|
||||
|| defined(__ARMEL__) \
|
||||
|| defined(__THUMBEL__) \
|
||||
|| defined(__AARCH64EL__) \
|
||||
|| defined(__MIPSEL__) \
|
||||
|| defined(__MIPSEL) \
|
||||
|| defined(_MIPSEL) \
|
||||
|| defined(__BFIN__) \
|
||||
|| (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
|
||||
#define MY_CPU_LE
|
||||
#endif
|
||||
|
||||
#if defined(__BIG_ENDIAN__) \
|
||||
|| defined(__ARMEB__) \
|
||||
|| defined(__THUMBEB__) \
|
||||
|| defined(__AARCH64EB__) \
|
||||
|| defined(__MIPSEB__) \
|
||||
|| defined(__MIPSEB) \
|
||||
|| defined(_MIPSEB) \
|
||||
|| defined(__m68k__) \
|
||||
|| defined(__s390__) \
|
||||
|| defined(__s390x__) \
|
||||
|| defined(__zarch__) \
|
||||
|| (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
|
||||
#define MY_CPU_BE
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(MY_CPU_LE) && defined(MY_CPU_BE)
|
||||
#error Stop_Compiling_Bad_Endian
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(MY_CPU_32BIT) && defined(MY_CPU_64BIT)
|
||||
#error Stop_Compiling_Bad_32_64_BIT
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef MY_CPU_NAME
|
||||
#ifdef MY_CPU_LE
|
||||
#define MY_CPU_NAME "LE"
|
||||
#elif defined(MY_CPU_BE)
|
||||
#define MY_CPU_NAME "BE"
|
||||
#else
|
||||
/*
|
||||
#define MY_CPU_NAME ""
|
||||
*/
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef MY_CPU_LE
|
||||
#if defined(MY_CPU_X86_OR_AMD64) \
|
||||
|| defined(MY_CPU_ARM64) \
|
||||
|| defined(__ARM_FEATURE_UNALIGNED)
|
||||
#define MY_CPU_LE_UNALIGN
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef MY_CPU_LE_UNALIGN
|
||||
|
||||
#define GetUi16(p) (*(const UInt16 *)(const void *)(p))
|
||||
#define GetUi32(p) (*(const UInt32 *)(const void *)(p))
|
||||
#define GetUi64(p) (*(const UInt64 *)(const void *)(p))
|
||||
|
||||
#define SetUi16(p, v) { *(UInt16 *)(p) = (v); }
|
||||
#define SetUi32(p, v) { *(UInt32 *)(p) = (v); }
|
||||
#define SetUi64(p, v) { *(UInt64 *)(p) = (v); }
|
||||
|
||||
#else
|
||||
|
||||
#define GetUi16(p) ( (UInt16) ( \
|
||||
((const Byte *)(p))[0] | \
|
||||
((UInt16)((const Byte *)(p))[1] << 8) ))
|
||||
|
||||
#define GetUi32(p) ( \
|
||||
((const Byte *)(p))[0] | \
|
||||
((UInt32)((const Byte *)(p))[1] << 8) | \
|
||||
((UInt32)((const Byte *)(p))[2] << 16) | \
|
||||
((UInt32)((const Byte *)(p))[3] << 24))
|
||||
|
||||
#define GetUi64(p) (GetUi32(p) | ((UInt64)GetUi32(((const Byte *)(p)) + 4) << 32))
|
||||
|
||||
#define SetUi16(p, v) { Byte *_ppp_ = (Byte *)(p); UInt32 _vvv_ = (v); \
|
||||
_ppp_[0] = (Byte)_vvv_; \
|
||||
_ppp_[1] = (Byte)(_vvv_ >> 8); }
|
||||
|
||||
#define SetUi32(p, v) { Byte *_ppp_ = (Byte *)(p); UInt32 _vvv_ = (v); \
|
||||
_ppp_[0] = (Byte)_vvv_; \
|
||||
_ppp_[1] = (Byte)(_vvv_ >> 8); \
|
||||
_ppp_[2] = (Byte)(_vvv_ >> 16); \
|
||||
_ppp_[3] = (Byte)(_vvv_ >> 24); }
|
||||
|
||||
#define SetUi64(p, v) { Byte *_ppp2_ = (Byte *)(p); UInt64 _vvv2_ = (v); \
|
||||
SetUi32(_ppp2_ , (UInt32)_vvv2_); \
|
||||
SetUi32(_ppp2_ + 4, (UInt32)(_vvv2_ >> 32)); }
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __has_builtin
|
||||
#define MY__has_builtin(x) __has_builtin(x)
|
||||
#else
|
||||
#define MY__has_builtin(x) 0
|
||||
#endif
|
||||
|
||||
#if defined(MY_CPU_LE_UNALIGN) && /* defined(_WIN64) && */ (_MSC_VER >= 1300)
|
||||
|
||||
/* Note: we use bswap instruction, that is unsupported in 386 cpu */
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#pragma intrinsic(_byteswap_ushort)
|
||||
#pragma intrinsic(_byteswap_ulong)
|
||||
#pragma intrinsic(_byteswap_uint64)
|
||||
|
||||
/* #define GetBe16(p) _byteswap_ushort(*(const UInt16 *)(const Byte *)(p)) */
|
||||
#define GetBe32(p) _byteswap_ulong(*(const UInt32 *)(const Byte *)(p))
|
||||
#define GetBe64(p) _byteswap_uint64(*(const UInt64 *)(const Byte *)(p))
|
||||
|
||||
#define SetBe32(p, v) (*(UInt32 *)(void *)(p)) = _byteswap_ulong(v)
|
||||
|
||||
#elif defined(MY_CPU_LE_UNALIGN) && ( \
|
||||
(defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) \
|
||||
|| (defined(__clang__) && MY__has_builtin(__builtin_bswap16)) )
|
||||
|
||||
/* #define GetBe16(p) __builtin_bswap16(*(const UInt16 *)(const Byte *)(p)) */
|
||||
#define GetBe32(p) __builtin_bswap32(*(const UInt32 *)(const Byte *)(p))
|
||||
#define GetBe64(p) __builtin_bswap64(*(const UInt64 *)(const Byte *)(p))
|
||||
|
||||
#define SetBe32(p, v) (*(UInt32 *)(void *)(p)) = __builtin_bswap32(v)
|
||||
|
||||
#else
|
||||
|
||||
#define GetBe32(p) ( \
|
||||
((UInt32)((const Byte *)(p))[0] << 24) | \
|
||||
((UInt32)((const Byte *)(p))[1] << 16) | \
|
||||
((UInt32)((const Byte *)(p))[2] << 8) | \
|
||||
((const Byte *)(p))[3] )
|
||||
|
||||
#define GetBe64(p) (((UInt64)GetBe32(p) << 32) | GetBe32(((const Byte *)(p)) + 4))
|
||||
|
||||
#define SetBe32(p, v) { Byte *_ppp_ = (Byte *)(p); UInt32 _vvv_ = (v); \
|
||||
_ppp_[0] = (Byte)(_vvv_ >> 24); \
|
||||
_ppp_[1] = (Byte)(_vvv_ >> 16); \
|
||||
_ppp_[2] = (Byte)(_vvv_ >> 8); \
|
||||
_ppp_[3] = (Byte)_vvv_; }
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef GetBe16
|
||||
|
||||
#define GetBe16(p) ( (UInt16) ( \
|
||||
((UInt16)((const Byte *)(p))[0] << 8) | \
|
||||
((const Byte *)(p))[1] ))
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef MY_CPU_X86_OR_AMD64
|
||||
|
||||
typedef struct
|
||||
{
|
||||
UInt32 maxFunc;
|
||||
UInt32 vendor[3];
|
||||
UInt32 ver;
|
||||
UInt32 b;
|
||||
UInt32 c;
|
||||
UInt32 d;
|
||||
} Cx86cpuid;
|
||||
|
||||
enum
|
||||
{
|
||||
CPU_FIRM_INTEL,
|
||||
CPU_FIRM_AMD,
|
||||
CPU_FIRM_VIA
|
||||
};
|
||||
|
||||
void MyCPUID(UInt32 function, UInt32 *a, UInt32 *b, UInt32 *c, UInt32 *d);
|
||||
|
||||
BoolInt x86cpuid_CheckAndRead(Cx86cpuid *p);
|
||||
int x86cpuid_GetFirm(const Cx86cpuid *p);
|
||||
|
||||
#define x86cpuid_GetFamily(ver) (((ver >> 16) & 0xFF0) | ((ver >> 8) & 0xF))
|
||||
#define x86cpuid_GetModel(ver) (((ver >> 12) & 0xF0) | ((ver >> 4) & 0xF))
|
||||
#define x86cpuid_GetStepping(ver) (ver & 0xF)
|
||||
|
||||
BoolInt CPU_Is_InOrder();
|
||||
BoolInt CPU_Is_Aes_Supported();
|
||||
BoolInt CPU_IsSupported_PageGB();
|
||||
|
||||
#endif
|
||||
|
||||
EXTERN_C_END
|
||||
|
||||
#endif
|
||||
Vendored
+64
@@ -0,0 +1,64 @@
|
||||
/* Delta.c -- Delta converter
|
||||
2009-05-26 : Igor Pavlov : Public domain */
|
||||
|
||||
#include "Precomp.h"
|
||||
|
||||
#include "Delta.h"
|
||||
|
||||
void Delta_Init(Byte *state)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < DELTA_STATE_SIZE; i++)
|
||||
state[i] = 0;
|
||||
}
|
||||
|
||||
static void MyMemCpy(Byte *dest, const Byte *src, unsigned size)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < size; i++)
|
||||
dest[i] = src[i];
|
||||
}
|
||||
|
||||
void Delta_Encode(Byte *state, unsigned delta, Byte *data, SizeT size)
|
||||
{
|
||||
Byte buf[DELTA_STATE_SIZE];
|
||||
unsigned j = 0;
|
||||
MyMemCpy(buf, state, delta);
|
||||
{
|
||||
SizeT i;
|
||||
for (i = 0; i < size;)
|
||||
{
|
||||
for (j = 0; j < delta && i < size; i++, j++)
|
||||
{
|
||||
Byte b = data[i];
|
||||
data[i] = (Byte)(b - buf[j]);
|
||||
buf[j] = b;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (j == delta)
|
||||
j = 0;
|
||||
MyMemCpy(state, buf + j, delta - j);
|
||||
MyMemCpy(state + delta - j, buf, j);
|
||||
}
|
||||
|
||||
void Delta_Decode(Byte *state, unsigned delta, Byte *data, SizeT size)
|
||||
{
|
||||
Byte buf[DELTA_STATE_SIZE];
|
||||
unsigned j = 0;
|
||||
MyMemCpy(buf, state, delta);
|
||||
{
|
||||
SizeT i;
|
||||
for (i = 0; i < size;)
|
||||
{
|
||||
for (j = 0; j < delta && i < size; i++, j++)
|
||||
{
|
||||
buf[j] = data[i] = (Byte)(buf[j] + data[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (j == delta)
|
||||
j = 0;
|
||||
MyMemCpy(state, buf + j, delta - j);
|
||||
MyMemCpy(state + delta - j, buf, j);
|
||||
}
|
||||
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
/* Delta.h -- Delta converter
|
||||
2013-01-18 : Igor Pavlov : Public domain */
|
||||
|
||||
#ifndef __DELTA_H
|
||||
#define __DELTA_H
|
||||
|
||||
#include "7zTypes.h"
|
||||
|
||||
EXTERN_C_BEGIN
|
||||
|
||||
#define DELTA_STATE_SIZE 256
|
||||
|
||||
void Delta_Init(Byte *state);
|
||||
void Delta_Encode(Byte *state, unsigned delta, Byte *data, SizeT size);
|
||||
void Delta_Decode(Byte *state, unsigned delta, Byte *data, SizeT size);
|
||||
|
||||
EXTERN_C_END
|
||||
|
||||
#endif
|
||||
Vendored
+478
-181
File diff suppressed because it is too large.
Load diff
Vendored
+50
-46
@@ -1,5 +1,5 @@
|
||||
/* LzFind.h -- Match finder for LZ algorithms
|
||||
2015-10-15 : Igor Pavlov : Public domain */
|
||||
2017-06-10 : Igor Pavlov : Public domain */
|
||||
|
||||
#ifndef __LZ_FIND_H
|
||||
#define __LZ_FIND_H
|
||||
@@ -8,45 +8,47 @@
|
||||
|
||||
EXTERN_C_BEGIN
|
||||
|
||||
typedef uint32_t CLzRef;
|
||||
typedef UInt32 CLzRef;
|
||||
|
||||
typedef struct _CMatchFinder
|
||||
{
|
||||
unsigned char *buffer;
|
||||
uint32_t pos;
|
||||
uint32_t posLimit;
|
||||
uint32_t streamPos;
|
||||
uint32_t lenLimit;
|
||||
Byte *buffer;
|
||||
UInt32 pos;
|
||||
UInt32 posLimit;
|
||||
UInt32 streamPos;
|
||||
UInt32 lenLimit;
|
||||
|
||||
uint32_t cyclicBufferPos;
|
||||
uint32_t cyclicBufferSize; /* it must be = (historySize + 1) */
|
||||
UInt32 cyclicBufferPos;
|
||||
UInt32 cyclicBufferSize; /* it must be = (historySize + 1) */
|
||||
|
||||
unsigned char streamEndWasReached;
|
||||
unsigned char btMode;
|
||||
unsigned char bigHash;
|
||||
unsigned char directInput;
|
||||
Byte streamEndWasReached;
|
||||
Byte btMode;
|
||||
Byte bigHash;
|
||||
Byte directInput;
|
||||
|
||||
uint32_t matchMaxLen;
|
||||
UInt32 matchMaxLen;
|
||||
CLzRef *hash;
|
||||
CLzRef *son;
|
||||
uint32_t hashMask;
|
||||
uint32_t cutValue;
|
||||
UInt32 hashMask;
|
||||
UInt32 cutValue;
|
||||
|
||||
unsigned char *bufferBase;
|
||||
Byte *bufferBase;
|
||||
ISeqInStream *stream;
|
||||
|
||||
UInt32 blockSize;
|
||||
UInt32 keepSizeBefore;
|
||||
UInt32 keepSizeAfter;
|
||||
|
||||
uint32_t blockSize;
|
||||
uint32_t keepSizeBefore;
|
||||
uint32_t keepSizeAfter;
|
||||
|
||||
uint32_t numHashBytes;
|
||||
UInt32 numHashBytes;
|
||||
size_t directInputRem;
|
||||
uint32_t historySize;
|
||||
uint32_t fixedHashSize;
|
||||
uint32_t hashSizeSum;
|
||||
UInt32 historySize;
|
||||
UInt32 fixedHashSize;
|
||||
UInt32 hashSizeSum;
|
||||
SRes result;
|
||||
uint32_t crc[256];
|
||||
UInt32 crc[256];
|
||||
size_t numRefs;
|
||||
|
||||
UInt64 expectedDataSize;
|
||||
} CMatchFinder;
|
||||
|
||||
#define Inline_MatchFinder_GetPointerToCurrentPos(p) ((p)->buffer)
|
||||
@@ -57,9 +59,9 @@ typedef struct _CMatchFinder
|
||||
((p)->streamEndWasReached \
|
||||
&& (p)->streamPos == (p)->pos \
|
||||
&& (!(p)->directInput || (p)->directInputRem == 0))
|
||||
|
||||
|
||||
int MatchFinder_NeedMove(CMatchFinder *p);
|
||||
unsigned char *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p);
|
||||
Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p);
|
||||
void MatchFinder_MoveBlock(CMatchFinder *p);
|
||||
void MatchFinder_ReadIfRequired(CMatchFinder *p);
|
||||
|
||||
@@ -69,16 +71,16 @@ void MatchFinder_Construct(CMatchFinder *p);
|
||||
historySize <= 3 GB
|
||||
keepAddBufferBefore + matchMaxLen + keepAddBufferAfter < 511MB
|
||||
*/
|
||||
int MatchFinder_Create(CMatchFinder *p, uint32_t historySize,
|
||||
uint32_t keepAddBufferBefore, uint32_t matchMaxLen, uint32_t keepAddBufferAfter,
|
||||
ISzAlloc *alloc);
|
||||
void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc);
|
||||
void MatchFinder_Normalize3(uint32_t subValue, CLzRef *items, size_t numItems);
|
||||
void MatchFinder_ReduceOffsets(CMatchFinder *p, uint32_t subValue);
|
||||
int MatchFinder_Create(CMatchFinder *p, UInt32 historySize,
|
||||
UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter,
|
||||
ISzAllocPtr alloc);
|
||||
void MatchFinder_Free(CMatchFinder *p, ISzAllocPtr alloc);
|
||||
void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, size_t numItems);
|
||||
void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue);
|
||||
|
||||
uint32_t * GetMatchesSpec1(uint32_t lenLimit, uint32_t curMatch, uint32_t pos, const unsigned char *buffer, CLzRef *son,
|
||||
uint32_t _cyclicBufferPos, uint32_t _cyclicBufferSize, uint32_t _cutValue,
|
||||
uint32_t *distances, uint32_t maxLen);
|
||||
UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *buffer, CLzRef *son,
|
||||
UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 _cutValue,
|
||||
UInt32 *distances, UInt32 maxLen);
|
||||
|
||||
/*
|
||||
Conditions:
|
||||
@@ -87,10 +89,10 @@ Conditions:
|
||||
*/
|
||||
|
||||
typedef void (*Mf_Init_Func)(void *object);
|
||||
typedef uint32_t (*Mf_GetNumAvailableBytes_Func)(void *object);
|
||||
typedef const unsigned char * (*Mf_GetPointerToCurrentPos_Func)(void *object);
|
||||
typedef uint32_t (*Mf_GetMatches_Func)(void *object, uint32_t *distances);
|
||||
typedef void (*Mf_Skip_Func)(void *object, uint32_t);
|
||||
typedef UInt32 (*Mf_GetNumAvailableBytes_Func)(void *object);
|
||||
typedef const Byte * (*Mf_GetPointerToCurrentPos_Func)(void *object);
|
||||
typedef UInt32 (*Mf_GetMatches_Func)(void *object, UInt32 *distances);
|
||||
typedef void (*Mf_Skip_Func)(void *object, UInt32);
|
||||
|
||||
typedef struct _IMatchFinder
|
||||
{
|
||||
@@ -103,14 +105,16 @@ typedef struct _IMatchFinder
|
||||
|
||||
void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable);
|
||||
|
||||
void MatchFinder_Init_2(CMatchFinder *p, int readData);
|
||||
void MatchFinder_Init_LowHash(CMatchFinder *p);
|
||||
void MatchFinder_Init_HighHash(CMatchFinder *p);
|
||||
void MatchFinder_Init_3(CMatchFinder *p, int readData);
|
||||
void MatchFinder_Init(CMatchFinder *p);
|
||||
|
||||
uint32_t Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, uint32_t *distances);
|
||||
uint32_t Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, uint32_t *distances);
|
||||
UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances);
|
||||
UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances);
|
||||
|
||||
void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, uint32_t num);
|
||||
void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, uint32_t num);
|
||||
void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num);
|
||||
void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num);
|
||||
|
||||
EXTERN_C_END
|
||||
|
||||
|
||||
Vendored
+13
-12
@@ -12,44 +12,45 @@
|
||||
#define kFix4HashSize (kHash2Size + kHash3Size)
|
||||
#define kFix5HashSize (kHash2Size + kHash3Size + kHash4Size)
|
||||
|
||||
#define HASH2_CALC hv = cur[0] | ((uint32_t)cur[1] << 8);
|
||||
#define HASH2_CALC hv = cur[0] | ((UInt32)cur[1] << 8);
|
||||
|
||||
#define HASH3_CALC { \
|
||||
uint32_t temp = p->crc[cur[0]] ^ cur[1]; \
|
||||
UInt32 temp = p->crc[cur[0]] ^ cur[1]; \
|
||||
h2 = temp & (kHash2Size - 1); \
|
||||
hv = (temp ^ ((uint32_t)cur[2] << 8)) & p->hashMask; }
|
||||
hv = (temp ^ ((UInt32)cur[2] << 8)) & p->hashMask; }
|
||||
|
||||
#define HASH4_CALC { \
|
||||
uint32_t temp = p->crc[cur[0]] ^ cur[1]; \
|
||||
UInt32 temp = p->crc[cur[0]] ^ cur[1]; \
|
||||
h2 = temp & (kHash2Size - 1); \
|
||||
temp ^= ((uint32_t)cur[2] << 8); \
|
||||
temp ^= ((UInt32)cur[2] << 8); \
|
||||
h3 = temp & (kHash3Size - 1); \
|
||||
hv = (temp ^ (p->crc[cur[3]] << 5)) & p->hashMask; }
|
||||
|
||||
#define HASH5_CALC { \
|
||||
uint32_t temp = p->crc[cur[0]] ^ cur[1]; \
|
||||
UInt32 temp = p->crc[cur[0]] ^ cur[1]; \
|
||||
h2 = temp & (kHash2Size - 1); \
|
||||
temp ^= ((uint32_t)cur[2] << 8); \
|
||||
temp ^= ((UInt32)cur[2] << 8); \
|
||||
h3 = temp & (kHash3Size - 1); \
|
||||
temp ^= (p->crc[cur[3]] << 5); \
|
||||
h4 = temp & (kHash4Size - 1); \
|
||||
hv = (temp ^ (p->crc[cur[4]] << 3)) & p->hashMask; }
|
||||
|
||||
#define HASH_ZIP_CALC hv = ((cur[2] | ((uint32_t)cur[0] << 8)) ^ p->crc[cur[1]]) & 0xFFFF;
|
||||
/* #define HASH_ZIP_CALC hv = ((cur[0] | ((UInt32)cur[1] << 8)) ^ p->crc[cur[2]]) & 0xFFFF; */
|
||||
#define HASH_ZIP_CALC hv = ((cur[2] | ((UInt32)cur[0] << 8)) ^ p->crc[cur[1]]) & 0xFFFF;
|
||||
|
||||
|
||||
#define MT_HASH2_CALC \
|
||||
h2 = (p->crc[cur[0]] ^ cur[1]) & (kHash2Size - 1);
|
||||
|
||||
#define MT_HASH3_CALC { \
|
||||
uint32_t temp = p->crc[cur[0]] ^ cur[1]; \
|
||||
UInt32 temp = p->crc[cur[0]] ^ cur[1]; \
|
||||
h2 = temp & (kHash2Size - 1); \
|
||||
h3 = (temp ^ ((uint32_t)cur[2] << 8)) & (kHash3Size - 1); }
|
||||
h3 = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); }
|
||||
|
||||
#define MT_HASH4_CALC { \
|
||||
uint32_t temp = p->crc[cur[0]] ^ cur[1]; \
|
||||
UInt32 temp = p->crc[cur[0]] ^ cur[1]; \
|
||||
h2 = temp & (kHash2Size - 1); \
|
||||
temp ^= ((uint32_t)cur[2] << 8); \
|
||||
temp ^= ((UInt32)cur[2] << 8); \
|
||||
h3 = temp & (kHash3Size - 1); \
|
||||
h4 = (temp ^ (p->crc[cur[3]] << 5)) & (kHash4Size - 1); }
|
||||
|
||||
|
||||
Vendored
+439
-308
@@ -1,357 +1,488 @@
|
||||
/* Lzma2Dec.c -- LZMA2 Decoder
|
||||
2009-05-03 : Igor Pavlov : Public domain */
|
||||
2019-02-02 : Igor Pavlov : Public domain */
|
||||
|
||||
/* #define SHOW_DEBUG_INFO */
|
||||
|
||||
#include "Precomp.h"
|
||||
|
||||
#ifdef SHOW_DEBUG_INFO
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "Lzma2Dec.h"
|
||||
|
||||
/*
|
||||
00000000 - EOS
|
||||
00000001 U U - Uncompressed Reset Dic
|
||||
00000010 U U - Uncompressed No Reset
|
||||
100uuuuu U U P P - LZMA no reset
|
||||
101uuuuu U U P P - LZMA reset state
|
||||
110uuuuu U U P P S - LZMA reset state + new prop
|
||||
111uuuuu U U P P S - LZMA reset state + new prop + reset dic
|
||||
00000000 - End of data
|
||||
00000001 U U - Uncompressed, reset dic, need reset state and set new prop
|
||||
00000010 U U - Uncompressed, no reset
|
||||
100uuuuu U U P P - LZMA, no reset
|
||||
101uuuuu U U P P - LZMA, reset state
|
||||
110uuuuu U U P P S - LZMA, reset state + set new prop
|
||||
111uuuuu U U P P S - LZMA, reset state + set new prop, reset dic
|
||||
|
||||
u, U - Unpack Size
|
||||
P - Pack Size
|
||||
S - Props
|
||||
*/
|
||||
u, U - Unpack Size
|
||||
P - Pack Size
|
||||
S - Props
|
||||
*/
|
||||
|
||||
#define LZMA2_CONTROL_LZMA (1 << 7)
|
||||
#define LZMA2_CONTROL_COPY_NO_RESET 2
|
||||
#define LZMA2_CONTROL_COPY_RESET_DIC 1
|
||||
#define LZMA2_CONTROL_EOF 0
|
||||
|
||||
#define LZMA2_IS_UNCOMPRESSED_STATE(p) (((p)->control & LZMA2_CONTROL_LZMA) == 0)
|
||||
|
||||
#define LZMA2_GET_LZMA_MODE(p) (((p)->control >> 5) & 3)
|
||||
#define LZMA2_IS_THERE_PROP(mode) ((mode) >= 2)
|
||||
#define LZMA2_IS_UNCOMPRESSED_STATE(p) (((p)->control & (1 << 7)) == 0)
|
||||
|
||||
#define LZMA2_LCLP_MAX 4
|
||||
#define LZMA2_DIC_SIZE_FROM_PROP(p) (((uint32_t)2 | ((p) & 1)) << ((p) / 2 + 11))
|
||||
#define LZMA2_DIC_SIZE_FROM_PROP(p) (((UInt32)2 | ((p) & 1)) << ((p) / 2 + 11))
|
||||
|
||||
#ifdef SHOW_DEBUG_INFO
|
||||
#define PRF(x) x
|
||||
#else
|
||||
#define PRF(x)
|
||||
#endif
|
||||
|
||||
typedef enum
|
||||
{
|
||||
LZMA2_STATE_CONTROL,
|
||||
LZMA2_STATE_UNPACK0,
|
||||
LZMA2_STATE_UNPACK1,
|
||||
LZMA2_STATE_PACK0,
|
||||
LZMA2_STATE_PACK1,
|
||||
LZMA2_STATE_PROP,
|
||||
LZMA2_STATE_DATA,
|
||||
LZMA2_STATE_DATA_CONT,
|
||||
LZMA2_STATE_FINISHED,
|
||||
LZMA2_STATE_ERROR
|
||||
LZMA2_STATE_CONTROL,
|
||||
LZMA2_STATE_UNPACK0,
|
||||
LZMA2_STATE_UNPACK1,
|
||||
LZMA2_STATE_PACK0,
|
||||
LZMA2_STATE_PACK1,
|
||||
LZMA2_STATE_PROP,
|
||||
LZMA2_STATE_DATA,
|
||||
LZMA2_STATE_DATA_CONT,
|
||||
LZMA2_STATE_FINISHED,
|
||||
LZMA2_STATE_ERROR
|
||||
} ELzma2State;
|
||||
|
||||
static SRes Lzma2Dec_GetOldProps(uint8_t prop, uint8_t *props)
|
||||
static SRes Lzma2Dec_GetOldProps(Byte prop, Byte *props)
|
||||
{
|
||||
uint32_t dicSize;
|
||||
if (prop > 40)
|
||||
return SZ_ERROR_UNSUPPORTED;
|
||||
dicSize = (prop == 40) ? 0xFFFFFFFF : LZMA2_DIC_SIZE_FROM_PROP(prop);
|
||||
props[0] = (uint8_t)LZMA2_LCLP_MAX;
|
||||
props[1] = (uint8_t)(dicSize);
|
||||
props[2] = (uint8_t)(dicSize >> 8);
|
||||
props[3] = (uint8_t)(dicSize >> 16);
|
||||
props[4] = (uint8_t)(dicSize >> 24);
|
||||
return SZ_OK;
|
||||
UInt32 dicSize;
|
||||
if (prop > 40)
|
||||
return SZ_ERROR_UNSUPPORTED;
|
||||
dicSize = (prop == 40) ? 0xFFFFFFFF : LZMA2_DIC_SIZE_FROM_PROP(prop);
|
||||
props[0] = (Byte)LZMA2_LCLP_MAX;
|
||||
props[1] = (Byte)(dicSize);
|
||||
props[2] = (Byte)(dicSize >> 8);
|
||||
props[3] = (Byte)(dicSize >> 16);
|
||||
props[4] = (Byte)(dicSize >> 24);
|
||||
return SZ_OK;
|
||||
}
|
||||
|
||||
SRes Lzma2Dec_AllocateProbs(CLzma2Dec *p, uint8_t prop, ISzAlloc *alloc)
|
||||
SRes Lzma2Dec_AllocateProbs(CLzma2Dec *p, Byte prop, ISzAllocPtr alloc)
|
||||
{
|
||||
uint8_t props[LZMA_PROPS_SIZE];
|
||||
SRes result = Lzma2Dec_GetOldProps(prop, props);
|
||||
if (result != 0)
|
||||
return result;
|
||||
return LzmaDec_AllocateProbs(&p->decoder, props, LZMA_PROPS_SIZE, alloc);
|
||||
Byte props[LZMA_PROPS_SIZE];
|
||||
RINOK(Lzma2Dec_GetOldProps(prop, props));
|
||||
return LzmaDec_AllocateProbs(&p->decoder, props, LZMA_PROPS_SIZE, alloc);
|
||||
}
|
||||
|
||||
SRes Lzma2Dec_Allocate(CLzma2Dec *p, uint8_t prop, ISzAlloc *alloc)
|
||||
SRes Lzma2Dec_Allocate(CLzma2Dec *p, Byte prop, ISzAllocPtr alloc)
|
||||
{
|
||||
uint8_t props[LZMA_PROPS_SIZE];
|
||||
SRes result = Lzma2Dec_GetOldProps(prop, props);
|
||||
if (result != 0)
|
||||
return result;
|
||||
return LzmaDec_Allocate(&p->decoder, props, LZMA_PROPS_SIZE, alloc);
|
||||
Byte props[LZMA_PROPS_SIZE];
|
||||
RINOK(Lzma2Dec_GetOldProps(prop, props));
|
||||
return LzmaDec_Allocate(&p->decoder, props, LZMA_PROPS_SIZE, alloc);
|
||||
}
|
||||
|
||||
void Lzma2Dec_Init(CLzma2Dec *p)
|
||||
{
|
||||
p->state = LZMA2_STATE_CONTROL;
|
||||
p->needInitDic = true;
|
||||
p->needInitState = true;
|
||||
p->needInitProp = true;
|
||||
LzmaDec_Init(&p->decoder);
|
||||
p->state = LZMA2_STATE_CONTROL;
|
||||
p->needInitLevel = 0xE0;
|
||||
p->isExtraMode = False;
|
||||
p->unpackSize = 0;
|
||||
|
||||
/* p->decoder.dicPos = 0; // we can use it instead of full init */
|
||||
LzmaDec_Init(&p->decoder);
|
||||
}
|
||||
|
||||
static ELzma2State Lzma2Dec_UpdateState(CLzma2Dec *p, uint8_t b)
|
||||
static ELzma2State Lzma2Dec_UpdateState(CLzma2Dec *p, Byte b)
|
||||
{
|
||||
switch(p->state)
|
||||
{
|
||||
case LZMA2_STATE_CONTROL:
|
||||
p->control = b;
|
||||
PRF(printf("\n %4X ", p->decoder.dicPos));
|
||||
PRF(printf(" %2X", b));
|
||||
if (p->control == 0)
|
||||
return LZMA2_STATE_FINISHED;
|
||||
if (LZMA2_IS_UNCOMPRESSED_STATE(p))
|
||||
{
|
||||
if ((p->control & 0x7F) > 2)
|
||||
return LZMA2_STATE_ERROR;
|
||||
p->unpackSize = 0;
|
||||
}
|
||||
else
|
||||
p->unpackSize = (uint32_t)(p->control & 0x1F) << 16;
|
||||
return LZMA2_STATE_UNPACK0;
|
||||
|
||||
case LZMA2_STATE_UNPACK0:
|
||||
p->unpackSize |= (uint32_t)b << 8;
|
||||
return LZMA2_STATE_UNPACK1;
|
||||
|
||||
case LZMA2_STATE_UNPACK1:
|
||||
p->unpackSize |= (uint32_t)b;
|
||||
p->unpackSize++;
|
||||
PRF(printf(" %8d", p->unpackSize));
|
||||
return (LZMA2_IS_UNCOMPRESSED_STATE(p)) ? LZMA2_STATE_DATA : LZMA2_STATE_PACK0;
|
||||
|
||||
case LZMA2_STATE_PACK0:
|
||||
p->packSize = (uint32_t)b << 8;
|
||||
return LZMA2_STATE_PACK1;
|
||||
|
||||
case LZMA2_STATE_PACK1:
|
||||
p->packSize |= (uint32_t)b;
|
||||
p->packSize++;
|
||||
PRF(printf(" %8d", p->packSize));
|
||||
return LZMA2_IS_THERE_PROP(LZMA2_GET_LZMA_MODE(p)) ? LZMA2_STATE_PROP:
|
||||
(p->needInitProp ? LZMA2_STATE_ERROR : LZMA2_STATE_DATA);
|
||||
|
||||
case LZMA2_STATE_PROP:
|
||||
{
|
||||
int lc, lp;
|
||||
if (b >= (9 * 5 * 5))
|
||||
return LZMA2_STATE_ERROR;
|
||||
lc = b % 9;
|
||||
b /= 9;
|
||||
p->decoder.prop.pb = b / 5;
|
||||
lp = b % 5;
|
||||
if (lc + lp > LZMA2_LCLP_MAX)
|
||||
return LZMA2_STATE_ERROR;
|
||||
p->decoder.prop.lc = lc;
|
||||
p->decoder.prop.lp = lp;
|
||||
p->needInitProp = false;
|
||||
return LZMA2_STATE_DATA;
|
||||
}
|
||||
}
|
||||
return LZMA2_STATE_ERROR;
|
||||
}
|
||||
|
||||
static void LzmaDec_UpdateWithUncompressed(CLzmaDec *p, const uint8_t *src, size_t size)
|
||||
{
|
||||
memcpy(p->dic + p->dicPos, src, size);
|
||||
p->dicPos += size;
|
||||
if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= size)
|
||||
p->checkDicSize = p->prop.dicSize;
|
||||
p->processedPos += (uint32_t)size;
|
||||
}
|
||||
|
||||
void LzmaDec_InitDicAndState(CLzmaDec *p, bool initDic, bool initState);
|
||||
|
||||
SRes Lzma2Dec_DecodeToDic(CLzma2Dec *p, size_t dicLimit,
|
||||
const uint8_t *src, size_t *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status)
|
||||
{
|
||||
size_t inSize = *srcLen;
|
||||
*srcLen = 0;
|
||||
*status = LZMA_STATUS_NOT_SPECIFIED;
|
||||
|
||||
while (p->state != LZMA2_STATE_FINISHED)
|
||||
{
|
||||
size_t dicPos = p->decoder.dicPos;
|
||||
if (p->state == LZMA2_STATE_ERROR)
|
||||
return SZ_ERROR_DATA;
|
||||
if (dicPos == dicLimit && finishMode == LZMA_FINISH_ANY)
|
||||
switch (p->state)
|
||||
{
|
||||
case LZMA2_STATE_CONTROL:
|
||||
p->isExtraMode = False;
|
||||
p->control = b;
|
||||
PRF(printf("\n %8X", (unsigned)p->decoder.dicPos));
|
||||
PRF(printf(" %02X", (unsigned)b));
|
||||
if (b == 0)
|
||||
return LZMA2_STATE_FINISHED;
|
||||
if (LZMA2_IS_UNCOMPRESSED_STATE(p))
|
||||
{
|
||||
*status = LZMA_STATUS_NOT_FINISHED;
|
||||
return SZ_OK;
|
||||
}
|
||||
if (p->state != LZMA2_STATE_DATA && p->state != LZMA2_STATE_DATA_CONT)
|
||||
{
|
||||
if (*srcLen == inSize)
|
||||
{
|
||||
*status = LZMA_STATUS_NEEDS_MORE_INPUT;
|
||||
return SZ_OK;
|
||||
}
|
||||
(*srcLen)++;
|
||||
p->state = Lzma2Dec_UpdateState(p, *src++);
|
||||
continue;
|
||||
}
|
||||
{
|
||||
size_t destSizeCur = dicLimit - dicPos;
|
||||
size_t srcSizeCur = inSize - *srcLen;
|
||||
ELzmaFinishMode curFinishMode = LZMA_FINISH_ANY;
|
||||
|
||||
if (p->unpackSize <= destSizeCur)
|
||||
{
|
||||
destSizeCur = (size_t)p->unpackSize;
|
||||
curFinishMode = LZMA_FINISH_END;
|
||||
}
|
||||
|
||||
if (LZMA2_IS_UNCOMPRESSED_STATE(p))
|
||||
{
|
||||
if (*srcLen == inSize)
|
||||
{
|
||||
*status = LZMA_STATUS_NEEDS_MORE_INPUT;
|
||||
return SZ_OK;
|
||||
}
|
||||
|
||||
if (p->state == LZMA2_STATE_DATA)
|
||||
{
|
||||
bool initDic = (p->control == LZMA2_CONTROL_COPY_RESET_DIC);
|
||||
if (initDic)
|
||||
p->needInitProp = p->needInitState = true;
|
||||
else if (p->needInitDic)
|
||||
return SZ_ERROR_DATA;
|
||||
p->needInitDic = false;
|
||||
LzmaDec_InitDicAndState(&p->decoder, initDic, false);
|
||||
}
|
||||
|
||||
if (srcSizeCur > destSizeCur)
|
||||
srcSizeCur = destSizeCur;
|
||||
|
||||
if (srcSizeCur == 0)
|
||||
return SZ_ERROR_DATA;
|
||||
|
||||
LzmaDec_UpdateWithUncompressed(&p->decoder, src, srcSizeCur);
|
||||
|
||||
src += srcSizeCur;
|
||||
*srcLen += srcSizeCur;
|
||||
p->unpackSize -= (uint32_t)srcSizeCur;
|
||||
p->state = (p->unpackSize == 0) ? LZMA2_STATE_CONTROL : LZMA2_STATE_DATA_CONT;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t outSizeProcessed;
|
||||
SRes res;
|
||||
|
||||
if (p->state == LZMA2_STATE_DATA)
|
||||
{
|
||||
int mode = LZMA2_GET_LZMA_MODE(p);
|
||||
bool initDic = (mode == 3);
|
||||
bool initState = (mode > 0);
|
||||
if ((!initDic && p->needInitDic) || (!initState && p->needInitState))
|
||||
return SZ_ERROR_DATA;
|
||||
|
||||
LzmaDec_InitDicAndState(&p->decoder, initDic, initState);
|
||||
p->needInitDic = false;
|
||||
p->needInitState = false;
|
||||
p->state = LZMA2_STATE_DATA_CONT;
|
||||
}
|
||||
if (srcSizeCur > p->packSize)
|
||||
srcSizeCur = (size_t)p->packSize;
|
||||
|
||||
res = LzmaDec_DecodeToDic(&p->decoder, dicPos + destSizeCur, src, &srcSizeCur, curFinishMode, status);
|
||||
|
||||
src += srcSizeCur;
|
||||
*srcLen += srcSizeCur;
|
||||
p->packSize -= (uint32_t)srcSizeCur;
|
||||
|
||||
outSizeProcessed = p->decoder.dicPos - dicPos;
|
||||
p->unpackSize -= (uint32_t)outSizeProcessed;
|
||||
|
||||
if (res != 0)
|
||||
return res;
|
||||
if (*status == LZMA_STATUS_NEEDS_MORE_INPUT)
|
||||
return res;
|
||||
|
||||
if (srcSizeCur == 0 && outSizeProcessed == 0)
|
||||
{
|
||||
if (*status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK ||
|
||||
p->unpackSize != 0 || p->packSize != 0)
|
||||
return SZ_ERROR_DATA;
|
||||
p->state = LZMA2_STATE_CONTROL;
|
||||
}
|
||||
if (*status == LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK)
|
||||
*status = LZMA_STATUS_NOT_FINISHED;
|
||||
}
|
||||
}
|
||||
}
|
||||
*status = LZMA_STATUS_FINISHED_WITH_MARK;
|
||||
return SZ_OK;
|
||||
}
|
||||
|
||||
SRes Lzma2Dec_DecodeToBuf(CLzma2Dec *p, uint8_t *dest, size_t *destLen, const uint8_t *src, size_t *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status)
|
||||
{
|
||||
size_t outSize = *destLen, inSize = *srcLen;
|
||||
*srcLen = *destLen = 0;
|
||||
for (;;)
|
||||
{
|
||||
size_t srcSizeCur = inSize, outSizeCur, dicPos;
|
||||
ELzmaFinishMode curFinishMode;
|
||||
SRes res;
|
||||
if (p->decoder.dicPos == p->decoder.dicBufSize)
|
||||
p->decoder.dicPos = 0;
|
||||
dicPos = p->decoder.dicPos;
|
||||
if (outSize > p->decoder.dicBufSize - dicPos)
|
||||
{
|
||||
outSizeCur = p->decoder.dicBufSize;
|
||||
curFinishMode = LZMA_FINISH_ANY;
|
||||
if (b == LZMA2_CONTROL_COPY_RESET_DIC)
|
||||
p->needInitLevel = 0xC0;
|
||||
else if (b > 2 || p->needInitLevel == 0xE0)
|
||||
return LZMA2_STATE_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
outSizeCur = dicPos + outSize;
|
||||
curFinishMode = finishMode;
|
||||
if (b < p->needInitLevel)
|
||||
return LZMA2_STATE_ERROR;
|
||||
p->needInitLevel = 0;
|
||||
p->unpackSize = (UInt32)(b & 0x1F) << 16;
|
||||
}
|
||||
return LZMA2_STATE_UNPACK0;
|
||||
|
||||
case LZMA2_STATE_UNPACK0:
|
||||
p->unpackSize |= (UInt32)b << 8;
|
||||
return LZMA2_STATE_UNPACK1;
|
||||
|
||||
case LZMA2_STATE_UNPACK1:
|
||||
p->unpackSize |= (UInt32)b;
|
||||
p->unpackSize++;
|
||||
PRF(printf(" %7u", (unsigned)p->unpackSize));
|
||||
return LZMA2_IS_UNCOMPRESSED_STATE(p) ? LZMA2_STATE_DATA : LZMA2_STATE_PACK0;
|
||||
|
||||
case LZMA2_STATE_PACK0:
|
||||
p->packSize = (UInt32)b << 8;
|
||||
return LZMA2_STATE_PACK1;
|
||||
|
||||
case LZMA2_STATE_PACK1:
|
||||
p->packSize |= (UInt32)b;
|
||||
p->packSize++;
|
||||
/* if (p->packSize < 5) return LZMA2_STATE_ERROR; */
|
||||
PRF(printf(" %5u", (unsigned)p->packSize));
|
||||
return (p->control & 0x40) ? LZMA2_STATE_PROP : LZMA2_STATE_DATA;
|
||||
|
||||
case LZMA2_STATE_PROP:
|
||||
{
|
||||
unsigned lc, lp;
|
||||
if (b >= (9 * 5 * 5))
|
||||
return LZMA2_STATE_ERROR;
|
||||
lc = b % 9;
|
||||
b /= 9;
|
||||
p->decoder.prop.pb = (Byte)(b / 5);
|
||||
lp = b % 5;
|
||||
if (lc + lp > LZMA2_LCLP_MAX)
|
||||
return LZMA2_STATE_ERROR;
|
||||
p->decoder.prop.lc = (Byte)lc;
|
||||
p->decoder.prop.lp = (Byte)lp;
|
||||
return LZMA2_STATE_DATA;
|
||||
}
|
||||
}
|
||||
return LZMA2_STATE_ERROR;
|
||||
}
|
||||
|
||||
static void LzmaDec_UpdateWithUncompressed(CLzmaDec *p, const Byte *src, SizeT size)
|
||||
{
|
||||
memcpy(p->dic + p->dicPos, src, size);
|
||||
p->dicPos += size;
|
||||
if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= size)
|
||||
p->checkDicSize = p->prop.dicSize;
|
||||
p->processedPos += (UInt32)size;
|
||||
}
|
||||
|
||||
void LzmaDec_InitDicAndState(CLzmaDec *p, BoolInt initDic, BoolInt initState);
|
||||
|
||||
|
||||
SRes Lzma2Dec_DecodeToDic(CLzma2Dec *p, SizeT dicLimit,
|
||||
const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status)
|
||||
{
|
||||
SizeT inSize = *srcLen;
|
||||
*srcLen = 0;
|
||||
*status = LZMA_STATUS_NOT_SPECIFIED;
|
||||
|
||||
while (p->state != LZMA2_STATE_ERROR)
|
||||
{
|
||||
SizeT dicPos;
|
||||
|
||||
if (p->state == LZMA2_STATE_FINISHED)
|
||||
{
|
||||
*status = LZMA_STATUS_FINISHED_WITH_MARK;
|
||||
return SZ_OK;
|
||||
}
|
||||
|
||||
dicPos = p->decoder.dicPos;
|
||||
|
||||
if (dicPos == dicLimit && finishMode == LZMA_FINISH_ANY)
|
||||
{
|
||||
*status = LZMA_STATUS_NOT_FINISHED;
|
||||
return SZ_OK;
|
||||
}
|
||||
|
||||
if (p->state != LZMA2_STATE_DATA && p->state != LZMA2_STATE_DATA_CONT)
|
||||
{
|
||||
if (*srcLen == inSize)
|
||||
{
|
||||
*status = LZMA_STATUS_NEEDS_MORE_INPUT;
|
||||
return SZ_OK;
|
||||
}
|
||||
(*srcLen)++;
|
||||
p->state = Lzma2Dec_UpdateState(p, *src++);
|
||||
if (dicPos == dicLimit && p->state != LZMA2_STATE_FINISHED)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
{
|
||||
SizeT inCur = inSize - *srcLen;
|
||||
SizeT outCur = dicLimit - dicPos;
|
||||
ELzmaFinishMode curFinishMode = LZMA_FINISH_ANY;
|
||||
|
||||
if (outCur >= p->unpackSize)
|
||||
{
|
||||
outCur = (SizeT)p->unpackSize;
|
||||
curFinishMode = LZMA_FINISH_END;
|
||||
}
|
||||
|
||||
res = Lzma2Dec_DecodeToDic(p, outSizeCur, src, &srcSizeCur, curFinishMode, status);
|
||||
src += srcSizeCur;
|
||||
inSize -= srcSizeCur;
|
||||
*srcLen += srcSizeCur;
|
||||
outSizeCur = p->decoder.dicPos - dicPos;
|
||||
memcpy(dest, p->decoder.dic + dicPos, outSizeCur);
|
||||
dest += outSizeCur;
|
||||
outSize -= outSizeCur;
|
||||
*destLen += outSizeCur;
|
||||
if (res != 0)
|
||||
return res;
|
||||
if (outSizeCur == 0 || outSize == 0)
|
||||
return SZ_OK;
|
||||
}
|
||||
if (LZMA2_IS_UNCOMPRESSED_STATE(p))
|
||||
{
|
||||
if (inCur == 0)
|
||||
{
|
||||
*status = LZMA_STATUS_NEEDS_MORE_INPUT;
|
||||
return SZ_OK;
|
||||
}
|
||||
|
||||
if (p->state == LZMA2_STATE_DATA)
|
||||
{
|
||||
BoolInt initDic = (p->control == LZMA2_CONTROL_COPY_RESET_DIC);
|
||||
LzmaDec_InitDicAndState(&p->decoder, initDic, False);
|
||||
}
|
||||
|
||||
if (inCur > outCur)
|
||||
inCur = outCur;
|
||||
if (inCur == 0)
|
||||
break;
|
||||
|
||||
LzmaDec_UpdateWithUncompressed(&p->decoder, src, inCur);
|
||||
|
||||
src += inCur;
|
||||
*srcLen += inCur;
|
||||
p->unpackSize -= (UInt32)inCur;
|
||||
p->state = (p->unpackSize == 0) ? LZMA2_STATE_CONTROL : LZMA2_STATE_DATA_CONT;
|
||||
}
|
||||
else
|
||||
{
|
||||
SRes res;
|
||||
|
||||
if (p->state == LZMA2_STATE_DATA)
|
||||
{
|
||||
BoolInt initDic = (p->control >= 0xE0);
|
||||
BoolInt initState = (p->control >= 0xA0);
|
||||
LzmaDec_InitDicAndState(&p->decoder, initDic, initState);
|
||||
p->state = LZMA2_STATE_DATA_CONT;
|
||||
}
|
||||
|
||||
if (inCur > p->packSize)
|
||||
inCur = (SizeT)p->packSize;
|
||||
|
||||
res = LzmaDec_DecodeToDic(&p->decoder, dicPos + outCur, src, &inCur, curFinishMode, status);
|
||||
|
||||
src += inCur;
|
||||
*srcLen += inCur;
|
||||
p->packSize -= (UInt32)inCur;
|
||||
outCur = p->decoder.dicPos - dicPos;
|
||||
p->unpackSize -= (UInt32)outCur;
|
||||
|
||||
if (res != 0)
|
||||
break;
|
||||
|
||||
if (*status == LZMA_STATUS_NEEDS_MORE_INPUT)
|
||||
{
|
||||
if (p->packSize == 0)
|
||||
break;
|
||||
return SZ_OK;
|
||||
}
|
||||
|
||||
if (inCur == 0 && outCur == 0)
|
||||
{
|
||||
if (*status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK
|
||||
|| p->unpackSize != 0
|
||||
|| p->packSize != 0)
|
||||
break;
|
||||
p->state = LZMA2_STATE_CONTROL;
|
||||
}
|
||||
|
||||
*status = LZMA_STATUS_NOT_SPECIFIED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*status = LZMA_STATUS_NOT_SPECIFIED;
|
||||
p->state = LZMA2_STATE_ERROR;
|
||||
return SZ_ERROR_DATA;
|
||||
}
|
||||
|
||||
SRes Lzma2Decode(uint8_t *dest, size_t *destLen, const uint8_t *src, size_t *srcLen,
|
||||
uint8_t prop, ELzmaFinishMode finishMode, ELzmaStatus *status, ISzAlloc *alloc)
|
||||
|
||||
|
||||
|
||||
ELzma2ParseStatus Lzma2Dec_Parse(CLzma2Dec *p,
|
||||
SizeT outSize,
|
||||
const Byte *src, SizeT *srcLen,
|
||||
int checkFinishBlock)
|
||||
{
|
||||
SRes res;
|
||||
SRes result;
|
||||
CLzma2Dec decoder;
|
||||
size_t outSize = *destLen, inSize = *srcLen;
|
||||
uint8_t props[LZMA_PROPS_SIZE];
|
||||
SizeT inSize = *srcLen;
|
||||
*srcLen = 0;
|
||||
|
||||
Lzma2Dec_Construct(&decoder);
|
||||
while (p->state != LZMA2_STATE_ERROR)
|
||||
{
|
||||
if (p->state == LZMA2_STATE_FINISHED)
|
||||
return (ELzma2ParseStatus)LZMA_STATUS_FINISHED_WITH_MARK;
|
||||
|
||||
*destLen = *srcLen = 0;
|
||||
*status = LZMA_STATUS_NOT_SPECIFIED;
|
||||
decoder.decoder.dic = dest;
|
||||
decoder.decoder.dicBufSize = outSize;
|
||||
if (outSize == 0 && !checkFinishBlock)
|
||||
return (ELzma2ParseStatus)LZMA_STATUS_NOT_FINISHED;
|
||||
|
||||
if (p->state != LZMA2_STATE_DATA && p->state != LZMA2_STATE_DATA_CONT)
|
||||
{
|
||||
if (*srcLen == inSize)
|
||||
return (ELzma2ParseStatus)LZMA_STATUS_NEEDS_MORE_INPUT;
|
||||
(*srcLen)++;
|
||||
|
||||
result = Lzma2Dec_GetOldProps(prop, props);
|
||||
if (result != 0)
|
||||
return result;
|
||||
result = LzmaDec_AllocateProbs(&decoder.decoder, props, LZMA_PROPS_SIZE, alloc);
|
||||
if (result != 0)
|
||||
return result;
|
||||
p->state = Lzma2Dec_UpdateState(p, *src++);
|
||||
|
||||
*srcLen = inSize;
|
||||
res = Lzma2Dec_DecodeToDic(&decoder, outSize, src, srcLen, finishMode, status);
|
||||
*destLen = decoder.decoder.dicPos;
|
||||
if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT)
|
||||
res = SZ_ERROR_INPUT_EOF;
|
||||
if (p->state == LZMA2_STATE_UNPACK0)
|
||||
{
|
||||
/* if (p->decoder.dicPos != 0) */
|
||||
if (p->control == LZMA2_CONTROL_COPY_RESET_DIC || p->control >= 0xE0)
|
||||
return LZMA2_PARSE_STATUS_NEW_BLOCK;
|
||||
/* if (outSize == 0) return LZMA_STATUS_NOT_FINISHED; */
|
||||
}
|
||||
|
||||
LzmaDec_FreeProbs(&decoder.decoder, alloc);
|
||||
return res;
|
||||
/* The following code can be commented.
|
||||
* It's not big problem, if we read additional input bytes.
|
||||
* It will be stopped later in LZMA2_STATE_DATA / LZMA2_STATE_DATA_CONT state. */
|
||||
|
||||
if (outSize == 0 && p->state != LZMA2_STATE_FINISHED)
|
||||
{
|
||||
/* checkFinishBlock is true. So we expect that block must be finished,
|
||||
* We can return LZMA_STATUS_NOT_SPECIFIED or LZMA_STATUS_NOT_FINISHED here
|
||||
* break; */
|
||||
return (ELzma2ParseStatus)LZMA_STATUS_NOT_FINISHED;
|
||||
}
|
||||
|
||||
if (p->state == LZMA2_STATE_DATA)
|
||||
return LZMA2_PARSE_STATUS_NEW_CHUNK;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (outSize == 0)
|
||||
return (ELzma2ParseStatus)LZMA_STATUS_NOT_FINISHED;
|
||||
|
||||
{
|
||||
SizeT inCur = inSize - *srcLen;
|
||||
|
||||
if (LZMA2_IS_UNCOMPRESSED_STATE(p))
|
||||
{
|
||||
if (inCur == 0)
|
||||
return (ELzma2ParseStatus)LZMA_STATUS_NEEDS_MORE_INPUT;
|
||||
if (inCur > p->unpackSize)
|
||||
inCur = p->unpackSize;
|
||||
if (inCur > outSize)
|
||||
inCur = outSize;
|
||||
p->decoder.dicPos += inCur;
|
||||
src += inCur;
|
||||
*srcLen += inCur;
|
||||
outSize -= inCur;
|
||||
p->unpackSize -= (UInt32)inCur;
|
||||
p->state = (p->unpackSize == 0) ? LZMA2_STATE_CONTROL : LZMA2_STATE_DATA_CONT;
|
||||
}
|
||||
else
|
||||
{
|
||||
p->isExtraMode = True;
|
||||
|
||||
if (inCur == 0)
|
||||
{
|
||||
if (p->packSize != 0)
|
||||
return (ELzma2ParseStatus)LZMA_STATUS_NEEDS_MORE_INPUT;
|
||||
}
|
||||
else if (p->state == LZMA2_STATE_DATA)
|
||||
{
|
||||
p->state = LZMA2_STATE_DATA_CONT;
|
||||
if (*src != 0)
|
||||
{
|
||||
/* first byte of lzma chunk must be Zero */
|
||||
*srcLen += 1;
|
||||
p->packSize--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (inCur > p->packSize)
|
||||
inCur = (SizeT)p->packSize;
|
||||
|
||||
src += inCur;
|
||||
*srcLen += inCur;
|
||||
p->packSize -= (UInt32)inCur;
|
||||
|
||||
if (p->packSize == 0)
|
||||
{
|
||||
SizeT rem = outSize;
|
||||
if (rem > p->unpackSize)
|
||||
rem = p->unpackSize;
|
||||
p->decoder.dicPos += rem;
|
||||
p->unpackSize -= (UInt32)rem;
|
||||
outSize -= rem;
|
||||
if (p->unpackSize == 0)
|
||||
p->state = LZMA2_STATE_CONTROL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p->state = LZMA2_STATE_ERROR;
|
||||
return (ELzma2ParseStatus)LZMA_STATUS_NOT_SPECIFIED;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
SRes Lzma2Dec_DecodeToBuf(CLzma2Dec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status)
|
||||
{
|
||||
SizeT outSize = *destLen, inSize = *srcLen;
|
||||
*srcLen = *destLen = 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
SizeT inCur = inSize, outCur, dicPos;
|
||||
ELzmaFinishMode curFinishMode;
|
||||
SRes res;
|
||||
|
||||
if (p->decoder.dicPos == p->decoder.dicBufSize)
|
||||
p->decoder.dicPos = 0;
|
||||
dicPos = p->decoder.dicPos;
|
||||
curFinishMode = LZMA_FINISH_ANY;
|
||||
outCur = p->decoder.dicBufSize - dicPos;
|
||||
|
||||
if (outCur >= outSize)
|
||||
{
|
||||
outCur = outSize;
|
||||
curFinishMode = finishMode;
|
||||
}
|
||||
|
||||
res = Lzma2Dec_DecodeToDic(p, dicPos + outCur, src, &inCur, curFinishMode, status);
|
||||
|
||||
src += inCur;
|
||||
inSize -= inCur;
|
||||
*srcLen += inCur;
|
||||
outCur = p->decoder.dicPos - dicPos;
|
||||
memcpy(dest, p->decoder.dic + dicPos, outCur);
|
||||
dest += outCur;
|
||||
outSize -= outCur;
|
||||
*destLen += outCur;
|
||||
if (res != 0)
|
||||
return res;
|
||||
if (outCur == 0 || outSize == 0)
|
||||
return SZ_OK;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SRes Lzma2Decode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen,
|
||||
Byte prop, ELzmaFinishMode finishMode, ELzmaStatus *status, ISzAllocPtr alloc)
|
||||
{
|
||||
CLzma2Dec p;
|
||||
SRes res;
|
||||
SizeT outSize = *destLen, inSize = *srcLen;
|
||||
*destLen = *srcLen = 0;
|
||||
*status = LZMA_STATUS_NOT_SPECIFIED;
|
||||
Lzma2Dec_Construct(&p);
|
||||
RINOK(Lzma2Dec_AllocateProbs(&p, prop, alloc));
|
||||
p.decoder.dic = dest;
|
||||
p.decoder.dicBufSize = outSize;
|
||||
Lzma2Dec_Init(&p);
|
||||
*srcLen = inSize;
|
||||
res = Lzma2Dec_DecodeToDic(&p, outSize, src, srcLen, finishMode, status);
|
||||
*destLen = p.decoder.dicPos;
|
||||
if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT)
|
||||
res = SZ_ERROR_INPUT_EOF;
|
||||
Lzma2Dec_FreeProbs(&p, alloc);
|
||||
return res;
|
||||
}
|
||||
Vendored
+61
-26
@@ -1,39 +1,35 @@
|
||||
/* Lzma2Dec.h -- LZMA2 Decoder
|
||||
2009-05-03 : Igor Pavlov : Public domain */
|
||||
2018-02-19 : Igor Pavlov : Public domain */
|
||||
|
||||
#ifndef __LZMA2_DEC_H
|
||||
#define __LZMA2_DEC_H
|
||||
|
||||
#include <boolean.h>
|
||||
#include "LzmaDec.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
EXTERN_C_BEGIN
|
||||
|
||||
/* ---------- State Interface ---------- */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned state;
|
||||
Byte control;
|
||||
Byte needInitLevel;
|
||||
Byte isExtraMode;
|
||||
Byte _pad_;
|
||||
UInt32 packSize;
|
||||
UInt32 unpackSize;
|
||||
CLzmaDec decoder;
|
||||
uint32_t packSize;
|
||||
uint32_t unpackSize;
|
||||
int state;
|
||||
uint8_t control;
|
||||
bool needInitDic;
|
||||
bool needInitState;
|
||||
bool needInitProp;
|
||||
} CLzma2Dec;
|
||||
|
||||
#define Lzma2Dec_Construct(p) LzmaDec_Construct(&(p)->decoder)
|
||||
#define Lzma2Dec_FreeProbs(p, alloc) LzmaDec_FreeProbs(&(p)->decoder, alloc);
|
||||
#define Lzma2Dec_Free(p, alloc) LzmaDec_Free(&(p)->decoder, alloc);
|
||||
#define Lzma2Dec_FreeProbs(p, alloc) LzmaDec_FreeProbs(&(p)->decoder, alloc)
|
||||
#define Lzma2Dec_Free(p, alloc) LzmaDec_Free(&(p)->decoder, alloc)
|
||||
|
||||
SRes Lzma2Dec_AllocateProbs(CLzma2Dec *p, uint8_t prop, ISzAlloc *alloc);
|
||||
SRes Lzma2Dec_Allocate(CLzma2Dec *p, uint8_t prop, ISzAlloc *alloc);
|
||||
SRes Lzma2Dec_AllocateProbs(CLzma2Dec *p, Byte prop, ISzAllocPtr alloc);
|
||||
SRes Lzma2Dec_Allocate(CLzma2Dec *p, Byte prop, ISzAllocPtr alloc);
|
||||
void Lzma2Dec_Init(CLzma2Dec *p);
|
||||
|
||||
|
||||
/*
|
||||
finishMode:
|
||||
It has meaning only if the decoding reaches output limit (*destLen or dicLimit).
|
||||
@@ -49,11 +45,52 @@ Returns:
|
||||
SZ_ERROR_DATA - Data error
|
||||
*/
|
||||
|
||||
SRes Lzma2Dec_DecodeToDic(CLzma2Dec *p, size_t dicLimit,
|
||||
const uint8_t *src, size_t *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status);
|
||||
SRes Lzma2Dec_DecodeToDic(CLzma2Dec *p, SizeT dicLimit,
|
||||
const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status);
|
||||
|
||||
SRes Lzma2Dec_DecodeToBuf(CLzma2Dec *p, uint8_t *dest, size_t *destLen,
|
||||
const uint8_t *src, size_t *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status);
|
||||
SRes Lzma2Dec_DecodeToBuf(CLzma2Dec *p, Byte *dest, SizeT *destLen,
|
||||
const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status);
|
||||
|
||||
|
||||
/* ---------- LZMA2 block and chunk parsing ---------- */
|
||||
|
||||
/*
|
||||
Lzma2Dec_Parse() parses compressed data stream up to next independent block or next chunk data.
|
||||
It can return LZMA_STATUS_* code or LZMA2_PARSE_STATUS_* code:
|
||||
- LZMA2_PARSE_STATUS_NEW_BLOCK - there is new block, and 1 additional byte (control byte of next block header) was read from input.
|
||||
- LZMA2_PARSE_STATUS_NEW_CHUNK - there is new chunk, and only lzma2 header of new chunk was read.
|
||||
CLzma2Dec::unpackSize contains unpack size of that chunk
|
||||
*/
|
||||
|
||||
typedef enum
|
||||
{
|
||||
/*
|
||||
LZMA_STATUS_NOT_SPECIFIED // data error
|
||||
LZMA_STATUS_FINISHED_WITH_MARK
|
||||
LZMA_STATUS_NOT_FINISHED //
|
||||
LZMA_STATUS_NEEDS_MORE_INPUT
|
||||
LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK // unused
|
||||
*/
|
||||
LZMA2_PARSE_STATUS_NEW_BLOCK = LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK + 1,
|
||||
LZMA2_PARSE_STATUS_NEW_CHUNK
|
||||
} ELzma2ParseStatus;
|
||||
|
||||
ELzma2ParseStatus Lzma2Dec_Parse(CLzma2Dec *p,
|
||||
SizeT outSize, /* output size */
|
||||
const Byte *src, SizeT *srcLen,
|
||||
int checkFinishBlock /* set (checkFinishBlock = 1), if it must read full input data, if decoder.dicPos reaches blockMax position. */
|
||||
);
|
||||
|
||||
/*
|
||||
LZMA2 parser doesn't decode LZMA chunks, so we must read
|
||||
full input LZMA chunk to decode some part of LZMA chunk.
|
||||
|
||||
Lzma2Dec_GetUnpackExtra() returns the value that shows
|
||||
max possible number of output bytes that can be output by decoder
|
||||
at current input positon.
|
||||
*/
|
||||
|
||||
#define Lzma2Dec_GetUnpackExtra(p) ((p)->isExtraMode ? (p)->unpackSize : 0);
|
||||
|
||||
|
||||
/* ---------- One Call Interface ---------- */
|
||||
@@ -75,11 +112,9 @@ Returns:
|
||||
SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src).
|
||||
*/
|
||||
|
||||
SRes Lzma2Decode(uint8_t *dest, size_t *destLen, const uint8_t *src, size_t *srcLen,
|
||||
uint8_t prop, ELzmaFinishMode finishMode, ELzmaStatus *status, ISzAlloc *alloc);
|
||||
SRes Lzma2Decode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen,
|
||||
Byte prop, ELzmaFinishMode finishMode, ELzmaStatus *status, ISzAllocPtr alloc);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
EXTERN_C_END
|
||||
|
||||
#endif
|
||||
Vendored
+998
-882
File diff suppressed because it is too large.
Load diff
Vendored
+175
-161
@@ -1,174 +1,190 @@
|
||||
/* LzmaDec.h -- LZMA Decoder
|
||||
2009-02-07 : Igor Pavlov : Public domain */
|
||||
2018-04-21 : Igor Pavlov : Public domain */
|
||||
|
||||
#ifndef __LZMA_DEC_H
|
||||
#define __LZMA_DEC_H
|
||||
|
||||
#include "7zTypes.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
EXTERN_C_BEGIN
|
||||
|
||||
/* #define _LZMA_PROB32 */
|
||||
/* _LZMA_PROB32 can increase the speed on some CPUs,
|
||||
but memory usage for CLzmaDec::probs will be doubled in that case */
|
||||
|
||||
typedef
|
||||
#ifdef _LZMA_PROB32
|
||||
UInt32
|
||||
#else
|
||||
UInt16
|
||||
#endif
|
||||
CLzmaProb;
|
||||
|
||||
|
||||
/* ---------- LZMA Properties ---------- */
|
||||
|
||||
#define LZMA_PROPS_SIZE 5
|
||||
|
||||
typedef struct _CLzmaProps
|
||||
{
|
||||
unsigned lc, lp, pb;
|
||||
uint32_t dicSize;
|
||||
} CLzmaProps;
|
||||
typedef struct _CLzmaProps
|
||||
{
|
||||
Byte lc;
|
||||
Byte lp;
|
||||
Byte pb;
|
||||
Byte _pad_;
|
||||
UInt32 dicSize;
|
||||
} CLzmaProps;
|
||||
|
||||
/* LzmaProps_Decode - decodes properties
|
||||
/* LzmaProps_Decode - decodes properties
|
||||
Returns:
|
||||
SZ_OK
|
||||
SZ_ERROR_UNSUPPORTED - Unsupported properties
|
||||
SZ_OK
|
||||
SZ_ERROR_UNSUPPORTED - Unsupported properties
|
||||
*/
|
||||
|
||||
SRes LzmaProps_Decode(CLzmaProps *p, const uint8_t *data, unsigned size);
|
||||
SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size);
|
||||
|
||||
|
||||
/* ---------- LZMA Decoder state ---------- */
|
||||
/* ---------- LZMA Decoder state ---------- */
|
||||
|
||||
/* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case.
|
||||
Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */
|
||||
/* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case.
|
||||
Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */
|
||||
|
||||
#define LZMA_REQUIRED_INPUT_MAX 20
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CLzmaProps prop;
|
||||
uint16_t *probs;
|
||||
uint8_t *dic;
|
||||
const uint8_t *buf;
|
||||
uint32_t range, code;
|
||||
size_t dicPos;
|
||||
size_t dicBufSize;
|
||||
uint32_t processedPos;
|
||||
uint32_t checkDicSize;
|
||||
unsigned state;
|
||||
uint32_t reps[4];
|
||||
unsigned remainLen;
|
||||
int needFlush;
|
||||
int needInitState;
|
||||
uint32_t numProbs;
|
||||
unsigned tempBufSize;
|
||||
uint8_t tempBuf[LZMA_REQUIRED_INPUT_MAX];
|
||||
} CLzmaDec;
|
||||
|
||||
#define LzmaDec_Construct(p) { (p)->dic = 0; (p)->probs = 0; }
|
||||
|
||||
void LzmaDec_Init(CLzmaDec *p);
|
||||
|
||||
/* There are two types of LZMA streams:
|
||||
0) Stream with end mark. That end mark adds about 6 bytes to compressed size.
|
||||
1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */
|
||||
|
||||
typedef enum
|
||||
{
|
||||
LZMA_FINISH_ANY, /* finish at any point */
|
||||
LZMA_FINISH_END /* block must be finished at the end */
|
||||
} ELzmaFinishMode;
|
||||
|
||||
/* ELzmaFinishMode has meaning only if the decoding reaches output limit !!!
|
||||
|
||||
You must use LZMA_FINISH_END, when you know that current output buffer
|
||||
covers last bytes of block. In other cases you must use LZMA_FINISH_ANY.
|
||||
|
||||
If LZMA decoder sees end marker before reaching output limit, it returns SZ_OK,
|
||||
and output value of destLen will be less than output buffer size limit.
|
||||
You can check status result also.
|
||||
|
||||
You can use multiple checks to test data integrity after full decompression:
|
||||
1) Check Result and "status" variable.
|
||||
2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize.
|
||||
3) Check that output(srcLen) = compressedSize, if you know real compressedSize.
|
||||
You must use correct finish mode in that case. */
|
||||
|
||||
typedef enum
|
||||
{
|
||||
LZMA_STATUS_NOT_SPECIFIED, /* use main error code instead */
|
||||
LZMA_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */
|
||||
LZMA_STATUS_NOT_FINISHED, /* stream was not finished */
|
||||
LZMA_STATUS_NEEDS_MORE_INPUT, /* you must provide more input bytes */
|
||||
LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK /* there is probability that stream was finished without end mark */
|
||||
} ELzmaStatus;
|
||||
|
||||
/* ELzmaStatus is used only as output value for function call */
|
||||
|
||||
|
||||
/* ---------- Interfaces ---------- */
|
||||
|
||||
/* There are 3 levels of interfaces:
|
||||
1) Dictionary Interface
|
||||
2) Buffer Interface
|
||||
3) One Call Interface
|
||||
You can select any of these interfaces, but don't mix functions from different
|
||||
groups for same object. */
|
||||
|
||||
|
||||
/* There are two variants to allocate state for Dictionary Interface:
|
||||
1) LzmaDec_Allocate / LzmaDec_Free
|
||||
2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs
|
||||
You can use variant 2, if you set dictionary buffer manually.
|
||||
For Buffer Interface you must always use variant 1.
|
||||
|
||||
LzmaDec_Allocate* can return:
|
||||
SZ_OK
|
||||
SZ_ERROR_MEM - Memory allocation error
|
||||
SZ_ERROR_UNSUPPORTED - Unsupported properties
|
||||
*/
|
||||
|
||||
SRes LzmaDec_AllocateProbs(CLzmaDec *p, const uint8_t *props, unsigned propsSize, ISzAlloc *alloc);
|
||||
void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc);
|
||||
|
||||
SRes LzmaDec_Allocate(CLzmaDec *state, const uint8_t *prop, unsigned propsSize, ISzAlloc *alloc);
|
||||
void LzmaDec_Free(CLzmaDec *state, ISzAlloc *alloc);
|
||||
|
||||
/* ---------- Dictionary Interface ---------- */
|
||||
|
||||
/* You can use it, if you want to eliminate the overhead for data copying from
|
||||
dictionary to some other external buffer.
|
||||
You must work with CLzmaDec variables directly in this interface.
|
||||
|
||||
STEPS:
|
||||
LzmaDec_Constr()
|
||||
LzmaDec_Allocate()
|
||||
for (each new stream)
|
||||
typedef struct
|
||||
{
|
||||
LzmaDec_Init()
|
||||
while (it needs more decompression)
|
||||
/* Don't change this structure. ASM code can use it. */
|
||||
CLzmaProps prop;
|
||||
CLzmaProb *probs;
|
||||
CLzmaProb *probs_1664;
|
||||
Byte *dic;
|
||||
SizeT dicBufSize;
|
||||
SizeT dicPos;
|
||||
const Byte *buf;
|
||||
UInt32 range;
|
||||
UInt32 code;
|
||||
UInt32 processedPos;
|
||||
UInt32 checkDicSize;
|
||||
UInt32 reps[4];
|
||||
UInt32 state;
|
||||
UInt32 remainLen;
|
||||
|
||||
UInt32 numProbs;
|
||||
unsigned tempBufSize;
|
||||
Byte tempBuf[LZMA_REQUIRED_INPUT_MAX];
|
||||
} CLzmaDec;
|
||||
|
||||
#define LzmaDec_Construct(p) { (p)->dic = NULL; (p)->probs = NULL; }
|
||||
|
||||
void LzmaDec_Init(CLzmaDec *p);
|
||||
|
||||
/* There are two types of LZMA streams:
|
||||
- Stream with end mark. That end mark adds about 6 bytes to compressed size.
|
||||
- Stream without end mark. You must know exact uncompressed size to decompress such stream. */
|
||||
|
||||
typedef enum
|
||||
{
|
||||
LzmaDec_DecodeToDic()
|
||||
use data from CLzmaDec::dic and update CLzmaDec::dicPos
|
||||
}
|
||||
}
|
||||
LzmaDec_Free()
|
||||
LZMA_FINISH_ANY, /* finish at any point */
|
||||
LZMA_FINISH_END /* block must be finished at the end */
|
||||
} ELzmaFinishMode;
|
||||
|
||||
/* ELzmaFinishMode has meaning only if the decoding reaches output limit !!!
|
||||
|
||||
You must use LZMA_FINISH_END, when you know that current output buffer
|
||||
covers last bytes of block. In other cases you must use LZMA_FINISH_ANY.
|
||||
|
||||
If LZMA decoder sees end marker before reaching output limit, it returns SZ_OK,
|
||||
and output value of destLen will be less than output buffer size limit.
|
||||
You can check status result also.
|
||||
|
||||
You can use multiple checks to test data integrity after full decompression:
|
||||
1) Check Result and "status" variable.
|
||||
2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize.
|
||||
3) Check that output(srcLen) = compressedSize, if you know real compressedSize.
|
||||
You must use correct finish mode in that case. */
|
||||
|
||||
typedef enum
|
||||
{
|
||||
LZMA_STATUS_NOT_SPECIFIED, /* use main error code instead */
|
||||
LZMA_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */
|
||||
LZMA_STATUS_NOT_FINISHED, /* stream was not finished */
|
||||
LZMA_STATUS_NEEDS_MORE_INPUT, /* you must provide more input bytes */
|
||||
LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK /* there is probability that stream was finished without end mark */
|
||||
} ELzmaStatus;
|
||||
|
||||
/* ELzmaStatus is used only as output value for function call */
|
||||
|
||||
|
||||
/* ---------- Interfaces ---------- */
|
||||
|
||||
/* There are 3 levels of interfaces:
|
||||
1) Dictionary Interface
|
||||
2) Buffer Interface
|
||||
3) One Call Interface
|
||||
You can select any of these interfaces, but don't mix functions from different
|
||||
groups for same object. */
|
||||
|
||||
|
||||
/* There are two variants to allocate state for Dictionary Interface:
|
||||
1) LzmaDec_Allocate / LzmaDec_Free
|
||||
2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs
|
||||
You can use variant 2, if you set dictionary buffer manually.
|
||||
For Buffer Interface you must always use variant 1.
|
||||
|
||||
LzmaDec_Allocate* can return:
|
||||
SZ_OK
|
||||
SZ_ERROR_MEM - Memory allocation error
|
||||
SZ_ERROR_UNSUPPORTED - Unsupported properties
|
||||
*/
|
||||
|
||||
SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAllocPtr alloc);
|
||||
void LzmaDec_FreeProbs(CLzmaDec *p, ISzAllocPtr alloc);
|
||||
|
||||
SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAllocPtr alloc);
|
||||
void LzmaDec_Free(CLzmaDec *p, ISzAllocPtr alloc);
|
||||
|
||||
/* ---------- Dictionary Interface ---------- */
|
||||
|
||||
/* You can use it, if you want to eliminate the overhead for data copying from
|
||||
dictionary to some other external buffer.
|
||||
You must work with CLzmaDec variables directly in this interface.
|
||||
|
||||
STEPS:
|
||||
LzmaDec_Construct()
|
||||
LzmaDec_Allocate()
|
||||
for (each new stream)
|
||||
{
|
||||
LzmaDec_Init()
|
||||
while (it needs more decompression)
|
||||
{
|
||||
LzmaDec_DecodeToDic()
|
||||
use data from CLzmaDec::dic and update CLzmaDec::dicPos
|
||||
}
|
||||
}
|
||||
LzmaDec_Free()
|
||||
*/
|
||||
|
||||
/* LzmaDec_DecodeToDic
|
||||
|
||||
The decoding to internal dictionary buffer (CLzmaDec::dic).
|
||||
You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!!
|
||||
/* LzmaDec_DecodeToDic
|
||||
|
||||
The decoding to internal dictionary buffer (CLzmaDec::dic).
|
||||
You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!!
|
||||
|
||||
finishMode:
|
||||
It has meaning only if the decoding reaches output limit (dicLimit).
|
||||
LZMA_FINISH_ANY - Decode just dicLimit bytes.
|
||||
LZMA_FINISH_END - Stream must be finished after dicLimit.
|
||||
It has meaning only if the decoding reaches output limit (dicLimit).
|
||||
LZMA_FINISH_ANY - Decode just dicLimit bytes.
|
||||
LZMA_FINISH_END - Stream must be finished after dicLimit.
|
||||
|
||||
Returns:
|
||||
SZ_OK
|
||||
status:
|
||||
LZMA_STATUS_FINISHED_WITH_MARK
|
||||
LZMA_STATUS_NOT_FINISHED
|
||||
LZMA_STATUS_NEEDS_MORE_INPUT
|
||||
LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK
|
||||
SZ_ERROR_DATA - Data error
|
||||
SZ_OK
|
||||
status:
|
||||
LZMA_STATUS_FINISHED_WITH_MARK
|
||||
LZMA_STATUS_NOT_FINISHED
|
||||
LZMA_STATUS_NEEDS_MORE_INPUT
|
||||
LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK
|
||||
SZ_ERROR_DATA - Data error
|
||||
*/
|
||||
|
||||
SRes LzmaDec_DecodeToDic(CLzmaDec *p, size_t dicLimit,
|
||||
const uint8_t *src, size_t *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status);
|
||||
SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit,
|
||||
const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status);
|
||||
|
||||
|
||||
/* ---------- Buffer Interface ---------- */
|
||||
@@ -179,13 +195,13 @@ SZ_ERROR_DATA - Data error
|
||||
to work with CLzmaDec variables manually.
|
||||
|
||||
finishMode:
|
||||
It has meaning only if the decoding reaches output limit (*destLen).
|
||||
LZMA_FINISH_ANY - Decode just destLen bytes.
|
||||
LZMA_FINISH_END - Stream must be finished after (*destLen).
|
||||
It has meaning only if the decoding reaches output limit (*destLen).
|
||||
LZMA_FINISH_ANY - Decode just destLen bytes.
|
||||
LZMA_FINISH_END - Stream must be finished after (*destLen).
|
||||
*/
|
||||
|
||||
SRes LzmaDec_DecodeToBuf(CLzmaDec *p, uint8_t *dest, size_t *destLen,
|
||||
const uint8_t *src, size_t *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status);
|
||||
SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen,
|
||||
const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status);
|
||||
|
||||
|
||||
/* ---------- One Call Interface ---------- */
|
||||
@@ -193,28 +209,26 @@ SRes LzmaDec_DecodeToBuf(CLzmaDec *p, uint8_t *dest, size_t *destLen,
|
||||
/* LzmaDecode
|
||||
|
||||
finishMode:
|
||||
It has meaning only if the decoding reaches output limit (*destLen).
|
||||
LZMA_FINISH_ANY - Decode just destLen bytes.
|
||||
LZMA_FINISH_END - Stream must be finished after (*destLen).
|
||||
It has meaning only if the decoding reaches output limit (*destLen).
|
||||
LZMA_FINISH_ANY - Decode just destLen bytes.
|
||||
LZMA_FINISH_END - Stream must be finished after (*destLen).
|
||||
|
||||
Returns:
|
||||
SZ_OK
|
||||
status:
|
||||
LZMA_STATUS_FINISHED_WITH_MARK
|
||||
LZMA_STATUS_NOT_FINISHED
|
||||
LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK
|
||||
SZ_ERROR_DATA - Data error
|
||||
SZ_ERROR_MEM - Memory allocation error
|
||||
SZ_ERROR_UNSUPPORTED - Unsupported properties
|
||||
SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src).
|
||||
SZ_OK
|
||||
status:
|
||||
LZMA_STATUS_FINISHED_WITH_MARK
|
||||
LZMA_STATUS_NOT_FINISHED
|
||||
LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK
|
||||
SZ_ERROR_DATA - Data error
|
||||
SZ_ERROR_MEM - Memory allocation error
|
||||
SZ_ERROR_UNSUPPORTED - Unsupported properties
|
||||
SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src).
|
||||
*/
|
||||
|
||||
SRes LzmaDecode(uint8_t *dest, size_t *destLen, const uint8_t *src, size_t *srcLen,
|
||||
const uint8_t *propData, unsigned propSize, ELzmaFinishMode finishMode,
|
||||
ELzmaStatus *status, ISzAlloc *alloc);
|
||||
SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen,
|
||||
const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode,
|
||||
ELzmaStatus *status, ISzAllocPtr alloc);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
EXTERN_C_END
|
||||
|
||||
#endif
|
||||
Vendored
+2167
-1500
File diff suppressed because it is too large.
Load diff
Vendored
+29
-33
@@ -1,11 +1,9 @@
|
||||
/* LzmaEnc.h -- LZMA Encoder
|
||||
2013-01-18 : Igor Pavlov : Public domain */
|
||||
2017-07-27 : Igor Pavlov : Public domain */
|
||||
|
||||
#ifndef __LZMA_ENC_H
|
||||
#define __LZMA_ENC_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "7zTypes.h"
|
||||
|
||||
EXTERN_C_BEGIN
|
||||
@@ -14,12 +12,10 @@ EXTERN_C_BEGIN
|
||||
|
||||
typedef struct _CLzmaEncProps
|
||||
{
|
||||
int level; /* 0 <= level <= 9 */
|
||||
uint32_t dictSize; /* (1 << 12) <= dictSize <= (1 << 27) for 32-bit version
|
||||
(1 << 12) <= dictSize <= (1 << 30) for 64-bit version
|
||||
default = (1 << 24) */
|
||||
uint64_t reduceSize; /* estimated size of data that will be compressed. default = 0xFFFFFFFF.
|
||||
Encoder uses this value to reduce dictionary size */
|
||||
int level; /* 0 <= level <= 9 */
|
||||
UInt32 dictSize; /* (1 << 12) <= dictSize <= (1 << 27) for 32-bit version
|
||||
(1 << 12) <= dictSize <= (3 << 29) for 64-bit version
|
||||
default = (1 << 24) */
|
||||
int lc; /* 0 <= lc <= 8, default = 3 */
|
||||
int lp; /* 0 <= lp <= 4, default = 0 */
|
||||
int pb; /* 0 <= pb <= 4, default = 2 */
|
||||
@@ -27,53 +23,53 @@ typedef struct _CLzmaEncProps
|
||||
int fb; /* 5 <= fb <= 273, default = 32 */
|
||||
int btMode; /* 0 - hashChain Mode, 1 - binTree mode - normal, default = 1 */
|
||||
int numHashBytes; /* 2, 3 or 4, default = 4 */
|
||||
uint32_t mc; /* 1 <= mc <= (1 << 30), default = 32 */
|
||||
UInt32 mc; /* 1 <= mc <= (1 << 30), default = 32 */
|
||||
unsigned writeEndMark; /* 0 - do not write EOPM, 1 - write EOPM, default = 0 */
|
||||
int numThreads; /* 1 or 2, default = 2 */
|
||||
|
||||
UInt64 reduceSize; /* estimated size of data that will be compressed. default = (UInt64)(Int64)-1.
|
||||
Encoder uses this value to reduce dictionary size */
|
||||
} CLzmaEncProps;
|
||||
|
||||
void LzmaEncProps_Init(CLzmaEncProps *p);
|
||||
void LzmaEncProps_Normalize(CLzmaEncProps *p);
|
||||
uint32_t LzmaEncProps_GetDictSize(const CLzmaEncProps *props2);
|
||||
UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2);
|
||||
|
||||
|
||||
/* ---------- CLzmaEncHandle Interface ---------- */
|
||||
|
||||
/* LzmaEnc_* functions can return the following exit codes:
|
||||
Returns:
|
||||
/* LzmaEnc* functions can return the following exit codes:
|
||||
SRes:
|
||||
SZ_OK - OK
|
||||
SZ_ERROR_MEM - Memory allocation error
|
||||
SZ_ERROR_PARAM - Incorrect paramater in props
|
||||
SZ_ERROR_WRITE - Write callback error.
|
||||
SZ_ERROR_WRITE - ISeqOutStream write callback error
|
||||
SZ_ERROR_OUTPUT_EOF - output buffer overflow - version with (Byte *) output
|
||||
SZ_ERROR_PROGRESS - some break from progress callback
|
||||
SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version)
|
||||
SZ_ERROR_THREAD - error in multithreading functions (only for Mt version)
|
||||
*/
|
||||
|
||||
typedef void * CLzmaEncHandle;
|
||||
|
||||
CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc);
|
||||
void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig);
|
||||
CLzmaEncHandle LzmaEnc_Create(ISzAllocPtr alloc);
|
||||
void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAllocPtr alloc, ISzAllocPtr allocBig);
|
||||
|
||||
SRes LzmaEnc_SetProps(CLzmaEncHandle p, const CLzmaEncProps *props);
|
||||
SRes LzmaEnc_WriteProperties(CLzmaEncHandle p, unsigned char *properties, size_t *size);
|
||||
void LzmaEnc_SetDataSize(CLzmaEncHandle p, UInt64 expectedDataSiize);
|
||||
SRes LzmaEnc_WriteProperties(CLzmaEncHandle p, Byte *properties, SizeT *size);
|
||||
unsigned LzmaEnc_IsWriteEndMark(CLzmaEncHandle p);
|
||||
|
||||
SRes LzmaEnc_Encode(CLzmaEncHandle p, ISeqOutStream *outStream, ISeqInStream *inStream,
|
||||
ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig);
|
||||
SRes LzmaEnc_MemEncode(CLzmaEncHandle p, unsigned char *dest, size_t *destLen, const unsigned char *src, size_t srcLen,
|
||||
int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig);
|
||||
ICompressProgress *progress, ISzAllocPtr alloc, ISzAllocPtr allocBig);
|
||||
SRes LzmaEnc_MemEncode(CLzmaEncHandle p, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen,
|
||||
int writeEndMark, ICompressProgress *progress, ISzAllocPtr alloc, ISzAllocPtr allocBig);
|
||||
|
||||
|
||||
/* ---------- One Call Interface ---------- */
|
||||
|
||||
/* LzmaEncode
|
||||
Return code:
|
||||
SZ_OK - OK
|
||||
SZ_ERROR_MEM - Memory allocation error
|
||||
SZ_ERROR_PARAM - Incorrect paramater
|
||||
SZ_ERROR_OUTPUT_EOF - output buffer overflow
|
||||
SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version)
|
||||
*/
|
||||
|
||||
SRes LzmaEncode(unsigned char *dest, size_t *destLen, const unsigned char *src, size_t srcLen,
|
||||
const CLzmaEncProps *props, unsigned char *propsEncoded, size_t *propsSize, int writeEndMark,
|
||||
ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig);
|
||||
SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen,
|
||||
const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark,
|
||||
ICompressProgress *progress, ISzAllocPtr alloc, ISzAllocPtr allocBig);
|
||||
|
||||
EXTERN_C_END
|
||||
|
||||
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
/* Precomp.h -- StdAfx
|
||||
2013-11-12 : Igor Pavlov : Public domain */
|
||||
|
||||
#ifndef __7Z_PRECOMP_H
|
||||
#define __7Z_PRECOMP_H
|
||||
|
||||
#include "Compiler.h"
|
||||
/* #include "7zTypes.h" */
|
||||
|
||||
#endif
|
||||
+2
@@ -57,7 +57,9 @@ void Discord_RunCallbacks(void);
|
||||
|
||||
/* If you disable the lib starting its own I/O thread,
|
||||
* you'll need to call this from your own */
|
||||
#ifdef DISCORD_DISABLE_IO_THREAD
|
||||
void Discord_UpdateConnection(void);
|
||||
#endif
|
||||
|
||||
void Discord_UpdatePresence(const DiscordRichPresence* presence);
|
||||
void Discord_ClearPresence(void);
|
||||
|
||||
Vendored
+134
-58
@@ -10,10 +10,22 @@
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
|
||||
#ifndef DISCORD_DISABLE_IO_THREAD
|
||||
#include <condition_variable>
|
||||
#include <thread>
|
||||
#endif
|
||||
|
||||
struct QueuedMessage
|
||||
{
|
||||
size_t length;
|
||||
char buffer[16384];
|
||||
|
||||
void Copy(const QueuedMessage& other)
|
||||
{
|
||||
length = other.length;
|
||||
if (length)
|
||||
memcpy(buffer, other.buffer, length);
|
||||
}
|
||||
};
|
||||
|
||||
struct User
|
||||
@@ -68,13 +80,66 @@ static User connectedUser;
|
||||
static Backoff ReconnectTimeMs(500, 60 * 1000);
|
||||
static auto NextConnect = std::chrono::system_clock::now();
|
||||
|
||||
static void update_reconnect_time(void)
|
||||
#ifndef DISCORD_DISABLE_IO_THREAD
|
||||
static void Discord_UpdateConnection(void);
|
||||
class IoThreadHolder
|
||||
{
|
||||
private:
|
||||
std::atomic_bool keepRunning{true};
|
||||
std::mutex waitForIOMutex;
|
||||
std::condition_variable waitForIOActivity;
|
||||
std::thread ioThread;
|
||||
|
||||
public:
|
||||
void Start()
|
||||
{
|
||||
keepRunning.store(true);
|
||||
ioThread = std::thread([&]() {
|
||||
const std::chrono::duration<int64_t, std::milli> maxWait{500LL};
|
||||
Discord_UpdateConnection();
|
||||
while (keepRunning.load()) {
|
||||
std::unique_lock<std::mutex> lock(waitForIOMutex);
|
||||
waitForIOActivity.wait_for(lock, maxWait);
|
||||
Discord_UpdateConnection();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Notify() { waitForIOActivity.notify_all(); }
|
||||
|
||||
void Stop()
|
||||
{
|
||||
keepRunning.exchange(false);
|
||||
Notify();
|
||||
if (ioThread.joinable())
|
||||
ioThread.join();
|
||||
}
|
||||
|
||||
~IoThreadHolder() { Stop(); }
|
||||
};
|
||||
#else
|
||||
class IoThreadHolder
|
||||
{
|
||||
public:
|
||||
void Start() {}
|
||||
void Stop() {}
|
||||
void Notify() {}
|
||||
};
|
||||
#endif /* DISCORD_DISABLE_IO_THREAD */
|
||||
|
||||
static IoThreadHolder* IoThread{nullptr};
|
||||
|
||||
static void UpdateReconnectTime(void)
|
||||
{
|
||||
NextConnect = std::chrono::system_clock::now() +
|
||||
std::chrono::duration<int64_t, std::milli>{ReconnectTimeMs.nextDelay()};
|
||||
}
|
||||
|
||||
#ifdef DISCORD_DISABLE_IO_THREAD
|
||||
extern "C" void Discord_UpdateConnection(void)
|
||||
#else
|
||||
static void Discord_UpdateConnection(void)
|
||||
#endif
|
||||
{
|
||||
if (!Connection)
|
||||
return;
|
||||
@@ -83,7 +148,7 @@ extern "C" void Discord_UpdateConnection(void)
|
||||
{
|
||||
if (std::chrono::system_clock::now() >= NextConnect)
|
||||
{
|
||||
update_reconnect_time();
|
||||
UpdateReconnectTime();
|
||||
Connection->Open();
|
||||
}
|
||||
}
|
||||
@@ -203,19 +268,16 @@ extern "C" void Discord_UpdateConnection(void)
|
||||
if (QueuedPresence.length)
|
||||
{
|
||||
QueuedMessage local;
|
||||
std::lock_guard<std::mutex> guard(PresenceMutex);
|
||||
local.length = QueuedPresence.length;
|
||||
if (local.length)
|
||||
memcpy(local.buffer, QueuedPresence.buffer, local.length);
|
||||
QueuedPresence.length = 0;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(PresenceMutex);
|
||||
local.Copy(QueuedPresence);
|
||||
QueuedPresence.length = 0;
|
||||
}
|
||||
if (!Connection->Write(local.buffer, local.length))
|
||||
{
|
||||
/* if we fail to send, requeue */
|
||||
std::lock_guard<std::mutex> guard(PresenceMutex);
|
||||
QueuedPresence.length = local.length;
|
||||
if (QueuedPresence.length)
|
||||
memcpy(QueuedPresence.buffer, local.buffer, QueuedPresence.length);
|
||||
QueuedPresence.Copy(local);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,6 +298,8 @@ static bool RegisterForEvent(const char* evtName)
|
||||
qmessage->length =
|
||||
JsonWriteSubscribeCommand(qmessage->buffer, sizeof(qmessage->buffer), Nonce++, evtName);
|
||||
SendQueue.CommitAdd();
|
||||
if (IoThread)
|
||||
IoThread->Notify();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -249,6 +313,8 @@ static bool DeregisterForEvent(const char* evtName)
|
||||
qmessage->length =
|
||||
JsonWriteUnsubscribeCommand(qmessage->buffer, sizeof(qmessage->buffer), Nonce++, evtName);
|
||||
SendQueue.CommitAdd();
|
||||
if (IoThread)
|
||||
IoThread->Notify();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -260,6 +326,10 @@ extern "C" void Discord_Initialize(
|
||||
int autoRegister,
|
||||
const char* optionalSteamId)
|
||||
{
|
||||
IoThread = new (std::nothrow) IoThreadHolder();
|
||||
if (!IoThread)
|
||||
return;
|
||||
|
||||
if (autoRegister)
|
||||
{
|
||||
if (optionalSteamId && optionalSteamId[0])
|
||||
@@ -284,38 +354,34 @@ extern "C" void Discord_Initialize(
|
||||
if (Connection)
|
||||
return;
|
||||
|
||||
Connection = RpcConnection::Create(applicationId);
|
||||
Connection = RpcConnection::Create(applicationId);
|
||||
Connection->onConnect = [](JsonDocument& readyMessage)
|
||||
{
|
||||
Discord_UpdateHandlers(&QueuedHandlers);
|
||||
char *userId = NULL;
|
||||
char *username = NULL;
|
||||
char *avatar = NULL;
|
||||
char *discriminator = NULL;
|
||||
bool in_data = false;
|
||||
bool in_user = false;
|
||||
|
||||
Discord_UpdateHandlers(&QueuedHandlers);
|
||||
|
||||
bool in_data = false, in_user = false;
|
||||
for (JsonReader r(readyMessage); r.NextKey();)
|
||||
{
|
||||
if (r.depth == 1)
|
||||
{
|
||||
in_data = !strcmp(r.key,"data");
|
||||
in_user = false;
|
||||
}
|
||||
else if (r.depth == 2 && in_data)
|
||||
in_user = !strcmp(r.key, "user");
|
||||
else if (r.depth == 3 && in_user)
|
||||
{
|
||||
if (!strcmp(r.key, "id" ))
|
||||
r.NextStrDup(&userId);
|
||||
else if (!strcmp(r.key, "username" ))
|
||||
r.NextStrDup(&username);
|
||||
else if (!strcmp(r.key, "avatar" ))
|
||||
r.NextStrDup(&avatar);
|
||||
else if (!strcmp(r.key, "discriminator"))
|
||||
r.NextStrDup(&discriminator);
|
||||
}
|
||||
if (r.depth == 1)
|
||||
{
|
||||
in_data = !strcmp(r.key,"data");
|
||||
in_user = false;
|
||||
}
|
||||
else if (r.depth == 2 && in_data)
|
||||
{
|
||||
in_user = !strcmp(r.key, "user");
|
||||
}
|
||||
else if (r.depth == 3 && in_user)
|
||||
{
|
||||
if (!strcmp(r.key, "id" )) r.NextStrDup(&userId);
|
||||
else if (!strcmp(r.key, "username" )) r.NextStrDup(&username);
|
||||
else if (!strcmp(r.key, "avatar" )) r.NextStrDup(&avatar);
|
||||
else if (!strcmp(r.key, "discriminator")) r.NextStrDup(&discriminator);
|
||||
}
|
||||
}
|
||||
|
||||
if (userId && username)
|
||||
@@ -332,14 +398,10 @@ extern "C" void Discord_Initialize(
|
||||
WasJustConnected.exchange(true);
|
||||
ReconnectTimeMs.reset();
|
||||
|
||||
if (userId)
|
||||
free(userId);
|
||||
if (username)
|
||||
free(username);
|
||||
if (avatar)
|
||||
free(avatar);
|
||||
if (discriminator)
|
||||
free(discriminator);
|
||||
if (userId ) free(userId );
|
||||
if (username ) free(username );
|
||||
if (avatar ) free(avatar );
|
||||
if (discriminator) free(discriminator);
|
||||
};
|
||||
Connection->onDisconnect = [](int err, const char* message)
|
||||
{
|
||||
@@ -350,8 +412,10 @@ extern "C" void Discord_Initialize(
|
||||
Handlers = {};
|
||||
}
|
||||
WasJustDisconnected.exchange(true);
|
||||
update_reconnect_time();
|
||||
UpdateReconnectTime();
|
||||
};
|
||||
|
||||
IoThread->Start();
|
||||
}
|
||||
|
||||
extern "C" void Discord_Shutdown(void)
|
||||
@@ -360,17 +424,26 @@ extern "C" void Discord_Shutdown(void)
|
||||
return;
|
||||
Connection->onConnect = nullptr;
|
||||
Connection->onDisconnect = nullptr;
|
||||
Handlers = {};
|
||||
Handlers = {};
|
||||
if (IoThread)
|
||||
{
|
||||
IoThread->Stop();
|
||||
delete IoThread;
|
||||
IoThread = nullptr;
|
||||
}
|
||||
|
||||
RpcConnection::Destroy(Connection);
|
||||
}
|
||||
|
||||
extern "C" void Discord_UpdatePresence(const DiscordRichPresence* presence)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(PresenceMutex);
|
||||
QueuedPresence.length = JsonWriteRichPresenceObj(
|
||||
QueuedPresence.buffer, sizeof(QueuedPresence.buffer),
|
||||
Nonce++, Pid, presence);
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(PresenceMutex);
|
||||
QueuedPresence.length = JsonWriteRichPresenceObj(
|
||||
QueuedPresence.buffer, sizeof(QueuedPresence.buffer), Nonce++, Pid, presence);
|
||||
}
|
||||
if (IoThread)
|
||||
IoThread->Notify();
|
||||
}
|
||||
|
||||
extern "C" void Discord_ClearPresence(void)
|
||||
@@ -378,8 +451,7 @@ extern "C" void Discord_ClearPresence(void)
|
||||
Discord_UpdatePresence(nullptr);
|
||||
}
|
||||
|
||||
extern "C" void Discord_Respond(const char* userId,
|
||||
/* DISCORD_REPLY_ */ int reply)
|
||||
extern "C" void Discord_Respond(const char* userId, /* DISCORD_REPLY_ */ int reply)
|
||||
{
|
||||
/* if we are not connected, let's not batch up stale messages for later */
|
||||
if (!Connection || !Connection->IsOpen())
|
||||
@@ -390,6 +462,8 @@ extern "C" void Discord_Respond(const char* userId,
|
||||
qmessage->length =
|
||||
JsonWriteJoinReply(qmessage->buffer, sizeof(qmessage->buffer), userId, reply, Nonce++);
|
||||
SendQueue.CommitAdd();
|
||||
if (IoThread)
|
||||
IoThread->Notify();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -456,14 +530,16 @@ extern "C" void Discord_RunCallbacks(void)
|
||||
*/
|
||||
while (JoinAskQueue.HavePendingSends())
|
||||
{
|
||||
auto req = JoinAskQueue.GetNextSendMessage();
|
||||
std::lock_guard<std::mutex> guard(HandlerMutex);
|
||||
if (Handlers.joinRequest)
|
||||
{
|
||||
DiscordUser du{req->userId, req->username, req->discriminator, req->avatar};
|
||||
Handlers.joinRequest(&du);
|
||||
}
|
||||
JoinAskQueue.CommitSend();
|
||||
auto req = JoinAskQueue.GetNextSendMessage();
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(HandlerMutex);
|
||||
if (Handlers.joinRequest)
|
||||
{
|
||||
DiscordUser du{req->userId, req->username, req->discriminator, req->avatar};
|
||||
Handlers.joinRequest(&du);
|
||||
}
|
||||
}
|
||||
JoinAskQueue.CommitSend();
|
||||
}
|
||||
|
||||
if (!isConnected)
|
||||
|
||||
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
+83
-34
@@ -18,9 +18,6 @@
|
||||
|
||||
/* Defines */
|
||||
|
||||
#define DISPLAY_WIDTH 960
|
||||
#define DISPLAY_HEIGHT 544
|
||||
#define DISPLAY_STRIDE_IN_PIXELS 1024
|
||||
#define DISPLAY_COLOR_FORMAT SCE_GXM_COLOR_FORMAT_A8B8G8R8
|
||||
#define DISPLAY_PIXEL_FORMAT SCE_DISPLAY_PIXELFORMAT_A8B8G8R8
|
||||
#define DISPLAY_BUFFER_COUNT 3
|
||||
@@ -44,6 +41,9 @@ extern const SceGxmProgram texture_tint_f_gxp;
|
||||
|
||||
/* Static variables */
|
||||
|
||||
static vita2d_video_mode_data video_mode_data;
|
||||
static vita2d_video_mode video_mode_initial;
|
||||
|
||||
static int pgf_module_was_loaded = 0;
|
||||
|
||||
static const SceGxmProgram *const clearVertexProgramGxp = &clear_v_gxp;
|
||||
@@ -60,8 +60,8 @@ static float clear_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
static unsigned int clear_color_u = 0xFF000000;
|
||||
static int clip_rect_x_min = 0;
|
||||
static int clip_rect_y_min = 0;
|
||||
static int clip_rect_x_max = DISPLAY_WIDTH;
|
||||
static int clip_rect_y_max = DISPLAY_HEIGHT;
|
||||
static int clip_rect_x_max = 0;
|
||||
static int clip_rect_y_max = 0;
|
||||
static int vblank_wait = 1;
|
||||
static int drawing = 0;
|
||||
static int clipping_enabled = 0;
|
||||
@@ -152,6 +152,40 @@ static void patcher_host_free(void *user_data, void *mem)
|
||||
free(mem);
|
||||
}
|
||||
|
||||
vita2d_video_mode_data vita2d_get_video_mode_data(){
|
||||
return video_mode_data;
|
||||
}
|
||||
|
||||
static int vita2d_switch_video_mode(vita2d_video_mode video_mode){
|
||||
if(video_mode > video_mode_initial)
|
||||
return -1;
|
||||
switch (video_mode)
|
||||
{
|
||||
case VITA2D_VIDEO_MODE_960x544:
|
||||
video_mode_data.width = 960;
|
||||
video_mode_data.height = 544;
|
||||
video_mode_data.stride = 1280;
|
||||
break;
|
||||
|
||||
case VITA2D_VIDEO_MODE_1280x720:
|
||||
video_mode_data.width = 1280;
|
||||
video_mode_data.height = 720;
|
||||
video_mode_data.stride = 1280;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
|
||||
clip_rect_x_max = video_mode_data.width;
|
||||
clip_rect_y_max = video_mode_data.height;
|
||||
|
||||
matrix_init_orthographic(_vita2d_ortho_matrix, 0.0f, video_mode_data.width, video_mode_data.height, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void display_callback(const void *callback_data)
|
||||
{
|
||||
SceDisplayFrameBuf framebuf;
|
||||
@@ -160,11 +194,14 @@ static void display_callback(const void *callback_data)
|
||||
memset(&framebuf, 0x00, sizeof(SceDisplayFrameBuf));
|
||||
framebuf.size = sizeof(SceDisplayFrameBuf);
|
||||
framebuf.base = display_data->address;
|
||||
framebuf.pitch = DISPLAY_STRIDE_IN_PIXELS;
|
||||
framebuf.pitch = video_mode_data.stride;
|
||||
framebuf.pixelformat = DISPLAY_PIXEL_FORMAT;
|
||||
framebuf.width = DISPLAY_WIDTH;
|
||||
framebuf.height = DISPLAY_HEIGHT;
|
||||
sceDisplaySetFrameBuf(&framebuf, SCE_DISPLAY_SETBUF_NEXTFRAME);
|
||||
framebuf.width = video_mode_data.width;
|
||||
framebuf.height = video_mode_data.height;
|
||||
if(sceDisplaySetFrameBuf(&framebuf, SCE_DISPLAY_SETBUF_NEXTFRAME)<0){
|
||||
if(video_mode_initial)
|
||||
vita2d_switch_video_mode(VITA2D_VIDEO_MODE_960x544);
|
||||
}
|
||||
|
||||
if (vblank_wait) {
|
||||
sceDisplayWaitVblankStart();
|
||||
@@ -218,7 +255,7 @@ static void _vita2d_make_fragment_programs(vita2d_fragment_programs *out,
|
||||
VITA2D_DEBUG("texture_tint sceGxmShaderPatcherCreateFragmentProgram(): 0x%08X\n", err);
|
||||
}
|
||||
|
||||
static int vita2d_init_internal(unsigned int temp_pool_size, SceGxmMultisampleMode msaa)
|
||||
static int vita2d_init_internal(unsigned int temp_pool_size, SceGxmMultisampleMode msaa, vita2d_video_mode video_mode)
|
||||
{
|
||||
int err;
|
||||
unsigned int i, x, y;
|
||||
@@ -229,6 +266,14 @@ static int vita2d_init_internal(unsigned int temp_pool_size, SceGxmMultisampleMo
|
||||
return 1;
|
||||
}
|
||||
|
||||
video_mode_initial = video_mode;
|
||||
|
||||
err = vita2d_switch_video_mode(video_mode);
|
||||
if(err<0){
|
||||
VITA2D_DEBUG("vita2d_switch_video_mode(): 0x%08X\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
SceGxmInitializeParams initializeParams;
|
||||
memset(&initializeParams, 0, sizeof(SceGxmInitializeParams));
|
||||
initializeParams.flags = 0;
|
||||
@@ -288,8 +333,8 @@ static int vita2d_init_internal(unsigned int temp_pool_size, SceGxmMultisampleMo
|
||||
SceGxmRenderTargetParams renderTargetParams;
|
||||
memset(&renderTargetParams, 0, sizeof(SceGxmRenderTargetParams));
|
||||
renderTargetParams.flags = 0;
|
||||
renderTargetParams.width = DISPLAY_WIDTH;
|
||||
renderTargetParams.height = DISPLAY_HEIGHT;
|
||||
renderTargetParams.width = video_mode_data.width;
|
||||
renderTargetParams.height = video_mode_data.height;
|
||||
renderTargetParams.scenesPerFrame = 1;
|
||||
renderTargetParams.multisampleMode = msaa;
|
||||
renderTargetParams.multisampleLocations = 0;
|
||||
@@ -304,15 +349,15 @@ static int vita2d_init_internal(unsigned int temp_pool_size, SceGxmMultisampleMo
|
||||
// allocate memory for display
|
||||
displayBufferData[i] = gpu_alloc(
|
||||
SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW,
|
||||
4*DISPLAY_STRIDE_IN_PIXELS*DISPLAY_HEIGHT,
|
||||
4*video_mode_data.stride*video_mode_data.height,
|
||||
SCE_GXM_COLOR_SURFACE_ALIGNMENT,
|
||||
SCE_GXM_MEMORY_ATTRIB_READ | SCE_GXM_MEMORY_ATTRIB_WRITE,
|
||||
&displayBufferUid[i]);
|
||||
|
||||
// memset the buffer to black
|
||||
for (y = 0; y < DISPLAY_HEIGHT; y++) {
|
||||
unsigned int *row = (unsigned int *)displayBufferData[i] + y*DISPLAY_STRIDE_IN_PIXELS;
|
||||
for (x = 0; x < DISPLAY_WIDTH; x++) {
|
||||
for (y = 0; y < video_mode_data.height; y++) {
|
||||
unsigned int *row = (unsigned int *)displayBufferData[i] + y*video_mode_data.stride;
|
||||
for (x = 0; x < video_mode_data.width; x++) {
|
||||
row[x] = 0xff000000;
|
||||
}
|
||||
}
|
||||
@@ -324,9 +369,9 @@ static int vita2d_init_internal(unsigned int temp_pool_size, SceGxmMultisampleMo
|
||||
SCE_GXM_COLOR_SURFACE_LINEAR,
|
||||
(msaa == SCE_GXM_MULTISAMPLE_NONE) ? SCE_GXM_COLOR_SURFACE_SCALE_NONE : SCE_GXM_COLOR_SURFACE_SCALE_MSAA_DOWNSCALE,
|
||||
SCE_GXM_OUTPUT_REGISTER_SIZE_32BIT,
|
||||
DISPLAY_WIDTH,
|
||||
DISPLAY_HEIGHT,
|
||||
DISPLAY_STRIDE_IN_PIXELS,
|
||||
video_mode_data.width,
|
||||
video_mode_data.height,
|
||||
video_mode_data.stride,
|
||||
displayBufferData[i]);
|
||||
|
||||
// create a sync object that we will associate with this buffer
|
||||
@@ -334,8 +379,8 @@ static int vita2d_init_internal(unsigned int temp_pool_size, SceGxmMultisampleMo
|
||||
}
|
||||
|
||||
// compute the memory footprint of the depth buffer
|
||||
const unsigned int alignedWidth = ALIGN(DISPLAY_WIDTH, SCE_GXM_TILE_SIZEX);
|
||||
const unsigned int alignedHeight = ALIGN(DISPLAY_HEIGHT, SCE_GXM_TILE_SIZEY);
|
||||
const unsigned int alignedWidth = ALIGN(video_mode_data.width, SCE_GXM_TILE_SIZEX);
|
||||
const unsigned int alignedHeight = ALIGN(video_mode_data.height, SCE_GXM_TILE_SIZEY);
|
||||
unsigned int sampleCount = alignedWidth*alignedHeight;
|
||||
unsigned int depthStrideInSamples = alignedWidth;
|
||||
if (msaa == SCE_GXM_MULTISAMPLE_4X) {
|
||||
@@ -713,9 +758,9 @@ static int vita2d_init_internal(unsigned int temp_pool_size, SceGxmMultisampleMo
|
||||
sizeof(void *),
|
||||
SCE_GXM_MEMORY_ATTRIB_READ,
|
||||
&poolUid);
|
||||
|
||||
|
||||
matrix_init_orthographic(_vita2d_ortho_matrix, 0.0f, DISPLAY_WIDTH, DISPLAY_HEIGHT, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
matrix_init_orthographic(_vita2d_ortho_matrix, 0.0f, video_mode_data.width, video_mode_data.height, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
backBufferIndex = 0;
|
||||
frontBufferIndex = 0;
|
||||
@@ -725,23 +770,27 @@ static int vita2d_init_internal(unsigned int temp_pool_size, SceGxmMultisampleMo
|
||||
if (pgf_module_was_loaded != SCE_SYSMODULE_LOADED)
|
||||
sceSysmoduleLoadModule(SCE_SYSMODULE_PGF);
|
||||
|
||||
vita2d_display_data displayData;
|
||||
displayData.address = displayBufferData[backBufferIndex];
|
||||
display_callback(&displayData);
|
||||
|
||||
vita2d_initialized = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int vita2d_init()
|
||||
{
|
||||
return vita2d_init_internal(DEFAULT_TEMP_POOL_SIZE, SCE_GXM_MULTISAMPLE_NONE);
|
||||
return vita2d_init_internal(DEFAULT_TEMP_POOL_SIZE, SCE_GXM_MULTISAMPLE_NONE, VITA2D_VIDEO_MODE_960x544);
|
||||
}
|
||||
|
||||
int vita2d_init_advanced(unsigned int temp_pool_size)
|
||||
{
|
||||
return vita2d_init_internal(temp_pool_size, SCE_GXM_MULTISAMPLE_NONE);
|
||||
return vita2d_init_internal(temp_pool_size, SCE_GXM_MULTISAMPLE_NONE, VITA2D_VIDEO_MODE_960x544);
|
||||
}
|
||||
|
||||
int vita2d_init_advanced_with_msaa(unsigned int temp_pool_size, SceGxmMultisampleMode msaa)
|
||||
int vita2d_init_advanced_with_msaa(unsigned int temp_pool_size, SceGxmMultisampleMode msaa, vita2d_video_mode video_mode)
|
||||
{
|
||||
return vita2d_init_internal(temp_pool_size, msaa);
|
||||
return vita2d_init_internal(temp_pool_size, msaa, video_mode);
|
||||
}
|
||||
|
||||
void vita2d_wait_rendering_done()
|
||||
@@ -780,7 +829,7 @@ int vita2d_fini()
|
||||
gpu_free(depthBufferUid);
|
||||
for (i = 0; i < DISPLAY_BUFFER_COUNT; i++) {
|
||||
// clear the buffer then deallocate
|
||||
memset(displayBufferData[i], 0, DISPLAY_HEIGHT*DISPLAY_STRIDE_IN_PIXELS*4);
|
||||
memset(displayBufferData[i], 0, video_mode_data.height*video_mode_data.stride*4);
|
||||
gpu_free(displayBufferUid[i]);
|
||||
|
||||
// destroy the sync object
|
||||
@@ -933,7 +982,7 @@ int vita2d_get_clipping_enabled()
|
||||
|
||||
void vita2d_set_clip_rectangle(int x_min, int y_min, int x_max, int y_max)
|
||||
{
|
||||
vita2d_set_viewport(0,0,DISPLAY_WIDTH,DISPLAY_HEIGHT);
|
||||
vita2d_set_viewport(0,0,video_mode_data.width,video_mode_data.height);
|
||||
clipping_enabled = 1;
|
||||
clip_rect_x_min = x_min;
|
||||
clip_rect_y_min = y_min;
|
||||
@@ -952,7 +1001,7 @@ void vita2d_set_clip_rectangle(int x_min, int y_min, int x_max, int y_max)
|
||||
SCE_GXM_STENCIL_OP_ZERO,
|
||||
0xFF,
|
||||
0xFF);
|
||||
vita2d_draw_rectangle(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, 0);
|
||||
vita2d_draw_rectangle(0, 0, video_mode_data.width, video_mode_data.height, 0);
|
||||
// set the stencil to 1 in the desired region
|
||||
sceGxmSetFrontStencilFunc(
|
||||
_vita2d_context,
|
||||
@@ -1003,9 +1052,9 @@ int vita2d_common_dialog_update()
|
||||
|
||||
updateParam.renderTarget.colorFormat = DISPLAY_COLOR_FORMAT;
|
||||
updateParam.renderTarget.surfaceType = SCE_GXM_COLOR_SURFACE_LINEAR;
|
||||
updateParam.renderTarget.width = DISPLAY_WIDTH;
|
||||
updateParam.renderTarget.height = DISPLAY_HEIGHT;
|
||||
updateParam.renderTarget.strideInPixels = DISPLAY_STRIDE_IN_PIXELS;
|
||||
updateParam.renderTarget.width = video_mode_data.width;
|
||||
updateParam.renderTarget.height = video_mode_data.height;
|
||||
updateParam.renderTarget.strideInPixels = video_mode_data.stride;
|
||||
|
||||
updateParam.renderTarget.colorSurfaceData = displayBufferData[backBufferIndex];
|
||||
updateParam.renderTarget.depthSurfaceData = depthBufferData;
|
||||
@@ -1100,7 +1149,7 @@ void vita2d_set_blend_mode_add(int enable)
|
||||
}
|
||||
|
||||
void vita2d_set_viewport(int x, int y, int width, int height){
|
||||
static float vh = DISPLAY_HEIGHT;
|
||||
float vh = video_mode_data.height;
|
||||
float sw = width / 2.;
|
||||
float sh = height / 2.;
|
||||
float x_scale = sw;
|
||||
|
||||
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
+122
@@ -0,0 +1,122 @@
|
||||
#ifndef RC_CONSOLES_H
|
||||
#define RC_CONSOLES_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*****************************************************************************\
|
||||
| Console identifiers |
|
||||
\*****************************************************************************/
|
||||
|
||||
enum {
|
||||
RC_CONSOLE_MEGA_DRIVE = 1,
|
||||
RC_CONSOLE_NINTENDO_64 = 2,
|
||||
RC_CONSOLE_SUPER_NINTENDO = 3,
|
||||
RC_CONSOLE_GAMEBOY = 4,
|
||||
RC_CONSOLE_GAMEBOY_ADVANCE = 5,
|
||||
RC_CONSOLE_GAMEBOY_COLOR = 6,
|
||||
RC_CONSOLE_NINTENDO = 7,
|
||||
RC_CONSOLE_PC_ENGINE = 8,
|
||||
RC_CONSOLE_SEGA_CD = 9,
|
||||
RC_CONSOLE_SEGA_32X = 10,
|
||||
RC_CONSOLE_MASTER_SYSTEM = 11,
|
||||
RC_CONSOLE_PLAYSTATION = 12,
|
||||
RC_CONSOLE_ATARI_LYNX = 13,
|
||||
RC_CONSOLE_NEOGEO_POCKET = 14,
|
||||
RC_CONSOLE_GAME_GEAR = 15,
|
||||
RC_CONSOLE_GAMECUBE = 16,
|
||||
RC_CONSOLE_ATARI_JAGUAR = 17,
|
||||
RC_CONSOLE_NINTENDO_DS = 18,
|
||||
RC_CONSOLE_WII = 19,
|
||||
RC_CONSOLE_WII_U = 20,
|
||||
RC_CONSOLE_PLAYSTATION_2 = 21,
|
||||
RC_CONSOLE_XBOX = 22,
|
||||
RC_CONSOLE_MAGNAVOX_ODYSSEY2 = 23,
|
||||
RC_CONSOLE_POKEMON_MINI = 24,
|
||||
RC_CONSOLE_ATARI_2600 = 25,
|
||||
RC_CONSOLE_MS_DOS = 26,
|
||||
RC_CONSOLE_ARCADE = 27,
|
||||
RC_CONSOLE_VIRTUAL_BOY = 28,
|
||||
RC_CONSOLE_MSX = 29,
|
||||
RC_CONSOLE_COMMODORE_64 = 30,
|
||||
RC_CONSOLE_ZX81 = 31,
|
||||
RC_CONSOLE_ORIC = 32,
|
||||
RC_CONSOLE_SG1000 = 33,
|
||||
RC_CONSOLE_VIC20 = 34,
|
||||
RC_CONSOLE_AMIGA = 35,
|
||||
RC_CONSOLE_ATARI_ST = 36,
|
||||
RC_CONSOLE_AMSTRAD_PC = 37,
|
||||
RC_CONSOLE_APPLE_II = 38,
|
||||
RC_CONSOLE_SATURN = 39,
|
||||
RC_CONSOLE_DREAMCAST = 40,
|
||||
RC_CONSOLE_PSP = 41,
|
||||
RC_CONSOLE_CDI = 42,
|
||||
RC_CONSOLE_3DO = 43,
|
||||
RC_CONSOLE_COLECOVISION = 44,
|
||||
RC_CONSOLE_INTELLIVISION = 45,
|
||||
RC_CONSOLE_VECTREX = 46,
|
||||
RC_CONSOLE_PC8800 = 47,
|
||||
RC_CONSOLE_PC9800 = 48,
|
||||
RC_CONSOLE_PCFX = 49,
|
||||
RC_CONSOLE_ATARI_5200 = 50,
|
||||
RC_CONSOLE_ATARI_7800 = 51,
|
||||
RC_CONSOLE_X68K = 52,
|
||||
RC_CONSOLE_WONDERSWAN = 53,
|
||||
RC_CONSOLE_CASSETTEVISION = 54,
|
||||
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_CONSOLE_HUBS = 100,
|
||||
RC_CONSOLE_EVENTS = 101
|
||||
};
|
||||
|
||||
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
|
||||
|
||||
#endif /* RC_CONSOLES_H */
|
||||
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
+19
-11
@@ -1,11 +1,11 @@
|
||||
#ifndef RHASH_H
|
||||
#define RHASH_H
|
||||
#ifndef RC_HASH_H
|
||||
#define RC_HASH_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "rconsoles.h"
|
||||
#include "rc_consoles.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -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
+142
@@ -0,0 +1,142 @@
|
||||
#ifndef RC_RUNTIME_H
|
||||
#define RC_RUNTIME_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "rc_error.h"
|
||||
|
||||
/*****************************************************************************\
|
||||
| Forward Declarations (defined in rc_runtime_types.h) |
|
||||
\*****************************************************************************/
|
||||
|
||||
#ifndef RC_RUNTIME_TYPES_H /* prevents pedantic redefinition error */
|
||||
|
||||
typedef struct lua_State lua_State;
|
||||
|
||||
typedef struct rc_trigger_t rc_trigger_t;
|
||||
typedef struct rc_lboard_t rc_lboard_t;
|
||||
typedef struct rc_richpresence_t rc_richpresence_t;
|
||||
typedef struct rc_memref_t rc_memref_t;
|
||||
typedef struct rc_value_t rc_value_t;
|
||||
|
||||
#endif
|
||||
|
||||
/*****************************************************************************\
|
||||
| Callbacks |
|
||||
\*****************************************************************************/
|
||||
|
||||
/**
|
||||
* Callback used to read num_bytes bytes from memory starting at address. If
|
||||
* num_bytes is greater than 1, the value is read in little-endian from
|
||||
* memory.
|
||||
*/
|
||||
typedef unsigned (*rc_runtime_peek_t)(unsigned address, unsigned num_bytes, void* ud);
|
||||
|
||||
/*****************************************************************************\
|
||||
| Runtime |
|
||||
\*****************************************************************************/
|
||||
|
||||
typedef struct rc_runtime_trigger_t {
|
||||
unsigned id;
|
||||
rc_trigger_t* trigger;
|
||||
void* buffer;
|
||||
rc_memref_t* invalid_memref;
|
||||
unsigned char md5[16];
|
||||
int serialized_size;
|
||||
char owns_memrefs;
|
||||
}
|
||||
rc_runtime_trigger_t;
|
||||
|
||||
typedef struct rc_runtime_lboard_t {
|
||||
unsigned id;
|
||||
int value;
|
||||
rc_lboard_t* lboard;
|
||||
void* buffer;
|
||||
rc_memref_t* invalid_memref;
|
||||
unsigned char md5[16];
|
||||
char owns_memrefs;
|
||||
}
|
||||
rc_runtime_lboard_t;
|
||||
|
||||
typedef struct rc_runtime_richpresence_t {
|
||||
rc_richpresence_t* richpresence;
|
||||
void* buffer;
|
||||
struct rc_runtime_richpresence_t* previous;
|
||||
char owns_memrefs;
|
||||
}
|
||||
rc_runtime_richpresence_t;
|
||||
|
||||
typedef struct rc_runtime_t {
|
||||
rc_runtime_trigger_t* triggers;
|
||||
unsigned trigger_count;
|
||||
unsigned trigger_capacity;
|
||||
|
||||
rc_runtime_lboard_t* lboards;
|
||||
unsigned lboard_count;
|
||||
unsigned lboard_capacity;
|
||||
|
||||
rc_runtime_richpresence_t* richpresence;
|
||||
|
||||
rc_memref_t* memrefs;
|
||||
rc_memref_t** next_memref;
|
||||
|
||||
rc_value_t* variables;
|
||||
rc_value_t** next_variable;
|
||||
}
|
||||
rc_runtime_t;
|
||||
|
||||
void rc_runtime_init(rc_runtime_t* runtime);
|
||||
void rc_runtime_destroy(rc_runtime_t* runtime);
|
||||
|
||||
int rc_runtime_activate_achievement(rc_runtime_t* runtime, unsigned id, const char* memaddr, lua_State* L, int funcs_idx);
|
||||
void rc_runtime_deactivate_achievement(rc_runtime_t* runtime, unsigned id);
|
||||
rc_trigger_t* rc_runtime_get_achievement(const rc_runtime_t* runtime, unsigned id);
|
||||
int rc_runtime_get_achievement_measured(const rc_runtime_t* runtime, unsigned id, unsigned* measured_value, unsigned* measured_target);
|
||||
|
||||
int rc_runtime_activate_lboard(rc_runtime_t* runtime, unsigned id, const char* memaddr, lua_State* L, int funcs_idx);
|
||||
void rc_runtime_deactivate_lboard(rc_runtime_t* runtime, unsigned id);
|
||||
rc_lboard_t* rc_runtime_get_lboard(const rc_runtime_t* runtime, unsigned id);
|
||||
int rc_runtime_format_lboard_value(char* buffer, int size, int value, int format);
|
||||
|
||||
|
||||
int rc_runtime_activate_richpresence(rc_runtime_t* runtime, const char* script, lua_State* L, int funcs_idx);
|
||||
int rc_runtime_get_richpresence(const rc_runtime_t* self, char* buffer, unsigned buffersize, rc_runtime_peek_t peek, void* peek_ud, lua_State* L);
|
||||
|
||||
enum {
|
||||
RC_RUNTIME_EVENT_ACHIEVEMENT_ACTIVATED, /* from WAITING, PAUSED, or PRIMED to ACTIVE */
|
||||
RC_RUNTIME_EVENT_ACHIEVEMENT_PAUSED,
|
||||
RC_RUNTIME_EVENT_ACHIEVEMENT_RESET,
|
||||
RC_RUNTIME_EVENT_ACHIEVEMENT_TRIGGERED,
|
||||
RC_RUNTIME_EVENT_ACHIEVEMENT_PRIMED,
|
||||
RC_RUNTIME_EVENT_LBOARD_STARTED,
|
||||
RC_RUNTIME_EVENT_LBOARD_CANCELED,
|
||||
RC_RUNTIME_EVENT_LBOARD_UPDATED,
|
||||
RC_RUNTIME_EVENT_LBOARD_TRIGGERED,
|
||||
RC_RUNTIME_EVENT_ACHIEVEMENT_DISABLED,
|
||||
RC_RUNTIME_EVENT_LBOARD_DISABLED
|
||||
};
|
||||
|
||||
typedef struct rc_runtime_event_t {
|
||||
unsigned id;
|
||||
int value;
|
||||
char type;
|
||||
}
|
||||
rc_runtime_event_t;
|
||||
|
||||
typedef void (*rc_runtime_event_handler_t)(const rc_runtime_event_t* runtime_event);
|
||||
|
||||
void rc_runtime_do_frame(rc_runtime_t* runtime, rc_runtime_event_handler_t event_handler, rc_runtime_peek_t peek, void* ud, lua_State* L);
|
||||
void rc_runtime_reset(rc_runtime_t* runtime);
|
||||
void rc_runtime_invalidate_address(rc_runtime_t* runtime, unsigned address);
|
||||
|
||||
int rc_runtime_progress_size(const rc_runtime_t* runtime, lua_State* L);
|
||||
int rc_runtime_serialize_progress(void* buffer, const rc_runtime_t* runtime, lua_State* L);
|
||||
int rc_runtime_deserialize_progress(rc_runtime_t* runtime, const unsigned char* serialized, lua_State* L);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* RC_RUNTIME_H */
|
||||
+400
@@ -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 */
|
||||
Vendored
-79
@@ -1,79 +0,0 @@
|
||||
#ifndef RCONSOLES_H
|
||||
#define RCONSOLES_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*****************************************************************************\
|
||||
| Console identifiers |
|
||||
\*****************************************************************************/
|
||||
|
||||
enum {
|
||||
RC_CONSOLE_MEGA_DRIVE = 1,
|
||||
RC_CONSOLE_NINTENDO_64 = 2,
|
||||
RC_CONSOLE_SUPER_NINTENDO = 3,
|
||||
RC_CONSOLE_GAMEBOY = 4,
|
||||
RC_CONSOLE_GAMEBOY_ADVANCE = 5,
|
||||
RC_CONSOLE_GAMEBOY_COLOR = 6,
|
||||
RC_CONSOLE_NINTENDO = 7,
|
||||
RC_CONSOLE_PC_ENGINE = 8,
|
||||
RC_CONSOLE_SEGA_CD = 9,
|
||||
RC_CONSOLE_SEGA_32X = 10,
|
||||
RC_CONSOLE_MASTER_SYSTEM = 11,
|
||||
RC_CONSOLE_PLAYSTATION = 12,
|
||||
RC_CONSOLE_ATARI_LYNX = 13,
|
||||
RC_CONSOLE_NEOGEO_POCKET = 14,
|
||||
RC_CONSOLE_GAME_GEAR = 15,
|
||||
RC_CONSOLE_GAMECUBE = 16,
|
||||
RC_CONSOLE_ATARI_JAGUAR = 17,
|
||||
RC_CONSOLE_NINTENDO_DS = 18,
|
||||
RC_CONSOLE_WII = 19,
|
||||
RC_CONSOLE_WII_U = 20,
|
||||
RC_CONSOLE_PLAYSTATION_2 = 21,
|
||||
RC_CONSOLE_XBOX = 22,
|
||||
/* 23 used to be EVENTS */
|
||||
RC_CONSOLE_POKEMON_MINI = 24,
|
||||
RC_CONSOLE_ATARI_2600 = 25,
|
||||
RC_CONSOLE_MS_DOS = 26,
|
||||
RC_CONSOLE_ARCADE = 27,
|
||||
RC_CONSOLE_VIRTUAL_BOY = 28,
|
||||
RC_CONSOLE_MSX = 29,
|
||||
RC_CONSOLE_COMMODORE_64 = 30,
|
||||
RC_CONSOLE_ZX81 = 31,
|
||||
RC_CONSOLE_ORIC = 32,
|
||||
RC_CONSOLE_SG1000 = 33,
|
||||
RC_CONSOLE_VIC20 = 34,
|
||||
RC_CONSOLE_AMIGA = 35,
|
||||
RC_CONSOLE_AMIGA_ST = 36,
|
||||
RC_CONSOLE_AMSTRAD_PC = 37,
|
||||
RC_CONSOLE_APPLE_II = 38,
|
||||
RC_CONSOLE_SATURN = 39,
|
||||
RC_CONSOLE_DREAMCAST = 40,
|
||||
RC_CONSOLE_PSP = 41,
|
||||
RC_CONSOLE_CDI = 42,
|
||||
RC_CONSOLE_3DO = 43,
|
||||
RC_CONSOLE_COLECOVISION = 44,
|
||||
RC_CONSOLE_INTELLIVISION = 45,
|
||||
RC_CONSOLE_VECTREX = 46,
|
||||
RC_CONSOLE_PC8800 = 47,
|
||||
RC_CONSOLE_PC9800 = 48,
|
||||
RC_CONSOLE_PCFX = 49,
|
||||
RC_CONSOLE_ATARI_5200 = 50,
|
||||
RC_CONSOLE_ATARI_7800 = 51,
|
||||
RC_CONSOLE_X68K = 52,
|
||||
RC_CONSOLE_WONDERSWAN = 53,
|
||||
RC_CONSOLE_CASSETTEVISION = 54,
|
||||
RC_CONSOLE_SUPER_CASSETTEVISION = 55,
|
||||
|
||||
RC_CONSOLE_HUBS = 100,
|
||||
RC_CONSOLE_EVENTS = 101
|
||||
};
|
||||
|
||||
const char* rc_console_name(int console_id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* RCONSOLES_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>
|
||||
|
||||
+95
-61
@@ -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 {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
+135
-18
@@ -1,5 +1,4 @@
|
||||
#include "rcheevos.h"
|
||||
#include "rconsoles.h"
|
||||
#include "rc_consoles.h"
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
@@ -13,9 +12,6 @@ const char* rc_console_name(int console_id)
|
||||
case RC_CONSOLE_AMIGA:
|
||||
return "Amiga";
|
||||
|
||||
case RC_CONSOLE_AMIGA_ST:
|
||||
return "Amiga ST";
|
||||
|
||||
case RC_CONSOLE_AMSTRAD_PC:
|
||||
return "Amstrad CPC";
|
||||
|
||||
@@ -40,6 +36,9 @@ const char* rc_console_name(int console_id)
|
||||
case RC_CONSOLE_ATARI_LYNX:
|
||||
return "Atari Lynx";
|
||||
|
||||
case RC_CONSOLE_ATARI_ST:
|
||||
return "Atari ST";
|
||||
|
||||
case RC_CONSOLE_CASSETTEVISION:
|
||||
return "CassetteVision";
|
||||
|
||||
@@ -58,6 +57,15 @@ const char* rc_console_name(int console_id)
|
||||
case RC_CONSOLE_EVENTS:
|
||||
return "Events";
|
||||
|
||||
case RC_CONSOLE_FAIRCHILD_CHANNEL_F:
|
||||
return "Fairchild Channel F";
|
||||
|
||||
case RC_CONSOLE_FM_TOWNS:
|
||||
return "FM Towns";
|
||||
|
||||
case RC_CONSOLE_GAME_AND_WATCH:
|
||||
return "Game & Watch";
|
||||
|
||||
case RC_CONSOLE_GAMEBOY:
|
||||
return "GameBoy";
|
||||
|
||||
@@ -79,6 +87,9 @@ const char* rc_console_name(int console_id)
|
||||
case RC_CONSOLE_INTELLIVISION:
|
||||
return "Intellivision";
|
||||
|
||||
case RC_CONSOLE_MAGNAVOX_ODYSSEY2:
|
||||
return "Magnavox Odyssey 2";
|
||||
|
||||
case RC_CONSOLE_MASTER_SYSTEM:
|
||||
return "Master System";
|
||||
|
||||
@@ -91,6 +102,12 @@ const char* rc_console_name(int console_id)
|
||||
case RC_CONSOLE_MSX:
|
||||
return "MSX";
|
||||
|
||||
case RC_CONSOLE_NEO_GEO_CD:
|
||||
return "Neo Geo CD";
|
||||
|
||||
case RC_CONSOLE_NEOGEO_POCKET:
|
||||
return "Neo Geo Pocket";
|
||||
|
||||
case RC_CONSOLE_NINTENDO:
|
||||
return "Nintendo Entertainment System";
|
||||
|
||||
@@ -100,8 +117,11 @@ const char* rc_console_name(int console_id)
|
||||
case RC_CONSOLE_NINTENDO_DS:
|
||||
return "Nintendo DS";
|
||||
|
||||
case RC_CONSOLE_NEOGEO_POCKET:
|
||||
return "Neo Geo Pocket";
|
||||
case RC_CONSOLE_NINTENDO_3DS:
|
||||
return "Nintendo 3DS";
|
||||
|
||||
case RC_CONSOLE_NOKIA_NGAGE:
|
||||
return "Nokia N-Gage";
|
||||
|
||||
case RC_CONSOLE_ORIC:
|
||||
return "Oric";
|
||||
@@ -142,14 +162,23 @@ const char* rc_console_name(int console_id)
|
||||
case RC_CONSOLE_SG1000:
|
||||
return "SG-1000";
|
||||
|
||||
case RC_CONSOLE_SHARPX1:
|
||||
return "Sharp X1";
|
||||
|
||||
case RC_CONSOLE_SUPER_NINTENDO:
|
||||
return "Super Nintendo Entertainment System";
|
||||
|
||||
case RC_CONSOLE_SUPER_CASSETTEVISION:
|
||||
return "Super CassetteVision";
|
||||
|
||||
case RC_CONSOLE_WONDERSWAN:
|
||||
return "WonderSwan";
|
||||
case RC_CONSOLE_SUPERVISION:
|
||||
return "Watara Supervision";
|
||||
|
||||
case RC_CONSOLE_THOMSONTO8:
|
||||
return "Thomson TO8";
|
||||
|
||||
case RC_CONSOLE_TIC80:
|
||||
return "TIC-80";
|
||||
|
||||
case RC_CONSOLE_VECTREX:
|
||||
return "Vectrex";
|
||||
@@ -166,6 +195,9 @@ const char* rc_console_name(int console_id)
|
||||
case RC_CONSOLE_WII_U:
|
||||
return "Wii-U";
|
||||
|
||||
case RC_CONSOLE_WONDERSWAN:
|
||||
return "WonderSwan";
|
||||
|
||||
case RC_CONSOLE_X68K:
|
||||
return "X68K";
|
||||
|
||||
@@ -175,6 +207,9 @@ const char* rc_console_name(int console_id)
|
||||
case RC_CONSOLE_ZX81:
|
||||
return "ZX-81";
|
||||
|
||||
case RC_CONSOLE_ZX_SPECTRUM:
|
||||
return "ZX Spectrum";
|
||||
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
@@ -302,6 +337,12 @@ 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 ===== */
|
||||
static const rc_memory_region_t _rc_memory_regions_magnavox_odyssey_2[] = {
|
||||
{ 0x000000U, 0x00003FU, 0x000040U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_magnavox_odyssey_2 = { _rc_memory_regions_magnavox_odyssey_2, 1 };
|
||||
|
||||
/* ===== Master System ===== */
|
||||
/* http://www.smspower.org/Development/MemoryMap */
|
||||
static const rc_memory_region_t _rc_memory_regions_master_system[] = {
|
||||
@@ -349,9 +390,23 @@ static const rc_memory_region_t _rc_memory_regions_nes[] = {
|
||||
{ 0x2008U, 0x3FFFU, 0x2008U, RC_MEMORY_TYPE_VIRTUAL_RAM, "Mirrored PPU Register" }, /* repeats every 8 bytes */
|
||||
{ 0x4000U, 0x4017U, 0x4000U, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "APU and I/O register" },
|
||||
{ 0x4018U, 0x401FU, 0x4018U, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "APU and I/O test register" },
|
||||
|
||||
/* NOTE: these are for the original NES/Famicom */
|
||||
{ 0x4020U, 0x5FFFU, 0x4020U, RC_MEMORY_TYPE_READONLY, "Cartridge data"}, /* varies by mapper */
|
||||
{ 0x6000U, 0x7FFFU, 0x6000U, RC_MEMORY_TYPE_SAVE_RAM, "Cartridge RAM"},
|
||||
{ 0x8000U, 0xFFFFU, 0x8000U, RC_MEMORY_TYPE_READONLY, "Cartridge ROM"},
|
||||
|
||||
/* NOTE: these are the correct mappings for FDS: https://fms.komkon.org/EMUL8/NES.html
|
||||
* 0x6000-0xDFFF is RAM on the FDS system and 0xE000-0xFFFF is FDS BIOS.
|
||||
* If the core implements a memory map, we should still be able to translate the addresses
|
||||
* correctly as we only use the classifications when a memory map is not provided
|
||||
|
||||
{ 0x4020U, 0x40FFU, 0x4020U, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "FDS I/O registers"},
|
||||
{ 0x4100U, 0x5FFFU, 0x4100U, RC_MEMORY_TYPE_READONLY, "Cartridge data"}, // varies by mapper
|
||||
{ 0x6000U, 0xDFFFU, 0x6000U, RC_MEMORY_TYPE_SYSTEM_RAM, "FDS RAM"},
|
||||
{ 0xE000U, 0xFFFFU, 0xE000U, RC_MEMORY_TYPE_READONLY, "FDS BIOS ROM"},
|
||||
|
||||
*/
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_nes = { _rc_memory_regions_nes, 11 };
|
||||
|
||||
@@ -359,7 +414,7 @@ static const rc_memory_regions_t rc_memory_regions_nes = { _rc_memory_regions_ne
|
||||
/* https://raw.githubusercontent.com/mikeryan/n64dev/master/docs/n64ops/n64ops%23h.txt */
|
||||
static const rc_memory_region_t _rc_memory_regions_n64[] = {
|
||||
{ 0x000000U, 0x1FFFFFU, 0x00000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }, /* RDRAM 1 */
|
||||
{ 0x200000U, 0x3FFFFFU, 0x00020000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }, /* RDRAM 2 */
|
||||
{ 0x200000U, 0x3FFFFFU, 0x00200000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }, /* RDRAM 2 */
|
||||
{ 0x400000U, 0x7FFFFFU, 0x80000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" } /* expansion pak - cannot find any details for real address */
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_n64 = { _rc_memory_regions_n64, 3 };
|
||||
@@ -386,6 +441,7 @@ static const rc_memory_region_t _rc_memory_regions_pc8800[] = {
|
||||
static const rc_memory_regions_t rc_memory_regions_pc8800 = { _rc_memory_regions_pc8800, 2 };
|
||||
|
||||
/* ===== PC Engine ===== */
|
||||
/* http://www.archaicpixels.com/Memory_Map */
|
||||
static const rc_memory_region_t _rc_memory_regions_pcengine[] = {
|
||||
{ 0x000000U, 0x001FFFU, 0x1F0000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" },
|
||||
{ 0x002000U, 0x011FFFU, 0x100000U, RC_MEMORY_TYPE_SYSTEM_RAM, "CD RAM" },
|
||||
@@ -411,6 +467,14 @@ static const rc_memory_region_t _rc_memory_regions_playstation[] = {
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_playstation = { _rc_memory_regions_playstation, 2 };
|
||||
|
||||
/* ===== PlayStation 2 ===== */
|
||||
/* https://psi-rockin.github.io/ps2tek/ */
|
||||
static const rc_memory_region_t _rc_memory_regions_playstation2[] = {
|
||||
{ 0x00000000U, 0x000FFFFFU, 0x00000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Kernel RAM" },
|
||||
{ 0x00100000U, 0x01FFFFFFU, 0x00100000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_playstation2 = { _rc_memory_regions_playstation2, 2 };
|
||||
|
||||
/* ===== Pokemon Mini ===== */
|
||||
/* https://www.pokemon-mini.net/documentation/memory-map/ */
|
||||
static const rc_memory_region_t _rc_memory_regions_pokemini[] = {
|
||||
@@ -420,6 +484,7 @@ static const rc_memory_region_t _rc_memory_regions_pokemini[] = {
|
||||
static const rc_memory_regions_t rc_memory_regions_pokemini = { _rc_memory_regions_pokemini, 2 };
|
||||
|
||||
/* ===== Sega CD ===== */
|
||||
/* https://en.wikibooks.org/wiki/Genesis_Programming#MegaCD_Changes */
|
||||
static const rc_memory_region_t _rc_memory_regions_segacd[] = {
|
||||
{ 0x000000U, 0x00FFFFU, 0x00FF0000U, RC_MEMORY_TYPE_SYSTEM_RAM, "68000 RAM" },
|
||||
{ 0x010000U, 0x08FFFFU, 0x80020000U, RC_MEMORY_TYPE_SAVE_RAM, "CD PRG RAM" } /* normally banked into $020000-$03FFFF */
|
||||
@@ -437,12 +502,17 @@ static const rc_memory_regions_t rc_memory_regions_saturn = { _rc_memory_regions
|
||||
/* ===== SG-1000 ===== */
|
||||
/* http://www.smspower.org/Development/MemoryMap */
|
||||
static const rc_memory_region_t _rc_memory_regions_sg1000[] = {
|
||||
{ 0x000000U, 0x0003FFU, 0xC000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
|
||||
/* TODO: should cartridge memory be exposed ($0000-$BFFF)? it's usually just ROM data, but may contain on-cartridge RAM
|
||||
* This not is also concerning: http://www.smspower.org/Development/MemoryMap
|
||||
* Cartridges may disable the system RAM and thus take over the full 64KB address space. */
|
||||
{ 0x000000U, 0x0003FFU, 0xC000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" },
|
||||
/* https://github.com/libretro/FBNeo/blob/697801c6262be6ca91615cf905444d3e039bc06f/src/burn/drv/sg1000/d_sg1000.cpp#L210-L237 */
|
||||
/* Expansion mode B exposes 8KB at $C000. The first 2KB hides the System RAM, but since the address matches,
|
||||
we'll leverage that definition and expand it another 6KB */
|
||||
{ 0x000400U, 0x001FFFU, 0xC400U, RC_MEMORY_TYPE_SYSTEM_RAM, "Extended RAM" },
|
||||
/* Expansion mode A exposes 8KB at $2000 */
|
||||
{ 0x002000U, 0x003FFFU, 0x2000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Extended RAM" },
|
||||
/* Othello exposes 2KB at $8000, and The Castle exposes 8KB at $8000 */
|
||||
{ 0x004000U, 0x005FFFU, 0x8000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Extended RAM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_sg1000 = { _rc_memory_regions_sg1000, 1 };
|
||||
static const rc_memory_regions_t rc_memory_regions_sg1000 = { _rc_memory_regions_sg1000, 4 };
|
||||
|
||||
/* ===== Super Cassette Vision ===== */
|
||||
static const rc_memory_region_t _rc_memory_regions_scv[] = {
|
||||
@@ -457,13 +527,36 @@ static const rc_memory_region_t _rc_memory_regions_scv[] = {
|
||||
static const rc_memory_regions_t rc_memory_regions_scv = { _rc_memory_regions_scv, 7 };
|
||||
|
||||
/* ===== Super Nintendo ===== */
|
||||
/* https://segaretro.org/Sega_Saturn_hardware_notes_(2004-04-27) */
|
||||
/* https://en.wikibooks.org/wiki/Super_NES_Programming/SNES_memory_map#LoROM */
|
||||
static const rc_memory_region_t _rc_memory_regions_snes[] = {
|
||||
{ 0x000000U, 0x01FFFFU, 0x7E0000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" },
|
||||
{ 0x020000U, 0x03FFFFU, 0xFE0000U, RC_MEMORY_TYPE_SAVE_RAM, "Cartridge RAM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_snes = { _rc_memory_regions_snes, 2 };
|
||||
|
||||
/* ===== Thomson TO8 ===== */
|
||||
/* https://github.com/mamedev/mame/blob/master/src/mame/drivers/thomson.cpp#L1617 */
|
||||
static const rc_memory_region_t _rc_memory_regions_thomson_to8[] = {
|
||||
{ 0x000000U, 0x07FFFFU, 0x000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_thomson_to8 = { _rc_memory_regions_thomson_to8, 1 };
|
||||
|
||||
/* ===== TIC-80 ===== */
|
||||
/* https://github.com/nesbox/TIC-80/wiki/RAM */
|
||||
static const rc_memory_region_t _rc_memory_regions_tic80[] = {
|
||||
{ 0x000000U, 0x003FFFU, 0x000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Video RAM" }, /* have to classify this as system RAM because the core exposes it as part of the RETRO_MEMORY_SYSTEM_RAM */
|
||||
{ 0x004000U, 0x005FFFU, 0x004000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Tile RAM" },
|
||||
{ 0x006000U, 0x007FFFU, 0x006000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Sprite RAM" },
|
||||
{ 0x008000U, 0x00FF7FU, 0x008000U, RC_MEMORY_TYPE_SYSTEM_RAM, "MAP RAM" },
|
||||
{ 0x00FF80U, 0x00FF8BU, 0x00FF80U, RC_MEMORY_TYPE_SYSTEM_RAM, "Input State" },
|
||||
{ 0x00FF8CU, 0x014003U, 0x00FF8CU, RC_MEMORY_TYPE_SYSTEM_RAM, "Sound RAM" },
|
||||
{ 0x014004U, 0x014403U, 0x014004U, RC_MEMORY_TYPE_SAVE_RAM, "Persistent Memory" }, /* this is also returned as part of RETRO_MEMORY_SYSTEM_RAM, but can be extrapolated correctly because the pointer starts at the first SYSTEM_RAM region */
|
||||
{ 0x014404U, 0x014603U, 0x014404U, RC_MEMORY_TYPE_SYSTEM_RAM, "Sprite Flags" },
|
||||
{ 0x014604U, 0x014E03U, 0x014604U, RC_MEMORY_TYPE_SYSTEM_RAM, "System Font" },
|
||||
{ 0x014E04U, 0x017FFFU, 0x014E04U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM"}
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_tic80 = { _rc_memory_regions_tic80, 10 };
|
||||
|
||||
/* ===== Vectrex ===== */
|
||||
/* https://roadsidethoughts.com/vectrex/vectrex-memory-map.htm */
|
||||
static const rc_memory_region_t _rc_memory_regions_vectrex[] = {
|
||||
@@ -478,6 +571,15 @@ static const rc_memory_region_t _rc_memory_regions_virtualboy[] = {
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_virtualboy = { _rc_memory_regions_virtualboy, 2 };
|
||||
|
||||
/* ===== Watara Supervision ===== */
|
||||
/* https://github.com/libretro/potator/blob/b5e5ba02914fcdf4a8128072dbc709da28e08832/common/memorymap.c#L231-L259 */
|
||||
static const rc_memory_region_t _rc_memory_regions_watara_supervision[] = {
|
||||
{ 0x0000U, 0x001FFFU, 0x0000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" },
|
||||
{ 0x2000U, 0x003FFFU, 0x2000U, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "Registers" },
|
||||
{ 0x4000U, 0x005FFFU, 0x4000U, RC_MEMORY_TYPE_VIDEO_RAM, "Video RAM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_watara_supervision = { _rc_memory_regions_watara_supervision, 3 };
|
||||
|
||||
/* ===== WonderSwan ===== */
|
||||
/* http://daifukkat.su/docs/wsman/#ovr_memmap */
|
||||
static const rc_memory_region_t _rc_memory_regions_wonderswan[] = {
|
||||
@@ -538,6 +640,9 @@ const rc_memory_regions_t* rc_console_memory_regions(int console_id)
|
||||
case RC_CONSOLE_INTELLIVISION:
|
||||
return &rc_memory_regions_intellivision;
|
||||
|
||||
case RC_CONSOLE_MAGNAVOX_ODYSSEY2:
|
||||
return &rc_memory_regions_magnavox_odyssey_2;
|
||||
|
||||
case RC_CONSOLE_MASTER_SYSTEM:
|
||||
return &rc_memory_regions_master_system;
|
||||
|
||||
@@ -572,11 +677,14 @@ const rc_memory_regions_t* rc_console_memory_regions(int console_id)
|
||||
return &rc_memory_regions_pcengine;
|
||||
|
||||
case RC_CONSOLE_PCFX:
|
||||
return &rc_memory_regions_pcfx;
|
||||
return &rc_memory_regions_pcfx;
|
||||
|
||||
case RC_CONSOLE_PLAYSTATION:
|
||||
return &rc_memory_regions_playstation;
|
||||
|
||||
case RC_CONSOLE_PLAYSTATION_2:
|
||||
return &rc_memory_regions_playstation2;
|
||||
|
||||
case RC_CONSOLE_POKEMON_MINI:
|
||||
return &rc_memory_regions_pokemini;
|
||||
|
||||
@@ -595,6 +703,15 @@ const rc_memory_regions_t* rc_console_memory_regions(int console_id)
|
||||
case RC_CONSOLE_SUPER_NINTENDO:
|
||||
return &rc_memory_regions_snes;
|
||||
|
||||
case RC_CONSOLE_SUPERVISION:
|
||||
return &rc_memory_regions_watara_supervision;
|
||||
|
||||
case RC_CONSOLE_THOMSONTO8:
|
||||
return &rc_memory_regions_thomson_to8;
|
||||
|
||||
case RC_CONSOLE_TIC80:
|
||||
return &rc_memory_regions_tic80;
|
||||
|
||||
case RC_CONSOLE_VECTREX:
|
||||
return &rc_memory_regions_vectrex;
|
||||
|
||||
@@ -602,7 +719,7 @@ const rc_memory_regions_t* rc_console_memory_regions(int console_id)
|
||||
return &rc_memory_regions_virtualboy;
|
||||
|
||||
case RC_CONSOLE_WONDERSWAN:
|
||||
return &rc_memory_regions_wonderswan;
|
||||
return &rc_memory_regions_wonderswan;
|
||||
|
||||
default:
|
||||
return &rc_memory_regions_none;
|
||||
|
||||
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>
|
||||
|
||||
Vendored
+20
-7
@@ -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);
|
||||
@@ -151,15 +153,19 @@ int rc_lboard_size(const char* memaddr) {
|
||||
rc_lboard_t* rc_parse_lboard(void* buffer, const char* memaddr, lua_State* L, int funcs_ndx) {
|
||||
rc_lboard_t* self;
|
||||
rc_parse_state_t parse;
|
||||
|
||||
if (!buffer || !memaddr)
|
||||
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);
|
||||
|
||||
rc_parse_lboard_internal(self, memaddr, &parse);
|
||||
|
||||
rc_destroy_parse_state(&parse);
|
||||
return parse.offset >= 0 ? self : 0;
|
||||
return (parse.offset >= 0) ? self : 0;
|
||||
}
|
||||
|
||||
int rc_evaluate_lboard(rc_lboard_t* self, int* value, rc_peek_t peek, void* peek_ud, lua_State* L) {
|
||||
@@ -167,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 */
|
||||
@@ -199,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;
|
||||
@@ -251,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
+132
-151
@@ -1,195 +1,163 @@
|
||||
#include "internal.h"
|
||||
#include "rc_internal.h"
|
||||
|
||||
#include <stdlib.h> /* malloc/realloc */
|
||||
#include <string.h> /* memcpy */
|
||||
|
||||
#define MEMREF_PLACEHOLDER_ADDRESS 0xFFFFFFFF
|
||||
|
||||
static rc_memref_value_t* rc_alloc_memref_value_sizing_mode(rc_parse_state_t* parse, unsigned address, char size, char is_indirect) {
|
||||
rc_memref_t* rc_alloc_memref(rc_parse_state_t* parse, unsigned address, char size, char is_indirect) {
|
||||
rc_memref_t** next_memref;
|
||||
rc_memref_t* memref;
|
||||
int i;
|
||||
|
||||
/* indirect address always creates two new entries; don't bother tracking them */
|
||||
if (is_indirect) {
|
||||
RC_ALLOC(rc_memref_value_t, parse);
|
||||
return RC_ALLOC(rc_memref_value_t, parse);
|
||||
}
|
||||
|
||||
memref = NULL;
|
||||
|
||||
/* have to track unique address/size/bcd combinations - use scratch.memref for sizing mode */
|
||||
for (i = 0; i < parse->scratch.memref_count; ++i) {
|
||||
memref = &parse->scratch.memref[i];
|
||||
if (memref->address == address && memref->size == size) {
|
||||
return &parse->scratch.obj.memref_value;
|
||||
}
|
||||
}
|
||||
|
||||
/* no match found - resize unique tracking buffer if necessary */
|
||||
if (parse->scratch.memref_count == parse->scratch.memref_size) {
|
||||
if (parse->scratch.memref == parse->scratch.memref_buffer) {
|
||||
parse->scratch.memref_size += 16;
|
||||
memref = (rc_memref_t*)malloc(parse->scratch.memref_size * sizeof(parse->scratch.memref_buffer[0]));
|
||||
if (memref) {
|
||||
parse->scratch.memref = memref;
|
||||
memcpy(memref, parse->scratch.memref_buffer, parse->scratch.memref_count * sizeof(parse->scratch.memref_buffer[0]));
|
||||
}
|
||||
else {
|
||||
parse->offset = RC_OUT_OF_MEMORY;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
parse->scratch.memref_size += 32;
|
||||
memref = (rc_memref_t*)realloc(parse->scratch.memref, parse->scratch.memref_size * sizeof(parse->scratch.memref_buffer[0]));
|
||||
if (memref) {
|
||||
parse->scratch.memref = memref;
|
||||
}
|
||||
else {
|
||||
parse->offset = RC_OUT_OF_MEMORY;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* add new unique tracking entry */
|
||||
if (parse->scratch.memref) {
|
||||
memref = &parse->scratch.memref[parse->scratch.memref_count++];
|
||||
memref->address = address;
|
||||
memref->size = size;
|
||||
memref->is_indirect = is_indirect;
|
||||
}
|
||||
|
||||
/* allocate memory but don't actually populate, as it might overwrite the self object referencing the rc_memref_value_t */
|
||||
return RC_ALLOC(rc_memref_value_t, parse);
|
||||
}
|
||||
|
||||
static rc_memref_value_t* rc_alloc_memref_value_constuct_mode(rc_parse_state_t* parse, unsigned address, char size, char is_indirect) {
|
||||
rc_memref_value_t** next_memref_value;
|
||||
rc_memref_value_t* memref_value;
|
||||
rc_memref_value_t* indirect_memref_value;
|
||||
|
||||
if (!is_indirect) {
|
||||
/* attempt to find an existing rc_memref_value_t */
|
||||
next_memref_value = parse->first_memref;
|
||||
while (*next_memref_value) {
|
||||
memref_value = *next_memref_value;
|
||||
if (!memref_value->memref.is_indirect && memref_value->memref.address == address &&
|
||||
memref_value->memref.size == size) {
|
||||
return memref_value;
|
||||
}
|
||||
/* attempt to find an existing memref that can be shared */
|
||||
next_memref = parse->first_memref;
|
||||
while (*next_memref) {
|
||||
memref = *next_memref;
|
||||
if (!memref->value.is_indirect && memref->address == address && memref->value.size == size)
|
||||
return memref;
|
||||
|
||||
next_memref_value = &memref_value->next;
|
||||
next_memref = &memref->next;
|
||||
}
|
||||
|
||||
/* no match found, create a new entry */
|
||||
memref = RC_ALLOC_SCRATCH(rc_memref_t, parse);
|
||||
*next_memref = memref;
|
||||
}
|
||||
else {
|
||||
/* indirect address always creates two new entries - one for the original address, and one for
|
||||
the indirect dereference - just skip ahead to the end of the list */
|
||||
next_memref_value = parse->first_memref;
|
||||
while (*next_memref_value) {
|
||||
next_memref_value = &(*next_memref_value)->next;
|
||||
}
|
||||
/* indirect references always create a new entry because we can't guarantee that the
|
||||
* indirection amount will be the same between references. because they aren't shared,
|
||||
* don't bother putting them in the chain.
|
||||
*/
|
||||
memref = RC_ALLOC(rc_memref_t, parse);
|
||||
}
|
||||
|
||||
/* no match found, create a new entry */
|
||||
memref_value = RC_ALLOC(rc_memref_value_t, parse);
|
||||
memref_value->memref.address = address;
|
||||
memref_value->memref.size = size;
|
||||
memref_value->memref.is_indirect = is_indirect;
|
||||
memref_value->value = 0;
|
||||
memref_value->previous = 0;
|
||||
memref_value->prior = 0;
|
||||
memref_value->next = 0;
|
||||
memset(memref, 0, sizeof(*memref));
|
||||
memref->address = address;
|
||||
memref->value.size = size;
|
||||
memref->value.is_indirect = is_indirect;
|
||||
|
||||
*next_memref_value = memref_value;
|
||||
return memref;
|
||||
}
|
||||
|
||||
/* also create the indirect deference entry for indirect references */
|
||||
if (is_indirect) {
|
||||
indirect_memref_value = RC_ALLOC(rc_memref_value_t, parse);
|
||||
indirect_memref_value->memref.address = MEMREF_PLACEHOLDER_ADDRESS;
|
||||
indirect_memref_value->memref.size = size;
|
||||
indirect_memref_value->memref.is_indirect = 1;
|
||||
indirect_memref_value->value = 0;
|
||||
indirect_memref_value->previous = 0;
|
||||
indirect_memref_value->prior = 0;
|
||||
indirect_memref_value->next = 0;
|
||||
int rc_parse_memref(const char** memaddr, char* size, unsigned* address) {
|
||||
const char* aux = *memaddr;
|
||||
char* end;
|
||||
unsigned long value;
|
||||
|
||||
memref_value->next = indirect_memref_value;
|
||||
if (*aux++ != '0')
|
||||
return RC_INVALID_MEMORY_OPERAND;
|
||||
|
||||
if (*aux != 'x' && *aux != 'X')
|
||||
return RC_INVALID_MEMORY_OPERAND;
|
||||
aux++;
|
||||
|
||||
switch (*aux++) {
|
||||
case 'm': case 'M': *size = RC_MEMSIZE_BIT_0; break;
|
||||
case 'n': case 'N': *size = RC_MEMSIZE_BIT_1; break;
|
||||
case 'o': case 'O': *size = RC_MEMSIZE_BIT_2; break;
|
||||
case 'p': case 'P': *size = RC_MEMSIZE_BIT_3; break;
|
||||
case 'q': case 'Q': *size = RC_MEMSIZE_BIT_4; break;
|
||||
case 'r': case 'R': *size = RC_MEMSIZE_BIT_5; break;
|
||||
case 's': case 'S': *size = RC_MEMSIZE_BIT_6; break;
|
||||
case 't': case 'T': *size = RC_MEMSIZE_BIT_7; break;
|
||||
case 'l': case 'L': *size = RC_MEMSIZE_LOW; break;
|
||||
case 'u': case 'U': *size = RC_MEMSIZE_HIGH; break;
|
||||
case 'k': case 'K': *size = RC_MEMSIZE_BITCOUNT; break;
|
||||
case 'h': case 'H': *size = RC_MEMSIZE_8_BITS; break;
|
||||
case 'w': case 'W': *size = RC_MEMSIZE_24_BITS; break;
|
||||
case 'x': case 'X': *size = RC_MEMSIZE_32_BITS; break;
|
||||
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
|
||||
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
|
||||
aux--;
|
||||
/* fallthrough */
|
||||
case ' ':
|
||||
*size = RC_MEMSIZE_16_BITS;
|
||||
break;
|
||||
|
||||
default:
|
||||
return RC_INVALID_MEMORY_OPERAND;
|
||||
}
|
||||
|
||||
return memref_value;
|
||||
value = strtoul(aux, &end, 16);
|
||||
|
||||
if (end == aux)
|
||||
return RC_INVALID_MEMORY_OPERAND;
|
||||
|
||||
if (value > 0xffffffffU)
|
||||
value = 0xffffffffU;
|
||||
|
||||
*address = (unsigned)value;
|
||||
*memaddr = end;
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
rc_memref_value_t* rc_alloc_memref_value(rc_parse_state_t* parse, unsigned address, char size, char is_indirect) {
|
||||
if (!parse->first_memref)
|
||||
return rc_alloc_memref_value_sizing_mode(parse, address, size, is_indirect);
|
||||
|
||||
return rc_alloc_memref_value_constuct_mode(parse, address, size, is_indirect);
|
||||
}
|
||||
|
||||
static unsigned rc_memref_get_value(rc_memref_t* self, rc_peek_t peek, void* ud) {
|
||||
static unsigned rc_peek_value(unsigned address, char size, rc_peek_t peek, void* ud) {
|
||||
unsigned value;
|
||||
|
||||
if (!peek)
|
||||
return 0;
|
||||
|
||||
switch (self->size)
|
||||
switch (size)
|
||||
{
|
||||
case RC_MEMSIZE_BIT_0:
|
||||
value = (peek(self->address, 1, ud) >> 0) & 1;
|
||||
value = (peek(address, 1, ud) >> 0) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_1:
|
||||
value = (peek(self->address, 1, ud) >> 1) & 1;
|
||||
value = (peek(address, 1, ud) >> 1) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_2:
|
||||
value = (peek(self->address, 1, ud) >> 2) & 1;
|
||||
value = (peek(address, 1, ud) >> 2) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_3:
|
||||
value = (peek(self->address, 1, ud) >> 3) & 1;
|
||||
value = (peek(address, 1, ud) >> 3) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_4:
|
||||
value = (peek(self->address, 1, ud) >> 4) & 1;
|
||||
value = (peek(address, 1, ud) >> 4) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_5:
|
||||
value = (peek(self->address, 1, ud) >> 5) & 1;
|
||||
value = (peek(address, 1, ud) >> 5) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_6:
|
||||
value = (peek(self->address, 1, ud) >> 6) & 1;
|
||||
value = (peek(address, 1, ud) >> 6) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_7:
|
||||
value = (peek(self->address, 1, ud) >> 7) & 1;
|
||||
value = (peek(address, 1, ud) >> 7) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_LOW:
|
||||
value = peek(self->address, 1, ud) & 0x0f;
|
||||
value = peek(address, 1, ud) & 0x0f;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_HIGH:
|
||||
value = (peek(self->address, 1, ud) >> 4) & 0x0f;
|
||||
value = (peek(address, 1, ud) >> 4) & 0x0f;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_8_BITS:
|
||||
value = peek(self->address, 1, ud);
|
||||
value = peek(address, 1, ud);
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_16_BITS:
|
||||
value = peek(self->address, 2, ud);
|
||||
value = peek(address, 2, ud);
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_24_BITS:
|
||||
/* peek 4 bytes - don't expect the caller to understand 24-bit numbers */
|
||||
value = peek(self->address, 4, ud) & 0x00FFFFFF;
|
||||
value = peek(address, 4, ud) & 0x00FFFFFF;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_32_BITS:
|
||||
value = peek(self->address, 4, ud);
|
||||
value = peek(address, 4, ud);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -200,45 +168,58 @@ static unsigned rc_memref_get_value(rc_memref_t* self, rc_peek_t peek, void* ud)
|
||||
return value;
|
||||
}
|
||||
|
||||
void rc_update_memref_value(rc_memref_value_t* memref, rc_peek_t peek, void* ud) {
|
||||
memref->previous = memref->value;
|
||||
memref->value = rc_memref_get_value(&memref->memref, peek, ud);
|
||||
if (memref->value != memref->previous)
|
||||
memref->prior = memref->previous;
|
||||
void rc_update_memref_value(rc_memref_value_t* memref, unsigned new_value) {
|
||||
if (memref->value == new_value) {
|
||||
memref->changed = 0;
|
||||
}
|
||||
else {
|
||||
memref->prior = memref->value;
|
||||
memref->value = new_value;
|
||||
memref->changed = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void rc_update_memref_values(rc_memref_value_t* memref, rc_peek_t peek, void* ud) {
|
||||
void rc_update_memref_values(rc_memref_t* memref, rc_peek_t peek, void* ud) {
|
||||
while (memref) {
|
||||
if (memref->memref.address != MEMREF_PLACEHOLDER_ADDRESS)
|
||||
rc_update_memref_value(memref, peek, ud);
|
||||
/* indirect memory references are not shared and will be updated in rc_get_memref_value */
|
||||
if (!memref->value.is_indirect)
|
||||
rc_update_memref_value(&memref->value, rc_peek_value(memref->address, memref->value.size, peek, ud));
|
||||
|
||||
memref = memref->next;
|
||||
}
|
||||
}
|
||||
|
||||
void rc_init_parse_state_memrefs(rc_parse_state_t* parse, rc_memref_value_t** memrefs) {
|
||||
void rc_init_parse_state_memrefs(rc_parse_state_t* parse, rc_memref_t** memrefs) {
|
||||
parse->first_memref = memrefs;
|
||||
*memrefs = 0;
|
||||
}
|
||||
|
||||
rc_memref_value_t* rc_get_indirect_memref(rc_memref_value_t* memref, rc_eval_state_t* eval_state) {
|
||||
unsigned new_address;
|
||||
|
||||
if (eval_state->add_address == 0)
|
||||
return memref;
|
||||
|
||||
if (!memref->memref.is_indirect)
|
||||
return memref;
|
||||
|
||||
new_address = memref->memref.address + eval_state->add_address;
|
||||
|
||||
/* an extra rc_memref_value_t is allocated for offset calculations */
|
||||
memref = memref->next;
|
||||
|
||||
/* if the adjusted address has changed, update the record */
|
||||
if (memref->memref.address != new_address) {
|
||||
memref->memref.address = new_address;
|
||||
rc_update_memref_value(memref, eval_state->peek, eval_state->peek_userdata);
|
||||
unsigned rc_get_memref_value(rc_memref_t* memref, int operand_type, rc_eval_state_t* eval_state) {
|
||||
/* if this is an indirect reference, handle the indirection. */
|
||||
if (memref->value.is_indirect) {
|
||||
const unsigned new_address = memref->address + eval_state->add_address;
|
||||
rc_update_memref_value(&memref->value, rc_peek_value(new_address, memref->value.size, eval_state->peek, eval_state->peek_userdata));
|
||||
}
|
||||
|
||||
return memref;
|
||||
return rc_get_memref_value_value(&memref->value, operand_type);
|
||||
}
|
||||
|
||||
unsigned rc_get_memref_value_value(rc_memref_value_t* memref, int operand_type) {
|
||||
switch (operand_type)
|
||||
{
|
||||
/* most common case explicitly first, even though it could be handled by default case.
|
||||
* this helps the compiler to optimize if it turns the switch into a series of if/elses */
|
||||
case RC_OPERAND_ADDRESS:
|
||||
return memref->value;
|
||||
|
||||
case RC_OPERAND_DELTA:
|
||||
if (!memref->changed) {
|
||||
/* fallthrough */
|
||||
default:
|
||||
return memref->value;
|
||||
}
|
||||
/* fallthrough */
|
||||
case RC_OPERAND_PRIOR:
|
||||
return memref->prior;
|
||||
}
|
||||
}
|
||||
Vendored
+58
-72
@@ -1,4 +1,4 @@
|
||||
#include "internal.h"
|
||||
#include "rc_internal.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
@@ -29,7 +29,7 @@ static int rc_parse_operand_lua(rc_operand_t* self, const char** memaddr, rc_par
|
||||
return RC_INVALID_LUA_OPERAND;
|
||||
}
|
||||
|
||||
if (!isalpha((unsigned char)*aux)) {
|
||||
if (!isalpha(*aux)) {
|
||||
return RC_INVALID_LUA_OPERAND;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ static int rc_parse_operand_lua(rc_operand_t* self, const char** memaddr, rc_par
|
||||
id = aux;
|
||||
#endif
|
||||
|
||||
while (isalnum((unsigned char)*aux) || *aux == '_') {
|
||||
while (isalnum(*aux) || *aux == '_') {
|
||||
aux++;
|
||||
}
|
||||
|
||||
@@ -68,88 +68,66 @@ static int rc_parse_operand_lua(rc_operand_t* self, const char** memaddr, rc_par
|
||||
|
||||
static int rc_parse_operand_memory(rc_operand_t* self, const char** memaddr, rc_parse_state_t* parse, int is_indirect) {
|
||||
const char* aux = *memaddr;
|
||||
char* end;
|
||||
unsigned long address;
|
||||
unsigned address;
|
||||
char size;
|
||||
int ret;
|
||||
|
||||
switch (*aux++) {
|
||||
switch (*aux) {
|
||||
case 'd': case 'D':
|
||||
self->type = RC_OPERAND_DELTA;
|
||||
++aux;
|
||||
break;
|
||||
|
||||
case 'p': case 'P':
|
||||
self->type = RC_OPERAND_PRIOR;
|
||||
++aux;
|
||||
break;
|
||||
|
||||
case 'b': case 'B':
|
||||
self->type = RC_OPERAND_BCD;
|
||||
++aux;
|
||||
break;
|
||||
|
||||
case '~':
|
||||
self->type = RC_OPERAND_INVERTED;
|
||||
++aux;
|
||||
break;
|
||||
|
||||
default:
|
||||
self->type = RC_OPERAND_ADDRESS;
|
||||
aux--;
|
||||
break;
|
||||
}
|
||||
|
||||
if (*aux++ != '0') {
|
||||
return RC_INVALID_MEMORY_OPERAND;
|
||||
}
|
||||
ret = rc_parse_memref(&aux, &self->size, &address);
|
||||
if (ret != RC_OK)
|
||||
return ret;
|
||||
|
||||
if (*aux != 'x' && *aux != 'X') {
|
||||
return RC_INVALID_MEMORY_OPERAND;
|
||||
}
|
||||
|
||||
aux++;
|
||||
|
||||
switch (*aux++) {
|
||||
case 'm': case 'M': self->size = RC_MEMSIZE_BIT_0; size = RC_MEMSIZE_8_BITS; break;
|
||||
case 'n': case 'N': self->size = RC_MEMSIZE_BIT_1; size = RC_MEMSIZE_8_BITS; break;
|
||||
case 'o': case 'O': self->size = RC_MEMSIZE_BIT_2; size = RC_MEMSIZE_8_BITS; break;
|
||||
case 'p': case 'P': self->size = RC_MEMSIZE_BIT_3; size = RC_MEMSIZE_8_BITS; break;
|
||||
case 'q': case 'Q': self->size = RC_MEMSIZE_BIT_4; size = RC_MEMSIZE_8_BITS; break;
|
||||
case 'r': case 'R': self->size = RC_MEMSIZE_BIT_5; size = RC_MEMSIZE_8_BITS; break;
|
||||
case 's': case 'S': self->size = RC_MEMSIZE_BIT_6; size = RC_MEMSIZE_8_BITS; break;
|
||||
case 't': case 'T': self->size = RC_MEMSIZE_BIT_7; size = RC_MEMSIZE_8_BITS; break;
|
||||
case 'l': case 'L': self->size = RC_MEMSIZE_LOW; size = RC_MEMSIZE_8_BITS; break;
|
||||
case 'u': case 'U': self->size = RC_MEMSIZE_HIGH; size = RC_MEMSIZE_8_BITS; break;
|
||||
case 'k': case 'K': self->size = RC_MEMSIZE_BITCOUNT; size = RC_MEMSIZE_8_BITS; break;
|
||||
case 'h': case 'H': self->size = size = RC_MEMSIZE_8_BITS; break;
|
||||
case 'w': case 'W': self->size = size = RC_MEMSIZE_24_BITS; break;
|
||||
case 'x': case 'X': self->size = size = RC_MEMSIZE_32_BITS; break;
|
||||
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
|
||||
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
|
||||
aux--;
|
||||
/* fallthrough */
|
||||
case ' ':
|
||||
self->size = size = RC_MEMSIZE_16_BITS;
|
||||
switch (self->size) {
|
||||
case RC_MEMSIZE_BIT_0:
|
||||
case RC_MEMSIZE_BIT_1:
|
||||
case RC_MEMSIZE_BIT_2:
|
||||
case RC_MEMSIZE_BIT_3:
|
||||
case RC_MEMSIZE_BIT_4:
|
||||
case RC_MEMSIZE_BIT_5:
|
||||
case RC_MEMSIZE_BIT_6:
|
||||
case RC_MEMSIZE_BIT_7:
|
||||
case RC_MEMSIZE_LOW:
|
||||
case RC_MEMSIZE_HIGH:
|
||||
case RC_MEMSIZE_BITCOUNT:
|
||||
/* these can all share an 8-bit memref and just mask off the appropriate data in rc_evaluate_operand */
|
||||
size = RC_MEMSIZE_8_BITS;
|
||||
break;
|
||||
|
||||
default:
|
||||
return RC_INVALID_MEMORY_OPERAND;
|
||||
size = self->size;
|
||||
break;
|
||||
}
|
||||
|
||||
address = strtoul(aux, &end, 16);
|
||||
|
||||
if (end == aux) {
|
||||
return RC_INVALID_MEMORY_OPERAND;
|
||||
}
|
||||
|
||||
if (address > 0xffffffffU) {
|
||||
address = 0xffffffffU;
|
||||
}
|
||||
|
||||
self->value.memref = rc_alloc_memref_value(parse, address, size, is_indirect);
|
||||
self->value.memref = rc_alloc_memref(parse, address, size, is_indirect);
|
||||
if (parse->offset < 0)
|
||||
return parse->offset;
|
||||
|
||||
*memaddr = end;
|
||||
*memaddr = aux;
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
@@ -204,9 +182,10 @@ int rc_parse_operand(rc_operand_t* self, const char** memaddr, int is_trigger, i
|
||||
break;
|
||||
|
||||
case 'v': case 'V': /* signed integer constant */
|
||||
negative = 0;
|
||||
++aux;
|
||||
|
||||
/* fallthrough */
|
||||
case '+': case '-': /* signed integer constant */
|
||||
negative = 0;
|
||||
if (*aux == '-')
|
||||
{
|
||||
negative = 1;
|
||||
@@ -238,7 +217,7 @@ int rc_parse_operand(rc_operand_t* self, const char** memaddr, int is_trigger, i
|
||||
break;
|
||||
|
||||
case '0':
|
||||
if (aux[1] == 'x' || aux[1] == 'X') {
|
||||
if (aux[1] == 'x' || aux[1] == 'X') { /* hex integer constant */
|
||||
/* fall through */
|
||||
default:
|
||||
ret = rc_parse_operand_memory(self, &aux, parse, is_indirect);
|
||||
@@ -251,8 +230,7 @@ int rc_parse_operand(rc_operand_t* self, const char** memaddr, int is_trigger, i
|
||||
}
|
||||
|
||||
/* fall through for case '0' where not '0x' */
|
||||
case '+': case '-':
|
||||
case '1': case '2': case '3': case '4': case '5':
|
||||
case '1': case '2': case '3': case '4': case '5': /* unsigned integer constant */
|
||||
case '6': case '7': case '8': case '9':
|
||||
value = strtoul(aux, &end, 10);
|
||||
|
||||
@@ -305,6 +283,18 @@ static int rc_luapeek(lua_State* L) {
|
||||
|
||||
#endif /* RC_DISABLE_LUA */
|
||||
|
||||
int rc_operand_is_memref(rc_operand_t* self) {
|
||||
switch (self->type) {
|
||||
case RC_OPERAND_CONST:
|
||||
case RC_OPERAND_FP:
|
||||
case RC_OPERAND_LUA:
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static const unsigned char rc_bits_set[16] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 };
|
||||
|
||||
unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
|
||||
@@ -312,7 +302,7 @@ unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
|
||||
rc_luapeek_t luapeek;
|
||||
#endif /* RC_DISABLE_LUA */
|
||||
|
||||
unsigned value = 0;
|
||||
unsigned value;
|
||||
|
||||
/* step 1: read memory */
|
||||
switch (self->type) {
|
||||
@@ -324,8 +314,9 @@ unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
|
||||
return 0;
|
||||
|
||||
case RC_OPERAND_LUA:
|
||||
#ifndef RC_DISABLE_LUA
|
||||
value = 0;
|
||||
|
||||
#ifndef RC_DISABLE_LUA
|
||||
if (eval_state->L != 0) {
|
||||
lua_rawgeti(eval_state->L, LUA_REGISTRYINDEX, self->value.luafunc);
|
||||
lua_pushcfunction(eval_state->L, rc_luapeek);
|
||||
@@ -351,18 +342,8 @@ unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
|
||||
|
||||
break;
|
||||
|
||||
case RC_OPERAND_ADDRESS:
|
||||
case RC_OPERAND_BCD:
|
||||
case RC_OPERAND_INVERTED:
|
||||
value = rc_get_indirect_memref(self->value.memref, eval_state)->value;
|
||||
break;
|
||||
|
||||
case RC_OPERAND_DELTA:
|
||||
value = rc_get_indirect_memref(self->value.memref, eval_state)->previous;
|
||||
break;
|
||||
|
||||
case RC_OPERAND_PRIOR:
|
||||
value = rc_get_indirect_memref(self->value.memref, eval_state)->prior;
|
||||
default:
|
||||
value = rc_get_memref_value(self->value.memref, self->type, eval_state);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -413,6 +394,9 @@ unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
|
||||
value = rc_bits_set[(value & 0x0F)]
|
||||
+ rc_bits_set[((value >> 4) & 0x0F)];
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* step 3: apply logic */
|
||||
@@ -443,6 +427,7 @@ unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_32_BITS:
|
||||
case RC_MEMSIZE_VARIABLE:
|
||||
value = ((value >> 28) & 0x0f) * 10000000
|
||||
+ ((value >> 24) & 0x0f) * 1000000
|
||||
+ ((value >> 20) & 0x0f) * 100000
|
||||
@@ -479,6 +464,7 @@ unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_32_BITS:
|
||||
case RC_MEMSIZE_VARIABLE:
|
||||
value ^= 0xffffffff;
|
||||
break;
|
||||
|
||||
|
||||
File renamed without changes.
+67
-30
@@ -1,53 +1,73 @@
|
||||
#ifndef INTERNAL_H
|
||||
#define INTERNAL_H
|
||||
|
||||
#include "rcheevos.h"
|
||||
#include "rc_runtime_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct rc_scratch_string {
|
||||
char* value;
|
||||
struct rc_scratch_string* left;
|
||||
struct rc_scratch_string* right;
|
||||
}
|
||||
rc_scratch_string_t;
|
||||
|
||||
#define RC_ALLOW_ALIGN(T) struct __align_ ## T { char ch; T t; };
|
||||
RC_ALLOW_ALIGN(rc_condition_t)
|
||||
RC_ALLOW_ALIGN(rc_condset_t)
|
||||
RC_ALLOW_ALIGN(rc_lboard_t)
|
||||
RC_ALLOW_ALIGN(rc_memref_value_t)
|
||||
RC_ALLOW_ALIGN(rc_memref_t)
|
||||
RC_ALLOW_ALIGN(rc_operand_t)
|
||||
RC_ALLOW_ALIGN(rc_richpresence_t)
|
||||
RC_ALLOW_ALIGN(rc_richpresence_display_t)
|
||||
RC_ALLOW_ALIGN(rc_richpresence_display_part_t)
|
||||
RC_ALLOW_ALIGN(rc_richpresence_lookup_t)
|
||||
RC_ALLOW_ALIGN(rc_richpresence_lookup_item_t)
|
||||
RC_ALLOW_ALIGN(rc_scratch_string_t)
|
||||
RC_ALLOW_ALIGN(rc_trigger_t)
|
||||
RC_ALLOW_ALIGN(rc_value_t)
|
||||
RC_ALLOW_ALIGN(char)
|
||||
|
||||
#define RC_ALIGNOF(T) (sizeof(struct __align_ ## T) - sizeof(T))
|
||||
#define RC_OFFSETOF(o, t) (int)((char*)&(o.t) - (char*)&(o))
|
||||
|
||||
#define RC_ALLOC(t, p) ((t*)rc_alloc((p)->buffer, &(p)->offset, sizeof(t), RC_ALIGNOF(t), &(p)->scratch))
|
||||
#define RC_ALLOC(t, p) ((t*)rc_alloc((p)->buffer, &(p)->offset, sizeof(t), RC_ALIGNOF(t), &(p)->scratch, RC_OFFSETOF((p)->scratch.objs, __ ## t)))
|
||||
#define RC_ALLOC_SCRATCH(t, p) ((t*)rc_alloc_scratch((p)->buffer, &(p)->offset, sizeof(t), RC_ALIGNOF(t), &(p)->scratch, RC_OFFSETOF((p)->scratch.objs, __ ## t)))
|
||||
|
||||
typedef struct rc_scratch_buffer {
|
||||
struct rc_scratch_buffer* next;
|
||||
int offset;
|
||||
unsigned char buffer[512 - 16];
|
||||
}
|
||||
rc_scratch_buffer_t;
|
||||
|
||||
typedef struct {
|
||||
rc_memref_t memref_buffer[16];
|
||||
rc_memref_t *memref;
|
||||
int memref_count;
|
||||
int memref_size;
|
||||
rc_scratch_buffer_t buffer;
|
||||
rc_scratch_string_t* strings;
|
||||
|
||||
union
|
||||
{
|
||||
rc_operand_t operand;
|
||||
rc_condition_t condition;
|
||||
rc_condset_t condset;
|
||||
rc_trigger_t trigger;
|
||||
rc_lboard_t lboard;
|
||||
rc_memref_value_t memref_value;
|
||||
rc_richpresence_t richpresence;
|
||||
rc_richpresence_display_t richpresence_display;
|
||||
rc_richpresence_display_part_t richpresence_part;
|
||||
rc_richpresence_lookup_t richpresence_lookup;
|
||||
rc_richpresence_lookup_item_t richpresence_lookup_item;
|
||||
} obj;
|
||||
struct objs {
|
||||
rc_condition_t* __rc_condition_t;
|
||||
rc_condset_t* __rc_condset_t;
|
||||
rc_lboard_t* __rc_lboard_t;
|
||||
rc_memref_t* __rc_memref_t;
|
||||
rc_operand_t* __rc_operand_t;
|
||||
rc_richpresence_t* __rc_richpresence_t;
|
||||
rc_richpresence_display_t* __rc_richpresence_display_t;
|
||||
rc_richpresence_display_part_t* __rc_richpresence_display_part_t;
|
||||
rc_richpresence_lookup_t* __rc_richpresence_lookup_t;
|
||||
rc_richpresence_lookup_item_t* __rc_richpresence_lookup_item_t;
|
||||
rc_scratch_string_t __rc_scratch_string_t;
|
||||
rc_trigger_t* __rc_trigger_t;
|
||||
rc_value_t* __rc_value_t;
|
||||
} objs;
|
||||
}
|
||||
rc_scratch_t;
|
||||
|
||||
typedef struct {
|
||||
unsigned add_value; /* AddSource/SubSource */
|
||||
unsigned add_hits; /* AddHits */
|
||||
int add_hits; /* AddHits */
|
||||
unsigned add_address; /* AddAddress */
|
||||
|
||||
rc_peek_t peek;
|
||||
@@ -59,6 +79,7 @@ typedef struct {
|
||||
char has_hits; /* one of more hit counts is non-zero */
|
||||
char primed; /* true if all non-Trigger conditions are true */
|
||||
char measured_from_hits; /* true if the measured_value came from a condition's hit count */
|
||||
char was_cond_reset; /* ResetNextIf triggered */
|
||||
}
|
||||
rc_eval_state_t;
|
||||
|
||||
@@ -71,27 +92,35 @@ typedef struct {
|
||||
void* buffer;
|
||||
rc_scratch_t scratch;
|
||||
|
||||
rc_memref_value_t** first_memref;
|
||||
rc_memref_t** first_memref;
|
||||
rc_value_t** variables;
|
||||
|
||||
unsigned measured_target;
|
||||
int lines_read;
|
||||
|
||||
char has_required_hits;
|
||||
}
|
||||
rc_parse_state_t;
|
||||
|
||||
void rc_init_parse_state(rc_parse_state_t* parse, void* buffer, lua_State* L, int funcs_ndx);
|
||||
void rc_init_parse_state_memrefs(rc_parse_state_t* parse, rc_memref_value_t** memrefs);
|
||||
void rc_init_parse_state_memrefs(rc_parse_state_t* parse, rc_memref_t** memrefs);
|
||||
void rc_init_parse_state_variables(rc_parse_state_t* parse, rc_value_t** variables);
|
||||
void rc_destroy_parse_state(rc_parse_state_t* parse);
|
||||
|
||||
void* rc_alloc(void* pointer, int* offset, int size, int alignment, rc_scratch_t* scratch);
|
||||
void* rc_alloc(void* pointer, int* offset, int size, int alignment, rc_scratch_t* scratch, int scratch_object_pointer_offset);
|
||||
void* rc_alloc_scratch(void* pointer, int* offset, int size, int alignment, rc_scratch_t* scratch, int scratch_object_pointer_offset);
|
||||
char* rc_alloc_str(rc_parse_state_t* parse, const char* text, int length);
|
||||
|
||||
rc_memref_value_t* rc_alloc_memref_value(rc_parse_state_t* parse, unsigned address, char size, char is_indirect);
|
||||
void rc_update_memref_values(rc_memref_value_t* memref, rc_peek_t peek, void* ud);
|
||||
void rc_update_memref_value(rc_memref_value_t* memref, rc_peek_t peek, void* ud);
|
||||
rc_memref_value_t* rc_get_indirect_memref(rc_memref_value_t* memref, rc_eval_state_t* eval_state);
|
||||
rc_memref_t* rc_alloc_memref(rc_parse_state_t* parse, unsigned address, char size, char is_indirect);
|
||||
int rc_parse_memref(const char** memaddr, char* size, unsigned* address);
|
||||
void rc_update_memref_values(rc_memref_t* memref, rc_peek_t peek, void* ud);
|
||||
void rc_update_memref_value(rc_memref_value_t* memref, unsigned value);
|
||||
unsigned rc_get_memref_value(rc_memref_t* memref, int operand_type, rc_eval_state_t* eval_state);
|
||||
unsigned rc_get_memref_value_value(rc_memref_value_t* memref, int operand_type);
|
||||
|
||||
void rc_parse_trigger_internal(rc_trigger_t* self, const char** memaddr, rc_parse_state_t* parse);
|
||||
|
||||
rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse);
|
||||
rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse, int is_value);
|
||||
int rc_test_condset(rc_condset_t* self, rc_eval_state_t* eval_state);
|
||||
void rc_reset_condset(rc_condset_t* self);
|
||||
|
||||
@@ -101,11 +130,19 @@ int rc_evaluate_condition_value(rc_condition_t* self, rc_eval_state_t* eval_stat
|
||||
|
||||
int rc_parse_operand(rc_operand_t* self, const char** memaddr, int is_trigger, int is_indirect, rc_parse_state_t* parse);
|
||||
unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state);
|
||||
char rc_parse_operator(const char** memaddr);
|
||||
|
||||
void rc_parse_value_internal(rc_value_t* self, const char** memaddr, rc_parse_state_t* parse);
|
||||
void rc_reset_value(rc_value_t* self);
|
||||
rc_value_t* rc_alloc_helper_variable(const char* memaddr, int memaddr_len, rc_parse_state_t* parse);
|
||||
void rc_update_variables(rc_value_t* variable, rc_peek_t peek, void* ud, lua_State* L);
|
||||
|
||||
void rc_parse_lboard_internal(rc_lboard_t* self, const char* memaddr, rc_parse_state_t* parse);
|
||||
|
||||
void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script, rc_parse_state_t* parse);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* INTERNAL_H */
|
||||
+505
@@ -0,0 +1,505 @@
|
||||
/* This file provides a series of functions for integrating RetroAchievements with libretro.
|
||||
* These functions will be called by a libretro frontend to validate certain expected behaviors
|
||||
* and simplify mapping core data to the RAIntegration DLL.
|
||||
*
|
||||
* Originally designed to be shared between RALibretro and RetroArch, but will simplify
|
||||
* integrating with any other frontends.
|
||||
*/
|
||||
|
||||
#include "rc_libretro.h"
|
||||
|
||||
#include "rc_consoles.h"
|
||||
#include "rc_compat.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
static rc_libretro_message_callback rc_libretro_verbose_message_callback = NULL;
|
||||
|
||||
/* a value that starts with a comma is a CSV.
|
||||
* if it starts with an exclamation point, it's everything but the provided value.
|
||||
* if it starts with an exclamntion point followed by a comma, it's everything but the CSV values.
|
||||
* values are case-insensitive */
|
||||
typedef struct rc_disallowed_core_settings_t
|
||||
{
|
||||
const char* library_name;
|
||||
const rc_disallowed_setting_t* disallowed_settings;
|
||||
} rc_disallowed_core_settings_t;
|
||||
|
||||
static const rc_disallowed_setting_t _rc_disallowed_bsnes_settings[] = {
|
||||
{ "bsnes_region", "pal" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const rc_disallowed_setting_t _rc_disallowed_dolphin_settings[] = {
|
||||
{ "dolphin_cheats_enabled", "enabled" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const rc_disallowed_setting_t _rc_disallowed_ecwolf_settings[] = {
|
||||
{ "ecwolf-invulnerability", "enabled" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const rc_disallowed_setting_t _rc_disallowed_fbneo_settings[] = {
|
||||
{ "fbneo-allow-patched-romsets", "enabled" },
|
||||
{ "fbneo-cheat-*", "!,Disabled,0 - Disabled" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const rc_disallowed_setting_t _rc_disallowed_fceumm_settings[] = {
|
||||
{ "fceumm_region", ",PAL,Dendy" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const rc_disallowed_setting_t _rc_disallowed_gpgx_settings[] = {
|
||||
{ "genesis_plus_gx_lock_on", ",action replay (pro),game genie" },
|
||||
{ "genesis_plus_gx_region_detect", "pal" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const rc_disallowed_setting_t _rc_disallowed_gpgx_wide_settings[] = {
|
||||
{ "genesis_plus_gx_wide_lock_on", ",action replay (pro),game genie" },
|
||||
{ "genesis_plus_gx_wide_region_detect", "pal" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const rc_disallowed_setting_t _rc_disallowed_mesen_settings[] = {
|
||||
{ "mesen_region", ",PAL,Dendy" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const rc_disallowed_setting_t _rc_disallowed_mesen_s_settings[] = {
|
||||
{ "mesen-s_region", "PAL" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const rc_disallowed_setting_t _rc_disallowed_pcsx_rearmed_settings[] = {
|
||||
{ "pcsx_rearmed_region", "pal" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const rc_disallowed_setting_t _rc_disallowed_picodrive_settings[] = {
|
||||
{ "picodrive_region", ",Europe,Japan PAL" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const rc_disallowed_setting_t _rc_disallowed_ppsspp_settings[] = {
|
||||
{ "ppsspp_cheats", "enabled" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const rc_disallowed_setting_t _rc_disallowed_snes9x_settings[] = {
|
||||
{ "snes9x_region", "pal" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const rc_disallowed_setting_t _rc_disallowed_virtual_jaguar_settings[] = {
|
||||
{ "virtualjaguar_pal", "enabled" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const rc_disallowed_core_settings_t rc_disallowed_core_settings[] = {
|
||||
{ "bsnes-mercury", _rc_disallowed_bsnes_settings },
|
||||
{ "dolphin-emu", _rc_disallowed_dolphin_settings },
|
||||
{ "ecwolf", _rc_disallowed_ecwolf_settings },
|
||||
{ "FCEUmm", _rc_disallowed_fceumm_settings },
|
||||
{ "FinalBurn Neo", _rc_disallowed_fbneo_settings },
|
||||
{ "Genesis Plus GX", _rc_disallowed_gpgx_settings },
|
||||
{ "Genesis Plus GX Wide", _rc_disallowed_gpgx_wide_settings },
|
||||
{ "Mesen", _rc_disallowed_mesen_settings },
|
||||
{ "Mesen-S", _rc_disallowed_mesen_s_settings },
|
||||
{ "PPSSPP", _rc_disallowed_ppsspp_settings },
|
||||
{ "PCSX-ReARMed", _rc_disallowed_pcsx_rearmed_settings },
|
||||
{ "PicoDrive", _rc_disallowed_picodrive_settings },
|
||||
{ "Snes9x", _rc_disallowed_snes9x_settings },
|
||||
{ "Virtual Jaguar", _rc_disallowed_virtual_jaguar_settings },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static int rc_libretro_string_equal_nocase(const char* test, const char* value) {
|
||||
while (*test) {
|
||||
if (tolower(*test++) != tolower(*value++))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (*value == '\0');
|
||||
}
|
||||
|
||||
static int rc_libretro_match_value(const char* val, const char* match) {
|
||||
/* if value starts with a comma, it's a CSV list of potential matches */
|
||||
if (*match == ',') {
|
||||
do {
|
||||
const char* ptr = ++match;
|
||||
int size;
|
||||
|
||||
while (*match && *match != ',')
|
||||
++match;
|
||||
|
||||
size = match - ptr;
|
||||
if (val[size] == '\0') {
|
||||
if (memcmp(ptr, val, size) == 0) {
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
char buffer[128];
|
||||
memcpy(buffer, ptr, size);
|
||||
buffer[size] = '\0';
|
||||
if (rc_libretro_string_equal_nocase(buffer, val))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} while (*match == ',');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* a leading exclamation point means the provided value(s) are not forbidden (are allowed) */
|
||||
if (*match == '!')
|
||||
return !rc_libretro_match_value(val, &match[1]);
|
||||
|
||||
/* just a single value, attempt to match it */
|
||||
return rc_libretro_string_equal_nocase(val, match);
|
||||
}
|
||||
|
||||
int rc_libretro_is_setting_allowed(const rc_disallowed_setting_t* disallowed_settings, const char* setting, const char* value) {
|
||||
const char* key;
|
||||
size_t key_len;
|
||||
|
||||
for (; disallowed_settings->setting; ++disallowed_settings) {
|
||||
key = disallowed_settings->setting;
|
||||
key_len = strlen(key);
|
||||
|
||||
if (key[key_len - 1] == '*') {
|
||||
if (memcmp(setting, key, key_len - 1) == 0) {
|
||||
if (rc_libretro_match_value(value, disallowed_settings->value))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (memcmp(setting, key, key_len + 1) == 0) {
|
||||
if (rc_libretro_match_value(value, disallowed_settings->value))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
const rc_disallowed_setting_t* rc_libretro_get_disallowed_settings(const char* library_name) {
|
||||
const rc_disallowed_core_settings_t* core_filter = rc_disallowed_core_settings;
|
||||
size_t library_name_length;
|
||||
|
||||
if (!library_name || !library_name[0])
|
||||
return NULL;
|
||||
|
||||
library_name_length = strlen(library_name) + 1;
|
||||
while (core_filter->library_name) {
|
||||
if (memcmp(core_filter->library_name, library_name, library_name_length) == 0)
|
||||
return core_filter->disallowed_settings;
|
||||
|
||||
++core_filter;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
unsigned char* rc_libretro_memory_find(const rc_libretro_memory_regions_t* regions, unsigned address) {
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < regions->count; ++i) {
|
||||
const size_t size = regions->size[i];
|
||||
if (address < size) {
|
||||
if (regions->data[i] == NULL)
|
||||
break;
|
||||
|
||||
return ®ions->data[i][address];
|
||||
}
|
||||
|
||||
address -= size;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void rc_libretro_init_verbose_message_callback(rc_libretro_message_callback callback) {
|
||||
rc_libretro_verbose_message_callback = callback;
|
||||
}
|
||||
|
||||
static void rc_libretro_verbose(const char* message) {
|
||||
if (rc_libretro_verbose_message_callback)
|
||||
rc_libretro_verbose_message_callback(message);
|
||||
}
|
||||
|
||||
static const char* rc_memory_type_str(int type) {
|
||||
switch (type)
|
||||
{
|
||||
case RC_MEMORY_TYPE_SAVE_RAM:
|
||||
return "SRAM";
|
||||
case RC_MEMORY_TYPE_VIDEO_RAM:
|
||||
return "VRAM";
|
||||
case RC_MEMORY_TYPE_UNUSED:
|
||||
return "UNUSED";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return "SYSTEM RAM";
|
||||
}
|
||||
|
||||
static void rc_libretro_memory_register_region(rc_libretro_memory_regions_t* regions, int type,
|
||||
unsigned char* data, size_t size, const char* description) {
|
||||
if (size == 0)
|
||||
return;
|
||||
|
||||
if (regions->count == (sizeof(regions->size) / sizeof(regions->size[0]))) {
|
||||
rc_libretro_verbose("Too many memory memory regions to register");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data && regions->count > 0 && !regions->data[regions->count - 1]) {
|
||||
/* extend null region */
|
||||
regions->size[regions->count - 1] += size;
|
||||
}
|
||||
else if (data && regions->count > 0 &&
|
||||
data == (regions->data[regions->count - 1] + regions->size[regions->count - 1])) {
|
||||
/* extend non-null region */
|
||||
regions->size[regions->count - 1] += size;
|
||||
}
|
||||
else {
|
||||
/* create new region */
|
||||
regions->data[regions->count] = data;
|
||||
regions->size[regions->count] = size;
|
||||
++regions->count;
|
||||
}
|
||||
|
||||
regions->total_size += size;
|
||||
|
||||
if (rc_libretro_verbose_message_callback) {
|
||||
char message[128];
|
||||
snprintf(message, sizeof(message), "Registered 0x%04X bytes of %s at $%06X (%s)", (unsigned)size,
|
||||
rc_memory_type_str(type), (unsigned)(regions->total_size - size), description);
|
||||
rc_libretro_verbose_message_callback(message);
|
||||
}
|
||||
}
|
||||
|
||||
static void rc_libretro_memory_init_without_regions(rc_libretro_memory_regions_t* regions,
|
||||
rc_libretro_get_core_memory_info_func get_core_memory_info) {
|
||||
/* no regions specified, assume system RAM followed by save RAM */
|
||||
char description[64];
|
||||
rc_libretro_core_memory_info_t info;
|
||||
|
||||
snprintf(description, sizeof(description), "offset 0x%06x", 0);
|
||||
|
||||
get_core_memory_info(RETRO_MEMORY_SYSTEM_RAM, &info);
|
||||
if (info.size)
|
||||
rc_libretro_memory_register_region(regions, RC_MEMORY_TYPE_SYSTEM_RAM, info.data, info.size, description);
|
||||
|
||||
get_core_memory_info(RETRO_MEMORY_SAVE_RAM, &info);
|
||||
if (info.size)
|
||||
rc_libretro_memory_register_region(regions, RC_MEMORY_TYPE_SAVE_RAM, info.data, info.size, description);
|
||||
}
|
||||
|
||||
static const struct retro_memory_descriptor* rc_libretro_memory_get_descriptor(const struct retro_memory_map* mmap, unsigned real_address, size_t* offset)
|
||||
{
|
||||
const struct retro_memory_descriptor* desc = mmap->descriptors;
|
||||
const struct retro_memory_descriptor* end = desc + mmap->num_descriptors;
|
||||
|
||||
for (; desc < end; desc++) {
|
||||
if (desc->select == 0) {
|
||||
/* if select is 0, attempt to explcitly match the address */
|
||||
if (real_address >= desc->start && real_address < desc->start + desc->len) {
|
||||
*offset = real_address - desc->start;
|
||||
return desc;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* otherwise, attempt to match the address by matching the select bits */
|
||||
/* address is in the block if (addr & select) == (start & select) */
|
||||
if (((desc->start ^ real_address) & desc->select) == 0) {
|
||||
/* calculate the offset within the descriptor, removing any disconnected bits */
|
||||
*offset = (real_address & ~desc->disconnect) - desc->start;
|
||||
|
||||
/* sanity check - make sure the descriptor is large enough to hold the target address */
|
||||
if (*offset < desc->len)
|
||||
return desc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*offset = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void rc_libretro_memory_init_from_memory_map(rc_libretro_memory_regions_t* regions, const struct retro_memory_map* mmap,
|
||||
const rc_memory_regions_t* console_regions) {
|
||||
char description[64];
|
||||
unsigned i;
|
||||
unsigned char* region_start;
|
||||
unsigned char* desc_start;
|
||||
size_t desc_size;
|
||||
size_t offset;
|
||||
|
||||
for (i = 0; i < console_regions->num_regions; ++i) {
|
||||
const rc_memory_region_t* console_region = &console_regions->region[i];
|
||||
size_t console_region_size = console_region->end_address - console_region->start_address + 1;
|
||||
unsigned real_address = console_region->real_address;
|
||||
|
||||
while (console_region_size > 0) {
|
||||
const struct retro_memory_descriptor* desc = rc_libretro_memory_get_descriptor(mmap, real_address, &offset);
|
||||
if (!desc) {
|
||||
if (rc_libretro_verbose_message_callback && console_region->type != RC_MEMORY_TYPE_UNUSED) {
|
||||
snprintf(description, sizeof(description), "Could not map region starting at $%06X",
|
||||
real_address - console_region->real_address + console_region->start_address);
|
||||
rc_libretro_verbose(description);
|
||||
}
|
||||
|
||||
rc_libretro_memory_register_region(regions, console_region->type, NULL, console_region_size, "null filler");
|
||||
break;
|
||||
}
|
||||
|
||||
snprintf(description, sizeof(description), "descriptor %u, offset 0x%06X",
|
||||
(unsigned)(desc - mmap->descriptors) + 1, (int)offset);
|
||||
|
||||
if (desc->ptr) {
|
||||
desc_start = (uint8_t*)desc->ptr + desc->offset;
|
||||
region_start = desc_start + offset;
|
||||
}
|
||||
else {
|
||||
region_start = NULL;
|
||||
}
|
||||
|
||||
desc_size = desc->len - offset;
|
||||
|
||||
if (console_region_size > desc_size) {
|
||||
if (desc_size == 0) {
|
||||
if (rc_libretro_verbose_message_callback && console_region->type != RC_MEMORY_TYPE_UNUSED) {
|
||||
snprintf(description, sizeof(description), "Could not map region starting at $%06X",
|
||||
real_address - console_region->real_address + console_region->start_address);
|
||||
rc_libretro_verbose(description);
|
||||
}
|
||||
|
||||
rc_libretro_memory_register_region(regions, console_region->type, NULL, console_region_size, "null filler");
|
||||
console_region_size = 0;
|
||||
}
|
||||
else {
|
||||
rc_libretro_memory_register_region(regions, console_region->type, region_start, desc_size, description);
|
||||
console_region_size -= desc_size;
|
||||
real_address += desc_size;
|
||||
}
|
||||
}
|
||||
else {
|
||||
rc_libretro_memory_register_region(regions, console_region->type, region_start, console_region_size, description);
|
||||
console_region_size = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned rc_libretro_memory_console_region_to_ram_type(int region_type) {
|
||||
switch (region_type)
|
||||
{
|
||||
case RC_MEMORY_TYPE_SAVE_RAM:
|
||||
return RETRO_MEMORY_SAVE_RAM;
|
||||
case RC_MEMORY_TYPE_VIDEO_RAM:
|
||||
return RETRO_MEMORY_VIDEO_RAM;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return RETRO_MEMORY_SYSTEM_RAM;
|
||||
}
|
||||
|
||||
static void rc_libretro_memory_init_from_unmapped_memory(rc_libretro_memory_regions_t* regions,
|
||||
rc_libretro_get_core_memory_info_func get_core_memory_info, const rc_memory_regions_t* console_regions) {
|
||||
char description[64];
|
||||
unsigned i, j;
|
||||
rc_libretro_core_memory_info_t info;
|
||||
size_t offset;
|
||||
|
||||
for (i = 0; i < console_regions->num_regions; ++i) {
|
||||
const rc_memory_region_t* console_region = &console_regions->region[i];
|
||||
const size_t console_region_size = console_region->end_address - console_region->start_address + 1;
|
||||
const unsigned type = rc_libretro_memory_console_region_to_ram_type(console_region->type);
|
||||
unsigned base_address = 0;
|
||||
|
||||
for (j = 0; j <= i; ++j) {
|
||||
const rc_memory_region_t* console_region2 = &console_regions->region[j];
|
||||
if (rc_libretro_memory_console_region_to_ram_type(console_region2->type) == type) {
|
||||
base_address = console_region2->start_address;
|
||||
break;
|
||||
}
|
||||
}
|
||||
offset = console_region->start_address - base_address;
|
||||
|
||||
get_core_memory_info(type, &info);
|
||||
|
||||
if (offset < info.size) {
|
||||
info.size -= offset;
|
||||
|
||||
if (info.data) {
|
||||
snprintf(description, sizeof(description), "offset 0x%06X", (int)offset);
|
||||
info.data += offset;
|
||||
}
|
||||
else {
|
||||
snprintf(description, sizeof(description), "null filler");
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (rc_libretro_verbose_message_callback && console_region->type != RC_MEMORY_TYPE_UNUSED) {
|
||||
snprintf(description, sizeof(description), "Could not map region starting at $%06X", console_region->start_address);
|
||||
rc_libretro_verbose(description);
|
||||
}
|
||||
|
||||
info.data = NULL;
|
||||
info.size = 0;
|
||||
}
|
||||
|
||||
if (console_region_size > info.size) {
|
||||
/* want more than what is available, take what we can and null fill the rest */
|
||||
rc_libretro_memory_register_region(regions, console_region->type, info.data, info.size, description);
|
||||
rc_libretro_memory_register_region(regions, console_region->type, NULL, console_region_size - info.size, "null filler");
|
||||
}
|
||||
else {
|
||||
/* only take as much as we need */
|
||||
rc_libretro_memory_register_region(regions, console_region->type, info.data, console_region_size, description);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int rc_libretro_memory_init(rc_libretro_memory_regions_t* regions, const struct retro_memory_map* mmap,
|
||||
rc_libretro_get_core_memory_info_func get_core_memory_info, int console_id) {
|
||||
const rc_memory_regions_t* console_regions = rc_console_memory_regions(console_id);
|
||||
rc_libretro_memory_regions_t new_regions;
|
||||
int has_valid_region = 0;
|
||||
unsigned i;
|
||||
|
||||
if (!regions)
|
||||
return 0;
|
||||
|
||||
memset(&new_regions, 0, sizeof(new_regions));
|
||||
|
||||
if (console_regions == NULL || console_regions->num_regions == 0)
|
||||
rc_libretro_memory_init_without_regions(&new_regions, get_core_memory_info);
|
||||
else if (mmap && mmap->num_descriptors != 0)
|
||||
rc_libretro_memory_init_from_memory_map(&new_regions, mmap, console_regions);
|
||||
else
|
||||
rc_libretro_memory_init_from_unmapped_memory(&new_regions, get_core_memory_info, console_regions);
|
||||
|
||||
/* determine if any valid regions were found */
|
||||
for (i = 0; i < new_regions.count; i++) {
|
||||
if (new_regions.data[i]) {
|
||||
has_valid_region = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(regions, &new_regions, sizeof(*regions));
|
||||
return has_valid_region;
|
||||
}
|
||||
|
||||
void rc_libretro_memory_destroy(rc_libretro_memory_regions_t* regions) {
|
||||
memset(regions, 0, sizeof(*regions));
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
#ifndef RC_LIBRETRO_H
|
||||
#define RC_LIBRETRO_H
|
||||
|
||||
/* this file comes from the libretro repository, which is not an explicit submodule.
|
||||
* the integration must set up paths appropriately to find it. */
|
||||
#include <libretro.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*****************************************************************************\
|
||||
| Disallowed Settings |
|
||||
\*****************************************************************************/
|
||||
|
||||
typedef struct rc_disallowed_setting_t
|
||||
{
|
||||
const char* setting;
|
||||
const char* value;
|
||||
} rc_disallowed_setting_t;
|
||||
|
||||
const rc_disallowed_setting_t* rc_libretro_get_disallowed_settings(const char* library_name);
|
||||
int rc_libretro_is_setting_allowed(const rc_disallowed_setting_t* disallowed_settings, const char* setting, const char* value);
|
||||
|
||||
/*****************************************************************************\
|
||||
| Memory Mapping |
|
||||
\*****************************************************************************/
|
||||
|
||||
/* specifies a function to call for verbose logging */
|
||||
typedef void (*rc_libretro_message_callback)(const char*);
|
||||
void rc_libretro_init_verbose_message_callback(rc_libretro_message_callback callback);
|
||||
|
||||
#define RC_LIBRETRO_MAX_MEMORY_REGIONS 32
|
||||
typedef struct rc_libretro_memory_regions_t
|
||||
{
|
||||
unsigned char* data[RC_LIBRETRO_MAX_MEMORY_REGIONS];
|
||||
size_t size[RC_LIBRETRO_MAX_MEMORY_REGIONS];
|
||||
size_t total_size;
|
||||
unsigned count;
|
||||
} rc_libretro_memory_regions_t;
|
||||
|
||||
typedef struct rc_libretro_core_memory_info_t
|
||||
{
|
||||
unsigned char* data;
|
||||
size_t size;
|
||||
} rc_libretro_core_memory_info_t;
|
||||
|
||||
typedef void (*rc_libretro_get_core_memory_info_func)(unsigned id, rc_libretro_core_memory_info_t* info);
|
||||
|
||||
int rc_libretro_memory_init(rc_libretro_memory_regions_t* regions, const struct retro_memory_map* mmap,
|
||||
rc_libretro_get_core_memory_info_func get_core_memory_info, int console_id);
|
||||
void rc_libretro_memory_destroy(rc_libretro_memory_regions_t* regions);
|
||||
|
||||
unsigned char* rc_libretro_memory_find(const rc_libretro_memory_regions_t* regions, unsigned address);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* RC_LIBRETRO_H */
|
||||
+422
-191
@@ -1,6 +1,6 @@
|
||||
#include "internal.h"
|
||||
#include "rc_internal.h"
|
||||
|
||||
#include "compat.h"
|
||||
#include "rc_compat.h"
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
@@ -11,7 +11,31 @@ enum {
|
||||
RC_FORMAT_UNKNOWN_MACRO = 103
|
||||
};
|
||||
|
||||
static const char* rc_parse_line(const char* line, const char** end) {
|
||||
static rc_memref_value_t* rc_alloc_helper_variable_memref_value(const char* memaddr, int memaddr_len, rc_parse_state_t* parse) {
|
||||
const char* end;
|
||||
rc_value_t* variable;
|
||||
unsigned address;
|
||||
char size;
|
||||
|
||||
/* single memory reference lookups without a modifier flag can be handled without a variable */
|
||||
end = memaddr;
|
||||
if (rc_parse_memref(&end, &size, &address) == RC_OK) {
|
||||
/* make sure the entire memaddr was consumed. if not, there's an operator and it's a comparison, not a memory reference */
|
||||
if (end == &memaddr[memaddr_len]) {
|
||||
/* just a memory reference, allocate it */
|
||||
return &rc_alloc_memref(parse, address, size, 0)->value;
|
||||
}
|
||||
}
|
||||
|
||||
/* not a simple memory reference, need to create a variable */
|
||||
variable = rc_alloc_helper_variable(memaddr, memaddr_len, parse);
|
||||
if (!variable)
|
||||
return NULL;
|
||||
|
||||
return &variable->value;
|
||||
}
|
||||
|
||||
static const char* rc_parse_line(const char* line, const char** end, rc_parse_state_t* parse) {
|
||||
const char* nextline;
|
||||
const char* endline;
|
||||
|
||||
@@ -20,29 +44,35 @@ static const char* rc_parse_line(const char* line, const char** end) {
|
||||
while (*nextline && *nextline != '\n')
|
||||
++nextline;
|
||||
|
||||
/* find a trailing comment marker (//) */
|
||||
/* if a trailing comment marker (//) exists, the line stops there */
|
||||
endline = line;
|
||||
while (endline < nextline && (endline[0] != '/' || endline[1] != '/' || (endline > line && endline[-1] == '\\')))
|
||||
++endline;
|
||||
|
||||
/* remove trailing whitespace */
|
||||
if (endline == nextline) {
|
||||
/* trailing whitespace on a line without a comment marker may be significant, just remove the line ending */
|
||||
if (endline > line && endline[-1] == '\r')
|
||||
--endline;
|
||||
} else {
|
||||
while (endline > line && isspace((unsigned char)endline[-1]))
|
||||
/* remove trailing whitespace before the comment marker */
|
||||
while (endline > line && isspace((int)((unsigned char*)endline)[-1]))
|
||||
--endline;
|
||||
}
|
||||
|
||||
/* end is pointing at the first character to ignore - makes subtraction for length easier */
|
||||
/* point end at the first character to ignore, it makes subtraction for length easier */
|
||||
*end = endline;
|
||||
|
||||
/* tally the line */
|
||||
++parse->lines_read;
|
||||
|
||||
/* skip the newline character so we're pointing at the next line */
|
||||
if (*nextline == '\n')
|
||||
++nextline;
|
||||
|
||||
return nextline;
|
||||
}
|
||||
|
||||
static rc_richpresence_display_t* rc_parse_richpresence_display_internal(const char* line, const char* endline, rc_parse_state_t* parse, rc_richpresence_t* richpresence) {
|
||||
static rc_richpresence_display_t* rc_parse_richpresence_display_internal(const char* line, const char* endline, rc_parse_state_t* parse, rc_richpresence_lookup_t* first_lookup) {
|
||||
rc_richpresence_display_t* self;
|
||||
rc_richpresence_display_part_t* part;
|
||||
rc_richpresence_display_part_t** next;
|
||||
@@ -111,78 +141,56 @@ static rc_richpresence_display_t* rc_parse_richpresence_display_internal(const c
|
||||
}
|
||||
|
||||
if (ptr > line) {
|
||||
if (!parse->buffer) {
|
||||
/* just calculating size, can't confirm lookup exists */
|
||||
part = RC_ALLOC(rc_richpresence_display_part_t, parse);
|
||||
|
||||
in = line;
|
||||
line = ++ptr;
|
||||
while (ptr < endline && *ptr != ')')
|
||||
++ptr;
|
||||
if (*ptr == ')') {
|
||||
rc_parse_value_internal(&part->value, &line, parse);
|
||||
if (parse->offset < 0)
|
||||
return 0;
|
||||
++ptr;
|
||||
} else {
|
||||
/* no closing parenthesis - allocate space for the invalid string */
|
||||
--in; /* already skipped over @ */
|
||||
rc_alloc_str(parse, line, (int)(ptr - in));
|
||||
}
|
||||
|
||||
} else {
|
||||
/* find the lookup and hook it up */
|
||||
lookup = richpresence->first_lookup;
|
||||
while (lookup) {
|
||||
if (strncmp(lookup->name, line, ptr - line) == 0 && lookup->name[ptr - line] == '\0') {
|
||||
part = RC_ALLOC(rc_richpresence_display_part_t, parse);
|
||||
*next = part;
|
||||
next = &part->next;
|
||||
|
||||
part->text = lookup->name;
|
||||
part->first_lookup_item = lookup->first_item;
|
||||
part->display_type = lookup->format;
|
||||
|
||||
in = line;
|
||||
line = ++ptr;
|
||||
while (ptr < endline && *ptr != ')')
|
||||
++ptr;
|
||||
if (*ptr == ')') {
|
||||
rc_parse_value_internal(&part->value, &line, parse);
|
||||
part->value.memrefs = 0;
|
||||
if (parse->offset < 0)
|
||||
return 0;
|
||||
++ptr;
|
||||
}
|
||||
else {
|
||||
/* non-terminated macro, dump the macro and the remaining portion of the line */
|
||||
--in; /* already skipped over @ */
|
||||
part->display_type = RC_FORMAT_STRING;
|
||||
part->text = rc_alloc_str(parse, in, (int)(ptr - in));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
lookup = lookup->next;
|
||||
}
|
||||
|
||||
if (!lookup) {
|
||||
/* find the lookup and hook it up */
|
||||
lookup = first_lookup;
|
||||
while (lookup) {
|
||||
if (strncmp(lookup->name, line, ptr - line) == 0 && lookup->name[ptr - line] == '\0') {
|
||||
part = RC_ALLOC(rc_richpresence_display_part_t, parse);
|
||||
memset(part, 0, sizeof(rc_richpresence_display_part_t));
|
||||
*next = part;
|
||||
next = &part->next;
|
||||
|
||||
/* find the closing parenthesis */
|
||||
part->text = lookup->name;
|
||||
part->lookup = lookup;
|
||||
part->display_type = lookup->format;
|
||||
|
||||
in = line;
|
||||
line = ++ptr;
|
||||
while (ptr < endline && *ptr != ')')
|
||||
++ptr;
|
||||
if (*ptr == ')')
|
||||
if (*ptr == ')') {
|
||||
part->value = rc_alloc_helper_variable_memref_value(line, (int)(ptr-line), parse);
|
||||
if (parse->offset < 0)
|
||||
return 0;
|
||||
++ptr;
|
||||
}
|
||||
else {
|
||||
/* non-terminated macro, dump the macro and the remaining portion of the line */
|
||||
--in; /* already skipped over @ */
|
||||
part->display_type = RC_FORMAT_STRING;
|
||||
part->text = rc_alloc_str(parse, in, (int)(ptr - in));
|
||||
}
|
||||
|
||||
/* assert: the allocated string is going to be smaller than the memory used for the parameter of the macro */
|
||||
part->display_type = RC_FORMAT_UNKNOWN_MACRO;
|
||||
part->text = rc_alloc_str(parse, line, (int)(ptr - line));
|
||||
break;
|
||||
}
|
||||
|
||||
lookup = lookup->next;
|
||||
}
|
||||
|
||||
if (!lookup) {
|
||||
part = RC_ALLOC(rc_richpresence_display_part_t, parse);
|
||||
memset(part, 0, sizeof(rc_richpresence_display_part_t));
|
||||
*next = part;
|
||||
next = &part->next;
|
||||
|
||||
/* find the closing parenthesis */
|
||||
while (ptr < endline && *ptr != ')')
|
||||
++ptr;
|
||||
if (*ptr == ')')
|
||||
++ptr;
|
||||
|
||||
/* assert: the allocated string is going to be smaller than the memory used for the parameter of the macro */
|
||||
part->display_type = RC_FORMAT_UNKNOWN_MACRO;
|
||||
part->text = rc_alloc_str(parse, line, (int)(ptr - line));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -195,76 +203,236 @@ static rc_richpresence_display_t* rc_parse_richpresence_display_internal(const c
|
||||
return self;
|
||||
}
|
||||
|
||||
static const char* rc_parse_richpresence_lookup(rc_richpresence_lookup_t* lookup, const char* nextline, rc_parse_state_t* parse)
|
||||
static int rc_richpresence_lookup_item_count(rc_richpresence_lookup_item_t* item)
|
||||
{
|
||||
if (item == NULL)
|
||||
return 0;
|
||||
|
||||
return (rc_richpresence_lookup_item_count(item->left) + rc_richpresence_lookup_item_count(item->right) + 1);
|
||||
}
|
||||
|
||||
static void rc_rebalance_richpresence_lookup_get_items(rc_richpresence_lookup_item_t* root,
|
||||
rc_richpresence_lookup_item_t** items, int* index)
|
||||
{
|
||||
if (root->left != NULL)
|
||||
rc_rebalance_richpresence_lookup_get_items(root->left, items, index);
|
||||
|
||||
items[*index] = root;
|
||||
++(*index);
|
||||
|
||||
if (root->right != NULL)
|
||||
rc_rebalance_richpresence_lookup_get_items(root->right, items, index);
|
||||
}
|
||||
|
||||
static void rc_rebalance_richpresence_lookup_rebuild(rc_richpresence_lookup_item_t** root,
|
||||
rc_richpresence_lookup_item_t** items, int first, int last)
|
||||
{
|
||||
int mid = (first + last) / 2;
|
||||
rc_richpresence_lookup_item_t* item = items[mid];
|
||||
*root = item;
|
||||
|
||||
if (mid == first)
|
||||
item->left = NULL;
|
||||
else
|
||||
rc_rebalance_richpresence_lookup_rebuild(&item->left, items, first, mid - 1);
|
||||
|
||||
if (mid == last)
|
||||
item->right = NULL;
|
||||
else
|
||||
rc_rebalance_richpresence_lookup_rebuild(&item->right, items, mid + 1, last);
|
||||
}
|
||||
|
||||
static void rc_rebalance_richpresence_lookup(rc_richpresence_lookup_item_t** root, rc_parse_state_t* parse)
|
||||
{
|
||||
rc_richpresence_lookup_item_t** items;
|
||||
rc_scratch_buffer_t* buffer;
|
||||
const int alignment = sizeof(rc_richpresence_lookup_item_t*);
|
||||
int index;
|
||||
int size;
|
||||
|
||||
/* don't bother rebalancing one or two items */
|
||||
int count = rc_richpresence_lookup_item_count(*root);
|
||||
if (count < 3)
|
||||
return;
|
||||
|
||||
/* allocate space for the flattened list - prefer scratch memory if available */
|
||||
size = count * sizeof(rc_richpresence_lookup_item_t*);
|
||||
buffer = &parse->scratch.buffer;
|
||||
do {
|
||||
const int aligned_offset = (buffer->offset + alignment - 1) & ~(alignment - 1);
|
||||
const int remaining = sizeof(buffer->buffer) - aligned_offset;
|
||||
|
||||
if (remaining >= size) {
|
||||
items = (rc_richpresence_lookup_item_t**)&buffer->buffer[aligned_offset];
|
||||
break;
|
||||
}
|
||||
|
||||
buffer = buffer->next;
|
||||
if (buffer == NULL) {
|
||||
/* could not find large enough block of scratch memory; allocate. if allocation fails,
|
||||
* we can still use the unbalanced tree, so just bail out */
|
||||
items = (rc_richpresence_lookup_item_t**)malloc(size);
|
||||
if (items == NULL)
|
||||
return;
|
||||
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
|
||||
/* flatten the list */
|
||||
index = 0;
|
||||
rc_rebalance_richpresence_lookup_get_items(*root, items, &index);
|
||||
|
||||
/* and rebuild it as a balanced tree */
|
||||
rc_rebalance_richpresence_lookup_rebuild(root, items, 0, count - 1);
|
||||
|
||||
if (buffer == NULL)
|
||||
free(items);
|
||||
}
|
||||
|
||||
static void rc_insert_richpresence_lookup_item(rc_richpresence_lookup_t* lookup,
|
||||
unsigned first, unsigned last, const char* label, int label_len, rc_parse_state_t* parse)
|
||||
{
|
||||
rc_richpresence_lookup_item_t** next;
|
||||
rc_richpresence_lookup_item_t* item;
|
||||
char number[64];
|
||||
|
||||
next = &lookup->root;
|
||||
while ((item = *next) != NULL) {
|
||||
if (first > item->last) {
|
||||
if (first == item->last + 1 &&
|
||||
strncmp(label, item->label, label_len) == 0 && item->label[label_len] == '\0') {
|
||||
item->last = last;
|
||||
return;
|
||||
}
|
||||
|
||||
next = &item->right;
|
||||
}
|
||||
else if (last < item->first) {
|
||||
if (last == item->first - 1 &&
|
||||
strncmp(label, item->label, label_len) == 0 && item->label[label_len] == '\0') {
|
||||
item->first = first;
|
||||
return;
|
||||
}
|
||||
|
||||
next = &item->left;
|
||||
}
|
||||
else {
|
||||
parse->offset = RC_DUPLICATED_VALUE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
item = RC_ALLOC_SCRATCH(rc_richpresence_lookup_item_t, parse);
|
||||
item->first = first;
|
||||
item->last = last;
|
||||
item->label = rc_alloc_str(parse, label, label_len);
|
||||
item->left = item->right = NULL;
|
||||
|
||||
*next = item;
|
||||
}
|
||||
|
||||
static const char* rc_parse_richpresence_lookup(rc_richpresence_lookup_t* lookup, const char* nextline, rc_parse_state_t* parse)
|
||||
{
|
||||
const char* line;
|
||||
const char* endline;
|
||||
const char* defaultlabel = 0;
|
||||
const char* label;
|
||||
char* endptr = 0;
|
||||
unsigned key;
|
||||
unsigned chars;
|
||||
|
||||
next = &lookup->first_item;
|
||||
unsigned first, last;
|
||||
int base;
|
||||
|
||||
do
|
||||
{
|
||||
line = nextline;
|
||||
nextline = rc_parse_line(line, &endline);
|
||||
nextline = rc_parse_line(line, &endline, parse);
|
||||
|
||||
if (endline - line < 2)
|
||||
break;
|
||||
|
||||
chars = 0;
|
||||
while (chars < (sizeof(number) - 1) && line + chars < endline && line[chars] != '=') {
|
||||
number[chars] = line[chars];
|
||||
++chars;
|
||||
}
|
||||
number[chars] = '\0';
|
||||
|
||||
if (line[chars] == '=') {
|
||||
line += chars + 1;
|
||||
|
||||
if (chars == 1 && number[0] == '*') {
|
||||
defaultlabel = rc_alloc_str(parse, line, (int)(endline - line));
|
||||
if (endline - line < 2) {
|
||||
/* ignore full line comments inside a lookup */
|
||||
if (line[0] == '/' && line[1] == '/')
|
||||
continue;
|
||||
}
|
||||
|
||||
if (number[0] == '0' && number[1] == 'x')
|
||||
key = strtoul(&number[2], &endptr, 16);
|
||||
else
|
||||
key = strtoul(&number[0], &endptr, 10);
|
||||
|
||||
if (*endptr && !isspace((unsigned char)*endptr)) {
|
||||
parse->offset = RC_INVALID_CONST_OPERAND;
|
||||
return nextline;
|
||||
}
|
||||
|
||||
item = RC_ALLOC(rc_richpresence_lookup_item_t, parse);
|
||||
item->value = key;
|
||||
item->label = rc_alloc_str(parse, line, (int)(endline - line));
|
||||
*next = item;
|
||||
next = &item->next_item;
|
||||
/* empty line indicates end of lookup */
|
||||
if (lookup->root)
|
||||
rc_rebalance_richpresence_lookup(&lookup->root, parse);
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
|
||||
if (!defaultlabel)
|
||||
defaultlabel = rc_alloc_str(parse, "", 0);
|
||||
/* "*=XXX" specifies default label if lookup does not provide a mapping for the value */
|
||||
if (line[0] == '*' && line[1] == '=') {
|
||||
line += 2;
|
||||
lookup->default_label = rc_alloc_str(parse, line, (int)(endline - line));
|
||||
continue;
|
||||
}
|
||||
|
||||
item = RC_ALLOC(rc_richpresence_lookup_item_t, parse);
|
||||
item->value = 0;
|
||||
item->label = defaultlabel;
|
||||
item->next_item = 0;
|
||||
*next = item;
|
||||
label = line;
|
||||
while (label < endline && *label != '=')
|
||||
++label;
|
||||
|
||||
if (label == endline) {
|
||||
parse->offset = RC_MISSING_VALUE;
|
||||
break;
|
||||
}
|
||||
++label;
|
||||
|
||||
do {
|
||||
/* get the value for the mapping */
|
||||
if (line[0] == '0' && line[1] == 'x') {
|
||||
line += 2;
|
||||
base = 16;
|
||||
} else {
|
||||
base = 10;
|
||||
}
|
||||
|
||||
first = strtoul(line, &endptr, base);
|
||||
|
||||
/* check for a range */
|
||||
if (*endptr != '-') {
|
||||
/* no range, just set last to first */
|
||||
last = first;
|
||||
}
|
||||
else {
|
||||
/* range, get last value */
|
||||
line = endptr + 1;
|
||||
|
||||
if (line[0] == '0' && line[1] == 'x') {
|
||||
line += 2;
|
||||
base = 16;
|
||||
} else {
|
||||
base = 10;
|
||||
}
|
||||
|
||||
last = strtoul(line, &endptr, base);
|
||||
}
|
||||
|
||||
/* ignore spaces after the number - was previously ignored as string was split on equals */
|
||||
while (*endptr == ' ')
|
||||
++endptr;
|
||||
|
||||
/* if we've found the equal sign, this is the last item */
|
||||
if (*endptr == '=') {
|
||||
rc_insert_richpresence_lookup_item(lookup, first, last, label, (int)(endline - label), parse);
|
||||
break;
|
||||
}
|
||||
|
||||
/* otherwise, if it's not a comma, it's an error */
|
||||
if (*endptr != ',') {
|
||||
parse->offset = RC_INVALID_CONST_OPERAND;
|
||||
break;
|
||||
}
|
||||
|
||||
/* insert the current item and continue scanning the next one */
|
||||
rc_insert_richpresence_lookup_item(lookup, first, last, label, (int)(endline - label), parse);
|
||||
line = endptr + 1;
|
||||
} while (line < endline);
|
||||
|
||||
} while (parse->offset > 0);
|
||||
|
||||
return nextline;
|
||||
}
|
||||
|
||||
void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script, rc_parse_state_t* parse) {
|
||||
rc_richpresence_display_t** nextdisplay;
|
||||
rc_richpresence_lookup_t** nextlookup;
|
||||
rc_richpresence_lookup_t* firstlookup = NULL;
|
||||
rc_richpresence_lookup_t** nextlookup = &firstlookup;
|
||||
rc_richpresence_lookup_t* lookup;
|
||||
rc_trigger_t* trigger;
|
||||
char format[64];
|
||||
@@ -274,21 +442,28 @@ void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script,
|
||||
const char* endline;
|
||||
const char* ptr;
|
||||
int hasdisplay = 0;
|
||||
int display_line = 0;
|
||||
int chars;
|
||||
|
||||
nextlookup = &self->first_lookup;
|
||||
/* special case for empty script to return 1 line read */
|
||||
if (!*script) {
|
||||
parse->lines_read = 1;
|
||||
parse->offset = RC_MISSING_DISPLAY_STRING;
|
||||
return;
|
||||
}
|
||||
|
||||
/* first pass: process macro initializers */
|
||||
line = script;
|
||||
while (*line)
|
||||
{
|
||||
nextline = rc_parse_line(line, &endline);
|
||||
while (*line) {
|
||||
nextline = rc_parse_line(line, &endline, parse);
|
||||
if (strncmp(line, "Lookup:", 7) == 0) {
|
||||
line += 7;
|
||||
|
||||
lookup = RC_ALLOC(rc_richpresence_lookup_t, parse);
|
||||
lookup = RC_ALLOC_SCRATCH(rc_richpresence_lookup_t, parse);
|
||||
lookup->name = rc_alloc_str(parse, line, (int)(endline - line));
|
||||
lookup->format = RC_FORMAT_LOOKUP;
|
||||
lookup->root = NULL;
|
||||
lookup->default_label = "";
|
||||
*nextlookup = lookup;
|
||||
nextlookup = &lookup->next;
|
||||
|
||||
@@ -299,14 +474,15 @@ void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script,
|
||||
} else if (strncmp(line, "Format:", 7) == 0) {
|
||||
line += 7;
|
||||
|
||||
lookup = RC_ALLOC(rc_richpresence_lookup_t, parse);
|
||||
lookup = RC_ALLOC_SCRATCH(rc_richpresence_lookup_t, parse);
|
||||
lookup->name = rc_alloc_str(parse, line, (int)(endline - line));
|
||||
lookup->first_item = 0;
|
||||
lookup->root = NULL;
|
||||
lookup->default_label = "";
|
||||
*nextlookup = lookup;
|
||||
nextlookup = &lookup->next;
|
||||
|
||||
line = nextline;
|
||||
nextline = rc_parse_line(line, &endline);
|
||||
nextline = rc_parse_line(line, &endline, parse);
|
||||
if (parse->buffer && strncmp(line, "FormatType=", 11) == 0) {
|
||||
line += 11;
|
||||
|
||||
@@ -322,10 +498,11 @@ void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script,
|
||||
}
|
||||
} else if (strncmp(line, "Display:", 8) == 0) {
|
||||
display = nextline;
|
||||
display_line = parse->lines_read;
|
||||
|
||||
do {
|
||||
line = nextline;
|
||||
nextline = rc_parse_line(line, &endline);
|
||||
nextline = rc_parse_line(line, &endline, parse);
|
||||
} while (*line == '?');
|
||||
}
|
||||
|
||||
@@ -333,12 +510,18 @@ void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script,
|
||||
}
|
||||
|
||||
*nextlookup = 0;
|
||||
self->first_lookup = firstlookup;
|
||||
|
||||
nextdisplay = &self->first_display;
|
||||
|
||||
/* second pass, process display string*/
|
||||
if (display) {
|
||||
/* point the parser back at the display strings */
|
||||
int lines_read = parse->lines_read;
|
||||
parse->lines_read = display_line;
|
||||
line = display;
|
||||
nextline = rc_parse_line(line, &endline);
|
||||
|
||||
nextline = rc_parse_line(line, &endline, parse);
|
||||
|
||||
while (*line == '?') {
|
||||
/* conditional display: ?trigger?string */
|
||||
@@ -347,7 +530,7 @@ void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script,
|
||||
++ptr;
|
||||
|
||||
if (ptr < endline) {
|
||||
*nextdisplay = rc_parse_richpresence_display_internal(ptr + 1, endline, parse, self);
|
||||
*nextdisplay = rc_parse_richpresence_display_internal(ptr + 1, endline, parse, firstlookup);
|
||||
if (parse->offset < 0)
|
||||
return;
|
||||
trigger = &((*nextdisplay)->trigger);
|
||||
@@ -360,15 +543,18 @@ void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script,
|
||||
}
|
||||
|
||||
line = nextline;
|
||||
nextline = rc_parse_line(line, &endline);
|
||||
nextline = rc_parse_line(line, &endline, parse);
|
||||
}
|
||||
|
||||
/* non-conditional display: string */
|
||||
*nextdisplay = rc_parse_richpresence_display_internal(line, endline, parse, self);
|
||||
*nextdisplay = rc_parse_richpresence_display_internal(line, endline, parse, firstlookup);
|
||||
if (*nextdisplay) {
|
||||
hasdisplay = 1;
|
||||
nextdisplay = &((*nextdisplay)->next);
|
||||
}
|
||||
|
||||
/* restore the parser state */
|
||||
parse->lines_read = lines_read;
|
||||
}
|
||||
|
||||
/* finalize */
|
||||
@@ -379,107 +565,152 @@ void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script,
|
||||
}
|
||||
}
|
||||
|
||||
int rc_richpresence_size(const char* script) {
|
||||
int rc_richpresence_size_lines(const char* script, int* lines_read) {
|
||||
rc_richpresence_t* self;
|
||||
rc_parse_state_t parse;
|
||||
rc_memref_t* first_memref;
|
||||
rc_value_t* variables;
|
||||
rc_init_parse_state(&parse, 0, 0, 0);
|
||||
rc_init_parse_state_memrefs(&parse, &first_memref);
|
||||
rc_init_parse_state_variables(&parse, &variables);
|
||||
|
||||
self = RC_ALLOC(rc_richpresence_t, &parse);
|
||||
rc_parse_richpresence_internal(self, script, &parse);
|
||||
|
||||
if (lines_read)
|
||||
*lines_read = parse.lines_read;
|
||||
|
||||
rc_destroy_parse_state(&parse);
|
||||
return parse.offset;
|
||||
}
|
||||
|
||||
int rc_richpresence_size(const char* script) {
|
||||
return rc_richpresence_size_lines(script, NULL);
|
||||
}
|
||||
|
||||
rc_richpresence_t* rc_parse_richpresence(void* buffer, const char* script, lua_State* L, int funcs_ndx) {
|
||||
rc_richpresence_t* self;
|
||||
rc_parse_state_t parse;
|
||||
|
||||
if (!buffer || !script)
|
||||
return NULL;
|
||||
|
||||
rc_init_parse_state(&parse, buffer, L, funcs_ndx);
|
||||
|
||||
self = RC_ALLOC(rc_richpresence_t, &parse);
|
||||
rc_init_parse_state_memrefs(&parse, &self->memrefs);
|
||||
rc_init_parse_state_variables(&parse, &self->variables);
|
||||
|
||||
rc_parse_richpresence_internal(self, script, &parse);
|
||||
|
||||
rc_destroy_parse_state(&parse);
|
||||
return parse.offset >= 0 ? self : 0;
|
||||
return (parse.offset >= 0) ? self : NULL;
|
||||
}
|
||||
|
||||
int rc_evaluate_richpresence(rc_richpresence_t* richpresence, char* buffer, unsigned buffersize, rc_peek_t peek, void* peek_ud, lua_State* L) {
|
||||
void rc_update_richpresence(rc_richpresence_t* richpresence, rc_peek_t peek, void* peek_ud, lua_State* L) {
|
||||
rc_richpresence_display_t* display;
|
||||
rc_richpresence_display_part_t* part;
|
||||
|
||||
rc_update_memref_values(richpresence->memrefs, peek, peek_ud);
|
||||
rc_update_variables(richpresence->variables, peek, peek_ud, L);
|
||||
|
||||
for (display = richpresence->first_display; display; display = display->next) {
|
||||
if (display->trigger.has_required_hits)
|
||||
rc_test_trigger(&display->trigger, peek, peek_ud, L);
|
||||
}
|
||||
}
|
||||
|
||||
static int rc_evaluate_richpresence_display(rc_richpresence_display_part_t* part, char* buffer, unsigned buffersize)
|
||||
{
|
||||
rc_richpresence_lookup_item_t* item;
|
||||
char tmp[256];
|
||||
char* ptr;
|
||||
char* ptr = buffer;
|
||||
const char* text;
|
||||
size_t chars;
|
||||
unsigned value;
|
||||
|
||||
rc_update_memref_values(richpresence->memrefs, peek, peek_ud);
|
||||
*ptr = '\0';
|
||||
while (part) {
|
||||
switch (part->display_type) {
|
||||
case RC_FORMAT_STRING:
|
||||
text = part->text;
|
||||
chars = strlen(text);
|
||||
break;
|
||||
|
||||
ptr = buffer;
|
||||
display = richpresence->first_display;
|
||||
while (display) {
|
||||
if (!display->next || rc_test_trigger(&display->trigger, peek, peek_ud, L)) {
|
||||
part = display->display;
|
||||
while (part) {
|
||||
switch (part->display_type) {
|
||||
case RC_FORMAT_STRING:
|
||||
text = part->text;
|
||||
chars = strlen(text);
|
||||
break;
|
||||
|
||||
case RC_FORMAT_LOOKUP:
|
||||
value = rc_evaluate_value(&part->value, peek, peek_ud, L);
|
||||
item = part->first_lookup_item;
|
||||
if (!item) {
|
||||
text = "";
|
||||
chars = 0;
|
||||
} else {
|
||||
while (item->next_item && item->value != value)
|
||||
item = item->next_item;
|
||||
|
||||
text = item->label;
|
||||
chars = strlen(text);
|
||||
}
|
||||
break;
|
||||
|
||||
case RC_FORMAT_UNKNOWN_MACRO:
|
||||
chars = snprintf(tmp, sizeof(tmp), "[Unknown macro]%s", part->text);
|
||||
text = tmp;
|
||||
break;
|
||||
|
||||
default:
|
||||
value = rc_evaluate_value(&part->value, peek, peek_ud, L);
|
||||
chars = rc_format_value(tmp, sizeof(tmp), value, part->display_type);
|
||||
text = tmp;
|
||||
break;
|
||||
}
|
||||
|
||||
if (chars > 0 && buffersize > 0) {
|
||||
if ((unsigned)chars >= buffersize) {
|
||||
/* prevent write past end of buffer */
|
||||
memcpy(ptr, text, buffersize - 1);
|
||||
ptr[buffersize - 1] = '\0';
|
||||
buffersize = 0;
|
||||
case RC_FORMAT_LOOKUP:
|
||||
value = part->value->value;
|
||||
text = part->lookup->default_label;
|
||||
item = part->lookup->root;
|
||||
while (item) {
|
||||
if (value > item->last) {
|
||||
item = item->right;
|
||||
}
|
||||
else if (value < item->first) {
|
||||
item = item->left;
|
||||
}
|
||||
else {
|
||||
memcpy(ptr, text, chars);
|
||||
ptr[chars] = '\0';
|
||||
buffersize -= (unsigned)chars;
|
||||
text = item->label;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ptr += chars;
|
||||
part = part->next;
|
||||
}
|
||||
chars = strlen(text);
|
||||
break;
|
||||
|
||||
return (int)(ptr - buffer);
|
||||
case RC_FORMAT_UNKNOWN_MACRO:
|
||||
chars = snprintf(tmp, sizeof(tmp), "[Unknown macro]%s", part->text);
|
||||
text = tmp;
|
||||
break;
|
||||
|
||||
default:
|
||||
value = part->value->value;
|
||||
chars = rc_format_value(tmp, sizeof(tmp), value, part->display_type);
|
||||
text = tmp;
|
||||
break;
|
||||
}
|
||||
|
||||
display = display->next;
|
||||
if (chars > 0 && buffersize > 0) {
|
||||
if ((unsigned)chars >= buffersize) {
|
||||
/* prevent write past end of buffer */
|
||||
memcpy(ptr, text, buffersize - 1);
|
||||
ptr[buffersize - 1] = '\0';
|
||||
buffersize = 0;
|
||||
}
|
||||
else {
|
||||
memcpy(ptr, text, chars);
|
||||
ptr[chars] = '\0';
|
||||
buffersize -= (unsigned)chars;
|
||||
}
|
||||
}
|
||||
|
||||
ptr += chars;
|
||||
part = part->next;
|
||||
}
|
||||
|
||||
return (int)(ptr - buffer);
|
||||
}
|
||||
|
||||
int rc_get_richpresence_display_string(rc_richpresence_t* richpresence, char* buffer, unsigned buffersize, rc_peek_t peek, void* peek_ud, lua_State* L) {
|
||||
rc_richpresence_display_t* display;
|
||||
|
||||
for (display = richpresence->first_display; display; display = display->next) {
|
||||
/* if we've reached the end of the condition list, process it */
|
||||
if (!display->next)
|
||||
return rc_evaluate_richpresence_display(display->display, buffer, buffersize);
|
||||
|
||||
/* triggers with required hits will be updated in rc_update_richpresence */
|
||||
if (!display->trigger.has_required_hits)
|
||||
rc_test_trigger(&display->trigger, peek, peek_ud, L);
|
||||
|
||||
/* if we've found a valid condition, process it */
|
||||
if (display->trigger.state == RC_TRIGGER_STATE_TRIGGERED)
|
||||
return rc_evaluate_richpresence_display(display->display, buffer, buffersize);
|
||||
}
|
||||
|
||||
buffer[0] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rc_evaluate_richpresence(rc_richpresence_t* richpresence, char* buffer, unsigned buffersize, rc_peek_t peek, void* peek_ud, lua_State* L) {
|
||||
rc_update_richpresence(richpresence, peek, peek_ud, L);
|
||||
return rc_get_richpresence_display_string(richpresence, buffer, buffersize, peek, peek_ud, L);
|
||||
}
|
||||
Loaded 100 of 397 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user